简体   繁体   English

如何生成均匀分布的游戏对象并沿圆形路径移动它们?

[英]How to spawn evenly spaced game objects and move them in a circular path?

I don't know why my code doesn't work as intended!我不知道为什么我的代码不能按预期工作!

when I put speed = 1, it works fine.当我将速度设置为 1 时,它工作正常。 But if I increase the speed, it doesn't work.但是如果我提高速度,它不起作用。

I tried to use FixedUpdate too on the circle class, but it didn't fix the issue.我也尝试在 circle 类上使用FixedUpdate ,但它没有解决问题。

I don't know what else I have to do.我不知道我还要做什么。

Actual Behavior:实际行为:实际行为链接

Expected Behavior:预期行为:预期行为的链接

Orbit Class:轨道类:

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

 public class Orbit : MonoBehaviour
 {
     public Circle circlePrefab;
     public Vector2 centerPoint = Vector2.zero;
     [Range(3, 360)] public int segments = 5;
     public float xRadius = 2f;
     public float yRadius = 2f;
     public int numberOfCircles = 0;
     public float speed = 0f;

     private Vector2 initPosition = new Vector2(0, 2f);
     private Vector2[] points;
     private List<float> distances = new List<float>();
     private float totalDistance = 0;

     // Start is called before the first frame update
     void Start()
     {
         points = new Vector2[segments];

         for (var i = 0; i < segments; i++)
         {
             Vector2 point = GetPathPoint(i / (float) segments);

             if (i > 0)
                 totalDistance += AddSegment(points[i - 1], point);

             points[i] = point;
         }

         totalDistance += AddSegment(points[segments - 1], points[0]);

         StartCoroutine(InitCircles());
     }

     private Vector2 GetPathPoint(float t)
     {
         var angle = t * 360f * Mathf.Deg2Rad;
         var x = Mathf.Sin(angle) * xRadius;
         var y = Mathf.Cos(angle) * yRadius;
         return new Vector2(centerPoint.x + x, centerPoint.y + y);
     }

     private float AddSegment(Vector2 from, Vector2 to)
     {
         float distance = (from - to).sqrMagnitude;
         distances.Add(distance);
         return distance;
     }

     private IEnumerator InitCircles()
     {
         yield return new WaitForSeconds(1);

         var time = new WaitForSeconds(totalDistance / speed / numberOfCircles);

         for (var i = 0; i < numberOfCircles; i++)
         {
             Circle circle = Instantiate(circlePrefab, initPosition, transform.rotation);
             circle.transform.parent = transform;
             circle.name = "circle " + i;
             circle.points = points;
             circle.distances = distances;
             circle.speed = speed;

             yield return time;
         }
     }
 }

Circle class:圈类:

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

 public class Circle : MonoBehaviour
 {
     public Vector2[] points;
     public float speed = 1f;
     public List<float> distances = new List<float>();

     private float distance = 0;
     private int currentIndex = 0;
     private float time = 0;
     private Vector2 currentPoint;
     private Vector2 nextPoint;

     // Start is called before the first frame update
     void Start()
     {
         currentPoint = points[currentIndex];
         nextPoint = points[currentIndex + 1];
     }

     // Update is called once per frame
     void Update()
     {
         if (transform.position == (Vector3) nextPoint)
         {
             currentIndex++;
             currentIndex %= distances.Count;

             time = 0;
             currentPoint = points[currentIndex];
             nextPoint = points[(currentIndex + 1) % points.Length];
         }

         time += Time.deltaTime;
         distance = time * speed / distances[currentIndex];
         transform.position = Vector2.Lerp(currentPoint, nextPoint, distance);
     }
 }

One problem is that your circles may overshoot your destination because you are using Vector2.Lerp .一个问题是您的圈子可能会超出您的目的地,因为您使用的是Vector2.Lerp Instead, consider finding the minimum between distance you need to travel and the distance left to the next point, and/or using Vector2.MoveTowards so that you are guaranteed to never overshoot your destination.相反,请考虑找到您需要行驶的距离与到下一个点的剩余距离之间的最小值,和/或使用Vector2.MoveTowards以确保您永远不会超过目的地。

