添加随机的游戏元素

来源:互联网 发布:网络机顶盒参数 编辑:程序博客网 时间:2024/06/07 22:37

文档路径:Unity Manual / Unity Overview / Creating Gameplay / Adding Random Gameplay Elements

添加随机的游戏元素

在许多游戏里,随机地选择物品或者数值是很重要的。本节展示了你该如何通过 Unity 内置的随机函数来实现一些常见的游戏机制。

从一个数组中随机地选出一个物品

随机地选择一个数组元素,可以归结为随机地选择一个范围从0到数组最大下标(这个值等于数组的长度减去1)的整数。这项工作可以很轻松地借助内置的 Random.Range 函数来完成:

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

请注意,如果是整形,Random.Range 返回一个取值范围包含第一个参数但不包含第二个参数的值。因此此处使用 myArray.Length 可以给出正确的结果。

如果是浮点数,则包含第一个参数和第二个参数。

以不同的几率选择物品

有时候,你需要随机地选择物品,且其中某些物品被选中的可能性相比另一些物品更大一些。举例来说,某个NPC可能以如下的多种不同方式与遇到的玩家进行交互:

  • 50%的几率打招呼
  • 25%的几率逃跑
  • 20%的几率立即发动攻击
  • 5%的几率赠送金币作为礼物

你可以将这些不同的结果可视化为一个纸带,这个纸带被分割为各个切片,每个切片占据了纸带总体长度的一部分。所占据的部分等于该结果被选中的几率。如此一来,做选择就等价于选择带子上的一个随机位置点(就像扔飞镖那样),然后看这个点落在哪一部分。


在脚本中,这个纸带实际上是一个浮点数数组,这个数组依次包含了各个物品的不同选中概率。这个随机点是通过将 Random.value 乘以数组中所有浮点数的和得到(数组中的浮点数之和不必非得是1;关键在于不同值之间的相对大小)。为了找到随机点落在哪个数组元素中,我们首先检查该值(Random.value 乘以浮点数之和)是不是小于数组中的第一个元素。如果是的话,那么第一个元素就是被选中的幸运儿。否则,将该值减去第一个元素的值,然后比较更新后的值与第二个元素的大小。重复这个过程,直到找到正确的元素。这件事用代码描述看起来如下:

//JS     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;  }  cop


//C#    float Choose (float[] probs) {        float total = 0;        foreach (float elem in probs) {          total += elem;      }        float randomPoint = Random.value * total;        for (int i= 0; i < probs.Length; i++) {          if (randomPoint < probs[i]) {              return i;          }          else {              randomPoint -= probs[i];          }      }      return probs.Length - 1;  }  

请注意,末尾的 return 语句是不可缺少的,因为 Random.value 可能返回 1 作为结果。如果是这种情况,那么搜索将会找不到随机位置点的落点位置。假如将这行

if (randomPoint < probs[i]) 
改为

if (randomPoint <= probs[i])

那么虽然能免去末尾的 return 语句,但是却会允许偶尔选中一个概率被设置为零的物品。

打乱一个列表

一个常见的游戏机制就是从已知的一堆物品里以乱序取出它们。例如,一整副扑克牌通常会被打乱,这样抽牌的顺序就无法被预测。你可以通过访问每一个元素并与其他随机下标的元素进行交换的方式来打乱数组中的元素。示例代码如下:

//JS    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;      }  }   

//C#    void Shuffle (int[] deck) {      for (int i = 0; i < deck.Length; i++) {          int temp = deck[i];          int randomIndex = Random.Range(0, deck.Length);          deck[i] = deck[randomIndex];          deck[randomIndex] = temp;      }  }

不重复地从一堆物品中取出其子集

一个常见的任务就是从一堆物品中不重复地、随机地取出一些物品,每个物品最多被选中一次。例如,你可能想在随机的重生点生成一些NPC,但是每个重生点仅一个NPC。这可以通过顺序地在物品上进行重复地做随机决定该物品是否要加入到被选中集合中来实现。对于每个被访问的物品,该物品被选中的概率等于剩余需要选中物品数量除以剩余候选物品数量。

举个例子,假设有十个重生点可供选择,而只需挑选其中的五个。那么第一个物品被选中的概率是 5/10 或者说是 0.5. 如果它被选中了,那么第二个物品被选中的概率就是 4/9 或者说是 0.44(还需要4个物品,剩余9个可供选择).否则第二个物品被选中的概率是 5/9 或者说是 0.56(还需要5个物品,剩余9个可供选择).重复这个操作直到被选中集合含有5个物品。你可以用如下的代码来完成这项工作:

view plain copy

//JS  var spawnPoints: Transform[];    function ChooseSet(numRequired: int) {      var result = new Transform[numRequired];      var numToChoose = numRequired;            for (numLeft = spawnPoints.Length; numLeft > 0; numLeft--) {          // 我们可以通过简单地加上 0.0 将整数转换为浮点数,这样就不是整除了。          // Hoxily注:此处原文没有加上括号。根据运算符的优先级,将会先计算除法,违背了上文的算法描述。          var prob = (numToChoose + 0.0) / (numLeft + 0.0);          if (Random.value <= prob) {              numToChoose--;              result[numToChoose] = spawnPoints[numLeft - 1];              if (numToChoose == 0)                  break;          }      }            return result;  }   cop


//C#  Transform[] spawnPoints;    Transform[] ChooseSet (int numRequired) {      Transform[] result = new Transform[numRequired];      int numToChoose = numRequired;        for (int numLeft = spawnPoints.Length; numLeft > 0; numLeft--) {          // Hoxily注:小心,“/”运算符两侧都是整数时,将是整除运算。          float prob = (float)numToChoose/(float)numLeft;            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;  

原文地址:http://blog.csdn.net/hoxily/article/details/46299285

原创粉丝点击