can't use WaitForSeconds with OnMouseDown - c#

I used OnMouseDown() to deactivate an object but i want the object to activate again in a few seconds. I have used WaitForSeconds() for other things but this time it just doesn't work
this is what i could gather by researching (the deactivating part works fine):
void Start()
{
StartCoroutine(wait());
}
void Update(){}
void OnMouseDown()
{
gameObject.SetActive(false);
}
IEnumarator wait()
{
yield return new WaitForSeconds(3);
gameObject.SetActive(true);
}

There are too many reasons your code isn't work right. You are doing it backwards. Your coroutine starts immediately when your program starts because wait() is called from the Start() function. When it starts, it pauses for 3 seconds and set your GameObject to SetActive(true);
If your GameObject is already visible to the screen, your code wont do anything because SetActive(true) will be called even when it is visible. If you fail to press/click on the screen before that 3 seconds, you wont be able to see SetActive(true); because your coroutine code would have finished running by that time.
Also, if you disable a GameObject, the coroutine attached to it will stop. The solution is to create a reference of the GameObject you want to disable then use that reference to disable and enable it from another script without problems.
Since provided a code, I fixed/re-wrote it for you. I replaced the OnMouseDown fucntion with something more robust.
All you have to do is create an empty GameObject. Attach this script to that empty GameObject. Then drag and drop that GameObject you want to disable and enable to the this "Game Object To Disable" slot in this script, from the Editor.
Do NOT attach this script to that GameObject you want to disable and enable.
Tested with cube and it worked.
using UnityEngine;
using System.Collections;
public class ALITEST: MonoBehaviour
{
//Reference to the GameObject you want to Disable/Enable
//Drag the Object you want to disable here(From the Editor)
public GameObject gameObjectToDisable;
void Start()
{
}
void Update()
{
//Keep checking if mouse is pressed
checkMouseClick();
}
//Code that checks when the mouse is pressed down(Replaces OnMouseDown function)
void checkMouseClick()
{
//Check if mouse button is pressed
if (Input.GetMouseButtonDown(0))
{
RaycastHit hitInfo = new RaycastHit();
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hitInfo))
{
//Check if the object clicked is that object
if (hitInfo.collider.gameObject == gameObjectToDisable)
{
Debug.Log("Cube hit");
StartCoroutine(wait()); //Call the function to Enable/Disable stuff
}
}
}
}
//This value is used to make sure that the coroutine is not called again while is it already running(fixes many bugs too)
private bool isRunning = false;
IEnumerator wait(float secondsToWait = 3)
{
//Exit coroutine while it is already running
if (isRunning)
{
yield break; //Exit
}
isRunning = true;
//Exit coroutine if gameObjectToDisable is not assigned/null
if (gameObjectToDisable == null)
{
Debug.Log("GAME OBJECT NOT ATTACHED");
isRunning = false;
yield break; //Exit
}
gameObjectToDisable.SetActive(false);
//Wait for x amount of Seconds
yield return new WaitForSeconds(secondsToWait);
//Exit coroutine if gameObjectToDisable is not assigned/null
if (gameObjectToDisable == null)
{
Debug.Log("GAME OBJECT NOT ATTACHED");
isRunning = false;
yield break; //Exit
}
gameObjectToDisable.SetActive(true);
isRunning = false;
}
}

Because you're calling StartCoroutine() in Start(), your coroutine will resume 3 seconds after the component is started. You want to call StartCoroutine(wait()) in OnMouseDown() so the GameObject will become active after that.

You cannot deactivate GameObject and continue Coroutine on it. If you deactivate GameObject that has runing Coroutine it will be stoped.
So if you want to do it right, you need Coroutine runing on other GameObject and from there actvating this GameObject.
If you need more help, ask.

Related

I'm trying to make an animation that can always happen when clicking a button after entering a trigger

