Spores of Malice

Spores of Malice was made in C++ and is a bullet hell game, inspired by Enter the Gungeon.
Details
- Genre: Top Down Adventure, Bullet hell
- Engine: The Game Engine
- Duration: 7 weeks at 50%
- Team: 6 programmers, 4 artists, 3 level design
My contributions for this project:
- Dialog system
- Destructible objects
- Double buffered rendering
- Particle system in both 2D and 3D
- Scripted boss event
Dialog System
This dialog system works together with Unity. It was created for our level designers to be able to place box colliders in Unity to trigger the corresponding text. The text is read from a .json file

Loading the text from .json file was done by code below.
if (std::filesystem::exists(aFilePath))
{
nlohmann::json jsonData = nlohmann::json::parse(file);
file.close();
std::vector<std::string> tempStringHolder;
for (int i = 0; i < jsonData["Text"].size(); i++)
{
for (int j = 0; j < jsonData["Text"][i]["value"].size(); j++)
{
tempStringHolder.push_back(jsonData["Text"][i]["value"][j]);
}
myMap[jsonData["Text"][i]["id"]] = tempStringHolder;
tempStringHolder = {};
}
}
Text example of the .json file
"Text": [
{
"id": 0,
"value": [
"Bob: Ohh my shroom, why can't I move?!",
"Nettlerot: You can't move because I am in control.\nNow, how did these controls work?",
"Nettlerot: aah yes, just like a keyboard and mouse,\nthe WASD keys move this shroom around."
]
},
{
"id": 1,
"value": [
"Bob: BENNY HELP ME! I am being\ncontrolled by an evil gun demon!",
"Benny: Bob is that you? My senses\nreally aren't what they used to be.",
"Nettlerot: Ahh Benny, the first in a long line of victims before my power.",
"Nettlerot: Now how can I make this\nshroom guy pull my trigger...?",
"Nettlerot: Of course, I use the mouse to aim him,\n then the LEFT MOUSE BUTTON to fire!",
"Nettlerot: I need to thank the gamer\ndemons for this later, HA HA HA HA"
]
},
Destructible objects
To create more fun interactions I added particle effects on the destructible objects.
I find the destructible objects to be the ideal amount of interference. In tense situations, it's hardly noticeable, but when I intentionally interact with it, it feels deeply satisfying.

Update loop for Destructible objects
bool Destructible::Update(float aDeltaTime, Tga::Vector3f aPlayerPos, BoxCollider aPlayerCollider)
{
float distToPlayer = (aPlayerPos - myTransform.GetPosition()).LengthSqr();
if (distToPlayer > myRadiusSquared)
{
return false;
}
if (myIsDead == true)
{
myDeadTimer += aDeltaTime;
}
if(myBoxCollider.CheckCollision(aPlayerCollider) == true)
{
for (int i = 0; i < myAllParticleSystems.size(); i++)
{
myAllParticleSystems[i]->Play(true);
if (myIsDead == false)
{
PlayDestroySound();
}
}
myIsDead = true;
}
if (CheckBulletCollisions() == true)
{
myIsDead = true;
return false;
}
return true;
}
Double buffered rendering
Main loop and logic thread creation.
// Logic Thread
std::thread logicThread(&CallUpdate, &gameWorld);
// Main loop
while (engine.BeginFrame())
{
syncDone = true;
gameWorld.Render();
while (!gameReady); // Wait for UpdateLoop
engine.EndFrame();
input.Update();
controllerInput.Refresh();
gameReady = false;
if (gameWorld.GameShouldQuit())
{
break;
}
}
logicThread.join();
Logic thread loop
void CallUpdate(GameWorld* aGameWorld)
{
Tga::Engine& engine = *Tga::Engine::GetInstance();
while (globalMessage != WM_NCDESTROY)
{
if (syncDone == true)
{
syncDone = false;
DoControllerInput();
aGameWorld->Update(engine.GetDeltaTime());
gameReady = true;
}
if (aGameWorld->GameShouldQuit())
{
break;
}
}
}
Particle System
I created a particle system for the previous project and wanted to keep developing the tool as it created a better mood for the game. The system was previously only 2D but for this game I developed it to become both 2D and 3D.

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 about the particle system click the link below.
Read more
Scripted boss event
I created a simple scripted event to distinguish boss fights from normal enemy fights. When entering a boss room the moment is locked and camera pans to the boss that falls down from the sky.
