How to Develop a Game
Probability and Randomness in Games
PublishDate: 2025-06-01 | CreateDate: 2025-06-01 | LastModify: 2025-06-01 | Creator:ljf12825

概率和随机是紧密相关的概念,它们常用于决定事件的发生与结果;在大多数游戏中,概率和随机数的使用涉及到随机事件的模拟,例如:

它们的发生的概率是相等或不等的,数值是随机的或伪随机的

随机和概率在非强竞技场景下的应用非常广泛,或增加了游戏性、或提高了游戏性能、或旨在提升玩家游戏时长、或旨在增加游戏营收,由此产生的效益和收益无疑是巨大的

从游戏实现到数值设计,从具体实现到哲学抽象

概率与随机的定义

具体实现

游戏中概率的实现方式通常取决于具体需求,以下是常见的几种实现

随机数生成器(Random Number Generator, RNG)

这是最常见的做法,通过随机数来模拟概率事件,可以用来生成各种各样的结果,如掉落物品、敌人AI行为、事件触发等

泊松分布概率质量函数(PMF):$P(k,\lambda)=\frac{\lambda^k\cdot e^{-\lambda}}{k!}$

其中:

泊松分布常用于模拟稀有事件,比如在一个时间段内生成一个非常小概率的事件(例如,掉落西游物品)。这种情况比较少见,但在模拟游戏中的“稀有事件”时很有用

应用场景:生成爆发性事件,比如在某个特定条件下掉落非常稀有的物品

泊松分布的随机数生成

为了生成符合泊松分布的随机数,可以使用逆变换抽样法

// 生成符合泊松分布的随机数
int GeneratePoissonRandom(double lambda)
{
    // 使用逆变换抽样法来生成泊松分布的随机数
    int k = 0;
    double L = Math.Exp(-lambda); // e^(-λ)
    double p = 1.0; // 初始值
    double sum = 0.0;

    // 循环直到累积的概率小于L
    while(p > L)
    {
        ++k;
        p *= Random.Shared.NextDouble(); // 生成[0, 1)之间的随机数
        sum += p;
    }

    return k - 1; // 返回事件发生的次数
}

解释:

  1. 生成泊松分布的随机数:
  1. 逆变换法:

加权概率

如果想给不同的事件分配不同的发生概率,可以使用加权概率

这常用于掉落系统、选择不同的路径、敌人AI决策等

举例:

假设有四个掉落物品和它们掉落的概率分别是:

可以将这些概率转化为权重

int[] weights = {50, 30, 15, 5}; // 权重数组
int totalWeight = weights.Sum(); // 权重总和
int randomValue = Random.Range(0, totalWeight);

int chosenItemIndex = 0;
int weightSum = 0;

// 根据随机值选择物品
for (int i = 0; i < weights.Length; ++i)
{
    weightSum += weights[i];
    if (randomValue < weightSum)
    {
        chosenItemIndex = i;
        break;
    }
}

这样,便可以根据权重控制概率的分布

事件驱动(状态机)

在一些复杂的游戏中,可能不仅仅是通过概率来决定事件的发生,还需要多重条件和事件的交互。

例如,AI行为可能需要根据玩家的行为,敌人血量等多重因素来随机选择。

可以使用类似状态机的结构,根据多个条件来触发不同的概率事件。

表格查找

通过查找一个概率表或曲线来返回结果,这在特定场景下非常有用

例如玩家经验值的增长可以通过查表来确定每个经验值段的增长概率

Dictionary<int, float> experienceTable = new Dictionary<int, float>
{
    {1, 0.2f},
    {2, 0.5f},
    {3, 0.3f},
};

int level = 2;
if (Random.Range(0f, 1f) < experienceTable[level])
{
    // 根据经验表,玩家升到下一等级
}

伪随机与真随机

游戏中通常使用的是伪随机数生成器(PRNG),它通过算法生成看似随机的数字,但实际上是可能预测的(如果知道种子)。对于一些安全性要求较高的情况,可能会使用真随机数生成器(例如利用系统时间、硬件噪声等)

伪随机数(Pseudo-Random Number)

伪随机数是通过某种算法从一个初始值(称为种子)生成的看似随机的数。虽然生成的数值分布和真正的随机数非常相似,但它们是可预测的,只要知道了生成过程的算法和种子值,理论上就可以预测后续的随机数

生成方式

伪随机数生成器(PRNG)通常依赖于一个数学公式(或递归关系)来计算每个随机数。最常见的PRNG算法是线性同余法(Linear Congruential Generator, LCG)梅森旋转算法(Mersenne Twister)

例如,梅森旋转算法(Mersenne Twister)是一个非常流行的伪随机数生成器,它提供了均匀分布的高质量随机数,并且有非常长的周期(大约2^32 - 1次生成)

特点
应用
// 使用种子初始化伪随机数生成器
System.Random rng = new System.Random(12345); // 种子为12345
int randomNumber = rng.Next();

真随机数(True Random Numbers)

