C# Animal Spawner 逻辑 - 需要建议
C# Animal Spawner Logic - Advice Required
在我的游戏中,我有散布在地形上的动物产卵器。这个想法是让动物只在玩家在刷怪箱范围内时生成,但如果在刷怪箱附近有太多动物时则不会。这是我到目前为止所做的,但现在我有点卡住了。谁能给我一些关于以下事情的指导:
- 重生几率计算正确吗?例如,将两只动物的产卵几率设置为 10% 和 90% 是否会使动物 A 有 10% 的产卵几率而动物 B 有 90% 的几率,或者我的数学有误?
- 我的半径计算正确吗?
- 最重要的是:我可以改进它吗?
代码:
[System.Serializable]
public class SpawnableAnimal
{
public string AnimalName;
public float spawnWeight;
public float spawnPercentage;
}
public class AnimalSpawner : MonoBehaviour {
public float maxSpawnRadius = 1000.0f;
public float noSpawnRadius = 700.0f;
public GameObject spawnedAnimal;
public SpawnableAnimal[] spawnableAnimals;
void Start () {
System.Random rand = new System.Random();
int randInt = rand.Next(0, 100);
float startTime = randInt / 100f;
float repeatTime = randInt / 100f;
InvokeRepeating("ReadyToSpawn", startTime, (60.0f + repeatTime));
}
void ReadyToSpawn()
{
Debug.Log("Ready to spawn");
bool canSpawn = true;
GameObject[] players = GameObject.FindGameObjectsWithTag("Player");
GameObject[] animals = GameObject.FindGameObjectsWithTag("Animal");
for(int i = 0; i < players.Length; i++)
{
if (Vector3.Distance(this.transform.position, players[i].transform.position) > maxSpawnRadius)
canSpawn = false;
if (Vector3.Distance(this.transform.position, players[i].transform.position) < noSpawnRadius)
canSpawn = false;
}
if (players.Length < 1)
canSpawn = false;
if (spawnedAnimal != null)
canSpawn = false;
if (canSpawn)
SpawnAnimal();
}
void SpawnAnimal()
{
System.Random rand = new System.Random();
double x = rand.NextDouble();
var totalWeight = spawnableAnimals.Select(a => a.spawnWeight).Sum();
for(int i = 0; i < spawnableAnimals.Length; i++)
{
float spawnPercentage = spawnableAnimals[i].spawnWeight / totalWeight;
if(x < spawnPercentage)
{
InstantiateAnimal(i);
return;
}
x -= spawnPercentage;
}
}
void InstantiateAnimal(int animalToSpawn)
{
if (animalToSpawn != -1)
spawnedAnimal = GameObject.Find("AnimalManager").GetComponent<AnimalManager>().SpawnAnimal(spawnableAnimals[animalToSpawn].AnimalName, this.transform.position, this.transform.rotation);
else Debug.Log("No animal to spawn!");
}
}
你在这里做整数除法:
float startTime = randInt / 100
我想你想要的是:
float startTime = randInt / 100f
你获得重生机会 "reversed" :
if(randInt <= spawnableAnimals[i].spawnChance
应该是:
if(randInt >= spawnableAnimals[i].spawnChance
然而,这并不能完全解决问题,因为:
arraySum += randInt;
与 :
做的一样
break;
因此,如果 spawnableAnimals 中的第一个元素的 spawnChance 为 10%,而 spawnableAnimals 中的第二个元素的 spawnChance 为 90%,您最终会得到第一个元素 10% 和 90% 的分配* 第二个为 90% = 81%,因为仅当第一个元素失败时才会尝试第二个元素。
Vector3.Distance(this.transform.position, players[i].transform.position)
这些位置是否相同space? Vector3.Distance(x, y)
是指 |x - y|
吗?如果是这样,那么计算对我来说看起来不错。
SpawnAnimal
看起来很奇怪。我不确定它在做什么。如果你能解释一下你背后的想法,我可能会明白。
我会给每只动物分配一个权重。这个数字是任意的,但它们的相对大小很重要。如果猫的权重为 100,狗的权重为 1000,则狗生成的可能性是猫的 10 倍。
旁白:为什么是权重而不是百分比?如果您定义猫有 10% 的生成几率,狗有 90% 的生成几率,然后您添加沙鼠,剩下多少百分比可以分配给沙鼠?百分比必须加到 1,因此您必须调整所有其他动物的百分比以腾出空间。如果我们使用重量,猫为 100,狗为 1000,那么我们可以添加重量为 500 的沙鼠,我们知道沙鼠的产卵率是猫的 5 倍,是狗的一半——无需调整。
假设您在 [0,1)
中生成一个随机数 x
(这可以用 NextDouble()
完成)。
我们想为每只动物分配一个 [0,1)
的间隔,该间隔与其体重成正比。先求总重量
var totalWeight = spawnableAnimals.Select(a => a.Weight).Sum();
据此我们可以为每只动物指定一个百分比 a.Weight / totalWeight
。这些百分比加起来为 1,因此您可以在区间 [0,1)
内分配这些百分比。 Pi
是从索引的动物集合 A
中产生动物 Ai
的概率,其中 0 <= i < n
和 n
是 |A|
(即数量动物)。
0.0 0.1 0.2 0.3 … 1.0
[ P1 )[ P2 )[ … Pn )
现在您只需要确定 x
所在的区间,即要生成的动物。这是执行此操作的程序算法。
- 初始化
A = the set of animals
、i = 0
、x = NextDouble()
、n = |A|
。
- 如果
i < n
- 那么:如果
x < Pi
- 然后:停止并生成
Ai
。
- 其他:
- 从
x
中减去Pi
- 增量
i
- 转到 (2)。
- 否则:停止,没有动物生成。
ith
间隔 (0 <= i < n
),其中总和由 0 <= j < i
索引是:
ΣP(j-1) <= x < ΣP(j-1) + Pi
(参见示例 table)
ΣP(j-1) - ΣP(j-1) <= x - ΣP(j-1) < ΣP(j-1) + Pi - ΣP(j-1)
(减ΣP(j-1)
)
0 <= x - ΣP(j-1) < Pi
(化简)
x - ΣP(j-1)
是通过累加减法实现的
0 <= x
由 0 <= NextDouble() < 1
实现,如果 x >= Pi
则 x - Pi >= 0
(循环不变量)。
- 因此,我们只需要测试
x < Pi
。
注意:这依赖于具有均匀分布的随机数生成器。
在我的游戏中,我有散布在地形上的动物产卵器。这个想法是让动物只在玩家在刷怪箱范围内时生成,但如果在刷怪箱附近有太多动物时则不会。这是我到目前为止所做的,但现在我有点卡住了。谁能给我一些关于以下事情的指导:
- 重生几率计算正确吗?例如,将两只动物的产卵几率设置为 10% 和 90% 是否会使动物 A 有 10% 的产卵几率而动物 B 有 90% 的几率,或者我的数学有误?
- 我的半径计算正确吗?
- 最重要的是:我可以改进它吗?
代码:
[System.Serializable]
public class SpawnableAnimal
{
public string AnimalName;
public float spawnWeight;
public float spawnPercentage;
}
public class AnimalSpawner : MonoBehaviour {
public float maxSpawnRadius = 1000.0f;
public float noSpawnRadius = 700.0f;
public GameObject spawnedAnimal;
public SpawnableAnimal[] spawnableAnimals;
void Start () {
System.Random rand = new System.Random();
int randInt = rand.Next(0, 100);
float startTime = randInt / 100f;
float repeatTime = randInt / 100f;
InvokeRepeating("ReadyToSpawn", startTime, (60.0f + repeatTime));
}
void ReadyToSpawn()
{
Debug.Log("Ready to spawn");
bool canSpawn = true;
GameObject[] players = GameObject.FindGameObjectsWithTag("Player");
GameObject[] animals = GameObject.FindGameObjectsWithTag("Animal");
for(int i = 0; i < players.Length; i++)
{
if (Vector3.Distance(this.transform.position, players[i].transform.position) > maxSpawnRadius)
canSpawn = false;
if (Vector3.Distance(this.transform.position, players[i].transform.position) < noSpawnRadius)
canSpawn = false;
}
if (players.Length < 1)
canSpawn = false;
if (spawnedAnimal != null)
canSpawn = false;
if (canSpawn)
SpawnAnimal();
}
void SpawnAnimal()
{
System.Random rand = new System.Random();
double x = rand.NextDouble();
var totalWeight = spawnableAnimals.Select(a => a.spawnWeight).Sum();
for(int i = 0; i < spawnableAnimals.Length; i++)
{
float spawnPercentage = spawnableAnimals[i].spawnWeight / totalWeight;
if(x < spawnPercentage)
{
InstantiateAnimal(i);
return;
}
x -= spawnPercentage;
}
}
void InstantiateAnimal(int animalToSpawn)
{
if (animalToSpawn != -1)
spawnedAnimal = GameObject.Find("AnimalManager").GetComponent<AnimalManager>().SpawnAnimal(spawnableAnimals[animalToSpawn].AnimalName, this.transform.position, this.transform.rotation);
else Debug.Log("No animal to spawn!");
}
}
你在这里做整数除法:
float startTime = randInt / 100
我想你想要的是:
float startTime = randInt / 100f
你获得重生机会 "reversed" :
if(randInt <= spawnableAnimals[i].spawnChance
应该是:
if(randInt >= spawnableAnimals[i].spawnChance
然而,这并不能完全解决问题,因为:
arraySum += randInt;
与 :
做的一样break;
因此,如果 spawnableAnimals 中的第一个元素的 spawnChance 为 10%,而 spawnableAnimals 中的第二个元素的 spawnChance 为 90%,您最终会得到第一个元素 10% 和 90% 的分配* 第二个为 90% = 81%,因为仅当第一个元素失败时才会尝试第二个元素。
Vector3.Distance(this.transform.position, players[i].transform.position)
这些位置是否相同space? Vector3.Distance(x, y)
是指 |x - y|
吗?如果是这样,那么计算对我来说看起来不错。
SpawnAnimal
看起来很奇怪。我不确定它在做什么。如果你能解释一下你背后的想法,我可能会明白。
我会给每只动物分配一个权重。这个数字是任意的,但它们的相对大小很重要。如果猫的权重为 100,狗的权重为 1000,则狗生成的可能性是猫的 10 倍。
旁白:为什么是权重而不是百分比?如果您定义猫有 10% 的生成几率,狗有 90% 的生成几率,然后您添加沙鼠,剩下多少百分比可以分配给沙鼠?百分比必须加到 1,因此您必须调整所有其他动物的百分比以腾出空间。如果我们使用重量,猫为 100,狗为 1000,那么我们可以添加重量为 500 的沙鼠,我们知道沙鼠的产卵率是猫的 5 倍,是狗的一半——无需调整。
假设您在 [0,1)
中生成一个随机数 x
(这可以用 NextDouble()
完成)。
我们想为每只动物分配一个 [0,1)
的间隔,该间隔与其体重成正比。先求总重量
var totalWeight = spawnableAnimals.Select(a => a.Weight).Sum();
据此我们可以为每只动物指定一个百分比 a.Weight / totalWeight
。这些百分比加起来为 1,因此您可以在区间 [0,1)
内分配这些百分比。 Pi
是从索引的动物集合 A
中产生动物 Ai
的概率,其中 0 <= i < n
和 n
是 |A|
(即数量动物)。
0.0 0.1 0.2 0.3 … 1.0
[ P1 )[ P2 )[ … Pn )
现在您只需要确定 x
所在的区间,即要生成的动物。这是执行此操作的程序算法。
- 初始化
A = the set of animals
、i = 0
、x = NextDouble()
、n = |A|
。 - 如果
i < n
- 那么:如果
x < Pi
- 然后:停止并生成
Ai
。 - 其他:
- 从
x
中减去 - 增量
i
- 转到 (2)。
Pi
- 从
- 然后:停止并生成
- 否则:停止,没有动物生成。
- 那么:如果
ith
间隔 (0 <= i < n
),其中总和由 0 <= j < i
索引是:
ΣP(j-1) <= x < ΣP(j-1) + Pi
(参见示例 table)ΣP(j-1) - ΣP(j-1) <= x - ΣP(j-1) < ΣP(j-1) + Pi - ΣP(j-1)
(减ΣP(j-1)
)0 <= x - ΣP(j-1) < Pi
(化简)x - ΣP(j-1)
是通过累加减法实现的0 <= x
由0 <= NextDouble() < 1
实现,如果x >= Pi
则x - Pi >= 0
(循环不变量)。- 因此,我们只需要测试
x < Pi
。
注意:这依赖于具有均匀分布的随机数生成器。