📜 ⬆️ ⬇️

Writing games in C ++, Part 1/3 - Writing a mini-framework

Writing games in C ++, Part 2/3 - State-based programming
Writing games in C ++, Part 3/3 - Classics of the genre

Hello, Habrahabr!

On Habré there are not a lot of lessons on creating games, why not support domestic developers?
I present to you my lessons that teach you how to create games in C ++ using SDL!
')

What you need to know




What is this part about?




The following posts will be more action, this is just a preparation :)



Why SDL?


I chose this library as the easiest and fastest to learn. Indeed, from the first read article on OpenGL or DirectX to the hundred thousandth reprint of the snake will take a long time.

Now you can start.

1.1. The beginning of time


Download SDL from the official site.
Create a Win32 project in Visual Studio, connect the libs and include the SDL (if you are not able to do this, then google will help you!)

You also need to use multi-byte character encoding. To do this, go to Project-> Properties-> Configuration Properties-> Character Set-> Use multibyte encoding.

We create the main.cpp file
#include <Windows.h> int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int) { return 0; } 

So far, he does nothing.

King and God of the Frame - Game Class
Game.h
 #ifndef _GAME_H_ #define _GAME_H_ class Game { private: bool run; public: Game(); int Execute(); void Exit(); }; #endif 

Game.cpp
 #include "Game.h" Game::Game() { run = true; } int Game::Execute() { while(run); return 0; } void Game::Exit() { run = false; } 


Create a file Project.h, it will be very useful to us in the future.
 #ifndef _PROJECT_H_ #define _PROJECT_H_ #include <Windows.h> #include "Game.h" #endif 


Change main.cpp
 #include "Project.h" int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int) { Game game; return game.Execute(); } 


Already a little better, but still somehow not thick.

1.2. Graphics


Create as much as 2 classes - Graphics for drawing graphics and Image for drawing images

Graphics.h
 #ifndef _GRAPHICS_H_ #define _GRAPHICS_H_ #include "Project.h" #include "Image.h" class Image; class Graphics { private: SDL_Surface* Screen; public: Graphics(int width, int height); Image* NewImage(char* file); Image* NewImage(char* file, int r, int g, int b); bool DrawImage(Image* img, int x, int y); bool DrawImage(Image* img, int x, int y, int startX, int startY, int endX, int endY); void Flip(); }; #endif 


Image.h
 #ifndef _IMAGE_H #define _IMAGE_H #include "Project.h" class Image { private: SDL_Surface* surf; public: friend class Graphics; int GetWidth(); int GetHeight(); }; #endif 


Change Project.h
 #ifndef _PROJECT_H_ #define _PROJECT_H_ #pragma comment(lib,"SDL.lib") #include <Windows.h> #include <SDL.h> #include "Game.h" #include "Graphics.h" #include "Image.h" #endif 


SDL_Surface - class from SDL for storing information about a picture
Consider Graphics
NewImage - there are 2 options for downloading images. The first option just loads the picture, and the second after that also gives transparency to the picture. If we have a red background in the picture, then enter r = 255, g = 0, b = 0
DrawImage - also 2 options for drawing pictures. The first draws the entire picture, the second only part of the picture. startX, startY - coordinates of the beginning of the image. endX, endY - the final coordinates of the image. This drawing method is used if image atlases are used. Here is an example of an atlas:

image
(the image is taken from the web resource interesnoe.info)

Consider Image
He simply holds his surface and gives access to his private members to the Graphics class, and he changes the surface.
In essence, this is a wrapper over SDL_Surface. It also gives the size of the picture.

Graphics.cpp
 #include "Graphics.h" Graphics::Graphics(int width, int height) { SDL_Init(SDL_INIT_EVERYTHING); Screen = SDL_SetVideoMode(width,height,32,SDL_HWSURFACE|SDL_DOUBLEBUF); } Image* Graphics::NewImage(char* file) { Image* image = new Image(); image->surf = SDL_DisplayFormat(SDL_LoadBMP(file)); return image; } Image* Graphics::NewImage(char* file, int r, int g, int b) { Image* image = new Image(); image->surf = SDL_DisplayFormat(SDL_LoadBMP(file)); SDL_SetColorKey(image->surf, SDL_SRCCOLORKEY | SDL_RLEACCEL, SDL_MapRGB(image->surf->format, r, g, b)); return image; } bool Graphics::DrawImage(Image* img, int x, int y) { if(Screen == NULL || img->surf == NULL) return false; SDL_Rect Area; Area.x = x; Area.y = y; SDL_BlitSurface(img->surf, NULL, Screen, &Area); return true; } bool Graphics::DrawImage(Image* img, int x, int y, int startX, int startY, int endX, int endY) { if(Screen == NULL || img->surf == NULL) return false; SDL_Rect Area; Area.x = x; Area.y = y; SDL_Rect SrcArea; SrcArea.x = startX; SrcArea.y = startY; SrcArea.w = endX; SrcArea.h = endY; SDL_BlitSurface(img->surf, &SrcArea, Screen, &Area); return true; } void Graphics::Flip() { SDL_Flip(Screen); SDL_FillRect(Screen,NULL, 0x000000); } 

