【问题标题】:Generating Dynamic Rotation产生动态旋转
【发布时间】:2015-12-02 17:49:14
【问题描述】:

我目前有一个脚本可以使游戏对象相对于另一个游戏对象移动,如下面的 .gif 所示。

我还没有弄清楚如何使同一个游戏对象相对于另一个游戏对象的位置旋转,使其始终以原始旋转结束,如下图:

那么,既然我有了“动态运动”(如 .gif 文件),我该如何创建“动态旋转”(如图所示)?

我没有完成此类任务的经验,并且搜索 Web 也无济于事,因此我非常感谢准备好部署的脚本。谢谢你的回答。

【问题讨论】:

  • 你想旋转多少次?只有 1 个(360 度)或 2 个游览(720 度)还是取决于距离?
  • 我现在想旋转 360 度,但我希望以后可以轻松调整该选项。

标签: c# unity3d rotation unity5


【解决方案1】:

如果我理解正确,您只是想确保物体在飞行中在空中翻转给定的时间。你可以使用这样的东西:

using UnityEngine;
using System.Collections;

public class RotaionManager : MonoBehaviour {

    // Motion start point
    public Transform origin;
    // Motion end point
    public Transform destination;

    // Start and en anle - 360 for single full rotation
    public float startAngle = 0;
    public float endAngle = 360;

    void Update () {

        // Distance to origin
        var dstOrigin = Vector3.Distance(origin.position, transform.position);
        // Distance to destination
        var dstDestination = Vector3.Distance(destination.position, transform.position);
        // Parameter
        float t = (dstDestination + dstOrigin == 0 ? 0 : (dstOrigin / (dstDestination + dstOrigin) ) );
        // The angle at current t
        float angle = Mathf.Lerp(startAngle, endAngle, t);

        // Rotate the object by the angle, then make sure it also faces destination
        transform.rotation = Quaternion.LookRotation(destination.position - origin.position) * Quaternion.Euler(angle, 0, 0);

    }
}

旋转角度的简单线性插值,基于到起点和终点的距离。您可以控制对象按开始和结束角度旋转的次数。观察旋转是为了除了绕自己的轴旋转外,对象还将面向目标的大致方向。

查看示例项目: https://www.dropbox.com/s/g00srsjlqjx1jcz/RotationProj.zip?dl=0

编辑

在源-目的地向量上使用与对象投影的距离可能会更好,而不是原始距离,它在陡峭的路径上看起来更好。更新函数将如下所示:

void Update () {

    // Projection of object on line between origin and destination
    var projection = Vector3.Project(transform.position - origin.position, origin.position - destination.position) + origin.position;
    // Distance to origin
    var dstOrigin = Vector3.Distance(origin.position, projection);
    // Distance to destination
    var dstDestination = Vector3.Distance(destination.position, projection);
    // Parameter
    float t = (dstDestination + dstOrigin == 0 ? 0 : (dstOrigin / (dstDestination + dstOrigin) ) );
    // The angle at current t
    float angle = Mathf.Lerp(startAngle, endAngle, t);

    // Rotate the object by the angle, then make sure it also faces destination
    transform.rotation = Quaternion.LookRotation(destination.position - origin.position) * Quaternion.Euler(angle, 0, 0);

}

我还更新了示例项目

编辑

另一种看起来更好的方法是忽略起点和终点的Y坐标,并进行投影。在某些极端情况下看起来要好一些。代码如下所示:

void Update () {

    Vector3 op = origin.position; op.y = 0;
    Vector3 dp = destination.position; dp.y = 0;
    // Projection of object on line between origin and destination
    var projection = Vector3.Project(transform.position - origin.position, op - dp) + op;
    // Distance to origin
    var dstOrigin = Vector3.Distance(op, projection);
    // Distance to destination
    var dstDestination = Vector3.Distance(dp, projection);
    // Parameter
    float t = (dstDestination + dstOrigin == 0 ? 0 : (dstOrigin / (dstDestination + dstOrigin) ) );
    // The angle at current t
    float angle = Mathf.Lerp(startAngle, endAngle, t);

    // Rotate the object by the angle, then make sure it also faces destination
    transform.rotation = Quaternion.LookRotation(dp - op) * Quaternion.Euler(angle, 0, 0);

}

【讨论】:

  • 优秀的答案。但是,您的脚本似乎只能在 X 和 Z 轴上正常工作,而不能在 Y 轴上正常工作。签出this
  • 看到这个后,我意识到投影点没有正确计算,当起点和终点高度不同时,这会导致旋转不完整。用正确的方法更新了答案。我还更新了示例项目,以说明起点或终点在 Y 轴上移动的情况。
  • 在最新的编辑中,Cube 在某些位置不能正确旋转。签出this
  • 整个算法基于对t 值的良好近似。该值表示对象已经经过的路径相对于总路径长度的部分。在这里,我给出了 2 种近似方法,即海湾原始距离或通过源-目的地向量上的投影。我没有看到以通用方式近似此的更好方法。更好的近似值是可能的,但为此您必须准确指定用于移动对象的算法,解决方案取决于它,因此如果您发布代码,它将能够提供帮助。
  • 再考虑一下,还有另一个很好的近似值,我更新了答案。虽然没有更新项目(保管箱现在正在维护中)。希望这会有所帮助。
【解决方案2】:

在类似情况下对我有帮助的一种可能性是transform.LookAt 函数。这使您可以始终面对目标。您可以在您的 Update 方法中添加类似 transform.LookAt(target.transform) 的内容,这样您将始终面对您的目标。到达目标后,您可能需要将其设置为一个恒定值,因为如果您与目标不在同一位置,可能会出现一些奇怪的旋转问题。

【讨论】:

  • 开放赏金,价值 +200 声望,开始准备部署脚本!
【解决方案3】:
  • 我假设你的游戏中有投掷者、石头和目标。投掷者 将石头扔到目标上,石头应该总共旋转 360 度。

    private float mag = 0 ;//will be total distance
    private Vector3 throwerPos ;
    private Boolean isThrown = false;// you have to set this true whenever you throw a rock.
    
    void Update()
    {
     .
     .
     if(isThrown)
     {
      mag = (target.position -thrower.position).magnitude;//get distance whenever you throw it.
      throwerPos = thrower.position;
      rotateIt();
     }
    } 
    
    void rotateIt()
    {
     float currMag = (rock.position - throwerPos).magnitute ;//get current distance between rock and thrower and rotate it depends on distance at the throwing time
         angle= Mathf.MoveTowards(0,360,currMag / mag);
         rock.getComponent<Transform>().eulerAngles = new Vector3(
         rock.getComponent<Transform>().eulerAngles.x,
         rock.getComponent<Transform>().eulerAngles.y,
         angle);
        if(angle == 360)
        {
          rock.getComponent<Transform>().eulerAngles.z=0;
          isThrown = false;
        }
    }
    

    我没有执行这段代码,这可能行不通。这只是一个想法。我认为有很多方法可以实现这一目标。

【讨论】:

  • 摇滚。只需使用 getcomponent... 而不是 rock.getcom.. 这是一个伪代码。你需要为你的游戏配置它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-10-15
  • 1970-01-01
  • 2020-10-31
  • 1970-01-01
  • 1970-01-01
  • 2019-10-06
  • 2020-03-11
相关资源
最近更新 更多