0
votes

Just starting out with Unity so please be patient :) We are building a Zelda like game - top down, no physics / gravity, tilemaps for the map. We don't want to use the dynamic rigidbodies because of all the physics behind it.

The tilemap has "Walls" with a CompositeCollider2D and Static rigidbody, our player has a CircleCollider2D and a kinematic rigidbody. Both the player and the walls have a physics material with zero friction.

What is the best way for the collision detection with a kinematic rigibody? We have several concepts like Physics2D.CircleCastAll but ended up with our player stuck in the walls or getting stuck at the corners (the player should move smoothly around the corner) or getting stuck in two overlapping colliders.

The best solution we came up with is this (and the player still runs into the colliders sometimes but at least don't get stuck anymore).

// MoveDirection is the movement input
movement = MoveDirection * Speed * Time.deltaTime;

List<RaycastHit2D> results = new List<RaycastHit2D>();
List<RaycastHit2D> resultsX = new List<RaycastHit2D>();
List<RaycastHit2D> resultsY = new List<RaycastHit2D>();

if (movement.x != 0)
{
    actorCollider.Cast(new Vector2(movement.x, 0), filter, resultsX, movement.magnitude);
    results.AddRange(resultsX);
}

if (movement.y != 0)
{
    actorCollider.Cast(new Vector2(0, movement.y), filter, resultsY, movement.magnitude);
    results.AddRange(resultsY);
}

foreach (RaycastHit2D hit in results)
{
    Vector2 normal = hit.normal;

    float projection = Vector2.Dot(movement, normal);

    if (projection < 0)
    {
        if (hit.distance > 0)
        {
            projection += Mathf.Max(hit.distance - 0.01f, 0);
        }

        movement -= projection * normal;
    }
}

// move player
newPosition = body.position + movement;

// make sure the player is positioned at pixel perfect positions
newPosition.x = (Mathf.Round(newPosition.x * 16) / 16);
newPosition.y = (Mathf.Round(newPosition.y * 16) / 16);
body.MovePosition(newPosition);

// this part moves the player back in case it is in a collider.
List<Collider2D> colliderList = new List<Collider2D>();
if (actorCollider.OverlapCollider(filter, colliderList) > 0)
{
    foreach (Collider2D hit in colliderList)
    {
        var distance = hit.Distance(actorCollider);
        body.MovePosition(body.position + (distance.normal * Mathf.Abs(distance.distance)));
    }
}

Player setup:

enter image description here

Walls setup:

enter image description here

Somehow it still feels way too complicated. Is this the best approach to do the collision detections or do we do it completely wrong and there are better / easier ways? Keep in mind we want to be able to walk around the straight corners somehow and not get stuck on them. And there are overlapping colliders or colliders next to each other.

Thank you for your time!

1
You uploaded 2 times the same imagejanavarro

1 Answers

0
votes

If I'm understanding what you're trying to do correctly then you are massively over complicating this. If you have two objects in your scene with colliders they will not 'go into each other' if you let Unity do the movement for you.

What I mean by this is that rather than performing your movement using something like Player.Transform.position = new Vector3(Player.Transform.position.x + 1, Player.Transform.position.y, Player.transform.position.z) you can attach a RigidBody2D component to your player and reference it in your code using something like myRigidBody = GetComponent<RigidBody2D>(); Then in your movement functions you would simply use something like myRigidBody.velocity = new Vector3(moveSpeed, myRigidBody.velocity.y, myRigidBody.velocity.z) to move the player. Then, so long as the player has a collider, the object you want it to not go into has a collider and non of the colliders have 'isTrigger' ticked then the player won't go into any scenery.

I did notice your 'pixel correction' type code and although I have never used anything like it myself I assume you can just use it on top of this stuff and it'll work fine.