PRE-ALPHA III / PRE-BETA I

Not seeing results from the effort you’re putting into a project can be difficult and demoralizing. Each week, I accomplish the tasks assigned to me, and each week, my scripts have worked well in standalone test scenes. I always look forward to the studio’s “State of the Game” videos to see how my work and that of others in my pod have come together to form a cohesive enemy. Unfortunately, during these videos, it quickly becomes apparent that while our scripts work well individually, combined they are a bit of a mess. Multiple scripts interact with the same components yet do not interact with each other, creating competition between whose functionality, if any, should persist. Realizing that there were only six weeks left in the project and that we didn't yet have a full-fledged enemy, I spent a lot of time trying to resolve this issue.

The tasks that I was directly assigned this sprint included a couple of fixes to my charger patrol scripts and finalizing the shooter behavior (excluding its ATTACKING state). For the patrol scripts, I was first asked to find the cause of mass framerate drops and occasional Unity crashes when too many chargers were placed in a scene. My instinct was that because GroundDirectionChooser.cs continues to search for new positions to move until a valid location is found, if there are no valid locations, then the script will loop indefinitely and cause a hit to game performance. I altered the search to stop looking for a destination after looping over a 360 degree arc (a positive change), but this did not fix the issue. I then wondered if perhaps GroundDirectionChooser.cs was getting called too frequently in the case that a location could not be found. To fix this, after searching in the full 360 degree arc (later changed to a modifiable parameter along with a boolean to determine bias towards or away from the player), I halved the size of the given travel distance and conducted another search, continuing this pattern until a location could be found. This was not the correct solution either, but I did incorporate the idea regardless – a serialized minimumTravelDistance was added (the previous default distance was changed from minimumTravelDistance to maximumTravelDistance) to determine when this halving should stop. My third attempt at a fix proved to be the correct one. Because Update() is called every frame, loops should never be used within this function, as Unity will wait for these loops to be completed before moving to the next frame. It didn’t occur to me that even though I did not have a loop in update, I did call GroundDirectionChooser every frame, which itself has a while loop. Adding a function call within Update() to handle GroundDirectionChooser’s call rather than Update() itself fixed the problem immediately.

Code for finding a new destination

Finding a new destination outside of Update() with the GroundDirectionChooser.cs changes

The other bug I was asked to fix with the charger patrols was found when testing the charger in a non-test scene. The charger frequently became stuck on the edges of NavMesh areas and refused to move once this occurred. This one took me a while to understand – I knew that the issue likely had something to do with the lack of walls in the non-test scene, but I had followed the design documentation and everything was executed as expected. I combed through the documentation and my code, and once I realized the issue, I was surprised I hadn’t noticed it sooner. The documentation indicates the use of raycasting to determine a valid location to move. However, if there is no obstacle in the charger’s way, the raycast will never hit anything, and the charger will attempt to move to a location not on the NavMesh. This was corrected with a NavMesh.SamplePosition() on the new location as well as the raycast (if either the location is not on a NavMesh OR an obstacle is in the way, do not move that direction).

GroundDirectionChooser code using NavMesh

Current iteration of GroundDirectionChooser.cs with new parameters and NavMesh.SamplePosition()

Adding behavior to ShooterDriver.cs (previously ShooterBehavior.cs) was an enormous task, and one that made me question whether there is a better way to handle state machines than I have with both this enemy and the charger. The complications lie in where the state behavior is called, namely Update(). This seems like a logical choice to determine when to transition into a new state, as I can simply check a list of parameters every frame. However, for the actual behavior, calling it every frame does not allow the previous behavior to finish before being overwritten. This resulted in a large number of additional checks every frame which was not ideal. I also was a bit unhappy with the number of global variables present (i.e. searchTimer should not exist outside of SEARCHING and isShooting should not exist outside of ATTACKING) and the sheer size of the script. I generally try to avoid large scripts in Unity to increase reusability and modularity, but this script ended up being over 300 lines long due the large number of variables shared between states – I’ll need to re-evaluate how to better separate concerns going into development of the third enemy. A new script was added as well – EnemyIdle.cs – that functions almost identically to the PATROL state of PatrolBehavior.cs, with a few exceptions. This was done per recommendation of the studio to get something up and running, but in another scenario with more available time, I would have ideally found a way to use EnemyIdle.cs within PatrolBehavior as well to prevent duplicate code. All that said, the behavior itself ended up working very well in the end. I put extensive work into understanding how the node detection scripts are handled (nodes in this case are NavMeshLinks that the shooter can use to jump to a new platform – a very common occurrence within this enemy’s behavior), coordinating with the other enemy programmers, and testing each state and the transitions between them.

Shooter enemy using NavMeshLinks while on patrol

Shooter enemy selecting NavMeshLinks while on patrol

The largest unplanned initiative that I undertook was finding a way to allow the charger scripts to accurately interact with one another. Previously, the charger functioned almost identically to when only the patrol scripts were active – the attack script commands were being overwritten by the setting of NavMeshAgent.destination within PatrolBehavior.cs. It required combing through the aggro detection script and two attack scripts, making slight modifications along the way, but I was able to make the charger work as intended within the confines of a test scene. This was primarily done using pre-existing Unity events to determine when PatrolBehavior.cs should and should not perform its behavior. I also fixed a bug within AttackBehavior.cs that invoked a charge attack every frame, rather than just once per attack state. Overall, while both this enemy and the shooter need additional work, I am very happy with the amount of progress that was made on both of these enemies throughout this sprint. Pulling them out of “development limbo” into a mostly functional state has renewed my excitement for the project.


Time breakdown:

Weekly studio meetings – 2 hours
Additional enemy pod meetings – 1.75 hours
PatrolBehavior.cs and GroundDirectionChooser.cs fixes – 3.5 hours
ShooterBehavior.cs and EnemyIdle.cs – 9 hours
Integrating charger scripts – 3 hours
Total time spent: 19.25 hours

Previous
Previous

PRE-BETA II

Next
Next

PRE-ALPHA II