Tomebound
Project Brief
The second project of semester 1 was a group client project where each student selected a project from a range provided by external clients. I elected to contribute to the development of 'Tomebound', a project assigned by our client, Rob Blofield.
Role
As one of the five game developers on the team, my responsibilities encompassed various tasks over the 12-week project. These efforts, combined with the contributions of my colleagues, aimed to produce a functional prototype for the game by the end of Semester 1.
​
Assigned tasks:​
01
Drone AI Movement
The first task I was given for this project was to create a drone AI movement mechanism that allowed the player to interact with selected objects and move them to a chosen destination.
This task was both an exciting challenge and a learning experience. Despite dedicating the first four weeks of the semester to this endeavor, the complexity of the task posed unforeseen challenges that, unfortunately led to the realisation of my limitations.
​
My initial approach involved the use of A* pathfinding, a renowned algorithm used for its efficiency in generating optimal routes. I did my best to integrate the A* algorithm into the game objects movement logic, expecting a fluid interaction with player-selected objects. However, during real-time gameplay, the need for dynamic recalculations in response to object movement led to unexpected issues. The A* implementation, while successful in static scenarios, was not successful when applied to the changing, player-driven environment of the game, and was something I simply did not have the time to learn and implement at it's full potential.
​
Subsequently, I explored Unity's built-in pathfinding system using NavMesh, anticipating that its integration in Unity would alleviate the challenges I encountered with A* pathfinding. While using Unity's NavMesh provided a slightly simplified logic and implementation in comparison to A* pathfinding, adapting it to meet the requirements of real-time object selection and movement proved to be a persistent hurdle. The system I was trying to use struggled to accommodate the unpredictable changes in the game objects transform, resulting in pathfinding solutions that were not fulfilling all the necessary requirements of being adaptable to any game map or player input.
Despite these efforts, completing this task remained out of reach within the project's timeframe. While the ultimate goal of a fully functional AI movement mechanism remains unrealised, this experience was an invaluable exploration of A* pathfinding, Unity's NavMesh, and the complexities of dynamic object interaction within Unity. This experience has equipped me with a deeper understanding of the challenges inherent in real-world game development scenarios and reinforces my commitment to continued growth within the field.
02
Lava stepping stone
The next task I undertook was the design and implementation of a lava stepping stone mechanism. This feature allows a block, when pushed into a lava pit, to transform into a stepping stone. This, in turn, enables players to safely cross the lava. The absence of a stepping stone block serves as a gameplay restriction, preventing the player from crossing the lava.
.png)
I began the process of creating this mechanism by looking at the components of the game objects in use as well as which components needed to be applied and using pseudocode in order to map out what the mechanism needed step by step.
(Final pseudocode shown to the left)
I wrote the script with flexibility in mind, and wanted to ensure that it could be quickly and efficiently adapted if required. In early iterations of the script, instead of using tags the script would check for the names of the GameObjects directly, however I felt that this was less flexible and could potentially break the mechanism should any GameObjects be renamed.
This script is applied to the lava GameObject itself, which is composed of three key elements .
-
'Lava Block' - The lava block prefab which is the parent object and holds the script as well as a small box collider which triggers the mechanism.
-
'Lava Box Collider' - An empty child game object with a box collider applied - this prevents the player moving across the lava in the absence of the stepping stone.
-
'Stepping Stone' - And lastly prefab of the stepping stone itself - this GameObject is by default deactivated on start.
_edited.jpg)

When an object with the tag "Block" (this can be changed in the inspector as required) enters the box collider on the 'Lava Block', the 'Lava Box Collider' is deactivated and the 'Stepping Stone' is activated allowing the player to cross safely.
Each individual component is assigned in the inspector, to it's corresponding name in the script.
03
Mimic Enemy
The next task I undertook for this project was to create an enemy behaviour that mimics and mirrors all the movements of the player. The player would need to tactically use it's own movement in combination with the environment in order to cause the enemy to fall into a trap e.g. lava.
I began once again by breaking down the mechanism into it's core components and what it needed to do. I ascertained that there were several things the script needed to keep track of in order to function correctly -
-
Keeping track of the two player characters in the game
-
Remember the position of the player characters
-
To check which player character is moving and and mimic that character instead
-
And to assess which direction the player was moving in, in order to invert the enemy movement if moving on the Z axis​
Early iterations of this script and testing found that the most effective method of enemy movement for this mechanism was to move the enemy based on the players movement as opposed to the players input - due to the two player character's being locked to one of either the X or Z axis. If the enemy movement was based on player input alone, the enemy could move in whichever direction was input despite the each player character only being able to move on one axis unless they are both holding the tome, which allows movement on both axis.
​
The GameObjects for each respective player character are assigned in the inspector - this is checked within the start method after which the initial player transform is set. The speed can also be edited within the inspector.
​


