Cross-platform game engine written with c++.
Install depencies:
#install emsdk for web
./install_emsdk.sh
#other dependencies
sudo apt update &&
sudo apt-get -y install cmake &&
sudo apt-get -y install g++-mingw-w64 &&
sudo apt-get -y install freeglut3-dev &&
sudo apt-get -y install xorg-dev libgl1-mesa-dev
To compile and run unit test of the engine run the setup.py script
python3 setup.py
Demos are located in the /demos folder. You can run them with this command:
./run_demo.sh <demo-name> <platform-name>
Example:
Running Example2D demo:
./run_demo.sh Example2D linux
The ASEngine includes a powerful AudioModule that provides features for audio playback, control, and manipulation. This module only supports .wav formats.
To integrate the AudioModule into your project, you need to add it through the registry function:
AudioModule::Init();
Check out the AudioExample demo available in the demos folder.
The AudioSource class represents an audio asset that can be loaded and played using the AudioEngine.
You can create it as a json file:
{
"Path": "path/to/audio.wav"
}
The AudioOutput class is responsible for managing the output settings of the audio, such as volume.
AudioOutput& audioOutput = AudioEngine::GetInstance().GetAudioOuput("Default");
audioOutput.SetVolume(0.5f); // Set volume to 50%
The AudioEngine class serves as the central hub for managing and playing audio within an application. It provides functionality to load, control, and play audio resources.
auto& audioEngine = AudioEngine::GetInstance();
ResourceRef<AudioSource> audioSource = AudioSource::GetResourceClass().Load("path/to/audio.json");
ResourceRef<AudioPlayer> player = audioEngine.Play("Default", audioSource, 1.0f, true);
if (audioEngine.IsPlaying(player)) {
// Do something while the audio is playing
}
audioEngine.Stop(player);
ASEngine provides a comprehensive 2D module that includes features for transform hierarchy, sprite management, text rendering, and various drawing functions. This module enhances performance by using batching techniques to minimize the number of draw calls.
To integrate the 2D module into your project, you need to add it through the registry function:
Module2D::Init();
You can check out the Example2D demo in the demos folder to test this module.
Renderer2D is a singleton system responsible for rendering 2D graphics. It offers a range of functionalities:
They are loaded from the renderer2d.settings.json files located in the assets folder.
Example:
{
"Layers": ["Default", "PixelViewport"],
"DefaultMaterial": "materials/default2D.material.json"
}
Rendering operations should be performed within the RenderSignal event to ensure that all custom rendering logic is executed correctly and efficiently. You can get the render signal with the GetOnRendere2D() method:
// add on render
m_OnRender2DConnectionID = Renderer2D::GetInstance().GetOnRender2D().Connect([this](Renderer2D& renderer2D)
{
OnRender2D(renderer2D);
});
The Layer2D class provides a suite of methods for rendering 2D graphics onto a specific layer. This class supports various drawing operations including shapes, sprites, and text, with customization options for materials and alignment.
Some drawing operations:
// Example of drawing a quad and retrieving the Quad2D object
Layer2D& layer = renderer2D.GetLayer("Default");
ResourceRef<Material> material = /* load your material */;
Quad2D& quad = layer.DrawQuad2D(material);
// Modify the quad
quad2D.Create(size, transform, color, Vector2::ZERO(), Vector2::ONE());
// Example of drawing a sprite
Layer2D& layer = renderer2D.GetLayer("Default");
ResourceRef<Sprite> sprite = Sprite::GetResourceClass().Load("sprites/your.sprite.json");
Matrix3x3 transform = Matrix3x3::Translate(Vector2(32.0f, 32.0f));
uint32_t hframe = 0; // Horizontal frame index
uint32_t vframe = 0; // Vertical frame index
layer.DrawSprite(sprite, transform, hframe, vframe, Color::WHITE());
// Example of drawing a filled rectangle
Layer2D& layer = renderer2D.GetLayer("Default");
Vector2 size(100.0f, 50.0f); // Width and height of the rectangle
Matrix3x3 transform = Matrix3x3::IDENTITY();
layer.DrawFillRectangle(size, transform, Color::RED());
// Example of drawing a line
Layer2D& layer = renderer2D.GetLayer("Default");
Vector2 start(0.0f, 0.0f); // Start point of the line
Vector2 end(100.0f, 100.0f); // End point of the line
float lineThickness = 2.0f; // Thickness of the line
Matrix3x3 transform = Matrix3x3::IDENTITY();
layer.DrawLine(start, end, lineThickness, transform, Color::RED());
// Example of drawing text
Layer2D& layer = renderer2D.GetLayer("Default");
ResourceRef<Font> font = Font::GetResourceClass().Load("fonts/your.font.json");
Matrix3x3 transform = Matrix3x3::IDENTITY();
layer.DrawText(font, "Hello World", transform, Color::WHITE());
// Example of drawing text with alignment
Layer2D& layer = renderer2D.GetLayer("Default");
ResourceRef<Font> font = Font::GetResourceClass().Load("fonts/your.font.json");
Matrix3x3 transform = Matrix3x3::IDENTITY();
layer.DrawTextAlign(font, "Hello World!", transform, TextHorizontalAlign::CENTER, TextVerticalAlign::MIDDLE, Color::WHITE());
A Sprite is a resource that consists of multiple frames arranged in a grid within a single Texture.
Example:
{
"Texture": {
"ImagePath": "textures/spr_run.png",
"Filter": "NEAREST",
"RepeatMode": "REPEAT"
},
"HFrames": 6,
"VFrames": 1,
"RelativeToCenter": true,
"Offset": {
"x": 0.0,
"y": 0.0
}
}
Fonts in ASEngine are also resources treated in a manner similar to sprites. When a font is created, it generates a spritesheet grouping all character in the font. This process allows the text to be rendered efficiently during runtime.
Example of font:
{
"FontPath": "fonts/kongtext.ttf",
"Size": 32,
"CharacterSeparation": 4,
"LineSeparation": 4,
"SpaceSize": 32
}
Transform2D is a component designed for handling 2D transformations of entities.
It provides properties for managing position, scale, and rotation, and methods to compute local and global transformation matrices.
The component also supports parent-child relationships, allowing for hierarchical transformations.
For efficient performance, it's recommended to cache global transformation results when possible.
Example:
"Transform2D": {
"Position": {
"x": 0.0,
"y": 0.0
},
"Scale": {
"x": 1.0,
"y": 1.0
},
"Rotation": 0.0,
"Entities": [
/* your child entities here ... */
]
}
Camera2D is a component used for managing 2D camera properties.
It includes a Target property to specify the viewport, a PixelSnapping option to enable or disable snapping of the camera position to pixel boundaries, and a set of Layers to control which layers the camera will render.
The Transform2D component is required for the camera to render entities into the target.
If the Target is not set, the camera renders directly to the screen.
Example:
{
"Camera2D": {
"Layers": ["Default"],
"PixelSnapping": true,
"Target": "viewports/pixel.viewport.json"
},
"Transform2D": {
"Position": {
"x": 0.0,
"y": 0.0
},
"Scale": {
"x": 1.0,
"y": 1.0
},
"Rotation": 0.0,
"Entities": []
}
}
ASEgnine offers a low level renderer that gives the user more control while also being easier to use than your typical OpenGL or Vulkan.
Only OpenGL is supported for now. But I plan to add Vulkan soon.
Buffers are used to store data in the GPU. There are 3 types of buffers:
- Array Buffers
- Index Buffers
- Uniform Buffers
Example:
// create your buffer
ResourceRef<Buffer> myBuffer = Buffer::GetResourceClass().New();
myBuffer->Create(BufferType::ARRAY);
// send data
myBuffer->SetData(data, size);
A texture is an image stored in the GPU.
Example:
// load the png
Image image{};
image.LoadPNG("path.to.image.png");
// create the texture
ResourceRef<Texture> myTexture = Texture::GetResourceClass().New();
myTexture->Create(image, TextureFilter::LINEAR, TextureRepeatMode::REPEAT);
Textures are serializable resources so you can create them in your assets folder as json files:
{
"ImagePath": "textures/leaf.png",
"Filter": "NEAREST",
"RepeatMode": "REPEAT"
}
You can then load them like this:
ResourceRef<Texture> texture = Texture::GetResourceClass().Load("textures/lead.texture.json");
ASEngine supports Vulkan GLSL. When you build your game project all the shaders present in the assets folder gets compiled to their .spirv equivilent.
Make sure your shader codes ends with .frag or .vert.
To create a shader your need to provide it with the vertex and fragment ShaderSources. In addition you will need a VertexInputDescriptor to give it the layout of the vertex data.
Example:
- Vertex shader:
#version 450
//attribute
layout(location = 0) in vec2 v_Position;
layout(location = 1) in vec4 v_Modulate;
// outs
layout(location = 0) out vec4 MODULATE;
// main function is generated
void main() {
MODULATE = v_Modulate;
gl_Position = vec4(vec3(v_Position, 0.0), 1.0);
}
- Fragment shader:
#version 450
layout(location = 0) in vec4 MODULATE;
layout(location = 0) out vec4 FragColor;
void main() {
FragColor = MODULATE;
}
- VertexInputDescriptor
{
"VertexInputLayouts":
[
{
"Binding": 0,
"InputRate": "VERTEX",
"VertexAttributes":
[
{
"Location": 0,
"Type": "VEC2"
}
]
}
]
}
Creating the shader:
// importing sources and descriptor
ResourceRef<ShaderSource> vertexSource = ShaderSource::GetResourceClass().Load("shaders/test.vert");
ResourceRef<ShaderSource> fragmentSource = ShaderSource::GetResourceClass().Load("shaders/test.frag");
ResourceRef<VertexInputDescriptor> descriptor = VertexInputDescriptor::GetResourceClass().Load("shaders/test.descriptor.json");
// create shader
ResourceRef<Shader> shader = Shader::GetResourceClass().New();
shader->Create(vertexSource, fragmentSource, descriptor);
You can also create the shader in your assets folder as a json file:
{
"Vertex": "shaders/test.vert",
"Fragment": "shaders/test.frag",
"VertexInputDescriptor": "shaders/test.descriptor.json"
}
A material is linked to a shader. It contains all the uniform data and samplers for the material.
Example:
ResourceRef<Material> material = Material::GetResourceClass().New();
material->Create(shader);
material->SetShaderParam("Wold", "Position", worldPosition);
material->SetShaderParam("Texture", myTexture);
Materials are serializable resources. You can create them as json files in the assets folder.
{
"Shader": "shaders/default2D/default2D.shader.json",
"UniformBuffers":
{
"World":
{
"Position":
{
"x": 5.0,
"y": 2.0,
"z": 0.0
}
}
},
"Samplers":
{
"Texture": "textures/leaf.texture.json"
}
}
Viewports are rendering contexts that allow you to draw onto a framebuffer and retrieve the results as textures.
Example:
ResourceRef<Viewport> viewport = Viewport::GetResourceClass().New();
viewport->Create(320, 180, 1);
To make a draw call your need to
- Bind the vertex buffers
Renderer::GetInstance().BindVertexBuffer(vertexBuffer, binding);
- Bind the index buffer
Renderer::GetInstance().BindIndexBuffer(indexBuffer);
- Bind a material
Renderer::GetInstance().BindMaterial(material);
You can also bind a shader and set the uniform buffers manually.
- Make a draw class
Renderer::GetInstance().DrawElements(indexCount);
or if you want to use GPU Instancing
Renderer::GetInstance().DrawElements(indexCount, instanceCount);
All rendering commands needs to be done in a viewport context.
Renderer::GetInstance().Begin(viewport);
// Draw commands to viewport
Renderer::GetInstance().End();
// ...
Renderer::GetInstance().Begin();
// Draw commands to backbuffer
Renderer::GetInstance().End();
Resources are garbage collected objects that can be loaded from a file or created programmatically.
Resources are always accessed using a ResourceReference.
// getting resource class
auto& textureClass = Texture::GetResourceClass();
// creating reference
ResourceRef<Texture> createdTexture = textureClass.New();
// loading resource
ResourceRef<Texture> loadedTexture = textureClass.Load("assets/example.texture.json");
// automatically destroyed if not needed
Example of resource resource class:
Header file:
class Buffer: public Resource
{
ASENGINE_DEFINE_RESOURCE(Buffer);
public:
virtual ~Buffer() {};
// create buffer
void Create(BufferType type);
private:
BufferType m_Type = BufferType::NONE;
};
Source file:
ASENGINE_SERIALIZE_RESOURCE_REF(Buffer);
// ...
Your register your resource class like this:
ASENGINE_REGISTER_RESOURCE_CLASS(Buffer);
If you want a resource class with different implementations (useful for API agnostic architectures), you can register your resource class like this:
ASENGINE_REGISTER_ABSTRACT_RESOURCE(Buffer, OpenGLBuffer);
The engine uses a pure ECS architecture. With:
-
Entity: just an ID
-
Component: Data only with no behaviour
-
System: Behaviour of entities that has some given components
Create a struct that inherits from Component.
Example:
struct SpriteRenderer: public Component<SpriteComponent>
{
ResourceRef<Sprite> SpriteToRender;
float Frame = 0.0f;
float FrameRate = 8.0f;
void OnCreate() override;
void OnDestroy() override;
};
Create a class that implements ISystem.
Example:
// render sprites on screen
class SpriteRenderingSystem: public ISystem
{
public:
// update
void Update(float delta)
{
// query components
EntityQuery<SpriteRenderer, TransformComponent>query{};
// if you have a small number of components you can use this
query.ForEach([&delta](SpriteComponent& sprite, TransformComponent& transform)
{
//behaviour...
});
// for better performances use this
query.ForEachCollection([&delta](ComponentCollection<SpriteRenderer>& sprites, ComponentCollection<TransformComponent>& transforms, size_t count)
{
for (size_t i = 0; i < count; i++)
{
// behaviour
}
});
}
};
You can create entities and delete with the help of an entity builder.
Example:
SpriteRenderer sprite{};
TransformComponent tranform{};
EntityBuilder builder;
builder.AddComponents(sprite, transform);
// create entity
auto& entityManager = EntityManager::GetInstance();
EntityID entityID = entityManager.Create(builder);
// destroy entity
entityManager.Destroy(entityID);
We are not done yet, you need to register your components and systems to the module your working on.
// component registry ...
ASENGINE_REGISTER_COMPONENT(SpriteRenderer);
// system registry ...
ASENGINE_REGISTER_SYSTEM(SpriteRenderingSystem);
-
Math
-
Log
-
UniqueStrings
The Engine is still under active development with missing important features.