Everything breaks after using PlayOneShot - c#

In a game I am working on, the player uses a cube to open doors. When the player interacts with the cube while near the door, the door slides open. I am now trying to make it so that while it slides open, it makes a sound as doors should but I kinda ran into some issues. When I tried to add in the sound for the door, it ignored the part where the door should open altogether.
Here is what I did:
I added an AudioSource to the Cube's child object, CORE because the Cube object already contains an AudioSource which will play at the same time as this sound and assigned it in my Cube's script...
public AudioSource Woosh;
void Start()
{
//Debug.Log("Script initialized!");
Hm = gameObject.GetComponent<AudioSource>();
anim = GameObject.Find("TheFirstDoor").GetComponent<Animator>();
//Door = GameObject.Find("TheFirstDoor").GetComponent<AudioSource>();
Woosh = GameObject.Find("CORE").GetComponent<AudioSource>();
}
Here's the interactive part of the script, it runs a check but for some reason, it is pretending CanBeKey is false...
public void OnActivate()
{
if(HasPlayed == false) //Have I played? Has the Time out passed?
{
HasPlayedFirstTime = true;
Hm.Play();
HasPlayed = true;
PlayTimeOut = 30.0f;
if(CanBeKey == true)
{
anim.Play("Door_Open");
//Door.Play();
StartCoroutine(PlayDaWoosh());
Debug.Log("OPENING!");
}
}
}
Now here is the IEnumerator part of the script, PlayDaWoosh()
IEnumerator PlayDaWoosh()
{
Woosh.PlayOneShot(Woosh.clip);
yield return new WaitForSeconds(Woosh.clip.length);
}
I am aware that the last code snippet is a bit messy but it was the best thing I can think of.
Here is the full script in case you are that curious.....
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CUBE_CORE : MonoBehaviour
{
public AudioSource Hm; // HM!!!
private bool HasPlayed = false;
public float PlayTimeOut = 0.0f;
public static bool CanBeKey = false;
public Animator anim;
public static bool HasPlayedFirstTime = false;
public static AudioSource Door;
public AudioSource Woosh;
// Start is called before the first frame update
void Start()
{
//Debug.Log("Script initialized!");
Hm = gameObject.GetComponent<AudioSource>();
anim = GameObject.Find("TheFirstDoor").GetComponent<Animator>();
//Door = GameObject.Find("TheFirstDoor").GetComponent<AudioSource>();
Woosh = GameObject.Find("CORE").GetComponent<AudioSource>();
}
// Update is called once per frame
void Update()
{
if(HasPlayed == true)
{
if (PlayTimeOut < 0.0f)
{
HasPlayed = false;
}
PlayTimeOut -= Time.smoothDeltaTime;
}
}
public void OnActivate()
{
if(HasPlayed == false) //Have I played? Has the Time out passed?
{
HasPlayedFirstTime = true;
Hm.Play();
HasPlayed = true;
PlayTimeOut = 30.0f;
if(CanBeKey == true)
{
anim.Play("Door_Open");
//Door.Play();
StartCoroutine(PlayDaWoosh());
Debug.Log("OPENING!");
}
}
}
private void OnTriggerEnter(Collider other)
{
if(other.gameObject.name == "SecondDoor")
{
anim = GameObject.Find("DoorSecond").GetComponent<Animator>();
}
if(other.gameObject.name == "ThirdDoor")
{
anim = GameObject.Find("TheThirdDoor").GetComponent<Animator>();
}
if(other.gameObject.name == "FourthDoor")
{
anim = GameObject.Find("TheFourthDoor").GetComponent<Animator>();
}
}
IEnumerator PlayDaWoosh()
{
Woosh.PlayOneShot(Woosh.clip);
yield return new WaitForSeconds(Woosh.clip.length);
}
}
Expected result: Door opening and sound playing
Actual result: Neither happening unless I remove anything that has to do with the Woosh
I apologize ahead of time if I wasn't specific enough or if the question was answered somewhere else. I am relatively new here.

Related

Show and hide animation for a hand

