TayLee Young|
Systems Programmer and Game Designer
Systems Programmer and Game Designer
Here is some of my most recent work! Some are still in production, some are finished.
This work showcases my skills in programming, game design, and systems. If you would to take a peek at my
resume, you can find it here, or over in my about me page.
I will be adding a blog section soon, which covers some other projects I have worked on, as well as my
thoughts on other related technological topics, so stay tuned for that!
On an alien planet, moths grow to be 5x the size of the ones we find on Earth. They are cute, cuddly, and worth quite the price. You set up a base on this planet, creating a greenhouse as your base of operations. Alone on the planet, you seek to catch, breed, and sell moths all to become a monopoly.
It's hard to sell these guys with how cute they are though...
My main goal was to design the Market, a massive computer sitting in the greenhouse. The player could catch or breed moths and sell them here, where the main point was to get as much money and monopolize the moth market. On the outside, it seemed simple — take the moth, look up a price, and sell it for said price. However, we wanted to be unique. Since this game initially started as an economy simulator, we decided to put a little more thought into it. The breakdown of how a single moth's value gets calculated is as follows
You find yourself lost in the depths of a dungeon. It's dark, damp, and cramped. Equipped with a solitary light, you venture forward, discovering large crystals that can shift and redirect light beams. Soon, it dawns on you — manipulating these beams unlocks pathways, each puzzle leading closer to an escape.
This student project was one of the first of my serious team-based projects. Having worked on some smaller projects before with fewer people, I had an idea of how it would go, ready for the challenges ahead, but it was a pivotal point in my learning.
// singleton instance
Engine* Engine::instance = new Engine();
Engine::EngineCode Engine::Start()
{
// time instance
time = new Time();
AudioManager.LoadMusicFromJSON("./Data/music.json");
AudioManager.LoadSFXFromJSON("./Data/SFX.json");
// initalize all systems
for (int i = 0; i < systemCount; ++i)
{
try
{
systems[i]->Init();
}
catch (EngineCode engineFailure)
{
assert(engineFailure && "Engine failed to initialize. Location: Engine::Start(), Init()");
return engineFailure;
}
}
// main game loop
while (isRunning && !closeRequested)
{
EngineCode code = NothingBad;
try
{
code = Update();
}
catch (EngineCode updateFailure)
{
assert(updateFailure && "Engine failed during update. Location: Engine::Start(), Update()");
return updateFailure;
}
if (code == CloseWindow)
break;
try
{
code = Render();
}
catch (EngineCode renderFailure)
{
assert(renderFailure && "Engine failed during render. Location: Engine::Start(), Render()");
return renderFailure;
}
if (code == CloseWindow)
break;
}
// stop engine
return Stop();
}
Engine::EngineCode Engine::Stop()
{
try
{
ShutDown();
}
catch (EngineCode shutdown)
{
assert(shutdown && "Engine failed to shut down. Location: Engine::Stop(), ShutDown()");
return shutdown;
}
return EngineExit;
}
void Engine::EngineAddSystem(BaseSystem* sys)
{
systems[systemCount++] = sys;
}
bool Engine::Paused()
{
return paused;
}
void Engine::SetPause(bool pause)
{
paused = pause;
}
// get the singleton instance
Engine* Engine::GetInstance()
{
if (instance == nullptr)
{
instance = new Engine();
}
return instance;
}
Engine::Engine() : isRunning(true), systemCount(0), systems(), paused(false), time(NULL), closeRequested(false)
{
}
Engine::~Engine()
{
if (instance != NULL)
{
delete instance;
}
}
Engine::EngineCode Engine::Update()
{
float dt = Time::Instance().Delta();
// update all systems
for (int i = 0; i < systemCount; ++i)
{
try {
systems[i]->Update(dt);
}
catch (EngineCode updateFailure)
{
switch (updateFailure)
{
case CloseWindow:
return updateFailure;
default:
assert(updateFailure && "Engine failed during system update. Location: Engine::Update()");
throw(updateFailure);
}
}
}
return NothingBad;
}
Engine::EngineCode Engine::Render()
{
// render all systems
for (int i = systemCount; ++i)
{
systems[i]->Render();
}
return NothingBad;
}
Engine::EngineCode Engine::ShutDown()
{
// close all systems
for (int i = systemCount - 1; i >= 0; --i)
{
systems[i]->Close();
}
delete time;
AudioManager.Free();
return EngineExit;
}
// set flag to close game engine for whatever reasons you want
void Engine::SetCloseRequest(bool close)
{
closeRequested = close;
}
int LevelCreatorScene::CreateCircleEntity()
{
int circles_existing = 0;
std::string number = "./Data/GameObjects/Circle";
std::string filename = "./Data/GameObjects/Circle.json";
Entity* temp = FileIO::GetInstance()->ReadEntity(filename);
temp->addKey = "Circle";
temp->key = "Circle" + std::to_string(circleCount);
tempEntities.push_back(temp);
++circleCount;
return 0;
}
int LevelCreatorScene::CreateDoorEntity()
{
int door_existing = 0;
std::string number = "./Data/GameObjects/Door";
std::string filename = "./Data/GameObjects/Door.json";
Entity* temp = FileIO::GetInstance()->ReadEntity(filename);
temp->addKey = "Door";
temp->key = "Door" + std::to_string(doorCount);
tempEntities.push_back(temp);
++doorCount;
return 0;
}
int LevelCreatorScene::CreateMirrorEntity(MirrorData mirror)
{
int door_existing = 0;
UNREFERENCED_PARAMETER(door_existing);
std::string number = "./Data/GameObjects/Mirror";
std::string filename;
switch (mirror.spriteDirection) {
case 1: filename = "./Data/GameObjects/MirrorTopLeft.json"; break;
case 2: filename = "./Data/GameObjects/MirrorTopRight.json"; break;
case 3: filename = "./Data/GameObjects/MirrorBottomLeft.json"; break;
case 4: filename = "./Data/GameObjects/MirrorBottomRight.json"; break;
default: return 0;
}
//classic naming conventions
Entity* temp = FileIO::GetInstance()->ReadEntity(filename);
temp->GetComponent()->SetReflection(mirror.direction);
temp->addKey = "Mirror"; // this is for the map holding functions and gives access to function for circle
temp->key = "Mirror" + std::to_string(mirrorCount);
tempEntities.push_back(temp);
++mirrorCount;
return 0;
}
int LevelCreatorScene::CreateEmitterEntity(EmitterData emit)
{
std::string number = "./Data/GameObjects/Emitter";
std::string filename = "./Data/GameObjects/tempEmitter.json";
Entity* temp = FileIO::GetInstance()->ReadEntity(filename);
switch (emit.spriteDirection) {
case 1:
{
BehaviorEmitter* mine = temp->GetComponent();
mine->SetDirection({ 0.0f, -1.0f });
mine->SetPosition(*temp->GetComponent()->GetTranslation());
break;
}
case 2:
{
BehaviorEmitter* mine = temp->GetComponent();
mine->SetPosition(*temp->GetComponent()->GetTranslation());
mine->SetDirection({ 0.0f, 1.0f });
break;
}
case 3:
{
BehaviorEmitter* mine = temp->GetComponent();
mine->SetDirection({ 1.0f, 0.0f });
mine->SetPosition(*temp->GetComponent()->GetTranslation());
break;
}
case 4:
{
BehaviorEmitter* mine = temp->GetComponent();
mine->SetDirection({ -1.0f, 0.0f });
mine->SetPosition(*temp->GetComponent()->GetTranslation());
break;
}
default: return 0;
}
temp->addKey = "Emitter"; // this is for the map holding functions and gives access to function for circle
temp->key = "Emitter" + std::to_string(emitterCount);
tempEntities.push_back(temp);
++emitterCount;
return 0;
}
ImGui::Text("Scene Settings");
ImGui::Text("./Data/Scenes/''LevelNameHere''");
if (ImGui::TreeNode("Export:"))
{
ImGui::Text("TileMapOnly");
ImGui::InputText(".json", myTextBuffer, sizeof(myTextBuffer));
if (ImGui::Button("ExportTileMap"))
{
std::string exportFileName = std::string(myTextBuffer);
if (exportFileName.empty())
{
ImGui::OpenPopup("EmptyExportFileNamePopup");
}
else if (!IsFileNameValid(exportFileName))
{
ImGui::OpenPopup("InvalidExportFileNamePopup");
}
else
{
FileIO::GetInstance()->ExportTileMap(myTextBuffer);
ImGui::OpenPopup("SuccessfulExport");
}
}
if (ImGui::Button("ExportFullScene"))
{
std::string exportFileName = std::string(myTextBuffer);
if (exportFileName.empty())
{
ImGui::OpenPopup("EmptyExportFileNamePopup");
}
else if (!IsFileNameValid(exportFileName))
{
ImGui::OpenPopup("InvalidExportFileNamePopup");
}
else
{
ExportScene(myTextBuffer);
ImGui::OpenPopup("SuccessfulExport");
}
}
ImGuiManager::RenderOKPopup("SuccessfulExport", "Exported!");
ImGuiManager::RenderOKPopup("EmptyExportFileNamePopup", "Please provide a file name for export!");
ImGuiManager::RenderOKPopup("InvalidExportFileNamePopup", "Illegal characters in the tilemap name GOOBER!");
ImGui::TreePop();
}
if (ImGui::TreeNode("Import:"))
{
static char filenameBuffer[256];
ImGui::InputText(".json", filenameBuffer, sizeof(filenameBuffer));
ImGui::Text("NOTE: this will reset the current level.");
{
if (ImGui::Button("Submit"))
{
std::string filename = "./Data/Scenes/" + std::string(filenameBuffer) + ".json";
if (std::string(filenameBuffer).empty())
{
ImGui::OpenPopup("EmptyImportFileNamePopup");
}
else if (!IsFileNameValid(filenameBuffer))
{
ImGui::OpenPopup("InvalidImportFileNamePopup");
}
else
{
std::ifstream file(filename);
sceneName = std::string(filenameBuffer);
if (file.is_open())
{
file.close();
Renderer::GetInstance()->CleanRenderer();
LevelBuilder::GetInstance()->LoadTileMap(filename);
if (EntityContainer::GetInstance()->CountEntities() > 0)
{
int size = EntityContainer::GetInstance()->CountEntities();
for (int i = 0; i < size; ++i)
{
if ((*EntityContainer::GetInstance())[i])
{
tempEntities.push_back((*EntityContainer::GetInstance())[i]);
tempEntities[i]->addKey = tempEntities[i]->GetRealName();
if (tempEntities[i]->GetRealName().compare("Player") == 0)
{
playerExists = true;
}
}
}
}
json counts = FileIO::GetInstance()->OpenJSON("./Data/Scenes/" + std::string(filenameBuffer) + "OBJECTS.json");
if (counts["Circle"].is_object())
{
circleCount = counts["Circle"]["count"];
}
if (counts["Door"].is_object())
{
doorCount = counts["Door"]["count"];
}
if (counts["Mirror"].is_object())
{
mirrorCount = counts["Mirror"]["count"];
}
if (counts["Emitter"].is_object())
{
emitterCount = counts["Emitter"]["count"];
}
if (counts["Receiver"].is_object())
{
ReceiverCount = counts["Receiver"]["count"];
}
if (!entityManager->InitializeProperties(filename + "OBJECTS.json"))
{
ImGui::OpenPopup("NoObjectsInScene");
}
ImGui::OpenPopup("SuccessfulImport");
}
else
{
ImGui::OpenPopup("FileNotFoundPopup");
}
}
}
ImGuiManager::RenderOKPopup("SuccessfulImport", "Successfully imported the scene!");
ImGuiManager::RenderOKPopup("NoObjectsInScene", "Objects do not exist in this scene.");
ImGuiManager::RenderOKPopup("EmptyImportFileNamePopup", "Please provide a file name for import!");
ImGuiManager::RenderOKPopup("InvalidImportFileNamePopup", "Please provide a valid file name without illegal characters for import!");
ImGuiManager::RenderOKPopup("InvalidImportFileNamePopup", "Please provide a valid file name without illegal characters for import!");
ImGuiManager::RenderOKPopup("FileNotFoundPopup", "File not found!");
}
ImGui::TreePop();
}
What happens, as a game designer, when you attempt to create a level whose layout is completely procedurally generated?
This project set out to test this concept by designing a dungeon crawler where no two playthroughs were the same.
The player would navigate a maze-like environment, defeat enemies, collect powerups, and kill the big-bad boss -- all while
traversing a dynamically generated level.
// Spawns a corridor
void GenerateCorridor(int startX, int startY)
{
// get a starting tile to start a corridor
SpawnTile(startX, startY);
// directions for movement (up, down, left, right)
List directions = new List
{
new Vector2Int(0, 1), // up
new Vector2Int(0, -1), // down
new Vector2Int(-1, 0), // left
new Vector2Int(1, 0) // right
};
// store x and y values
int currentX = startX;
int currentY = startY;
// store directions
Vector2Int currentDirection = directions[RNG.Next(directions.Count)];
// store branch points
Stack branchPoints = new Stack();
bool canPlaceTile = true;
int attempts = 0; // number of retries when finding a valid direction
int maxAttempts = 60; // maximum retries before stopping corridor generation
while (canPlaceTile)
{
if (IsValidTilePlacement(currentX, currentY))
{
TrySpawnPowerup(currentX, currentY);
TrySpawnEnemy(currentX, currentY);
// 80% chance to mark this tile as a branch point
if (RNG.Next(100) < 80)
branchPoints.Push(new Vector2Int(currentX, currentY));
// 0.5% chance to generate a trident-shaped branch
if (RNG.Next(100) < 0.5 && GenerateTridentBranch(ref currentX, ref currentY, ref currentDirection, branchPoints))
continue;
// 50% chance to generate a staircase pattern
if (RNG.Next(100) < 50 && GenerateStaircasePattern(ref currentX, ref currentY, currentDirection))
continue;
// 10% chance to generate a room
if (RNG.Next(100) < 10 && GetDistanceFromCenter(currentX, currentY) > 15)
{
int[] possibleSizes = { 3, 5, 7 };
PlaceRoom(currentX, currentY, possibleSizes[RNG.Next(possibleSizes.Length)], possibleSizes[RNG.Next(possibleSizes.Length)]);
continue;
}
// 20% chance to change direction
if (RNG.Next(100) < 20)
{
currentDirection = directions[RNG.Next(directions.Count)];
// 50% chance to generate a spiral
if (RNG.Next(100) < 50 && GenerateSpiralPattern(ref currentX, ref currentY, ref currentDirection))
continue;
}
// move in a valid direction or backtrack if stuck
if (!TryChangeDirection(ref currentX, ref currentY, ref currentDirection, directions, branchPoints, ref attempts, maxAttempts))
canPlaceTile = false;
}
else if (!TryBacktrack(ref currentX, ref currentY, branchPoints, ref currentDirection, directions))
{
canPlaceTile = false;
}
}
}
// generates a trident branch
bool GenerateTridentBranch(ref int x, ref int y, ref Vector2Int direction, Stack branches)
{
Vector2Int originalBranch = new Vector2Int(x, y);
Vector2Int initialDirection = direction;
for (int i = 0; i < 7; i++)
{
x += direction.x;
y += direction.y;
if (!IsValidTilePlacement(x, y)) return false;
SpawnTile(x, y);
}
branches.Push(originalBranch);
return true;
}
// generates a staircase pattern
bool GenerateStaircasePattern(ref int x, ref int y, Vector2Int direction)
{
for (int i = 0; i < 50; i++)
{
x += direction.x;
y += direction.y;
if (!IsValidTilePlacement(x, y)) return false;
SpawnTile(x, y);
}
return true;
}
// generates a spiral pattern
bool GenerateSpiralPattern(ref int x, ref int y, ref Vector2Int direction)
{
int[] spiralSteps = { 7, 6, 6, 4, 4, 3, 2 };
foreach (int steps in spiralSteps)
{
for (int i = 0; i < steps; i++)
{
x += direction.x;
y += direction.y;
if (!IsValidTilePlacement(x, y)) return false;
SpawnTile(x, y);
}
direction = new Vector2Int(-direction.y, direction.x);
}
return true;
}
void SetWeaponStats()
{
WeaponLogic weapon = GetComponentInChildren();
if (weapon == null) return; // No weapon attached, exit early
// Set base stats per enemy type
SetBaseWeaponStats(weapon);
// Apply buffs based on which bosses have been killed
ApplyBossBuffs(weapon);
}
// Sets the base weapon stats per enemy type
void SetBaseWeaponStats(WeaponLogic weapon)
{
switch (enemyType)
{
case EnemyType.BaseEnemy:
weapon.ShotCooldown = 3f;
weapon.BulletsPerShot = 1;
weapon.BulletSpreadAngle = 0.0f;
weapon.BulletRange = 5.0f;
weapon.BulletSpeed = 3.0f;
break;
case EnemyType.FastEnemy:
weapon.ShotCooldown = 3f;
weapon.BulletsPerShot = 1;
weapon.BulletSpreadAngle = 0.0f;
weapon.BulletRange = 5.0f;
weapon.BulletSpeed = 6.0f;
break;
case EnemyType.SpreadEnemy:
weapon.ShotCooldown = 6f;
weapon.BulletsPerShot = 4;
weapon.BulletSpreadAngle = 25f;
weapon.BulletRange = 5.0f;
weapon.BulletSpeed = 5.0f;
break;
case EnemyType.TankEnemy:
weapon.ShotCooldown = 3f;
weapon.BulletsPerShot = 3;
weapon.BulletSpreadAngle = 0.0f;
weapon.BulletRange = 6.0f;
weapon.BulletSpeed = 2.0f;
break;
case EnemyType.UltraEnemy:
weapon.ShotCooldown = 5f;
weapon.BulletsPerShot = 5;
weapon.BulletSpreadAngle = 50f;
weapon.BulletRange = 20.0f;
weapon.BulletSpeed = 5.0f;
break;
case EnemyType.BossEnemy:
weapon.ShotCooldown = 3f;
weapon.BulletsPerShot = 8;
weapon.BulletSpreadAngle = 40.0f;
weapon.BulletRange = 100.0f;
weapon.BulletSpeed = 4.0f;
break;
}
}
// Applies boss kill buffs to the weapon and enemy stats
void ApplyBossBuffs(WeaponLogic weapon)
{
if (!PCGObject.boss1Killed && !PCGObject.boss2Killed && !PCGObject.boss3Killed && !PCGObject.boss4Killed)
return; // No buffs to apply, exit early
// List of boss buffs to apply
(bool bossKilled, ref bool buffApplied, float shotCooldownReduction, int healthIncrease, float speedIncrease,
float bulletRangeIncrease, float bulletSpeedIncrease, int bulletsPerShotIncrease, float bulletSpreadIncrease, int aggroRangeIncrease)[] bossBuffs =
{
(PCGObject.boss1Killed, ref boss1BuffApplied, -0.3f, 1, 0.25f, 0f, 0f, 2, 0f, 0),
(PCGObject.boss2Killed, ref boss2BuffApplied, -0.3f, 1, 0.25f, 5.0f, 2f, 0, 0f, 0),
(PCGObject.boss3Killed, ref boss3BuffApplied, -0.3f, 1, 0.25f, 0f, 0f, 0, 5f, 0),
(PCGObject.boss4Killed, ref boss4BuffApplied, -0.3f, 1, 0.25f, 0f, 0f, 0, 0f, 5)
};
foreach (var (bossKilled, buffApplied, cooldownReduction, healthIncrease, speedIncrease, rangeIncrease, speedBoost, bulletsPerShot, spreadIncrease, aggroIncrease) in bossBuffs)
{
if (bossKilled && !buffApplied)
{
weapon.ShotCooldown += cooldownReduction;
Health += healthIncrease;
Speed += speedIncrease;
weapon.BulletRange += rangeIncrease;
weapon.BulletSpeed += speedBoost;
weapon.BulletsPerShot += bulletsPerShot;
weapon.BulletSpreadAngle += spreadIncrease;
AggroRange += aggroIncrease;
buffApplied = true;
}
}
// TankEnemy and UltraEnemy get extra bonuses
if (enemyType == EnemyType.TankEnemy || enemyType == EnemyType.UltraEnemy)
{
foreach (var (bossKilled, buffApplied, cooldownReduction, healthIncrease, speedIncrease, rangeIncrease, speedBoost, bulletsPerShot, spreadIncrease, aggroIncrease) in bossBuffs)
{
if (bossKilled && !buffApplied)
{
weapon.ShotCooldown += cooldownReduction;
Health += (enemyType == EnemyType.TankEnemy ? 2 : healthIncrease);
Speed += speedIncrease;
weapon.BulletRange += rangeIncrease;
weapon.BulletSpeed += speedBoost;
weapon.BulletsPerShot += bulletsPerShot + (enemyType == EnemyType.UltraEnemy ? 1 : 0);
weapon.BulletSpreadAngle += spreadIncrease;
AggroRange += (enemyType == EnemyType.TankEnemy ? 7 : aggroIncrease);
buffApplied = true;
}
}
}
}
With the grip of a powerful corporation around your neck, demanding you mine resources for their profit and to relieve your debt,
you must land your ship on an alien planet and drill deep down, careful to not make too much noise. Will you ever, truly, be free?
"The last frontier is the human mind and we are its pioneers." - Prey 2017
Hello!
I am TayLee Young, a third year college student at DigiPen Institute of Technology.
I am currently studying Computer Science and Game Design. I focus in systems programming,
data analysis, and game development. I am proficient in C, C++, C#, and I am learning Python,
CSS and HTML. I am also familiar with Unity, Unreal Engine, and Visual Studio.
I have worked on several projects, some of which you can find in the Work section. I have
worked teams, big and small, and I have experience in project management and team leadership.
Of course, I am always looking to expand my knowledge and experience.
I am deeply motivated to create games that are more than just entertainment. I want to create
works of art -- art that inspires people to see things in a new light, to create deeply personal
and provocative worlds, and to challenge the status quo. I want to not only create games,
but to create experiences that change people's lives.
I am heavily inspired by games like Prey, Night in the Woods, Journey, Silent Hill,
and Minecraft. These have impacted my life in ways that I never thought possible. Some
inspired my path for game design, others have inspired my deep love and appreciation
for the art of creation. I will always remember with fondness the first time I played these games,
as they have made me who I am today!
With every project I am in and every team I am apart of, I push strongly for inclusivity,
diversity, and accessibility. I believe that games are for everyone. All people, regardless
of their background, should experience the joy of this art. As a member of the LGBT+ community,
I know firsthand the importance of representation, inclusion, and acceptance. No one should
feel isolated in their experience. This is my mission and goal. To create games that are for
everyone, that inspire everyone, and that change everyone.
Currently, I am apart of team ADMIS, working on a student project about a corrupt corporation,
aliens, and a big beefy drill. Feel free to read about that too!
I am looking for internships and job opportunities, so feel free to contact me anytime!