The constructor initializes the SDL and creates a screen.
The Flip function should be called every time after drawing the pictures, it presents the resulting screen and cleans the screen in black for further drawing.
The rest of the functions of little interest, I recommend to understand them yourself

Image.cpp
 #include "Image.h" int Image::GetWidth() { return surf->w; } int Image::GetHeight() { return surf->h; } 

No, you're doing everything right, this file should be like this :)

You need to change Game.h, Game.cpp and main.cpp
Game.h
 #ifndef _GAME_H_ #define _GAME_H_ #include "Project.h" class Graphics; class Game { private: bool run; Graphics* graphics; public: Game(); int Execute(int width, int height); void Exit(); }; #endif 

Here we add a pointer to the Graphics and in the Execute add the screen size

Game.cpp
 #include "Game.h" Game::Game() { run = true; } int Game::Execute(int width, int height) { graphics = new Graphics(width,height); while(run); SDL_Quit(); return 0; } void Game::Exit() { run = false; } 


Nothing special, except skip the SDL_Quit function to clear the SDL

main.cpp
 #include "Project.h" int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int) { Game game; return game.Execute(500,350); } 

Here we create a 500 by 350 screen.

1.3. Input


Need to work with keyboard input.

Create Input.h
 #ifndef _INPUT_H_ #define _INPUT_H_ #include "Project.h" class Input { private: SDL_Event evt; public: void Update(); bool IsMouseButtonDown(byte key); bool IsMouseButtonUp(byte key); POINT GetButtonDownCoords(); bool IsKeyDown(byte key); bool IsKeyUp(byte key); byte GetPressedKey(); bool IsExit(); }; #endif 

SDL_Event - a class of some event, we keep it in Input to not create an object of this class every cycle
Below are the methods that are not of particular interest. Note: methods with the end of Down are called when the key is pressed, and with the end of Up, when it is lowered.

Input.cpp
 #include "Input.h" void Input::Update() { while(SDL_PollEvent(&evt)); } bool Input::IsMouseButtonDown(byte key) { if(evt.type == SDL_MOUSEBUTTONDOWN) if(evt.button.button == key) return true; return false; } bool Input::IsMouseButtonUp(byte key) { if(evt.type == SDL_MOUSEBUTTONUP) if(evt.button.button == key) return true; return false; } POINT Input::GetButtonDownCoords() { POINT point; point.x = evt.button.x; point.y = evt.button.y; return point; } bool Input::IsKeyDown(byte key) { return (evt.type == SDL_KEYDOWN && evt.key.keysym.sym == key); } bool Input::IsKeyUp(byte key) { return (evt.type == SDL_KEYUP && evt.key.keysym.sym == key); } byte Input::GetPressedKey() { return evt.key.keysym.sym; } bool Input::IsExit() { return (evt.type == SDL_QUIT); } 

Here we process our event object in the Update function, and the rest of the functions simply check the type of the event and its values.

We change now Game.h and Game.cpp
 #ifndef _GAME_H_ #define _GAME_H_ #include "Project.h" #include "Graphics.h" class Graphics; #include "Input.h" class Input; class Game { private: bool run; Graphics* graphics; Input* input; public: Game(); int Execute(int width, int height); Graphics* GetGraphics(); Input* GetInput(); void Exit(); }; #endif 

As you can see, we added a pointer to the Input and created the return methods of the Graphics and Input.

Game.cpp
 #include "Game.h" Game::Game() { run = true; } int Game::Execute(int width, int height) { graphics = new Graphics(width,height); input = new Input(); while(run) { input->Update(); } delete graphics; delete input; SDL_Quit(); return 0; } Graphics* Game::GetGraphics() { return graphics; } Input* Game::GetInput() { return input; } void Game::Exit() { run = false; } 


1.4. Results


This was the first lesson. If you have reached this place, I congratulate you! You have the will inherent in the programmer :) See the links at the beginning of the article for more lessons to learn more!

For all questions, please contact the PM, and if you are not lucky enough to be registered in the Habré, write to me at izarizar@mail.ru

Source: https://habr.com/ru/post/197278/


All Articles