A Lion's Life - V0.6: New AI Sound & Stealth System


Figure 1: Title art by our 2D & Concept Artist, Tyler Geraldes.


PDF Version


SOUND & STEALTH SYSTEM FOR A LION'S LIFE

Figure 2: The player is charging up their pounce

My goal with this system was to add more interesting interactions and approaches to hunting for prey.  In this game, the player’s hunger will slowly deplete over time, and they will need to hunt for food to survive.

This was a challenge from not only a programming standpoint, but also a design one.  Along the way I made a few changes to how the AI reacts and when their state changes.  This was to make it more realistic and add an extra layer of depth to make the system more interesting. This was my first time properly programming an AI using C#.  I learned how to use switches to create proper state changes and how to use mixers for sound effects.


Original Intention

Figure 3: The purple in the bottom bubble to the left, reflects how loud the player is being.

I wanted to use Breath of The Wild (BOTW) as inspiration for this system.  In BOTW, enemies have an aware and alerted state.  If the player is heard, the enemy will look around for them.  If the player is seen, the enemy will attack them.  Whether or not player can be heard is dependent on how close they are to an enemy and how loud they are being.  The player has a stealth stat that can make them quieter when enhanced with certain items.

The following diagrams detail how the AI was originally going to function.  In Figure 5, the player is making noise by walking but is outside of the AI’s hearing range (yellow) and vision cone (red).  The AI has no reaction.  In Figure 6, the player is inside of both hearing range and vision cone.  The bush blocks the AI from being able to see the player.  Since the AI can still hear normally and the player is making noise, they are heard.  The AI becomes alert and turns towards the player.  Lastly, in Figure 7, The player has entered the AI’s vision cone and is not covered by a bush.  The AI becomes aware that the player is there, turns in the opposite direction, and flees.  Aware is a hard state and alert is a soft state.  This means the AI can leave the alert state to go into the aware state but cannot go from aware to alert until aware is finished executing.  In this case, the aware state makes the AI turn and flee for a set amount of time.

Figure 4: Legend for following diagrams.


Figure 5: The player is out of the AI’s hearing range and vision cone. So, the AI doesn’t know the player is there.


Figure 6: The player is rustling in a bush. They are within the AI’s sound and vision cone. The AI is alert and turns towards the sound’s source. Even though the player is inside the AI’s vision cone, the bush gives them cover.


Figure 7: The player has been seen. Therefore, the AI is aware that the player is there. The AI turns and runs away from the player.


PROJECT MILESTONE #1

Figure 8: The player is producing sound by walking.

This milestone includes all the work I did from the beginning of the project up until right before Code Review #1.  My goal was to create a working sound controller and add all sounds the player would create.  This included different footstep sounds depending on how fast the player was moving.  This was to set me up for future milestones where I could hook up the AI reactions to these sounds.


New Features & Changes

Created sound controller.  (SoundController script.)

  • The sound controller I put together gathers all the AudioSources it finds in the scene so there is no longer a need to drag and drop individual sounds into the controller.  I was using drag and drop when I was first starting just to keep it simple and make sure the sounds were playing correctly.  Having the script gather the clips on its own makes for a much more efficient setup.  The script loops to search for the specified sound clip each time it is requested.  This is done by entering the name of the sound clip.  This script exists as a singleton as it is a manager and there is no need for more than one.

Added sound effects in game.

  • The sound effects added include quick scratching, pouncing, eating, drinking, walking, sprinting, sprinting x2, and sprinting x4.

Player makes different footstep sounds based on how fast they are moving. (CheckForSounds() function in PlayerMovement script.)

  • The CheckForSounds() function in the player movement script detects how fast player is moving and then plays the appropriate walking/sprinting sound.


Known Bugs & Issues

Figure 9: The sound controller has separate play and stop functions for each sound effect, this is inefficient and has repeated code.

Sound controller requires a play and stop function for each sound effect.

  • CheckForSounds() function detects how fast player is moving and then plays the appropriate walking/sprinting sound.

Figure 10: The sound clips were written in the script as comments so I can reference them to put into the start and stop functions.

Hard coded sound clip names.

  • Changing the name of the sound clip in the inspector will mean that the sound controller will no longer be able to play the requested sound.  The clip names are entered into the sound controller commented out so it’s easy to see them and enter them into the play and stop functions.

Figure 11: Each AudioSource component was on it's won individual gameobject.

Cluttered hierarchy.

  • Each audio source takes up its own game object.  This was from when the sound controller was relying on drag and dropping AudioSources to access them.  This is taking up unnecessary space, as the sound controller no longer requires drag and drop.

Figure 12: Walking/sprinting states are not defined clearly enough, thus there are issues with sounds overlapping.

Camera effecting starting and stopping of walking/sprinting sounds.

  • CheckForSounds() function does not work entirely as intended.  When the player moves the camera, the sound stops and starts playing.  I’m having trouble detecting what state the player is in (walking, sprinting, etc.).  I have control over the speed but not the magnitude, which is the value I need to access to have the state changes be accurate.


Next Steps

Condense the sound controller.

  • Rather than having play and stop functions for each sound effect, I am planning to condense it down to one play and one stop function that take the sound clip name as an argument.  This way there is no repeated code.
  • I am going to create constant string variables for the sound clip names.  This way, if the name changes in the inspector, it will be much easier to update the sound controller.
  • When calling a sound clip function, the constant string variable will be passed in rather than the string name each time (avoiding hard coding).

 Remove hard coded sound clip names.

  • I am going to create constant string variables for the sound clip names.  This way, if the name changes in the inspector, it will be much easier to update the sound controller.
  • When calling a sound clip function, the constant string variable will be passed in rather than the string name each time (avoiding hard coding).

Condense sound objects in the in the hierarchy.

  • Empty objects that contain one AudioSource are no longer needed.
  • The sound controller does not use drag and drop anymore, so all AudioSource components can live on one empty object.  (Drag and drop wouldn’t know which AudioSource to use, which is why it was originally set up this way.)
  • Potential solutions include creating ranges between movement mode, and/or adding the movement sounds to a mixer.

 Look into solutions to fix camera sound bug.

  • Potential solutions include creating ranges between movement mode, and/or adding the movement sounds to a mixer.


PROJECT MILESTONE #1.1

I took the feedback from Code Review #1 and began to make some changes.  The goal was to make my code more efficient, legible, and easier to use and expand upon.  The main area of focus was the sound controller, as there were many areas identified that were inefficient.


New Features & Changes

Figure 13: There is now only one play and stop function. They take a string that is the sound clip name.

Condensed sound controller.

  • Sound controller now only has one play and one stop function, rather than one per sound. Takes the sound name as an argument so it knows what sound to play.

Figure 14: The sound clip names are stored as constant strings in the sound controller. So, when another script needs to play a sound, they can reference the name of the variable and not have to type in a specific name.

Change to constant strings.

  • Sound clip names are now stored as constant strings. The variable names can be passed into the play and stop functions rather than the strings name itself to avoid hard coding.

Figure 15: AudioSource components can now all live on one gameobject.

Condensed where AudioSources live in the hierarchy.

  • Now all player AudioSource components are on one game object attached to the player. Makes the hierarchy cleaner and easier to read/find things.


Next Steps

Clean up player walking sound modes (camera bug still not fixed).

  • Need proper distinguished movement modes.
  • Potential solutions include creating ranges between movement mode, and/or adding the movement sounds to a mixer.
  • Add wire Gizmos to be able to visualize how far the AI can see and hear.

 Add Gizmos on AI for vision and hearing ranges.

  • Add wire Gizmos to be able to visualize how far the AI can see and hear.


PROJECT MILESTONE #2

Figure 16: The player is making noise outside of the AI's hearing range and is outside the vision cone. Therefore, the AI has no reaction.

This milestone includes work done from the last update (1.1) up until Code Review #2.  At this point the sound controller has already been refined and it’s time to start diving into the AI code.  The extent of AI code I had going into this was simply, “if the player is within x distance, turn in the opposite direction and flee”.  The goal was to take this base functionality and hook it up to execute with the desired condition.  I also wanted to fix any bugs, as they create issues that prevent further progress.

New Features & Changes

Figure 17: AI has Gizmo wireframe (yellow) as hearing range and game object for vision cone (blue).

AI has Gizmos attached to visualize hearing range and vision cone.

  • I used a wireframe Gizmo to represent how far the AI’s hearing range is.
  • I created a quick cone model to use as the vision cone, as I was having difficulties with drawing one using Gizmos.

 AI can see player and run away in response.

  • When the player collider enters the AI’s vision cone collider, the AI turns in the opposite direction and flees.

Figure 18: CheckForSounds() function in the player movement script. It has been updated to include better ways to check what sound should be playing.

Added proper state changes to movement modes.

  • Now the top and the bottom if statement conditions check for a range rather than an exact value.
  • This is using the speed value since it’s difficult to know what the magnitude is at different speeds until the player is going at that speed (makes it difficult to check ranges).  Since sprinting has its own set speed variable, it’s using that to check and keep everything relative to the speed values.
  • Sprinting at the various different speeds are kept tack of elsewhere, where the changing of speed is happening.  I found it was the easiest way to get the information CheckForSounds() needs.

 Fixed issues where camera movement stops and starts movement sound clips.

  • After updating the checks in CheckForSounds(), the issue where the camera movement starts and stops sound clips ceased to exist.

Figure 19: This function decides what the clamp should be depending on how fast the player is moving.

Magnitude clamps have been put in place for different movement modes.

  • Depending on what movement mode the player is in the clamp will change.
  • The clamp is checked and applied where the movement is calculated.

Figure 20: The clamp is determined and applied where the movement code is calculated.

