How to open same animation with two same box colliders? - c#

I have a level in a game where when you get in a box collider, a portal opens, and when you leave it, it closes. Both portals need to open when I enter in box collider 1, and both need to close when i leave it. This also needs to happen when I enter in a box collider 2 of the second portal. I have a script for box collider 1 and I apply it to box collider 2. It checks if the player is in a collider. I have an animator bool which directly takes variable from box collider script to check in range. I use that bool for animation. However, that animator bool doesen't work for box collider 2. Variable for box collider works, but not animator bool. Is there a way to connect that second one, or do i need to make a new script for that box collider?
Box Collider Code:
public bool inRange;
private void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Player"))
{
inRange = true;
}
}
private void OnTriggerExit2D(Collider2D other)
{
if (other.CompareTag("Player"))
{
inRange = false;
}
}
}
Portal Script Code:
public class Portal : MonoBehaviour {
private Animator anim;
private bool inPortalRange;
public GameObject portalBorder;
void Start ()
{
anim = GetComponent<Animator>();
}
void Update ()
{
OpenPortal();
UpdateAnimation();
}
private void UpdateAnimation()
{
anim.SetBool("inPortalRange", inPortalRange);
}
private void OpenPortal()
{
PortalBorder poborder = portalBorder.GetComponent<PortalBorder>();
inPortalRange = poborder.inRange;
}
}
A picture of a situation:

First of all you should not use GetComponent every frame. Either like the anim you should rather store it right away. Or you could simply make portalBorder of type PortalBorder then the according reference is set automatically when referencing it via the Inspector.
Then yes currently you are only updating one of the animators. In order to control them both you have to connect them somehow.
I would do something like this
public class Portal : MonoBehaviour
{
private Animator anim;
private bool inPortalRange;
// Public read-only access
public bool InPortalRange => inPortalRange;
// Reference each other via the Inspector in both portals
public Portal OtherPortal;
// Give this directly the according type so you don't need GetComponent at all
public PortalBorder portalBorder;
// I would recommend to do things always as early as possible
// Awake is executed before Start
private void Awake()
{
anim = GetComponent<Animator>();
}
private void Update ()
{
OpenPortal();
UpdateAnimation();
}
private void UpdateAnimation()
{
// Here now use the range of either this or the other portal
anim.SetBool("inPortalRange", InPortalRange || OtherPortal.inPortalRange );
}
private void OpenPortal()
{
inPortalRange = portalBorder.inRange;
}
}
However instead of making it a polling call in Update I would actually rather use an event driven approach:
public class PortalBorder : MonoBehaviour
{
public UnityEvent OnEnteredPortalRange;
public UnityEvent OnLeftPortalRange;
private void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Player"))
{
OnEnteredPortalRange.Invoke();
}
}
private void OnTriggerExit2D(Collider2D other)
{
if (other.CompareTag("Player"))
{
OnEnteredPortalRange.Invoke();
}
}
}
Now your script has to UnityEvent (just like the onClick of buttons) where you can add callbacks either via the inspector or using code
public class Portal : MonoBehaviour
{
public Animator anim;
private bool inPortalRange;
// Public read-only access
public bool InPortalRange => inPortalRange;
// Reference each other via the Inspector in both portals
public Portal OtherPortal;
// Give this directly the according type so you don't need GetComponent at all
public PortalBorder portalBorder;
// I would recommend to do things always as early as possible
// Awake is executed before Start
private void Awake()
{
anim = GetComponent<Animator>();
// Instead of checking a bool in Update simply
// wait until the according events get invoked
portalBorder.OnEnteredPortalRange.AddListener(EnablePortal);
portalBorder.OnLeftPortalRange.AddListener(DisablePortal);
}
private void OnDestroy()
{
// always make sure to remove callbacks when not needed anymore
// in roder to avoid NullReferenceExceptions
portalBorder.OnEnteredPortalRange.RemoveListener(EnablePortal);
portalBorder.OnLeftPortalRange.RemoveListener(DisablePortal);
}
public void EnablePortal()
{
anim.SetBool("inPortalRange", true);
OtherPortal.anim.SetBool("inPortalRange", true);
}
public void DisablePortal()
{
anim.SetBool("inPortalRange", false);
OtherPortal.anim.SetBool("inPortalRange", false);
}
}

