Unity3D说明文档翻译-Adding Random Gameplay Elements

凌翔宇
2023-12-01

Adding Random Gameplay Elements

添加随机游戏元素

Randomly chosen items or values are important in many games. This sections shows how you can use Unity’s built-in random functions to implement some common game mechanics.

随机选择项和值在许多游戏中非常重要.这部分教你如何使用Unity的内建随机函数来实现常见游戏功能.

Choosing a Random Item from an Array

从数组选择一个随机项

Picking an array element at random boils down to choosing a random integer between zero and the array’s maximum index value (which is equal to the length of the array minus one). This is easily done using the built-in Random.Range function:-

随机挑选一个数组元素归根到底就是随机选择一个在0和数组最大索引值(等于数组长度减1)之间的整数.这使用内建的随机->值域函数很容易做到:

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

Note that Random.Range returns a value from a range that includes the first parameter but excludes the second, so using myArray.Length here gives the correct result.

注意,Random.Range函数返回一个给定值域内的值,包括第一参数但不包括第二个,所以这里使用的myArray.Length是正确的.

Choosing Items with Different Probabilities

用不同概率选择项

Sometimes, you need to choose items at random but with some items more likely to be chosen than others. For example, an NPC may react in several different ways when it encounters a player:-

有时,你需要随机选择项但某些项比其他项更能被选中.如当一个NPC碰到一个玩家时可能有几种不同的反应方式:

· 50% chance of friendly greeting

· 25% chance of running away

· 20% chance of immediate attack

· 5% chance of offering money as a gift

· 50%机会友好招呼

· 25%机会跑开

· 20%机会立即攻击

· 5%机会提供金钱作为礼物

· 

You can visualise these different outcomes as a paper strip divided into sections each of which occupies a fraction of the strip’s total length. The fraction occupied is equal to the probability of that outcome being chosen. Making the choice is equivalent to picking a random point along the strip’s length (say by throwing a dart) and then seeing which section it is in.

你可以想像这些不同结果为一张纸条分为几个部分,每个占据纸条总长度的以部分.被占据的部分相当于选定结果的概率.使选择等于随机沿着纸条的长度上的点(称为投一个镖)然后看它在哪部分.

In the script, the paper strip is actually an array of floats that contain the different probabilities for the items in order. The random point is obtained by multiplying Random.value by the total of all the floats in the array (they need not add up to 1; the significant thing is the relative size of the different values). To find which array element the point is “in”, firstly check to see if it is less than the value in the first element. If so, then the first element is the one selected. Otherwise, subtract the first element’s value from the point value and compare that to the second element and so on until the correct element is found. In code, this would look something like the following:-

在脚本上,纸条实际上是一个浮点数组包含提供的不同概率项.随机点通过Random.value乘以数组中所有浮点数的总和(它们不需要加1,重要的是不同值的相对大小)获得.找到点在哪个数组元素内,首先检查它是否小于第一个元素的值.如果是则第一个元素是一个选择.否则,从点的值减去第一个元素的值再和第二个元素比较等等,直到找到正确的元素.在代码中,看起来像下面这样:

 

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

}

 

//C#

 

    float Choose (float[] probs) {

 

        float total = 0;

//将数组所有项相加

        foreach (float elem in probs) {

            total += elem;

        }

//获取在0到数组所有项总和间的随机值

        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;

    }

 

Note that the final return statement is necessary because Random.value can return a result of 1. In this case, the search will not find the random point anywhere. Changing the line

注意,最后一个返回语句是必须的,因为Random.value函数可能返回1.在这种情况下,将搜索不到随机点的位置.改变行

 if (randomPoint < probs[i])

…to a less-than-or-equal test would avoid the extra return statement but would also allow an item to be chosen occasionally even when its probability is zero.

为小于等于测试将避免额外的返回语句但也允许一个项偶尔被选中即使当它可能是0.

Weighting continuous random values

连续加权随机数

The array of floats method works well if you have discrete outcomes, but there are also situations where you want to produce a more continuous result - say, you want to randomize the number of gold pieces found in a treasure chest, and you want it to be possible to end up with any number between 1 and 100, but to make lower numbers more likely. Using the array-of-floats method to do this would require that you set up an array of 100 floats (i.e. sections on the paper strip) which is unwieldy; and if you aren’t limited to whole numbers but instead want any number in the range, it’s impossible to use that approach.

