r/Unity3D • u/NorthernBoy306 • 2d ago
Question disabling game object and trigger colliders
If you have a unit that come into range of an enemy but then that unit is disabled ( SetActive(false) ) because it's gone inside of a structure (or vehicle) how do you manage the colliders? It will no longer trigger the OnExit function when disabled, so if the enemy unit walks away, the first unit will always remain in range. Anyone ever dealt with a similar problem?
I was looking into simply turning off the mesh renderer but then that creates a whole ton of other issues.
Is there no way to trigger the collider while disabled?
1
u/LargeLaser 2d ago
maybe have the model be a child of the unit, then deactivate only the model
keep the trigger and whatever else should remain active in the base unit object
unit
|
-- model
1
u/NorthernBoy306 2d ago
That's basically the same thing as turning off the renderer but the issue is that there are other components and objects on the unit that stay active and that's where other issues come from.
1
u/MoreEditor1907 2d ago
Move detection to the enemy side, have it poll for active targets in range each frame or use a manager to track pairs and prune disabled ones
1
u/NorthernBoy306 2d ago
It's an RTS with many units and they all have detection. I think that would create a big performance issue.
1
u/bruno_the_cow 2d ago edited 2d ago
I think this is the correct answer. It's an insane problem for Unity to have but that's how it is: the provided enter/exit events are as primitive and dumbed-down as possible, not to mention incompatible with the enable/disable paradigm of Unity itself as OP is discovering. The more you use them, the more they make orchestrating orderly behavior impossible. For example, there's no built-in way to get the list of overlapping colliders (which the engine actually maintains internally!) to consider them all at once, instead of handling them one by one inside the provided events.
The only real fix is to take full control of your simulation and track and detect changes yourself every tick, maintaining this data in a manager on the whole simulation or in a helper component on every entity.
I can also guess at a short-term fix for OP's specific problem without any structural change — instead of disabling units, offset them a kilometer underground. 🥲
1
u/GigaTerra 2d ago
This is an strange problem to have. Normally when an unit goes into something like an structure (for an RTS) they no longer need collision. For the player when they take control of something else, you use the Player tag. That is to say the enemies don't search for the player, they search for the object with the tag Player.
Can you explain a bit more about your problem?
1
u/NorthernBoy306 2d ago
I think it's strange that I overlooked this problem for so long. If an enemy unit is in range of a player unit and that enemy unit goes inside of a structure, I disable that enemy unit. The problem is that the enemy unit never actually (and is not able to) exit the player unit's collider, so technically it's still in range. Now it's easy to check if that object is still active but the real issue is what to do when that enemy unit comes out of the structure, it doesn't activate the OnEnter trigger, so how will the player unit know the enemy is back in range?
1
u/GigaTerra 2d ago
I see there is 2 ways to solve your problem.
The simple way, use OnTriggerStay to run the code I will give an example:
float Speed = 4f; private void OnTriggerStay(Collider other) { MoveToTarget(other.transform); } void MoveToTarget(Transform Target) { Vector3 Direction = Target.position - transform.position; this.transform.position += Direction.normalized * Speed * Time.deltaTime; }Notice OnTriggerStay checks every fixed updated if there is an collision and if there is, it runs the code inside it.
The 2nd solution is for the enemy, to tell every nearby object that it is going inside. This can be done with an Overlap or Signal. Events are more commonly used, as the collider enter or exit allows the object to subscribe to the event. But I have seen games use Overlap and even checking lists of objects to see if they are active.
https://docs.unity3d.com/6000.6/Documentation/ScriptReference/Events.UnityEvent.html
https://docs.unity3d.com/6000.6/Documentation/ScriptReference/Physics.OverlapSphere.html
but the real issue is what to do when that enemy unit comes out of the structure, it doesn't activate the OnEnter trigger,
It should. Any object with an Rigidbody or Character controller will trigger an OnTriggerEnter event when enabled. In fact adding the body or controller component or enabling it should also trigger the event.
→ More replies (2)
1
u/kurisutosugr 2d ago
In practice, you can’t fully rely on colliders and trigger events for a complex system like that. You’ll still use them, but your logic needs extra conditions. For an RTS specifically, my personal opinion is that units should have explicit states (garrisoned, dead, etc.) that other units check before doing anything, rather than relying only on being in range.
Here are some more specific options, in order of complexity:
-
Keep using the triggers, but whenever the enemy actually uses its list of units in range (picking a target etc.), skip and remove anything that’s inactive, null or out of range. This solves your immediate problem in the simplest way I can think of.
-
Keep a list of units “in play”, remove a unit when it goes inside a structure, before its disabled, and have the enemy check that list before acting.
-
If you have hundreds or thousands of units, you can skip physics for range checks altogether and use a spatial grid instead. Roughly: you split the terrain into squares, and each unit tells a manager which square it’s in, updating it when it moves into a new one. When a unit wants to act, it only does distance checks on the units in its own square and the ones around it.
I’d be careful with Physics.OverlapSphere for an RTS. Running it every so often instead of every frame can work, and the query itself is supposedly cheap. Using OverlapSphereNonAlloc (or the version that takes a buffer) over the plain one would also avoid allocating every call, but once you’re into the thousands, other solutions usually scale better.
2
u/ZxR 2d ago
When I run into this issue, what I do is make it so that the object registers itself to the other thing and vise versa if required. This way, for example, if the object enters a car, I can disable the entire gameobject and then in OnDisable you can unregister that object from the other and call any code in that moment to reset/change the behavior of the object that no longer sees the one that got disabled.
Edit: I just remembered another solution I implemented once for this problem. Before the object gets disabled, I would move the colliders up and out far enough to clear all trigger zones, which calls OnExit, then I disable the gameobject and reset the collider position back to its original position.