Как исправить, что 3D AABB Collision не работает правильно на углах - PullRequest
0 голосов
/ 31 декабря 2018

В настоящее время я работаю над клоном Minecraft и сейчас пытаюсь реализовать столкновение игрока с землей.Мой подход состоит в том, чтобы получить блоки, с которыми игрок мог столкнуться, через поле столкновения игрока, расширенное его скоростью, и оттуда обрабатывать каждый блок, сравнивая расстояние вдоль каждой оси со скоростью в этом направлении и сохраняя меньшую единицу.Затем, в конце концов, я двигаю игрока наименьшим возвращаемым движением вдоль каждой оси.

Теперь у меня проблема в том, что при перемещении в угол или край блока он работает не так, как ожидалось (иначе), но вместо этого заставляет игрока сгибаться внутри блока или даже позволить ему полностью войти в блок.Как я могу это исправить?

Player.cs (метод обновления):

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 (внутри 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);
...