Unity Inspector实现类似骑砍快速战斗的Spawn Chance滑块联动
实现Unity Inspector中Spawn Chance滑块的联动调整(类似骑砍战团快速战斗军队滑块)
我想在Unity Inspector里实现类似《骑马与砍杀:战团》快速战斗模式的滑块联动功能——拖动其中一个grid item的Spawn Chance(byte类型,用Range属性做的滑块)时,其他滑块会自动调整,保证所有值的总和始终是100。
我用OnValidate写了代码,但现在调整值时滑块会直接锁死在100,达不到预期效果,代码如下:
// the sum of all spawn chances should be 100 List<byte> spawnChances = new List<byte>(); foreach (Spawnable item in gridItems) { spawnChances.Add(item.spawnChance); } if (spawnChances.Count > 0) { byte sum = 0; foreach (byte chance in spawnChances) { sum += chance; } if (sum != 100) { foreach (Spawnable item in gridItems) { item.spawnChance = (byte)(item.spawnChance / sum * 100); // if i do this, when i change the value // the editor snaps it to 100 } } }
问题分析
原代码的核心问题有两个:
- 没有区分用户正在修改的滑块,每次都对所有滑块按比例缩放,导致用户刚调整的值被强制覆盖
- byte是整数类型,
item.spawnChance / sum会先做整数除法(结果为0或1),再乘以100就会出现直接跳到100或0的情况,完全丢失比例信息
修正方案
核心思路是锁定用户正在修改的滑块值,只调整其他滑块,同时处理整数精度问题,保证总和始终为100。以下是完整代码:
using System.Collections.Generic; using UnityEngine; public class SpawnManager : MonoBehaviour { public List<Spawnable> gridItems = new List<Spawnable>(); // 缓存上一次的Spawn Chance值,用来判断哪个项被修改 private List<byte> lastSpawnChances = new List<byte>(); private void OnValidate() { if (gridItems.Count == 0) return; // 初始化缓存列表 if (lastSpawnChances.Count != gridItems.Count) { lastSpawnChances.Clear(); foreach (var item in gridItems) { lastSpawnChances.Add(item.spawnChance); } return; } // 找出用户修改的项 int modifiedIndex = -1; byte modifiedValue = 0; for (int i = 0; i < gridItems.Count; i++) { if (gridItems[i].spawnChance != lastSpawnChances[i]) { modifiedIndex = i; modifiedValue = gridItems[i].spawnChance; break; } } // 无修改项时,直接统一缩放至总和100 if (modifiedIndex == -1) { RescaleAllToSum100(); UpdateLastCache(); return; } // 处理用户输入超过100的情况:强制当前项为100,其他为0 int remaining = 100 - modifiedValue; if (remaining < 0) { gridItems[modifiedIndex].spawnChance = 100; for (int i = 0; i < gridItems.Count; i++) { if (i != modifiedIndex) gridItems[i].spawnChance = 0; } UpdateLastCache(); return; } // 计算其他项的总权重(上一次的数值总和) int otherTotal = 0; for (int i = 0; i < gridItems.Count; i++) { if (i != modifiedIndex) otherTotal += lastSpawnChances[i]; } // 其他项总权重为0时,平均分配剩余值 if (otherTotal == 0) { int itemCount = gridItems.Count - 1; int perItem = remaining / itemCount; int remainder = remaining % itemCount; for (int i = 0; i < gridItems.Count; i++) { if (i != modifiedIndex) { gridItems[i].spawnChance = (byte)(perItem + (remainder > 0 ? 1 : 0)); if (remainder > 0) remainder--; } } } else { // 按上一次的比例分配剩余值,处理整数精度 int currentSum = 0; for (int i = 0; i < gridItems.Count; i++) { if (i != modifiedIndex) { float ratio = (float)lastSpawnChances[i] / otherTotal; byte newValue = (byte)(remaining * ratio); gridItems[i].spawnChance = newValue; currentSum += newValue; } } // 补全差值,保证总和为100 int diff = remaining - currentSum; if (diff != 0) { for (int i = 0; i < gridItems.Count; i++) { if (i != modifiedIndex && gridItems[i].spawnChance > 0) { gridItems[i].spawnChance += (byte)diff; break; } } } } UpdateLastCache(); } // 统一缩放所有项至总和100(初始化或无修改时调用) private void RescaleAllToSum100() { int sum = 0; foreach (var item in gridItems) sum += item.spawnChance; if (sum == 0) { // 总和为0时平均分配 int perItem = 100 / gridItems.Count; int remainder = 100 % gridItems.Count; for (int i = 0; i < gridItems.Count; i++) { gridItems[i].spawnChance = (byte)(perItem + (remainder > 0 ? 1 : 0)); if (remainder > 0) remainder--; } return; } // 按比例缩放前n-1项,最后一项补差值 int currentSum = 0; for (int i = 0; i < gridItems.Count - 1; i++) { float ratio = (float)gridItems[i].spawnChance / sum; byte newValue = (byte)(100 * ratio); gridItems[i].spawnChance = newValue; currentSum += newValue; } gridItems[gridItems.Count - 1].spawnChance = (byte)(100 - currentSum); } // 更新缓存的上一次数值 private void UpdateLastCache() { lastSpawnChances.Clear(); foreach (var item in gridItems) lastSpawnChances.Add(item.spawnChance); } } [System.Serializable] public class Spawnable { [Range(0, 100)] public byte spawnChance; // 这里可以添加其他属性,比如预制体、名称等 }
代码说明
- 用
lastSpawnChances记录上一次的数值,精准定位用户修改的滑块 - 锁定用户修改的滑块值,只调整其他滑块的比例,符合直觉操作
- 处理了多种边界情况:总和为0、用户输入超过100、整数精度丢失
- 保证所有滑块的Spawn Chance总和始终为100,和骑砍战团的滑块逻辑一致
内容的提问来源于stack exchange,提问作者Obscure021
相关产品推荐
相关产品推荐

