Перевел физику грейфера из Обработки в Unity, чтобы получить разные результаты - PullRequest
0 голосов
/ 10 сентября 2018

tl; dr Перемещение моей игры из Обработки в Unity.Код, отвечающий за захват вручную путем изменения скорости игрока, не работает, даже если он в основном копируется / вставляется.


Привет, я работал над моим проектом над обработкой летом, иНа прошлой неделе я решил перевести его на Unity.

С чем я столкнулся, так это с физикой грейфера / веревки.Предполагается, что он, по сути, удерживает игрока внутри круга (определяется конечной точкой веревки и длиной веревки).Когда игрок выходит за пределы этого круга, позиция игрока перемещается обратно к краю круга, и скорость игрока устанавливается на касательную к кругу.

Уменьшение длины веревки при качании должноускорить вас.(См. С плавающей точкой )

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

(я также сделал несколько изображений для описания движения, которое производят обе версии)

Обработка

Код

(предупреждение: плохое и избыточное)

Обновление физики:

exists = (endPoint != null);
if(lgth<=0) lgth = 1;
if(exists) {
  currentLength = phs.position.dist(endPoint);
  if(currentLength > lgth) {
    float angle = getAngle(endPoint, phs.position);
    phs.addPosition(abs(currentLength - lgth), angle);
    float angleBetween = getAngle(phs.position, endPoint);
    PVector relativeVelocity = new PVector(phs.velocity.x + phs.position.x, phs.velocity.y + phs.position.y);
    float displacement = angleBetween - 90;

    Line l1 = lineFromTwoPoints(relativeVelocity, endPoint);
    Line l2 = lineFromAngle(phs.position, displacement);
    PVector pointToLerpTo = intersection(l1, l2);
    if(pointToLerpTo!=null) {
      phs.velocity.x = pointToLerpTo.x-phs.position.x;
      phs.velocity.y = pointToLerpTo.y-phs.position.y;
    }
    else phs.velocity.mult(0);
  }
}

, когда игрок укорачивает веревку, скорость увеличивается:

if(exists) {
  float newLgth = lgth-d;
  float distance = getDistance(phs.position, endPoint);
  if(distance > newLgth) {
    float ratio = (distance-newLgth)/lgth;
    phs.velocity.setMag(phs.velocity.mag()*(1+ratio));
  }
  lgth = newLgth;
}

Движение от обработки (хорошо)

Игрок начинает движение вниз с левого края веревочного круга.Не теряет скорость и продолжает вращаться несколько раз, пока гравитация не замедлит его.

Unity

Код

оба кодовых блока сверху обрабатываются в одном и том жеместо здесь, под FixedUpdate() (проблемная часть, похоже, скоростной участок)

distance = Vector2.Distance(transform.position, endpoint);
if(connected && distance > length) {
    //lerp position -> endpoint// keep gameObject within length of the rope
    float posLerpAmount = (distance - length) / distance;
    transform.position = Vector2.Lerp(transform.position, endpoint, posLerpAmount);

    //'lerp' velocity -> endpoint// keep the velocity locked to the tangent of the circle around the endpoint
    Vector2 relativeVelocity = GetComponent<Rigidbody2D>().velocity + (Vector2)transform.position;
    Line l1 = Geometry.LineFromTwoPoints(relativeVelocity, endpoint);
    Line l2 = Geometry.LineFromAngle(transform.position, Geometry.GetAngle(endpoint, transform.position) - 90);
    if(!Geometry.AreParallel(l1, l2)) {
        Vector2 pointToLerpTo = Geometry.Intersection(l1, l2) - (Vector2)transform.position;
        GetComponent<Rigidbody2D>().velocity = pointToLerpTo;
    }
    else GetComponent<Rigidbody2D>().velocity = new Vector2(0, 0);

    //increases the magnitude of the velocity based on how far the rope moved the object's position
    float ratio = (distance - length) / length;
    GetComponent<Rigidbody2D>().velocity *= 1 + ratio;

    distance = length;
}

Движение от Единства (плохо)

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


Если кому-то нужно, чтобы я объяснил класс Geometry (линии, пересечения), тогда я могу, но я думаю, что в основном это говорит само за себя.В противном случае, я думаю, что объяснил это как мог.Заранее благодарен за любую помощь.

(кроме того, StackOverflow не позволяет мне добавить тег Unity2d, так что, думаю, я должен согласиться на Unity3d)

1 Ответ

0 голосов
/ 10 сентября 2018

Я обнаружил, что Rigidbody2D.velocity.magnitude равен , а не как далеко объект перемещается при каждом обновлении физики.Именно это и стало причиной проблемы, потому что код Обработки основывался на скорости, добавляемой непосредственно к позиции при каждом обновлении.

Чтобы исправить это, я сделал ту же геометрию, но масштабировал скорость до% от того, сколько скорости было фактически «использовано» (обычно оно перемещается на 2% от фактического вектора скорости).

Вот окончательный код в Unity: (на этот раз я показываю заливку FixedUpdate(), с удалением ненужных частей)

float lastMagnitude;
Vector2 lastPosition;
void FixedUpdate() {
    float velocityMoved = Vector2.Distance(lastPosition, transform.position) / lastMagnitude;
    Debug.Log(velocityMoved * 100 + "%"); //this is usually 2%

    bool shortenedRope = false;
    if(Input.GetButton("Shorten Rope")) {
        shortenedRope = true;
        length -= ropeShortenLength;
    }

    distance = Vector2.Distance(transform.position, endpoint);
    if(connected && distance > length) {
        //lerp position -> endpoint// keep gameObject within length of the rope
        float posLerpAmount = (distance - length) / distance;
        transform.position = Vector2.Lerp(transform.position, endpoint, posLerpAmount);

        //'lerp' velocity -> endpoint// keep the velocity locked to the tangent of the circle around the endpoint
        Vector2 adjustedVelocity = rigidbody.velocity * velocityMoved;
        Vector2 relativeVelocity = adjustedVelocity + (Vector2)transform.position;
        Line l1 = Geometry.LineFromTwoPoints(relativeVelocity, endpoint);
        Line l2 = Geometry.LineFromAngle(transform.position, Geometry.GetAngle(endpoint, transform.position) - 90);
        if(!Geometry.AreParallel(l1, l2)) {
            Vector2 pointToLerpTo = Geometry.Intersection(l1, l2) - (Vector2)transform.position;
            rigidbody.velocity = pointToLerpTo;
            rigidbody.velocity /= velocityMoved;
        }
        else rigidbody.velocity = new Vector2(0, 0);

        //'give back' the energy it lost from moving it's position
        if(shortenedRope) {
            float ratio = (distance - length) / length;
            rigidbody.velocity *= 1 + ratio;
        }

        distance = length;
    }
    lastPosition = transform.position;
    lastMagnitude = rigidbody.velocity.magnitude;
}

РЕДАКТИРОВАТЬ: Недавно выяснилось, что лучше использовать Time.deltaFixedTime вместо переменной, которую я сделал SpeedMoved, поскольку Time.deltaFixedTime являетсяуже рассчитано.

...