Skip to content

Core Engine Architecture and Game Object Systems #4

@Frogpants

Description

@Frogpants

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions