I need to make an image pop up when the player has died or crashed but i do not know how to do it, i'm trying to make a game in unity using c#
but i have made a code that will tell show the user an image before they start (tap to start image) and all i want to do is display another one that tell to user to start again
does the code have to be similar to this or do i have to start from scratch?
public class StartScreenScript : MonoBehaviour {
static bool sawOnce = false;
// Use this for initialization
void Start () {
if(!sawOnce) {
GetComponent<SpriteRenderer>().enabled = true;
Time.timeScale = 0;
}
sawOnce = true;
}
// Update is called once per frame
void Update () {
if(Time.timeScale==0 && (Input.GetKeyDown(KeyCode.Space) || Input.GetMouseButtonDown(0)) ) {
Time.timeScale = 1;
GetComponent<SpriteRenderer>().enabled = false;
}
}
}
this code show the an image telling the user to tap the screen and the image then goes away until the user closes the game then comes back on however i want to display a "you are dead image" every time the player dies can someone please help me
p.s this is for a 2d game
Well one way to do it would be using Unity3D's GUI.DrawTexture that given a texture draws it at a given position. Here is a sample call to the method.
GUI.DrawTexture(new Rect(leftAnchor, topAnchor, textureWidth, textureHeight), textureSource);
This is my approach and it works in most cases:
Create a GameObject that will work as a dead screen. Apply a sprite
or whatever telling the user to click to restart.
Add the previous GameObject to the PlayerController so he can
instantiate it.
When the player is dead call PlayerController.ShowDeadScreen()
When the user clicks inside DeadScreen GameObject it will call your
PlayerController.PlayAgain function and destroy itself. So you must handle everything
the game need to be restarted.
PlayerController example code
public class PlayerController : MonoBehaviour {
public GameObject deadScreen;
void Start() { }
void Update() { }
public void ShowDeadScreen()
{
// show DeadScreen GameObject on the center of the screen
GameObject go = Instantiate(deadScreen, new Vector(0, 0, 0), Quaternation.Identity) as GameObject;
go.playerController = this;
}
public void PlayAgain()
{
// handle game restart
}
}
DeadScreen example code
public class DeadScreen : MonoBehaviour {
public PlayerController playerController;
void Start() { }
void Update() { }
void OnMouseDown()
{
// when user clicks inside this GameObject start the game again
playerController.PlayAgain();
Destroy(this.gameObject);
}
}
Related
So recently I started coding my first FPS game. I experienced a problem with my pause menu. The problem is when I have my game paused my mouse is still controling the camera and when I want to press some buttons in menu camera keeps following my mouse. I searched for solution to this problem on web, but I haven't found the solution (even my code is similar to some I've found).
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class PauseMenu : MonoBehaviour
{
public static bool gameIsPaused;
public GameObject pauseMenuUI;
void Update()
{
if (Input.GetKeyDown(KeyCode.Escape))
{
Pause();
}
}
public void Resume()
{
Cursor.lockState = CursorLockMode.Locked;
pauseMenuUI.SetActive(false);
Time.timeScale = 1f;
gameIsPaused = false;
}
void Pause()
{
Cursor.lockState = CursorLockMode.None;
pauseMenuUI.SetActive(true);
gameIsPaused=true;
Time.timeScale = 0f;
}
public void LoadMenu()
{
Time.timeScale = 1f;
SceneManager.LoadScene("Menu");
}
public void QuitGame()
{
Debug.Log("Quitting game...");
Application.Quit();
}
}
What I would do is to update the camera according to the pause conditon. like so:
public class CameraRotation : MonoBehaviour
{
public isGamePaused; // changed from outside when you pause/unpause the game
void Update()
{
if (isGamePaused) {
...
}
}
}
The problem with gameIsPaused (usual code convention naming to state is a bool would be isGamePaused :)) is that until you set it to true in the menu the camera will keep moving, so you may need to set the boolean to true at the time the menu pops up.
Even its not the suited case for static variables, if you want to check the pause state of your game from the camera script, you can do so like this:
public class CameraRotation : MonoBehaviour
{
public isGamePaused; // changed from outside when you pause/unpause the game
void Update()
{
if (PauseMenu.gameIsPaused) {
...
}
}
}
static stands for static in memory, so the variable value can be checked anytime from anywhere with ClassName.staticVariableName. With this I mean that as long as you set the PauseMenu.gameIsPaused variable at the times in the code where you´d like you should be able to make it work, by working I mean freeze/unfreeze the camera at the exact moment you want.
Your static PauseMenu.IsPaused is fine and a good way to do it although I usually put something like that on a GameManager class. You can find a Unity Singleton pattern online so you can start having PauseMenu.Instance.XXX so it's easy to access anything you need. That being said, you need to find the script controlling your camera and check the PauseMenu.IsPaused. Most likely you have a script attached to your camera, and all you generally need to do is find the Update() function and just do: if (PauseMenu.IsPaused) return; to stop it from working.
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);
}
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);
}
}
}
I have in the Hierarchy one scene and 3 parent objects. The 3 parent objects are :
Game Data (Where all the gameplay objects are childs of it)
Main Menu
Game Manager (That control on pausing/unpausing the game in some cases)
The idea the main goal is when the game start it's starting with the main menu then when clicking the start new game button it will start the game when hit the escape key it will be back to the main menu and again I can start a new game or resuming the game. I also did that while the game is running if you hit the P button it will pause/unpause the game.
First the script that is attached to the Game Manager :
Here I'm doing a reference for the Dialogue System, Depth Of Field, Main Menu And in the bottom I'm using find to find back the Depth Of Field and the Dialogue System since I'm destroying the Game Data object when starting a new game :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class GameController : MonoBehaviour
{
public GameObject dialogueSystem;
public DepthOfField depthOfField;
public static bool gamepaused = false;
public GameObject mainMenu;
private void Start()
{
Time.timeScale = 0f;
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.P))
{
gamepaused = !gamepaused;
if (gamepaused)
{
Time.timeScale = 0f;
}
else
{
Time.timeScale = 1f;
}
}
if (Input.GetKeyDown(KeyCode.Escape))
{
if(depthOfField == null || dialogueSystem == null)
{
depthOfField = GameObject.Find("Player Camera").GetComponent<DepthOfField>();
dialogueSystem = GameObject.Find("Dialogue System");
}
depthOfField.DepthOfFieldInit(3f);
Time.timeScale = 0f;
dialogueSystem.SetActive(false);
mainMenu.SetActive(true);
Cursor.visible = true;
Cursor.lockState = CursorLockMode.None;
}
}
}
The problem is that the Dialogue System and Depth Of Field are in the Game Data and when I destroy the Game Data and instantiate the Game Data using a prefab over again to make a new game to start the variables in the GameController script lost reference and I need to use find. But using find so many times is a good idea ?
This script the next one is attached to the Main Menu :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class MainMenu : MonoBehaviour
{
public GameObject gameDataPrefab;
public GameObject gameData;
public GameObject mainMenu;
public void StartNewGame()
{
var gdata = GameObject.Find("Game Data");
if (gdata != null)
{
Destroy(gdata);
}
GameObject go = Instantiate(gameDataPrefab);
go.name = "Game Data";
mainMenu.SetActive(false);
GameController.gamepaused = false;
Time.timeScale = 1f;
}
public void ResumeGame()
{
}
public void QuitGame()
{
Application.Quit();
}
}
Here when I click the UI button to start a new game in the function StartNewGame I destroy the Game Data and create a new one. This make the variables in the GameController script to lost references.
The last script is attached to the Game Data this script I'm calling the method DepthOfFieldInit to make effect when I hit the escape key back to the Main Menu.
The problem is that some variables are lost references since the Game Data is destroyed. For example I'm getting null on :
playerLockMode.PlayerLockState(true, true);
Since playerLockMode is also on the Game Data that have been destroyed.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.PostProcessing;
public class DepthOfField : MonoBehaviour
{
public UnityEngine.GameObject player;
public PostProcessingProfile postProcessingProfile;
public bool dephOfFieldFinished = false;
public LockSystem playerLockMode;
private Animator playerAnimator;
private float clipLength;
private Coroutine depthOfFieldRoutineRef;
// Start is called before the first frame update
void Start()
{
if (depthOfFieldRoutineRef != null)
{
StopCoroutine(depthOfFieldRoutineRef);
}
playerAnimator = player.GetComponent<Animator>();
AnimationClip[] clips = playerAnimator.runtimeAnimatorController.animationClips;
foreach (AnimationClip clip in clips)
{
clipLength = clip.length;
}
DepthOfFieldInit(clipLength);
// Don't forget to set depthOfFieldRoutineRef to null again at the end of routine!
}
public void DepthOfFieldInit(float duration)
{
var depthOfField = postProcessingProfile.depthOfField.settings;
depthOfField.focalLength = 300;
StartCoroutine(changeValueOverTime(depthOfField.focalLength, 1, duration));
postProcessingProfile.depthOfField.settings = depthOfField;
}
public IEnumerator changeValueOverTime(float fromVal, float toVal, float duration)
{
playerLockMode.PlayerLockState(true, true);
float counter = 0f;
while (counter < duration)
{
var dof = postProcessingProfile.depthOfField.settings;
counter += Time.deltaTime;
float val = Mathf.Lerp(fromVal, toVal, counter / duration);
dof.focalLength = val;
postProcessingProfile.depthOfField.settings = dof;
yield return null;
}
playerAnimator.enabled = false;
dephOfFieldFinished = true;
depthOfFieldRoutineRef = null;
}
}
But I don't understand if I instantiate right away back the Game Data when starting a new game why the variables lost references ? And is there a better way to use Find at any place many times after the Game Data have been destroyed ? And maybe the whole code in the StartNewGame is wrong ?
My main goal is when clicking the StartNewGame start over a new game. When hit the escape key pause the game and go back to the main menu.
Here is a screenshot of my Hierarchy :
I'm pretty sure that
animation.Play("DoorOpen");
Would play the animation "DoorOpen", but when i'm trying to put it in my code, it just giving me an error message:
The Animation attached to this GameObject (null if there is none attached).
using UnityEngine;
using System.Collections;
public class DoorPhysics : MonoBehaviour {
int Open = 0;
// Update is called once per frame
void Update() {
if (Open == 0) {
if (Input.GetKeyDown("e")) {
animation.Play("DoorOpen");
}
}
}
}
You need to show location of gameobjects in unity, they do not know eachother, you have to always use :
GameObject.GetComponent<T>()
GetComponentInParent<T>()
GetComponentInChildren<T>()
best practice is to get object references at Start()
also you should attach IMPORTANT!!! Animation component to the object this script it attached to
public class DoorPhysics : MonoBehaviour {
public Animation animation;
int Open = 0;
void Start()
{
animation=GameObject.GetComponent<Animation>(); //if your have derived type change Animation to good class DoorAnimation for example
}
void Update()
{
if (Open == 0) {
if (Input.GetKeyDown("e")) {
this.animation.Play("DoorOpen");
}
}
}
}
if that code wont work, you will need show me your GameObject hierarchy
And if your just start your trip with it learn MonoBehaviour call order
and life cycles of events