我正在为我的2d物理引擎写一个角度关节。除了当最大角度为负且最小角度为正(当角度直接在左侧)时,它起作用。
正如您所看到的,所有其他球都在其公差角度内移动,但是直接向左移动的球没有。
public class AngleJoint extends Joint {
private float minAngle;
private float maxAngle;
public AngleJoint(
final GameEntity a,
final GameEntity b,
final float midAngle,
final float tolerance
) {
super(a, b);
assert tolerance >= 0;
minAngle = midAngle - tolerance;
maxAngle = midAngle + tolerance;
while (minAngle > Math.PI) {
minAngle -= 2 * Math.PI;
}
while (minAngle < -Math.PI) {
minAngle += 2 * Math.PI;
}
while (maxAngle > Math.PI) {
maxAngle -= 2 * Math.PI;
}
while (maxAngle < -Math.PI) {
maxAngle += 2 * Math.PI;
}
System.out.println(minAngle + ", " + maxAngle);
}
@Override
public void update() {
assert getA() != null && getB() != null;
final CManifold m = new CManifold();
m.a = getA();
m.b = getB();
final Vec2D aToB = getB().center().minus(getA().center());
// angle from A to B
final float angle = aToB.getTheta();
if (angle >= minAngle && angle <= maxAngle) {
// we don't need to do anything
return;
}
final float distBtoA = aToB.length();
final float closestAngleBound
= Math.abs(angle - maxAngle) < Math.abs(angle - minAngle)
? maxAngle : minAngle;
// where we should be
final Vec2D solvedLocation
= getA().center().plus(
new Vec2D((float) (
Math.cos(closestAngleBound) * distBtoA),
(float) (Math.sin(closestAngleBound) * distBtoA)
)
);
final Vec2D correction = solvedLocation.minus(getB().center());
final float d = correction.length();
m.setNormal(correction.divide(d));
m.setPenetration(d);
Collisions.fixCollision(m, false);
}
}
这是我创建这个特定场景的地方。
final Vec2D centerV = new Vec2D(500, 700);
center = createBall(centerV, 75);
center.setMass(GameEntity.INFINITE_MASS);
entities.add(center);
final float vertices = 6;
final float dist = 120;
GameEntity first = null;
GameEntity last = null;
for (int i = 0; i < vertices; i++) {
final float angle = (float) (2 * Math.PI / vertices * i);
final Vec2D newCenter
= new Vec2D(
(float) (centerV.x + Math.cos(angle) * dist),
(float) (centerV.y + Math.sin(angle) * dist)
);
final GameEntity vertex = createBall(newCenter, 10);
entities.add(vertex);
if (last != null) {
// constraints.add(new DistanceJoint(last, vertex));
} else {
first = vertex;
}
constraints.add(new DistanceJoint(center, vertex));
constraints.add(new AngleJoint(center, vertex, angle, .1f));
last = vertex;
if (i == vertices - 1 && first != null) {
// constraints.add(new DistanceJoint(first, vertex));
}
}
}
如何修复我的更新方法,以便左侧的球与其他球的行为类似?
答案 0 :(得分:1)
这部分代码:
if (angle >= minAngle && angle <= maxAngle) {
// we don't need to do anything
return;
}
永远不会导致有问题的角度回归;由于maxAngle为负且minAngle为正,因此角度永远不会大于正的minAngle且小于负的maxAngle。
结果,即使当角度在两个极限之间时,该关节的执行也会落到这样的部分,即,角度被迫朝向最接近的极限。由于它被迫达到最接近的极限,它不能在极限之间自由弹跳。
对于maxAngle为负且minAngle为正的情况,您需要一些特殊的案例代码来替换上面引用的测试。你可能还需要解决其他问题,但这将是一个开始。
答案 1 :(得分:0)
我通过首先执行Warren Dew建议的检查来解决问题,但也改变了一点我的数学。这是完成的联合课程
public class AngleJoint extends Joint {
// angles stored between -Pi and Pi
private final float minAngle;
private final float maxAngle;
/**
*
* @param a
* @param b
* @param midAngle
* the angle in the range of -Pi to Pi.
* @param tolerance
* the angle tolerance in both directions. 0 <= tolerance < Pi
*/
public AngleJoint(final GameEntity a, final GameEntity b, final float midAngle, final float tolerance) {
super(a, b);
if (tolerance < 0 || tolerance >= AngleUtils.PI) {
throw new IllegalArgumentException("Tolerance must be >= 0 and < Pi");
}
minAngle = AngleUtils.normalize(midAngle - tolerance);
maxAngle = AngleUtils.normalize(midAngle + tolerance);
}
@Override
public void update() {
assert getA() != null && getB() != null;
final CManifold m = new CManifold();
m.a = getA();
m.b = getB();
final Vec2D aToB = getB().center().minus(getA().center());
// angle from A to B
final float angle = aToB.getTheta();
if (angle >= minAngle && angle <= maxAngle) {
// we don't need to do anything
return;
}
// if we are in that dumb spot where maxAngle < min Angle (directly to the left) we need extra checks
if (maxAngle < minAngle && (angle <= maxAngle && angle >= -AngleUtils.PI || angle >= minAngle && angle <= AngleUtils.PI)) {
return;
}
final float distBtoA = aToB.length();
final float closestAngleBound = AngleUtils.angleDifference(angle, maxAngle) < AngleUtils.angleDifference(angle, minAngle) ? maxAngle
: minAngle;
// where we should be
final Vec2D solvedLocation = getA().center().plus(
new Vec2D((float) (Math.cos(closestAngleBound) * distBtoA), (float) (Math.sin(closestAngleBound) * distBtoA)));
final Vec2D correction = solvedLocation.minus(getB().center());
final float d = correction.length();
m.setNormal(correction.divide(d));
m.setPenetration(d);
Collisions.fixCollision(m, false);
}
}