Работа мечты в один клик 💼

💭Мечтаешь работать в Сбере, но не хочешь проходить десять кругов HR-собеседований? Теперь это проще, чем когда-либо!
💡AI-интервью за 15 минут – и ты уже на шаг ближе к своей новой работе.
Как получить оффер?
📌 Зарегистрируйся
📌 Пройди AI-интервью
📌 Получи обратную связь сразу же!
HR больше не тянут время – рекрутеры свяжутся с тобой в течение двух дней! 🚀
Реклама. ПАО СБЕРБАНК, ИНН 7707083893. Erid 2VtzquscAwp
С нуля создадим собственный игровой движок с помощью библиотеки SFML и C++, чтобы разобраться, как происходит создание ядра.
В этом проекте мы создадим собственный игровой движок на C++. Движок будет очень простым, но готовым к расширению возможностей. Конечная игра с использованием этого кода тоже крайне проста: наш персонаж сможет перемещаться влево и право, а из графики – только бэкграунд и фигурка персонажа.
Подготовка Visual Studio
Создадим новый проект в Visual Studio. Обратите внимание, что проект требует библиотеку SFML, поэтому если вы не настраивали окружение для нее, прочтите сначала небольшое руководство по настройке.
Настройка VS:
- Откройте Visual Studio и выберите File | New Project. В левом меню отметьте язык C++ и шаблон HelloSFML. Назовите проект Simple Game Engine.
- Выберите правой кнопкой мыши файл HelloSFML.cpp в окне Solution Explorer (под Source Files), далее – Rename и назовите файл Main. Это подходящее имя, так как файл будет содержать основную функцию.
- Откройте Main.cpp, удалите все содержимое файла.
- Переместите файл библиотеки SFML.dll из каталога Диск:\SFML\bin в Диск:\Visual Studio Stuff\Projects\Simple Game Engine\Simple Game Engine. Если названия в путях отличаются, вероятно, вы сделали отличную от руководства настройку.
Теперь можно приступить к коду. Исходный код и дополнительные ресурсы будут доступны на этой странице.
Проектируем собственный игровой движок
Самое важное – запуск движка, который будет происходить в файле Main.cpp, но им мы займемся немного позже.
Класс персонажа
Bob – простой класс для представления фигурки персонажа, управляемой игроком. Код класса будет легко расширяться, а что самое главное – его несложно переписать под любой другой игровой объект, который вы захотите добавить. Для этого потребуется заменить текстуру и описать поведение нового объекта в методе update().
Bob.h
Займемся заголовками класса. Выберите правой кнопкой Header Files в Solution Explorer и нажмите Add | New Item. В окне Add New Item выберите Header File (.h), затем в поле Name введите Bob. Нажмите Add и добавьте код заголовка класса:
#pragma once
#include <SFML/Graphics.hpp>
using namespace sf;
class Bob
{
// Все private переменные могут быть доступны только внутри объекта
private:
// Позиция Боба
Vector2f m_Position;
// Объявляем объект Sprite
Sprite m_Sprite;
// Добавдяем текстуру
Texture m_Texture;
// Логические переменные для отслеживания направления движения
bool m_LeftPressed;
bool m_RightPressed;
// Скорость Боба в пикселях в секунду
float m_Speed;
// Открытые методы
public:
// Настраиваем Боба в конструкторе
Bob();
// Для отправки спрайта в главную функцию
Sprite getSprite();
// Для движения Боба
void moveLeft();
void moveRight();
// Прекращение движения
void stopLeft();
void stopRight();
// Эта функция будет вызываться на каждый кадр
void update(float elapsedTime);
};
Здесь мы объявили объекты типа Texture и Sprite. Дальше мы свяжем эти объекты и любое действие на экране с объектом Sprite будет сопровождаться изображением Боба:
Кликните правой кнопкой, чтобы сохранить
Bob.cpp
Теперь приступим к описанию методов.
Выберите правой кнопкой мыши Source Files в Solution Explorer и откройте Add | New Item. В окне Add New Item кликните по C++ File (.cpp), а в поле Name укажите Bob.cpp. Теперь добавьте в файл код:
#include "stdafx.h"
#include "bob.h"
Bob::Bob()
{
// Вписываем в переменную скорость Боба
m_Speed = 400;
// Связываем текстуру и спрайт
m_Texture.loadFromFile("bob.png");
m_Sprite.setTexture(m_Texture);
// Устанавливаем начальную позицию Боба в пикселях
m_Position.x = 500;
m_Position.y = 800;
}
// Делаем приватный спрайт доступным для функции draw()
Sprite Bob::getSprite()
{
return m_Sprite;
}
void Bob::moveLeft()
{
m_LeftPressed = true;
}
void Bob::moveRight()
{
m_RightPressed = true;
}
void Bob::stopLeft()
{
m_LeftPressed = false;
}
void Bob::stopRight()
{
m_RightPressed = false;
}
// Двигаем Боба на основании пользовательского ввода в этом кадре,
// прошедшего времени и скорости
void Bob::update(float elapsedTime)
{
if (m_RightPressed)
{
m_Position.x += m_Speed * elapsedTime;
}
if (m_LeftPressed)
{
m_Position.x -= m_Speed * elapsedTime;
}
// Теперь сдвигаем спрайт на новую позицию
m_Sprite.setPosition(m_Position);
}
В конструкторе мы установили значение переменной m_Speed на 400. Это значит, что Боб пересечет экран шириной в 1920 пикселей за 5 секунд. Также мы загрузили файл Bob.png в Texture и связали его с объектом Sprite. В переменных m_Position.x и m_Position.y установлено начальное положение Боба.
Функция update обрабатывает два If. Первое If проверяет, нажата ли правая кнопка (m_RightPressed), а второе следит за левой (m_LeftPressed). В каждом If скорость (m_Speed) умножается на elapsedTime. Переменная elapsedTime рассчитывается в функции Start движка (класс Engine). Им мы и займемся далее.
Пишем класс Engine
Класс Engine будет контролировать все остальное.
Engine.h
Добавим заголовок. Откройте окно Add New Item (так же, как для класса Bob), выберите Header File (.h) и в поле Name введите Engine.h. Добавьте в файл следующий код:
#pragma once
#include <SFML/Graphics.hpp>
#include "Bob.h";
using namespace sf;
class Engine
{
private:
RenderWindow m_Window;
// Объявляем спрайт и текстуру для фона
Sprite m_BackgroundSprite;
Texture m_BackgroundTexture;
// Экземпляр Боба
Bob m_Bob;
void input();
void update(float dtAsSeconds);
void draw();
public:
// Конструктор движка
Engine();
// Функция старт вызовет все приватные функции
void start();
};
Класс библиотеки SFML, RenderWIndow, используется для рендера всего, что есть на экране. Переменные Sprite и Texture нужны для создания фона. Также в заголовке мы создали экземпляр класса Bob.
Engine.cpp
В Engine.cpp мы опишем конструктор и функцию start. Создайте файл класса так же, как для Bob.cpp, и добавьте в него код:
#include "stdafx.h"
#include "Engine.h"
Engine::Engine()
{
// Получаем разрешение экрана, создаем окно SFML и View
Vector2f resolution;
resolution.x = VideoMode::getDesktopMode().width;
resolution.y = VideoMode::getDesktopMode().height;
m_Window.create(VideoMode(resolution.x, resolution.y),
"Simple Game Engine",
Style::Fullscreen);
// Загружаем фон в текстуру
// Подготовьте изображение под ваш размер экрана в редакторе
m_BackgroundTexture.loadFromFile("background.jpg");
// Связываем спрайт и текстуру
m_BackgroundSprite.setTexture(m_BackgroundTexture);
}
void Engine::start()
{
// Расчет времени
Clock clock;
while (m_Window.isOpen())
{
// Перезапускаем таймер и записываем отмеренное время в dt
Time dt = clock.restart();
float dtAsSeconds = dt.asSeconds();
input();
update(dtAsSeconds);
draw();
}
}
Функция конструктора получает разрешение экрана и разворачивает игру на весь экран с помощью m_Window.create. В конструкторе же загружается Texture и связывается с объектом Sprite.
Пример фонового изображения
Скачайте пример изображения или используйте любое другое на свое усмотрение. Переименуйте файл в background.jpg и поместите в каталог Simple Game Engine/Simple Game Engine.
Игровой цикл
Следующие три функции будут описаны каждая в своем файле, но при этом они должны быть частью Engine.h. Поэтому в начале каждого файла укажем директиву #include «Engine.h», так что Visual Studio будет знать, что мы делаем.
Обрабатываем пользовательский ввод
Создайте файл Input.cpp и добавьте в него код:
#include "stdafx.h"
#include "Engine.h"
void Engine::input()
{
// Обрабатываем нажатие Escape
if (Keyboard::isKeyPressed(Keyboard::Escape))
{
m_Window.close();
}
// Обрабатываем нажатие клавиш движения
if (Keyboard::isKeyPressed(Keyboard::A))
{
m_Bob.moveLeft();
}
else
{
m_Bob.stopLeft();
}
if (Keyboard::isKeyPressed(Keyboard::D))
{
m_Bob.moveRight();
}
else
{
m_Bob.stopRight();
}
}
Функция input обрабатывает нажатия клавиш через константу Keyboard::isKeyPressed, предоставляемую SFML. При нажатии Escape m_Window будет закрыто. Для клавиш A и D вызывается соответствующая функция движения.
Обновляем игровые объекты
Теперь опишем простую функцию update. Каждый игровой объект будет иметь собственную функцию update.
Создайте файл Update.cpp и добавьте в него код:
#include "stdafx.h"
#include "Engine.h"
using namespace sf;
void Engine::update(float dtAsSeconds)
{
m_Bob.update(dtAsSeconds);
}
Поскольку у нас пока только один объект "Боб", мы вызываем только функцию m_Bob.update.
Отрисовка сцены
Это последняя функция класса Engine. Создайте файл Draw.cpp и добавьте в него код:
#include "stdafx.h"
#include "Engine.h"
void Engine::draw()
{
// Стираем предыдущий кадр
m_Window.clear(Color::White);
// Отрисовываем фон
m_Window.draw(m_BackgroundSprite);
m_Window.draw(m_Bob.getSprite());
// Отображаем все, что нарисовали
m_Window.display();
}
Экран очищается методом clear, затем отрисовывается фон. Первым делом должен быть отрисован фон, чтобы потом поверх него можно было отрисовать Боба.
Запускаем движок в main()
Теперь вернемся к нашему Main.cpp. Время добавить в него немного кода:
#include "stdafx.h"
#include "Engine.h"
int main()
{
// Объявляем экземпляр класса Engine
Engine engine;
// Вызываем функцию start
engine.start();
// Останавливаем программу программу, когда движок остановлен
return 0;
}
Несколько слов в конце
Наш собственный игровой движок получился очень простым: он умеет только двигать главный объект и закрывать программу. Он не умеет обрабатывать столкновения, работать с интерфейсом и еще много чего. Однако он отлично описывает то, как строится ядро игрового проекта с нуля. К тому же, как мы уже выяснили, класс Bob расширяется и адаптируется под другие объекты, так что дайте волю фантазии и попробуйте поэкспериментировать с окружением.
Комментарии