Unity中如何确保每个GameObject被实例化偶数次?
解决方案
核心思路
要保证每个tilePrefabs中的预制体被实例化偶数次,得先锁定每个预制体的生成次数(必须是偶数),再把这些实例随机分配到地图的可用位置上——原代码靠随机概率判断的逻辑完全没法控制生成次数,这是问题根源。
具体实现步骤
- 先收集所有可放置的坐标位置,避免重复遍历浪费性能;
- 给每个预制体设定偶数的生成次数(可以固定值,也可以随机生成偶数);
- 批量生成所有需要的实例,打乱实例和位置的顺序后再分配,既保证随机性又严格控制次数。
修改后的完整代码
using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; public class LevelGenerator : MonoBehaviour { public int width = 10; public int height = 10; public GameObject[] tilePrefabs; // 每个预制体最少生成的偶数次数,可按需调整 public int minEvenCountPerPrefab = 2; // 每个预制体最多生成的偶数次数,可按需调整 public int maxEvenCountPerPrefab = 8; void Start() { GenerateLevel(); } void GenerateLevel() { // 第一步:收集所有可用放置位置 List<Vector2> availablePositions = new List<Vector2>(); for (int x = -10; x < width; x++) { for (int y = -4; y < height; y++) { availablePositions.Add(new Vector2(x, y)); } } // 第二步:给每个预制体分配偶数生成次数 Dictionary<GameObject, int> prefabCountMap = new Dictionary<GameObject, int>(); int totalRequiredInstances = 0; foreach (var prefab in tilePrefabs) { // 生成min到max之间的随机偶数 int randomEven = Random.Range(minEvenCountPerPrefab / 2, maxEvenCountPerPrefab / 2 + 1) * 2; prefabCountMap[prefab] = randomEven; totalRequiredInstances += randomEven; } // 检查位置是否足够,不够就自动调整(取位置总数的偶数部分分配) if (totalRequiredInstances > availablePositions.Count) { totalRequiredInstances = availablePositions.Count - (availablePositions.Count % 2); int perPrefab = totalRequiredInstances / tilePrefabs.Length; // 确保每个预制体的次数是偶数 perPrefab = perPrefab - (perPrefab % 2); foreach (var prefab in tilePrefabs) { prefabCountMap[prefab] = perPrefab; } // 处理总数剩余的情况(比如预制体数量是奇数),给前几个预制体各加2次(保持偶数) int remaining = totalRequiredInstances - perPrefab * tilePrefabs.Length; for (int i = 0; i < remaining / 2; i++) { prefabCountMap[tilePrefabs[i]] += 2; } } // 第三步:批量生成实例并打乱顺序 List<GameObject> instancesToPlace = new List<GameObject>(); foreach (var kvp in prefabCountMap) { for (int i = 0; i < kvp.Value; i++) { GameObject instance = Instantiate(kvp.Key, Vector3.zero, Quaternion.identity); instancesToPlace.Add(instance); } } // 洗牌算法打乱实例和位置的顺序,保证随机放置 ShuffleList(instancesToPlace); ShuffleList(availablePositions); // 第四步:把实例分配到随机位置 for (int i = 0; i < instancesToPlace.Count; i++) { instancesToPlace[i].transform.position = availablePositions[i]; } } // 通用洗牌方法,用于打乱列表 void ShuffleList<T>(List<T> list) { int n = list.Count; while (n > 1) { n--; int k = Random.Range(0, n + 1); T temp = list[k]; list[k] = list[n]; list[n] = temp; } } void FixedUpdate() { if ((GameControl.control.matches >= 69) && (GameControl.control.matches < 207)) { SceneManager.LoadScene("Level2"); } } }
代码关键点说明
- 位置收集:一次性把所有坐标存入列表,避免原代码三层循环的无效遍历;
- 偶数次数控制:通过
*2的方式确保生成次数是偶数,同时做了位置不足的容错处理; - 随机分配:用洗牌算法打乱实例和位置,既保留随机性,又不会出现次数失控的情况。
简化版方案(固定偶数次数)
如果不需要随机次数,只想让每个预制体固定生成偶数次(比如每个生成2次),可以用更简单的逻辑:
void GenerateLevel() { List<Vector2> availablePositions = new List<Vector2>(); for (int x = -10; x < width; x++) { for (int y = -4; y < height; y++) { availablePositions.Add(new Vector2(x, y)); } } // 每个预制体固定生成2次(偶数) int fixedEvenCount = 2; int totalInstances = tilePrefabs.Length * fixedEvenCount; if (totalInstances > availablePositions.Count) { Debug.LogError("可用位置不足,无法生成指定数量的实例"); return; } ShuffleList(availablePositions); int posIndex = 0; foreach (var prefab in tilePrefabs) { for (int i = 0; i < fixedEvenCount; i++) { Instantiate(prefab, availablePositions[posIndex], Quaternion.identity); posIndex++; } } }
内容的提问来源于stack exchange,提问作者BevvyOfFun
相关产品推荐
相关产品推荐

