防止对象在随机位置生成时重叠(Unity 2D)

问题描述 投票:0回答:1

我正在努力阻止预制对象在生成时重叠。我在 Google 上搜索了很多类似的问题,并意识到这个问题在游戏开发中很常见,但我自己很难解决。

我有一个带有 Box Collider 2D 和 Sprite Renderer 组件的预制件。它们绑定到屏幕边界并在此边界内的任何位置随机生成。每 8 秒,预制件的数量就会增加 1,直到达到 8 个对象。问题在于,随着对象数量的增加,可用于生成的空间会减少,预制件会尝试寻找任何可用空间。如果找不到任何预制件,它会在任何位置生成,导致与其他预制件重叠(参见 GIF)。

Issue representation

如何确保在生成大量对象时预制件不会相互重叠? GIF 显示仍然有足够的可用空间用于生成预制件,但由于某种原因,它没有检测到这些空间并与其他对象重叠。我尝试了一些方法,但没有成功(查看 GetUniqueSpawnPosition)。

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

public class SpikeController : MonoBehaviour
{
    public int initialSpikeCount = 3;          // Initial number of spikes
    public int maxSpikes = 8;                  // Maximum number of spikes
    public int spikesIncreaseInterval = 8;     // Interval for increasing spikes
    public float spikeSpawnInterval = 1.5f;    // Spikes disappear time
    public GameObject spikePrefab;             // Spike prefab

    public Transform LeftBoundary;            

    private int currentSpikeCount;
    private List<GameObject> spikes = new List<GameObject>();

    // Spike size
    public Vector3 spikeSize = new Vector3(2.84f, 2.84f, 2.84f);

    void Start()
    {
        currentSpikeCount = initialSpikeCount;
        SpawnSpikes();
        StartCoroutine(SpikeIncreaseRoutine());
    }

    void Update()
    {
        if (spikes.Count < currentSpikeCount)
        {
            SpawnSpikes();
        }
    }

    private IEnumerator SpikeIncreaseRoutine()
    {
        while (currentSpikeCount < maxSpikes)
        {
            yield return new WaitForSeconds(spikesIncreaseInterval);
            IncreaseSpikeCount();
        }
    }

    private void SpawnSpikes()
    {
        ClearSpikes();  // Remove previous spikes if any

        for (int i = 0; i < currentSpikeCount; i++)
        {
            Vector3 spawnPosition = GetUniqueSpawnPosition();
            GameObject spike = Instantiate(spikePrefab, spawnPosition, Quaternion.Euler(0, 0, 90f)); // Spawning only on the left
            
            // Set the size of the prefab
            spike.transform.localScale = spikeSize;
            
            spikes.Add(spike);
            StartCoroutine(RemoveSpikeAfterDelay(spike, spikeSpawnInterval));
        }
    }

    private Vector3 GetUniqueSpawnPosition()
    {
        BoxCollider2D boundaryCollider = LeftBoundary.GetComponent<BoxCollider2D>();
        float boundaryYMin = LeftBoundary.position.y - boundaryCollider.size.y / 2;
        float boundaryYMax = LeftBoundary.position.y + boundaryCollider.size.y / 2;

        float x = -2.24f; // Fixed position on the left
        float y = 0;

        float spikeHeight = spikeSize.y;  // Spike height

        // Adjust y range to avoid spawning outside the boundary
        float boundaryMin = boundaryYMin + spikeHeight / 2;
        float boundaryMax = boundaryYMax - spikeHeight / 2;

        // Try to find a unique spawn position
        for (int attempt = 0; attempt < 100; attempt++)
        {
            y = Random.Range(boundaryMin, boundaryMax); // Adjusted for the height of the spike

            Vector3 potentialPosition = new Vector3(x, y, 0);

            if (!IsOverlappingWithExistingSpikes(potentialPosition, spikeSize.x, spikeSize.y))
            {
                return potentialPosition;
            }
        }

        Debug.LogWarning("Cannot find unique positions");
        return new Vector3(x, Random.Range(boundaryMin, boundaryMax), 0);
    }

    private bool IsOverlappingWithExistingSpikes(Vector3 position, float width, float height)
    {
        // Check for overlap with existing colliders
        foreach (var spike in spikes)
        {
            if (Vector2.Distance(position, spike.transform.position) < (width / 2 + spikeSize.x / 2))
            {
                return true;
            }
        }

        return false;
    }

    public void IncreaseSpikeCount() 
    {
        if (currentSpikeCount < maxSpikes)
        {
            currentSpikeCount++;
            SpawnSpikes();  // Create new spikes after increasing the count
        }
    }

    private void ClearSpikes()
    {
        foreach (var spike in spikes)
        {
            Destroy(spike);
        }
        spikes.Clear();
    }

    private IEnumerator RemoveSpikeAfterDelay(GameObject spike, float delay)
    {
        yield return new WaitForSeconds(delay);
        spikes.Remove(spike);
        Destroy(spike);
    }
}

我尝试了 100 次尝试来找到生成预制件的位置。我将寻找唯一位置的尝试次数限制为 100 次,因为我不确定更多的尝试次数可能会如何影响整体性能,而且增加尝试次数似乎没有任何区别。

我希望它能为每个预制件找到一个独特的位置,这样它们就不会重叠。然而,它并没有按预期工作。当预制件找不到唯一的位置时,它们会开始寻找任何可用的空间来生成,这会导致重叠。

unity-game-engine 2d overlapping
1个回答
0
投票

抖动网格

  1. 均匀地创建 n 个尖峰。
  2. 为每个尖峰的位置添加随机偏移。

泊松盘

  1. 随机添加一个秒杀。
  2. 每次在前一个峰值附近的随机位置添加新的峰值。

已排序列表

  1. 将峰值保存在有序(按位置)列表中。
  2. 从列表中随机选择一个尖峰。
  3. 计算此尖峰与列表(或边界)中的上一个/下一个尖峰之间的空间是否可以容纳至少一个尖峰,如果是,则在空间中的随机位置添加一个尖峰,如果不是,则重复上一步.
© www.soinside.com 2019 - 2024. All rights reserved.