I'm making a game with unity 2022 and was struggling to make this animator [bool turn true] after entering a trigger and pressing "E". and then go false after still holding "E" still or not pressing it. also am struggling with Get Key stuff. it isn't working mate and I have been trying for hours at a time with struggles so yeah. this is my code. it's basically someone pressing a button (the object in unity I'm using), making it click until you click it again, please help this is for a school summer project!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
class Fixedpress : MonoBehaviour
{
public Animator Tributton;
bool YUIp = false;
// Start is called before the first frame update
void OnTriggerEnter(Collider other)
{
if(other.tag == "player")
{
YUIp = true;
}
}
void OnTriggerStay(Collider other)
{
if (other.tag == "Player" && YUIp == true)
{
if (Input.GetKeyDown(KeyCode.E))
{
Tributton.SetBool("Fiveyon", true);
Debug.Log("THY");
}
else if (Input.GetKey(KeyCode.E))
{
Tributton.SetBool("Fiveyon", false);
Debug.Log("THe");
}
else if (Input.GetKeyUp(KeyCode.Return))
{
Tributton.SetBool("Fiveyon", false);
Debug.Log("THane");
}
}
}
void OnTriggerExit(Collider other)
{
if(other.tag == "Player")
{
YUIp = false;
}
}
}
I mostly need help with the press e code stuff to make work so yeah :D
thank you
sorry for the bad grammar its 3 am rn
I think I got it now.
You want
Player has to be in the trigger in order to start the animation
Pressing E should trigger the animation only ONCE
I would no use a Bool parameter in that case but rather a Trigger and then Animator.SetTrigger.
Your transitions should be somewhat like e.g.
Idle State --condition-Fiveyon--> Your Animation
Your Animation --exit-time-no-further-condition--> Idle State
Then you could probably do something like e.g.
public class Fixedpress : MonoBehaviour
{
public Animator Tributton;
private Coroutine routine;
private void OnTriggerEnter(Collider other)
{
// not sure if needed right now, some of the events are triggered even on disabled components
if(!enabled) return;
// in general rather use CompareTag instead of ==
// it is slightly faster and also shows an error if the tag doesn't exist instead of failing silent
if(!other.CompareTag("player")) return;
// just in case to prevent concurrent routines
if(routine != null) StopCoroutine(routine);
// start a new Coroutine
routine = StartCoroutine(WaitForKeyPress());
}
private IEnumerator WaitForKeyPress()
{
// check each FRAME if the key goes down
// This is way more reliable as OnTriggerStay which is called
// in the physics loop and might skip some frames
// This also prevents from holding E while entering the trigger, it needs to go newly down
yield return new WaitUntil(() => Input.GetKeyDown(KeyCode.E));
// set the trigger once and finish the routine
// There is no way to trigger twice except exit the trigger and enter again now
Tributton.SetTrigger("Fiveyon");
Debug.Log("Fiveyon!");
// If you even want to prevent this from getting triggered ever again simply add
enabled = false;
// Now this can only be triggered ONCE for the entire lifecycle of this component
// (except you enable it from the outside again of course)
}
void OnTriggerExit(Collider other)
{
if(!other.CompareTag("player")) return;
// when exiting the trigger stop the routine so later button press is not handled
if(routine != null) StopCoroutine(routine);
}
}

How to add wait time between death and level reset. c#

I am new to Unity and C#, I am trying to add a 'wait time' to my death script. So when my Player dies it shows a particle animation and then resets the level, however, at the moment my particles are playing but the level doesn't reset.
public GameObject particles;
public Player_Movement player;
void OnCollisionEnter2D(Collision2D Col)
{
if (Col.collider.tag == "Enemy")
{
Instantiate(particles, transform.position, Quaternion.identity);
Destroy(gameObject);
StartCoroutine("RestartGameCo");
}
}
public IEnumerator RestartGameCo()
{
yield return new WaitForSeconds(0.5f);
SceneManager.LoadScene("Level1");
}
Destroy(gameObject);
StartCoroutine("RestartGameCo");
Your code is fine, but you destroy the gameobject that has this script on it. Which also destroys the script, and stops all the coroutines. So it will never be called.
A solution is to make the object invisible in some way, like disabling the mesh renderer and collider instead of destroying it.
You are actually destroying the gameobject itself before even running the co-routine. It means that the co-routine "RestartGameCo" won't even run. One way to debug these kind of things is using Debug.log messages.
Code:
void OnCollisionEnter2D(Collision2D Col)
{
if (Col.collider.tag == "Enemy")
{
Instantiate(particles, transform.position, Quaternion.identity);
StartCoroutine("RestartGameCo");
}
}
public IEnumerator RestartGameCo()
{
yield return new WaitForSeconds(0.5f);
SceneManager.LoadScene("Level1");
}
Let me know if it helps.
I know a couple options about that. Here's one.
By using the Thread, it will pause your application for the amount of time of your desire. To do so, you need to add using System.Threading; to your the current cs file you are editing. Now simply change your code to that:
void OnCollisionEnter2D(Collision2D Col)
{
Thread.Sleep(5000) // 5000 in milliseconds
if (Col.collider.tag == "Enemy")
{
Instantiate(particles, transform.position, Quaternion.identity);
Destroy(gameObject);
StartCoroutine("RestartGameCo");
}
}
public IEnumerator RestartGameCo()
{
Thread.Sleep(5000)
yield return new WaitForSeconds(0.5f);
SceneManager.LoadScene("Level1");
}