So . I work at a 2d game in unity and I have some problems with some mechanics and i really need some help from you guys. All I want to do is to make my character hand to stop and grab a object. When I press E the animation of the hand start and have a collider for each frame, in case that the hand collide with a closer object, the animation to stop at that frame. I have a week since I try to figure it out. If you want to help me we can do it on discord. I will put the codes here, maybe the reason that I can't to what I want to do is very clear and I dont see it.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class showHide : MonoBehaviour
{
[SerializeField]
GameObject hand;
private Animator freeze;
public bool touch = false;
private bool ok = true;
// Start is called before the first frame update
void Start()
{
freeze = GetComponent<Animator>();
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.E) && ok == true)
{
freeze.Play("Follower");
StartCoroutine(show());
}
}
void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "box")
{
StartCoroutine(colliderShow());
}
}
private IEnumerator show()
{
ok = false;
hand.SetActive(true);
yield return new WaitForSeconds(4f);
hand.SetActive(false);
ok = true;
}
private IEnumerator colliderShow()
{
touch = true;
print(touch);
yield return new WaitForSeconds(4f);
touch = false;
print(touch);
}
private void FreezeAniamtion()
{
freeze.speed = 0f;
StartCoroutine(waitTime());
}
private IEnumerator waitTime()
{
yield return new WaitForSeconds(0f);
freeze.speed = 1f;
}
}
This is the code that activate and deactivate my hand object , including the animation .
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class facingScript : MonoBehaviour
{
private SpriteRenderer render;
private Camera mainCam;
private Vector3 mousePos;
private Animator aniHand;
public bool atinge;
void Start()
{
mainCam = Camera.main;
render = GetComponent<SpriteRenderer>();
aniHand = GetComponent<Animator>();
gameObject.SetActive(false);
}
// Update is called once per frame
void Update()
{
mousePos = Input.mousePosition;
mousePos = mainCam.ScreenToWorldPoint(
new Vector3(mousePos.x, mousePos.y, mainCam.transform.position.z - transform.position.z)
);
Vector3 rotation = mousePos - transform.position;
if (rotation.x > 0)
render.flipY = true;
else
render.flipY = false;
if (atinge == false)
aniHand.speed = 1f;
Debug.Log(aniHand.speed);
}
// Collision with objects
void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "box")
{
atinge = true;
aniHand.speed = 0f;
StartCoroutine(freezingTime());
}
}
private IEnumerator freezingTime()
{
yield return new WaitForSeconds(4f);
atinge = false;
}
// No collision with any object
private void FreezeAnimation()
{
aniHand.speed = 0f;
StartCoroutine(waitTime());
}
private IEnumerator waitTime()
{
yield return new WaitForSeconds(0f);
aniHand.speed = 1f;
}
}
This is the hand component script . When the hand don't collide with anything the object is deactivated . The FreezeAnimation() is a event at the last frame. I tryed to do a collider animation for the showHide component that will be exact with the hand animation collider for each frame to check if the hand collide in the showHide script and if it collide to have a 4 seconds of waiting with the hand at that position.
I really tryed my best but I really can't figure it out.

Game Freezes When Ever Lives Get Removed In Unity [duplicate]

