Collisions not getting noticed unless approached diagonally

So, I am currently working on a script to interact with objects from a top-down perspective, both my player character and interactable object are squares.

My player is generally able to interact with the object, except every now and again (seemingly at random) the left (and only the left) side of the object becomes impossible to interact with, due to some kind of thin invisible hitbox(?) appearing there. This only happens upon pressing play, this hitbox never appears while the code is running.

This hitbox is completely impenetrable, EXCEPT if the player moves diagonally into it, at which point the player moves past it to the object’s “actual” hitbox, and it doesn’t return until I press play again.

There doesn’t appear to be anything wrong with my box colliders in either the player or object, and after doing some Debug.Log - ing, it appears that the problem is caused by the thin hitbox blocking the collision that should occur (and not a problem with my interaction code.)

I’m using C#, here’s my interaction script with some of the collision code inside it

protected virtual void Start()
{
    boxCollider = GetComponent<BoxCollider2D>();
    player = GameObject.Find("Player");
    thePlayerScript = player.GetComponent<Player>();
    playerTransform = player.GetComponent<Transform>();
    interAction= InputSystem.actions.FindAction("Interact");
}
protected virtual void Update()
{
    //collision work
    boxCollider.OverlapCollider(filter, hits);
    for (int i = 0; i < hits.Length; i++)
    {
        if (hits[i] == null)
            continue;

        //This section only happens if there's a collision.
        //If player is to the right and facing left:
        if ((playerTransform.position.x <= transform.position.x + 0.4) && thePlayerScript.playerDirection == "Left" && interAction.IsPressed() && (playerTransform.position.y >= transform.position.y - 0.1) && (playerTransform.position.y <= transform.position.y + 0.1))
        {
            Interact();
        }
        //If player is to the left and facing right:
        if ((playerTransform.position.x >= transform.position.x - 0.4) && thePlayerScript.playerDirection == "Right" && interAction.IsPressed() && (playerTransform.position.y >= transform.position.y - 0.1) && (playerTransform.position.y <= transform.position.y + 0.1))
        {
            Interact();
        }
        //If player is to the top and facing down:
        if ((playerTransform.position.y <= transform.position.y + 0.4) && thePlayerScript.playerDirection == "Down" && interAction.IsPressed() && (playerTransform.position.x >= transform.position.x - 0.1) && (playerTransform.position.x <= transform.position.x + 0.1))
        {
            Interact();
        }
        //If player is to the bottom and facing up:
        if ((playerTransform.position.y >= transform.position.y - 0.4) && thePlayerScript.playerDirection == "Up" && interAction.IsPressed() && (playerTransform.position.x >= transform.position.x - 0.1) && (playerTransform.position.x <= transform.position.x + 0.1))
        {
            Interact();
        }

        //The array is not cleaned up every time, so we do it ourself
        hits[i] = null;
    }
}

Then here’s the player script’s movement and collision code:

        hity = Physics2D.BoxCast(transform.position, boxCollider.size, 0, new Vector2(0, moveDelta.y), Mathf.Abs(moveDelta.y * Time.deltaTime), LayerMask.GetMask("Actor", "Blocking"));
        if (hity.collider == null)
        {

            //makes player actually move (y)
            transform.Translate(0, moveDelta.y * Time.deltaTime, 0);

        }

        hitx = Physics2D.BoxCast(transform.position, boxCollider.size, 0, new Vector2(moveDelta.x, 0), Mathf.Abs(moveDelta.x * Time.deltaTime), LayerMask.GetMask("Actor", "Blocking"));
        if (hitx.collider == null)
        {

            //makes player actually move (x)
            transform.Translate(moveDelta.x * Time.deltaTime, 0, 0);

        }

(There is no blocking layer related to this problem, and the object is on the actor layer.)

Does anyone have an idea about how I would fix this issue?

My apologies if I used incorrect terminology or failed to give enough information, I am still very new to C# programming and Unity.

Don’t do this:

With Physics (or Physics2D), never manipulate the Transform directly. If you manipulate the Transform directly, you are bypassing the physics system and you can reasonably expect glitching and missed collisions and other physics mayhem.

This means you may not change transform.position, transform.rotation, you may not call transform.Translate(), transform.Rotate() or other such methods, and also transform.localScale is off limits. You also cannot set rigidbody.position or rigidbody.rotation directly. These ALL bypass physics.

Always use the .MovePosition() and .MoveRotation() methods on the Rigidbody (or Rigidbody2D) instance in order to move or rotate things. Doing this keeps the physics system informed about what is going on.

