【问题标题】:Move 2D object at constant speed and turn towards touch point匀速移动二维物体并转向接触点
【发布时间】:2019-09-11 14:23:28
【问题描述】:

我一直在尝试让 2D 玩家像子弹一样一直向前移动(在这种情况下,向前是游戏对象的局部 X 轴,因为这就是角色的方式面对)并且仅在您触摸屏幕上的某个点时改变方向,在这种情况下,它应该会平稳地开始转向该点。

我遇到的一个问题是我无法让角色在它之前面对的最后一个方向上以恒定的速度平稳移动,而我发现的另一个问题是角色正在转身错误的轴,而不是基于 Z 轴旋转,而是始终在 Y 轴上旋转,这使得精灵对相机变得不可见。

这是我现在拥有的代码:

Vector3 lastTouchPoint;
private void Start()
{
    lastTouchPoint = transform.position;
}

void Update()
{
    if (Input.touchCount > 0)
    {
        // The screen has been touched so store the touch
        Touch touch = Input.GetTouch(0);

        if (touch.phase == TouchPhase.Stationary || touch.phase == TouchPhase.Moved)
        {
            // If the finger is on the screen, move the object smoothly to the touch position
            lastTouchPoint = Camera.main.ScreenToWorldPoint(new Vector3(touch.position.x, touch.position.y, 10));

        }
    }
    transform.position = Vector3.Lerp(transform.position, lastTouchPoint, Time.deltaTime);
    //Rotate towards point
    Vector3 targetDir = lastTouchPoint - transform.position;
    transform.LookAt(lastTouchPoint);

}

提前致谢!

【问题讨论】:

  • 问题是您不希望精灵实际上以 3d 方式移动,这就是您对 lerp 等所做的。您只希望它围绕指向相机的 z 轴旋转。您可能希望它的位置移动,而不是恶意指向的位置。
  • 我将如何实现这一目标? @BugFinder

标签: c# unity3d


【解决方案1】:

让角色以恒定的速度平稳移动

您可能不明白 Lerp 到底是什么:这在给定因子的两个位置之间进行插值,其中 0 完全是第一个位置,1 完全是最后一个位置,例如0.5f 表示在两个位置之间的中心。

如果两个位置之间的距离越远,速度就会越快,并且两个位置之间的距离越小,速度就会越来越慢。在某些情况下,尤其是在像您的情况这样小的因素的情况下,对象甚至可能永远不会真正到达目标位置。

将其与Time.deltaTime 的动态因子一起使用是没有意义的,因为该值每帧都会发生变化,并且在0,017 附近的某处抖动(假设为 60 FPS)。

你宁愿使用Vector3.MoveTowards 以固定的恒定速度

// set via the Inspector
public float speedInUnitsPerSecond = 1;

...

transform.position = Vector3.MoveTowards(transform.position, lastTouchPoint, Time.deltaTime * speedInUnitsPerSecond);

如果您想继续移动在到达触摸位置后停止。

如果您宁愿继续朝着相应的方向移动,那么您可以存储方向而不是位置并使用直截了当的Transform.Translate

// set via the Inspector
public float speedInUnitsPerSecond = 1;
private Vector2 lastDirection;

privtae void Update()
{
    ...

    // If the finger is on the screen, move the object smoothly to the touch position
    var touchPosition = Camera.main.ScreenToWorldPoint(new Vector3(touch.position.x, touch.position.y, 10));
    lastDirection = (touchPosition - transform.position).normalized;

    ...

    // move with constant speed in the last direction
    transform.Translate(lastDirection * Time.deltaTime * speedInUnitsPerSecond);
    ...
}

角色围绕错误的轴转动,而不是基于 Z 轴旋转,而是始终在 Y 轴上旋转

注意Transform.LookAt 有一个optional 第二个参数worldUp,默认是Vector3.up,所以围绕全局Y 轴旋转!

由于您希望围绕Z 轴旋转,您应该通过

transform.LookAt(lastTouchPoint, Vector3.forward);

我当然不知道你的设置,但也请注意

LookAt

旋转变换,使 forward 向量指向worldPosition

正如您所描述的,您也可能不希望对象前向向量指向目标位置,而是实际上是对象右 (X) 向量!

您可以通过直接设置transform.right 来做到这一点,例如

transform.right = (lastTouchPoint - transform.position).normalized;

transform.right = lastDirection;

顺便说一句,实际上只设置一次旋转就足够了,即它改变的那一刻

if (touch.phase == TouchPhase.Stationary || touch.phase == TouchPhase.Moved)
{
    // If the finger is on the screen, move the object smoothly to the touch position
    lastTouchPoint = Camera.main.ScreenToWorldPoint(new Vector3(touch.position.x, touch.position.y, 10));

    transform.right = (lastTouchPoint - transform.position).normalized;
}

if (touch.phase == TouchPhase.Stationary || touch.phase == TouchPhase.Moved)
{
    // If the finger is on the screen, move the object smoothly to the touch position
    var touchPosition = Camera.main.ScreenToWorldPoint(new Vector3(touch.position.x, touch.position.y, 10));
    lastDirection = (touchPosition - transform.position).normalized;


    transform.right = lastDirection;
}

【讨论】:

  • 我确实明白 Lerp 是什么。那是试图让运动平稳,但它没有达到我想要的效果。最后我自己修好了
【解决方案2】:

我最终找到了我自己的问题的答案,使用代码从另一个帖子中顺利旋转。代码如下:

    Vector3 lastTouchPoint;
    Vector3 direction;
    Vector3 vectorToTarget;

    //Character controller variables
    public float moveSpeed = 5f;
    public float angularSpeed = 3f;

    private void Start()
    {
        lastTouchPoint = transform.position;
    }

    void Update()
    {
        if (Input.touchCount > 0)
        {
            // The screen has been touched so store the touch
            Touch touch = Input.GetTouch(0);

            if (touch.phase == TouchPhase.Began)
            {
                // If the finger is on the screen, move the object smoothly to the touch position
                lastTouchPoint = Camera.main.ScreenToWorldPoint(new Vector3(touch.position.x, touch.position.y, 10));
                direction = lastTouchPoint - transform.position;
                vectorToTarget = lastTouchPoint - transform.position;
            }
        }
        transform.position += direction.normalized * moveSpeed * Time.deltaTime;

        float angle = Mathf.Atan2(vectorToTarget.y, vectorToTarget.x) * Mathf.Rad2Deg;
        Quaternion q = Quaternion.AngleAxis(angle, Vector3.forward);
        transform.rotation = Quaternion.Slerp(transform.rotation, q, Time.deltaTime * angularSpeed);
    }

【讨论】:

  • 另一个帖子是什么?
  • Quaternion.Slerp(transform.rotation, q, Time.deltaTime * angularSpeed); 再次...不要使用Time.deltaTime 作为插值因子.. 这个因子应该保持不变
  • Time.deltaTime 是自上一帧以来的时间。如果你想以每单位时间 x 的速率做某事,那么即使它不是恒定的,它仍然是一种方法
  • 是的,我知道...但是您不希望您的插值 factor 在帧之间发生变化...您想要一个常数因子,如 0.5 或 @987654326 @ ... 将其乘以 Time.deltaTime 是没有意义的,您应该使用 Quaternion.RotateTowards 进行匀速旋转
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多