簡體   English   中英

如何在 Unity3D 中以最小距離生成對象

[英]How to Spawn Objects in Unity3D with a Minimum Distance between

我正在編寫一個隨機的"Stone" Spawner生成器,目前有一個大問題。 我有一些解決方法的想法,但想知道一種性能友好的方法。

所以我Spawn the Objects on the Sphere Surface方法是這樣的:

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

public class SpawnObjects : MonoBehaviour
{
    public Vector3 centerOfSphere = new Vector3(0, 5000, 0);
    public float radiusOfSphere = 5000.0f;

    public List<GameObject> stones;
    public int stonesToSpawn = 1;

    void Start()
    {
        Vector3 center = transform.position + centerOfSphere;       //Setting the center position of the Sphere

        //This for loop is spawning the Stones on a random position on Sphere Surface
        for (int i = 0; i < stonesToSpawn; i++)
        {
            Vector3 pos = RandomCircle(center, radiusOfSphere);
            Quaternion rot = Quaternion.FromToRotation(Vector3.forward, center - pos);
            Instantiate(stones[Random.Range(0, stones.Count)], pos, rot);
        }
    }

    //Method returns a Random Position on a Sphere
    Vector3 RandomCircle(Vector3 center, float radius) 
    {
        float alpha = UnityEngine.Random.value * 360;
        float beta = UnityEngine.Random.value * 360;
        Vector3 pos;
        pos.x = radius * Mathf.Cos(beta) * Mathf.Cos(alpha);
        pos.y = radius * Mathf.Cos(beta) * Mathf.Sin(alpha);
        pos.z = radius * Mathf.Sin(beta);

        return pos;
    }
}

所以感謝您的以下解釋! :)

如上所述,為了讓您的生活更輕松,只需使用Random.onUnitSphere ,這樣您的整個方法RandomCircle (順便說一句,更改該名稱!)可以縮小到

private Vector3 RandomOnSphere(Vector3 center, float radius)
{
    return center + Random.onUnitSphere * radius;
}

然后為了使它們之間的距離最小,可能有多種方法,但我想最簡單的-蠻力-方法是:

  • 存儲已使用的位置
  • 當你得到一個新的隨機 position 檢查與現有的距離
  • 繼續獲取新的隨機位置,直到找到一個與現有位置不太接近的位置

這當然很大程度上取決於您的用例以及對象的數量和最小距離等 - 換句話說,我讓您來確保在給定的球體半徑下,請求的數量和最小距離完全可行。

您總是可以離開“緊急出口”並在嘗試 100 次后放棄。

像這樣的東西

// Linq offers some handy query shorthands that allow to shorten
// long foreach loops into single calls
using System.Linq;

...

private const int MAX_ATTEMPTS = 100;

public float minimumDistance = 1f;

void Start()
{
    var center = transform.position + centerOfSphere;

    // It is cheaper to use with square magnitudes
    var minDistanceSqr = minimumDistance * minimumDistance;

    // For storing the already used positions
    // Already initialize with the correct capacity, this saves resources
    var usedPositions = new List<Vector3>(stonesToSpawn);

    for (int i = 0; i < stonesToSpawn; i++)
    {
        // Keep track of the attempts (for the emergency break)
        var attempts = 0;
        Vector3 pos = Vector3.zero;
        do
        {
            // Get a new random position
            pos = RandomOnSphere(center, radiusOfSphere);
            // increase the attempts
            attempts++;

            // We couldn't find a "free" position within the 100 attempts :(
            if(attempts >= MAX_ATTEMPTS)
            {
                throw new Exception ("Unable to find a free spot! :'(");
            }
        }
        // As the name suggests this checks if any "p" in "usedPositions" is too close to the given "pos"
        while(usedPositions.Any(p => (p - pos).sqrMagnitude <= minDistanceSqr)));
        
        var rot = Quaternion.FromToRotation(Vector3.forward, center - pos);
        Instantiate(stones[Random.Range(0, stones.Count)], pos, rot);

        // Finally add this position to the used ones so the next iteration
        // also checks against this position
        usedPositions.Add(pos);
    }
}

在哪里

usedPositions.Any(p => (p - pos).sqrMagnitude <= minDistanceSqr))

基本上等於做類似的事情

private bool AnyPointTooClose(Vector3 pos, List<Vector3> usedPositions, float minDistanceSqr)
{
    foreach(var p in usedPositions)
    {
        if((p - pos).sqrMagnitude <= minDistanceSqr)
        {
            return true;
        }
    }

    return false;
}

如果這對你來說更好理解

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM