一、API
Random.Range(4,10)//随机生成4到10之间的整数。不包含10
Random.Range(4,5f)//随机生成4到5之间的小数
2.伪随机数
public class Main: MonoBehaviour
{
private void Awake()
{
Random.InitState(0);
}
void Update()
{
if(Input.GetKeyDown(KeyCode.Space))
{
print("生成的随机数是: "+Random.Range(1000,1024));
}
}
}
观察发现两次运行结果显示生成的随机数出现的顺序是一样的。所以称为伪随机数。改成这样Random.InitState((int)System.DateTime.Now.Ticks);
,种子设置成当前时间,每次运行就不一样了。
3.其它常用API
insideUnitSphere:返回半径为1的球体内的一个随机点。(只读)
insideUnitCircle:返回半径为1的圆内的一个随机点。(只读)
onUnitSphere:返回半径为1的球体在表面上的一个随机点。(只读)
二、【Unity】关于射击扩散的几种办法
在多数射击游戏中,子弹的弹道通常不是固定的,而是在一个区域范围内变化的,有固定弹道变化,如 CS:GO,也有随机的弹道变化,本文主要讲解如何制作随机的弹道。
1.圆形随机
让我们先来个最简单的,在 Unity 中最常规的获取随机值的办法是使用 Random.Range 方法,我们可以用这种方法来同时获取我们需要的 x 和 y 的(0 - 1)随机值,但这样我们只能获取到一个正方形区域内的 Vector2 值,一般的射击散布中是不会使用正方形范围的随机弹道的。(也可能有游戏这么玩)
Unity 提供了一个方法Random.insideUnitCircle
,它会返回半径为 1 的圆内的一个随机点(Vector2),所以我们直接使用就好啦,现在我们就获取了一个圆形区域内的随机二维值。
2.双圆随机
只有一个圆形内的随机值是远远不够的,它会使我们的弹道完全随机,像喝了假酒的亚瑟·摩根,准星如同摆设,玩家的射击体验会很糟糕,所以我们需要让准星的随机值大概率是落在准星近点,小概率落在准星远点。
这很简单,我们只需要使用 Random.Range 先随机一个值,然后准备两个圆形的随机圆,一个正常的随机圆形大小,一个缩小几倍的圆形,然后让 Random.Range 的随机值大概率落在小圆形中,小概率落在大圆形中就可以啦。
如:noise = 3 >= Random.Range(1, 11) ? Random.insideUnitCircle * 0.2f : Random.insideUnitCircle;
3.高斯分布法
双圆随机是一个取巧的办法,效果只能说勉强过得去,接下来这个方法才是常规办法,要讲解这个方法,首先需要了解高斯分布,也叫正态分布。
简单来说,我们使用高斯分布,给定均值 μ 和方差 σ ,它便会随机计算出一个趋近于均值 μ 的值,其中方差 σ 越小,随机值越趋近于均值。说人话就是,高斯分布可以让我们更高概率获取偏移量小的值。
高斯函数的详解我推荐看这个视频,非常的有趣
但即便看完了视频,你可能也不知道如何在 Unity 中,用代码来实现高斯函数,你需要看这篇国外文章
了解高斯分布 Understanding the Gaussian distribution
如何生成高斯分布数 How togenerate Gaussian distributed numbers
如果你嫌麻烦,也可以直接食用下面的 Code
#region 生成高斯分布数
// mean:均值,variance:方差
// min和max用于去掉不需要的偏差值
public static float NextGaussian(float mean, float variance, float min, float max) {
float x;
do {
x = NextGaussian(mean, variance);
} while (x < min || x > max);
return x;
}
public static float NextGaussian(float mean, float standard_deviation) {
return mean + NextGaussian() * standard_deviation;
}
public static float NextGaussian() {
float v1, v2, s;
do {
v1 = 2.0f * Random.Range(0f, 1f) - 1.0f;
v2 = 2.0f * Random.Range(0f, 1f) - 1.0f;
s = v1 * v1 + v2 * v2;
} while (s >= 1.0f || s == 0f);
s = Mathf.Sqrt((-2.0f * Mathf.Log(s)) / s);
return v1 * s;
}
#endregion
食用栗子
noise = new Vector2(NextGaussian(0, 0.2f, -1, 1), NextGaussian(0, 0.2f, -1, 1));
一个在线图表:https://www.desmos.com/calculator/n2icttf1bw
4.柏林噪声法
柏林噪声法是一个很神奇的函数,它可以随机出不那么随机的随机数(有点绕hhh),例如 Minecraft 的地形随机就是使用了这个方法,关于柏林噪声在子弹散布中的使用效果,我个人测试的效果不是很理想,可能是我的做法有误,所以我就不多讲了,可以参考下面这篇文章
Unity 自带的柏林噪声函数 : Mathf.PerlinNoise
5.如何使最开始不发生扩散
很多时候,我们更希望子弹最开始的几发是准确的,越往后的枪口抖动和后座力越大,例如 CS 中 AK47 的前三发是没有任何偏移的,要实现这个效果,我的做法是添加一个扩散值 diffusionValue 的参数,这个参数的初始值为 0 ,会根据我们射击的时间越来越大,同时扩散的范围也会越来越大。
想要实现固定开火次数内无扩散,只需要做一个计数器即可。当然也可以参考CS:GO的做法,制作一个固定弹道
三、Unity3D中随机函数的应用
1.随机选择具有不同概率的事件
当你你需要选择具有不同发生概率的事件元素时。运用的场景为在游戏中玩家遇到NPC时,NPC在系统内部有好几种与玩家交互的方法。但是这些方法发生的概率并不相等。例如有50%几率进行问好;25%几率跑开;20%几率攻击玩家;5%几率为玩家提供礼品。
学过概率统计学的同学,一定做过在连续时间内发生事件概率的模型。我们也可以将上面描述的需求抽象为一个模型,我们将整个NPC与玩家交互系统看做一个纸带,按照事件发生的概率将纸带分为若干段,将大概率事件排在前面,小概率事件排在后面。当玩家遇到NPC时代表在这个纸带上随机选择一个点。抽象结果用图如下:
image.png
随机选择的点在哪个事件范围内就会发生相应的事件。将这个模型用代码表示为:
float Choose(float[] probs)
{
//将事件元素加入到数组中,如上面有4个元素,分别为50,25,20,5
float total = 0;
foreach (float elem in probs)
{
total += elem;
}
//Random.value方法返回一个0—1的随机数
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;
}
过程描述:首先检查随机数是否小于第一个元素,如果小于那么第一个元素就被选择了。否则用随机数的值减去第一个元素的值然后与第二个元素进行比较,以此类推直到找到正确的元素为止。
过程验证:如上图所示,假如随机数最终为82,那么首先将82与第一个元素50比较:82>50,因此应该将82-50=32与第二个元素25进行比较:32>25,因此应该将32-25=7与第三个元素20进行比较:7<20,那么第三个元素被选中,相对应的事件发生。
2.使用随机数打乱数组顺序
这个案例应用的最好场景就是纸牌游戏中的洗牌操作,思想也比较简单,就是依次将数组内的每个元素与一个随机序号的元素进行交换。
#region 随机数
/// <summary>
/// 随机列表
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list"></param>
public static void RandomList<T>(List<T> list)
{
for (int i = 0; i < list.Count; i++)
{
int randomIndex = UnityEngine.Random.Range(i, list.Count);
T temp = list[i];
list[i] = list[randomIndex];
list[randomIndex] = temp;
}
}
/// <summary>
/// 随机数组
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="array"></param>
public static void RandomArray<T>(T[] array)
{
for (int i = 0; i < array.Length; i++)
{
int randomIndex = Random.Range(i, array.Length);
T temp = array[i];
array[i] = array[randomIndex];
array[randomIndex] = temp;
}
}
#endregion
3.使用随机数在数组中无重复的选择元素
在概率论中我们常常见到这样题目:一个球队10个人,要选择5个人去参加比赛。在游戏中的应用场景为你希望在几个不同的点随机生成一个不相同的NPC。例如需要在10个NPC中随机选择5个,第一个NPC被选中的概率为5/10,如果第一个被选中了那么第二个被选中的概率就变成了4/9,如果第一个被淘汰那么第二个被选中的概率就为5/9
Transform[] spawnPoints;
Transform[] ChooseSet(int numRequired)
{
Transform [] result=new Transform[numRequired];
Int numToChoose=numRequired;
for(int numLeft=spawnPoints.Length;numLeft>0;numLeft--)
{
float prob=(numToChoose+0.0)/(numLeft+0.0);
if(Random.value<=prob)
{
numToChoose--;
result[numToChoose]=spawnPoints[numLeft-1];
if(numToChoose==0)
break;
}
}
return result;
}
过程描述:扫描待选择的NPC数组,如果生成的随机数小于被选中的概率则表示该元素被选中,直到所需要的元素个数都被选中。
注意点:进行选择的时候应该先将待选择的NPC数组进行“洗牌“操作。
网友评论