How can I prevent colliders from skipping through the pickups?


Hi,

I’m currently developing my first project which I actually plan on finishing :slight_smile: It’s a sidescroller, where a bird can be shifted between 3 lanes (upper, middle, lower) to collect pickups for points. As I aim for 30fps (older Android phones) and the bird can get pretty fast, I ran into the issue where the bird skipped through the pickups and the collider wouldn’t trigger. I resolved this with a linecast from its current position to its position in the prior frame; this works good enough.

Now I wanted to add a “combo” feature, where a bonus is rewarded whenever 10 pickups are being collected without missing one. For this I have a combo counter with a reset whenever a pickup is being missed. To achieve this, I added two colliders - one above and one below the bird. I left the mesh renderer on in the pic for clarity. Now the colliders have the same issue of skipping through the pickups, but I cannot resolve it with a linecast, as pickups are not being detected when the bird is in the progress of switching lanes and only halfway there (the linecast is just a point and not as wide as the shown collider). Next, I tried a spherecast of the size of the whole “collider”:

private Vector3 lastPosition;
private RaycastHit hit;
[SerializeField]
private LayerMask collisionLayerPickups;
private float rayRadius;

private void Start()
{
    rayRadius = transform.localScale.y / 2;
    lastPosition = transform.position;
}

private void Update()
{
    float rayLength = Vector3.Distance(transform.position, lastPosition);
    Vector3 rayDirection = (lastPosition - transform.position).normalized;

    if (Physics.SphereCast(transform.position + new Vector3(rayRadius, 0, 0), rayRadius, rayDirection, out hit, rayLength, collisionLayerPickups))
    {
        if (hit.transform.tag == "Pickup")
        {
            GameplayManager.Instance.ComboCounter(0, true);
        }
    }
    lastPosition = transform.position;
}

However, this is also not working consistently. Some pickups are not recognised, and sometimes it seemingly randomly detects pickups where there are none.

As a last idea, I could add a huge collider behind the bird, big enough that no pickups can skip through it; however, this seems like a dirty solution, as it’s not very systematic (if the birds gets even faster, eventually things could slip through).

I hope you understand my issue. How would one solve this in a clean and tidy way? I am wondering the same for bullets in shooters, etc.

Thanks for your help!

Hello,
are you keeping every elements “alive” all the time in your side-scroller? You would accumulate the data behind the bird, limiting the length of the flight (if you’re working for old phones, making sure the data stored remains low is not trivial).
If not, you can capture the moment the pickup is destroyed.
Another problem with the big collider is to make sure that pickups are only counted once (and not every frame), so that you can start your counter even if some of them are still behind.
If your bird’s position can be easily accessed by the pickups, a simple monobehaviour script on them checking if their position is behind the bird would be far more reliable. Add a boolean on that component (like “alreadyChecked”) so that your counter can start over as soon as the pickup is counted as missed.

1 Like

Thank you! I was so deep into spherecasts & Co. that I didn’t even think as simple as checking the player position vs. the pickup positions. This is what I have on my pickups now for destroying them (the -4 offset is where the actual screen ends behind the player, the linecast is an extra step to not accidentally disable the pickup before it was checked for collection by the player class in case of a high speed / low framerate).

    private void OnEnable()
    {
        lastPositionPlayer = player.transform.position;
    }

    private void Update()
    {
        currentPostionPlayer = player.transform.position;
        CheckPickupMiss();
        lastPositionPlayer = currentPostionPlayer;
    }

    private void CheckPickupMiss()
    {
        if (currentPostionPlayer.x - 4f > transform.position.x && lastPositionPlayer.x - 4f < transform.position.x)
        {
            if (Physics.Linecast(currentPostionPlayer, lastPositionPlayer, out hit, collisionLayerPickups))
            {
                if (hit.transform.gameObject == gameObject)
                {
                    return;
                }
            }
            else
            {
                GameplayManager.Instance.ComboCounter(0, true);
                gameObject.SetActive(false);
            }
        }
    }

Thanks for pointing out the other things to look out for, but I think my code performs pretty decent. I am aware of issues like floating point inaccuracy and also concepts like object pooling and designed my code around it! Although I’m a starter, I’m trying to learn good practices from the get go :slight_smile:
Thanks again for the reply!