How can I run StartCoroutine once?

The code in my script works fine. But sometimes it does not drop after I contact the rigid body collides. When it touches another corner, it restarts the StartCoroutine. I want to run it once to get ahead of it. How can I get it? (More descriptive: In my game, a ball is falling from above, and it stops for 3 seconds when hit by obstacles. I do not want it to stop in that obstacle again after it has hit once.
public void OnCollisionEnter2D(Collision2D col)
{
if (col.collider.CompareTag("Player"))
{
hitEffect.transform.position = col.contacts[0].point;
hitEffect.gameObject.SetActive(true);
GameManager.Instance.playerController.anim.Squeeze();
col.gameObject.GetComponent<Rigidbody2D>().simulated = false;
StartCoroutine (SetKinematic_Coroutine(col));
}
}
public IEnumerator SetKinematic_Coroutine(Collision2D col)
{
yield return new WaitForSeconds(1f);
col.gameObject.GetComponent<Rigidbody2D>().simulated = true;
}
Best way for preventing multi call of a coroutine is to use a reference:
private IEnumerator coroutine = null;
private void Method()
{
if(condition == true && this coroutine == null)
{
this.coroutine = MyCoroutine();
StartCoroutine(this.coroutine);
}
}
private IEnumerator MyCoroutine()
{
yield return null;
this.coroutine = null; // Set it back to null when leaving the coroutine.
}
When the condition is met and the coroutine is null (you are not running it already), it will assign to the reference and call the starting of the coroutine. While the coroutine is running, this.coroutine is not null and the double condition cannot be met anymore. When the coroutine is done, this.coroutine is set to null so next time the double condition is run, they will be both true.
You can also use a basic boolean for flag, but the usage of the IEnumerator reference can also be used to cancel.
You could turn the collider of the ball off whilst it is stopped so it can't receive any more hits during that time.
public IEnumerator SetKinematic_Coroutine(Collision2D col)
{
//turn the collider off
col.gameObject.GetComponent<Collider2D>().enabled = false;
yield return new WaitForSeconds(1f);
col.gameObject.GetComponent<Rigidbody2D>().simulated = true;
//turn the collider back on after we have waited
col.gameObject.GetComponent<Collider2D>().enabled = true;
}
Add a list of objects already hit by it and, for every hit, check if the object it is colliding with is in the list, if not add it and run the code.
private List<Collider2D> collided = new List<Collider2D>();
public void OnCollisionEnter2D(Collision2D col) {
if (col.collider.CompareTag("Player") && !collided.Contains(col.collider)) {
collided.Add(col.collider);
// ...
The Contains call might give you trouble if to many colliders are added to the list frequently, but otherwise this should do it.

Disable GameObject but continue to run Coroutine

I have a BonusController script as a component of a Bonus gameObject. This Bonus must be destroyed on collision but has to "act" for a few seconds. I have started a coroutine for this, but the script stops its execution (I think it's because I set the gameObject inactive). Here is the code of BonusController:
void OnTriggerEnter2D(Collider2D col)
{
StartCoroutine(speedUp(1));
gameObject.SetActive(false);
}
IEnumerator speedUp(float seconds)
{
Debug.Log("before");
yield return new WaitForSeconds(seconds);
Debug.Log("after"); // <--- This is never called
}
How can I remove the object and don't stop the coroutine script execution?
Can't you just disable the mesh renderer and collider? This way the gameobject will still exists, but the user won't be able to see it.
You cannot pull the ground on which you are standing. :)
Just disable the SpriteRenderer as you are using 2D methods. And keep the object alive and enable.
{
StartCoroutine(speedUp(1));
//gameObject.SetActive (false);
GetComponent<SpriteRenderer> ().enabled = false;
GetComponent<Collider2D> ().enabled = false;
// Above line will deactivate the first collider it will find.
}
IEnumerator speedUp(float seconds)
{
Debug.Log("before");
yield return new WaitForSeconds(seconds);
Debug.Log("after"); // <--- This is never called
}
In order to destroy the object after some time, call destroy inside the coroutine after the yield.
yield return WaitForSeconds(second);
Destroy(this);
Now it is properly destroyed and will free up memory opposed to still being there, invisible, but taking up resources.

Jump Button in Unity for Canvas

I am looking for a code or script in C# or Java to make my cube tagged as a Player jump, in below script.
I have written some code and attached it to a button on canvas, but the problem is when I press and hold the button, it keeps jumping and makes an infinitly high jump.
Here is my script written in C#
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class DownstateButton : Button
{
public void Update()
{
//A public function in the selectable class which button inherits from.
if(IsPressed())
{
WhilePressed();
}
}
public void WhilePressed()
{
var player =GameObject.FindWithTag("Player");
player.transform.Translate(0, 1, 0);
//It was for jump
}
}
Hook the PointerDown (called when the button is pressed down) and PointerUp (button has been let go again) events from the UIButton and weight the translation of the position with Time.deltaTime, and you should be good to go. (player.transform.Translate(0,1 * Time.deltaTime, 0), optionally multiply it with another factor for speed modulation.) References: http://unity3d.com/learn/tutorials/modules/beginner/ui/ui-events-and-event-triggers
EDIT: Yeah, some example code. First, I have an EventTrigger component on the button. I use this sothat I can hook the PointerDown and PointerUp events as described before. It looks like this in the inspector:
(Use the "Add New Event Type" button to redirect the event calls.)
Then, I have this script on the button. The code speaks for itself.
using UnityEngine;
using UnityEngine.EventSystems;
public class JumpButton : MonoBehaviour {
private bool shouldJump = false;
// Update is called once per frame
void Update () {
//Find the player
var player = GameObject.FindGameObjectWithTag("Player");
//No player? exit out.
if (player == null)
return;
//Is the jump button currently being pressed?
if (shouldJump)
{
//Translate it upwards with time.
player.transform.Translate(new Vector3(0, Time.deltaTime * 5, 0));
//Make sure the Rigidbody is kinematic, or gravity will pull us down again
if (player.GetComponent<Rigidbody>().isKinematic == false)
player.GetComponent<Rigidbody>().isKinematic = true;
}
//Not jumping anymore? reset the Rigidbody.
else
player.GetComponent<Rigidbody>().isKinematic = false;
}
//When the button is being pressed down, this function is called.
public void ButtonPressedDown(BaseEventData e)
{
shouldJump = true;
}
//When the button is released again, this function is called.
public void ButtonPressedUp(BaseEventData e)
{
shouldJump = false;
}
}
The thing with switching to a kinematic rigidbody is optional, though. Also the speed can be adjusted with the multiplicative constant in the Translate() call. I tested this code with a standard cube, that has the Player tag and a Rigidbody on it, and it works fine.
Happy coding.
You could use a unity coroutine.
At the start of the routine, you'd set (for example) "isJumping" (a bool), and then before you start your loop bit, you'd check if you are jumping by checking 'isJumping'.
If not "isJumping", set isJumping to true, do your jump, and then on the completion of the routine, set isJumping to false.
//untested (uncompiled) code written on the fly
bool isJumping = false;
IEnumerator doJump()
{
if (!isJumping) {
isJumping = true;
// do jump (probably a loop)
while (jumpCondition) {
// jump instructions
yield return
}
//unset isJumping, inside 'if' but after yield return
isJumping = false
}
}
Note : code after yield return in a coroutine will only (probably) be run once, and only run as the coroutine exists (because no more yielding means the coroutine is at an end)

Categories

Resources