A modern C++20 game engine built with Entity-Component-System (ECS) architecture, featuring 2D/3D graphics, physics simulation, audio management, and comprehensive asset management systems.
- Features
- Architecture
- Getting Started
- Core Systems
- Components
- Systems
- Asset Management
- Examples
- API Reference
- Contributing
- License
- Entity-Component-System (ECS) Architecture: High-performance data-oriented design
- OpenGL Rendering: Modern OpenGL-based 2D/3D graphics rendering
- Physics Simulation: Both 2D and 3D physics with collision detection
- Audio System: SoLoud-based audio management with 3D spatial audio
- Input Handling: Cross-platform input management
- Event System: Type-safe event handling and dispatching
- Asset Management: Texture, model, and audio asset management
- Text Rendering: TrueType font rendering system
- Tilemap Support: 2D tilemap rendering for level design
- Windows (Primary)
- OpenGL 3.3+ compatible systems
The Achoium Engine follows a strict Entity-Component-System architecture:
- World: Central ECS manager handling entities, components, and systems
- Entity: Lightweight ID representing game objects
- Components: Data containers (Transform, Sprite, RigidBody, etc.)
- Systems: Logic processors that operate on components
- Pre-Update Systems: Time updates, input processing
- Update Systems: Game logic, physics simulation
- Post-Update Systems: Rendering, audio updates
- Visual Studio 2022 or compatible C++20 compiler
- OpenGL 3.3+ compatible graphics driver
- Windows SDK
#include "Achoium.h"
using namespace ac;
int main()
{
// Create and initialize the world
World world;
InitEngine(world);
// Create a camera entity
Entity camera = world.CreateEntity("MainCamera");
world.Add<Camera>(camera, Camera{});
world.Add<AudioListener>(camera, AudioListener());
world.Add<Transform>(camera, Transform());
// Main game loop
while (true)
{
world.Update();
// Handle window updates and rendering
auto& window = world.GetResourse<WinWindow>();
auto& renderer = world.GetResourse<OpenGLRenderer>();
window.OnUpdate();
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
return 0;
}The World class is the heart of the ECS system:
// Entity management
Entity entity = world.CreateEntity("MyEntity");
world.DeleteEntity(entity);
// Component management
world.Add<Transform>(entity, Transform({0, 0, 0}));
world.Delete<Transform>(entity);
Transform& transform = world.Get<Transform>(entity);
// System management
world.AddUpdateSystem(MySystem, 0); // Priority 0-9Resources are singleton-like objects managed by the World:
// Access engine resources
TextureManager& textures = world.GetResourse<TextureManager>();
AudioManager& audio = world.GetResourse<AudioManager>();
InputManager& input = world.GetResourse<InputManager>();
EventManager& events = world.GetResourse<EventManager>();Handles position, rotation, and scale in 3D space:
Transform transform;
transform.position = {100.0f, 200.0f, 0.0f};
transform.rotation = glm::quat(glm::vec3(0, 0, glm::radians(45.0f)));
transform.scale = {2.0f, 2.0f, 1.0f};2D graphics rendering:
// Create sprite from texture
Sprite sprite = Sprite::Create("MyTexture", world.GetResourse<TextureManager>());
world.Add<Sprite>(entity, std::move(sprite));// Audio source for playing sounds
AudioSource source;
source.audioID = audioManager.GetAudioID("MySound");
world.Add<AudioSource>(entity, source);
// Audio listener for receiving audio (typically on camera)
world.Add<AudioListener>(camera, AudioListener());// Rigid body for physics simulation
RigidBody rigidBody;
rigidBody.mass = 1.0f;
rigidBody.useGravity = true;
world.Add<RigidBody>(entity, rigidBody);
// Box collider
BoxCollider boxCollider;
boxCollider.size = {1.0f, 1.0f, 1.0f};
world.Add<BoxCollider>(entity, boxCollider);// 2D rigid body
RigidBody2D rigidBody2D;
rigidBody2D.mass = 1.0f;
world.Add<RigidBody2D>(entity, rigidBody2D);
// Circle collider 2D
CircleCollider2D circleCollider;
circleCollider.radius = 0.5f;
world.Add<CircleCollider2D>(entity, circleCollider);Text rendering with customizable fonts and colors:
Text text("Hello World", 48.0f, Text::PivotMiddleCenter, {1.0f, 1.0f, 1.0f});
world.Add<Text>(entity, text);void MyCustomSystem(World& world)
{
// Access resources
Time& time = world.GetResourse<Time>();
InputManager& input = world.GetResourse<InputManager>();
// Process entities with specific components
auto view = world.View<Transform, Sprite>();
for (Entity entity : view)
{
Transform& transform = world.Get<Transform>(entity);
Sprite& sprite = world.Get<Sprite>(entity);
// System logic here
transform.position.x += 100.0f * time.Delta();
}
}
// Register the system
world.AddUpdateSystem(MyCustomSystem, 1);- Physics Systems: Handle 2D/3D physics simulation and collision detection
- Render Systems: Process sprite, text, and tilemap rendering
- Audio Systems: Update 3D audio positioning and playback
- Input Systems: Process keyboard and mouse input
TextureManager& textureManager = world.GetResourse<TextureManager>();
// Load textures
textureManager.AddTexture("Player", "Assets/Images/player.png");
textureManager.AddTexture("Background", "Assets/Images/bg.jpg");
// Use textures
uint32_t textureID = textureManager.GetTextureID("Player");
Sprite sprite = Sprite::Create("Player", textureManager);AudioManager& audioManager = world.GetResourse<AudioManager>();
// Register audio files
AudioID soundID = audioManager.RegisterAudio("Jump", "Assets/Audio/jump.wav");
AudioID musicID = audioManager.RegisterAudio("BGM", "Assets/Audio/background.ogg");
// Play audio
audioManager.Play(soundID);
audioManager.PlayByName("BGM", true, 0.7f); // Loop with 70% volume// Create player entity
Entity player = world.CreateEntity("Player");
// Add visual representation
Sprite playerSprite = Sprite::Create("PlayerTexture", world.GetResourse<TextureManager>());
world.Add<Sprite>(player, std::move(playerSprite));
// Add transform for positioning
world.Add<Transform>(player, Transform({400, 300, 0}));
// Add custom movement component
struct PlayerMovement {
float speed = 200.0f;
int leftKey = AC_KEY_A;
int rightKey = AC_KEY_D;
int upKey = AC_KEY_W;
int downKey = AC_KEY_S;
};
world.Add<PlayerMovement>(player, PlayerMovement{});void PlayerMovementSystem(World& world)
{
InputManager& input = world.GetResourse<InputManager>();
Time& time = world.GetResourse<Time>();
auto players = world.View<Transform, PlayerMovement>();
for (Entity entity : players)
{
Transform& transform = world.Get<Transform>(entity);
PlayerMovement& movement = world.Get<PlayerMovement>(entity);
glm::vec3 velocity = {0, 0, 0};
if (input.IsKeyPressed(movement.leftKey))
velocity.x -= movement.speed;
if (input.IsKeyPressed(movement.rightKey))
velocity.x += movement.speed;
if (input.IsKeyPressed(movement.upKey))
velocity.y += movement.speed;
if (input.IsKeyPressed(movement.downKey))
velocity.y -= movement.speed;
transform.position += velocity * time.Delta();
}
}
// Register the system
world.AddUpdateSystem(PlayerMovementSystem, 1);// Define custom events
struct PlayerDeathEvent {
Entity playerEntity;
glm::vec3 deathPosition;
};
// Event handler
bool OnPlayerDeath(const PlayerDeathEvent& event)
{
ACMSG("Player died at position: " << event.deathPosition.x << ", " << event.deathPosition.y);
// Handle player death logic
return true;
}
// Register event listener
EventManager& eventManager = world.GetResourse<EventManager>();
eventManager.AddListener<PlayerDeathEvent>(OnPlayerDeath);
// Trigger event
PlayerDeathEvent deathEvent{playerEntity, transform.position};
eventManager.Invoke(deathEvent, AllowToken<PlayerDeathEvent>());Entity CreateEntity(string_view tag = "")- Create new entityvoid DeleteEntity(Entity id)- Remove entity and all componentsvoid Add<T>(Entity id, T&& component)- Add component to entityvoid Delete<T>(Entity id)- Remove component from entityT& Get<T>(Entity id)- Get component referencebool Has<T>(Entity id)- Check if entity has componentSimpleView<Components...> View<Components...>()- Create component viewT& GetResourse<T>()- Access engine resource
glm::vec3 position- World positionglm::quat rotation- Rotation quaternionglm::vec3 scale- Scale factors
uint32_t textureID- Texture identifieruint32_t width, height- Sprite dimensionsglm::vec4 color- Tint colorstatic Sprite Create(string name, TextureManager& manager)- Factory method
bool IsKeyPressed(int keycode)- Check if key is currently heldbool IsKeyDown(int keycode)- Check if key was just pressedbool IsKeyUp(int keycode)- Check if key was just releasedbool IsMouseButtonPressed(int button)- Check mouse button state
Common key codes defined in the engine:
AC_KEY_W,AC_KEY_A,AC_KEY_S,AC_KEY_D- WASD keysAC_KEY_SPACE- Space barAC_KEY_ENTER- Enter keyAC_KEY_UP,AC_KEY_DOWN,AC_KEY_LEFT,AC_KEY_RIGHT- Arrow keys
- Open the solution in Visual Studio 2022
- Set the SandBox project as startup project
- Build the solution (F7)
- Run the application (F5)
GameEngine/
|-- Achoium/ # Core engine code
| |-- Core/ # ECS implementation
| |-- Event/ # Event system
| |-- Render/ # Rendering systems
| |-- Input/ # Input management
| |-- AssetManagement/ # Asset managers
| +-- EngineSystems/ # Built-in systems
|-- SandBox/ # Example application
| |-- Assets/ # Game assets
| +-- GameComponents/ # Custom components
+-- Dependency/ # Third-party libraries
When contributing to the Achoium Engine:
- Follow the existing code style and conventions
- Add appropriate documentation for new features
- Test thoroughly before submitting changes
- Update this README if adding new major features
- Ensure your code follows C++20 standards
- All contributions should be made under the terms of the MIT License
This project is licensed under the MIT License - see the LICENSE file for details.
The Achoium Engine uses several third-party libraries, each with their own licenses:
- OpenGL: Industry standard graphics API
- SoLoud: Audio engine (zlib/libpng license)
- GLM: OpenGL Mathematics library (MIT License)
- FreeType: Font rendering library (FreeType License)
- stb_image: Image loading library (Public Domain)
Please refer to the respective library documentation and licenses for more information.
This documentation covers the core features of the Achoium Game Engine. For more detailed information about specific systems or advanced usage, please refer to the source code documentation and examples in the SandBox project.