浮点数组方法在你有离散的结果时工作得很好,但也有一些情况下如你想要制作许多连续的结果.比如说你想随机许多金币在宝箱内找到,并且你想它在1到100内,但更倾向于小点的数.使用浮点数组方法需要你设置一个包含100内浮点数的数组(即纸片上分成的部分数),这是很笨拙的做法.且如果你不限制整体数量相反想要范围内的任意数,是不可能使用这种方法的.

A better approach for continuous results is to use an AnimationCurve to transform a ‘raw’ random value into a ‘weighted’ one; by drawing different curve shapes, you can produce different weightings. The code is also simpler to write:

一个关于连续结果的更好方法是使用动画曲线来转换原始的随机值为一个加权的.通过绘制不同曲线形状,你可以制作不同权重.代码也更容易写:

//JS

 

function CurveWeightedRandom(curve: AnimationCurve) {

    return curve.Evaluate(Random.value);

}

 

//C#

 

float CurveWeightedRandom(AnimationCurve curve) {

    return curve.Evaluate(Random.value);

}

A ‘raw’ random value between 0 and 1 is chosen by reading from Random.value. It is then passed to curve.Evaluate(), which treats it as a horizontal coordinate, and returns the corresponding vertical coordinate of the curve at that horizontal position. Shallow parts of the curve have a greater chance of being picked, while steeper parts have a lower chance of being picked.

通过Random.value读取一个0到1之间的原始随机值.然后传到curve.Evaluate()函数,对它做横向处理并返回曲线在横向坐标位置相应的纵坐标上的值.平坦部分更容易选中而陡峭部分被选中的机会较低.

A linear curve does not weight values at all; the horizontal coordinate is equal to the vertical coordinate for each point on the curve.

一个线性曲线没有加权值.在曲线的每个点上横坐标等于纵坐标.

This curve is shallower at the beginning, and then steeper at the end, so it has a greater chance of low values and a reduced chance of high values. You can see that the height of the curve on the line where x=0.5 is at about 0.25, which means there’s a 50% chance of getting a value between 0 and 0.25.

这条曲线开始平坦最后陡峭,所以它有较大机会选中小的值而较小机会选中大的值.你可以看到曲线的高度在x=0.5时大约为0.25,这意味着有50%的机会获得在0到0.25之间的值.

This curve is shallow at both the beginning and the end, making values close to the extremes more common, and steep in the middle which will make those values rare. Notice also that with this curve, the height values have been shifted up: the bottom of the curve is at 1, and the top of the curve is at 10, which means the values produced by the curve will be in the 1–10 range, instead of 0–1 like the previous curves.

这条曲线在开始和结尾平坦,使得值在两端更常见而在中间变稀少.注意在此曲线上,高度值被上移:曲线的底端在1上,而顶端在10上,意味着此曲线产生的值在1到10之间,而不是之前曲线的0到1.

 

Notice that these curves are not probability distribution curves like you might find in a guide to probability theory, but are more like inverse cumulative probability curves.

注意,这些曲线不是你在概率论上看到的概率分布曲线,但更像反累积概率曲线.

By defining a public AnimationCurve variable on one of your scripts, you will be able to see and edit the curve through the Inspector window visually, instead of needing to calculate values.

在你的脚本上定义一个公共动画曲线变量时,你将能在检视面板窗口上可看到和编辑此曲线,而不是需要去计算值.

This technique produces floating-point numbers. If you want to calculate an integer result - for example, you want 82 gold pieces, rather than 82.1214 gold pieces - you can just pass the calculated value to a function like Mathf.RoundToInt().

此技术产生浮点数.如果你想计算一个整数结果.如,你想82个金币而不是82.1444个金币,你仅需把计算值传入一个函数像Mathf.RoundToInt().

Shuffling a List

混编列表

A common game mechanic is to choose from a known set of items but have them arrive in random order. For example, a deck of cards is typically shuffled so they are not drawn in a predictable sequence. You can shuffle the items in an array by visiting each element and swapping it with another element at a random index in the array:-

一个常见游戏技术是从一组已知项选择但要按随机顺序.例如,代表一副扑克牌洗牌,这样它们便被引入了一个不可知的顺序.你可以通过访问数组内每个元素并和另一个元素通过随机索引交换洗牌任何数组:

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

        }

    }

 

Choosing from a Set of Items Without Repetition

