【问题标题】:Create just one sphere that travels between two points in a loop within a half circle?只创建一个在半圆内循环中的两点之间行进的球体?
【发布时间】:2019-06-25 08:55:32
【问题描述】:

关于此处提出的问题 (How to place spheres in a half circle shape between 2 points),它在 A 和 B 两点之间生成球体。 如何在一个循环周期中只创建一个从 A 点移动到 B 点,然后从 B 点回到 A 点的球体?在这种情况下如何使用 Lerp?

我尝试让球体按照以下代码中描述的角度(半圆)移动,但它始终沿直线移动。

以下代码在两点之间生成球体。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GetCurves : MonoBehaviour
{

    public GameObject A;
    public GameObject B;

    public int amount;

    [ContextMenu("PlaceSpheres()")]
    public void Start()
    {
        PlaceSpheres(A.transform.position, B.transform.position, amount);
    }

    public void PlaceSpheres(Vector3 posA, Vector3 posB, int numberOfObjects)
    {
        // get circle center and radius
        var radius = Vector3.Distance(posA, posB) / 2f;
        var centerPos = (posA + posB) / 2f;

        // get a rotation that looks in the direction
        // posA -> posB
        var centerDirection = Quaternion.LookRotation((posB - posA).normalized);

        for (var i = 0; i < numberOfObjects; i++)
        {

            var angle = Mathf.PI * (i+1) / (numberOfObjects + 1); //180 degrees
            var x = Mathf.Sin(angle) * radius;
            var z = Mathf.Cos(angle) * radius;
            var pos = new Vector3(x, 0, z);
            // Rotate the pos vector according to the centerDirection
            pos = centerDirection * pos;

            var sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
            sphere.transform.position = centerPos + pos;
            sphere.transform.localScale = new Vector3(0.05f, 0.05f, 0.05f);
        }
    }
}

我创建的以下脚本使对象在循环中的两点之间移动,但只能沿直线移动。如何让它在曲线中移动(180 度)?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RunInLoop : MonoBehaviour
{

    public float speed = 0.25f;
    public Transform PointA;
    public Transform PointB;
    private Vector3 origin;
    private bool backToOrigin;

    void Start()
    {
        transform.position = PointA.transform.position;
        origin = transform.position;
    }

    void Update()
    {

            transform.position = Vector3.MoveTowards(transform.position, backToOrigin ? origin : PointB.transform.position, speed * Time.deltaTime);

            // if one of the two positions is reached invert the flag
            if (transform.position == PointB.transform.position || transform.position == origin)
            {
                backToOrigin = !backToOrigin;
            }

    }
}

【问题讨论】:

  • 您的代码只运行一次并将项目​​放置在一个球体中。它没有任何动作。这是某种形式的学校作业吗?本周似乎有很多关于放置东西和移动它们的问题
  • 是的,这段代码只是在两点之间放置了多个球体。我尝试只放置一个球体在两点之间移动,但它不遵循我正在创建的路径。不,绝对不是学校作业,我正在尝试在 Unity 上做新的事情,这就是我坚持的地方。
  • 您需要展示您是如何尝试移动它的。问题真的不是统一,而是如何沿着路径前进的逻辑
  • 我已使用新脚本更新了帖子,该脚本使对象从 A 点移动到 B 点并循环返回。 (直线)
  • 更正它会直线移动,因为这就是你告诉它要做的事情。您需要找到弧上的点并移动到该点,而不仅仅是在 a->b 之间

标签: c# unity3d


【解决方案1】:

使用您的代码的解决方案

正如我在 my last answer 中告诉你的那样,提供了你的第一个代码,你应该将它们存储在一个列表中,然后让对象在它们之间移动:

public class GetCurves : MonoBehaviour
{
    public GameObject A;
    public GameObject B;

    public int amount;
    public float moveSpeed;

    private List<Vector3> positions = new List<Vector3>();
    private Transform sphere;
    private int currentIndex = 0;
    private bool movingForward = true;

    private void Start()
    {
        sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere).transform;
        sphere.transform.localScale = new Vector3(0.05f, 0.05f, 0.05f);

        GeneratePositions(A.transform.position, B.transform.position, amount);

        sphere.position = positions[0];
    }

    private void GeneratePositions(Vector3 posA, Vector3 posB, int numberOfObjects)
    {
        // get circle center and radius
        var radius = Vector3.Distance(posA, posB) / 2f;
        var centerPos = (posA + posB) / 2f;

        // get a rotation that looks in the direction
        // posA -> posB
        var centerDirection = Quaternion.LookRotation((posB - posA).normalized);

        for (var i = 0; i < numberOfObjects; i++)
        {

            var angle = Mathf.PI * (i + 1) / (numberOfObjects + 1); //180 degrees
            var x = Mathf.Sin(angle) * radius;
            var z = Mathf.Cos(angle) * radius;
            var pos = new Vector3(x, 0, z);
            // Rotate the pos vector according to the centerDirection
            pos = centerDirection * pos;

            // store them in a list this time
            positions.Add(centerPos + pos);
        }
    }

    private void Update()
    {
        if (positions == null || positions.Count == 0) return;

        // == for Vectors works with precision of 0.00001
        // if you need a better precision instead use
        //if(!Mathf.Approximately(Vector3.Distance(sphere.position, positions[currentIndex]), 0f))
        if (sphere.position != positions[currentIndex])
        {
            sphere.position = Vector3.MoveTowards(sphere.transform.position, positions[currentIndex], moveSpeed * Time.deltaTime);

            return;
        }

        // once the position is reached select the next index
        if (movingForward)
        {
            if (currentIndex + 1 < positions.Count)
            {
                currentIndex++;
            }
            else if (currentIndex + 1 >= positions.Count)
            {
                currentIndex--;
                movingForward = false;
            }
        }
        else
        {
            if (currentIndex - 1 >= 0)
            {
                currentIndex--;
            }
            else
            {
                currentIndex++;
                movingForward = true;
            }
        }
    }
}