The script then checks whether the isFollowing bool is true - which in future development will be dependent on other in game factors such as which floor the player character is on.
If isFollowing is true then the direction the player has moved is calculated by subtracting the previous player position from the current player position. Following this, movement along the z-axis is inverted, so that the enemy moves towards the player as they move forward.
It is then necessary to determine the target position for the enemy by adding the movement direction to its current position allowing movement of the enemy towards the target position using linear interpolation. It is important to then update the previous player position for the next frame, otherwise as I quickly realised during testing, the movement direction would be calculated using the original starting point of the player in each frame.
Finally the script checks which player character is moving and it switches to follow the other character if it is currently moving to allow for dynamic control of the enemy behaviour.
Destroyed by lava
The DestroyByLava script is a very simple script that is used to deactivate the game object it is attached to when another collider with the tag 'Lava' enters its trigger collider.
This allows for enemies or other in game objects such as crates etc to be destroyed/die in game when they come in contact with the lava block collider, which has a 'Lava' tag assigned to it.
This script can of course be modified to apply to objects other than lava that may cause their destruction in game.

04
Patrolling enemy
The final mechanic I completed for this project during semester one was for a patrolling fiery axolotl enemy.
This enemy patrols a row of the tiles in game (left to right), and when the player reaches the row of tiles in the adjacent row, the enemy rushes to stand in front of the player and block the way whilst growing in size. The only way to move past this enemy is to drop a bucket of water onto the enemy, thus freezing it for a number of seconds allowing the player to move past before it awakens.
This mechanic proved to be the most challenging of the three that I completed due to the number of different methods requiring efficient interactions. As I have done previously, I broke down the mechanic into a number of sections to tackle one by one. Below is a walkthrough of the the main methods within this script.​
​Patrol & Player Detection:
​
The enemy moves between a start point and an end point using Mathf.PingPong to create a smooth back-and-forth motion.
​
The Patrol method handles the enemy's patrolling behaviour, updating its position based on the implemented ping-pong movement pattern.

The script then uses a detection radius (defined by detectionRadiusObject, detectionRadiusX, and detectionRadiusY) to detect the player within a specified range.
When the player is detected, the enemy stops patrolling and starts blocking the player's path.

Blocking Player:
The BlockPlayer method moves the enemy towards the player, effectively blocking their path.
If the player moves out of the detection radius, the enemy resumes patrolling.
The script interacts with a GameManager, which has a controllingObject property. It uses this object's position to gauge the current selected players position.
After the completion of the full script and during some testing and demonstration, an issue was found (shown right) that results in the enemy repeatedly and rapidly switching between its patrolling and blocking states. This is caused by one player character moving out of the detection radius whilst the other is still in it. The enemy is under these conditions unable to distinguish which state it needs to be in
resulting in the displayed behaviour. This is something I have been unable to resolve during this semester due to time constraints, however it is something I hope to work on and fix in semester 2.

Freezing Mechanism:
If the enemy comes into contact with an object tagged as 'Water', it triggers a sequence of freeze enemy methods changing the behaviour of the enemy temporarily.
In the FreezeEnemy method, the code initiates the freezing process by setting the 'isFrozen' flag and recording the current time. The enemy then visually transforms into a larger version of itself, indicating its frozen state. Simultaneously, the code stops the enemy from patrolling and blocking, making it temporarily inactive by anchoring it in place. I added a Debug.Log which confirms the enemy is frozen and the triggering 'Water' object is deactivated.
In the CheckFrozenDuration method, the code tracks how long the enemy has been frozen. When the predefined 'freezeDuration' is exceeded, the enemy
resumes the relevant behaviour based on the player's position. If the player is within the detection radius, the enemy resumes blocking; otherwise, it returns to patrolling. The end of the frozen state is logged, indicating that the enemy is has returned to engaging with the in game world as normal.
Object States and Toggle:
SetObjectStates initialises the state of the enemy's visual representation (blocking or patrolling).
ToggleObjectStates switches between blocking and patrolling visual states.

The key lesson I have learnt whilst completing this task is that debugging and testing is key to successful practice. Despite there being a lot of trial and error along the way, and whilst I was unable to resolve every problem encountered on my own, I have learnt a great deal of new skills and have built upon the ability to put code together with purpose.
05
Documentation
Finally, to close off this project and handover my work for semester one over to my client, I completed the documentation for all the work I carried out. I learnt to use markdown for the first time whilst completing this, and although it took some time to decrypt, I now feel relatively confident in it's use and consider it to be a valuable tool which I look forward to using and building upon in the future.
​
The completed documentation will allow anyone working on the project in the future to work with and understand my code and any other work I have done. I also hope to personally continue to work on this project and improve the systems I have put into place and will be able to refer to these documents as a reference to build upon and update them as required.
​
The links for each of these pages are shown below - full scripts for each of the aforementioned sections can be found within this documentation.
​
​