I am very new to Unity and C#, and am just starting to learn the basics. I made a simple game that has a cube moving forward, and if it hits an obstacle, the game restarts. However, when I try to use Invoke to delay the time after the game restarts when the cube collides, the restart is instant.
I haven't been able to try much since I am still new to C# and Unity.
This is my GameManager script:
using UnityEngine;
using UnityEngine.SceneManagement;
public class GameManager : MonoBehaviour
{
bool gameHasEnded = false;
public float restartDelay = 3f;
public void EndGame()
{
if (gameHasEnded == false)
{
gameHasEnded = true;
Debug.Log("Game Over!");
Invoke("Restart", restartDelay);
Restart();
}
}
void Restart()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}
}
When the player collides with an object (Obstacle script):
using UnityEngine;
public class PlayerCollision : MonoBehaviour
{
public PlayerMovement movement;
void OnCollisionEnter(Collision collisionInfo)
{
if (collisionInfo.collider.tag == "Obstacle")
{
movement.enabled = false;
FindObjectOfType<GameManager>().EndGame();
}
}
}
I want the restart to be delayed so the game doesn't restart instantly. Any help would be greatly appreciated :)
You called Restart() after invoking the Restart function with a delay.
// ...
if (gameHasEnded == false) {
gameHasEnded = true;
Debug.Log("Game Over!");
Invoke("Restart", restartDelay);
// This below is called instantly.
// It does not wait for the restartDelay.
Restart();
}
// ...
Just simply remove the Restart() call like so:
if (gameHasEnded == false) {
gameHasEnded = true;
Debug.Log("Game Over!");
Invoke("Restart", restartDelay);
}
Note that the code does not 'pause' at Invoke(). Think of Invoke as a async/coroutine operation.
Related
I'm trying to make my enemy freeze when it gets in contact with an ice projectile. Everything works no errors except the animator doesn't play the animations and the script doesn't get enabled / disabled.
I've tried script = GetComponent(); and anim = gameObject.GetComponent(); too for that section. And for the set active ive tried script.enabled script.setactive ect ect. None of it works. I think this might have to do with the get component but IDK. These are on instantiated prefabs btw.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyHealth : MonoBehaviour
{
public float health;
public float freezeTime;
public Animator anim;
public Enemy script;
// Start is called before the first frame update
IEnumerator Wait(){
yield return new WaitForSeconds (freezeTime);
Debug.Log("waiting over");
}
void Start()
{
anim = gameObject.GetComponent<Animator>();
script = gameObject.GetComponent<Enemy>();
}
// Update is called once per frame
void Update()
{
if(health <= 0){
Dead();
}
}
void OnTriggerEnter2D(Collider2D other){
if(other.CompareTag("Harmful")){
Destroy(other.gameObject);
TakeDamage();
}
if(other.CompareTag("Freezing")){
anim.SetBool("IsFrozen", true);
Destroy(other.gameObject);
gameObject.GetComponent<Enemy>().enabled = false;
TakeDamage();
Debug.Log("waiting started");
StartCoroutine(Wait());
anim.SetBool("IsFrozen", false);
gameObject.GetComponent<Enemy>().enabled = true;
}
}
void TakeDamage(){
health -= 1f;
}
void Dead(){
Destroy(gameObject);
}
}
The problem was not in fact the animator or the scripts being set to false but it was actually because I had started a wait function but it started the wait function and immediately ran the code that was supposed to go after the wait function.
TLDR; waited but the code ran anyway.
If you want the completed working code here it is. The spacing is a little weird because of stackoverflow btw:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyHealth : MonoBehaviour
{
public float health;
public float freezeTime;
public Animator anim;
public Enemy script;
public Rigidbody2D rb;
public bool WaitOver;
// Start is called before the first frame update
IEnumerator Wait(){
yield return new WaitForSeconds (freezeTime);
WaitOver = true;
Debug.Log("waiting over");
}
void Start()
{
anim = gameObject.GetComponent<Animator>();
script = gameObject.GetComponent<Enemy>();
rb = gameObject.GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
if(health <= 0){
Dead();
}
if (WaitOver == true){
anim.SetBool("IsFrozen", false);
gameObject.GetComponent<Enemy>().enabled = true;
rb.isKinematic = false;
}
}
void OnTriggerEnter2D(Collider2D other){
if(other.CompareTag("Harmful")){
Destroy(other.gameObject);
TakeDamage();
}
if(other.CompareTag("Freezing")){
WaitOver = false;
anim.SetBool("IsFrozen", true);
rb.isKinematic = true;
Destroy(other.gameObject);
GetComponent<Enemy>().enabled = false;
TakeDamage();
Debug.Log("waiting started");
StartCoroutine(Wait());
}
}
void TakeDamage(){
health -= 1f;
}
void Dead(){
Destroy(gameObject);
}
}
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`).
so I’m trying to create a respawn system as a beginner, and everything seems to be working the first time but the second time it seems to be unbehaving. If anyone knows how to help me, I would appreciate it
LevelControl:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class LevelControl : MonoBehaviour
{
public int index;
public string levelName;
public GameObject GameOverPanel;
public static LevelControl instance;
private void Awake()
{
if (instance == null)
{
DontDestroyOnLoad(gameObject);
instance = GetComponent<LevelControl>();
}
}
void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Player"))
{
//Loading level with build index
SceneManager.LoadScene(index);
//Loading level with scene name
SceneManager.LoadScene(levelName);
//Restart level
//SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
}
public void LoadLevel1()
{
SceneManager.LoadScene("Game");
}
public GameObject GetGameOverScreen()
{
return GameOverPanel;
}
}
PlayerMovement:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public CharacterController2D controller;
public float runSpeed = 40f;
float horizontalMove = 0f;
bool jump = false;
bool crouch = false;
// Update is called once per frame
void Update()
{
horizontalMove = Input.GetAxisRaw("Horizontal") * runSpeed;
if (Input.GetButtonDown("Jump"))
{
jump = true;
}
if (Input.GetButtonDown("Crouch"))
{
crouch = true;
}
else if (Input.GetButtonUp("Crouch"))
{
crouch = false;
}
}
void FixedUpdate()
{
// Move our character
controller.Move(horizontalMove * Time.fixedDeltaTime, crouch, jump);
jump = false;
//FindObjectOfType<GameManager>().EndGame();
}
void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "Enemy")
{
//Destroy(FindObjectOfType<CharacterController2D>().gameObject);
GameObject.Find("Player").SetActive(false);
LevelControl.instance.GetGameOverScreen().SetActive(true);
}
}
}
Error In Unity Video: https://imgur.com/a/Sr0YCWk
If your wondering what I'm trying to do, well, when the 2 colliders of the Player and Enemy collide, I want a restart button to pop up, and the Character to get destroyed, and after that restart the level as is.
You didn't provide much but I try to work with what we have.
In LevelController you have
private void Awake()
{
if (instance == null)
{
DontDestroyOnLoad(gameObject);
instance = GetComponent<LevelControl>();
}
}
First of all just use
instance = this;
;)
Then you are doing
LevelControl.instance.GetGameOverScreenn().SetActive(true);
I don't see your setup but probably GetGameOverScreenn might not exist anymore after the Scene reload while the instance still does due to the DontDestroyOnLoad.
Actually, why even use a Singleton here? If you reload the entire scene anyway you could just setup the references once via the Inspector and wouldn't have to worry about them later after scene changes...
Also
GameObject.Find("Player").SetActive(false);
seems odd .. isn't your PlayerController attached to the Player object anyway? You could just use
gameObject.SetActive(false);
Ok, Normally it would be easier just to do this:
if (collision.gameObject.tag == "Enemy")
{
//Destroy(FindObjectOfType<CharacterController2D>().gameObject);
gameObject.SetActive(false);
LevelControl.instance.GetGameOverScreen().SetActive(true);
But this will NOT work if you want to attach the script to any other gameobjects for any reason. If you are then first make a game object variable containing the player, like this:
public GameObject Player = GameObject.Find("Player");
Then say
Player.SetActive(false);
This creates a player gameobject which you can access by calling the variable.
In a game I am working on, the player uses a cube to open doors. When the player interacts with the cube while near the door, the door slides open. I am now trying to make it so that while it slides open, it makes a sound as doors should but I kinda ran into some issues. When I tried to add in the sound for the door, it ignored the part where the door should open altogether.
Here is what I did:
I added an AudioSource to the Cube's child object, CORE because the Cube object already contains an AudioSource which will play at the same time as this sound and assigned it in my Cube's script...
public AudioSource Woosh;
void Start()
{
//Debug.Log("Script initialized!");
Hm = gameObject.GetComponent<AudioSource>();
anim = GameObject.Find("TheFirstDoor").GetComponent<Animator>();
//Door = GameObject.Find("TheFirstDoor").GetComponent<AudioSource>();
Woosh = GameObject.Find("CORE").GetComponent<AudioSource>();
}
Here's the interactive part of the script, it runs a check but for some reason, it is pretending CanBeKey is false...
public void OnActivate()
{
if(HasPlayed == false) //Have I played? Has the Time out passed?
{
HasPlayedFirstTime = true;
Hm.Play();
HasPlayed = true;
PlayTimeOut = 30.0f;
if(CanBeKey == true)
{
anim.Play("Door_Open");
//Door.Play();
StartCoroutine(PlayDaWoosh());
Debug.Log("OPENING!");
}
}
}
Now here is the IEnumerator part of the script, PlayDaWoosh()
IEnumerator PlayDaWoosh()
{
Woosh.PlayOneShot(Woosh.clip);
yield return new WaitForSeconds(Woosh.clip.length);
}
I am aware that the last code snippet is a bit messy but it was the best thing I can think of.
Here is the full script in case you are that curious.....
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CUBE_CORE : MonoBehaviour
{
public AudioSource Hm; // HM!!!
private bool HasPlayed = false;
public float PlayTimeOut = 0.0f;
public static bool CanBeKey = false;
public Animator anim;
public static bool HasPlayedFirstTime = false;
public static AudioSource Door;
public AudioSource Woosh;
// Start is called before the first frame update
void Start()
{
//Debug.Log("Script initialized!");
Hm = gameObject.GetComponent<AudioSource>();
anim = GameObject.Find("TheFirstDoor").GetComponent<Animator>();
//Door = GameObject.Find("TheFirstDoor").GetComponent<AudioSource>();
Woosh = GameObject.Find("CORE").GetComponent<AudioSource>();
}
// Update is called once per frame
void Update()
{
if(HasPlayed == true)
{
if (PlayTimeOut < 0.0f)
{
HasPlayed = false;
}
PlayTimeOut -= Time.smoothDeltaTime;
}
}
public void OnActivate()
{
if(HasPlayed == false) //Have I played? Has the Time out passed?
{
HasPlayedFirstTime = true;
Hm.Play();
HasPlayed = true;
PlayTimeOut = 30.0f;
if(CanBeKey == true)
{
anim.Play("Door_Open");
//Door.Play();
StartCoroutine(PlayDaWoosh());
Debug.Log("OPENING!");
}
}
}
private void OnTriggerEnter(Collider other)
{
if(other.gameObject.name == "SecondDoor")
{
anim = GameObject.Find("DoorSecond").GetComponent<Animator>();
}
if(other.gameObject.name == "ThirdDoor")
{
anim = GameObject.Find("TheThirdDoor").GetComponent<Animator>();
}
if(other.gameObject.name == "FourthDoor")
{
anim = GameObject.Find("TheFourthDoor").GetComponent<Animator>();
}
}
IEnumerator PlayDaWoosh()
{
Woosh.PlayOneShot(Woosh.clip);
yield return new WaitForSeconds(Woosh.clip.length);
}
}
Expected result: Door opening and sound playing
Actual result: Neither happening unless I remove anything that has to do with the Woosh
I apologize ahead of time if I wasn't specific enough or if the question was answered somewhere else. I am relatively new here.
I have wrote a code that semi-works for me. Everything works aside from working animation.
What I'm trying to do is that when Player enters trigger, and if he/she presses E it will perform an animation. (Animation is working fine btw, so it's just code error?).
Here's what I got so far:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class barTrigger : MonoBehaviour {
// press shit to drink text
public GameObject drinkText;
// bottle
public GameObject alcBottle;
// drink animator
public GameObject animatorOjbect;
Animator drinkAmin;
public bool triggerIsOn;
// Use this for initialization
void Start () {
drinkAmin = animatorOjbect.GetComponent<Animator> ();
drinkText.SetActive(false);
alcBottle.SetActive(false);
}
// Update is called once per frame
void Update () {
}
void OnTriggerEnter(Collider other){
triggerIsOn = true;
if (other.gameObject.name == "vThirdPersonController") {
drinkText.SetActive (true);
}
if (triggerIsOn && Input.GetKeyDown (KeyCode.E)) {
drinkAmin.Play ("Dab");
alcBottle.SetActive (true);
}
}
void OnTriggerExit(Collider other){
if (other.gameObject.name == "vThirdPersonController") {
drinkText.SetActive (false);
alcBottle.SetActive (false);
}
}
}
OnTriggerEnter is called once and it will be hard to get press the E button and get Input.GetKeyDown(KeyCode.E) to detect that at the-same time. Checking the E input in the OnTriggerStay function is more appropriate for this but OnTriggerStay does not work sometimes so count it out. Although it's worth knowing that it exist.
Move Input.GetKeyDown(KeyCode.E) to the Update function which is called every frame then set it to true in the OnTriggerEnter function and false in the OnTriggerExit function. Below are the code that should be changed.
void Update()
{
if (triggerIsOn && Input.GetKeyDown(KeyCode.E))
{
Debug.Log("E button pressed");
drinkAmin.Play("Dab");
alcBottle.SetActive(true);
}
}
void OnTriggerEnter(Collider other)
{
if (other.gameObject.name == "vThirdPersonController")
{
Debug.Log("Detected vThirdPersonController");
triggerIsOn = true;
drinkText.SetActive(true);
}
}
void OnTriggerExit(Collider other)
{
if (other.gameObject.name == "vThirdPersonController")
{
Debug.Log("Lost vThirdPersonController");
triggerIsOn = false;
drinkText.SetActive(false);
alcBottle.SetActive(false);
}
}
Once you get that working, starting using the CompareTag function instead of gameObject.name as that is more proficient.