Road To Eden
Road To Eden was our end of 2nd year project at the Academy Of Interactive Entertainment. You play as Alana as she transports her brother's soul to Eden where he will be laid to rest. Along the way you must solve puzzles, fight creatures and traverse the floating islands to reach your goal. Road To Eden takes inspiration from similar games such as Journey and Sky: Children of the light through it's aesthetic and premise.
​
I was the sole programmer on the project with a team of 6 other members - 3 Designers and 3 Artists. The vast majority of the script work was completed by myself alongside custom shaders and scene lighting. I ended up coming away from the project with modular systems to use in other projects and it was overall a fantastic experience allowing me to work in a large team in developing a large title..
​
Trailer:
​
​
AI - Utility AI
​
Once design work began on the AI it became necessary to choose an AI system to implement. In previous projects I've used behavior trees which dictated AI decision making, however for this project I wanted to try a new approach, one that would give me more flexibility in tweaking behavior without handling edge cases like in behavior tree implementations. In the end I settled on a utility AI approach which offered the flexibility I wished for. However this also required some extra time for tweaking weights and values till the AI felt right.
​
Our game includes flocks of wolves which act as the main aggressors. This was factored into the decision of using utility AI which provided a more dynamic and fluid transition between states through it's use of fuzzy logic, so the wolves could create emergent behavior outside of what a simple decision tree could provide. Along with creating dynamic group behavior with goal based decision making synced across the flock, so the wolves can more easily work together to take down the player.
​
​
Blackboard
​
To effectively communicate data across abstract systems I developed a generic blackboard which accepts all types of C# data. This was used to communicate data across flock members and between AI actions. To avoid spelling mistakes through strings I decided to use an enum list which would hold the names of all possible data contained within the blackboards. In the future however I will look into swapping this back with a string hash method given the versatility of using strings and portability across multiple projects. However this is up for future design.
Example of blackboard used to get flock target:
AI Conditions
To help make the AI system as modular as possible a condition system was set up. This system would allow AI to make logic checks against conditions, which would then return whether the condition has been met. This was achieved through ScriptableObjects that derived from an abstract Condition class. This class would contain an evaluate function to return whether the condition was met or not and would take two parameters, the querying object and the target object.
​
At first I attempted to use standard unity types such as GameObject when passing through the objects, however requiring constant querying of objects for components would take a noticeable hit to performance and wouldn't be very concise while writing conditions. To fix this I instead chose to create the interface IConditionObject. This would be derived from by all possible condition objects and would contain public properties of all possible components or values that a condition might need to query, thus reducing CPU time considerably. Most of these properties would use lazy assignment to make derived classes quicker and easier to implement.
​
Example Conditions:
AI Debugging
​
When starting on the design process for the AI it became quickly apparent that creating adequate AI debugging tools would be required to quicken the speed of development. As the framework for the AI was being set up I set aside some time to write up some in-game UI tools to look into the decision processes of the AI in real- time. This included a drop-down list which dynamically lists all actions possible by the agent and outputs the score it returned when evaluated. This list would update the next time a new action was picked. Selecting the continuous update button would change it so the list will update the evaluations in real time.
​
These tools made it really easy to see in real time what the AI was thinking, giving me vital information to tweak the weights of actions for balancing.
​
Along with AI Action tools I also created a panel to print out all the values inside blackboards. This was used to look into flock and single AI blackboards to see the invisible data from within the editor. A list of blackboard types was used to identify which blackboard to print out and would depend on whether an AI agent was selected. Possible blackboard types where:
​
-
Scene Blackboard
-
Flock Blackboard
-
AI Blackboard
​
To show the values within the blackboard the script would loop over the blackboard and using .ToString() would print out the name and values of all its elements. This method currently only works for single value types and doesn't work for printing out arrays or lists. However in the future edge cases for containers that derive from IEnumerable could be used to identify and print out the contents of an array within another sub-menu. However time constraints for the project meant that further additions had to be put off.
UI Debugging Tools:
Puzzle Framework
​
One of our large design parameters was to include various puzzles along the players journey. A task of which I quickly realized would require a framework to ensure puzzles were quick to create and allow for easy querying of puzzle states (ie. Start puzzle X or Has puzzle X run?). This resulted in a highly modular system which can handle puzzles of all types.
​
The framework was split up into several components, these included:
​
-
Components - Pieces of a puzzle (ie. puzzle objects) each is a script of their own, but all derive from the interface IPuzzleComponent, which provides the properties to interface with the puzzle framework such as ConditionMet which is set to true once the condition of the object is met.
-
Component Groups - Collections of puzzle components which provides methods to query it's components / Verify whether all of it's components have met their conditions.
-
Puzzles - Base class for all puzzles which holds component groups and contains methods to query the puzzles state.
​
Each puzzle was split up in a hierarchical manner with a puzzle able to hold multiple puzzle component groups, which individually would hold onto puzzle components. Each component level would have requirements that must be met in order to let their components run or not. This meant that the execution of some components would be put off till other condition groups had finished.
​
For more modular control over puzzles, I created an abstract ScriptableObject class labeled PuzzleLogic. This provided abstract methods for derived types to run logic within a puzzle in a modular fashion. One example might be a puzzle where the goal is to shoot all targets within a time frame. Once the first target is shot a timer counts down on the puzzle logic called "CompleteWithinSeconds" which is attached to the component group. If all of the groups components didn't finish their conditions once the timer finishes, the group will reset the condition of all it's members ready to try again.
Puzzle Documentation:
Puzzle Component Group:
Puzzle:
​