Yesterday, Sunday 24th January was the deadline for the 2021 Search for a Star awards. 23:59pm to be precise and I submitted my entry, Beyond, at 23:58pm. This post is a reflection of what I planned, what happened and what could be done differently next time.
The brief was surprisingly simple: to develop a game, in Unity, based on this starting scene:
Gameplay Research and Initial Concept Design
For the initial ideation, the ICEDIP creativity model was used. The laptop skeleton scene included with the starter project (Figure 1) initially caused a major stumbling block: it was difficult to see, how to work this mechanic into the game. In short, I was stumped!
The ‘D’ in ICEDIP is for Digestion. Walking away from the project brief for a short period to let the subconscious take over provided the answer needed. The 1980’s film ‘Wargames’ was showing on TV and the scene where David and Jennifer broke into the WOPR computer system was strikingly similar to the starter project. Staying with the sci-fi film genre from that era and adding ‘Dark Star’, ‘Event Horizon’ and ‘Tron’ to the mash-up unblocked the ideation which led to the ICEDIP Inspiration phase brainstorm shown in Figure 2.
The game starts with player enjoying a text-based adventure game on a laptop. Unknown to the player, the laptop is infected with an advanced AI virus that is activated on the next command the player issues. Once activated, just as in the film ‘Tron‘, the virus ‘digitises’ the player and brings them into their own domain: inside the game world.
From here, the main game begins. The objective is simple: escape the game world by finding the clues and items needed to reach the master control terminal and open a portal to get back home. If only it were that easy: the system wants to fight back.
This was a big step forward in my game development career: my first attempt at making a 3D game and a first person shooter (FPS).
Game Design
As I was entering the programming category I felt the project needed a heavy bias towards software and software development practices. From the outset I wanted to establish a solid Test and QA foundation. This was a big project and I wanted a mechanism in place, a safety net, that if/when something broke I knew about it sooner rather than later. I wanted to build quality in from the outset, not leave it to the end
One of the first activities I undertook was to setup an automated build and test system to check and verify each of my source branches. Jenkins was the obvious choice. I’ve used it before and it integrates very well with Git-based version control systems. Figure 4 shows a screenshot of Jenkins running a pipeline build, configured via a ‘jenkinsfile’ stored in the project’s repo. The graph shows the number of test cases executed and the pass rate. The green area gives the stage by stage status of each build.
As I have a deep fascination with Game AI I chose to take inspiration from the research of Jeff Orkin into Goal Oriented Action Planning (GOAP) AI systems and write an implementation. In its simplest form, a GOAP AI system creates a prioritised action plan from a set of atomic actions that an AI agent can execute to fulfil a goal .
The rationale behind choosing to implement a GOAP AI system is that it can create complex, pseudo-intelligent behaviour through the definition and implementation of a set of atomic actions. The downside being the planning algorithm can be costly and therefore detrimental to performance.
Figure 5 provides a simplified view of the GOAP architecture for this project. As it can be complex to set up and configure, the design goal was to allow a developer to control the system in a human readable format through the Unity Inspector. New actions, goals and beliefs can be added very quickly in this way. When the game scene loads, these are then compiled into C# dictionaries and lists for the agent and planner scripts to use internally.
Having the Jenkins build and test system available throughout the development of this system was a huge advantage. The software is relatively straightforward and even so, writing a suite of test cases for each class during development caught several defects and helped to improve the implementation along the way.
The GOAP Planner class was originally intended to make use of the A* pathfinding algorithm . Whilst A* is also implemented as part of this project, based on the algorithm presented in AI Game Programming Wisdom , it is not currently being used in this project (other than by the test cases that accompany it). I decided to keep it a part of this project to offer an additional example of my source code and intend to utilise it when I optimise the path finding mechanism in my GOAP implementation.
Actions within the GOAP system were designed to be very lightweight, simple classes. Each one only needs to implement three methods as a minimum, each called from the Agent’s LateUpdate() method:
PrePerform()
Called at the beginning of the Action, e.g. to set the movement animation (walk, run etc.)StartPostPerform()
Called at the end of the movement phase and before the Action’s effect takes place, e.g. to cancel the movement animation and initiate the action-specific animationEndPostPerform()
Called at the end of the Action’s effect e.g. to cancel the action-specific animation.
These three methods are intended to interact with the Agent’s Animator to control which animation sequence is played, see Figure 6.
Almost all actions will have two distinct phases: the Movement Phase where the NPC Agent is moved to the location of the action and the Effect Phase where it performs the action. This is echoed in the Animator state machine which can be thought of as having two, distinct sub-state machines: one for the movement animations and one for the effects (Figure 7). The ‘Action’ parameter is used to select the animation to play. Negative values indicate a Movement Phase animation and positive an Effect Phase animation with Idle sitting in the middle on zero and acting as a star point. This approach significantly simplifies the transition conditions. Figure 8 extends this concept further by adding in the states for taking damage and NPC death.
At the beginning of each atomic action, the concrete Action class transitions the Animator from the IDLE state to one of the available movement states through a call to its PrePerform()
method. When the movement has completed, StartPostPerform()
is called to transition the Animator momentarily back to IDLE before playing one of the EFFECT animations. This animation is played to completion or looped until the Action’s ‘Duration’ time has expired at which point the EndPostPerform()
method is called to return the Animator back to IDLE, ready for the next action to begin.ww
The atomic actions identified for the game are defined in the table below:
Action | Conditions | Effects | Move Animation | Effect Animation | Duration | Cost |
Use Terminal | Has Data | +Data Entered-Has Data+Unstable (Random) | Walk | Type | 10 | 10 |
Record Data | Needs Data | +Has Data-Needs Data | Walk | Type | 10 | 10 |
Change Settings | Unstable | +Settings Changed-Unstable | Walk | Type | 10 | 1 |
Monitor Equipment | Settings Changed | -Settings Changed+Stable | Walk | Watch | 15 | 1 |
Bored | Data EnteredStable | -Data Entered-Stable (Random) | Walk | Look | 10 | 15 |
Get Supplies | Needs SuppliesSupplies Available | -Supplied Available+Has Supplies | Walk | Pick up | ||
Deliver Supplies | Needs SuppliesHas Supplies | -Needs Supplies-Has Supplies+Supplies Delivered | Walk | Put down | ||
Patrol | Find Player | +Patrol+Tired | Walk | N/A | N/A | N/A |
Rest | Tired | Rested | Walk | Idle | 10 | 1 |
Within the game, there are two classes of NPC:
- Utility Robots
- Sentries (Warriors in the Unity project hierarchy)
The Utility Robots are passive by nature and merrily go about their business. They have three main goals: to record and enter data, to keep the equipment stable and to not get bored. To achieve this, 5 actions have been defined:
- Record Data
- Enter Data
- Adjust Settings
- Monitor Equipment
- Look out of the window
Sentries are aggressive and (if the actions were implemented) would kill the player on sight. They have three actions:
- Patrol
- Go Home
- Rest
Most of their time is spent patrolling the corridors and rooms looking for the player. When they get tired they return to their quarters to rest before returning to their duties.
The concept level design was a simple pencil sketch on squared paper (Figure 9, Figure 10, and Figure 11)
In reality this was extremely ambitious and was both scaled down and simplified for the prototype game.
Reflective Summary
One thing that was very evident from running this project was that I started far too late. In truth, I was on the verge of giving up owing to family, university and work pressures in the run-up to Christmas. Finding Making time for the project was all but impossible until January and by then is was a big up-hill struggle trying to make up ground.
I significantly underestimated the amount of work involved and overcommitted as well. My original level design concept was far too big: it took much longer to complete than I anticipated and even now is very sparse and lacking in both props and details (Figure 13, Figure 15, and Figure 16). As a programmer, I definitely have a new-found respect for level designers, 3D modelers and artists!
This was my first ever attempt at creating a 3D game and, without doubt, the project was really great fun. It’s nowhere near finished but I’m really pleased that I got this far. The GOAP AI system was interesting and rewarding to write. I intend to develop it further as there are a number of big optimisations and enhancements I’ve identified and plan to implement these throughout the remainder of my Masters’ course.
Learning how to use the Unity test runner was another big learning point. In hindsight I now have to question the value of this: it hasn’t added to the project per se but it did catch and remove a lot of defects in the software that would otherwise have gone unnoticed. Whilst it didn’t directly contribute to advancing the project development it did prevent me from being held up through defects and logical coding errors in the later stages when I applied the AI to the NPCs.
My design for the animation state machine was another big plus point. Dividing the animations between movement and effects then using the ‘idle’ state as the transition between the two halves significantly simplified the overall design. As can be seen in Figure 8, ‘idle’ evolved into a star point, it being the only state connected to (almost) all other states. This paid dividends when I started to implement the combat animations, specifically for the NPC to take damage and ultimately die (evolution from Figure 7 to Figure 8).
The game is far from finished, and, were it not for the submission deadline would definitely not have been released into the wild as it stands, there being a huge amount of work to do to finish it, e.g.
- Implement a means of quiting the game [I cannot believe I overlooked this!!!]
- Implement NPC actions for Player Found and Kill Player
- Sentry NPCs to alert nearby sentries when the player is found
- Implement Take Cover action to avoid return fire from player
- Define NPC actions for teamwork and coordinated attacks on the player
- Add additional props and scenery to the base to provide that cover
- Finish the level design
- Patch up holes adjacent to doors
- Add sound effects and background music
- Design and implement UI and HUD
- Improve scene transition (this was very rushed to meet the submission deadline)
- Bring the laptop text display into the second scene by reworking one of the console assets
- Fix assets to work with URP shader (this was a late change and not all graphics are working as they should)
- Play test, play test, play test
Search for a Star was really enjoyable and put the fun back into writing software. The result is nowhere near finished but I achieved far more than I thought possible back in December. And the best part? 3D games aren’t nearly as scary to develop as I originally thought.
BTW, if you’d like to take a look at the game as it stands, it is available here. Download the ZIP file, uncompress and run the executable. To exit the game, use ALT-F4 or ALT-TAB and close the window.
List of Figures
Figure 1. THORN, 2021 Search for a Star Starting Scene
Figure 2. THORN, 2021 Search for a Star Inspiration Brainstorm
Figure 3. THORN, 2021 Starting Scene Dialogue
Figure 4. THORN, 2021 Jenkins running Unity build and test
Figure 5. THORN, 2021 GOAP AI Software Architecture
Figure 6. THORN, 2021 Agent Animator State Machine
Figure 7. THORN, 2021 Animation State Machine
Figure 8. THORN, 2021 Animation State Machine Showing Initial Combat States
Figure 9. THORN, 2021 Level 1 Concept Design
Figure 10. THORN, 2021 Level 2 Concept Design
Figure 11. THORN, 2021 Level 3 Concept Design
Figure 12. THORN, 2021 Top-down View of Final Level Design
Figure 13. THORN, 2021 Residential Corridor, Level 1
Figure 14. THORN, 2021 Staircase Form Command and Control to Level 1
Figure 15. THORN, 2021 Command and Control
Figure 16. THORN, 2021 Main Reactor Room — Unfinished