Unity 2D游戏星星生成异常及ArgumentOutOfRangeException报错求助
Unity 2D星星生成系统问题排查与修复
问题现象
- 星星出现抽搐、仅在玩家后方生成等异常行为
- 运行一段时间后抛出
ArgumentOutOfRangeException,提示“minValue不能大于maxValue”
问题根源分析
1. 随机数范围逻辑错误
starPos.Next(1,4)是左闭右开区间,实际只会生成1、2、3三个值,case4永远不会触发,直接废掉四分之一的生成逻辑FloatRandom方法直接把传入的min和max转整数传给Next(),一旦玩家位置变化导致min > max,就会触发参数异常
2. 生成位置计算逻辑倒置
以case1的x坐标计算为例:10 - player.transform.position.x和14 - player.transform.position.x,当玩家x坐标大于12时,前者数值会比后者大,不仅触发异常,还会导致生成位置完全偏离预期,出现星星只在玩家后方的情况
3. 位置判断与生成逻辑不匹配
isFar判断的是星星与玩家的相对距离,但生成位置用反向计算而非相对偏移,导致生成区域混乱,出现抽搐等异常行为
修复方案
1. 修正随机数逻辑
- 把
starPos.Next(1,4)改成starPos.Next(1,5),让case4能正常触发 - 在
FloatRandom中增加范围校验,若min > max则自动交换,避免参数异常
2. 重构生成位置计算
将生成位置改为基于玩家当前位置的正向偏移,比如玩家右侧x偏移10-14,直接写成player.transform.position.x + 10到player.transform.position.x +14,彻底避免范围倒置问题
3. 简化代码逻辑
优化isFar方法的判断方式,减少冗余代码
修复后的完整代码
using System; using UnityEngine; public class SpawnStars : MonoBehaviour { public GameObject starPrefab; public GameObject player; private Random starPos = new Random(); private GameObject[] starClones = new GameObject[30]; private void Start() { for (int i = 0; i < starClones.Length; i++) { starClones[i] = StarSpawn(); } } void Update() { for (int i = 0; i < starClones.Length; i++) { if (starClones[i] != null && IsFar(starClones[i])) { Destroy(starClones[i]); starClones[i] = StarSpawn(); } } } GameObject StarSpawn() { float posX, posY; int random = starPos.Next(1, 5); // 修正范围,让case4能触发 switch (random) { case 1: // 玩家右侧区域 posX = FloatRandom(player.transform.position.x + 10, player.transform.position.x + 14); posY = FloatRandom(player.transform.position.y - 10, player.transform.position.y + 11); break; case 2: // 玩家左侧区域 posX = FloatRandom(player.transform.position.x - 14, player.transform.position.x - 10); posY = FloatRandom(player.transform.position.y - 10, player.transform.position.y + 11); break; case 3: // 玩家上方区域 posX = FloatRandom(player.transform.position.x - 14, player.transform.position.x + 15); posY = FloatRandom(player.transform.position.y + 6, player.transform.position.y + 9); break; case 4: // 玩家下方区域 posX = FloatRandom(player.transform.position.x - 14, player.transform.position.x + 15); posY = FloatRandom(player.transform.position.y - 9, player.transform.position.y - 6); break; default: // 兜底逻辑,防止异常 posX = player.transform.position.x + starPos.Next(-5, 5); posY = player.transform.position.y + starPos.Next(-5, 5); break; } return Instantiate(starPrefab, new Vector3(posX, posY, 0), player.transform.rotation); } bool IsFar(GameObject star) { Vector2 offset = star.transform.position - player.transform.position; return Mathf.Abs(offset.x) >= 15 || Mathf.Abs(offset.y) >= 15; } float FloatRandom(float min, float max) { // 确保min <= max,否则交换 if (min > max) { (min, max) = (max, min); } // 处理边界情况,避免Next参数相同 if (Math.Abs(min - max) < 0.01f) { return min; } int minInt = Convert.ToInt32(min * 100); int maxInt = Convert.ToInt32(max * 100); float random = starPos.Next(minInt, maxInt); return random / 100f; } }
额外优化建议
- 直接用Unity自带的
Random.Range()替代自定义的FloatRandom,Unity原生方法支持浮点数,还会自动处理min>max的情况,代码更简洁 - 引入对象池技术,复用星星对象,减少频繁Destroy/Instantiate带来的性能开销
内容的提问来源于stack exchange,提问作者DAVIS
相关产品推荐
相关产品推荐

