【问题标题】:In Unity Calculate Points a Given Distance Perpendicular to a Line at Angles on a Circle在 Unity 中计算点在圆上以一定角度垂直于直线的给定距离
【发布时间】:2020-08-07 20:09:44
【问题描述】:

我制作了一个模型来说明我要计算的内容。给定两个锚点之间的一条线(向量),我想将一个或多个游戏对象放置在与该向量的中点相切的一定距离处,并沿垂直于向量的圆以指定的角度(弧度?)放置。

在这个插图中,一个假想的圆放置在中点并垂直于 Anchor 1 和 Anchor 2 之间的线。我想计算三个点(P1、P2 和 P3)的 Vector3 位置。目的是在每个点放置对象。整个组件将是一个可以在空间中旋转的游戏对象。 (将有一个游戏对象,每个对象都有一个子对象。)

我搜索了 StackOverflow 和 Unity 社区,但找不到可以帮助我做出这三个展示位置的示例。

有什么想法吗?

【问题讨论】:

  • 输入的具体内容是什么? Vector3 Anchor1;Vector3 Anchor2; 和半径 float r;?
  • 我会有这些输入。我已经尝试过RotateAround(Vector3 point, Vector3 axis, float angle) 以及其他尝试。困难在于将这些点放置在半径为r 且与顶部中心偏移n 度的假想圆上。
  • 我的三角函数不足。
  • 我可以将单个对象放置在距中点指定距离处。它在垂直于该锚线的假想平面中围绕同一点旋转/环绕其他物体。
  • 所以,基本上:你想在一个圆上的等距点实例化 N 个游戏对象,然后变换圆,使其以线段的中点为中心并旋转,使其平面垂直于线段?如果您已经可以对一个对象执行此操作(如您所述),则对子层次结构的根执行此操作是一回事。

标签: c# unity3d


【解决方案1】:

考虑向量数学和四元数,而不是三角。使用叉积找到 0 角度偏移和四元数根据角度旋转它。解释见 cmets。

public void PlaceObjects(Transform anchor1, Transform anchor2, float r, 
        List<Transform> objs, List<float> angles)
{
    // lists must be non-null and same size
    Debug.Assert(objs != null);
    Debug.Assert(angles != null);
    Debug.Assert(objs.Count == angles.Count);
 
    // Find midpoint and axis of rotation   
    Vector3 midpoint = 0.5f * (anchor1.position + anchor2.position);
    Vector3 axis = (anchor2.position - anchor1.position).normalized;

    // What direction should the the "zero" offset be based on? 
    // Base it on the local up of the "assembly parent" this script is attached to?
    // or Vector3.up if "0 angle" should approximate world up?
    Vector3 upDirection = transform.up;

    // Of directions perpendicular to the axis find the closest to upDirection
    // See https://stackoverflow.com/a/57698547/1092820 for more information
    Vector3 axisRight = Vector3.Cross(upDirection, axis);

    if (axisRight == Vector3.zero) 
    {
        // upDirection & axis are colinear, no unique "up-ish" exists.
        // Just give up and don't move anything.
        return;
    }

    Vector3 zeroOffsetDir = Vector3.Cross(axis, axisRight);

    for (int i = 0 ; i < objs.Count ; i++)
    {
        Transform obj = objs[i];
        float angle = angles[i];
 
        // Find a rotation that describes how to rotate a "0 angle" offset into the one 
        // the current object needs
        Quaternion rot = Quaternion.AngleAxis(angle, axis); 
     
        // Find the offset by rotating the "zero" offset, then extending it by the radius
        Vector3 offset = r * (rot * zeroOffsetDir);

        // Set the object's position based on its offset and the location of the midpoint
        obj.position = midpoint + offset;

        // Optionally, set the object's rotation based on its current forward and the new up:
        // obj.rotation = Quaternion.LookRotation(obj.forward, offset);
    }
}

