Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
  • Help Room /
avatar image
0
Question by Cirrocumulus · Feb 09, 2018 at 08:30 PM · triggerscollisionslogic

Using OnTriggerStay for entering, staying or exiting

I have been struggling with what was supposed to be a very simple logic problem for the last two hours. I finally managed to make it work but I wonder if there's a more elegant solution (forgive me for the horizontal rules but for the life of me I can't figure out how to write paragraph breaks here, double space at the end doesn't work).


What I'm looking for is a trigger Collider2D which can report at all times if something is touching it (or inside its zone). Because any number from 0 to 10 objects might be in the zone at any given time, I can't do it with OnTriggerEnter2D and OnTriggerExit2D, for two reasons: (1) I don't want to keep a list or a count variable because I don't need them for anything else (2) With Enter and Exit I get less predictable behaviour if the trigger is enabled when something is already touching it, or if an object gets destroyed while inside the trigger.


So I'm going with OnTriggerStay2D instead. But I don't want it to continuously send a message when there's a collider present. I just need it to report once if something is in there or not, as soon as the script is enabled and at any point after that. I also don't need to check it every single frame (0.1 seconds is more than enough in my case). So I managed to do this with two booleans (after hours of trying to figure out the 'if' structure of the current and last check):


 using UnityEngine;

 public class SafeZone : MonoBehaviour {

     private Collider2D col;
     private bool isClearThisCheck;
     private bool isClearLastCheck;
     private float checkInterval;
     private float timeSinceLastCheck;

     void Awake()
     {
         col = GetComponent<Collider2D>();
         isClearLastCheck = true;
         checkInterval = 0.5f;
     }

     private void FixedUpdate()
     {
         if (Time.time > timeSinceLastCheck + checkInterval)
         {
             if (isClearThisCheck && !isClearLastCheck)
             {
                 Debug.Log("FREE");
                 // Send a message.
                 isClearLastCheck = true;
             }
             isClearThisCheck = true;
             timeSinceLastCheck = Time.time;
         }
     }

     private void OnTriggerStay2D(Collider2D collision)
     {
         isClearThisCheck = false;
         if (isClearLastCheck)
         {
             Debug.Log("OCCUPIED");
             // Send a message here too.
             isClearLastCheck = false;
         }
     }
 }


This works and does what it's supposed to do. It does feel like reinventing the wheel, though, and I wonder if there's a simpler boolean check possible (or a Unity check I'm not aware of). Also, this only works because FixedUpdate is called before the collision. If I put this in Update instead, the whole logic gets reversed and it doesn't work. Is there any way to do this with less code?


P.S. For some reason, this code outputs free whenever I deactivate the script in the Inspector while the trigger is in the occupied state. Not a big deal, but I don't really understand why this happens.

Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

2 Replies

· Add your reply
  • Sort: 
avatar image
1

Answer by spidermancy612 · Feb 09, 2018 at 10:16 PM

In all reality, counting based on enter and exit is the best way. Is there a reason you don't want to use them?

Comment
Add comment · Show 6 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image pako · Feb 09, 2018 at 10:29 PM 1
Share

I totally agree! He gave a couple of reasons which I$$anonymous$$HO are not good reasons:

I don't want to keep a list or a count variable because I don't need them for anything else

It doesn't matter if you don't need them anywhere else. If you need them here it's enough to have them.

With Enter and Exit I get less predictable behaviour if the trigger is enabled when something is already touching it, or if an object gets destroyed while inside the trigger

You don't describe the "less predictable behaviour". In any case, I think it would be best to handle the cases that cause this behaviour, i.e. "if the trigger is enabled when something is already touching it, or if an object gets destroyed while inside the trigger", rather that not use Enter and Exit.

But if you have "commited" to not using Enter and Exit, then you already have a working solution... which you already know is not the best solution ;-)

avatar image Cirrocumulus · Feb 09, 2018 at 10:45 PM 0
Share

Well for example, like I wrote, OnTriggerExit isn't called when an object is destroyed when inside the trigger. When I tried using only Enter and Exit I ended up including a Stay just for this purpose, which gets called every physics frame anyway. I had in front of me three OnTrigger methods which basically handled the same thing... Also, in my project, the SafeZone component is enabled only on certain occasions (it doesn't check for collisions all the time), and gets disabled after certain events are called. From the tests I've done, if you enabled a trigger Collider when something is already within its bounds you won't get an Enter, only an Exit (when the object exits). These were the main reasons for this implementation. But like I said, it feels like reinventing the wheel :)

avatar image pako Cirrocumulus · Feb 10, 2018 at 12:11 AM 0
Share

