GUInity
is a 3D component-based game engine. It's a personal project started by myself, Guilherme Cunha.
Objectives
- Study and practice C++ in a large project. It's also a way of learning new C++11 concepts.
- Get a better understanding of how game engines work under the hood.
- Integrate several libraries into one software.
- Develop with cross-platform in mind (currently GUInity works for both Windows and MacOS)
Name
I chose the name GUInity because it summarizes well what I'm trying to do:
- "GUI" stands for Gui-lherme not for Graphical User Interface. Because it's mine! My precious!
- "nity" stands for Unity. It's the engine I have the most experience and the one that I'm using as a base for a lot of design decisions.
Dependencies
Please note that even though I'm developing the engine from scratch, I'm also using a bunch of libraries to aid the development. For the purposes of this project, I've chosen to use, so far, the following libraries:
- PhysX (3.1.1) - Physics library developed by NVIDIA
- FreeType (2.4.0) - .ttf library
- Boost(1.57.0) - I don't believe this set of libraries need any introduction
- GLFW(3.0.4) - Cross-platform library for handling window and basic OpenGL configurations
- libPNG - .png library
- FBX SDK (2015.1) - .fbx library
- FMOD Studio (1.05.02) - Sound library
Also please note that due to possible licensing issues of the libraries and operational system specific binaries, I'm not providing any of them in this repository. The benefit is that the repository is fairly compact.
Goal
- Get to a point where it's easy to create a simple game using code, in an component-based engine that has built-in physics, graphics and audio systems. ✅
- Have an editor to ease the development.
My goal is NOT to create a new and better Unity, but to understand how they were able to develop it. Unity has grown a lot over the last few years, they've been obviously doing something right. Therefore, I think it's a good foundation for a game engine.
Here's the Trello board where I keep features that I'd like to implement in the future.Overall Look and Description
So I've been talking about component-based here and there but what does that mean? Developers that are familiar with Unity are already used to this concept but if that's not the case, here's the main idea.
The game objects, Actors, are merely containers that exist in the World. All the behaviour that an Actor must have is done by adding and removing Components to it, not through standard inheritance. For example, to create an Actor that would be rendered on the screen, it would need a MeshFilter, reference to a Mesh Assets and a Mesh Renderer components. The code would be:
// Create an Actor
shared_ptr spaceShipRoot = Factory::CreateActor("SpaceShip");
// Set the scale of the Actor
spaceShipRoot->transform->setScale(glm::vec3(1.2,1.2,1.2));
// Add a MeshFilter to the Actor
shared_ptr meshFilter = spaceShipRoot->AddComponent();
// Set the MeshFilter Mesh
meshFilter->setMesh(spaceShipMesh);
// Add a MeshRenderer to the Actor
shared_ptr meshRenderer = spaceShipRoot->AddComponent();
// Set the Material for the MeshRenderer
meshRenderer->setMaterial(spaceShipMaterial);
The components that are available right now are:
- Camera - Adds a viewing frustrum that's used to render the scene. Only one camera is supported at the moment.
- Collider - Add Collision capabilities to the Actors. They can be used either for real physics simulation or as collision volumes (triggers). Colliders come in several shapes: Spheres, Boxes, Capsules and Meshes (convex hull of a mesh).
- Light - Lights up the world. Currently there's only one type of Light, point light and only one is used by the rendering module.
- MeshComponent - Has a reference to a Mesh. There are two types currently, a MeshFilter that references a MeshAsset and a FontMesh that generates a mesh based on a string.
- MeshRenderer - Has a reference to a Material which describes how a mesh should be rendered on the screen.
- RigidBody - Transforms a regular actor into a rigid body, allowing for physics simulation.
- RigidStatic - The same as RigidBody but the body is always static.
- ScriptComponent - Allows for custom behaviour
The piece of code above is enough for creating an Actor and rendering it on the screen but you must've noticed that it references some variables that were not declared in it. The variables that are missing are references to Assets. Assets are game assets that are imported or generated by GUInity.
The files that are inside the "data" folder are imported at the start of the program and can be accessed through the AssetsDatabase. Examples of Assets that are imported automatically are:
- Textures (.png)
- Meshes (.obj, .fbx)
- Sound (.mp3, .wav, .ogg)
Examples of Assets that needs to be created manually are :
- Shaders - Code that describes how to render a mesh (Vertex and Fragment shader)
- Material - References a shader allowing for custom parameters
- Fonts - A .ttf is imported and a Texture is generated based on an alphabet.
- PhysicsMaterial - Describes the physical properties of an Actor that is under physics simulation
The piece of code that completes the previous one would be this:
// Gets a reference to the Mesh spaceShip.fbx
shared_ptr<Mesh> spaceShipMesh = AssetDatabase::getAsset<Mesh>("spaceShip.fbx");
// Gets a reference to the Texture spaceShipTexture.png
shared_ptr<Texture> spaceShipTexture = AssetDatabase::getAsset<Texture>("spaceShipTexture.png");
// Creates a Shader using a vertex shader and a fragment shader
shared_ptr<Shader> diffuseShader = AssetDatabase::createShader("LightShader", CommonData("vsLight.vs"),
CommonData("fsLight.fragmentshader"));
// Creates a Material using the LightShader created above
shared_ptr<Material> spaceShipMaterial = AssetDatabase::createMaterial("SpaceShipMaterial", diffuseShader);
// Sets the texture of the material
spaceShipMaterial->setParamTexture("_textureSampler", spaceShipTexture);
This is all very good but... how do I add custom behaviour to my Actors? I'm glad you asked!
Custom behaviour is added to the Actors in the form of ScriptComponents. They have certain functions that are called when the game is awakening, ticking or being shutdown.
One example of a custom behaviour is the following class PlayerScript
// PlayerScript.h
class PlayerScript : public ScriptComponent
{
public:
// The velocity drag
float dragFactor;
// How fast I move?
float moveSpeed;
// How fast I turn?
float rotateSpeed;
// Current velocity
glm::vec3 velocity;
// Called to initialize the script
virtual void awake() override;
// Called every frame
virtual void tick(float deltaSecods) override;
// Apply drag to velocity
void applyDrag(float deltaSeconds);
// Called when this Actor collided with another Actor
virtual void onCollision(shared_ptr<Actor> actor) override;
};
// PlayerScript.cpp
void PlayerScript::awake()
{
// Initialize variables
dragFactor = 0.01f;
moveSpeed = 0.1f;
rotateSpeed = 5;
}
void PlayerScript::tick(float deltaSeconds)
{
// Get reference to actor that owns this script
shared_ptr<Actor> actorLock = actor.lock();
if (!actorLock)
return;
// Get reference to its transform
shared_ptr<Transform> transform = actorLock->transform;
// If keyboard arrow up is being pressed
if (Input::getKey(GLFW_KEY_UP))
{
// Add to current velocity
velocity += transform->getUp() * moveSpeed;
}
// If keyboard arrow up is being pressed
if (Input::getKey(GLFW_KEY_LEFT))
{
// Get current rotation and apply some rotation CCW
glm::quat rot = transform->getRotation();
glm::quat left = glm::angleAxis(deltaSeconds * rotateSpeed, transform->getForward());
transform->setRotation(left * rot);
}
if (Input::getKey(GLFW_KEY_RIGHT))
{
// Get current rotation and apply some rotation CW
glm::quat rot = transform->getRotation();
glm::quat right = glm::angleAxis(deltaSeconds * rotateSpeed, -transform->getForward());
transform->setRotation(right * rot);
}
// Translate the actor based on current velocity
glm::vec3 position = transform->getPosition();
position += velocity * deltaSeconds;
transform->setPosition(position);
// Apply drag to velocity
applyDrag(deltaSeconds);
}
void PlayerScript::applyDrag(float deltaSeconds)
{
// Add opposite force
velocity -= velocity * dragFactor;
}
void PlayerScript::onCollision(shared_ptr<Actor> actor)
{
// This actor collided with another one. Do something!
}
This is just a basic introduction and there's much more to explore in GUInity.
The following class diagram is the most complete one I could make while still keeping it readable.

GUInity game example
Below is a video showcasing a small Asteroids-like game developed using GUInity.
Considerations
Creating a game engine is not a simple task. Instead of trying to create the most optimized engine ever, I'm just "doing it", for now. Every now and then, when I feel like I completely grasped a concept, I go back and do my best to optimize it. Most of the times, I aim for readability and try to experiment with new features of C++11. I've been learning a lot from this project and intend to carry it on as well as I can.
I develop this project alongside my Master in Digital Media. This means I don't have as much time as I'd like to work on it. It also means that some periods this repository will have more updates than others.