This question already has answers here:
How to make the script wait/sleep in a simple way in unity
(7 answers)
Closed 1 year ago.
Whenever lives get removed by an enemy in my game in unity, the game temporarily freezes. I think it has to do with the line "Thread.Sleep(3000);" but I am not sure of an alternative wait that will not freeze the entire game temporarily.
I would greatly appreciate if you could help me!
Thanks.
using System.Collections;
using System.Collections.Generic;
using System;
using System.Threading;
using UnityEngine;
public class Enemy : MonoBehaviour
{
public Transform attackPoint;
public float attackrange = 0.5f;
public LayerMask playerlayers;
public float speed = 3f;
private Transform target;
bool hitID = false;
private void Update()
{
if (target != null)
{
float step = speed * Time.deltaTime;
transform.position = Vector2.MoveTowards(transform.position, target.position, step);
Collider2D[] hitplayers = Physics2D.OverlapCircleAll(attackPoint.position, attackrange, playerlayers);
foreach (Collider2D player in hitplayers)
{
if (hitID == false)
{
hitID = true;
player.GetComponent<HeartSystem>().TakeDamage(1);
Thread.Sleep(3000);
hitID = false;
}
}
}
}
private void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.tag == "Player")
{
target = other.transform;
}
}
private void OnTriggerExit2D(Collider2D other)
{
if (other.gameObject.tag == "Player")
{
target = null;
}
}
void OnDrawGizmosSelected()
{
if (attackPoint == null)
return;
Gizmos.DrawWireSphere(attackPoint.position, attackrange);
}
}
Enemy.cs
using System.Collections;
using UnityEngine;
public class Enemy : MonoBehaviour
{
public Transform attackPoint;
public float attackrange = 0.5f;
public LayerMask playerlayers;
public float speed = 3f;
private Transform _target;
private bool _hitID;
private void Update()
{
if (_target != null)
{
var step = speed * Time.deltaTime;
transform.position = Vector2.MoveTowards(transform.position, _target.position, step);
var hitPlayers = new Collider2D[10];
Physics2D.OverlapCircleNonAlloc(attackPoint.position, attackrange, hitPlayers, playerlayers);
foreach (var player in hitPlayers)
{
if (_hitID == false)
{
StartCoroutine(HitCoroutine(player));
}
}
}
}
private IEnumerator HitCoroutine(Collider2D player)
{
_hitID = true;
player.GetComponent<HeartSystem>().TakeDamage(1);
yield return new WaitForSeconds(3);
_hitID = false;
}
private void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.CompareTag("Player"))
{
_target = other.transform;
}
}
private void OnTriggerExit2D(Collider2D other)
{
if (other.gameObject.CompareTag("Player"))
{
_target = null;
}
}
private void OnDrawGizmosSelected()
{
if (attackPoint == null)
return;
Gizmos.DrawWireSphere(attackPoint.position, attackrange);
}
}
Try this.
In a case like this you want to use a coroutine.
You can read about coroutines here:
https://docs.unity3d.com/Manual/Coroutines.html
Unity is single threaded, so you are actually suspending the entire thread the game is running on. Since the frames are updated on this thread, you're even freezing the visual frame updates.
You have a few ways to manage "waiting" in game.
Coroutines:
A process that can be suspended until a later condition is met (including time)
void Update()
{
//Your other code goes here
if (hitID == false)
{
hitID = true;
player.GetComponent<HeartSystem>().TakeDamage(1);
StartCoroutine(ResetHitIDAfterSeconds(3))
}
}
IEnumerator ResetHitIDAfterSeconds(float seconds)
{
yield return new WaitForSeconds(seconds);
hitID = false;
}
The above code allows you to have the same logic, and will kick off a coroutine that waits for 3 seconds before setting hitID back to false.
Time.deltaTime:
You can also track the elapsed time in your Update method and manage your variables actively based on the elapsed time. Time.deltaTime returns the amount of time that elapsed since the previous frame.
public class ConstantRotation : MonoBehaviour
{
public float timeElapsed = 0f;
void Update()
{
//Your other code goes here
if (hitID == false)
{
hitID = true;
player.GetComponent<HeartSystem>().TakeDamage(1);
timeElapsed = 0f;
}
else
{
timeElapsed += Time.deltaTime
if(timeElapsed >= 3.0f)
{
hitId = false;
}
}
}
}
You would likely want to modify the above code a bit to avoid having the logic run on every frame, and only run it on hit.

How can I disable the audio for a specific scene?