For one thing, I don't know the functionality of your // Send a message and // Send a message here too. or how you would use "a list or a count variable". It really restricts the contents of my comments, and it is also the reason why I didn't respond to your question until @spidermancy612 posted his reply (I've been following your post for hours...).

But lets assume that your functionality would be served well by keeping a list of all GameObjects that are inside the trigger, say List<GameObject> gameObjectsInsideSafeZone.

Then what you could do is this: In the script that destroys a GameObject (which could potentially be inside the safe zone) create an event that would be triggered when the GameObject gets destroyed.

You could then subscribe to this event inside the SafeZone script and run an event handler, whenever the event gets triggered to remove the destroyed GameObject from the List: gameObjectsInsideSafeZone.Remove(destroyedGameObject).

So, the gameObjectsInsideSafeZone list would be maintained with the correct GamObjects, without needing to use Stay.

Also, in my project, the SafeZone component is enabled only on certain occasions (it doesn't check for collisions all the time)

Oh, yes it does (check for collisions all the time). OnTrigger events get called even on disabled scripts:

Per the documentation:

Trigger events will be sent to disabled $$anonymous$$onoBehaviours, to allow enabling Behaviours in response to collisions

e.g. https://docs.unity3d.com/ScriptReference/Collider2D.OnTriggerEnter2D.html

if you enabled a trigger Collider when something is already within its bounds you won't get an Enter

Yes this happens, and it's reasonable that it happens like that.

You could use Collider2D.OverlapCollider to get the colliders of the GameObjects that are already touching the safe zone collider when it gets enabled, and add them to the gameObjectsInsideSafeZoneList . I've never needed to use this, but it seem the right method to call:

https://docs.unity3d.com/ScriptReference/Collider2D.OverlapCollider.html

avatar image Cirrocumulus pako · Feb 10, 2018 at 07:06 AM 0
Share

Thank you for your suggestions.

"create an event that would be triggered when the GameObject gets destroyed"

This would mean having to remember to add an event to each and every entity, past or future, possibly an interface, and have the SafeZone subscribe to all of them. I find this very cumbersome and it also tightly couples the colliders and the SafeZone. It can already find out which collider is around with OnTriggerEnter2D. The fact the Exit isn't called when the object is destroyed is a design decision I don't really understand.

"OnTrigger events get called even on disabled scripts"

I am disabling not the script, but rather the gameObject it's attached to (and on which the trigger Collider2D sits). I'm sorry if I wasn't clear. AFAI$$anonymous$$ $$anonymous$$onoBehaviours on disabled gameobjects are not called.

"Collider2D.OverlapCollider"

It would be one more thing to check in an already rapidly-growing script (counts, lists, event messaging, three OnTrigger functions), but thank you for mentioning it, I didn't know it existed. I am really surprised that Unity doesn't natively encapsulate something like the above with simple OnTrigger calls, I don't know the reasons for it.
Show more comments
avatar image
0

Answer by yummy81 · Feb 10, 2018 at 01:21 PM

Inspired by the idea pako suggested about the events and all this trigger stuff I wrote these two simple scripts. First, I created new empty gameobject, added to it SpriteRenderer with a white circle as a sprite and CircleCollider2D with checkbox IsTrigger set to true. Finally, I attached my Hero.cs script to it. I duplicated it a couple of times and positioned each of them randomly. And then I created new gameobject, added to it SpriteRenderer with a red circle as a sprite. Next, I added CircleCollider2D with ckeckbox IsTrigger set to true and Rigidbody2D with BodyType set to Kinematic. Then I added to it SafeZone.cs script and pressed Play. I moved all these gameobjects in the Scene View. When Hero enters the trigger area of the SafeZone then it is added to the gameObjectsInsideSafeZone list (it can be seen in the inspector since the list is public). When it exists it is removed from the list. And if you destroy Hero when it is in the trigger area of the SafeZone (by simply deleting it in the Hierarchy window) then it will also be removed. Here are two scripts. Hero.cs:

 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 
 public class Hero : MonoBehaviour
 {
     private event System.Action<Collider2D> action;
     private Collider2D col;
     
     private void Start()
     {
         action += SafeZone.Instance.Remove;
         col = GetComponent<Collider2D>();
     }
     
     private void OnDestroy()
     {
         if (action!=null)
             action(col);
         action -= SafeZone.Instance.Remove;
     }
 }
 
 

SafeZone.cs:

 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 
 public class SafeZone : MonoBehaviour
 {
     public static SafeZone Instance {get;set;}
     [SerializeField]
     private List<GameObjectInsideSafeZone> gameObjectsInsideSafeZone;
     
     private void Awake()
     {
         gameObjectsInsideSafeZone = new List<GameObjectInsideSafeZone>();
         Instance = this;
     }
     
     private void OnTriggerEnter2D(Collider2D other)
     {
         gameObjectsInsideSafeZone.Add(new GameObjectInsideSafeZone(other.GetInstanceID(), other.gameObject));
     }
     
     private void OnTriggerExit2D(Collider2D other)
     {
         Remove(other);
     }
     
     public void Remove(Collider2D other)
     {
         foreach(var d in gameObjectsInsideSafeZone)
         {
             if (other.GetInstanceID() == d.id)
             {
                 gameObjectsInsideSafeZone.Remove(d);
                 break;
             }
         }        
     }    
 }
 
 [System.Serializable]
 public struct GameObjectInsideSafeZone
 {
     [SerializeField]
     public int id;
     [SerializeField]
     public GameObject go;
     public GameObjectInsideSafeZone(int id, GameObject go) : this()
     {
         this.id = id;
         this.go = go;
     }
 }
Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

79 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Frogger Logs OnTriggerEnter2D OnTriggerExit2D 0 Answers

Moving to Next Level on collision 1 Answer

OnTriggerExit not working as expected 5 Answers

Falling Obstacle 2 Answers

Using OnPointerClick with Buttons 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges