So I want this script to check if bool is true and if it is then play an audio once but if its false then don't play the audio but the problem is that this script is in multiple gameobjects and is always constantly turning on and off so the bool may be true on this gameobject but false on the other and so when I tried checking the bool value it returns both true and false and so never really executing the if condition to play the music until both gameobjects meet the same condition...how do I fix this?
ps: the gameobject isn't a prefab
private void Update()
{
if (isChasing && inSightArea)
{
if (!enemyFOV.chaseSound.isPlaying)
{
Debug.Log("music playing");
enemyFOV.chaseSound.Play();
}
Debug.Log(enemyFOV.chaseSound.isPlaying);
}
else
{
Debug.Log("is patrolling");
enemyFOV.chaseSound.Stop();
}
}
the "music playing" debug log seems to be logging at the right time i want but the enemyFOV.chaseSound.Play(); somehow didn't run at all until both bool from both object returns true for some reason..?
It sounds to me like your enemyFOV is the same instance for both your objects.
So if in one instance the isChasing && inSightArea is true, but on another it is not
=> The one object starts the sound, the other object stops the sound immediately.
I would rather store all currently chasing objects and let your EnemyFOV class control the sound like e.g.
public class EnemyFOV : MonoBehaviour
{
// store all currently chasing instances
private readonly HashSet<YourScript> currentlyChasing = HashSet<YourScript>();
// your audiosource as you have it anyway
public AudioSource chaseSound;
// call this one instead
public void SetChasing(YourScript yourScript, bool iAmChasing)
{
if(iAmChasing)
{
// Store this instance
if(!currentlyChasing.Contains(yourScript)) currentlyChasing.Add(yourScript);
}
else
{
// remove this instance
if(currentlyChasing.Contains(yourScript)) currentlyChasing.Remove(yourScript);
}
// If there is at least one instance and no sound is playing
if(currentlyChasing.Count > 0 && !chaseSound.isPlaying)
{
// -> start the sound
Debug.Log("music playing");
chaseSound.Play();
}
// If there is no chasing instance but sound is playing
else if (currentlyChasing.Count == 0 && chaseSound.isPlaying)
{
// -> stop the sound
chaseSound.Stop();
}
}
}
Where YourScript is the script from which you are calling your original code.
There instead you will now do
private void Update()
{
enemyFOV.SetChasing(this, isChasing && inSightArea);
}
Related
I'm making a game with unity 2022 and was struggling to make this animator [bool turn true] after entering a trigger and pressing "E". and then go false after still holding "E" still or not pressing it. also am struggling with Get Key stuff. it isn't working mate and I have been trying for hours at a time with struggles so yeah. this is my code. it's basically someone pressing a button (the object in unity I'm using), making it click until you click it again, please help this is for a school summer project!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
class Fixedpress : MonoBehaviour
{
public Animator Tributton;
bool YUIp = false;
// Start is called before the first frame update
void OnTriggerEnter(Collider other)
{
if(other.tag == "player")
{
YUIp = true;
}
}
void OnTriggerStay(Collider other)
{
if (other.tag == "Player" && YUIp == true)
{
if (Input.GetKeyDown(KeyCode.E))
{
Tributton.SetBool("Fiveyon", true);
Debug.Log("THY");
}
else if (Input.GetKey(KeyCode.E))
{
Tributton.SetBool("Fiveyon", false);
Debug.Log("THe");
}
else if (Input.GetKeyUp(KeyCode.Return))
{
Tributton.SetBool("Fiveyon", false);
Debug.Log("THane");
}
}
}
void OnTriggerExit(Collider other)
{
if(other.tag == "Player")
{
YUIp = false;
}
}
}
I mostly need help with the press e code stuff to make work so yeah :D
thank you
sorry for the bad grammar its 3 am rn
I think I got it now.
You want
Player has to be in the trigger in order to start the animation
Pressing E should trigger the animation only ONCE
I would no use a Bool parameter in that case but rather a Trigger and then Animator.SetTrigger.
Your transitions should be somewhat like e.g.
Idle State --condition-Fiveyon--> Your Animation
Your Animation --exit-time-no-further-condition--> Idle State
Then you could probably do something like e.g.
public class Fixedpress : MonoBehaviour
{
public Animator Tributton;
private Coroutine routine;
private void OnTriggerEnter(Collider other)
{
// not sure if needed right now, some of the events are triggered even on disabled components
if(!enabled) return;
// in general rather use CompareTag instead of ==
// it is slightly faster and also shows an error if the tag doesn't exist instead of failing silent
if(!other.CompareTag("player")) return;
// just in case to prevent concurrent routines
if(routine != null) StopCoroutine(routine);
// start a new Coroutine
routine = StartCoroutine(WaitForKeyPress());
}
private IEnumerator WaitForKeyPress()
{
// check each FRAME if the key goes down
// This is way more reliable as OnTriggerStay which is called
// in the physics loop and might skip some frames
// This also prevents from holding E while entering the trigger, it needs to go newly down
yield return new WaitUntil(() => Input.GetKeyDown(KeyCode.E));
// set the trigger once and finish the routine
// There is no way to trigger twice except exit the trigger and enter again now
Tributton.SetTrigger("Fiveyon");
Debug.Log("Fiveyon!");
// If you even want to prevent this from getting triggered ever again simply add
enabled = false;
// Now this can only be triggered ONCE for the entire lifecycle of this component
// (except you enable it from the outside again of course)
}
void OnTriggerExit(Collider other)
{
if(!other.CompareTag("player")) return;
// when exiting the trigger stop the routine so later button press is not handled
if(routine != null) StopCoroutine(routine);
}
}
I am trying to do when i destroy all boxes something happen.
My code is;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class destroy : MonoBehaviour
{
private string BALL_TAG = "ball";
public AudioClip coin;
public AudioSource src;
public float numBox = 120f;
public bool isDestroyed;
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag(BALL_TAG))
{
src.clip = coin;
src.Play();
Destroy(gameObject);
isDestroyed = true;
}
}
private void Update()
{
boxes();
}
public void boxes()
{
if(isDestroyed == true)
numBox -= 1f;
if(numBox == 119)
SceneManager.LoadScene("mainManu");
}
private IEnumerator Two()
{
yield return new WaitForSeconds(1f);
Destroy(gameObject);
}
}
But it doesn't work.
It is suppose to do when I broke 1 box it sends me to menu.
I think its problem in "numBox -= 1f;" because I don't know hot to make this.
I don't understand your code completely. So, I need to make some assumptions.
I think the Script is attached to the box and every box has this Script. I also think, that your player Shoots Ball. Those Balls have a collider with an ball tag.
There are multiple problems with your code.
The first one is, that your count variable, numBox, is saved in your destroy Script, which is placed on each box.
this means, that every Box is counting for itself.
You have to centralize this. There are multiple ways for doing this.
One way is to declare this variable as static(https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/static)
This is not best practice, but works.
A Better way is to have a Script on Your Player, which holds this number and every Box searches for this Script and change this number if it is destroyed.
The second big Problem is, that your are doing some really weird thing in your Update and the collision handling
First of all, you are setting isDestroyed to true. Then in your boxes method, which is called in every Frame, you are decrementing your numBox variable by one, if this is Destroyed is true.
So if your Box gets hit, you are decrementing every frame.
After that you are checking every frame if your numBox is 119
If so, you change the Scene.
This is the reason, why you are getting to your MainMenu after only one boy
This behaviour is very weird, because it is totally unnecessary. You can reduce your variable directly in in your OnCollisionEnter2D Method.
There are some little things, which can be improved.
When you are trying to play a Sound, you don't have to specify the AudioClip in code. You can assign this directly in Unity on the AudioSource Component via drag and drop. This makes your code simpler.
You are not calling the Two Coroutine. You've specified this Coroutine but don't call it.
//Script on Player
public class PlayerBoxDestroyManager:MonoBehaviour
{
public int StartBoxes = 120;
private int Boxes;
private void Start()
{
Boxes = StartBoxes;
}
public void DestroyBox()
{
//Reduce our Boxes count
//This is equal to Boxes -= 1
// Boxes = Boxes -1
Boxes--;
// If we have less or zero Boxes left, we End call our EndGame methode
if(Boxes <= 0)
{
EndGame();
}
}
private void EndGame()
{
// We change the Scene to the mainMenu
SceneManager.LoadScene("mainManu");
}
}
```
//Script on all Boxes
public class Box : MonoBehaviour
{
public string Balltag = "ball";
//Audio Source the Audio Clip has to be assigned in the Unity editor
public AudioSource Coin;
private void OnCollisionEnter2D(Collision2D collision)
{
//Check it colliding Object has the right Tag
if(collision.transform.tag == Balltag)
{
//Get the reference to the Player Script
PlayerBoxDestroyManager PBDM = FindObjectOfType<PlayerBoxDestroyManager>();
//We can now access the Destroy Box Methode
PBDM.DestroyBox();
//Play the sound
Coin.Play();
//If we destroy our Object now, the Sound would also be deletet.
//We want to hear the sound, so we have to wait, till the sound is finished.
StartCoroutine(WaitTillAudioIsFinished());
}
}
IEnumerator WaitTillAudioIsFinished()
{
//we wait till the sound is finished
while (Coin.isPlaying)
{
yield return null;
}
//if finished, we destroy the Gameobject
Destroy(gameObject);
}
}
I hope I helped you. If you have questions, feel free to ask.
And sorry for my English:)
I just introduced a pause option to my little testing game. I have audio in background (that plays throughout the whole game, even when the scene changes), so I've decided to make the music stop while the game is paused. For some reason, it works just fine UNTIL the game reloads/changes scene.
Then an error pops up
"The object of type 'AudioSource' has been destroyed."
Can anybody help? Also.. I thought it's a component, not an object! Might be both, I'm not sure.
How I make my music continuous:
void Awake()
{
if (instance != null)
{
Destroy(gameObject);
}
else
{
instance = this;
GameObject.DontDestroyOnLoad(gameObject);
}
}
My pause menu:
public static bool GameIsPaused = false;
public GameObject pauseMenuUI;
public AudioSource song;
void Update()
{
if (Input.GetKeyDown(KeyCode.Escape))
{
if (GameIsPaused)
{
Resume();
}
else
{
Pause();
}
}
}
void Resume()
{
pauseMenuUI.SetActive(false);
Time.timeScale = 1f;
GameIsPaused = false;
song.mute = false;
}
void Pause()
{
pauseMenuUI.SetActive(true);
Time.timeScale = 0f;
GameIsPaused = true;
song.mute = true;
}
Thank you!
Your problem could be with this line here
if (instance != null)
This is a singleton pattern and the purpose of this line is to prevent two instances of your singleton class from existing, which is a big no-no. This means if you try make a new object but one already exists, it will destroy the new object.
However, if Awake()is called again for any reason on the first singleton then it will see that the member instance is not null and promptly destroy itself.
A fix would be to change it to this:
if (instance != null && instance != this)
This would prevent your singleton from destroying itself.
Pretty sure it's your audio variable in the pause menu script. I think the pause menu dissociates itself from that AudioSrouce on a new scene load. To check this, go into game mode and load a new scene. When in the new scene, go over to the hierarchy and click the GameObject with the pause menu script on it. Now check to see if the public var AudioSource has anything associate with it.
I am making a multiplayer game using Unet. Currently, the players can move independently (i.e., if user one presses d, then character one will move right if user two presses d then character two will move right) - however, both players shoot 'through' the host.
My movement code is inside the PlayerUnit script. It has one !isLocalPlayer check at the top, in the Update() method. This check is performed before the script detects for user inputs (i.e., w, s, mouse1, etc.).
This all works fine when the correct key is pressed; the proper action is performed.
Eg:
if (Input.GetKeyDown(up))
Jump();
Shooting works in a slightly different way as it involves a different script.
When mouse1 is pressed, the Shoot() method in PlayerUnit is called. This, in turn, uses a Command inside the PlayerUnit script to call a ClientRpc in a separate WeaponMaster script, which calls the Fire() coroutine.
In PlayerUnit:
private void Shoot ()
{
//WeaponMaster.wM.Shoot();
CmdShoot();
}
[Command]
void CmdShoot ()
{
WeaponMaster.wM.RpcShoot();
}
In WeaponMaster:
[ClientRpc]
public void RpcShoot ()
{
if (ammo != 0)
{
if (isAutomatic == true)
{
keepFiring = true;
StartCoroutine(Fire());
}
if (isAutomatic == false && noQuickFire == true)
StartCoroutine(Fire());
}
}
WeaponMaster has one !isLocalPlayer check as well, once again in the Update() method.
When debugging, I know that playerTwo's (client) Shoot() method is being called, but playerOne's RpcShoot is being executed. Everything works fine when playerOne shoots - hence the problem of both players shooting 'through' the host.
All the scripts are on the same object with one NetworkIdentity component.
I'm convinced the problem is just a missing authority check - but I've tried adding !isLocalPlayer tests in the various methods to no avail.
As said my guess is that a Singleton pattern in WeaponMaster.wM is not a good approach here since you get the wrong reference and since everything goes through the server might simply always get the same reference no matter who called CmdShoot.
You should rather store each players own WeaponMaster reference and go through it like in PlayerUnit
// Already reference this via the inspector (drag&drop)
[SerializeField] private WeaponMaster weaponMaster;
// or as fallback get it on runtime
private void Awake()
{
if(!weaponMaster) weaponMaster = GetComponent<WeaponMaster>();
}
public void Shoot ()
{
if(!isLocalPlayer) return;
CmdShoot();
}
[Command]
private void CmdShoot ()
{
RpcShoot();
}
// Personally in general I would leave any RPC
// as close to the method calling it
// as possible for better maintainability
[ClientRpc]
private void RpcShoot ()
{
weaponMaster.Shoot();
}
and accordingly in WeaponMaster
public void Shoot()
{
if (ammo == 0) return;
if (isAutomatic)
{
keepFiring = true;
StartCoroutine(Fire());
}
else if (!isAutomatic && noQuickFire)
StartCoroutine(Fire());
}
I've been trying for a little bit here to set a muteSound boolean in my SoundManager and then to switch to a new scene and keep the value previously stored in muteSound but I'm unsuccessful.
I tried the DontDestroyOnLoad(this); in hopes that it'd bring it to the new scene but for some reason it isn't.
Would any of you know what my problem could be? Am I using the correct function?
Thanks,
Some would say use static. That would work but avoid doing that as you will run into other problems. What you need is the PlayerPrefs. Save the value on exit. Read the value when game starts. You can do that in your SoundManager script.
bool muteSound = false;
//Load the value when game starts (default is false)
void Start()
{
muteSound = intToBool(PlayerPrefs.GetInt("muteSound", 0));
}
int boolToInt(bool val)
{
if (val)
return 1;
else
return 0;
}
bool intToBool(int val)
{
if (val != 0)
return true;
else
return false;
}
//Save on Exit
void OnDisable()
{
PlayerPrefs.SetInt("muteSound", boolToInt(muteSound));
}
You can actually pass a game object from scene to scene and all the values for classes assigned to that game object are maintained between scenes.
You basically just create an empty game object that stores your manager scripts and then pass the game object from scene to scene when you load them. This should preserve the values for the scripts attached to the empty game object.
EDIT: Fixed some spelling/grammatical errors.
Instead of typing
DontDestroyOnLoad(this); type
void Awake() {
DontDestroyOnLoad(gameObject);
}
If this doesn't work I suggest you check out the PlayerPrefs.
When the level is finished usePlayerPrefs.SetFloat("pref name", variable); or .SetInt(); or .SetBool();
And when the next level loads usevalue = PlayerPrefs.GetFloat("pref name"); or .GetInt(); or .GetBool();
This player prefs can also be used as saves because they are stored inside the computers registry.
If you use this for loading things such as money and you are loading it for the first time, do this:
if(PlayerPrefs.HasKey("money")) {
money = PlayerPrefs.GetFloat("money");
} else {
PlayerPrefs.SetFloat("money", 0);
money = 0;
}
If you don't do this the values can get messy a lot. I had big problems when I didn't use PlayerPrefs.HasKey();
Here was my solution:
public class SoundManager : MonoBehaviour {
public static SoundManager instance = null;
protected virtual void Awake()
{
if (instance == null)
instance = this;
else if (instance != this)
Destroy(gameObject);
DontDestroyOnLoad(gameObject);
}
}
While I thought my problem was because of my changing the class to a singleton the actual problem was that there was another class that needed to be a singleton.
Since the rest of the code wouldn't be mine to share, here is a tutorial which helped me understand what I was doing much better if anyone else ever runs into this type of problem.