选定一组项目不带重复项

A common task is to pick a number of items randomly from a set without picking the same one more than once. For example, you may want to generate a number of NPCs at random spawn points but be sure that only one NPC gets generated at each point. This can be done by iterating through the items in sequence, making a random decision for each as to whether or not it gets added to the chosen set. As each item is visited, the probability of its being chosen is equal to the number of items still needed divided by the number still left to choose from.

一个常见的任务是没有重复的从一个组中挑选若干项.例如,你可能想生成若干NPC在随机刷怪点但每个点只能生成一个NPC.这可通过顺序迭代项做到,随机决定每个点是否被添加到选中组.当每个项被访问时,它被选中的概率等于仍然需要的项的数量除以可供选择的项的数量.

As an example, suppose that ten spawn points are available but only five must be chosen. The probability of the first item being chosen will be 5 / 10 or 0.5. If it is chosen then the probability for the second item will be 4 / 9 or 0.44 (ie, four items still needed, nine left to choose from). However, if the first was not chosen then the probability for the second will be 5 / 9 or 0.56 (ie, five still needed, nine left to choose from). This continues until the set contains the five items required. You could accomplish this in code as follows:-

举个例子,假如有10个刷怪点可用但仅选挑选5个.第一个项被挑选的概率为5/10或0.5.如果它被选中则第二项将是4/9或0.44(即,4个依然需要,9个可供选择).然而,如果第一个没有被选中则第二个的概率将是5/9或0.56(即,5个依然需要,9个可供选择).以此继续直到组包含所需的5个项.你可以通过如下代码实现:

 

//JS

 

 

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;

}

 

 

//C#

 

    Transform[] spawnPoints;

 

    Transform[] ChooseSet (int numRequired) {

        Transform[] result = new Transform[numRequired];

 

        int numToChoose = numRequired;

 

        for (int numLeft = spawnPoints.Length; numLeft > 0; numLeft--) {

 

            float prob = (float)numToChoose/(float)numLeft;

 

            if (Random.value <= prob) {

                numToChoose--;

                result[numToChoose] = spawnPoints[numLeft - 1];

 

                if (numToChoose == 0) {

                    break;

                }

            }

        }

        return result;

    }

Note that although the selection is random, items in the chosen set will be in the same order they had in the original array. If the items are to be used one at a time in sequence then the ordering can make them partly predictable, so it may be necessary to shuffle the array before use.

注意,尽管选择是随机的,选中项也是按它们在原来数组上的次序排列的.如果每次按顺序使用一个项则顺序也可以部分的预测,所以可能需要在使用前洗牌数组.

Random Points in Space

空间随机点

A random point in a cubic volume can be chosen by setting each component of a Vector3 to a value returned by Random.value:-

一个随机点在立方体内可通过设置每个每个组件一个通过Random.value返回值的三维向量来选择:

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

This gives a point inside a cube with sides one unit long. The cube can be scaled simply by multiplying the X, Y and Z components of the vector by the desired side lengths. If one of the axes is set to zero, the point will always lie within a single plane. For example, picking a random point on the “ground” is usually a matter of setting the X and Z components randomly and setting the Y component to zero.

这里给出一个在立方体内的点.方块可以通过乘以X,Y,Z组件所需边长的向量缩放.如果其中一条轴设为0,此点将总是在一个平面上.如在地面挑选一个随机点通常是随机设置X和Z组件而设置Y组件为0.

When the volume is a sphere (ie, when you want a random point within a given radius from a point of origin), you can use Random.insideUnitSphere multiplied by the desired radius:-

当容器是一个球(即当你想要一个给定半径的随机点时),你可以使用Random.insideUnitSphere乘以所需半径:

 var randWithinRadius = Random.insideUnitSphere * radius;

Note that if you set one of the resulting vector’s components to zero, you will not get a correct random point within a circle. Although the point is indeed random and lies within the right radius, the probability is heavily biased toward the edge of the circle and so points will be spread very unevenly. You should use Random.insideUnitCircle for this task instead:-

注意,如果你设置一个产生向量的分量为0,你将不会获得一个在一个圆内的正确的随机点.尽管此点确实是随机的且在正确的半径内,但概率严重偏向圆的边缘,所以分布将非常不均匀.你应该使用Random.insideUnitCircle做这个工作:

 var randWithinCircle = Random.insideUnitCircle * radius;

 

 类似资料: