Curse That Magic Cat! is a lighthearted, single-player PC game in which players strive to balance quiet stealth with loud destruction. After having been cursed and transformed into a cat, you must race Magnus the Wizard to use three spellbooks in each level and free yourself. Along the way, you can exact your revenge by destroying his home, angering him, and making him curse that magic cat! The theme was selected to show off artists’ stylization and environmental skills, while the gameplay showcases the work of the software developers through elements like AI programming and user interface. Level designers worked to balance the competing goals of the game (stealth versus destruction versus time) and to construct compelling levels for players to explore.
The game was made over 12 weeks’ worth of three-hour capstone classes using groundwork laid by the team during the summer semester. Ultimately, Curse That Magic Cat! provided the team experience with a focused, fast-paced development cycle while producing a full game in the Unreal 5 engine. The developers also gained further experience with teamwork as they built team culture, established norms and pipelines, and collaborated across disciplines to create the game.
Sprint Planning with team leads
Deliverable Negotiation with Programmers
Cross-Discipline communication
Technology Design Documentation
Code Review, Debug, and Profile
Daily build and package
I and Qifan Huang were responsible for the wizard AI, which should patrol through rooms, collect the spellbooks, search or chase for the cat when its attention gets high, and carry the cat to the nearest cage. We work closely with the designers to create and tweak AI behaviors, and we use a data-driven method to control AI, based on numbers in data tables filled by designers. The behavior transition is shown in the picture below.
AI patrols to next room
How does AI patrol from room to room, with both randomness and order?
Patrolling is the basic behavior that the AI should always choose unless something more urgent happens, like searching or carrying. AI would patrol through each "Room" in the given order from the designers, trying to collect the spellbook in this area. "Room" is defined by volumes placed by the designers, each given a unique id. The connecting relations between rooms, which are commonly known as doors, are defined as an adjacency list, stored as data tables. AI would always take the shortest path generated by BFS when trying to move to another room.
In each "Room", AI would pick interest points randomly, and walk to that chosen interest point, before the randomized investigation time for this "Room" is up. Each interest point, placed by designers, also has a randomized waiting time for AI to investigate this point.
AI searches for cat
Pick the next destination for searching from sight and hearing perceptions
2. How does AI search across rooms, and react to simultaneous events or unreachable searching point?
Searching happens when AI's attention gets high enough. Annoying behaviors, like things broken, cat meow, cat being seen, would increase AI's attention, attention would decrease gradually when AI is not very angry and no annoying behaviors happen. AI would try to search around the location where annoying behaviors happen, so that when the cat meows under a table, the AI would search the walkable place around the table. Even the cat is in another room, AI would still go to that room and search if it is in the search stage, opening the closed door if this new room is not visited yet.
The perception of these annoying behaviors is through Unreal's AI perception system, and all stimulus would be remembered in the given time period. When AI is in the search stage and needs to pick the most interesting point to search, all stimuli and memories would rank up, and the strongest and latest position would be chosen.
But it is quite possible that AI couldn't reach the exact interest point, like when the cat is under a shelf. We deal with this through two steps: pick a reachable point near this point, and generate a new interest point when AI has been stuck for some time.
AI carrys cat to cage
Update struggle count for cat: playing struggle SFX, updating cage or cat's displacement, switching BGM, etc.
3. To where does AI carry the cat, and what does AI do when cat gets free?
AI would carry the cat to the nearest cage and lock the cat up. The nearest cage should reside in an open room, because the AI couldn't open a door while carrying the cat in both hands. The cat could struggle both on the way or in the cage. If the cat struggles and gets free on the way, AI would ignore cat for some time and turn to search stage. Otherwise, AI would continue to patrol through rooms.
Status before AI starts to carry cat is remembered, so that AI could pick up from where it was after recovering from carry stage.
All audio playing in the game is controlled by two audio managers, one for UI audio the other for in-game audio. One audio should be in-game only if it fades when players get farther from its position. I was responsible for the in-game audio manager, which enables the AI to hear sounds with different strength if they play at different locations, as the AI hearing event is reported with explicit loudness given manually.
The initial design was to use the flooding algorithm: the game world is divided into cubes, all positions are translated into cubes; the cube corresponds to sound source would start spreads out in 3 dimensions, with decreasing loudness at non-solid cubes, and 0 loudness at solid cubes; the spreading would stop when AI's position is reached or loudness already turns to 0. This solution failed, because we were only using the blueprint for development and the execution time soon exceeds the required maximum frame time as the game world got larger.
So I turned to Unreal's default pathfinding functions to calculate the distance on XY-plane, and the rough distance for sound traveling around obstacles could be calculated easily.
Communication generally
Daily packaging and playtests
Flexibility on schedules
Planning, new tasks are always coming up
No structured lock for integration
Information lost when not logged to JIRA
Overcommunicate, both verbally and through JIRA
Write test cases for your features
Stop, playtest and reassess often to adjust plans
Make plans for integration as well