Мне нужна помощь со следующей проблемой, так как я потратил много дней, не найдя приемлемого решения.
Я делаю игру для Android (используя libgdx), где главный герой (по имени Герой) виден сверху (игра с видом сверху) и ходит по полю. Пользователь перемещает персонажа, проводя пальцем по экрану. Палец не обязательно должен быть на персонаже.
Персонаж использует две анимации, одну анимацию, когда он движется вперед (то есть когда его «у» больше нуля, так как пользователь смотрит на игру с «неба»), и другую анимацию, когда он движется назад (то есть когда его «y» меньше нуля, помните, я разрабатываю игру с видом сверху).
Наконец, мне нужно, чтобы персонаж всегда двигался с ПОСТОЯННОЙ скоростью.
Короче говоря, я хотел бы управлять персонажем пальцем и перемещать его в направлении, которое отмечает мой палец, всегда с ПОСТОЯННОЙ скоростью.
Это было бы очень просто, если бы я мог задавать положение персонажа каждое дельта-время, но я использую box2d, который знает только о linearVelocity, импульсах, силах и т. д.
Я пробовал использовать 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();
...
Входной процессор:
@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.
}
}
Проблема, с которой я сталкиваюсь, заключается в том, что если я двигаю пальцем очень быстро, персонаж движется очень быстро. Я хотел бы, чтобы персонаж всегда двигался НА ПОСТОЯННОЙ СКОРОСТИ.
Другой подход, который я пробовал, - использовать событие панорамирования:
Слушатель жестов:
@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 пальцем по экрану с ПОСТОЯННОЙ СКОРОСТЬЮ.
Большое Вам спасибо.
Vector2 newVelocity = new Vector2(delta.x, delta.y); newVelocity.nor(); newVelocity.x = newVelocity.x * Constants.HERO_LINEAR_VELOCITY; newVelocity.y = newVelocity.y * Constants.HERO_LINEAR_VELOCITY;
Мне кажется, что проблема связана с большим количеством значений, которые я получаю в событии. Мне нужна какая-то фильтрация нижних частот, как говорит гексафракция ранее. 27.12.2017