When processing the death sequence, the explosion audio clip is not being played. This happened after Ben suggested stopping the “thrust” audio clip before playing the “explosion” audio clip. In short, my code performs the following sequence, but the explosion audio clip is not being played.
audioSource.Stop();
audioSource.PlayOneShot(explosionAudio);
During debugging and troubleshooting, I found a few issues.
-
The value of state member variable seems to be cached and not changing
-
[SerializeField] explosionAudio is being set to null outside of my C# script
What am I doing wrong? Is there a bug with C# or Unity or both?
I am using Unity version 2019.3.11f1 (ceef2d848e70) Personal
I thought that this may be a thread synchronization issue, and I added ‘lock(this)’ to synchronize setting the value of state, but this did’t help. I also added volatile to the state declaration ‘volatile State state’, but this didn’t work either.
Below is my code.
using System;
using UnityEngine;
using UnityEngine.SceneManagement;
public class Rocket : MonoBehaviour
{
// rotation control system
[SerializeField] float rcsThrust = 10f;
[SerializeField] float mainThrust = 100f;
[SerializeField] AudioClip mainEngineAudio;
[SerializeField] AudioClip explosionAudio;
[SerializeField] AudioClip finishedAudio;
enum State
{
Alive,
Dying,
Transcending
}
volatile State state;
int currentPlayLevel = 0;
Rigidbody rigidBody;
AudioSource audioSource;
// Start is called before the first frame update
void Start()
{
rigidBody = GetComponent<Rigidbody>();
audioSource = GetComponent<AudioSource>();
ResetGameEnvironment();
}
// Update is called once per frame
void Update()
{
if (state == State.Alive )
{
HandleThrustInput();
HandleRotateInput();
}
}
// Reports when collisions have occurred
void OnCollisionEnter(Collision collision)
{
Debug.Log("OnCollision - ");
if (state != State.Alive)
{
return;
}
switch (collision.gameObject.tag)
{
case "Friendly":
// do nothing
break;
case "Finish":
FinishAndTransitionToNextLevelLevel();
break;
default:
DieAndRestartLevel();
break;
}
}
private void FinishAndTransitionToNextLevelLevel()
{
if (state != State.Transcending)
{
Debug.Log("StartFinishedSequence - state = State.Transcending");
state = State.Transcending;
if (audioSource.isPlaying)
{
Debug.Log("StartFinishedSequence - audioSource.Stop()");
audioSource.Stop();
}
audioSource.PlayOneShot(finishedAudio);
// LoadNextLevel after 1 second
Invoke("LoadNextLevel", 1f); // todo parameterize delay time
}
else
{
Debug.Log("StartFinishedSequence state already State.Transcending");
}
}
private void DieAndRestartLevel()
{
Debug.Log("Starting Death Sequence state: " + state );
// Variable state is not synchronized when it is updated.
// OnCollisionEnter may be called multiple times
// Through race condition, this may be called again just before
// state is set to State.Dying
if (state != State.Dying)
{
Debug.Log("StartDeathSequence - gettting lock");
lock (this)
{
Debug.Log("StartDeathSequence - got lock");
if (state != State.Dying)
{
Debug.Log("StartDeathSequence - state = State.Dying");
state = State.Dying;
// If mainEngineAudio is playing,
// stop playing it.
if (audioSource.isPlaying)
{
Debug.Log("StartDeathSequence - audioSource.Stop()");
audioSource.Stop();
}
if (explosionAudio != null)
{
Debug.LogError("Playing explosionAudio");
audioSource.PlayOneShot(explosionAudio);
}
else
{
// This may be set to null because the scene could have been
// reloaded because of a race condition
Debug.LogError("explosionAudio is null!!");
}
// LoadNextLevel after 1 second
Invoke("ReloadLevel", 1f); // todo parameterize delay time
}
}
}
else
{
Debug.Log("StartDeathSequence state already State.Dying");
}
}
private void ReloadLevel()
{
LoadLevel(currentPlayLevel);
}
private void LoadNextLevel()
{
currentPlayLevel++;
LoadLevel(currentPlayLevel);
}
private void LoadLevel(int levelId)
{
SceneManager.LoadScene(levelId);
}
private void ResetGameEnvironment()
{
Debug.Log("ResetGameEnvironment - state = State.Alive");
state = State.Alive;
// If any audio is playing,
// stop playing audio.
if (audioSource.isPlaying)
{
Debug.Log("ResetGameEnvironment - audioSource.Stop()");
audioSource.Stop();
}
}
private void HandleThrustInput()
{
float thrustSpeed = mainThrust * Time.deltaTime;
if (Input.GetKey(KeyCode.Space))
{
if (!audioSource.isPlaying)
{
if (mainEngineAudio != null)
{
audioSource.PlayOneShot(mainEngineAudio);
}
}
// apply force in the direction rocket is facing
rigidBody.AddRelativeForce(Vector3.up * thrustSpeed);
}
if (Input.GetKeyUp(KeyCode.Space))
{
if (audioSource.isPlaying)
{
Debug.Log("RespondToThrustInput - audioSource.Stop()");
audioSource.Stop();
}
}
}
private void HandleRotateInput()
{
float rotationSpeed = rcsThrust * Time.deltaTime;
// Take manual control of rotation
rigidBody.freezeRotation = true;
if (Input.GetKey(KeyCode.A))
{
transform.Rotate(Vector3.forward * rotationSpeed);
}
else if (Input.GetKey(KeyCode.S))
{
transform.Rotate(-Vector3.forward * rotationSpeed);
}
// return rotation control back to Physics engine
rigidBody.freezeRotation = false;
}
}
Below is the output from the Unity Editor console:
Note that even though state is being set to State.Dying, somehow, on subsequent calls to OnCollisionEnter(), state’s value is changed back to State.Alive.
-------------------------------------------------------------------
ResetGameEnvironment - state = State.Alive
UnityEngine.Debug:Log(Object)
Rocket:ResetGameEnvironment() (at Assets/Scenes/Rocket.cs:155)
Rocket:Start() (at Assets/Scenes/Rocket.cs:32)
-------------------------------------------------------------------
ResetGameEnvironment - audioSource.Stop()
UnityEngine.Debug:Log(Object)
Rocket:ResetGameEnvironment() (at Assets/Scenes/Rocket.cs:161)
Rocket:Start() (at Assets/Scenes/Rocket.cs:32)
-------------------------------------------------------------------
ResetGameEnvironment - state = State.Alive
UnityEngine.Debug:Log(Object)
Rocket:ResetGameEnvironment() (at Assets/Scenes/Rocket.cs:155)
Rocket:Start() (at Assets/Scenes/Rocket.cs:32)
-------------------------------------------------------------------
OnCollision -
UnityEngine.Debug:Log(Object)
Rocket:OnCollisionEnter(Collision) (at Assets/Scenes/Rocket.cs:48)
-------------------------------------------------------------------
OnCollision -
UnityEngine.Debug:Log(Object)
Rocket:OnCollisionEnter(Collision) (at Assets/Scenes/Rocket.cs:48)
-------------------------------------------------------------------
OnCollision -
UnityEngine.Debug:Log(Object)
Rocket:OnCollisionEnter(Collision) (at Assets/Scenes/Rocket.cs:48)
-------------------------------------------------------------------
OnCollision -
UnityEngine.Debug:Log(Object)
Rocket:OnCollisionEnter(Collision) (at Assets/Scenes/Rocket.cs:48)
-------------------------------------------------------------------
OnCollision -
UnityEngine.Debug:Log(Object)
Rocket:OnCollisionEnter(Collision) (at Assets/Scenes/Rocket.cs:48)
-------------------------------------------------------------------
Starting Death Sequence state: Alive
UnityEngine.Debug:Log(Object)
Rocket:DieAndRestartLevel() (at Assets/Scenes/Rocket.cs:91)
Rocket:OnCollisionEnter(Collision) (at Assets/Scenes/Rocket.cs:63)
-------------------------------------------------------------------
StartDeathSequence - gettting lock
UnityEngine.Debug:Log(Object)
Rocket:DieAndRestartLevel() (at Assets/Scenes/Rocket.cs:99)
Rocket:OnCollisionEnter(Collision) (at Assets/Scenes/Rocket.cs:63)
-------------------------------------------------------------------
StartDeathSequence - got lock
UnityEngine.Debug:Log(Object)
Rocket:DieAndRestartLevel() (at Assets/Scenes/Rocket.cs:102)
Rocket:OnCollisionEnter(Collision) (at Assets/Scenes/Rocket.cs:63)
-------------------------------------------------------------------
StartDeathSequence - state = State.Dying
UnityEngine.Debug:Log(Object)
Rocket:DieAndRestartLevel() (at Assets/Scenes/Rocket.cs:105)
Rocket:OnCollisionEnter(Collision) (at Assets/Scenes/Rocket.cs:63)
-------------------------------------------------------------------
StartDeathSequence - audioSource.Stop()
UnityEngine.Debug:Log(Object)
Rocket:DieAndRestartLevel() (at Assets/Scenes/Rocket.cs:112)
Rocket:OnCollisionEnter(Collision) (at Assets/Scenes/Rocket.cs:63)
-------------------------------------------------------------------
Playing explosionAudio
UnityEngine.Debug:LogError(Object)
Rocket:DieAndRestartLevel() (at Assets/Scenes/Rocket.cs:117)
Rocket:OnCollisionEnter(Collision) (at Assets/Scenes/Rocket.cs:63)
-------------------------------------------------------------------
OnCollision -
UnityEngine.Debug:Log(Object)
Rocket:OnCollisionEnter(Collision) (at Assets/Scenes/Rocket.cs:48)
-------------------------------------------------------------------
Starting Death Sequence state: Alive
UnityEngine.Debug:Log(Object)
Rocket:DieAndRestartLevel() (at Assets/Scenes/Rocket.cs:91)
Rocket:OnCollisionEnter(Collision) (at Assets/Scenes/Rocket.cs:63)
-------------------------------------------------------------------
StartDeathSequence - gettting lock
UnityEngine.Debug:Log(Object)
Rocket:DieAndRestartLevel() (at Assets/Scenes/Rocket.cs:99)
Rocket:OnCollisionEnter(Collision) (at Assets/Scenes/Rocket.cs:63)
-------------------------------------------------------------------
StartDeathSequence - got lock
UnityEngine.Debug:Log(Object)
Rocket:DieAndRestartLevel() (at Assets/Scenes/Rocket.cs:102)
Rocket:OnCollisionEnter(Collision) (at Assets/Scenes/Rocket.cs:63)
-------------------------------------------------------------------
StartDeathSequence - state = State.Dying
UnityEngine.Debug:Log(Object)
Rocket:DieAndRestartLevel() (at Assets/Scenes/Rocket.cs:105)
Rocket:OnCollisionEnter(Collision) (at Assets/Scenes/Rocket.cs:63)
-------------------------------------------------------------------
StartDeathSequence - audioSource.Stop()
UnityEngine.Debug:Log(Object)
Rocket:DieAndRestartLevel() (at Assets/Scenes/Rocket.cs:112)
Rocket:OnCollisionEnter(Collision) (at Assets/Scenes/Rocket.cs:63)
-------------------------------------------------------------------
explosionAudio is null!!
UnityEngine.Debug:LogError(Object)
Rocket:DieAndRestartLevel() (at Assets/Scenes/Rocket.cs:124)
Rocket:OnCollisionEnter(Collision) (at Assets/Scenes/Rocket.cs:63)
-------------------------------------------------------------------
ResetGameEnvironment - state = State.Alive
UnityEngine.Debug:Log(Object)
Rocket:ResetGameEnvironment() (at Assets/Scenes/Rocket.cs:155)
Rocket:Start() (at Assets/Scenes/Rocket.cs:32)
-------------------------------------------------------------------
ResetGameEnvironment - audioSource.Stop()
UnityEngine.Debug:Log(Object)
Rocket:ResetGameEnvironment() (at Assets/Scenes/Rocket.cs:161)
Rocket:Start() (at Assets/Scenes/Rocket.cs:32)
-------------------------------------------------------------------
ResetGameEnvironment - state = State.Alive
UnityEngine.Debug:Log(Object)
Rocket:ResetGameEnvironment() (at Assets/Scenes/Rocket.cs:155)
Rocket:Start() (at Assets/Scenes/Rocket.cs:32)
-------------------------------------------------------------------
OnCollision -
UnityEngine.Debug:Log(Object)
Rocket:OnCollisionEnter(Collision) (at Assets/Scenes/Rocket.cs:48)
-------------------------------------------------------------------
OnCollision -
UnityEngine.Debug:Log(Object)
Rocket:OnCollisionEnter(Collision) (at Assets/Scenes/Rocket.cs:48)
-------------------------------------------------------------------
OnCollision -
UnityEngine.Debug:Log(Object)
Rocket:OnCollisionEnter(Collision) (at Assets/Scenes/Rocket.cs:48)
-------------------------------------------------------------------
OnCollision -
UnityEngine.Debug:Log(Object)
Rocket:OnCollisionEnter(Collision) (at Assets/Scenes/Rocket.cs:48)
-------------------------------------------------------------------