如何使 LibGDX 动作 moveTo() 以曲线从一个点到另一个点进行动画处理?

问题描述 投票:0回答:2

我正在 LibGDX 中开发一个项目,我正在为我的一些精灵使用 Scene2D 演员。在这方面,我有一个精灵,它在屏幕上的某个位置生成,需要移动到屏幕上的另一个位置。为此,我在操作中使用

moveTo(xPos, yPos, duration, interpolation)
方法来制作移动动画。

但是,当我使用这种方法时,演员按照我的指示移动,但它只沿直线移动,从 A 点到 B 点。我尝试了几种插值选项,例如圆插值等,但似乎只是影响动画线的速度。

现在我的问题是:如何让我的动画从 A 到 B 形成一条平滑的曲线(见图)?

我目前正在使用此代码来制作动作动画:

adultCustomerPointActor.addAction(Actions.sequence(
    Actions.moveTo(300, 200, 2f, Interpolation.circle)
));

预先感谢您的帮助:)

java animation libgdx interpolation scene2d
2个回答
2
投票

这是一道几何问题。使用向量,找到两点之间的中间点:

vec1.set(bx, by).sub(ax, ay).scl(0.5f).add(ax, ay);

从点之间的向量获取另一个 90 或 270 的向量:

vec2.set(bx, by).sub(ax, ay).rotate90().add(vec1);

可以缩放此

vec2
以调整弧线的极限曲率。如果你不管它,你就会得到四分之一圆。您还可以将其缩放为负值以反转曲率。

然后将第二个向量添加到第一个向量以找到圆弧的中心点,我们可以将其称为点 C。

vec1.set(bx, by).sub(vec2); // CB
vec3.set(ax, ay).sub(vec2); // CA
float angle = vec1.angle(vec3);

现在你需要一个从 C 点指向 A 点的向量。你将旋转这个向量直到它到达 B 点。所以你需要 CA 和 CB 之间的角度。

这里有一个非常简单的类来实现这一点。它还没有考虑到你是否想要弧线向上或向下以及是否想要缩放它看起来有多极端。您可以使用 getter/setter 将它们添加为附加参数。我还没有测试过,所以可能需要一些调试。

public class ArcToAction extends MoveToAction {
    private float angle;
    private final Vector2 vec1 = new Vector2(), vec2 = new Vector2(), vec3 = new Vector2();

    @Override
    protected void begin () {
        super.begin();
        float ax = target.getX(getAlignment()); // have to recalculate these because private in parent
        float ay = target.getY(getAlignment());
        vec1.set(getX(), getY()).sub(ax, ay);
        vec2.set(vec1).rotate90();
        vec1.scl(0.5f).add(ax, ay);
        vec2.add(vec1);
        vec1.set(bx, by).sub(vec2); // CB
        vec3.set(ax, ay).sub(vec2); // CA
        angle = vec1.angle(vec3);
    }

    protected void update (float percent) {
        if (percent >= 1){
            target.setPosition(getX(), getY(), getAlignment());
            return;
        }

        vec1.set(vec3).rotate(percent * angle);
        target.setPosition(vec1.x, vec1.y, getAlignment());
    }

}

如果你想支持自动池化,可以添加这样的方法:

static public ArcToAction arcTo (float x, float y, float duration, Interpolation interpolation) {
    ArcToAction action = Actions.action(ArcToAction .class);
    action.setPosition(x, y);
    action.setDuration(duration);
    action.setInterpolation(interpolation);
    return action;
}

0
投票

虽然这是一个老问题,但我在寻找答案时遇到了它,并决定实现以下类以启用从 A 点到 B 点的电弧。请随意在您自己的项目中使用它。

import com.badlogic.gdx.math.Interpolation;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.scenes.scene2d.actions.Actions;
import com.badlogic.gdx.scenes.scene2d.actions.MoveToAction;

/**
 * This class extends MoveToAction to move an actor along an arc.
 */
public class ArcMovetoAction extends MoveToAction {
  private float height;
  private float length;
  private float angle;
  private float a;
  private float b;
  private final Vector2 position = new Vector2();

  /**
   * Sets the height of the arc.
   *
   * @param height the height of the arc
   */
  private void setHeight(float height) {
    this.height = height;
  }

  /**
   * Called when the action starts.
   */
  @Override
  protected void begin() {
    super.begin();

    float startX = getStartX();
    float startY = getStartY();
    float endX = getX();
    float endY = getY();

    length = (float) Math.hypot(endX - startX, endY - startY);

    a = (float) ((startY - height) / Math.pow(startX - length / 2, 2));
    b = -2 * a * length / 2;

    Vector2 start = new Vector2(startX, startY);
    Vector2 end = new Vector2(endX, endY);

    if (endX < startX) {
      end.x = -end.x;
    }

    angle = (float) Math.toDegrees(Math.atan2(end.y - start.y, end.x - start.x));
  }

  /**
   * Updates the action based on the percent done.
   *
   * @param percent the percent done
   */
  @Override
  protected void update(float percent) {
    if (percent == 0 || percent >= 1 || height == 0) {
      super.update(percent);
      return;
    }

    float x = percent * length;
    float y = (float) (a * Math.pow(x, 2) + b * x);

    position.set(x, y).add(getStartX(), getStartY()).rotateDeg(angle);

    if (getX() < getStartX()) {
      position.x = -position.x;
    }

    target.setPosition(position.x, position.y, getAlignment());
  }

  /**
   * Creates a new ArcMovetoAction.
   *
   * @param x             the x-coordinate to move to
   * @param y             the y-coordinate to move to
   * @param height        the height of the arc
   * @param duration      the duration of the action
   * @param interpolation the interpolation to use
   * @return the created ArcMovetoAction
   */
  public static ArcMovetoAction arcTo(float x, float y, float height, float duration, Interpolation interpolation) {
    ArcMovetoAction action = Actions.action(ArcMovetoAction.class);
    action.setPosition(x, y);
    action.setDuration(duration);
    action.setInterpolation(interpolation);
    action.setHeight(height);
    return action;
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.