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

相關文章
相關標籤/搜索