文档目标:解决高频战斗(每秒死亡 50+ 怪物)下的掉落性能问题,并实现策划对“掉落节奏”的精确控制。
在传统的 RPG 中,每个怪物死亡时都会跑一次 Loot Table:
// ❌ 坏味道:每秒跑 50 次,GC 爆炸
void OnDie() {
if (Random.value < 0.01f) DropItem(); // 1% 几率
}
问题:
Random 调用 + 复杂的 Loot Table 权重计算。我们不再问“这个怪掉不掉?”,而是问“现在该不该掉?”。
策划定义的不是“几率”,而是 DPM (Drops Per Minute)。
2.0 / 60 = 0.033 的“掉落势能”。_dropBudget。_dropBudget += dropRatePerSecond * Time.deltaTime。_dropBudget >= 1.0f?_dropBudget -= 1.0f。为了让“杀精英怪”比“杀小怪”更有价值,我们引入 Score。
设定一个 Drop Threshold (掉落阈值),例如 500 分。
_currentScore。_currentScore += mob.Score。_currentScore > Threshold:
_currentScore = 0 (或者保留溢出部分 _currentScore -= Threshold)。450 ~ 550 之间浮动,避免节奏太死板。public class LootSystem : MonoBehaviour
{
// 策划配置:期望每杀多少分的怪掉一件装备
public float ScorePerDrop = 100f;
// 当前积累的分数
private float _accumulatedScore = 0f;
// 单例访问
public static LootSystem Instance;
public void OnEnemyKilled(float enemyScore, Vector3 pos)
{
_accumulatedScore += enemyScore;
// 检查是否满足掉落条件
// 使用 while 处理“一刀秒全屏”导致瞬间分数暴涨的情况
while (_accumulatedScore >= ScorePerDrop)
{
SpawnLoot(pos);
_accumulatedScore -= ScorePerDrop;
// 可选:每次掉落后略微增加下一次的阈值,防止通货膨胀
// ScorePerDrop *= 1.05f;
}
}
private void SpawnLoot(Vector3 pos)
{
// 从对象池获取掉落物,零 GC
var item = PoolManager.Get(PrefabID.LegendaryItem);
item.transform.position = pos;
}
}
| 特性 | 传统概率法 | 蓄水池/预算算法 |
|---|---|---|
| 掉落节奏 | 极不稳定 (可能连黑) | 平滑且可控 |
| 性能开销 | 高 (每次死亡都 Roll) | 极低 (仅加法运算) |
| 经济控制 | 难 (怪越多掉越多) | 完美 (恒定产出) |
| 玩家体验 | “这游戏爆率太低” | “杀够怪一定会掉” (保底心理) |