Fixed issues where player moves faster on diagonals.

  • After adding clamps, it fixed the issue of the player moving faster on diagonals (ex. Holding A and W at the same time or D and W).


Known Bugs & Issues

Figure 22: After the loudness of a sound has been calculated, it gets saved to an array. The name of the clip gets saved to a seperate array and I'm relying on them being in the same position for tracking.

How to save the name and loudness of each sound clip in something like a Vector.

  • I have the samples and loudness saved to separate arrays and would like to store these values together in something like a Vector (ex. Vector3.sampleName, like Vector3.x).
  • I was suggested to use a class, which I didn’t think about.  I have worked with custom classes in Python but never in C#.


Figure 23: AI was not fleeing over time and stopped as soon as it could no longer see the player.

How to get my AI to continue fleeing even after it can no longer see the player (flee for a period of time).

  • Here, no matter what I changed regarding how long the AI should flee for, no difference was observed.
  • I was suggested to use a coroutine to make this continue to run after the play is no longer in sight.
  • At this point in time, I did not fully understand why this function was only running on one frame, and it did not have to do with weather I used a coroutine or not.  See: Figure 32.


Figure 24: Trying to figure out the best way to add each clip loudness together with artificial alterations for design reasons.

How to figure out what sounds are playing, what needs to be altered and add them to a total.

  • I wasn’t sure how to go about getting the total loudness.  This was especially and issue because it needs to not include the background music that was in an unknown position within the sounds grabbed by the sound controller.
  • I also wanted to artificially alter the loudness of some clips for design purposes (ex. Walking and sprinting may be the same loudness but sprinting needs to be louder than walking to have the desired effect).
  • I was suggested adding a parameter to the custom class (that would store the name and loudness) for how much I needed to artificially alter. I ended up having the functionality for adding the sounds together in this class as well.  See: Figure 32.


Next Steps

Create custom class to store the data I need.

  • This is exactly what I need to more efficiently store the data I need.

Try a coroutine for making the AI flee over a period of time.

  • This could be a solution to this problem, so I’m willing to give it a try and see what happens.


PROJECT MILESTONE #3

Figure 25: AI now has two different hearing ranges and different rations to the player making sound above or below 0.005.

Here I made a lot of changes and refined the design of how the AI functions.  I created a finite state machine to better illustrate what I wanted.  I needed these details because I now had the measurement I was going to use to figure out when the AI should react and how it should react.

Figure 26: Finite state machine for how and when the AI should be reacting to the player.

New Features & Changes

Figure 27: The sound controller calls the custom class to add it to the total loudness.

Fixed sound controller to calculate and check for the total loudness of the player.

  • The custom class is called to add the total loudness if the sound is playing.  The total gets reset and rechecked each frame.

Figure 28: The custom class accounts for any artificial boost or reduction in loudness.

Created custom class to store information on sound clips.

  • The custom class stores the name loudness and how much the clip needs to be modified by.

Sounds that need to be artificially boosted (currently the different sprinting clips), are with the combined use of the custom class and the sound controller.

Figure 29: Sound mixer the sample clips live on.

Audio sources have been put on mixers to fix them playing over each other when undesired.


Known Bugs & Issues

Figure 30: AI hearing collision code.

Colliders overlapping preventing AI from properly changing states.

  • It was suggested I use distance checks to fix this issue.
  • I took this advice, and it fixed the issue.


Figure 31: AI flee code.

AI still stopping sooner than desired.

  • I was recommended cutting the coroutine, as I did not need to wait for anything.
  • At this point in time, I still did not fully understand why this function was only running on one frame, and it did not have to do with whether I used a coroutine or not.


Next Steps

Switch from colliders to distance checks.

Fix AI stopping sooner than desired.


PROJECT MILESTONE #3.1

New Features

Ai properly reacts to hearing the player at different ranges and noise levels.  (Using distance check instead of colliders.)

Removed coroutines.

Figure 32: AI can now flee over time using an if statement rather than a loop.

AI fleeing fixed.

  • The issue was that because I was using a for loop, the AI would complete the Flee From function in one frame instead of over time.  To fix this issue, I changed it to use an if/else statement, and now it works!

Figure 33: AI now uses switches to better determine what state it's in (previously used a bool).

AI now uses switches to fix problem where it doesn't know what state it should be in (or states overriding).

  • Turning is a soft state that returns to default each frame.
  • Default state acts as a reset and searches for what state the AI should be in.

Figure 34: AI cannot flee from seeing the player if they are in cover.

Cover implemented.

  • The player can now hide in trigger collider objects that are tagged with “Cover”.
  • The AI cannot flee due to vision reason when the player is hidden in cover.
  • The AI can still execute all hearing actions with no alterations, however.

Files

A Lion's Life - V0.6.zip 37 MB
Jan 25, 2022

Get A Lion's Life

Leave a comment

Log in with itch.io to leave a comment.