I'm trying to keep the player skin, even when reloading the scene, or moving on to a new one. The player object changes every scene.
The player skin is chosen in a pause menu, with three buttons. Each one of those buttons calls a function in the script below. I'm trying to call one of these functions, based on what value the PlayerPrefs int holds, and the function does get called; But throws the error MissingReferenceException: The object of type 'GameObject' has been destroyed but you are still trying to access it
Below is what i have already tried, but this throws an error on scene reload (death)
i don't know what i'm doing wrong here.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class Pausemenu : MonoBehaviour
{
public static bool gameIsPaused = false;
public GameObject pauseMenuUI;
public Material BelgianMat;
public Material Ball2;
public Material Rainbow;
public GameObject Player;
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Escape)) {
if (gameIsPaused) {
Resume();
} else {
Pause();
}
}
}
public void Resume(){
pauseMenuUI.SetActive(false);
Time.timeScale = 1f;
gameIsPaused = false;
}
void Pause() {
pauseMenuUI.SetActive(true);
Time.timeScale = 0f;
gameIsPaused = true;
}
public void LoadMenu() {
Time.timeScale = 1f;
gameIsPaused = false;
SceneManager.LoadScene("Menu");
}
public void QuitGame() {
Debug.Log("Quitting");
Application.Quit();
}
public void ApplyBelgian() {
Player.GetComponent<Renderer>().material = BelgianMat;
PlayerPrefs.SetInt("playerMat", 0);
}
public void ApplyBall2() {
Player.GetComponent<Renderer>().material = Ball2;
Debug.Log("Applied ball two");
PlayerPrefs.SetInt("playerMat", 1);
}
public void ApplyRainbow() {
Player.GetComponent<Renderer>().material = Rainbow;
PlayerPrefs.SetInt("playerMat", 2);
}
void OnEnable()
{
Debug.Log("OnEnable called");
SceneManager.sceneLoaded += OnSceneLoaded;
}
// called second
void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
Debug.Log("OnSceneLoaded: " + scene.name);
Debug.Log(mode);
if (PlayerPrefs.GetInt("playerMat") == 0) {
ApplyBelgian();
}
else if (PlayerPrefs.GetInt("playerMat") == 1) {
Debug.Log("gonna apply ball 2");
ApplyBall2();
}
else if (PlayerPrefs.GetInt("playerMat") == 2) {
ApplyRainbow();
}
}
}
I don't know why but it seems like the reference to the Player object may be broken after loading the scene. If this is the case, add a "Player" tag to your Player object and add this to the OnEnable function: Player = GameObject.FindWithTag("Player");
Related
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class PauseMenu : MonoBehaviour
{
public static bool gameIsPaused = false;
public GameObject PauseMenuUI;
void Update()
{
if (Input.GetKeyDown(KeyCode.Escape))
{
if (gameIsPaused)
{
Resume();
} else
{
Pause();
}
}
Debug.Log("KeyInput");
}
public void Resume ()
{
PauseMenuUI.SetActive(false);
Time.timeScale = 1f;
gameIsPaused = false;
}
void Pause ()
{
PauseMenuUI.SetActive(true);
Time.timeScale = 0f;
gameIsPaused = true;
}
public void LoadMenu()
{
Debug.Log("Loading Menu...");
Time.timeScale = 1f;
SceneManager.LoadScene ("MainMenu");
}
public void QuitGame()
{
Debug.Log ("Quitting Game");
Application.Quit();
}
}
My code wont open the actual pause screen is there any reason why
i have tried editing it in different ways and i also tried some other code online but it never seems to work for me i have also went onto the unity forms and that wasn't really helpful
im trying to follow this video
and i think i did everything right but its not doing anything then i click the escape button
using System.Collections.Generic;
using UnityEngine;
public class PauseMenu : MonoBehaviour
{
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();
}
}
}
public void Resume()
{
pauseMenuUI.SetActive(false);
Time.timeScale = 1f;
GameIsPaused = false;
}
void Pause()
{
pauseMenuUI.SetActive(true);
Time.timeScale = 0f;
GameIsPaused = true;
}
public void LoadMenu()
{
Debug.Log("loading menu");
}
public void QuitGame()
{
Debug.Log("quiting game");
}
}
this is how the panel looks like
it was before in Canvas but i moved it because i was trying to make it work but it didnt help
someone knows maybe how to fix it?
I'm doing an educational game for kids..
But I stopped at the end of the scene
I could not make a code to start the new scene..
in the First Script
when play game the scenes does not stop until the last scene.
YouWin.pictureInPlace++;
I searched a lot and did not find my question, so I consulted you. It was easier to do a button to go to the Next scene but I prefer to do it automaticly
I think this task can be accomplished by the boolean but its need to reference game object.. and the script on 2 images.
The First Script (Manager) has put on Four images At Canvas..
The second one (YouWin) I put on empty GameObject..
thanks for the help
The first script (Manger)
using UnityEngine;
using UnityEngine.EventSystems;
public class Manager : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
Vector2 pos1;
public GameObject pos2;
private bool canMove;
public GameObject winner;
void Start()
{
pos1 = transform.position;
canMove = true;
}
public void OnBeginDrag(PointerEventData eventData)
{
Debug.Log(eventData);
}
public void OnDrag(PointerEventData eventData)
{
if (canMove)
transform.position = Input.mousePosition;
}
public void OnEndDrag(PointerEventData eventData)
{
float distance = Vector3.Distance(transform.position, pos2.transform.position);
if (distance < 50)
{
transform.position = pos2.transform.position;
transform.localScale = pos2.transform.localScale;
canMove = false;
winner.GetComponent<YouWin>().pictureInPlace++;
}
else
{
transform.position = pos1;
}
}
}
The second script (YouWin)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class YouWin : MonoBehaviour
{
public int NumberOfImages;
public int pictureInPlace;
public string sceneName;
void Update()
{
if (pictureInPlace == NumberOfImages)
{
StartCoroutine(LoadScene());
Debug.Log("You Win!");
}
}
IEnumerator LoadScene()
{
yield return new WaitForSeconds(1.5f);
SceneManager.LoadScene(sceneName);
}
}
It looks like you didn't reference YouWin in your Manager script. You should include it by adding it as a global variable public YouWin <variable_name>; or by referencing your empty GameObject and getting the YouWin component:
public GameObject emptyObject;
emptyObject.GetComponent<YouWin>().picturesInPlace++;
After all thanks for helping me but I was able to find the right solution
just make in the first Script (Manager) :
bool static Done;
void Start()
{
bool Done = false;
}
public void OnEndDrag(PointerEventData eventData)
{
float distance = Vector3.Distance(transform.position, pos2.transform.position);
if (distance < 50)
{
transform.position = pos2.transform.position;
transform.localScale = pos2.transform.localScale;
canMove = false;
bool Done = True;
}
}
and just call it from the second Script (YouWin)
public string sceneName;
void Update()
{
if(Youwin.Done)
{
StartCoroutine(LoadScene());
}
}
IEnumerator LoadScene()
{
yield return new WaitForSeconds(0.5f);
SceneManager.LoadScene(sceneName);
}
This IS The right solution ;
So I put the object in the scene and then I made it "invisible" (deactivate if you will) from the inspector (the checkmark box next to the object's name) and after waiting 8 seconds it doesn't become visible. I am using Unity 2d and C#.
I have the game start paused for three seconds then plays after that which works. The first script is that one. The item is supposed to reappear after 8 seconds so after the game resumes, which doesn't work.
//delay before level starts script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class countDown : MonoBehaviour
{
public GameObject CountDown;
private void Start()
{
StartCoroutine("StartDelay");
}
void Update()
{
}
IEnumerator StartDelay()
{
Time.timeScale = 0;
float pauseTime = Time.realtimeSinceStartup + 3f;
while (Time.realtimeSinceStartup < pauseTime)
yield return 0;
CountDown.gameObject.SetActive(false);
Time.timeScale = 1;
}
{
//script for the flower to appear
IEnumerator Start()
{
print(Time.time);
yield return new WaitForSeconds(8);
print(Time.time);
flowerInScene.gameObject.SetActive(true);
}
[SerializeField] Transform flowerInScene;
}
I still don't really get your two methods called Start
You can simply call a StartCoroutine at the end of another Coroutine so you can chain them together (though there are surely better ways to do what you want in general):
using System.Collections;
using UnityEngine;
public class CountDown : MonoBehaviour
{
public GameObject CountDownObject;
public GameObject flowerObject;
private void Start()
{
StartCoroutine(Delay());
}
private IEnumerator Delay()
{
yield return new WaitForSeconds(3);
HideCountdown();
StartCoroutine(FlowerDelay());
}
private void HideCountdown()
{
CountDownObject.SetActive(false);
}
private IEnumerator FlowerDelay()
{
yield return new WaitForSeconds(8);
ShowFlower();
}
private void ShowFlower()
{
flowerObject.SetActive(true);
}
}
I personaly don't like Coroutines .. they are not so easy to debug sometimes. I would prefer doing something like this with simple timers (though in the first moment it does look worse). Advantage is I can now directly watch the timer count down in the inspector:
using UnityEngine;
public class SimpleCountDown : MonoBehaviour
{
[Header("The Objects")]
public GameObject CountDownObject;
public GameObject FlowerObject;
[Header("Settings")]
// Here you can adjust the delay times
public float StartOffset = 3;
public float FlowerOffset = 8;
[Header("Debug")]
public float startTimer;
public float flowerTimer;
public bool isStartDelay;
public bool isFlowerDelay;
private void Start()
{
startTimer = StartOffset;
flowerTimer = FlowerOffset;
isStartDelay = true;
}
private void Update()
{
if (!isStartDelay && !isFlowerDelay) return;
if (isStartDelay)
{
startTimer -= Time.deltaTime;
if (startTimer <= 0)
{
HideCountdown();
isStartDelay = false;
isFlowerDelay = true;
}
}
if (isFlowerDelay)
{
flowerTimer -= Time.deltaTime;
if (flowerTimer <= 0)
{
ShowFlower();
isFlowerDelay = false;
this.enabled = false;
}
}
}
private void HideCountdown()
{
CountDownObject.SetActive(false);
}
private void ShowFlower()
{
FlowerObject.SetActive(true);
}
}
In the game I am creating a 'Game Over' scene which loads once the player loses the game. During the game, the score is counted by the player's position on the screen and increases as the player's y-axis position increases.
I used this code to display the score on the scene the game was actually played:
using UnityEngine;
using UnityEngine.UI;
public class PlayerScore : MonoBehaviour
{
public Transform player;
public Text scoreText;
// Update is called once per frame
void Update()
{
scoreText.text = player.position.y.ToString("0");
}
}
I tried to use the same coding to display the final score of the player on the 'Game Over' screen. The problem I faced was that I was not able to identify the player on the 'Game Over' scene as the player was not an object or sprite on that scene.
Is there an easy way to reference the player sprite from the previous scene into the final ('Game Over') scene so I can use it to determine the final score?
This is the script I tried with a game object of the 'EndScene' within the playing scene:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class EndMenu: MonoBehaviour
{
public Text scoreText;
public Transform player;
public static bool GameEnds = false;
public GameObject endMenuUI;
void OnCollisionEnter2D(Collision2D exampleCol)
{
if(exampleCol.collider.tag == "Obstacle")
{
endMenuUI.SetActive(true);
Time.timeScale = 0.0001f;
GameEnds = true;
}
}
public void Retry()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex - 1);
}
public void BackToMenu()
{
SceneManager.LoadScene("Menu");
}
public void Quit()
{
Debug.Log("Quitting game...");
Application.Quit();
}
// Update is called once per frame
void Update()
{
scoreText.text = player.position.y.ToString("0");
}
}
You can keep the player using DontDestroyOnLoad
You could add this to the player:
void Awake() {
DontDestroyOnLoad(transform.gameObject);
}
You could do other stuff like keeping just the data... but the easier way should be this one.
Keep in mind that the player will remain in you gameover screen.
In my opinion the best idea could be creating a new gameobject in your game scene called "GameOverScreen" and disable it until you need it.
I think the easiest solution would be to simply store the score as a static variable that will persist across scene loads, then manually reset it when you start over. For example:
public class PlayerScore : MonoBehaviour
{
public Transform player;
public Text scoreText;
public static string scoreString;
// Update is called once per frame
void Update()
{
scoreString = player.position.y.ToString("0");
scoreText.text = scoreString;
}
}
Now you will be able to access scoreString from anywhere in your code and it will be whatever it was when the PlayerScore component last ran its Update(). Then in your EndMenu class, you would simply update your Retry() and BackToMenu() methods like so:
public void Retry()
{
PlayerScore.scoreString = "0";
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex - 1);
}
public void BackToMenu()
{
PlayerScore.scoreString = "0";
SceneManager.LoadScene("Menu");
}
And your EndMenu.Update() method becomes the following:
// Update is called once per frame
void Update()
{
scoreText.text = PlayerScore.scoreString;
}
//try to use playerprefs to save the score
public class PlayerScore : MonoBehaviour
{
public Transform player;
public Text scoreText;
// Update is called once per frame
void Update()
{
scoreText.text = player.position.y.ToString("0");
PlayerPrefs.SetInt("CurrentScore", player.position.y);
}
}
//for another scene get the value of saved score using playerprefs.getInt
public class EndMenu: MonoBehaviour
{
public Text scoreText;
public Transform player;
public static bool GameEnds = false;
public GameObject endMenuUI;
void OnCollisionEnter2D(Collision2D exampleCol)
{
if(exampleCol.collider.tag == "Obstacle")
{
endMenuUI.SetActive(true);
Time.timeScale = 0.0001f;
GameEnds = true;
}
}
public void Retry()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex - 1);
}
public void BackToMenu()
{
SceneManager.LoadScene("Menu");
}
public void Quit()
{
Debug.Log("Quitting game...");
Application.Quit();
}
// Update is called once per frame
void Update()
{
scoreText.text = PlayerPrefs.GetInt("Score",0);
}
}