Each of this Grandchild Triggers has a script that keeps the platforms “On area” to know if I have a wall, floor or ceiling.
Non of the triggers touch the other triggers (and the script doesn’t count parent children and grandchildren collisions), top and bottom ones have a small width and left and right have a small height. (Image)
The problem comes when I run “too fast” against a wall, the top trigger triggers even if it is still not over the wall or even near it. The colliders won’t let that happen, but somehow the top trigger tries to predict next frames and triggers itself.
This is a problem since my gravity depends on where I am on the floor or if I hit a ceiling, so if I run too fast vs a wall while jumping my jump force goes to 0 and then starts going down (while it should have kept going up)
Any idea on how to prevent this from happening?
I did a debug log to name the object that is triggering the action and it is in fact the wall. (player velocity is going left)
Each trigger is a GameObject with parent Triggers and GrandParent Player
Also, this only happens when the player is moving “fast”, if I walk to it the top trigger does not triggers.
(Moved it from 2D since this is actually a physics problem)
There’s not enough information here to offer much of a diagnosis. How are you moving the Rigidbody2D? What is the ‘Trigger Zone’ (is it the obstacle edge you’re referring to).
Ok, I am moving my rigidbody entering the velocity itself
velocity = verticalVector+horitzontalVector;
My horizontalVector = input horizontal
Still if left trigger is on and horizontal.x <0 then horizontal.x = 0; (same for right)
vertical vector
if input vertical vector = input (jump)
every frame += gravity;
if bottom collider is on and gravity is negative vertical = 0;
if top collider is on and gravity is positive vertical = 0;
On this image the Player is moving left, each of the triggers (top, right, bottom and left) are in blue, ONLY the left trigger is on a trigger zone since it is touching the obstacle edge. Still the top trigger “OnTriggerEnter” sends info and says that it is triggering vs the obstacle to its left.
For the next frame the horizontalVector will become 0 meaning that the Player will no longer be moving to the left and the top trigger will never hit the obstacle to its left. For this frame “OnTriggerExit” of the top trigger sends info that the trigger is no longer active.
Still the top trigger that is about .21 Unity unities or .21 meters to the right of the left platfrom collider or the obstacle edge decides to trigger itself as if was overlaping or touching the obstacle to its left.
Trigger code just in case:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class IsColliding : MonoBehaviour {
public delegate void OnSpecialChange(bool value);
public OnSpecialChange Colliding;
public OnSpecialChange Ladder;
public delegate void OnVectorChange(Vector2 value);
public OnVectorChange newMovementVector;
private bool b_colliding = false;
private List<Collider2D> OnArea;
private bool b_ladder = false;
private List<Collider2D> OnLadder;
private Collider2D movingPlatform;
private MovingPlatform movingScript;
public bool isBottom = false;
void Start()
{
OnArea = new List<Collider2D>();
OnLadder = new List<Collider2D>();
StartCoroutine(CheckIfStillColliding());
}
void OnTriggerEnter2D(Collider2D other)
{
if(other.gameObject != null)
{
if(transform.parent != null)
{
if(transform.name == "Top")
{
Debug.Log(other.name);
Debug.Break();
}
if(other.GetInstanceID() != transform.parent.GetInstanceID() )
{
if(!OnLadder.Contains(other) && other.tag == "Ladder")
OnLadder.Add(other);
else if(!OnArea.Contains(other) && other.tag != "Ladder")
OnArea.Add(other);
}
if(isBottom)
{
if(other != movingPlatform && other.gameObject.GetComponent<MovingPlatform>() != null)
{
movingPlatform = other;
movingScript = other.gameObject.GetComponent<MovingPlatform>();
movingScript.newVector+=ChangeVector;
if(newMovementVector != null)
newMovementVector(movingScript.GetMVector());
}
if(other.gameObject.GetComponent<FallAfterTime>() != null)
{
other.gameObject.GetComponent<FallAfterTime>().StartFall();
}
}
}else{
if(!OnLadder.Contains(other) && other.tag == "Ladder")
OnLadder.Add(other);
else if(!OnArea.Contains(other) && other.tag != "Ladder")
OnArea.Add(other);
}
}
RunArea();
RunLadder();
}
void OnTriggerExit2D(Collider2D other)
{
OnArea.Remove(other);
OnLadder.Remove(other);
RunArea();
RunLadder();
if(isBottom)
{
if(movingPlatform != null && other == movingPlatform)
{
movingPlatform = null;
movingScript.newVector-=ChangeVector;
movingScript = null;
if(newMovementVector != null)
newMovementVector(Vector2.zero);
}
if(other.gameObject.GetComponent<FallAfterTime>() != null)
{
other.gameObject.GetComponent<FallAfterTime>().WhenTouchEndsFall();
}
}
}
void RunArea()
{
if(!b_colliding && OnArea.Count > 0)
{
b_colliding = true;
if(Colliding != null)
Colliding(true);
} else if (b_colliding && OnArea.Count == 0)
{
b_colliding = false;
if(Colliding != null)
Colliding(false);
}
}
void RunLadder()
{
if(!b_ladder && OnLadder.Count > 0)
{
b_ladder = true;
if(Ladder != null)
Ladder(true);
} else if (b_ladder && OnLadder.Count == 0)
{
b_ladder = false;
if(Ladder != null)
Ladder(false);
}
}
void ChangeVector(Vector2 value)
{
if(newMovementVector != null)
newMovementVector(value);
}
//ThisCoroutine works 4 times (north, east, south west) 60 times per second and checks all the colliders, hardly it will be more than 8 total.
IEnumerator CheckIfStillColliding()
{
while(true)
{
for(int i =0; i < OnArea.Count; i++)
{
if(!OnArea[i].enabled || OnArea[i] == null)
{
OnArea.Remove(OnArea[i]);
RunArea();
}
}
//60 frames per second
yield return new WaitForSeconds(0.16f);
}
}
}
Here is a gif showing it, when the ball hits the right wall and stops sliding up means that the top trigger got triggered (does not happen every time)
Also this time it only happened on the right side.
It can also be replicated without a vertical force so the jump is not the problem, the horizontal velocity might, but only if the physics try to predict next couple of frames instead of actually waiting for the physics to work and then check the triggers.
P.S.: I am using Unity 5.0 but the same happens on Unity 5.1.1f1 (might be important)
I would say create a much more simplified script without co-routines and a whole bunch of logic in the OnTriggerEnter2D callback. Just a debug.log to see what you’re actually getting.
What I mean by this is that you’re saying that you’re inferring that the trigger is triggering because it stops because you assume the logic you’ve entered is correct. Why not just output a simple check to see if that trigger is indeed triggering and trace through the problem? I’m not saying it isn’t, just that I’d prefer to not end up debugging yet another project.
If you have such a repro case and it still looks wrong then please submit a bug case and post the case number here so I can look at it.
Discrete collision detection for faster moving objects causes overlaps which are then solved by applying a force to resolve the overlap. This can lead to temporary overlaps or even completely penetration of colliders.
I did all the debugging before making the post, even making the booleans public to see how they are triggering, a Debug.Break(); to be 100% sure and a Time.frameCount when trigger enters and exits. (The triggers were not my first thought when I saw this was happening)
Top and Bottom triggers get activated when the player moves “too fast” (if I change speed values to be too high it happens every time I hit a wall) still this is not the same to all the triggers since it only activates right OR left (the one actually over the wall).
Also it is only active for 1 frame meaning that the next frame it always exits (when it does know that the trigger is not inside the trigger area)
The continuous collision detection makes it slightly harder to happen but still happens, I have also played with interpolation and all the physics2D settings in hope to see if it can do any difference (extrapolate makes it worse).
The Coroutine exists just as a garbage collector, some times the trigger exit won’t happen (if I disable/destroy the colliding tile).
My workaround is waiting to the end of the next frame before sending the update, I just wanted to know if someone else had the same problem and how did he/she fixed it.
Physics obviously don’t get updated per frame nor does collision reporting, only during the fixed update (after you get the fixed-update callback) so this is very confusing.
Well something is really wrong there then if interpolate/extrapolate makes it worse because those do not affect the Rigidbody2D position at all; they simply modify the transform during frame-update to move from the last RB position to the current (in the case of interpolate) and from the current RB position to an extrapolated RB position based entirely on the RB linear velocity (extrapolate). They both modify the transform position only, the Rigidbody2D stays whereever the physics system puts it. This would suggest that something is using the transform position to move the Rigidbody2D position.