Related

Raycast hits 2 objects at the same time

I have recently developed in unity and I have a problem with using the raycast.
I created 3 scripts:
Interactor: Hooked to the player, it manages all the raycast features
InteractionObject: Hooked to the objects that need to animate
InteractionRaycast: Hooked to objects that need to be destroyed
Everything works, the only problem is that when the animation of the gameobject takes place (In my case the gameobject was a pillow), the other gameobject (It is under the pillow) is destroyed at the same time as the animation begins.
My goal is to first move the pillow and then click on the gameobject to be destroyed, what can I do?
Thank you in advance for your help
Interactor.cs
public class Interactor : MonoBehaviour
{
[SerializeField]
private float _interctRange;
private InteractionObject _interactObject;
private InteractionRaycast _interactionRaycast;
private Camera _camera;
private RaycastHit _hit;
// Start is called before the first frame update
void Start()
{
_camera = Camera.main;
}
// Update is called once per frame
void Update()
{
if (Input.GetButton("Fire1"))
{
Physics.Raycast(Camera.main.transform.position, Camera.main.transform.forward, out _hit, _interctRange);
if (_hit.transform)
{
_interactObject = _hit.transform.GetComponent<InteractionObject>();
}
if (_interactObject)
{
_interactObject.PerfomAction();
}
}
}
}
InteractionObject.cs
public class InteractionObject : MonoBehaviour
{
[SerializeField]
private Vector3 _openPosition, _closePosition;
[SerializeField]
private float _animationTime;
private Hashtable _iTweenArgs;
[SerializeField]
public bool _isOpen;
// Start is called before the first frame update
void Start()
{
_iTweenArgs = iTween.Hash();
_iTweenArgs.Add("position", _openPosition);
_iTweenArgs.Add("time", _animationTime);
_iTweenArgs.Add("islocal", true);
}
public void PerfomAction()
{
if (Input.GetButton("Fire1"))
{
if (_isOpen)
{
_iTweenArgs["position"] = _closePosition;
}
else
{
_iTweenArgs["position"] = _openPosition;
}
_isOpen = !_isOpen;
iTween.MoveTo(gameObject, _iTweenArgs);
}
}
}
InteractionRaycast.cs
public class InteractionRaycast : MonoBehaviour
{
[SerializeField]
private float _range;
Ray _myRay;
RaycastHit _hit;
// Update is called once per frame
void Update()
{
if (Input.GetButton("Fire1"))
{
Physics.Raycast(Camera.main.transform.position, Camera.main.transform.forward, out _hit, _range);
if (_hit.transform)
{
Destroy(gameObject);
}
}
}
}
Tip: Use RaycastAll() and filter out the objects you want based on conditions.
It might help you with your problem, although you first should pay attention to #derHugo answer. It points out many aspects that you will want to improve in your code.
Your InteractionRaycast will destroy this own gameObject it is attaced to completely regardless of what exactly you are hitting.
You either want to additionally check like e.g.
if (Input.GetButton("Fire1"))
{
var ray = _camera.ViewportPointToRay(new Vector3(0.5f, 0.5f));
if(Physics.Raycast(ray, out var hit, _interctRange))
{
// Is this actually the object that was hit?
if(hit.transform == transform)
{
Destroy(gameObject);
}
}
}
Or - and in general I would do that - instead of having such a component on each and every object you can interact with and shooting hundreds of redundant raycasts, I would rather have a component on your player object, shoot one single raycast and interact with whatever you hit.
Both your target objects can have a common interface
public interface IInteractionObject
{
void PerfomAction();
}
meaning both types need to implement a method called PerformAction without parameters.
And rather interact directly with that in
public class Interactor : MonoBehaviour
{
[SerializeField]
private float _interctRange;
private Camera _camera;
// Start is called before the first frame update
void Start()
{
_camera = Camera.main;
}
// Update is called once per frame
void Update()
{
if (Input.GetButton("Fire1"))
{
var ray = _camera.ViewportPointToRay(new Vector3(0.5f, 0.5f));
// was something hit at all? => Check the API and return values of methods!
if(Physics.Raycast(ray, out var hit, _interctRange))
{
// Did we hit an IInteractionObject
if(hit.transform.TryGetComponent<IInteractionObject>(out var interactable))
{
// This class has no idea what exactly it is interacting with and doesn't need to know
interactable.PerfomAction();
}
}
}
}
}
and then you have different implementations:
public class AnimatedInteractionObject : MonoBehaviour, IInteractionObject
{
[SerializeField] private Vector3 _openPosition;
[SerializeField] private Vector3 _closePosition;
[SerializeField] private float _animationTime;
[SerializeField] public bool _isOpen;
private Hashtable _iTweenArgs;
private void Start()
{
_iTweenArgs = iTween.Hash();
_iTweenArgs.Add("position", _openPosition);
_iTweenArgs.Add("time", _animationTime);
_iTweenArgs.Add("islocal", true);
}
public void PerfomAction()
{
_isOpen = !_isOpen;
// use ternary makes it easier to read
_iTweenArgs["position"] = _isOpen ? _openPosition : _closePosition;
iTween.MoveTo(gameObject, _iTweenArgs);
}
}
and
public class DestroyInteractionObject : MonoBehaviour, IInteractionObject
{
public void PerfomAction()
{
// This is only called by the Interactor
// it already does a key and raycast check so no need to do that here
Destroy(gameObject);
}
}

Animation keeps getting triggered when I step on the tiles

I'm developing a TopDown 2D game on Unity with some RPG elements and I did as so, when the player steps on a set of tiles placed on the map, it triggers an animation with a UI showing some text. However, the animation gets called multiple times while the player keeps stepping on the tiles and even when he exits the trigger area.
What I need help with is how to make the animation only trigger once and, while the UI is in on the screen, the player is not allowed to move until the player presses the button in the UI (already programmed and working).
Here is the code I used to make the trigger function:
public class TriggerScript : MonoBehaviour
{
public string popUp;
public Animator animator;
void Start()
{
animator = GetComponent<Animator>();
}
private void OnTriggerEnter2D(Collider2D collision)
{
PopUpSystem pop = GameObject.FindGameObjectWithTag("GameManager").GetComponent<PopUpSystem>();
pop.PopUp(popUp);
Debug.Log("trigger");
animator.SetTrigger("pop");
}
}
And here is the PopUpSystem that sets the text:
public class PopUpSystem : MonoBehaviour
{
public GameObject popUpBox;
public Animator popupanimation;
public TMP_Text popUpText;
public void PopUp(string text)
{
popUpBox.SetActive(true);
popUpText.text = text;
popupanimation.SetTrigger("pop");
}
}
If, in order to help me, you need more information and details, please ask me in the comment section.
Note that I am new to Unity and have zero experience with it so, if you can be patient and explain things in a simple way, I would enjoy that!
Thank you for reading.
Edit: this is the animator window:
Edit 2: The code that I use for the movement of the player:
public class PLayerController : MonoBehaviour
{
private Rigidbody2D MyRB;
private Animator myAnim;
public string popUp;
[SerializeField]
private float speed;
// Start is called before the first frame update
void Start()
{
MyRB = GetComponent<Rigidbody2D>();
myAnim = GetComponent<Animator>();
}
private void Move()
{
myAnim.SetTrigger("popUp");
}
// Update is called once per frame
void Update()
{
MyRB.velocity = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical")) * speed * Time.deltaTime;
myAnim.SetFloat("moveX", MyRB.velocity.x);
myAnim.SetFloat("moveY", MyRB.velocity.y);
if ((Input.GetAxisRaw("Horizontal")==1) ||(Input.GetAxisRaw("Horizontal") == -1)||(Input.GetAxisRaw("Vertical") == 1)||(Input.GetAxisRaw("Vertical") == -1))
{
myAnim.SetFloat("LastMoveX", Input.GetAxisRaw("Horizontal"));
myAnim.SetFloat("LastMoveY", Input.GetAxisRaw("Vertical"));
}
}
}
Edit 3: Code with boolean:
public class TriggerScript : MonoBehaviour
{
public string popUp;
public Animator animator;
bool condition = false;
void Start()
{
animator = GetComponent<Animator>();
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (condition == false)
{
PopUpSystem pop = GameObject.FindGameObjectWithTag("GameManager").GetComponent<PopUpSystem>();
pop.PopUp(popUp);
Debug.Log("trigger");
animator.SetTrigger("pop");
condition = true;
}
}
private void OnTriggerExit2D(Collider2D collision)
{
animator.SetTrigger("close");
}
All you have to do is to set your TriggerScript to something like this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TriggerScript : MonoBehaviour
{
public string popUp;
private void OnTriggerEnter2D(Collider2D collision)
{
PopUpSystem pop = GameObject.FindGameObjectWithTag("GameManager").GetComponent<PopUpSystem>();
if (collision.gameObject.tag == "Player")
{
pop.PopUp(popUp);
}
}
private void OnTriggerExit2D(Collider2D collision)
{
PopUpSystem pop = GameObject.FindGameObjectWithTag("GameManager").GetComponent<PopUpSystem>();
pop.closeBox();
}
}
and then set your PopUpSystemScript to something like this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
public class PopUpSystem : MonoBehaviour
{
public GameObject popUpBox;
public Animator popupanimation;
public TMP_Text popUpText;
public void PopUp(string text)
{
popUpBox.SetActive(true);
popUpText.text = text;
popupanimation.SetTrigger("pop");
}
public void closeBox()
{
popupanimation.SetTrigger("close");
}
}
Now click on the popUp and close animation clips (in Animations folder) and remove the Loop Time check mark. and you should be all set to see the animation appears and disappears.
To stop the player, you can either use Time.timeScale. or set the rigidbody to isKenimatic.

GameObject won't change after updating it with a Method (object reference not set)

Unity newb here :)
I have a button which calls this function which basicly sets a prefab to instantiate.
public void setTurret()
{
towerNode.setTurretType(turret)
Debug.Log("selected shop turret:" + turret.name);
}
My Tower_Node class handles the actual instantiate
public class Tower_Node : MonoBehaviour
{
private GameObject turret;
public void setTurretType(TowerType _turret)
{
turret= _turret.prefab;
}
private void OnMouseDown()
{
Instantiate(turret,GetBuildPosition(),Quaternion.identity);
}
...}
EDIT: What I also tried
public class Tower_Node : MonoBehaviour
{
private TowerType turret;
public void setTurretType(TowerType _turret)
{
turret = _turret;
}
private void OnMouseDown()
{
Instantiate(turret.prefab,GetBuildPosition(),Quaternion.identity);
}
...}
EDIT:
This is how the references in the inspector look. The shop script is the script with the setTurret() method
--
Setting the turret with the setTurretType method works. If i check it with Debug.Log() i get the right TowerType but outside of the function the gameobject is still =null and when i try to instantiate it gives me an NullReferenceException (because Gameobject is null obviously)
What am i missing here?
Thank you for your answers.
switch OnMouseDown() for
if (Input.GetMouseButtonDown(0))
in your update loop
just make sure that turret.prefab is a prefab
Instantiate(turret.prefab, GetBuildPosition(),Quaternion.identity);
I would say that if your TowerType class has a GameObject var named prefab, you should either assign the prefab to the prefab, or assign the turret to the _turret and the access the prefab.
Either this:
public void setTurretType(TowerType _turret) {
turret = _turret;
hoverTurret = _turret.hoverPrefab;
}
private void OnMouseDown()
{
Instantiate(turret.prefab, GetBuildPosition(),Quaternion.identity);
}
or
public void setTurretType(TowerType _turret) {
turret.prefab = _turret.prefab;
hoverTurret = _turret.hoverPrefab;
}
private void OnMouseDown()
{
Instantiate(turret.prefab, GetBuildPosition(),Quaternion.identity);
}
should work.
Edit:
You could set the turret while instiantiating:
public void setTurretType(TowerType _turret) {
turret.prefab = _turret.prefab;
hoverTurret = _turret.hoverPrefab;
}
private void OnMouseDown()
{
turret = Instantiate(turret.prefab, GetBuildPosition(),Quaternion.identity);
}

