0
\$\begingroup\$

I'm currently working on a minecraft clone and right now trying to implement player to terrain collision. My approach is to get the blocks which the player might collide with through the collision box of the player expanded by his velocity and from there handle each block by comparing the distance along each axis with the velocity in that direction and keeping the smaller one. Then finally I move the player by the smallest returned movement along each axis.

Now the problem I currently have is that, when moving into a corner or edge of a block it doesn't work as expected (it does so otherwise) but instead makes the player glitch around inside block or even letting him walk fully into the block. How could I fix that?

Player.cs (Update method):

IList<Box> collidables = world.GetBoxes(collisionBox.Moved(position).Expanded(velocity * 2));

foreach (Box c in collidables) {
    velocity = collisionBox.Moved(position).ResolveCollision(c, velocity);
}

position += velocity;

Box.cs(inside struct Box):

public Box Expanded(Vector3 expand) {
    Vector3 _min = min;
    Vector3 _max = max;

    if (expand.X < 0) {
        _min.X += expand.X;
    } else {
        _max.X += expand.X;
    }

    if (expand.Y < 0) {
        _min.Y += expand.Y;
    } else {
        _max.Y += expand.Y;
    }

    if (expand.Z < 0) {
        _min.Z += expand.Z;
    } else {
        _max.Z += expand.Z;
    }

    return new Box(_min, _max);
}

public float ResolveXCollision(Box b, float x) {
    if (b.Max.Y <= Min.Y || b.Min.Y >= Max.Y) {
        return x;
    }

    if (b.Max.Z <= Min.Z || b.Min.Z >= Max.Z) {
        return x;
    }

    if ((x > 0) && (Center.X <= b.Center.X)) {
        float max = b.Min.X - Max.X;
        if (Math.Abs(max) < Math.Abs(x)) {
            x = max;
        }
    }

    if ((x < 0) && (Center.X >= b.Center.X)) {
        float max = b.Max.X - Min.X;
        if (Math.Abs(max) < Math.Abs(x)) {
            x = max;
        }
    }

    return x;
}

public float ResolveYCollision(Box b, float y) {
    if (b.Max.X <= Min.X || b.Min.X >= Max.X) {
        return y;
    }

    if (b.Max.Z <= Min.Z || b.Min.Z >= Max.Z) {
        return y;
    }

    if ((y > 0) && (Center.Y <= b.Center.Y)) {
        float max = b.Min.Y - Max.Y;
        if (Math.Abs(max) < Math.Abs(y)) {
            y = max;
        }
    }

    if ((y < 0) && (Center.Y >= b.Center.Y)) {
        float max = b.Max.Y - Min.Y;
        if (Math.Abs(max) < Math.Abs(y)) {
            y = max;
        }
    }

    return y;
}

public float ResolveZCollision(Box b, float z) {
    if (b.Max.X <= Min.X || b.Min.X >= Max.X) {
        return z;
    }

    if (b.Max.Y <= Min.Y || b.Min.Y >= Max.Y) {
        return z;
    }

    if ((z > 0) && (Center.Z <= b.Center.Z)) {
        float max = b.Min.Z - Max.Z;
        if (Math.Abs(max) < Math.Abs(z)) {
            z = max;
        }
    }

    if ((z < 0) && (Center.Z >= b.Center.Z)) {
        float max = b.Max.Z - Min.Z;
        if (Math.Abs(max) < Math.Abs(z)) {
            z = max;
        }
    }

    return z;
}

public Vector3 ResolveCollision(Box b, Vector3 mov) {
    float x = ResolveXCollision(b, mov.X);
    Box xb = b.Moved(new Vector3(x, 0, 0));

    float y = ResolveYCollision(xb, mov.Y);
    Box yb = xb.Moved(new Vector3(0, y, 0));

    float z = ResolveZCollision(yb, mov.Z);
    Box zb = yb.Moved(new Vector3(0, 0, z));

    return new Vector3(x, y, z);
}

public Box Moved(Vector3 movement) => new Box(min + movement, max + movement);
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.