You can find the project Trello here
- Language: C++
- IDE: Visual Studio 2022
- Libarary(ies):
- SFML (For renderering, audio & camera)
The program is iscolated into 2 projects within the solution, the game engine (Engine) and the client (Game). The entry point is within the Engine project. Within the Client project, there is a class that derives from the Game
class. The entry point will instanciate the class within the Client on the heap and run the setup functions.
There are 3 functions that must be implemented, void OnStart()
this is called when the program starts, void OnUpdate()
this is called each frame. void OnExit()
this is called at the end of the program.
Boilerplate Application Class:
#include "Engine.h"
class Application : public Engine::Game
{
public:
Application() { }
~Application() { }
void OnStart()
{
}
void OnUpdate()
{
}
void OnExit()
{
}
};
/// <summary>
/// Gives the engine a main class derived from Engine::Game
/// </summary>
/// <returns>Engine::Game*</returns>
Engine::Game* Engine::CreateGame()
{
return new Application();
}
You are able to create events on the client or use events created in the Engine by subscribing a function to the event. Subscribing to an event will call the function when the event is dispatched.
Event Name | Description |
---|---|
OnApplicationStart | This event is dispatched when the application starts |
OnApplicationExit | This event is dispatched when the application ends |
OnWindowClose | This event is dispatched when you tell the window to close (Using ant+F4 or the exit cross) |
OnWindowResize | This event is dispatched when the window is resized |
OnWindowMove | This event is dispatched when the window is moved |
MouseDown | This event is dispatched when a mouse button is pressed |
MouseUp | This event is dispatched when a mouse button is released |
MouseMove | This event is dispatched when the mouse is moved |
MouseScroll | This event is dispatched when the mouse scroll wheel is moved |
KeyDown | This event is dispatched when a key is pressed |
KeyUp | This event is dispatched when a key is released |
You can access the data via the corresponding classes on the event.
Using an Engine Event Example:
// Printing out the keycode when a key is pressed
void LogKeyPress(Engine::Event& e)
{
std::cout << "KEY PRESSED: " << (int)e.KeyEvent->KeyCode << std::endl;
}
void OnStart()
{
// Subscribing event using the GetEventFromName function
Engine::EventDispatcher::GetEventFromName("KeyDown") += EventFunc(LogKeyPress);
}
Using a Client Event Example:
// Printing out the keycode when a key is pressed
void Test(Engine::Event& e)
{
std::cout << "Hello World" << std::endl;
}
void OnStart()
{
// Creates the event called "TestEvent" and returns the event ID
size_t eventID = Engine::EventDispatcher::GenerateEvent("TestEvent",Engine::EventType::UserDefined);
// Uses the GetEventFromID function to get the event and subscribes
Engine::EventDispatcher::GetEventFromID(eventID) += EventFunc(Test);
}
There are two ways to get user input, by using the event system (described above this) or using the Input class. However the event system will take more to set up.
Both the keyboard and mouse make use of 3 types of functions:
IsDown()
this will check if the key/button is down that frameIsReleasedThisFrame()
this will check if the key/button was released that frameIsUp()
this will check if the key/button is not pressed that frame.
Keyboard Implementations:
IsKeyDown(Keys _keyCode)
IsKeyReleasedThisFrame(Keys _keyCode)
IsKeyUp(Keys _keyCode)
Mouse Implementations:
IsMouseButtonDown(MouseButton _button)
IsMouseButtonReleasedThisFrame(MouseButton _button)
IsMouseButtonUp(MouseButton _button)
Examples:
if (Engine::Input::IsKeyDown(Engine::Keys::KEY_A))
std::cout << "A was pressed" << std::endl;
if (Engine::Input::IsKeyReleasedThisFrame(Engine::Keys::KEY_B))
std::cout << "B was released" << std::endl;
if (Engine::Input::IsKeyUp(Engine::Keys::KEY_C))
std::cout << "C is not pressed" << std::endl;
if (Engine::Input::IsMouseButtonDown(Engine::MouseButton::MOUSE_BUTTON_LEFT))
std::cout << "Left button is down" << std::endl;
if (Engine::Input::IsMouseButtonReleasedThisFrame(Engine::MouseButton::MOUSE_BUTTON_RIGHT))
std::cout << "Right button was released" << std::endl;
if (Engine::Input::IsMouseButtonUp(Engine::MouseButton::MOUSE_BUTTON_LEFT))
std::cout << "Left button is not down" << std::endl;
The engine has a Logger
class, this will allow you to print messages to the console in colour. You are also able to use C++ predefined functions to print to the console.
Function Name | Color |
---|---|
Trace() |
White |
Debug() |
Green |
Info() |
Grey |
Warn() |
Yellow |
Error() |
Red |
Fatal() |
Blue |
Example:
void OnStart()
{
Engine::Logger::Debug("Hello World");
}
Use the Logger
functions the same as printf()
, you can find references here.
You are able to create entities(parents) with components(children). You can access entities with the ECS
namespace and components with the Components
namespace.
To create an entity, give the AddEntity
function a tag to assign with the entity. The function will return a pointer that points to the entity stored in the engine.
Engine::Entity* e = Engine::ECS::AddEntity("player");
To delete an entity, use the Destroy
function. Note that the entity will be destroyed at the start of the next frame.
e->Destroy();
To add a component to an entity, you must first have a pointer to the entity. Simply access the class on the entity and assign it a new component.
e->transform = new Engine::Components::Transform(Types::Vec2f(5.0f, 5.0f), Types::Vec2f(100.0f, 100.0f), Types::Vec2f(0.2f, 0.2f), 0);
To delete a component, assign the entity's component class as a nullptr
e->transform = nullptr;
Component | Description | Args |
---|---|---|
Transform | Stores position, size, velocity and rotation of an entity. | Vec2f pos, Vec2f size, Vec2f vel, float rotation |
BoxCollider | Allows 2D box collision. Requires Transform. | Vec2f size |
RectMesh | Renders a square or rectangle shape. Requires Transform. | int outlineThickness, RGBA outlineColour |
CircleMesh | Renders a circle shape. Requires Transform. Note the size of the circle is the radius and determined by the width(x value) of Transform.m_Size | int outlineThickness, RGBA outlineColour |
ConvexMesh | Renders a shape defined by a list of Type::Vec2f points. Requires Transform. | int outlineThickness, RGBA outlineColour, std::vectorTypes::Vec2f points |
Colour | Tells the renderer what colour to assign to the entity. Requires Mesh. | RGBA colour |
Texture | Tells the renderer what texture to assign to the entity Requires Mesh | STRING filePath |
Renderer | Renderers an entity to the screen. Requires Mesh, Colour/Texture, Transform | sf::Mesh object, int order, RenderType renderType, RenderShape shape |
Parent | Sets the entity's parent object, use offset Transform to position away from parent. Requires Parent.Transform, Transform | size_t parentID, Transform* parentTransform, Transform offsetTransform |
Text | Displays a string of text, note that a SFML font must be loaded into a variable. Requires Transform | STRING text, int fontSize, sf::Font* font |
Button | Fires a callback when a button is pressed, *note you must pair the button with text, shape or texture for the button to be visible. | std::function<void()> callback, MouseButton button, Vec2f pos, Vec2f size |
Checkbox | Works the same way as a button however it stores a value with is switched when the button is pressed. | MouseButton button, Vec2f pos, Vec2f size |
TextInput | When focused on, it will store all characters typed in a STRING array, note that a SFML font must be loaded into a variable. Requires Transform | STRING placeHolder, int fontSize, sf::Font* font, Vec2f pos, Vec2f size |
It is important to apply the Engine::Scene
base class to any class you want to use as a scene.
class TestScene : public Engine::Scene