角度约束对某些角度不起作用

时间:2015-08-14 01:24:06

标签: java math 2d physics game-physics

我正在为我的2d物理引擎写一个角度关节。除了当最大角度为负且最小角度为正(当角度直接在左侧)时,它起作用。

正如您所看到的,所有其他球都在其公差角度内移动,但是直接向左移动的球没有。

6 angle joints connected to the center

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));
        }
    }
}

如何修复我的更新方法,以便左侧的球与其他球的行为类似?

2 个答案:

答案 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);
    }

}