如果您想坚持单一职责原则,您还可以将移动与列表生成分开,例如

public class GetCurves : MonoBehaviour
{
    public GameObject A;
    public GameObject B;

    public int amount;
    public float moveSpeed;

    private void Start()
    {
        GeneratePositions(A.transform.position, B.transform.position, amount);
    }

    private void GeneratePositions(Vector3 posA, Vector3 posB, int numberOfObjects)
    {
        // get circle center and radius
        var radius = Vector3.Distance(posA, posB) / 2f;
        var centerPos = (posA + posB) / 2f;

        // get a rotation that looks in the direction
        // posA -> posB
        var centerDirection = Quaternion.LookRotation((posB - posA).normalized);

        List<Vector3> positions = new List<Vector3>();    

        for (var i = 0; i < numberOfObjects; i++)
        {

            var angle = Mathf.PI * (i + 1) / (numberOfObjects + 1); //180 degrees
            var x = Mathf.Sin(angle) * radius;
            var z = Mathf.Cos(angle) * radius;
            var pos = new Vector3(x, 0, z);
            // Rotate the pos vector according to the centerDirection
            pos = centerDirection * pos;

            // store them in a list this time
            positions.Add(centerPos + pos);
        }

        var sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
        sphere.transform.localScale = new Vector3(0.05f, 0.05f, 0.05f);

        var movement = sphere.AddComponent<MoveBetweenPoints>();
        movement.positions = positions;
        movement.moveSpeed = moveSpeed;
    }

在单独的脚本中

public class MoveBetweenPoints : MonoBehaviour
{
    public List<Vector3> positions = new List<Vector3>();
    public float moveSpeed;

    privtae bool movingForward = true;
    private int currentIndex = 0;

    private void Update()
    {
        if (positions == null || positions.Count == 0) return;

        // == for Vectors works with precision of 0.00001
        // if you need a better precision instead use
        //if(!Mathf.Approximately(Vector3.Distance(sphere.position, positions[currentIndex]), 0f))
        if (sphere.position != positions[currentIndex])
        {
            transform.position = Vector3.MoveTowards(transform.position, positions[currentIndex], moveSpeed * Time.deltaTime);

            return;
        }

        // once the position is reached select the next index
        if (movingForward)
        {
            if (currentIndex + 1 < positions.Count)
            {
                currentIndex++;
            }
            else if (currentIndex + 1 >= positions.Count)
            {
                currentIndex--;
                movingForward = false;
            }
        }
        else
        {
            if (currentIndex - 1 >= 0)
            {
                currentIndex--;
            }
            else
            {
                currentIndex++;
                movingForward = true;
            }
        }
    }
}


实际解决方案

但是,如果您想要在圆形曲线上平滑移动……为什么还要将该圆形曲线减少到一定数量的位置?你可以直接按照180°之间的角度移动,像这样:

public class GetCurves : MonoBehaviour
{
    public GameObject A;
    public GameObject B;
    // now in Angles per second
    public float moveSpeed;

    private Transform sphere;
    private bool movingForward = true;
    private float angle;

    private void Start()
    {
        sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere).transform;
        sphere.transform.localScale = new Vector3(0.05f, 0.05f, 0.05f);
    }

    private void Update()
    {
        if (movingForward)
        {
            angle += moveSpeed * Time.deltaTime;
        }
        else
        {
            angle -= moveSpeed * Time.deltaTime;
        }

        if (angle < 0)
        {
            angle = 0;
            movingForward = true;
        }
        else if (angle > 180)
        {
            angle = 180;
            movingForward = false;
        }

        // get circle center and radius
        var radius = Vector3.Distance(A.transform.position, B.transform.position) / 2f;
        var centerPos = (A.transform.position + B.transform.position) / 2f;

        // get a rotation that looks in the direction
        // posA -> posB
        var centerDirection = Quaternion.LookRotation((B.transform.position - A.transform.position).normalized);

        var x = Mathf.Sin(angle * Mathf.Deg2Rad) * radius;
        var z = Mathf.Cos(angle * Mathf.Deg2Rad) * radius;
        var pos = new Vector3(x, 0, z);
        // Rotate the pos vector according to the centerDirection
        pos = centerDirection * pos;

        sphere.position = centerPos + pos;
    }
}

【讨论】:

  • 感谢您的更改和此代码,我更新了一些更改,例如 Vector3.MoveTowards 和其他一些更改。我注意到球体从 A 点前面的一个位置(posA.position + 1)移动到 B 点之前的一个位置(posB.position -1)。我可以在代码中进行哪些更改以使球体正好从 A 点开始并准确地在 B 点结束?
  • 我在原始答案的 cmets 中也提到了这一点。我故意从 A 的偏移量开始并在 B 之前结束一个偏移量。您可以删除 var angle = Mathf.PI * (i + 1) / (numberOfObjects + 1); 行中的两个 +1 并且它必须是 if (currentIndex - 1 &gt;= 0) 刚刚修复了这个
  • 是的,我按照您在上一篇文章中给出的相同说明进行操作,但球体从未达到两个给定点。
  • 哦,是的,应该是var angle = Mathf.PI * i / (numberOfObjects - 1); (gif)
  • 顺便说一句,请参阅Actual Solution 下的最后更新...如果您不想放置各种对象而只移动一个...那么为什么还要存储一组特定位置?您可以直接沿着原始曲线移动
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-19
  • 1970-01-01
  • 2018-10-31
  • 2019-04-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多