-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Core Engine Architecture and Game Object Systems
Overview
This project implements a lightweight 2D game framework written in C++ using OpenGL and GLFW. The architecture separates high-level gameplay objects (player, characters, tiles, camera) from low-level engine utilities (math, input, rendering). This separation keeps gameplay logic simple while allowing the engine systems to remain reusable and modular.
The goal of this structure is to provide:
- Simple game object definitions
- Lightweight math utilities
- Direct OpenGL-based rendering
- Flexible input handling
- A minimal but expandable 2D engine core
The following sections describe both the high-level gameplay structures and the low-level systems that support them.
High-Level Game Systems
The high-level layer defines gameplay entities and world structures. These structures store state and properties used by the gameplay logic and rendering system.
Player
The Player struct represents the main controllable entity in the game world.
struct Player {
vec2 pos = vec2(0.0);
vec2 dim = vec2(45.0);
vec2 vel = vec2(0.0);
GLuint texture = 0;
float coins = 0.0;
float health = 100.0;
float cooldown = 0.0;
float speed = 0.2;
};Responsibilities
The player stores fundamental gameplay state including:
- Position (
pos) — location in world space - Dimensions (
dim) — size used for rendering and collision - Velocity (
vel) — movement speed and direction - Texture (
texture) — OpenGL texture used for rendering - Coins (
coins) — currency or collectible tracking - Health (
health) — current player health value - Cooldown (
cooldown) — ability cooldown timer - Speed (
speed) — base movement speed
This struct is intentionally minimal and functions primarily as a data container. Game logic such as movement, combat, and interaction would operate on this data.
Character (NPC / Enemy)
The Character struct represents non-player entities such as enemies or NPCs.
struct Character {
vec2 pos = vec2(0.0);
vec2 dim = vec2(45.0);
float health = 100.0;
GLuint texture = 0;
float cooldown = 0.0;
float speed = 0.4;
vec2 target;
std::vector<vec2> positions = {};
};Responsibilities
Characters maintain their own gameplay properties including:
- Position and dimensions
- Health
- Movement speed
- Target position
- Pathfinding targets
The positions vector represents a sequence of positions the character may follow, which can be used for:
- pathfinding
- patrol routes
- AI navigation
- queued movement commands
The target variable represents the immediate movement target.
This design allows for simple AI behaviors such as:
- following the player
- navigating toward objectives
- walking along predefined paths
Tile System
The Tile struct defines a single tile in the world.
struct Tile {
vec2 pos = vec2(0.0);
int id = 0;
int layer = 0;
};Responsibilities
Tiles define the static world structure.
Fields include:
- Position (
pos) — location of the tile in world space - ID (
id) — tile type identifier - Layer (
layer) — rendering or collision layer
This system allows the game to support:
- multiple tile types
- layered tilemaps
- different rendering priorities
Camera
The Camera struct defines the viewing system used to render the game world.
struct Camera {
vec2 pos = vec2(0.0);
float zoom = 5.0;
vec2 vel = vec2(0.0);
vec2 target;
};Responsibilities
The camera tracks and controls how the world is viewed.
Key properties include:
- Position (
pos) — camera location in world space - Zoom (
zoom) — scaling factor for world rendering - Velocity (
vel) — used for smooth camera motion - Target (
target) — position the camera moves toward
This structure enables features like:
- camera follow
- smooth camera interpolation
- dynamic zooming
- cinematic movement
Low-Level Engine Systems
The low-level systems provide core functionality used throughout the engine, including math utilities, input handling, and rendering.
Vector Math (vec2)
The vec2 class provides a lightweight 2D vector math utility used throughout the engine.
class vec2 {
public:
float x;
float y;Vectors are used for:
- positions
- velocities
- sizes
- directions
- camera movement
Constructors
The class supports several constructors:
vec2(float x1, float y1)
vec2(float n)
vec2()This allows flexible initialization such as:
vec2 a = vec2(10, 20);
vec2 b = vec2(5);
vec2 c;Vector Arithmetic
The class overloads operators to support intuitive vector math.
Examples include:
+
-
*
/
Example usage:
vec2 a = vec2(10, 5);
vec2 b = vec2(2, 3);
vec2 result = a + b;Scalar operations are also supported:
vec2 scaled = a * 2.0;This makes vector operations concise and readable throughout the codebase.
Input System
The input system wraps GLFW input handling into simple utility namespaces.
Keyboard Input
namespace Input {
void Init(GLFWwindow* window);
void Update();
bool IsDown(int key);
bool IsPressed(int key);
bool IsReleased(int key);
bool IsDown(const std::string& name);
bool IsPressed(const std::string& name);
bool IsReleased(const std::string& name);
}Features
The keyboard input system tracks:
- current key states
- pressed events
- released events
This allows logic such as:
Input::IsDown(KEY_W)
Input::IsPressed(KEY_SPACE)
String-based inputs also allow customizable key bindings.
Mouse Input
namespace Mouse {
void Init(GLFWwindow* window);
void Update();
double X();
double Y();
double DeltaX();
double DeltaY();
bool IsDown(int button);
bool IsPressed(int button);
bool IsReleased(int button);
double ScrollX();
double ScrollY();
void Lock();
void Unlock();
bool IsLocked();
}Features
The mouse system provides:
- cursor position
- movement delta
- button states
- scroll input
- cursor locking
Cursor locking allows features such as:
- first-person camera control
- preventing the cursor from leaving the window
Rendering System
Rendering is handled through the Image module, which provides simplified OpenGL drawing functions.
namespace Image {
bool Init();
GLuint Load(const char* path);
void Draw(GLuint texture, vec2 pos, float size, float angle = 0.0);
void Draw(GLuint texture, vec2 pos, vec2 size, float angle = 0.0);
void DrawRect(vec2 pos, vec2 size, float r, float g, float b, float angle = 0.0);
}Texture Loading
Textures are loaded using:
GLuint texture = Image::Load("assets/player.png");
This returns an OpenGL texture ID that can be reused for rendering.
Sprite Rendering
Sprites can be drawn using either:
Image::Draw(texture, pos, size);
or
Image::Draw(texture, pos, vec2(width, height));
Optional rotation is supported via the angle parameter.
Primitive Rendering
The renderer also supports drawing simple rectangles:
Image::DrawRect(pos, size, r, g, b);
This is useful for:
- debugging
- UI elements
- collision visualization
Architectural Design Philosophy
The architecture of this project focuses on simplicity and flexibility.
Key design principles include:
Data-Oriented Gameplay Objects
Game entities such as Player and Character are simple data containers. Game systems operate on this data rather than embedding logic directly inside the structs.
Lightweight Engine Core
The engine avoids heavy abstractions and instead provides:
- minimal math utilities
- simple rendering wrappers
- straightforward input handling