So I wanted to continue the same music from the same time while changing from the main menu to a new scene. so I did this:
using UnityEngine;
public class AudioManager : MonoBehaviour
{
public AudioSource bgm;
void Update()
{
DontDestroyOnLoad(bgm);
}
}
this works fine. but when I come back to the main menu the same music starts playing twice at the same time. So what can I do so that this doesn't happen?
Remove the Update function including the "DontDestroyOnLoad(bgm)" Line
Remove the variable named bgm
Create a static instance variable of the class in the class itself and use it to check whether you should destroy it or not. Example:
private static AudioManager audioManager;
private void Awake() {
DontDestroyOnLoad (gameObject);
if (audioManager == null) {
audioManager = this;
} else {
Destroy(gameObject);
}
}
Related
I am making Replay logic for my game, where when I click replay I got to the Main Page. The problem I am facing is that after clicking Play on the game after coming from Replay, the Zombie character in my game is not showing up. The game is running without the player. I am posting the script, check the Replay function which is attached to Replay button in the game.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
public class GameManager : MonoBehaviour
{
public static GameManager instance = null;
private bool playerActive = false;
private bool gameOver = false;
private bool gameStarted = false;
private GameObject newZombie;
[SerializeField] private GameObject mainMenu; //contains main menu content
[SerializeField] private GameObject endGame; //contains game over content
[SerializeField] private GameObject zombie;
public bool PlayerActive{
get{
return playerActive;
}
}
public bool GameOver{
get{
return gameOver;
}
}
public bool GameStarted{
get{
return gameStarted;
}
}
void Awake()
{
if(instance == null){
instance = this;
}else if(instance != this){
Destroy(gameObject);
}
Assert.IsNotNull(mainMenu);
Assert.IsNotNull(endGame);
DontDestroyOnLoad(gameObject);
}
// Start is called before the first frame update
void Start()
{
endGame.SetActive(false);
mainMenu.SetActive(true);
}
// Update is called once per frame
void Update()
{
}
public void PlayerCollided()
{
gameOver = true;
endGame.SetActive(true);
mainMenu.SetActive(false);
DontDestroyOnLoad(gameObject);
}
public void PlayerStartedGame()
{
playerActive = true;
}
public void EnterGame()
{
endGame.SetActive(false);
mainMenu.SetActive(false);
gameStarted = true;
}
public void Replay()
{
endGame.SetActive(false);
mainMenu.SetActive(true);
gameOver = false;
newZombie = Instantiate(zombie) as GameObject;
}
There are a lot of assumptions we have to make based on the information you gave.
Try instantiating the zombie on a specific location. You're using Instantiate(gameObject), but there's a different variant to the Instantiate method which also takes a Vector3 position to spawn the object as a second argument.
If that doesn't work, please reply with answers to the following questions:
Does the zombie spawn at all (is it in the hierarchy)
Which methods exactly do the buttons invoke, for example:
[1]: https://i.stack.imgur.com/9xbgy.png
You're using a singleton pattern but you don't change scenes in this class. Are you changing scenes in any of your scripts? If yes, you will have to consider looking into them because every script that is persisting between scenes with the DontDestroyOnLoad() method could potentially interfere with your player.
Vlad
I want to have a singleton which will store, update and show player score through all levels (scenes), but something works wrong.
This my singletone script GameStatus.cs:
using UnityEngine;
using TMPro;
public class GameStatus : MonoBehaviour
{
public static GameStatus instance;
[SerializeField] int pointsPerBlock = 50;
[SerializeField] TextMeshProUGUI scoreText;
[SerializeField] public static int currentScore = 0;
private void Awake()
{
if (instance != null)
{
Destroy(gameObject);
}
else
{
instance = this;
DontDestroyOnLoad(gameObject);
}
}
public void AddToScore()
{
currentScore += pointsPerBlock;
scoreText.text = currentScore.ToString();
}
}
I also have another script for objects player need to destroy, called Block.cs. Here it is:
using UnityEngine;
public class Block : MonoBehaviour
{
Level level;
GameStatus gameStatus;
private void Start()
{
level = FindObjectOfType<Level>();
gameStatus = FindObjectOfType<GameStatus>();
level.CountBreakableBlocks();
}
private void OnCollisionEnter2D(Collision2D collision)
{
DestroyBlock();
}
private void DestroyBlock()
{
level.BlockDestroyed();
gameStatus.AddToScore();
Destroy(gameObject);
}
}
And on level 1 everything works fine, but when the game goes to the next level this happens:
Player score stops updating.
If I use Debug.Log(currentScore); in GameStatus.cs I can see that this variable doesn't change when player breaks blocks, but if use Debug.Log(gameStatus.currentScore); in Block.cs then I can see that this variable is getting updated.
Debug.Log(FindObjectsOfType().Length); shows that there is one GameStatus object in the first level and two objects in the next levels, although I can't see the second one GameStatus in hierarchy.
So my question is - what's wrong and how to fix it?
If you use singleton, there is no point to make
currentScore
static variable, just make it
public int currentScore;
Also in your block cs you can just call
GameStatus.instance.AddToScore();
No need to make reference at start
I have multiple enemies set up in a level, all using the same behaviour and animator scripts. When I hit or kill one of them, all of them get hit or killed. I need them to function as separate instances.
I have tried referencing only an instance of the script:
private GoblinBehaviour goblin;
goblin = GetComponent<GoblinBehaviour>();
goblin.IncrementHits(1);
But that doesn't work. An error arises that says that script can't be accessed with an instance and needs a type instead.
code for hit detection script:
public class RangedDetection : MonoBehaviour
{
private GoblinBehaviour goblin;
void OnTriggerEnter(Collider other)
{
//on colliding destroy rocks after its life time
Destroy(gameObject, rockLifeTime);
if (other.gameObject.tag.Equals("Enemy"))
//if (other.tag == "Enemy")
{
Destroy(other.gameObject);
}
else if (other.gameObject.tag.Equals("Goblin"))//goblin should play
death animation
{
goblin = GetComponent<GoblinBehaviour>();
goblin.IncrementHits(1);
GetComponent<BoxCollider>().enabled = false; //Removing hit
collider so it only hits target once.
}
}
}
Simplified Code for goblin script:
public class GoblinBehaviour : MonoBehaviour
{
Transform player;
public static bool isDead;
public static bool isPunching;
public static bool isHit;
public GameObject particles;
public Animator anim;
public void IncrementHits(int hitCount)
{
isHit = true;
hits += hitCount;
if (hits >= 2) isDead = true;
}
void Die()
{
Instantiate(particles, transform.position, transform.rotation);
Destroy(gameObject);
}
void updateAnim()
{
anim.SetBool("Punch_b", isPunching);
anim.SetBool("hit", isHit);
}
}
Things should animate and act separately, I'm not sure how to reference only the current instance of the script.
While your code is incomplete and the problem cannot be said for sure, it looks like you are using statics improperly.
Static properties are instance analagous. In other words, all of your goblin instances share any static properties (isPunching, isHit, isDead, etc.). Making these values static allows you to reference them directly without having to obtain the instance you're affecting, but results in you updating all goblins at once.
Your solution will involve removing the static modifier from your GoblinBehaviour properties unless the properties are meant to be shared across all instances.
I just want reload a scene after GameOver. I have searched on the net but it didn't give me a clear reason and solution. Can someone point me the list of reason for this. I have posted below code for my scene reload.
Time.timeScale = 1;
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
Then my code for GameManager
public class GameManager : MonoBehaviour
{
private void Awake()
{
if (instance == null)
{
instance = this;
}
DontDestroyOnLoad(this);
}
}
Kindly inform me if you need additional info. Thanks
Hi from what I saw on your code you might be missing some stuff on your sigleton code for the game manager, for example not having multiple instances for the game manager (each time you reload a scene you're making a new one and not destroying it on load).
withouth seeing all your scene I can't find out the why but I did some simple code to reload a scene and keep one scene manager only.
using UnityEngine;
using UnityEngine.SceneManagement;
public class GMScript : MonoBehaviour
{
private static GMScript instance;
private void Awake()
{
if (instance == null || instance == this)
{
instance = this;
}
else
{
Destroy(this.gameObject);
}
DontDestroyOnLoad(this);
Debug.Log("Scene reloaded");
}
public void ReloadScene()
{
Time.timeScale = 1;
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}
private void Update()
{
if(Input.GetKeyDown(KeyCode.Space))
{
ReloadScene();
}
}
}
Also make sure the scene is added to project settings, I'm using space key for reloading but you can put out any method fits your needs.
Also make sure all your objects that will need access of the gamemanager update their reference once the scene loads(preferably on start method, as we use awake for the singleton handle), this can be done using a find method like GameObject.FindWithTag.
Found the problem I have this code
public class GameManager : MonoBehaviour
{
private void Awake()
{
if (instance == null)
{
instance = this;
}
DontDestroyOnLoad(this);
}
void Start()
{
shapeSpawnerGO = GameObject.Find("SpawnShapesObj");
scoreGO = GameObject.Find("ScoreText");
lifeGo = GameObject.Find("LifeText");
}
}
Then after reload scene the reference for this 3 becomes null it has something to do DontDestroyOnLoad(this); which means the GameManager script won't be destroyed after reload so when the game reloads the GameManager stays while it's reference get's destroyed. I'll check for efficient solution on this one
I tried both setting enabled to true and SetActive(true) to diplay a Gameover Image. However, none of them works. I have a public Image gameOverImage declared and set the gameOverImage.enabled in the Start() to false.
private void Start()
{
gameOverImage.enabled = false;
}
Then in one of my function, I put:
public void killAt(Vector2 loc)
{
foreach (GameObject obj in setup.GetActive())
{
if (obj.GetComponent<PieceInfo>().GetLocation() == loc)
{
if (obj.GetComponent<PieceInfo>().GetPieceType() == 'G')
{
gameOver = true;
gameOverImage.enabled = true;
Debug.Log("?????");
}
setup.deactivate(obj);
break;
}
}
}
The console does have ????? logged but no gameOverImage displayed in the game view. The game is over because I couldn't click my game any more. Any ideas? I also tried UI text. It doesn't work as well.
In Unity in order to activate an object you need to have it in the scene. If the GameObject does not exist in the scene, or in your case the UI Element that contains the Image, is not in your scene SetActive(true) or Enabled = true will not have any effect. You will need to instantiate the object. To make it exist in your world.
Prefabs, are useful to store a common configuration that can be used multiple times in your game but they do not exist in the scene, that is why you have to instantiate them.
For your GameOver Image you have a few options the simplest is this:
using UnityEngine;
using UnityEngine.UI;
public class EnableUI : MonoBehaviour {
public Image GameOverImage;
// Use this for initialization
void Start () {
GameOverImage.gameObject.SetActive(false);
}
// Update is called once per frame
void Update () {
if(Input.GetKeyDown(KeyCode.Space))
{
GameOverImage.gameObject.SetActive(true);
}
}
}
If you want to instantiate it:
using UnityEngine;
public class EnableUI : MonoBehaviour {
// This is a prefab that is canvas with your game over image nested as a child image UI under it
public GameObject GameOverObject;
// Use this for initialization
void Start () { }
// Update is called once per frame
void Update () {
if(Input.GetKeyDown(KeyCode.Space))
{
Instantiate(GameOverObject);
}
}
}