简体   繁体   English

在Unity中计算点一个给定的距离垂直于一条线在一个圆上的角度

[英]In Unity Calculate Points a Given Distance Perpendicular to a Line at Angles on a Circle

I crafted a model to illustrate what I am trying to calculate.我制作了一个 model 来说明我要计算的内容。 Given a line (vector) between two anchor points, I want to place one or more game objects a certain distance tangential to the midpoint of that vector and at designated angles (radians?) along a circle that is perpendicular to the vector.给定两个锚点之间的一条线(向量),我想将一个或多个游戏对象放置在与该向量的中点相切的一定距离处,并沿垂直于向量的圆以指定的角度(弧度?)放置。

In this illustration, an imaginary circle is placed at midpoint and perpendicular to the line between Anchor 1 and Anchor 2. I want to calculate the Vector3 positions of three points (P1, P2, and P3).在这个插图中,一个假想的圆放置在中点并垂直于 Anchor 1 和 Anchor 2 之间的线。我想计算三个点(P1、P2 和 P3)的 Vector3 位置。 The intent is to place objects at each of those points.目的是在每个点放置对象。 The entire assembly will a gameobject that can rotate in space.整个组件将是一个可以在空间中旋转的游戏对象。 (There will be a single game object with each object a child.) (将有一个游戏 object 每个 object 一个孩子。)

在此处输入图像描述

I have scoured StackOverflow and the Unity communities and cannot find examples that help me make those three placements.我搜索了 StackOverflow 和 Unity 社区,但找不到可以帮助我完成这三个位置的示例。

Any ideas?有任何想法吗?

Instead of trig, consider vector math and quaternions.考虑向量数学和四元数,而不是三角函数。 Use cross products to find the 0 angle offset and quaternions to rotate it according to the angle.使用叉积找到 0 角度偏移和四元数根据角度旋转它。 See comments for explanation.解释见评论。

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);
    }
}

For those interested, here's my Component adaptation of @Ruzihm's solution.对于那些感兴趣的人,这是我对@Ruzihm 解决方案的组件改编。 The PlaceObjectsOnCirclePerpendicularToVectorLine component is placed on an empty game object. PlaceObjectsOnCirclePerpendicularToVectorLine组件放置在一个空游戏 object 上。

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;
        }
    }
}

Here's what it looks like in the Scene view:这是它在场景视图中的样子: 三个对象以等距角放置在垂直于一条线的圆上的场景视图

And here's what the component looks like in the inspector:这是检查器中组件的样子: 检查器中的组件属性

Again, brilliant, @Ruzihm!再次,精彩,@Ruzihm! Exactly what I wanted!正是我想要的!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM