Перемещайте тело, имитируя движение моего пальца на экране с постоянной скоростью

Мне нужна помощь в решении следующей проблемы, так как я потратил много дней, не найдя приемлемого решения.

Я делаю игру для Android (использую libgdx), где главный герой (с именем Hero) виден сверху (игра сверху вниз) и ходит по полю. Пользователь перемещает персонажа, перемещая палец по экрану. Палец не должен быть на персонаже.

Персонаж использует две анимации, одну анимацию, когда он движется вперед (то есть, когда его "у" больше нуля, поскольку пользователь смотрит на игру со "неба"), и другую анимацию, когда он движется назад (то есть, когда его "у" меньше нуля, помните, я разрабатываю игру с видом сверху вниз).

Наконец, мне нужно, чтобы персонаж всегда двигался с постоянной скоростью.

Короче говоря, я хотел бы обращаться с персонажем пальцем и перемещать его в направлении, отмечающем мой палец, всегда с постоянной скоростью.

Это было бы очень легко, если бы я мог установить положение символа каждый раз, но я использую box2d, который знает только о линейной скорости, импульсах, силах и т. Д.

Я попытался использовать mouseJoint, где hitbody - мой главный герой (Hero), а GroundBody - невидимое тело.

// Invisible zero size ground body
// to which we can connect the mouse joint
Body groundBody;
BodyDef bodyDef = new BodyDef();
groundBody = world.createBody(bodyDef);

/* player is an instance of my Hero's class, which has a box2d body and
update, draw methods, etc.
*/
hitBody = player.getB2body(); 
...

InputProcessor:

@Override
public boolean touchDown(int i, int i1, int i2, int i3) {
        gameCam.unproject(testPoint.set(i, i1, 0));
        MouseJointDef def = new MouseJointDef();
        def.bodyA = groundBody;
        def.bodyB = hitBody;
        def.collideConnected = true;
        def.target.set(testPoint.x, testPoint.y);
        def.maxForce = 1000.0f * hitBody.getMass();
        mouseJoint = (MouseJoint) world.createJoint(def);
        hitBody.setAwake(true);
}

@Override
public boolean touchUp(int i, int i1, int i2, int i3) {
    player.getB2body().setLinearVelocity(0,0);

    // if a mouse joint exists we simply destroy it
    if (mouseJoint != null) {
        world.destroyJoint(mouseJoint);
        mouseJoint = null;
    }
    return false;
}

@Override
public boolean touchDragged(int i, int i1, int i2) {
    // if a mouse joint exists we simply update
    // the target of the joint based on the new
    // mouse coordinates
    if (mouseJoint != null) {
        gameCam.unproject(testPoint.set(i, i1, 0));
        mouseJoint.setTarget(target.set(testPoint.x, testPoint.y));
        evaluateMovementDirection();
    }
    return false;
}

private void evaluateMovementDirection() {
    float vy = player.getB2body().getLinearVelocity().y;
    float vx = player.getB2body().getLinearVelocity().x;

    // Test to Box2D for velocity on the y-axis.
    // If Hero is going positive in y-axis he is moving forward.
    // If Hero is going negative in y-axis he is moving backwards.
    if (vy > 0.0f) {
        player.onMovingUp(); // In draw, I'll use a "moving forward" animation
    } else if (vy < 0.0f) {
        player.onMovingDown(); // In draw, I'll use a "movieng backwards" animation
    } else {
        player.onStanding(); // vy == 0 In draw, I'll use a texture showing my Hero standig.
    }
}

Проблема, с которой я столкнулся, заключается в том, что если я очень быстро двигаю пальцем, персонаж движется очень быстро. Я хотел бы, чтобы персонаж всегда двигался с постоянной скоростью.

Другой подход, который я попробовал, - использовать событие pan:

GestureListener:

