Why is my pause menu script only working once? SetActive only working with false - c#

I'm wondering why my pause script doesn't work as expected. It should freeze the time and bring up the Pause menu, but instead it only freezes and nothing happens after that. And i can't resume the game too
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
public class PauseMenu : MonoBehaviour
{
// Start is called before the first frame update
public static bool GameIsPaused = false;
public GameObject pauseMenuUI;
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Escape))
{
if (GameIsPaused)
{
Resume();
}
else
{
Pause();
}
}
}
void Resume()
{
pauseMenuUI.SetActive(false);
Time.timeScale = 1f;
GameIsPaused = false;
}
void Pause()
{
pauseMenuUI.SetActive(true);
Time.timeScale = 0f;
GameIsPaused = true;
}
}

SetActive(false) will disable the game object, i.e. it won't update anymore. So if your pauseMenuUI is the object you call that on (or a descent of it) then your script won't be called anymore.
The solution is to put your script on another object (e.g. a parent of pauseMenuUI`).

Related

Unity 2D: Making the Player Gameobject Reactivate after being turned off

I can't get the Player Gameobject to reappear. The player deactivates the moment the Timeline starts when they touch the box collider, but the player never reactivates. I have tried using Player.SetActive(true) in the coroutine and even using an Invoke method with no luck. Any ideas on how to fix this? Please help.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Playables;
public class TimelineTrigger : MonoBehaviour
{
// calling items for Unity
public PlayableDirector timeline;
public GameObject Player;
public GameObject CutsceneCollider;
public GameObject CutsceneMC;
// Start is called before the first frame update
void Start()
{
// calls the playable director and turns off the MC for the scene
timeline = timeline.GetComponent<PlayableDirector>();
CutsceneMC.SetActive(false);
}
void Update()
{
Player = GameObject.FindGameObjectWithTag("Player");
}
private void EnableAfterTimeline()
{
Player = GameObject.FindGameObjectWithTag("Player");
Player.SetActive(true);
}
public void OnTriggerEnter2D (Collider2D col)
{
if (col.CompareTag("Player"))
{
// plays the cutscene and starts the timer
timeline.Play();
Player.SetActive(false);
Invoke("EnableAfterTimeline", 18);
CutsceneMC.SetActive(true);
StartCoroutine(FinishCut());
}
IEnumerator FinishCut()
{
// once the cutscene is over using the duration, turns off the collider and the MC.
yield return new WaitForSeconds(17);
CutsceneMC.SetActive(false);
CutsceneCollider.SetActive(false);
}
}
}
The issue here is that coroutines in Unity can't be run on inactive GameObjects, so FinishCut never gets executed.
This can be worked around by having a separate MonoBehaviour in the scene to which the responsibility of running a coroutine can be off-loaded. This even makes it possible to start static coroutines from static methods.
using System.Collections;
using UnityEngine;
[AddComponentMenu("")] // Hide in the Add Component menu to avoid cluttering it
public class CoroutineHandler : MonoBehaviour
{
private static MonoBehaviour monoBehaviour;
private static MonoBehaviour MonoBehaviour
{
get
{
var gameObject = new GameObject("CoroutineHandler");
gameObject.hideFlags = HideFlags.HideAndDontSave; // hide in the hierarchy
DontDestroyOnLoad(gameObject); // have the object persist from one scene to the next
monoBehaviour = gameObject.AddComponent<CoroutineHandler>();
return monoBehaviour;
}
}
public static new Coroutine StartCoroutine(IEnumerator coroutine)
{
return MonoBehaviour.StartCoroutine(coroutine);
}
}
Then you just need to tweak your code a little bit to use this CoroutineHandler to run the coroutine instead of your inactive GameObject.
public void OnTriggerEnter2D (Collider2D col)
{
if (col.CompareTag("Player"))
{
// plays the cutscene and starts the timer
timeline.Play();
Player.SetActive(false);
Invoke("EnableAfterTimeline", 18);
CutsceneMC.SetActive(true);
CoroutineHandler.StartCoroutine(FinishCut()); // <- Changed
}
IEnumerator FinishCut()
{
// once the cutscene is over using the duration, turns off the collider and the MC.
yield return new WaitForSeconds(17);
CutsceneMC.SetActive(false);
CutsceneCollider.SetActive(false);
}
}
If you have only one Player instance and it's accessible from the start, you'd better set it once in the Start method.
void Start()
{
// calls the playable director and turns off the MC for the scene
timeline = timeline.GetComponent<PlayableDirector>();
CutsceneMC.SetActive(false);
Player = GameObject.FindGameObjectWithTag("Player");
}
void Update()
{
}
private void EnableAfterTimeline()
{
Player.SetActive(true);
}

coroutine animation not playing as expected

so everything seems to be running as far as the AI switching different States, however when it gets in range with the player the State freezes in attack and does not play any Attack Animation, I have created the AI with Bone Rigging, Im wondering if this may be affecting it, here is my script for calling the Animation and the script for one the animation has finished to go back to idleFollow state.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MeleeWeapon : Weapon
{
[SerializeField] private float attackDelay = 1f;
private Collider2D damageAreaCollider2D;
private Animator animatorAttack;
private bool attacking;
private readonly int useMeleeWeapon = Animator.StringToHash(name:"UseMeleeWeapon");
private void Start()
{
damageAreaCollider2D = GetComponent<BoxCollider2D>();
animatorAttack = GetComponent<Animator>();
//animatorAttack.Play(useMeleeWeapon);
}
public override void UseWeapon()
{
StartCoroutine(routine: Attack());
}
/*protected override void Update()
{
base.Update();
// FlipMeleeWeapon();
}*/
private IEnumerator Attack()
{
if (attacking)
{
yield break;
}
// Attack
attacking = true;
damageAreaCollider2D.enabled = true;
animatorAttack.Play(useMeleeWeapon);
// Stop Attack
yield return new WaitForSeconds(attackDelay);
damageAreaCollider2D.enabled = false;
attacking = false;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(menuName = "AI/Decisions/Attack Completed", fileName = "AttackCompleted")]
public class DecisionAttackCompleted : AIDecision
{
public override bool Decide(StateController controller)
{
return AttackCompleted(controller);
}
private bool AttackCompleted(StateController controller)
{
if (controller.CharacterWeapon.CurrentWeapon.GetComponent<Animator>().GetCurrentAnimatorStateInfo(0).length
> controller.CharacterWeapon.CurrentWeapon.GetComponent<Animator>().GetCurrentAnimatorStateInfo(0).normalizedTime)
{
return true;
}
return false;
}
}
Ok I fixed the issue, I ended up placing this code into the WeaponMelee script, but now im dealing with a weird glitch where the AI keeps tring to face the left instead of the player when they are close to eachother. this has to do with the boxcollider2D so ive placed a capsulecollider2d inside of him and changed the script to capsulecollider2d as well. but the issue is still happening
private void OnTriggerEnter2D(Collider2D collision)
{
StartCoroutine(routine: Attack());
}

Audio stops playing after loading my game from the main menu scene

When I pause the game, the audio pauses too, but I created a "go back to menu" button, it goes to the main menu but when I hit start again and the game starts running, my audio is dead, I can not hear anything.
Here is my code:
using UnityEngine; using UnityEngine.SceneManagement;
public class pausemenu : MonoBehaviour { public GameObject pauseMenu;
public bool isPaused;
void Start()
{
pauseMenu.SetActive(false);
}
// Update is called once per frame
void Update()
{
if(Input.GetKeyDown(KeyCode.Escape))
{
if(isPaused)
{
ResumeGame();
}
else
{
PauseGame();
}
}
}
public void PauseGame()
{
pauseMenu.SetActive(true);
Time.timeScale = 0f;
isPaused = true;
AudioListener.pause = true;
}
public void ResumeGame()
{
pauseMenu.SetActive(false);
Time.timeScale = 1f;
isPaused = false;
AudioListener.pause = false;
}
public void GoToMainMenu()
{
Time.timeScale = 1f;
SceneManager.LoadScene("menu1");
}
public void QuitTheGame()
{
Application.Quit();
}
}
In the PauseGame() function you are setting the AudioListener.pause to true. So, in the GoToMainMenu() function set the AudioListener.pause to false.
If you pause your game and call PauseGame() AudioListener.pause is set to true.
If you resume you correctly set AudioListener.pause to false.
However, if you stop your game (by going into some main menu) and start a new one (or the previous one) the static bool AudioListener.pause is still false because of your pause menu intervention.
Even if your starting setup results in pausemenu.Start() getting called when starting a new game, this will not affect AudioListener.pause. You have to explicitly call pausemenu.ResumeGame().

Why the scene DontDestroyOnLoad is staying when switchig back to main menu?

The problem is that when I hit escape to go baco to main menu then I have the dont destroy objects original on the main menu and also the same objects in the DontDestroyOnLoad scene.
I have in the main menu scene 3 objects Player, Game Manager, Scene Loader that each one have attached a script DontDestroy :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DontDestroy : MonoBehaviour
{
private void Awake()
{
if (GameManager.backToMainMenu == false)
{
DontDestroyOnLoad(transform);
}
}
}
In the Game Manager object also attached another script :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameManager : MonoBehaviour
{
public SceneLoader sceneLoader;
public PlayerController playerController;
public CamMouseLook camMouseLook;
public static bool backToMainMenu = false;
public static bool togglePauseGame;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.P))
{
PauseGame();
}
if (Input.GetKeyDown(KeyCode.Escape))
{
BackToMainMenu();
}
}
public void PauseGame()
{
togglePauseGame = !togglePauseGame;
if (togglePauseGame == true)
{
playerController.enabled = false;
camMouseLook.enabled = false;
Time.timeScale = 0f;
}
else
{
playerController.enabled = true;
camMouseLook.enabled = true;
Time.timeScale = 1f;
}
}
private void BackToMainMenu()
{
sceneLoader.LoadScene(0);
playerController.enabled = false;
camMouseLook.enabled = false;
Cursor.lockState = CursorLockMode.None;
Time.timeScale = 0f;
backToMainMenu = true;
}
}
When I press the escape key it's switching scenes between 1 and 0 and loading scene 0 the main menu.
But then the result is this :
So I pressed escape and back to main menu but the DontDestroyOnLoad also still loaded not removed so I have this 3 objects Player, Game Manager, Scene Loader duplicated.
If I will click on new game again the main menu scene will be remove so there will be no duplication but when back to main menu the DontDestroyOnLoad is stay.
That's exactly what DontDestroyOnLoad it's supposed to do, preserve a GameObject across multiple scenes. Your GameManager script starts with backToMainMenu stetted to false, which will trigger the execution of DontDestroyOnLoad on the Awake function the first time, but not the second (since, when you go back to the main menu, backToMainMenu is setted to true).
Yup, the objects will persists across loading of scenes. That means if there are some already in a scene to begin with there will be multiple ones after loading / reloading a scene.
I deal with by making an Init class & object in every scene, which checks a static variable, and instantiates the objects that are supposed to persist. That way you can start the game from every scene.
class Init {
public static bool hasInstantiatedController = false;
public GameObject GameController;
void Awake() {
if (!hasInstantiatedController) {
hasInstantiatedController = true;
Instantiate (GameController, transform.position, transform.rotation);
}
}
}

How do I pause GameObject for a couple of seconds and unpause after a couple of seconds in unity3d

I need the gameobject to pause on its own in my scene for 7f or 8f and un-pause at 2f on its own. The script I have is letting my pause by key. Here is my script :
{
sing UnityEngine;
using System.Collections;
public class star : MonoBehaviour {
GameObject[] pauseObjects;
void Start () {
pauseObjects = GameObject.FindGameObjectsWithTag("Player");
}
void pauseGameobject()
{
if()
{
start coroutine("wait");
}
}
public ienumenator wait()
{
time.timescale = 0;
yield return new waitforsceonds(7);
time.timesale = 1;
}
void pauseGameobject()
{
if()
{
start coroutine("wait");
}
}
public ienumenator wait()
{
time.timescale = 0;
yield return new waitforsceonds(7);
time.timesale = 1;
}
}
You can use coroutines to insert delays in the update() loop. Coroutines use generators, which "yield", rather than functions/methods which "return". What this allows for is code that operates asynchronously while still being written in a linear fashion.
The built in coroutine you're most likely looking for is WaitForSeconds. To start a coroutine you simply call StarCoroutine() and pass in any method of type IEnumerator. This method will yield periodically. In the following example, WaitForSeconds(5) will yield after 5 seconds. Fractions of a second can also be used, represented by floats, for example 2.5 would be two and a half seconds.
using UnityEngine;
using System.Collections;
public class WaitForSecondsExample : MonoBehaviour {
void Start() {
StartCoroutine(Example());
}
IEnumerator Example() {
Debug.Log(Time.time); // time before wait
yield return new WaitForSeconds(5);
Debug.Log(Time.time); // time after wait
}
}
It's not too clear what you mean by pause on it's one, however I'm going to answer broadly, to try and help you.
If you whant to pause a single game object externally you can deactivate it and activate it accordingly with this code: gameObject.SetActive(false);
Instead if you want to pause the game object internally you can make a bool and in the update test wether or not it's true:
using UnityEngine;
using System.Collections;
bool update = false
public class ActiveObjects : MonoBehaviour
{
void Start ()
{
//Do stuff
}
void Update ()
{
if(update){
//Do stuff
}
//decide wether or not to pause the game object
}
}
If you want to pause the game you can set the Time.timeScale to 0, or just pause all game objects.
Here you can find how to make a timer, all you need to do is count down a variable using timeLeft -= Time.deltaTime;.
Hope I helped you,
Alex
Edit:
Ok, here is the script, keep in mind I have no way to test it ;)
using UnityEngine;
using System.Collections;
public class star : MonoBehaviour {
GameObject[] pauseObjects;
public float timer = 7;
float t = 0;
bool pause = false;
void Start () {
pauseObjects = GameObject.FindGameObjectsWithTag("Player");
t = timer;
}
void Update() {
if(pause){
if(t<0){
t=timer;
pause = false;
time.timescale = 1;
}else{
t -= Time.deltaTime;
time.timescale = 0;
}
}
}

Categories