Как я могу улучшить свой дизайн кода, чтобы убрать необходимость в instanceof в Java? - PullRequest
2 голосов
/ 24 марта 2020

Я решил попытаться создать простой игровой движок и, похоже, застрял в том, как проверять наличие столкновений между различными типами коллайдеров для моих игровых объектов без необходимости повторного использования instanceof. Например, некоторые игровые объекты могут использовать SphereCollider, а другие могут использовать AABBCollider, оба из которых расширяют класс Collider.

Вот код в моем классе GameObject.

public final boolean collidesWith(Collider c) {
    if (this.collider instanceof Collider3D) {
        if (c instanceof AABBCollider3D) {
            if (((Collider3D) this.collider).collidesWith((AABBCollider3D) c))
                return true;
        } else if (c instanceof SphereCollider3D) {
            if (((Collider3D) this.collider).collidesWith((SphereCollider3D) c))
                return true;
        } else if (c instanceof AABBCollider2D) {
            if (((Collider3D) this.collider).intersects((AABBCollider2D) c))
                return true;
        }
    } else if (this.collider instanceof Collider2D) {
        // Do 2D collision stuff...
    }
}

Collider3D class

public abstract class Collider3D extends Collider {

public Collider3D(RigidBody rigidBody) {
    super(rigidBody);
}


public abstract boolean collidesWith(AABBCollider3D collider);

public abstract boolean collidesWith(SphereCollider3D collider);

public abstract boolean intersects(AABBCollider2D collider);

public abstract boolean intersects(CircleCollider2D collider);

}

AABBCollider3D class

public class AABBCollider3D extends Collider3D {

private final Cuboid collisionBounds;


public AABBCollider3D(RigidBody rigidBody) {
    super(rigidBody);

    this.collisionBounds = new Cuboid();
}


@Override
public void update() {
    final Vector3f objectLocation = getRigidBody().getObject().getLocation().getPosition();
    final Matrix4f transformMatrix = getRigidBody().getObject().getTransformationMatrix();

    final float width = transformMatrix.m03();
    final float height = transformMatrix.m13();
    final float depth = transformMatrix.m23();

    // Set the collision bounds' location
    collisionBounds.getLocation().setPosition(
            objectLocation.x,
            objectLocation.y,
            objectLocation.z);  

    // Set the dimension of the collision bounds
    collisionBounds.setWidth(width);
    collisionBounds.setHeight(height);
    collisionBounds.setDepth(depth);
}

@Override
public boolean collidesWith(AABBCollider3D collider) {
    // Check collisions...
}

@Override
public boolean collidesWith(SphereCollider3D collider) {
    // Check collisions...
}

@Override
public boolean intersects(AABBCollider2D collider) {
    // Check collisions...
}

@Override
public boolean intersects(CircleCollider2D collider) {
    // Check collisions...
}

}

Я хотел бы знать, есть ли лучший способ сделать это, главным образом по двум причинам:

  1. Я слышал, что использование ключевого слова instanceof для проверки типов объектов таким способом - обходной путь для неправильного проектирования кода
  2. Добавление пользовательских типов коллайдера в будущем потребует от меня расширения логики c здесь, так как ну как в моем классе коллайдера

1 Ответ

3 голосов
/ 24 марта 2020

Добавьте методы к вашему типу Collider для каждого специального подтипа. Это своего рода вырожденный шаблон посетителя.

Таким образом, он будет выглядеть примерно так:

interface Collider {
    boolean collidesWith(Collider other);

    // I've used the same name here,
    //   not necessary but makes things easier.
    boolean collidedBy(AABBCollider3D collider);
    boolean collidedBy(SphereCollider3D collider);
    // ...
}

Каждый подтип будет выглядеть так:

public class AABBCollider3D implements Collider {
    public boolean collidesWith(Collider other) {
        return other.collidedBy(this);
    }

    public boolean collidedBy(AABBCollider3D collider) {
        // ... code for AABBCollider3D collided by AABBCollider3D.
    }
    public boolean collidedBy(SphereCollider3D collider) {
        // ... code for AABBCollider3D collided by SphereCollider3D.
    }
    // ...
}

Это создаст взаимные зависимости между Collider подтипами. Чтобы уменьшить влияние этого разделения, введите тип между тем, где это необходимо, и тем, где это не так.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...