Unity 3d pass bool variable between two objects

how i can pass a simple boolean variable between two different object?
I can try this but didn't work...
First script:
public class CollisionController : MonoBehaviour
{
public PlayerMovement movement;
public bool active = false;
private void OnCollisionEnter(Collision collision)
{
if(collision.collider.tag == "Obstacle")
{
active = true;
}
}
}
Second script (that read the boolean variable "active")
public class EmptyControllerColl : MonoBehaviour
{
public CollisionController controller;
public PlayerMovement movement;
public bool activeLocal = false;
private void Start()
{
GetComponentInChildren<CollisionController>();
}
void Update()
{
activeLocal = controller.active;
if(activeLocal == false)
{
Debug.Log("Nothing...");
}
if(activeLocal == true)
{
Debug.Log("Game over");
}
}
}
When the variable bool "Active" change its status, the variable "activeLocal" don't change status.. How can I resolve this problem?
Collision Controller is "connect" to Cube Object.
EmptyControllerColl is "connect" to emptyGameObject (parent of Cube).
This line
_ = GameObject.Find("cubo Variant").GetComponent<CollisionController>().active;
makes no sense. First of all there is no field or variable declared with the name _ so this shouldn't even compile at all. And secondly what do you need this for? Rather store the according reference once in the controller field and reuse it later.
Then for your usecase there is no need at all to store the value in a local variable ... this makes things only more complicated. Simply where you need it get the value from controller.active.
Also do not use == for tags. Rather check via CompareTag. The problem is that == silently fails if you have any typo or the tag doesn't exist at all. CompareTag rather throws an error that the given tag is not valid.
public class EmptyControllerColl : MonoBehaviour
{
// Best already drag this in via the Inspector in Unity
[SerializeField] private CollisionController controller;
public PlayerMovement movement;
// As fallback get it ONCE on runtime
private void Awake()
{
// since you say the cube is a child of this empty object you do not use
// Find at all but can simply use GetComponentInChildren
if(!controller) controller = GetComponentInChildren<CollisionController>(true);
}
void Update()
{
// No need to store this in a local field at all
if(!controller.active)
{
Debug.Log("Nothing...");
}
// use if else since both cases are exclusive and you don't even need to check the value twice
else
{
Debug.Log("Game over");
}
}
}
Event Driven - part A
In general you should avoid poll checks for a bool value in Update and rather come up with a more event driven solution! An example could look like:
public class CollisionController : MonoBehaviour
{
public PlayerMovement movement;
// Here everyone who wants can add listeners that get called as soon as
// we invoke this event. We will do it everytime the 'active' value is changed
public event Action<bool> OnActiveStateChanged;
// Backing field for 'active'
private bool _active;
// Property that reads and writes '_active'
// Everytime it is assigned it also invokes 'OnActiveStateChanged'
private bool active
{
get { return _active; }
set
{
_active = value;
OnActiveStateChanged?.Invoke(_active);
}
}
private void OnCollisionEnter(Collision collision)
{
if(collision.collider.CompareTag("Obstacle"))
{
active = true;
}
}
}
Now you would register a listener for this event like
public class EmptyControllerColl : MonoBehaviour
{
// Best already drag this in via the Inspector in Unity
[SerializeField] private CollisionController controller;
public PlayerMovement movement;
// As fallback get it ONCE on runtime
private void Awake()
{
// since you say the cube is a child of this empty object you do not use
// Find at all but can simply use GetComponentInChildren
if(!controller) controller = GetComponentInChildren<CollisionController>(true);
// register a callback. It is allowed an save to unregister first
// which makes sure this is only registered exactly once
controller.OnActiveStateChanged -= HandleControlerActiveStateChanged;
controller.OnActiveStateChanged += HandleControlerActiveStateChanged;
}
private void HandleGameOver()
{
Debug.Log("Game over");
}
private void HandleControlerActiveStateChanged(bool value)
{
if(!value)
{
Debug.Log("Nothing...");
}
else
{
Debug.Log("Game over");
}
}
private void OnDestroy()
{
// always clean up listeners
controller.OnActiveStateChanged -= HandleControlerActiveStateChanged;
}
}
This now is way more efficient since you don't all time run an Update method. Instead the HandleControlerActiveStateChanged is only called when the value of active is actually changed.
Event Driven - part B
And then actually in your case there is need to use a bool at all you could use a simple event Action instead and remove all the bools entirely:
public class CollisionController : MonoBehaviour
{
public PlayerMovement movement;
public event Action OnGameOver;
private void OnCollisionEnter(Collision collision)
{
if(collision.collider.CompareTag("Obstacle"))
{
OnGameOver?.Invoke();
}
}
}
Now you would register a listener for this event like
public class EmptyControllerColl : MonoBehaviour
{
[SerializeField] private CollisionController controller;
public PlayerMovement movement;
private void Awake()
{
if(!controller) controller = GetComponentInChildren<CollisionController>(true);
controller.OnGameOver -= HandleGameOver;
controller.OnGameOver += HandleGameOver;
}
private void HandleGameOver()
{
Debug.Log("Game over");
}
private void OnDestroy()
{
controller.OnGameOver -= HandleGameOver;
}
}
using UnityEngine;
public class CollisionController : MonoBehaviour
{
void Start()
{
// Calls the function ApplyDamage with a value of 5
// Every script attached to the game object
// that has an ApplyDamage function will be called.
gameObject.SendMessage("ApplyDamage", 5.0);
}
}
public class EmptyControllerColl : MonoBehaviour
{
public void ApplyDamage(float damage)
{
print(damage);
}
}
https://docs.unity3d.com/ScriptReference/GameObject.SendMessage.html

