cangine
Loading...
Searching...
No Matches
🎮 cangine

A lightweight 2D game engine written in C++, built on top of OpenGL. Designed for simplicity — create windows, render shapes and text, play audio, build UIs, handle input, and compose game objects with a clean component system.


✨ Features

  • Component-based GameObject system — Attach and detach components (Transform, QuadRendererComponent, TextRendererComponent, and custom ones) at runtime
  • Built-in UI objects — Label, Button, InputField and Panel game objects with click/return event handlers, placeholders and styling
  • Camera — Movable Camera object; attach custom components to control panning and view
  • 2D Rendering — Render colored/textured quads, triangles and text out of the box
  • Audio playback — Load and control sounds via the AudioManager (play, pause, stop, seek, volume, looping) powered by miniaudio
  • Collision detection — Rectangle overlap (SAT, rotation/scale aware) and point-in-rect checks
  • Input handling — Keyboard (getKey, getKeyDown, getKeyUp) and mouse events (click, release, move, scroll, enter)
  • FPS & delta time — getFPS(), getInstantFPS(), getDeltaTime() on the Window object
  • Shader system — Load and manage GLSL shaders with minimal boilerplate
  • Texture loading — Load PNG/JPEG images via stb_image
  • Simple app abstraction — Subclass CangineApp and override onFirstFrame() / onFrame(dt) for a clean game loop, with instantiateObject<T>() for spawning objects

📦 Dependencies

Library Purpose
GLFW Window creation & input
GLAD OpenGL function loader
FreeType Font rendering
stb_image Texture/image loading
miniaudio Audio playback
zlib Compression (FreeType dependency)

All dependencies are bundled under cangine/external/.


🔧 Building

Requirements

  • CMake ≥ 3.10
  • A C++17-compatible compiler (GCC, Clang, or MSVC)

Build the engine

make engine

Build in debug mode

make engined

Build and run an example

make example EXAMPLE=simplehelloworld
make example EXAMPLE=ui
make example EXAMPLE=audio
make example EXAMPLE=sandbox

Debug an example

make exampled EXAMPLE=sandbox

Clean build files

make clean

Makefile variables

Variable Default Description
EXAMPLE sandbox Example to build/run
BUILDDIR build CMake output directory
BUILDEXAMPLES OFF Build example projects
EXPORTCOMPILECOMMANDS ON Export compile_commands.json

🚀 Quick Start

Install dependencies

sudo apt install libglfw3-dev libfreetype-dev libepoxy-dev libfontconfig-dev

Clone and build

git clone https://github.com/huseyinhealth/cangine.git
cd cangine
make engine

The simplest way to use cangine is to subclass CangineApp and spawn objects with instantiateObject<T>():

using namespace cangine;
class MyApp : public CangineApp {
public:
MyApp() : CangineApp(800, 600, "My Game") {}
void onFirstFrame() override {
// Called once before the game loop starts.
// Spawn a text label
Label* hello = instantiateObject<Label>("Hello, world!");
// Center it on screen (Z index 80+ is the UI layer)
Transform::posFor(hello, Vector3(hello->getSize() / -2, 80));
}
void onFrame(double dt) override {
// Called every frame with delta time in seconds.
}
};
int main() {
MyApp().run();
return 0;
}
Public entry point for the cangine engine.
Base application class that owns the window and main loop.
Definition cangine.hpp:43
Convenience game object that displays a line of text.
Definition label_object.hpp:23
Vector2 getSize()
Returns the rendered size of the text.
Definition label_object.cpp:14
Definition audiomanager.cpp:4
Three-component floating-point vector.
Definition vector3.hpp:13

🧩 Architecture

