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
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 am making a multiplayer game in which the characters can change scenes. I have items scattered in different scenes which the player can pick up and add to his inventory. The items have an 'AddItem' script attached to them. If the player is in the first scene and picks up the item, it is added to the inventory, however when the player changes the scene and picks up an item in that scene (which is using same script), it throws null reference error. Here is the script:
public class AddItem : MonoBehaviour
{
public Item item;
DialogueConditions dc;
private GameObject player;
private void Start()
{
player = GameObject.Find("Engineer");
dc = player.GetComponent<DialogueConditions>();
}
void pickUp()
{
Debug.Log("Picking up : " + item.name);
HasBriefDoc();
bool itemPickedUp = Inventory.instance.Add(item);
if(itemPickedUp == true)
{
Destroy(gameObject);
}
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
pickUp();
}
}
public void HasBriefDoc()
{
if (item == null)
{
dc.hasBrief = false;
}
else if (item.name == "Brief Doc")
{
dc.hasBrief = true;
}
else
{
dc.hasBrief = false; //throws null reference
}
}
}
After debugging, I found out that after scene change, the object 'dc' is assigned with null reference in the Start() method. I also tried initializing it with FindObjectOfType<DialogueConditions>(); and adding the start code to the Awake() method, but the problem still persists. The only way it is working is when I drop the 'dc' object and directly use static variables, which is something I do not prefer.
To anyone who is facing this issue, my problem was occurring due to wrong execution order of functions. The 'AddItem' script was running before my player had even instantiated, on which this DialogueConditions script was attached. That is why it was returning null reference. I changed the order of execution by instantiating my player in the Awake() method, as it will then run before the Start() method of AddItem, hence the null reference exception was removed.
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);
}
}
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.
When my Player (GameObject) meets Lava, they should respawn in a specific scene.
This is the code I have assigned to the Player:
void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "Lava")
{
GameObject.Find("Controller").GetComponent<Controller>().Respawn();
}
}
Controller is a GameObject, that I don't want to Destroy by Changing level, so this is the code for my Controller GameObject:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class Controller : MonoBehaviour
{
private static bool created = false;
public static Controller instance;
GameObject Player;
Vector3 respawnPoint;
void Awake()
{
if (instance = null)
{
instance = this;
}
else
{
Destroy(this.gameObject);
return;
}
if (!created)
{
DontDestroyOnLoad(this.gameObject);
created = true;
Player = GameObject.Find("Player");
respawnPoint = GameObject.Find("RespawnPoint").transform.position;
}
}
public void Respawn()
{
SceneManager.LoadScene(0);
Player.transform.position = respawnPoint;
}
}
RespawnPoint is just an invisible Cube GameObject, where I want the player to respawn.
Let's say the Game Starts with Scene "0" (this is where the RespawnPoint is, too.)
Then the Player goes to Scene "1" and dies (meets Lava). Then I want the Game to change back to Scene "0" and teleport the Player to the RespawnPoint.
The Scene-Change works good, but the player always starts at the same position, where he starts the first time and he's not teleported to the RespawnPoint.
What am I doing wrong?!
First of all you lack the "==" in the first 'if' from the Awake: if (instance == null
The code is fine or it does seem so to me, but the RespawnPoint should be in the Scene your Player meets the lava not in the Scene you are loading. If not the starting position of the player will always be (0,0,0).
I would recommend coming to this in a completely different way. I would make a public Transform[] Spawnpoints. Since the transform is public you can assign different objects to it. Make an empty game object and position it where you want to spawn. Then use
Void OnTriggerEnter(collider2D, other){
if(other.gameObject.tag == lava) {
transform.position = spawnpoints[0].position;
}
}
In the inspector set the Transform to be a size of 1 and set the respawn GameObject as the one transform.
Thanks to your answers, they helped me solving this problem.
I added a "DontDestroyOnLoad" to the RespawnPoint and than i changed the Controller Code to this:
{
private static bool created = false;
public static Controller instance;
void Awake()
{
if (instance == null)
{
instance = this;
}
else
{
Destroy(this.gameObject);
return;
}
if (!created)
{
DontDestroyOnLoad(this.gameObject);
created = true;
}
}
public void Respawn()
{
SceneManager.LoadScene(0);
GameObject.Find("Player").transform.position = GameObject.Find("RespawnPoint").transform.position;
}
}
now the player gets teleported to the correct RespawnPoint. Thanks for your help!