Object Leaves Parent's Trigger During Parent Movement

**Scenario:**

  • 1 Platform GameObject (contains 2 Box colliders - 1 trigger/other not)
  • 1 Cube GabeObject (contains box collider and player controller)
  • The platform moves up and down
  • The cube rides the platform

The cube has a script that applies gravity and allows the cube to jump. The cube has Trigger events that handle parenting the platform.
(See code below)

Expected Result:

  1. Cube lands on platform
  2. Cube becomes child to platform
  3. Cube moves with platform
  4. Cube jumps
  5. Cube no longer child to platform
  6. Cube falls
  7. Repeat

Problem:

The issue occurs when the platform moves downward. This causes a TriggerExit() to be thrown and the cube receives this before moving with the platform. This causes a bouncing issue while the platform moves down.

CODE:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BoxGravity : MonoBehaviour {

    public float jumpForce = 20f;
    public float gravityScale = 10f;

    private CharacterController controller;
    private Vector3 moveDirection;

    void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.tag == "Platform")
        {
            transform.parent = other.transform;
        }
    }

    void OnTriggerStay(Collider other){/*Same as enter*/}

    void OnTriggerExit(Collider other)
    {
        if (other.gameObject.tag == "Platform")
        {
            transform.parent = null;
        }
    }

    void Start () {
        controller = GetComponent<CharacterController>();
        moveDirection = new Vector3(0, 0, 0);
	}
	
	void Update () {
		
        if(controller.isGrounded)
        {
            moveDirection.y = 0;

            if (Input.GetButtonDown("Jump"))
            {
                moveDirection.y = jumpForce;
            }
        }

        moveDirection.y = moveDirection.y + (Physics.gravity.y * gravityScale * Time.deltaTime);

        controller.Move(moveDirection*Time.deltaTime);
    }
}

Sample image:

Question:

How can I prevent the TriggerExit() from getting called before the child moves with its parent?

If not possible, how can I prevent this bouncing issue?

2 Answers

2

I’ve seen a problem like this before, when colliders are moved they actually get recalculated and that can fire an ontriggerexit. I forget how I got around it, and I can never find anything in the manual about it I had to hear about it at Unite 2016 in an optimizations talk XD.


you could try using a downward raycast from the player to detect a platform. if its within x distance reparent it and if not then parent to nothing, but youd need to be raycasting pretty much constantly is the problem there.


I suppose you could do a mash of both, like on trigger enter and on trigger exit fire that raycast and if theres a platform parent or stay parented, and if not then parent to nothing (just to avoid constantly casting.)

I have tried raycasting, but this just comes with too many issues. Its costly to raycast, especially if done constantly. This also causes landing and jumping issues. If the raycast is too short, then the issue doesn't get fixed and more resources get spent, if its too long then jumping becomes too complicated and normally does not work. This can also cause landing problems and creates a landing that is not smooth. I'm looking for some way I can prevent an improper call to OnTriggerExit() or some kind of check or flag to determine if an improper call was made.

One fix I came across is to have bools keep track of if the cube is doing any kind of movement action. In the example above, it would be as simple as this:

void OnTriggerExit(Collider other)
    {
        if (other.gameObject.tag == "Platform")
        {
            if(isJumping)
                transform.parent = null;
        }
    }

This is a pseudo way of determining a false OnTriggerExit() throw. However, this can become complicated real fast. What if a new outside source moves the cube off the platform? Like a gust of wind or another cube? Now you have another bool. And then new things could happen, thus more checks. etc.

My idea is to check for 2 bools.

private bool isMoving, isPushed;

void OnTriggerExit(Collider other)
 {
     if (other.gameObject.tag == "Platform")
     {
         if(isMoving || isPushed)
             transform.parent = null;
     }
 }
  1. isMoving and isPushed is set to false every Update
  2. Whenever player input moves cube, set isMoving = true;
  3. Whenever an outside source moves cube, set isPushed = true;

This answers the *“If not possible, how can I prevent this bouncing issue?” *** ** but not how to prevent a false OnTriggerExit() from being thrown.

NOTE: This can still have issues. if you are moving while the false OnTriggerExit() is thrown, the issue will still occur.