【讨论】:

  • 太棒了!正是我需要的。我用 Vector3 函数的各种组合试验了三个星期,但没有得到我想要的,所以我没有任何代码示例可以分享,而且很难描述它(这可能是为什么有人投反对票的原因我的帖子没有表现出努力或研究)。但是因为很难描述,所以我在 SketchUp 中进行了建模,并希望图片能够传达目标。我很高兴你能理解我的模型。
  • 我必须进行一项更改才能使您的解决方案起作用,那就是注释掉检查是否axisRight == Vector3.zero。在运行时,这总是返回 true,并且执行没有进一步进行。
  • 如果锚点没有移动,我还添加了几个缓存变量以跳过 Update() 调用。
【解决方案2】:

对于那些感兴趣的人,这是我对@Ruzihm 解决方案的组件改编。 PlaceObjectsOnCirclePerpendicularToVectorLine 组件被放置在一个空的游戏对象上。

using System.Collections.Generic;
using UnityEngine;

public class PlaceObjectsOnCirclePerpendicularToVectorLine : MonoBehaviour
{
    // Adapted from https://stackoverflow.com/a/63308834/13052746, by Ruzihm.

    public Transform anchor1;
    public Transform anchor2;
    public float radius;
    public List<Transform> objs;
    public List<float> angles;

    private Vector3 anchor1PriorPosition;
    private Vector3 anchor2PriorPosition;

    // Start is called before the first frame update
    void Start()
    {
        PlaceObjects(
            anchor1,
            anchor2,
            radius,
            objs,
            angles);
    }

    // Update is called once per frame
    void Update()
    {
        PlaceObjects(
            anchor1,
            anchor2,
            radius,
            objs,
            angles);
    }

    public void PlaceObjects(
        Transform anchor1, 
        Transform anchor2, 
        float radius,
        List<Transform> objs, 
        List<float> angles)
    {

        if (anchor1PriorPosition == anchor1.position &&
            anchor2PriorPosition == anchor2.position)
        {
            // The anchors haven't moved.
            return;
        } else
        {
            anchor1PriorPosition = anchor1.position;
            anchor2PriorPosition = anchor2.position;
        }

        // lists must be non-null and same size
        Debug.Assert(objs != null);
        Debug.Assert(angles != null);
        Debug.Assert(objs.Count == angles.Count);

        // Find midpoint and axis of rotation   
        Vector3 midpoint = 0.5f * (anchor1.position + anchor2.position);
        Vector3 axis = (anchor2.position - anchor1.position).normalized;

        // What direction should the the "zero" offset be based on? 
        // Base it on the local up of the "assembly parent" this script is attached to?
        // or Vector3.up if "0 angle" should approximate world up?
        Vector3 upDirection = transform.up;

        // Of directions perpendicular to the axis find the closest to upDirection
        // See https://stackoverflow.com/a/57698547/1092820 for more information
        Vector3 axisRight = Vector3.Cross(upDirection, axis);

        //if (axisRight == Vector3.zero)
        //{
        //    // upDirection & axis are colinear, no unique "up-ish" exists.
        //    // Just give up and don't move anything.
        //    return;
        //}

        Vector3 zeroOffsetDir = Vector3.Cross(axis, axisRight);

        for (int i = 0; i < objs.Count; i++)
        {
            Transform obj = objs[i];
            float angle = angles[i];

            // Find a rotation that describes how to rotate a "0 angle" offset into the one 
            // the current object needs
            Quaternion rot = Quaternion.AngleAxis(angle, axis);

            // Find the offset by rotating the "zero" offset, then extending it by the radius
            Vector3 offset = radius * (rot * zeroOffsetDir);

            // Set the object's position based on its offset and the location of the midpoint
            obj.position = midpoint + offset;
        }
    }
}

这是场景视图中的样子:

下面是组件在检查器中的样子:

再次,精彩,@Ruzihm!正是我想要的!

【讨论】:

  • 谢谢!仅供参考,因为您将if (axisRight == Vector3.zero) 签出,如果轴和upDirection 共线,那么所有内容都将直接放置在轴上。根据 anchor1anchor2transform.up 相比如何保证定位,这可能是不可能的,在这种情况下这不是问题。无论如何,只是让你知道;)
猜你喜欢
  • 1970-01-01
  • 2020-12-24
  • 2011-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多