Thank you for the tip, it is highly likely that my physics are better off with Rigidbody controlling them.

However, this issue has still persisted, despite there being no mention of transform in my code. It may be worth mentioning that now the invisible hitbox barrier has moved to both the top and bottom sides of the object, rather than the left, and that while I was testing, I set the player’s rigidbody to kinematic just to see what would happen. Despite the fact that it should have been able to pass through the object, it still collided with the hitbox on top of the object. (It still passed through on the left and right.)

Here’s the updated player code, in case it helps:

hit = Physics2D.BoxCast(transform.position, boxCollider.size, 0, new Vector2(moveDelta.x, moveDelta.y), Mathf.Abs(moveDelta.y * Time.deltaTime), LayerMask.GetMask("Actor", "Blocking"));
if (hit.collider == null)
{
    rigidBody.MovePosition(rigidBody.position + moveDelta * Time.deltaTime);
}

The only changes that have been made to interaction code are that they are now checking the rigidbody position, rather than the transform position.

Again, thanks for alerting me to the problems with transform, but it appears that the issue here is related to some other part of the code.

EDIT: It seems that this problem ONLY appears after the player has moved diagonally at least once. I’d rather not disable diagonal movement, but hopefully this is useful for diagnosing the problem.

How are you actually coming to this conclusion? Colliders don’t appear out how nowhere.

The issue could be in your crazy-long, repeated if-statements, where any single, hard-to-notice typo could be creating a bug. Try to avoid writing code like that, as it makes your code hard to read and next-to-impossible to debug.

Condense those statements into a loop (such as using an array with a length of 4 storing your cardinal directions), and break each of those conditionals and calculations down into individual values/operations before you do your final assessment.

Also, don’t use strings to infer information, as you are for player direction. At the very least, use an Enum. Better yet, use a Vector2 direction for the player’s heading, and compare that to the direction to the interaction object using Vector2.Dot to determine whether the player is facing said object.

All that said, why even check this every frame? Why even check all around the player? You should only need to do one check in the direction the player is facing if they press the interaction input. If they don’t press anything, why even run any of this code at all in the first place?

Thank you for the criticisms, upon updating the player code to improve them as per your suggestions, the odd “hitboxes” disappeared.

To answer your query, I was aware that colliders don’t appear out of nowhere, but I was struggling for any other word to explain what was actually happening. My apologies for using incorrect terminology.

For any poor, unfortunate soul who manages to mess up their code as badly as I did, here is the player script that fixed the bug:

if (moveDelta.x != 0)
{
    lastDirection.x = moveDelta.x;
}
if (moveDelta.y != 0)
{
    lastDirection.y = moveDelta.y;
}

hit = Physics2D.BoxCast(rigidBody.position, boxCollider.size, 0, new Vector2(moveDelta.x, moveDelta.y), Mathf.Abs(moveDelta.y * Time.deltaTime), LayerMask.GetMask("Actor", "Blocking"));
if (hit.collider == null)
{
    rigidBody.MovePosition(rigidBody.position + moveDelta * Time.deltaTime);
}

if (interAction.IsPressed())
{
    isObjectHere = Physics2D.BoxCast(rigidBody.position, new Vector2(0.1f, 0.1f), 0, lastDirection, 0.4f, LayerMask.GetMask("Actor"));
    if (isObjectHere.collider != null && isObjectHere.collider.gameObject.GetComponent<Interactable>() != null)
    {
        isObjectHere.collider.gameObject.SendMessage("Interact");
    }
}

I’m vaguely certain that this is nearly as inefficient as my previous code, but at least it doesn’t cause a major bug, so I count that as a win.

Thank you to those who helped.

If you have an actual BoxCollider2D (or any Collider2D) then don’t impersonate it by reading its properties and then passing it to the BoxCast call, instead simply call Collider2D.Cast then you only have to say where you want to cast and how and can ignore the actual collider settings which may change in the future requiring you to change your query.

Also note that you can call Rigidbody2D.Cast too which will cast all the colliders attached to it to save you even caring about what you’ve added or how they’re configured.

Also, use the overloads of physics queries that take the ContactFilter2D and create a public field so you can configure it in the inspector. Either that or simply add a public field of type LayerMask and again, configure what it should hit there. This is far, far better than putting in hard-coded strings throughout your code.

If you’ve not done so already, I would highly recommend just browsing the Scripting API for these types to see what’s available; there’s a lot of functionality that can help you here.

Good luck.