Unity 随机数与随机种子

随机数几乎应用于游戏开发的方方面面,例如,随机生成的地图,迷宫,怪物属性等,在Unity中,使用随机数很是方便:dom

 1         //
 2         // 摘要:
 3         //     Return a random integer number between min [inclusive] and max [exclusive] (Read
 4         //     Only).
 5         //
 6         // 参数:
 7         //   min:
 8         //
 9         //   max:
10         public static int Range(int min, int max);
 1         //
 2         // 摘要:
 3         //     Return a random float number between min [inclusive] and max [inclusive] (Read
 4         //     Only).
 5         //
 6         // 参数:
 7         //   min:
 8         //
 9         //   max:
10         [FreeFunction]
11         public static float Range(float min, float max);
1         //
2         // 摘要:
3         //     Returns a random number between 0.0 [inclusive] and 1.0 [inclusive] (Read Only).
4         public static float value { get; }

正常状况下使用以上三种彻底够用了,注意整型的随机是左开右闭的。固然了,你也可使用System.Random中的方法来随机,能够构造出相似于Unity中的扩展方法:测试

 1     static public int Range(this System.Random random, int min, int max)
 2     {
 3         return random.Next(min, max);
 4     }
 5 
 6     static public float Range(this System.Random random, float min, float max)
 7     {
 8         var r = random.NextDouble();
 9         return (float)(r * (max - min) + min);
10     }

值得注意的是,System.Random须要实例化才能随机,而UnityEngine.Random是直接使用。ui

 

但不少时候,咱们除了须要随机数以外,可能会有保留上次随机结果的需求,换句话说,从某一时刻起,咱们但愿每次都能随机出和上次相同的结果,这个时候就该随机种子出场了。this

举例来讲,当玩家须要从新进入一次他之前随机出来过的一个迷宫地图进行二次创做,又好比,咱们在开发过程当中,某个随机单位出现了Bug,但若是下次又无法产生以前随机结果的话,那么就会出现十分头疼的情况了,这样极可能永远有个难以排查的潜在Bug一直在开发过程当中而又难以再次复现。spa

因此,强烈建议,只要是作相对比较复杂的随机行为,咱们最好利用随机种子来执行随机3d

固然了,你说我将全部随机的数据结果序列化保存到本地,那也没问题,但相比随机种子只须要保存一个整型数据来讲,哪一种方式更可取显而易见。这样也能够大大减小游戏保存的数据容量。code

 

说了这么半天,什么是随机种子呢?blog

顾名思义,一个种子对应着一个结果,随机种子对应的就是一个惟一的随机结果。游戏

 1         //
 2         // 摘要:
 3         //     Initializes the random number generator state with a seed.
 4         //
 5         // 参数:
 6         //   seed:
 7         //     Seed used to initialize the random number generator.
 8         [NativeMethod("SetSeed")]
 9         [StaticAccessor("GetScriptingRand()", StaticAccessorType.Dot)]
10         public static void InitState(int seed);

上面的方法中,参数seed就是传入的随机种子,若是在脚本的一开始执行调用了此方法,那么只有当这次随机种子与上次的种子不相同时,才能随机出不一样的随机结果,不然随机的结果老是同样的。ip

注意,这里指的随机结果是指的全部的随机结果,是一个随机数表,它从本质上改变的是整个UnityEngine.Random类的全部随机方法执行的结果,包括最开始列举的三种中的任意一种。

下面作一个测试就很容易理解了:

 1 using UnityEngine;
 2 
 3 public class RanTest : MonoBehaviour
 4 {
 5     public bool bDebug;
 6     //System.Random random;
 7     void Start()
 8     {
 9         //random = new System.Random((int)System.DateTime.Now.Ticks);
10         //string s = "";
11         //for(int i = 0; i < 233; i++)
12         //{
13         //    s += random.Range(0, 10) + ",";
14         //}
15         //Debug.Log(s);
16 
17         int seed = (int)System.DateTime.Now.Ticks;
18         if (bDebug)
19         {
20             seed = PlayerPrefs.GetInt("Seed");   
21         }
22         else
23         {
24             PlayerPrefs.SetInt("Seed", (int)System.DateTime.Now.Ticks);
25         }
26         Random.InitState(seed);
27         string s = "";
28         for (int i = 0; i < 32; i++)
29         {
30             s += Random.Range(0, 10) + ",";
31         }
32         Debug.Log(s);
33     }
34 }

好比我开了一个Debug模式,若是勾选,则随机种子是从上次保存的数据中读取,随机出来的结果永远是同样的,由于我并无对保存的数据种子进行任何的更改。

结果以下:

 

咱们发现每次的随机数都同样,由于它们都源于同一个随机种子,不管以后再随机多少次,结果都是这个随机数序列,这个种子对应的结果已经被计算机固定了,除非种子更改,否则随机结果不会变。

当我关闭Debug模式时,正常的随机种子时刻都不会同样,这里用到了System.DateTime.Now.Ticks来保证获得和上次的种子毫不相同的整型,也可使用guid等。

每次在本地备份一次上一次随机种子的记录,以便随时能够再现上一次随机的结果,只须要轻松勾选Debug便可:

 

 例如,我在第三次时发现了随机产生的其余Bug,这样我只用启动Debug模式反复分析几遍后把复现的隐藏Bug修改结束后再回到正常模式产生新的随机数就好。

 

另外,咱们也能够利用System.Random类的构造方法来实现一样的随机效果,一个构造方法带有随机种子的参数,一个则没有,原理和上面是同样的:

 1 public Random(); 2 public Random(int Seed); 

这个时候改变的就是System.Random类的随机方法,而非UnityEngine.Random的随机方法。

因此一开始就决定好整个开发过程当中用的随机类也不容忽视,建议要么就所有用Unity中的,要么就所有用System中的,这样调整起来天然更驾轻就熟事半功倍。

 

个人博客即将同步至腾讯云+社区,邀请你们一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=12ri51jwydxyj