You also need to keep track of how far you need to travel and loop until all of the distance you need to travel this frame is covered:您还需要跟踪您需要行驶和循环的距离,直到您需要行驶此框架的所有距离都被覆盖:

 void Update()
 {
     float distanceToTravel = speed * Time.deltaTime;

     while (distanceToTravel > 0) 
     {
         if (transform.position == (Vector3) nextPoint)
         {
             currentIndex = (currentIndex + 1) % distances.Count;

             currentPoint = points[currentIndex];
             nextPoint = points[(currentIndex + 1) % points.Length];
         }

         float distanceThisIteration = Mathf.Min(distanceToTravel,
                 Vector2.Distance(transform.position, nextPoint));

         transform.position = 
                 Vector2.MoveTowards(transform.position, nextPoint, distanceThisIteration);

         distanceToTravel -= distanceThisIteration;
     }
 }

In the code in the question, when/if you do overshoot your destination with Lerp , then you will enter a condition transform.position == (Vector3) nextPoint will forever resolve to false .在问题的代码中,当/如果您使用Lerp超出目的地时,您将输入条件transform.position == (Vector3) nextPoint永远解析为false Using MoveTowards instead guarantees that transform.position == (Vector3) nextPoint will eventually resolve to true (as long as speed is nonzero!).使用MoveTowards保证transform.position == (Vector3) nextPoint最终会解析为 true(只要speed不为零!)。

Also, Vector2.sqrMagnitude is not an acceptable way to calculate distance!此外, Vector2.sqrMagnitude不是计算距离的可接受方法! Use Vector2.magnitude or Vector2.Distance(v1,v2) instead:使用Vector2.magnitudeVector2.Distance(v1,v2)代替:

 private float AddSegment(Vector2 from, Vector2 to)
 {
     float distance = Vector2.Distance(from, to);
     distances.Add(distance);
     return distance;
 }

The last problem is that there is rounding type of error that occurs when using WaitForSeconds .最后一个问题是使用WaitForSeconds时会出现舍入类型的错误。 From the documentation :文档

There are some factors which can mean the actual amount of time waited does not precisely match the amount of time specified:有一些因素可能意味着实际等待的时间量与指定的时间量不完全匹配:

  1. Start waiting at the end of the current frame.在当前帧的末尾开始等待。 If you start WaitForSeconds with duration 't' in a long frame (for example, one which has a long operation which blocks the main thread such as some synchronous loading), the coroutine will return 't' seconds after the end of the frame, not 't' seconds after it was called.如果在长帧中以持续时间 't' 启动 WaitForSeconds(例如,具有阻塞主线程的长时间操作(例如某些同步加载)),协程将在帧结束后返回 't' 秒,在它被调用后不是“t”秒。

  2. Allow the coroutine to resume on the first frame after 't' seconds has passed, not exactly after 't' seconds has passed.允许协程在 't' 秒过去后在第一帧恢复,而不是在 't' 秒过去后。

So since Unity will often resume the coroutine the first frame after t seconds have passed, it's actually adding on a fraction of a fraction of a second of an error.因此,由于 Unity 通常会在经过t秒后的第一帧恢复协程,因此它实际上会增加几分之一秒的错误。 So, each time you yield WaitForSeconds in a row, you are adding up that error which leads to the first and the last Circle being very close to each other.因此,每次连续产生WaitForSeconds时,您都会将该错误相加,导致第一个和最后一个Circle彼此非常接近。

To fix this, you can create a coroutine that will create each sphere that begins a WaitForSeconds all starting from the same frame:要解决此问题,您可以创建一个协程,该协程将创建从同一帧开始的WaitForSeconds每个球体:

private IEnumerator InitCircles()
{
    yield return new WaitForSeconds(1);


    for (var i = 0; i < numberOfCircles; i++)
    {
        StartCoroutine(WaitCreateCircle(i));
    }
}

private IEnumerator WaitCreateCircle(int index)
{
    var time = index * totalDistance / speed / numberOfCircles;

    yield return new WaitForSeconds(time);
    Circle circle = Instantiate(circlePrefab, initPosition, transform.rotation);
    circle.transform.parent = transform;
    circle.name = "circle " + index;
    circle.points = points;
    circle.distances = distances;
    circle.speed = speed;

}

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

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