cangine/
├── engine/
│ ├── GameObject # Base entity — holds a list of IComponents
│ ├── ObjectManager # Manages all active GameObjects
│ ├── components/
│ │ ├── IComponent # Base class for all components
│ │ ├── Transform # Position, rotation, scale (+ posFor helper)
│ │ ├── CameraComponent # View/camera transform
│ │ ├── QuadRendererComponent # Renders a colored/textured rectangle
│ │ ├── TextRendererComponent # Renders a string with FreeType
│ │ └── ui/
│ │ ├── ButtonComponent
│ │ ├── InputFieldComponent
│ │ └── PanelComponent
│ └── gameobjects/
│ ├── CameraObject # Camera entity (auto-attaches CameraComponent)
│ └── ui/
│ ├── Label # Text label object
│ ├── Button # Clickable button object
│ ├── InputField # Text input object
│ └── Panel # Container/background panel object
├── audio/
│ ├── AudioManager # Loads and controls sound playback (miniaudio)
│ └── Sound # Sound source description (path, name, volume)
├── platform/
│ ├── Window # GLFW window wrapper, frame loop, FPS
│ ├── Input # Static keyboard query (getKey / getKeyDown / getKeyUp)
│ ├── Key # Key enum
│ └── Mouse # Mouse event types and data
├── render/
│ ├── Shader # GLSL shader loader/linker
│ ├── Texture # stb_image texture wrapper
│ ├── VAO / VBO / EBO # OpenGL buffer object wrappers
│ └── renderer/
│ ├── RenderSystem # Top-level renderer, drives quad/text/primitive rendering
│ ├── QuadRenderer # Batches and draws textured quads
│ ├── TextRenderer # Draws glyphs via FreeType
│ └── PrimitiveRenderer # Draws untextured primitives
├── shapes/
│ ├── Rectangle # Shape + overlap / containsPoint helpers
│ ├── Triangle # Triangle shape definition
│ └── Vertex # Vertex struct (position, color, UV)
├── types/
│ ├── Vector2 / Vector3 # Math types
│ └── Dimensions # Width/height helper
└── util/
├── Color # RGBA color (0–255 inputs, normalized internally)
└── Position # Screen-to-clip-space normalization helpers

🎨 Core API Reference

CangineApp

class MyApp : public CangineApp {
MyApp() : CangineApp(800, 600, "Title") {}
void onFirstFrame() override { /* setup */ }
void onFrame(double dt) override { /* per-frame logic */ }
};
// Spawn any GameObject-derived type:
GameObject* obj = instantiateObject<GameObject>();
Label* lbl = instantiateObject<Label>("text");
// Audio manager is available as a member:
audioMan->add("./music.mp3", "music");
audioMan->play("music", true);
Base class for everything that lives in the scene.
Definition game_object.hpp:20

Color

Color red(255, 0, 0);
Color semiTransparent(0, 128, 255, 0.5f); // alpha is 0.0–1.0
// Built-in presets
Colors::BLACK, Colors::WHITE, Colors::GRAY, Colors::DARK_GRAY,
Colors::RED, Colors::GREEN, Colors::BLUE
RGBA color stored as normalized floating-point components.
Definition color.hpp:12

Transform

Transform* t = gameObject->getComponent<Transform>();
t->setPosition(Vector3(x, y, z));
t->setRotation(Vector3(0, 0, angle)); // Z-axis rotation for 2D
t->setScale(Vector3(1, 1, 1));
// Convenience: position a whole object in one call
Transform::posFor(gameObject, Vector3(x, y, z));
Component holding an object's position, rotation and scale.
Definition transform_component.hpp:17
void setRotation(Vector3 rotationIn)
Sets the rotation.
Definition transform_component.cpp:22
void setScale(Vector3 scaleIn)
Sets the scale.
Definition transform_component.cpp:30
void setPosition(Vector3 positionIn)
Sets the position.
Definition transform_component.cpp:14

‍Layering: the Z component acts as a layer. A Z index of 80 and above is treated as the UI layer (rendered on top of world objects).

UI objects

// Label
Label* lbl = instantiateObject<Label>("Score: 0");
lbl->setText("Score: 42");
lbl->setColor(Colors::GREEN);
Vector2 size = lbl->getSize();
// Button
Button* btn = instantiateObject<Button>("Click me!");
btn->registerOnClickEvent([&](const MouseEvent* e) {
// handle click
});
// InputField
InputField* field = instantiateObject<InputField>();
field->setPlaceholder("Name?");
field->setOnReturn([&](std::string value) {
// handle submitted text
});
std::string text = field->getContent();
Clickable button game object with a text label.
Definition button_object.hpp:19
void registerOnClickEvent(std::function< void(const MouseEvent *)> handler)
Registers a callback invoked when the button is clicked.
Definition button_object.hpp:53
Editable text-field game object.
Definition inputfield_object.hpp:16
void setPlaceholder(std::string placeholderIn)
Sets the placeholder text shown when empty.
Definition inputfield_object.hpp:35
void setOnReturn(std::function< void(std::string)> functionIn)
Sets the callback invoked when Enter is pressed.
Definition inputfield_object.hpp:93
const std::string & getContent() const
Returns the current content.
Definition inputfield_object.hpp:104
void setText(std::string text)
Sets the displayed text.
Definition label_object.cpp:22
void setColor(Color colorIn)
Sets the text color.
Definition label_object.cpp:30
A mouse event carrying its type, button, position and scroll offset.
Definition mouse.hpp:37
Two-component floating-point vector.
Definition vector2.hpp:13

QuadRendererComponent

auto* qrc = new QuadRendererComponent();
gameObject->appendComponent<QuadRendererComponent>(qrc);
qrc->setRectWidth(128);
qrc->setRectHeight(64);
qrc->setRectColor(Colors::WHITE);
// Optional: set a texture
Texture tex("path/to/image.png");
qrc->setTexture(tex);
Component that draws its game object as a textured rectangle.
Definition quadrenderer_component.hpp:22
virtual void setRectWidth(int widthIn)
Sets the rectangle width.
Definition quadrenderer_component.cpp:26
Wraps an OpenGL texture object.
Definition texture.hpp:12

TextRendererComponent

auto* trc = new TextRendererComponent();
gameObject->appendComponent<TextRendererComponent>(trc);
trc->text = "Score: 42";
trc->color = Colors::GREEN;
trc->backgroundColor = Color(0, 0, 0, 0.5f);
Vector2 size = trc->getSize(); // text bounding box
Component that renders a text string, optionally over a background.
Definition textrenderer_component.hpp:18
std::string text
The string to render.
Definition textrenderer_component.hpp:31

Camera

Camera* camera = new Camera(); // CameraComponent is attached automatically
camera->appendComponent<CustomCameraController>(controller);
Game object representing the scene camera.
Definition camera_object.hpp:11
void appendComponent(T *component)
Attaches an existing component to this object.
Definition game_object.hpp:58

Audio

audioMan->add("./sound.mp3", "sfx");
audioMan->play("sfx", /*loop=*/false);
audioMan->pause("sfx");
audioMan->resume("sfx");
audioMan->stop("sfx");
audioMan->setVolume("sfx", 0.5f); // 0.0–1.0
audioMan->seek("sfx", 10.0f); // seconds
float pos = audioMan->getPosition("sfx");
float dur = audioMan->getDuration("sfx");
bool playing = audioMan->isPlaying("sfx");

Input

// Held down
if (Input::getKey(Key::A)) { ... }
// Just pressed this frame
if (Input::getKeyDown(Key::Space)) { ... }
// Just released this frame
if (Input::getKeyUp(Key::Escape)) { ... }

Mouse events

Override onMouseEvent in a custom IComponent:

void onMouseEvent(const MouseEvent* e) override {
if (e->event == MouseEventType::MouseClick &&
e->button == MouseButton::Right) {
// e->pos contains the cursor position
}
}
MouseEventType event
The kind of event.
Definition mouse.hpp:38
MouseButton button
Button involved; only meaningful for click/release events.
Definition mouse.hpp:39

Rectangle collision

// Point-in-rect (transform-aware: respects rotation & scale)
bool hit = Rectangle::containsPoint(rect, transform, mousePos);
// Rect-rect overlap (SAT-based, transform-aware)
bool colliding = Rectangle::overlaps(rectA, transformA, rectB, transformB);

📁 Project Structure

cangine/
├── cangine/ # Engine library source
│ ├── src/
│ ├── external/ # Bundled third-party libraries
│ ├── shaders/ # default.vert / default.frag
│ └── test/
├── examples/
│ ├── simplehelloworld/ # Minimal CangineApp subclass example
│ ├── helloworld/ # Manual engine setup example
│ ├── ui/ # Buttons and labels
│ ├── inputfield/ # Text input + submit handling
│ ├── panel/ # Panel/container UI
│ ├── audio/ # Audio playback controls
│ ├── camera/ # Draggable camera
│ ├── aimtrainer/ # Aim trainer mini-game
│ ├── survival/ # Survival mini-game
│ └── sandbox/ # Full-featured test/demo scene
├── CMakeLists.txt
└── Makefile # Convenience wrapper around CMake

📄 License

See LICENSE.txt for details.