Nano Hash - криптовалюты, майнинг, программирование

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

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

Я делаю игру для 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 пальцем по экрану с ПОСТОЯННОЙ СКОРОСТЬЮ.

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


  • Можете ли вы проверить, достаточно ли чисты необработанные значения сенсорного экрана для устойчивого движения? Если нет, рассмотрите возможность выполнения операции фильтрации нижних частот. 27.12.2017
  • Спасибо за совет. Я узнал, что в libgdx есть функция Lerp в Vector2, которая оказалась полезной. Насколько мне известно, Lerp (линейная интерполяция) — это не то же самое, что фильтр нижних частот, но я думаю, что он работает (по крайней мере, в большинстве случаев). Мой персонаж кажется мне более устойчивым при перемещении по экрану. Не могли бы вы проверить мой новый подход в вопросе? У вас есть другое предложение? Я очень ценю это. Спасибо. 27.12.2017
  • Я не уверен, что lerp делает именно то, что вы хотите, поскольку он интерполирует, а не фильтрует. Вполне вероятно, что это может сработать, но это не метод сглаживания, с которым я когда-либо сталкивался в прошлом. Вы можете сделать простой фильтр первого порядка, всегда принимая движение равным (0,7 * input + 0,3 * lastFrameMovement) или около того, но это все равно будет неточным, если вы не задействуете deltaTime в самом спаде. 27.12.2017
  • Это код из библиотеки libgdx: public Vector2 lerp(Vector2 target, float alpha) { float invAlpha = 1.0F - alpha; this.x = this.x * invAlpha + target.x * alpha; this.y = this.y * invAlpha + target.y * alpha; return this; } Я думаю, это именно то, что вы предложили. Во всяком случае, это намного лучше, чем раньше, но не так сильно, как хотелось бы. Большое спасибо за вашу идею, я собираюсь использовать ее в своем коде, но я продолжу исследовать, могу ли я добавить что-то еще. 28.12.2017

Ответы:


1

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

dragPos.sub(currentPos);

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

dragPos.sub(currentPos).nor().scl(CONSTANT_SPEED);
27.12.2017
  • Да, именно это я и сделал во втором подходе: 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
  • Это выглядит слишком сложным для того, что вы хотите. Мой подход будет двигаться с постоянной скоростью. Если вы используете дельты, убедитесь, что вы также учитываете дельта-время, возможно, поэтому оно дрожит для вас. 28.12.2017
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

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

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

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

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..