添加随机的游戏元素

随机选取项目或值在很多游戏中非常重要。该部分说明如何使用 Unity 内置随机函数执行一些常见的游戏机制。

从数组选择随机项目

可随机挑选数组元素,是因为能够选择零到数组最大索引值(等于数组长度减去一)之间的随机整数。使用内置 Random.Range 函数就可轻松做到:-

var element = myArray[Random.Range(0, myArray.Length)];

请注意,Random.Range 从包含第一个参数但排除第二个参数的范围中返回一个值,所以在此处使用 myArray.Length 能给出正确结果。

用不同概率选择项目

有时候,您需要随机选择项目,但有些项目被选中的概率比其他项目大。例如,NPC 在遇到玩家时会以几种不同的方式作出反应:-

您可以想象这些不同的结果分布在分成几部分的条形纸上,每种结果占总长度的一部分。所占部分与选中结果的概率相等。做选择就是选取条形纸长度上的随机点(比如说扔飞镖),然后看看它落在哪个部分。

在脚本中,条形纸实际上是一个浮点数组,按顺序包含项目的不同概率。随机点由 Random.value 乘以数组中所有浮点总数得出(它们合计起来不一定等于 1;重点是不同值获得的相对大小)。要找出随机点“落在”哪个数组元素,首先检查看它是否小于第一个元素的值。如果是,则将选中第一个元素。否则,从点值减去第一个元素的值,再与第二个元素比较,直到找到正确的元素。类似于下面这个代码:-

function Choose(probs:float[]) {
	var total = 0;

	for (elem in probs) {
		total += elem;
	}

	var randomPoint = Random.value * total;

	for (i = 0; i < probs.Length; i++) {
		if (randomPoint < probs[i])
			return i;
		else
			randomPoint -= probs[i];
	}

	return probs.Length - 1;
}

请注意,最后一个返回语句不能省,因为 Random.value 会返回结果 1。在这种情况下,无法搜索到随机点。更改代码行

if (randomPoint < probs[i])

...一个小于或等于的测试会避免额外的返回语句,但即便选中一个项目的概率为零,也允许偶尔选中该项目。

随机排序

一个普遍的游戏机制是从一组随机排序的已知项目中进行选择。例如,一副牌通常被打乱,这样就不会以可预测的顺序抓牌。您可以通过访问数组中的每个元素,并随机与数组中的其他元素交换,来打乱数组项目的顺序:-

function Shuffle(deck:int[]) {
	for (i = 0; i < deck.Length; i++) {
		var temp = deck[i];
		var randomIndex = Random.Range(0, deck.Length);
		deck[i] = deck[randomIndex];
		deck[randomIndex] = temp;
	}
}

从一组项目中非重复选取

常见任务是从一组项目中随机挑选若干项目,同一个项目最多只能选中一次。例如,您可能想在随机复活点生成若干 NPC,但要确保每个点处只能生成一个 NPC。为此,可以依次迭代项目,随机决定每个项目是否要添加到选择集。访问完每个项目后,剩下项目被选中的概率等于需要选择的项目数量除以剩下的所有项目数量。

例如,假设有十个复活点可选,但必须只能选择五个。第一个项目被选中的概率是 5 / 10 或 0.5。如果选择第一个项目,则第二个项目的概率为 4 / 9 或 0.44(即仍需从九个剩下的项目中选择四个)。然而,如果没有选择第一个项目,则第二个项目的概率为 5 / 9 或 0.56(即仍需从九个剩下的项目中选择五个)。这个操作一直持续到选择集包含五个所需项目。您可以在如下代码中完成此目的:-

var spawnPoints:Transform[];

function ChooseSet(numRequired:int) {
	var result = new Transform[numRequired];

	var numToChoose = numRequired;

	for (numLeft = spawnPoints.Length; numLeft > 0; numLeft--) {
		// Adding 0.0 is simply to cast the integers to float for the division.
		var prob = numToChoose + 0.0 / numLeft + 0.0;

		if (Random.value <= prob) {
			numToChoose--;
			result[numToChoose] = spawnPoints[numLeft - 1];

			if (numToChoose == 0)
				break;
		}
	}

	return result;
}

请注意,尽管选择是随机的,但选择集中的项目与在原始数组中的顺序相同。如果项目排序是一次性的话,那么部分随机项目是可预见的,所以有必要在使用之前打乱数组的顺序。

空间中的随机点

将 Vector3 每个分量设为 Random.value 返回的值,通过这种方式来选择三维空间中的随机点:-

var randVec = Vector3(Random.value, Random.value, Random.value);

上述函数在边长为一个单位的立方体中给出一个点。将向量的 X、Y 和 Z 分量乘以所需边长,就可将立方体进行简单的缩放。如果一个轴的值设为零,该点始终位于单一平面。例如,如选择“地面”上的一个随机点,通常是随机设置 X 和 Z 分量,而将 Y 分量设为零。

当它是一个球体时(即:当您想从原点引出的给定半径内获取一个随机点时),您可以用 Random.insideUnitSphere 乘以所需半径大小:-

var randWithinRadius = Random.insideUnitSphere * radius;

请注意,如果您将结果向量的某个分量设为零,则*不能*在一个圆内获得正确的随机点。尽管该点确实是随机的,且在右半径内,但点的分布概率在很大程度上会偏向圆的边缘,因此点分布极不均匀。为完成此任务,应当使用 Random.insideUnitCircle:-

var randWithinCircle = Random.insideUnitCircle * radius;

Page last updated: 2013-06-20