Root of Evil

Root of Evil was made in C++ using an engine called The Game Engine, provided to us by The Game Assembly. The Game Engine uses DX11.
Details
- Genre: 2.5D Platformer
- Engine: The Game Engine
- Duration: 12 weeks at 50%
- Team: 6 programmers, 4 artists, 3 level design, 3 procedural artists
My contributions for this project:
- Particle system
- Disintegrating block
- Separate threading for Update and Render loops with double buffers
Particle System
I created a particle system for this project to involve our artists much more in the visual appeal for our game. As The Game Engine we used were limited in its capabilities I wanted to add another element to our game that created a more appealing setting to be immersed in.

The particle system is something I have been working on during most of the game projects at school and is something I am very proud of.
To get a more in depth explanation of the particle system click the link below.
Read more
Disintegrating block
When tasked with creating this game project, we were required to include a destructible object. I wanted to go beyond a simple animation and create something more dynamic. After playtesting games made by second-year students, I saw the potential of shaders and became interested in exploring them. Since our coursework hadn't yet covered GPU communication, getting started was challenging. However, this early experience gave me an advantage later in my education when working with GPU buffers and shader-related coding.

To only play the shader when the player collides with the block I set up a buffer to the GPU to enable a variable that starts a timer for running the code.
void DisintegratingBuffer::UpdateShader()
{
D3D11_MAPPED_SUBRESOURCE mappedBuffer = {};
Tga::DX11::Context->Map(myDissolveBuffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedBuffer);
memcpy(mappedBuffer.pData, &myDissolveData, sizeof(DissolveBufferData));
Tga::DX11::Context->Unmap(myDissolveBuffer.Get(), 0);
Tga::DX11::Context->PSSetConstantBuffers(9u, 1, myDissolveBuffer.GetAddressOf());
myNoiseTexture->SetAsResourceOnSlot(9);
}
Shader Code
The function of the disintegrating block was to give it a noise texture that has a cut-off threshold. The threshold variable is changed over time to set the alpha as 0 where for each pixel on the texture that is below the cut-off threshold.
float2 modifiedUV = input.texCoord0;
modifiedUV.x *= uvScale;
modifiedUV.y *= uvScale;
float noise = noiseTexture.Sample(defaultSampler, modifiedUV).r;
noise -= timeElapsed / maxTime;
float3 colour = radiance;
if (noise < minAlphaTreshold)
{
discard;
result.color.rgba = (0.0f, 0.0f, 0.0f, 0.0f);
return result;
}
result.color.rgb = (float3) colour;
result.color.a = albedo.a;
return result;
Threading
During one of our courses we learned about threading and double buffered rendering and at the start of the project I created the base of the project and included the threading. This was also made easier at the start as none of the others I worked with needed to adjust their type of coding for this reason.
Main thread
// Logic Thread
std::thread logicThread(&CallUpdate, &gameWorld);
// Main loop
while (engine.BeginFrame())
{
if (introVideo.IsPlaying() == true)
{
introVideo.Update(engine.GetDeltaTime());
introVideo.Render(engine);
}
else
{
syncDone = true;
gameWorld.Render();
while (!gameReady); // Wait for UpdateLoop
}
engine.EndFrame();
input.Update();
controllerInput.Refresh();
gameReady = false;
if (gameWorld.GameShouldQuit())
{
break;
}
}
if (logicThread.joinable() == true)
{
logicThread.join();
}
Logic thread
void CallUpdate(GameWorld* aGameWorld)
{
Tga::Engine& engine = *Tga::Engine::GetInstance();
while (globalMessage != WM_NCDESTROY)
{
if (syncDone == true)
{
syncDone = false;
if (aGameWorld->GetSoundEffectsOn())
{
Windbox::UpdateAudio(engine.GetDeltaTime());
}
aGameWorld->Update(engine.GetDeltaTime());
gameReady = true;
}
if (aGameWorld->GameShouldQuit())
{
break;
}
}
}
The double buffers we used was set up for the reason to not have any threading issues with accessing the same data from different threads.
struct RenderData3DSprite
{
Tga::SpriteSharedData* sharedData;
Tga::Sprite3DInstanceData* spriteInstance;
};
struct RenderData3D
{
const Tga::ModelInstance* modelInstance = 0;
};
struct AnimatedRenderData3D
{
const Tga::AnimatedModelInstance* animatedModelInstance;
};
struct BatchedSpriteRenderData
{
Tga::SpriteSharedData sharedData;
std::vector<Tga::Sprite3DInstanceData> instances;
};