Пишем игры на C++, Часть 3/3 — Классика жанра
Здравствуй, Хабрахабр!
На хабре не очень много уроков по созданию игр, почему бы не поддержать отечественных девелоперов?
Представляю вам свои уроки, которые учат создавать игры на C++ с использованием SDL!
Что нужно знать
- Хотя бы начальные знания C++ (использовать будем Visual Studio)
- Терпение
О чем эта часть?
- Мы создадим каркас для всех игр, в качестве отрисовщика будем использовать SDL. Это библиотека для графики.
В следующих постах будет больше экшена, это лишь подготовка 🙂
Почему SDL?
Я выбрал эту библиотеку как наиболее легкую и быструю в освоении. Действительно, от первой прочитанной статьи по OpenGL или DirectX до стотысячного переиздания змейки пройдет немало времени.
Теперь можно стартовать.
1.1. Начало начал
Скачиваем SDL с официального сайта.
Создаем проект Win32 в Visual Studio, подключаем lib’ы и includ’ы SDL (если вы не умеете этого делать, то гугл вам в помощь!)
Также необходимо использовать многобайтную кодировку символов. Для этого идем в Проект->Свойства->Свойства конфигурации->Набор символов->Использовать многобайтную кодировку.
Создаем файл main.cpp
#include <Windows.h> int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int) { return 0; }
Пока что он ничего не делает.
Царь и бог каркаса — класс Game
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; }
Создаем файл Project.h, он нам очень пригодится в будущем
#ifndef _PROJECT_H_ #define _PROJECT_H_ #include <Windows.h> #include "Game.h" #endif
Изменяем main.cpp
#include "Project.h" int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int) { Game game; return game.Execute(); }
Уже чуточку получше, но все равно как-то не густо.
1.2. Графика
Создаем аж 2 класса — Graphics для отрисовки графики и Image для отрисовки картинок
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
Изменяем 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 — класс из SDL для хранения информации об картинке
Рассмотрим Graphics
NewImage — есть 2 варианта загрузки картинки. Первый вариант просто грузит картинку, а второй после этого еще и дает прозрачность картинке. Если у нас красный фон в картинке, то вводим r=255,g=0,b=0
DrawImage — тоже 2 варианта отрисовки картинки. Первый рисует всю картинку целиком, второй только часть картинки. startX, startY — координаты начала части картинки. endX, endY — конечные координаты части картинки. Этот метод рисования применяется, если используются атласы картинок. Вот пример атласа:
(изображение взято из веб-ресурса interesnoe.info)
Рассмотрим Image
Он просто держит свой сурфейс и дает право доступа к своим закрытым членам классу Graphics, а он изменяет сурфейс.
По сути, это обертка над SDL_Surface. Также он дает размер картинки
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); }
В конструкторе инициализируется SDL и создается экран.
Функция Flip должна вызываться каждый раз после отрисовки картинок, она представляет получившееся на экран и чистит экран в черный цвет для дальнешней отрисовки.
Остальные функции малоинтересны, рекомендую разобраться в них самому
Image.cpp
#include "Image.h" int Image::GetWidth() { return surf->w; } int Image::GetHeight() { return surf->h; }
Нет, вы все правильно делаете, этот файл и должен быть таким 🙂
Надо изменить Game.h, Game.cpp и 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
Тут мы добавляем указатель на Graphics и в Execute добавляем размер экрана
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; }
Ничего особенного, разве что не пропустите функцию SDL_Quit для очистки SDL
main.cpp
#include "Project.h" int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int) { Game game; return game.Execute(500,350); }
Тут мы создаем экран размером 500 на 350.
1.3. Ввод
Надо поработать со вводом с клавиатуры
Создаем 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 — класс какого-нибудь события, его мы держим в Input’е для того, чтобы не создавать объект этого класса каждый цикл
Ниже расположены методы, не представляющие особого интереса. Примечание: методы с окончанием Down вызываются, когда клавиша была нажата, а с окончанием Up — когда опущена.
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); }
Здесь мы обрабатываем наш объект событий в функции Update, а остальные функции просто проверяют тип события и его значения.
Изменяем теперь Game.h и 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
Как видно, мы добавили указатель на Input и создали методы-возвращатели Graphics и 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. Итоги
Это был первый урок. Если вы дошли до этого места, я вас поздравляю! У вас есть воля, присущая программисту 🙂 Смотрите ссылки в начале статьи на последующие уроки для того, чтобы узнать еще много нового!
По всем вопросам обращайтесь в ЛС, а если вам не повезло быть зарегистрированным на хабре, пишите на мейл izarizar@mail.ru
ссылка на оригинал статьи http://habrahabr.ru/post/197278/
Добавить комментарий