I'm using this line :
AudioListener.pause = true;
The problem is that I have two scenes loaded, I'm loading another scene with the
LoadSceneMode.Additive
Then the audio pausing is effecting both scenes.
This is my full code for pausing/resuming the game. Index 0 is the Main Menu scene and index 1 is the Game scene. I have two scenes the Main Menu and the Game.
When I hit the escape key to the main menu I want to pause the audio in the Game scene and then when hitting the escape ley again back to the game I want to resume the audio in the game scene.
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class BackToMainMenu : MonoBehaviour
{
public GameObject[] objsToDisable;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Escape))
{
if (Time.timeScale == 0)
{
DisableEnableUiTexts(true);
AudioListener.pause = false;
SceneManager.UnloadSceneAsync(0);
Cursor.visible = false;
Time.timeScale = 1;
}
else
{
Time.timeScale = 0;
MenuController.LoadSceneForSavedGame = false;
AudioListener.pause = true;
SceneManager.LoadScene(0, LoadSceneMode.Additive);
SceneManager.sceneLoaded += SceneManager_sceneLoaded;
Cursor.visible = true;
}
}
}
private void SceneManager_sceneLoaded(Scene arg0, LoadSceneMode arg1)
{
DisableEnableUiTexts(false);
}
private void DisableEnableUiTexts(bool enabled)
{
foreach (GameObject go in objsToDisable)
{
if (go.name == "Cameras")
{
foreach(Transform child in go.transform)
{
if(child.name == "Main Camera")
{
if (enabled == false)
{
child.GetComponent<Camera>().enabled = false;
}
else
{
child.GetComponent<Camera>().enabled = true;
}
}
}
}
else
{
go.SetActive(enabled);
}
}
}
}
As mentioned one possibility is to use AudioSource.mute but of course you'd have to use it on all sources of according scene.
When you already know the index you can do e.g.
var scene = SceneManager.GetSceneAtBuildIndex(1);
if(scene.isValid)
{
// Iterate through the scene Hierarchy and get all sources (also inactive/disabled ones)
var audioSources = new List<AudioSource>();
foreach (var root in scene.GetRootGameObjects())
{
audioSources.AddRange(root.GetComponentsInChildren<AudioSource>(true));
}
foreach(var source in audioSources)
{
source.mute = !enabled;
//Or
source.pause = !enabled;
}
}
Alternatively you could use the AudioMixer which I'd say is more convenient for this.
Here would simply but all the sources into a certain AudioMixerGroup and make the volume an exposed parameter.
Then you can simply silent that entire group by using SetFloat.
Btw be very careful with naming your method parameters like already existing fields/properties! Yes, they will be shadowed anyway but it only leads to confusion for you and other readers. There already is Behavior.enabled which MonoBehavior and thus your class derives from.
Can you try using audio_source_name.mute = true &/or audio_source_name.Stop()?
Example:
void Update()
{
if (Input.GetKeyDown(KeyCode.Escape))
{
if (Time.timeScale == 0)
{
DisableEnableUiTexts(true);
// AudioListener.pause = true;
audio_source_name.mute = false; // <--------
SceneManager.UnloadSceneAsync(0);
Cursor.visible = false;
Time.timeScale = 1;
}
else
{
Time.timeScale = 0;
MenuController.LoadSceneForSavedGame = false;
// AudioListener.pause = true;
audio_source_name.mute = false; // <--------
SceneManager.LoadScene(0, LoadSceneMode.Additive);
SceneManager.sceneLoaded += SceneManager_sceneLoaded;
Cursor.visible = true;
}
}
}
You could also do it this way (Source: Unity - Scripting API: AudioSource.mute):
// Mutes-Unmutes the sound from this object each time the user presses space.
using UnityEngine;
using System.Collections;
public class ExampleClass : MonoBehaviour
{
AudioSource audioSource;
void Start()
{
audioSource = GetComponent<AudioSource>();
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
audioSource.mute = !audioSource.mute;
}
}

Instantiate Object Only once on game start Unity 3D

I have a game with several levels, each level has 6 scenes, the game start directly without any menu scene, and when the player open the game he can continue from the last scene that he already reached.
I want to instantiate some elements only on game opening (like Best score, Tap to play etc...), I mean that they should be instantiated only once on the start of the game (on the level he reached).
I tried this code in GameManager but it instantiate the elements in every scene:
public GameObject PlayButton;
bool GameHasEnded = false;
public float RestartDelay = 2f;
public float NextLevelDelay = 5f;
public int level_index;
private static bool loaded = false;
private void Start()
{
if (!loaded)
{
loaded = true;
level_index = PlayerPrefs.GetInt("Last_Level");
SceneManager.LoadScene(level_index);
}
GameObject canvas = GameObject.Find("Canvas");
GameObject play = Instantiate(PlayButton, canvas.transform.position, Quaternion.identity);
play.transform.SetParent(canvas.transform, false);
}
public void CompleteLevel()
{
Invoke("NextLevel", NextLevelDelay);
}
public void EndGame()
{
if (GameHasEnded == false)
{
GameHasEnded = true;
Invoke("Restart", RestartDelay);
}
}
void NextLevel()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex +1);
level_index = SceneManager.GetActiveScene().buildIndex + 1;
PlayerPrefs.SetInt("Last_Level", level_index);
PlayerPrefs.Save();
}
void Restart()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().path);
}
You already have an if block there with the static flag loaded
Since you there load another scene you need a similar second flag e.g.
private static bool loadedPrefab = false;
private void Start()
{
if (!loaded)
{
loaded = true;
level_index = PlayerPrefs.GetInt("Last_Level");
SceneManager.LoadScene(level_index);
// Return because you don't want to execute the rest yet
// but instead in the new loaded scene
return;
}
// The same way skip if the prefab was already loaded before
if(!loadedPrefab)
{
loadedPrefab = true;
GameObject canvas = GameObject.Find("Canvas");
GameObject play = Instantiate(PlayButton, canvas.transform.position, Quaternion.identity);
play.transform.SetParent(canvas.transform, false);
}
}

