Я пишу 2-й физический движок для развлечения.Вызовите подходящую функцию, основанную на типе объектов
Я написал разные классы для разных форм. BoxObject
- это прямоугольник, BallObject
- это круг и LineObject
линия. Все они реализуют интерфейс PhysicsObject
.
Все объекты, которые моделируются, находятся в списке.
public static List<PhysicsObject> objects = new ArrayList<PhysicsObject>();
Все эти объекты необходимо проверять на предмет столкновения друг с другом.
Collision
класс содержит методы с именем penetrationData
, чтобы получить данные о проникновении для всех возможных пар типов, например. penetrationData(BoxObject a, BallObject b)
и penetrationData(BallObject a, LineObject b)
и т.п. здесь, как выглядит.
package com.optimus.game.physics;
public class Collision {
// Default - no collision
public static float[] penetrationData(PhysicsObject a, PhysicsObject b) {
return null;
}
// Line vs Circle
public static float[] penetrationData(LineObject line, BallObject ball) {
float distance = line.distance(ball.x, ball.y);
if (Math.abs(distance) > ball.radius) {
return null;
}
float penetration = ball.radius - Math.abs(distance);
float normalX = line.perpendicularX();
float normalY = line.perpendicularY();
return new float[] {normalX, normalY, penetration};
}
// Circle vs Line
public static float[] penetrationData(BallObject ball, LineObject line) {
float[] data = penetrationData(line, ball);
// reverse the normal
if (data != null) {
data[0] = -data[0];
data[1] = -data[1];
}
return data;
}
// Circle vs Circle
public static float[] penetrationData(BallObject a, BallObject b) {
// Vector from A to B
float normalX = b.x - a.x;
float normalY = b.y - a.y;
float penetration = 0;
// calculate the penetration and normal direction
// ...
// ... (code skipped)
return new float[] {normalX, normalY, penetration};
}
// Rect vs Rect
public static float[] penetrationData(BoxObject a, BoxObject b) {
// Vector from A to B
float normalX = b.x - a.x;
float normalY = b.y - a.y;
float penetration = 0;
// calculate the penetration and normal direction
// ...
// ... (code skipped)
return new float[] {normalX, normalY, penetration};
}
// Rect vs Circle
public static float[] penetrationData(BoxObject box, BallObject ball) {
// Vector from A to B
float normalX = ball.x - (box.x + box.width/2f);
float normalY = ball.y - (box.y + box.height/2f);
float penetration = 0;
// calculate the penetration and normal direction
// ...
// ... (code skipped)
return new float[] {normalX, normalY, penetration};
}
// Circle vs Rect
public static float[] penetrationData(BallObject ball, BoxObject box) {
float[] data = penetrationData(box, ball);
// reverse the normal
if (data != null) {
data[0] = -data[0];
data[1] = -data[1];
}
return data;
}
}
И я проверяю на столкновение как ...
public static void checkAndResolveCollisions(List<PhysicsObject> objects) {
// check collision between all objects in a list
// list of objects can contain BallObject, LineObject and
// BoxObject all of which implement PhysicsObject
// loop over all possible pairs
for (int i = 0; i < objects.size(); i++) {
for (int j = i + 1; j < objects.size(); j++) {
// here I want that apt function is called based on type
// but objects.get(int i) always returns a PhysicsObject
float[] data = Collision.penetrationData(objects.get(i), objects.get(j));
// data = null implies no overlap
if (data == null) {
continue;
}
// Calculate relative velocity in terms of the normal direction
float normalX = data[0];
float normalY = data[1];
float penetration = data[2];
// Resolve the collision along the normal
// ...
// ... (code skipped)
}
}
}
Но это не работает и penetrationData
возвращается null
каждый раз, когда ...
Что работа, если я получаю данные проникновения как этот, но этот код воняет, я могу сказать, просто посмотрев на него ...
// here is what works, but I know is bad java code
float[] data = getAptPenetrationData(objects.get(i), objects.get(j));
Где getAptPenetrationData
является общей функцией, приведения типов, а затем вызывает функцию склонный
// generic function that calls others
public static float[] getAptPenetrationData(PhysicsObject a, PhysicsObject b) {
// bad java code...
boolean aIsBall = a instanceof BallObject;
boolean aIsBox = a instanceof BoxObject;
boolean aIsLine = a instanceof LineObject;
boolean bIsBall = b instanceof BallObject;
boolean bIsBox = b instanceof BoxObject;
boolean bIsLine = b instanceof LineObject;
float[] data = null;
// circle vs circle
if (aIsBall && bIsBall) {
data = Collision.penetrationData((BallObject)a, (BallObject)b);
// box vs box
} else if (aIsBox && bIsBox) {
data = Collision.penetrationData((BoxObject)a, (BoxObject)b);
// box vs circle
} else if (aIsBox && bIsBall) {
data = Collision.penetrationData((BoxObject)a, (BallObject)b);
// circle vs box
} else if (aIsBall && bIsBox) {
data = Collision.penetrationData((BallObject)a, (BoxObject)b);
// circle vs line
} else if (aIsBall && bIsLine) {
data = Collision.penetrationData((BallObject)a, (LineObject)b);
// line vs circle
} else if (aIsLine && bIsBall) {
data = Collision.penetrationData((LineObject)a, (BallObject)b);
}
return data;
}
Я должен что-то отсутствует, и там должен быть лучший способ сделать то, что я пытаюсь сделать. Любая помощь приветствуется. Благодарю.
Edit:
Согласно предложению ЗИМ-ZAM, в столкновении теперь решена, как это ...
public static void checkAndResolveCollisions(List<PhysicsObject> objects) {
// check collision between all objects in a list
// list of objects can contain BallObject, LineObject and
// BoxObject all of which implement PhysicsObject
// loop over all possible pairs
for (int i = 0; i < objects.size(); i++) {
for (int j = i + 1; j < objects.size(); j++) {
// does not work, goes into infinite recursion
// still calls PhysicsObject.penetrationData(PhysicsObject)
float[] data = objects.get(i).penetrationData(objects.get(j));
// data = null implies no overlap
if (data == null) {
continue;
}
// Calculate relative velocity in terms of the normal direction
float normalX = data[0];
float normalY = data[1];
float penetration = data[2];
// Resolve the collision along the normal
// ...
// ... (code skipped)
}
}
}
, но он работает только если я добавляю ниже сигнатур методов PhysicsObject
интерфейса
public PenetrationData penetrationData(PhysicsObject other);
public PenetrationData penetrationData(BallObject other);
public PenetrationData penetrationData(BoxObject other);
public PenetrationData penetrationData(LineObject other);
, который по-прежнему не идеален, каждый раз, когда я реализую новый PhysicsObject
Я буду должны изменить интерфейс.
PhysicsObject реализации (только penetrationData
функции) -
BallObject.java
public class BallObject implements PhysicsObject {
@Override
public float[] penetrationData(PhysicsObject other) {
return other.penetrationData(this);
}
@Override
public float[] penetrationData(LineObject line) {
return Collision.penetrationData(this, line);
}
@Override
public float[] penetrationData(BoxObject box) {
return Collision.penetrationData(this, box);
}
@Override
public float[] penetrationData(BallObject ball) {
return Collision.penetrationData(this, ball);
}
// ... rest of the code
}
BoxObject.java
public class BoxObject implements PhysicsObject {
@Override
public float[] penetrationData(PhysicsObject other) {
return other.penetrationData(this);
}
@Override
public float[] penetrationData(LineObject line) {
return null; // not implemented
}
@Override
public float[] penetrationData(BoxObject box) {
return Collision.penetrationData(this, box);
}
@Override
public float[] penetrationData(BallObject ball) {
return Collision.penetrationData(this, ball);
}
// ... rest of the code
}
Line Объект.java
public class LineObject implements PhysicsObject {
@Override
public float[] penetrationData(PhysicsObject other) {
return other.penetrationData(this);
}
@Override
public float[] penetrationData(LineObject line) {
return null; // not implemented
}
@Override
public float[] penetrationData(BoxObject box) {
return null; // not implemented
}
@Override
public float[] penetrationData(BallObject ball) {
return Collision.penetrationData(this, ball);
}
// ... rest of the code
}
Было бы лучше иметь общий интерфейс/абстрактный класс для ваших объектов и использовать общие методы из интерфейса/абстрактного класса вместо выполнения всех этих сравнений 'if-else'. –
От Effective C++, Scott Meyers: * «В любое время, когда вы находите себе код формы», если объект имеет тип T1, а затем что-то делать, но если он имеет тип T2, тогда сделайте что-нибудь еще, «* slap –
@Baadshah Я бы ... но это не улучшило бы мой код ... ваши предложения могут ... – Optimus