建议阅读缩放比例:150%

前言

在多数射击游戏中,子弹的弹道通常不是固定的,而是在一个区域范围内变化的,有固定弹道变化,如 CS:GO,也有随机的弹道变化,本文主要讲解如何制作随机的弹道。

制作随机扩散的几种方法

圆形随机

让我们先来个最简单的,在 Unity 中最常规的获取随机值的办法是使用 Random.Range 方法,我们可以用这种方法来同时获取我们需要的 x 和 y 的(0 - 1)随机值,但这样我们只能获取到一个正方形区域内的 Vector2 值,一般的射击散布中是不会使用正方形范围的随机弹道的。(也可能有游戏这么玩)

正方形的随机分布
正方形分布.png

Unity 提供了一个方法
Random.insideUnitCircle

它会返回半径为 1 的圆内的一个随机点(Vector2),所以我们直接使用就好啦,现在我们就获取了一个圆形区域内的随机二维值。

双圆随机

只有一个圆形内的随机值是远远不够的,它会使我们的弹道完全随机,像喝了假酒的亚瑟·摩根,准星如同摆设,玩家的射击体验会很糟糕,所以我们需要让准星的随机值大概率是落在准星近点,小概率落在准星远点。

这很简单,我们只需要使用 Random.Range 先随机一个值,然后准备两个圆形的随机圆,一个正常的随机圆形大小,一个缩小几倍的圆形,然后让 Random.Range 的随机值大概率落在小圆形中,小概率落在大圆形中就可以啦。

如:noise = 3 >= Random.Range(1, 11) ? Random.insideUnitCircle * 0.2f : Random.insideUnitCircle;

高斯分布法

双圆随机是一个取巧的办法,效果只能说勉强过得去,接下来这个方法才是常规办法,要讲解这个方法,首先需要了解高斯分布,也叫正态分布。

正态分布.png

简单来说,我们使用高斯分布,给定均值 μ 和方差 σ ,它便会随机计算出一个趋近于均值 μ 的值,其中方差 σ 越小,随机值越趋近于均值。说人话就是,高斯分布可以让我们更高概率获取偏移量小的值。

高斯函数.png

高斯函数的详解我推荐看这个视频,非常的有趣

考上清华和中500万彩票哪个更难?

但即便看完了视频,你可能也不知道如何在 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

柏林噪声法

柏林噪声法是一个很神奇的函数,它可以随机出不那么随机的随机数(有点绕hhh),例如 Minecraft 的地形随机就是使用了这个方法,关于柏林噪声在子弹散布中的使用效果,我个人测试的效果不是很理想,可能是我的做法有误,所以我就不多讲了,可以参考下面这篇文章

不只是噪声,更是数学美 —— 浅谈Perlin Noise

原始柏林噪声
原始柏林噪声.png

Unity 自带的柏林噪声函数Mathf.PerlinNoise

如何使最开始不发生扩散

很多时候,我们更希望子弹最开始的几发是准确的,越往后的枪口抖动和后座力越大,例如 CS 中 AK47 的前三发是没有任何偏移的,要实现这个效果,我的做法是添加一个扩散值 diffusionValue 的参数,这个参数的初始值为 0 ,会根据我们射击的时间越来越大,同时扩散的范围也会越来越大。

想要实现固定开火次数内无扩散,只需要做一个计数器即可。

当然也可以参考CS:GO的做法,制作一个固定弹道