真随机数是通过物理过程产生的随机数,而非通过算法计算。它们的生成依赖于某种物理现象(例如电子噪声、放射性衰变等),这些现象的结果是完全不可预测的,具有完全的随机性

生成方式

真随机数通常是由硬件随机数生成器(HRNG)生成。这些生成器通过测量物理现象(如热噪声、光子的运动等)来产生随机数。常见的硬件随机数生成器有基于噪声的电路、光子计数器等。

例如,Intel的RDRAND指令可以通过硬件生成真随机数,或者一些第三方硬件设备也可以用来生成真随机数

特点
应用

有些平台提供硬件支持来生成真随机数,例如Intel的RDRAND指令,或者使用操作系统提供的随机源(如/dev/random

// C语言示例:从/dev/random读取真随机数
FILE* fp = fopen("/dev/random", "r");
unsigned int rand_num;
fread(&rand_num, sizeof(rand_num), 1, fp);
fclose(fp);

蒙特卡洛方法模拟连续概率事件

有些游戏可能需要连续的概率事件,而不仅仅是一次性判定。比如,在模拟物理系统、模拟经济系统等复杂系统中,连续的事件可能会影响最终的概率结果

蒙特卡洛方法(Monte Carlo Method)是一类基于随机数的数值计算方法,通过模拟随机样本来解决数学问题,特别是那些难以通过传统解析方法解决的问题。它得名于摩纳哥的蒙特卡洛赌场,因为这种方法的核心思想与赌博中的随机性相似——依赖于大量的随机抽样来推测最终结果。

基本概念

蒙特卡洛方法通过生成大量的随机数来模拟系统的行为,并通过这些随机样本来估计一个复杂系统的结果或计算一个数学问题的近似解。它尤其适用于求解那些具有复杂概率模型或多维积分的问题。

应用场景

蒙特卡洛方法可以应用于很多领域,特别是那些无法通过简单解析解法解决的问题。常见的应用包括:

蒙特卡洛方法的基本步骤

  1. 定义问题:首先明确想要解决的问题,通常是计算某个期望值、概率、积分等
  2. 随机采样:根据问题的定义,随机生成一组输入样本。这些样本可以来自一个已知的概率分布或均匀分布
  3. 评估样本:对每个样本计算相应的函数值(或其他相关量)
  4. 求取统计量:对所有样本的结果进行统计分析,通常是求取平均值、方差等统计量
  5. 结果估计:根据样本的统计量来估计问题的解

应用实例

示例1:估计圆周率(π)

最经典的蒙特卡洛方法之一是通过随机点来估计圆周率(π)。可以将一个单位正方形放入单位圆内,然后随机生成一些点,计算这些点落在圆内的比例。

步骤:

  1. 随机生成一些点(x, y),其中xy的取值范围为[0, 1]
  2. 判断每个点是否落在单位圆内:即检查x^2 + y^2 <= 1
  3. 计算落在圆内的点的比例,乘以4就是π的近似值

数学推导:
单位元的面积是π * r ^ 2,而正方形的面积是1 * 1 = 1,所以落在圆内的点的比例大约是π / 4

// C#
int totalPoints = 1000000;
int pointsInsideCircle = 0;

for (int i = 0; i < totalPoints; ++i)
{
    float x = Random.Range(0f, 1f);
    float y = Random.Range(0f, 1f);
    if (x * x + y * y <= 1) ++ pointsInsideCircle;

    float piEstimate = 4 * (float)pointsInsideCircle / totalPoints;
}

示例2:估算积分 估算函数f(x) = x ^ 2在区间[0, 1]上的定积分

步骤:

  1. 随机生成Nx值,范围是[0, 1]
  2. 对于每个x,计算f(x) = x ^ 2的值
  3. 求所有f(x)的平均值,并乘以区间长度

数学推导:
积分的近似值就是f(x)的平均值乘以区间长度

// C#
int totalSamples = 1000000;
float sum = 0;

for (int i = 0; i < totalSample; ++i)
{
    float x = Random.Range(0f, 1f);
    sum += x * x;
}

float integralEstimate = sum / totalSamples;

蒙特卡洛方法的优缺点

优点:

缺点:

蒙特卡洛方法的改进

蒙特卡洛方法在游戏中的应用

哲学抽象

随机性与平衡性

游戏中的随机性往往需要与游戏的平衡性进行协调。过高的随机性可能导致游戏体验的不稳定和不可预测,而过低的随机性则可能让游戏失去挑战性和趣味性。例如,在战斗系统中,伤害值可能会受到随机性影响,但在确保平衡的前提下,可以给玩家一定的“可预测性”来增加策略性

常见随机性问题:

使用随机数种子

为了避免“随机性”被完全掌控(比如在测试时),开发者可以人为设定随机数的种子,通过固定种子来保证结果的可复现性

多人游戏中的随机性

在多人游戏中,同步随机性(例如,随机数的生成应该在所有玩家的客户端和服务器上同步)可能成为一个问题。为了保证公平性,通常会把随机数生成放在服务器端进行,并将结果同步到客户端