Player stuck on reactivation - not changing position to spawn

I'm writing a script controlling the enter and exit of a space ship in a 2D game.
The problem I'm running into is when exiting the ship, the player is reactivated, and you regain control, but it does not move the player to the empty gameobject I have attached to the ship, in a beginners attempt to move the player to the location of the ship, without ever destroying the player. The player simply spawns on its last known location prior to entering the ship, and I seem to be forced into that location, I can try to move, but its like I'm anchored to that position.
using UnityEngine;
using System.Collections;
public class SpaceShipActivator : MonoBehaviour
{
//Takeoff
public bool inRange;
public SpaceShipAtmosphereController SpaceShipAtmosphereController;
public PlayerController playerControl;
public GameObject Player;
public bool onBoard;
//End Takeoff
//Landing
public Transform Spawn;
public Transform landingGear2;
public Transform landingGear;
public LayerMask whatIsGround;
float groundRadius = 0.2f;
bool getIn;
bool landingGearStable;
bool landingGearStable2;
bool shipStable;
//EndLanding
Animator anim;
void Start()
{
anim = GetComponent<Animator>();
inRange = false;
SpaceShipAtmosphereController.enabled = false;
}
void Update()
{
AnimationUpdate();
//Launch
if (inRange && Input.GetButtonDown("Interact") && !onBoard)
{
LaunchShip();
}
//End launch
CheckLandingGear();
//Land
if(onBoard && Input.GetButtonDown("Roll"))
{
LandShip();
}
//End Land
}
//*********************************Functions**************************************
void LaunchShip()
{
anim.SetBool("Get In", true);
this.gameObject.tag = "Player";
SpaceShipAtmosphereController.enabled = true;
Player.tag = "InactivePlayer";
playerControl.enabled = false;
Player.SetActive(false);
onBoard = true;
}
void LandShip()
{
anim.SetBool("Get Out", true);
this.gameObject.tag = "InactivePlayer";
Player.tag = "Player";
playerControl.enabled = true;
SpaceShipAtmosphereController.enabled = false;
onBoard = false;
Player.SetActive(true);
Player.transform.position = Spawn.transform.position;
}
void CheckLandingGear()
{
//landingGear
landingGearStable = Physics2D.OverlapCircle(landingGear.position, groundRadius, whatIsGround);
landingGearStable2 = Physics2D.OverlapCircle(landingGear2.position, groundRadius, whatIsGround);
if (landingGearStable && landingGearStable2)
{
shipStable = true;
}
//End landingGear
}
void AnimationUpdate()
{
anim.SetBool("Get In", false);
anim.SetBool("Get Out", false);
}
//For launching ship
//IF player: inRange enter/exit boolean control.
void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Player"))
{
//Debug.Log("houi p;gberf");
inRange = true;
}
}
void OnTriggerExit2D(Collider2D other)
{
if (other.CompareTag("Player"))
{
inRange = false;
}
}
//End IF player: inRange enter/exit boolean control.
}
On a side note, In that same script I've included landingGear, which is set to be two empty gameobjects that basically share Y and are spaced apart on X to check that the spaceship is not only grounded but level for the most part, so the animation doesn't look silly AND to check if the player can even get out. I know this isn't the question, but any input on it would be appreciated, but please don't feel obligated.
Thanks for the help

Categories