gaze always execute even after PointExit

I have a problem about my gaze on VR . What I am trying to do is that gaze upon the button I want to select then hide the first gameobject parent then show the second gameobject parent . Now the second gameobject parent will be shown and when I try to gaze upon the back button it will show the first gameobject parent and hide the second gameobject parent . The problem occurs here, when I am trying to nothing and don't gaze on the buttons it automatically show my second gameobject parent and go back to first parent gameobject and hide and show and hide and show always.
public float gazeTime = 2f;
private float timer;
private bool gazedAt;
public Setting setting;
private void Start()
{
}
public void Update()
{
if (gazedAt)
{
timer += Time.deltaTime;
if (timer >= gazeTime)
{
// execute pointerdown handler
ExecuteEvents.Execute(gameObject, new PointerEventData(EventSystem.current), ExecuteEvents.pointerDownHandler);
timer = 0f;
}
else
{
return;
}
}
else
{
return;
}
}
public void PointerEnter()
{
gazedAt = true;
Debug.Log("PointerEnter");
}
public void PointerExit()
{
gazedAt = false;
Debug.Log("PointerExit");
}
//Method for going to setting
public void Setting()
{
setting.ActivateSetting();
}
//Method for going back to main menu
public void GoBack()
{
setting.GoBackToMainMenu();
}
Here's how my Setting code is setup
public GameObject setting;
public GameObject back;
public void ActivateSetting()
{
setting.SetActive(false);
back.SetActive(true);
}
public void GoBackToMainMenu()
{
back.SetActive(false);
setting.SetActive(true);
}
What I want is that it will only show the gameobject parent if I gaze upon it.
After calling the click once you reset the timer but you didn't reset gazedAt
=> the Update method did still run the timer and call the click again.
It seems that your PointerExit is not called at all and therefore the button never reset.
Instead of the EventTrigger I would strongly recommend to use the interfaces IPointerEnterHandler and IPointerExitHandler like
public class YourClass : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
{
//...
public void OnPointerEnter()
{
}
public void OnPointerExit()
{
}
I would actually not use Update at all but prefer a Coroutine. Also don't use the complex call of ExecuteEvents.Execute(gameObject, new PointerEventData(EventSystem.current), ExecuteEvents.pointerDownHandler); instead use either Getcomponent<Button>().onClick.Invoke(); or call the method directly
private Button _button;
private void Awake()
{
// Get the reference only once to avoid
// having to get it over and over again
_button = GetComponent<Button>();
}
private IEnumerator Gaze()
{
// wait for given time
yield return new WaitForSeconds(gazeTime);
// call the buttons onClick event
_button.onClick.Invoke();
// or as said alternatively directly use the methods e.g.
setting.ActivateSetting();
}
public void OnPointerEnter()
{
Debug.Log("PointerEnter");
// start the coroutine
StartCoroutine(Gaze());
}
public void OnPointerExit()
{
Debug.Log("PointerExit");
// stop/interrupt the coroutine
StopCoroutine(Gaze());
}
As you can see there is no need at all for the timer and gazedAt values so you can't forget to reset them somewhere. It also avoids that the method is called repeatedly.
If you don't want to use a Button at all you could also add your own UnityEvent like
// add callbacks e.g. in the Inspector or via script
public UnityEvent onGazedClick;
// ...
onGazedClick.Invoke();

Categories