@Override
public boolean pan(float x, float y, float deltaX, float deltaY) {
    /*
    * DeltaX is positive when I move my finger to the left, negative otherwise.
    * DeltaY is positive when I move my finger down, negative otherwise.
    */

    // In b2body y-axes sign is the opposite.
    deltaY = -deltaY;

    // DeltaX and deltaY are in pixels, therefore delta is in metres.
    Vector2 delta = new Vector2(deltaX / Constants.PPM, deltaY / Constants.PPM);

    // Deltas too small are discarded
    if (delta.len() > Constants.HERO_SENSIBILITY_METERS) {
        /*
        * origin.x = player.getB2body().getPosition().x
        * origin.y = player.getB2body().getPosition().y
        *
        * destination.x = origin.x + delta.x
        * destination.y = origin.y + delta.y
        *
        * To go from origin to destination we must subtract their position vectors: destination - origin.
        * Thus destination - origin is (delta.x, delta.y).
        */
        Vector2 newVelocity = new Vector2(delta.x, delta.y);

        // Get the direction of the previous vector (normalization)
        newVelocity.nor();

        // Apply constant velocity on that direction
        newVelocity.x = newVelocity.x * Constants.HERO_LINEAR_VELOCITY;
        newVelocity.y = newVelocity.y * Constants.HERO_LINEAR_VELOCITY;

        // To avoid shaking, we only consider the newVelocity if its direction is slightly different from the direction of the actual velocity.
        // In order to determine the difference in both directions (actual and new) we calculate their angle.
        if (Math.abs(player.getB2body().getLinearVelocity().angle() - newVelocity.angle()) > Constants.HERO_ANGLE_SENSIBILITY_DEGREES) {
            // Apply the new velocity
            player.getB2body().setLinearVelocity(newVelocity);
            evaluateMovementDirection();
        }
    } else {
        // Stop
        player.getB2body().setLinearVelocity(0, 0);
        evaluateMovementDirection();
    }
    return true;
}

Проблема с этим заключается в том, что движение очень нестабильное и "грязное". Персонаж дрожит.

Я попробовал этот подход (спасибо @hexafraction). Используя этот код, мой персонаж перемещается по экрану более плавно. Это не идеально, но это что-то...

@Override
public boolean pan(float x, float y, float deltaX, float deltaY) {
    /*
    * DeltaX is positive when I move my finger to the left, negative otherwise.
    * DeltaY is positive when I move my finger down, negative otherwise.
    * Both are in pixels, thus to get meters I must divide by Constants.PPM.
    */

    // In b2body y-axes sign is the opposite.
    deltaY = -deltaY;

    /*
    * origin.x = player.getB2body().getPosition().x
    * origin.y = player.getB2body().getPosition().y
    *
    * destination.x = origin.x + deltaX / Constants.PPM
    * destination.y = origin.y + deltaY / Constants.PPM
    *
    * To go from origin to destination we must subtract their position vectors: destination - origin.
    * Thus, destination - origin is (deltaX / Constants.PPM, deltaY / Constants.PPM).
    */
    candidateVelocity.x = deltaX / Constants.PPM;
    candidateVelocity.y = deltaY / Constants.PPM;

    // Get the direction of the previous vector (normalization)
    candidateVelocity.nor();

    // Apply constant velocity on that direction
    candidateVelocity.x = candidateVelocity.x * Constants.HERO_LINEAR_VELOCITY;
    candidateVelocity.y = candidateVelocity.y * Constants.HERO_LINEAR_VELOCITY;

    // Linear interpolation to avoid character shaking
    heroVelocity.lerp(candidateVelocity, Constants.HERO_ALPHA_LERP);

    // Apply the result
    player.getB2body().setLinearVelocity(heroVelocity);

    // Depending on the result, we change the animation if needed
    evaluateMovementDirection();
}
return true;
}

Мне нужно предложение о том, как решить эту проблему. Я имею в виду, перемещайте персонаж box2d пальцем по экрану в ПОСТОЯННОЙ СКОРОСТИ.

Большое спасибо.

1 ответ

Рассчитайте направление, в котором вы хотите двигаться:

dragPos.sub(currentPos);

Нормализуйте его и умножьте с постоянной скоростью:

dragPos.sub(currentPos).nor().scl(CONSTANT_SPEED);
Другие вопросы по тегам