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

         //
// 摘要:
// Return a random integer number between min [inclusive] and max [exclusive] (Read
// Only).
//
// 参数:
// min:
//
// max:
public static int Range(int min, int max);
         //
// 摘要:
// Return a random float number between min [inclusive] and max [inclusive] (Read
// Only).
//
// 参数:
// min:
//
// max:
[FreeFunction]
public static float Range(float min, float max);
         //
// 摘要:
// Returns a random number between 0.0 [inclusive] and 1.0 [inclusive] (Read Only).
public static float value { get; }

正常情况下使用以上三种完全够用了,注意整型的随机是左开右闭的。当然了,你也可以使用System.Random中的方法来随机,可以构造出类似于Unity中的扩展方法:

     static public int Range(this System.Random random, int min, int max)
{
return random.Next(min, max);
} static public float Range(this System.Random random, float min, float max)
{
var r = random.NextDouble();
return (float)(r * (max - min) + min);
}

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

但很多时候,我们除了需要随机数之外,可能会有保留上次随机结果的需求,换句话说,从某一时刻起,我们希望每次都能随机出和上次相同的结果,这个时候就该随机种子出场了。

举例来说,当玩家需要重新进入一次他以前随机出来过的一个迷宫地图进行二次创作,又比如,我们在开发过程中,某个随机单位出现了Bug,但如果下次又没法产生之前随机结果的话,那么就会出现十分头疼的状况了,这样很可能永远有个难以排查的潜在Bug一直在开发过程中而又难以再次复现。

所以,强烈建议,只要是做相对比较复杂的随机行为,我们最好利用随机种子来执行随机

当然了,你说我将所有随机的数据结果序列化保存到本地,那也没问题,但相比随机种子只需要保存一个整型数据来说,哪种方式更可取显而易见。这样也可以大大减少游戏保存的数据容量。

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

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

         //
// 摘要:
// Initializes the random number generator state with a seed.
//
// 参数:
// seed:
// Seed used to initialize the random number generator.
[NativeMethod("SetSeed")]
[StaticAccessor("GetScriptingRand()", StaticAccessorType.Dot)]
public static void InitState(int seed);

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

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

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

 using UnityEngine;

 public class RanTest : MonoBehaviour
{
public bool bDebug;
//System.Random random;
void Start()
{
//random = new System.Random((int)System.DateTime.Now.Ticks);
//string s = "";
//for(int i = 0; i < 233; i++)
//{
// s += random.Range(0, 10) + ",";
//}
//Debug.Log(s); int seed = (int)System.DateTime.Now.Ticks;
if (bDebug)
{
seed = PlayerPrefs.GetInt("Seed");
}
else
{
PlayerPrefs.SetInt("Seed", (int)System.DateTime.Now.Ticks);
}
Random.InitState(seed);
string s = "";
for (int i = ; i < ; i++)
{
s += Random.Range(, ) + ",";
}
Debug.Log(s);
}
}

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

结果如下:

我们发现每次的随机数都一样,因为它们都源于同一个随机种子,无论之后再随机多少次,结果都是这个随机数序列,这个种子对应的结果已经被计算机固定了,除非种子更改,不然随机结果不会变。

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

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

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

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

public Random(); public Random(int Seed);

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

所以一开始就决定好整个开发过程中用的随机类也不容忽视,建议要么就全部用Unity中的,要么就全部用System中的,这样调整起来自然更得心应手事半功倍。

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

Unity 随机数与随机种子的更多相关文章

  1. [C++基础]随机数,随机种子数

    #include <stdlib.h> #include <iostream> #include <ctime> using namespace std; void ...

  2. [改善Java代码]不要随便设置随机种子

    建议30: 不要随便设置随机种子 随机数在太多的地方使用了,比如加密.混淆数据等,我们使用随机数是期望获得一个唯一的.不可仿造的数字,以避免产生相同的业务数据造成混乱.在Java项目中通常是通过Mat ...

  3. Java Random随机种子

    第一种情况 Random rand = new Random(47); for(int i=0;i<10;i++) System.out.println(rand.nextInt(100)); ...

  4. c++ rand随机数生成(随机种子设置)

    需求:每次初始化不同的随机数 1.默认 //这样用每次都会产生相同数字 #include <stdlib.h> #include <stdio.h> #define N 10 ...

  5. Pytorch随机种子

    最近在做比赛的时候,遇到了一个最好结果,但是之后无论怎样都复现不出来最好结果了.猜测是不是跟Pytorch中的随机种子有关. 训练过程 在训练过程中,若相同的数据数据集,相同的训练集.测试集划分方式, ...

  6. shell 生成指定范围随机数与随机字符串 .

    shell 生成指定范围随机数与随机字符串         分类:             shell              2014-04-22 22:17     20902人阅读     评 ...

  7. random and password 在Linux下生成crypt加密密码的方法,shell 生成指定范围随机数与随机字符串

    openssl rand -hex n (n is number of characters) LANG=c < /dev/urandom tr -dc _A-Z-a-z-0-9 | head ...

  8. BZOJ2296: 【POJ Challenge】随机种子

    2296: [POJ Challenge]随机种子 Time Limit: 1 Sec  Memory Limit: 128 MBSec  Special JudgeSubmit: 114  Solv ...

  9. [BZOJ2296] [POJ Challenge] 随机种子

    Description 1tthinking除了随机算法,其他什么都不会.但是他还是可以ac很多题目,他用的是什么呢?他会选择一个好的随机种子,然后输出答案.往往他选择的一个好的种子可以有99%的概率 ...

随机推荐

  1. [LC] 380. Insert Delete GetRandom O(1)

    Design a data structure that supports all following operations in average O(1) time. insert(val): In ...

  2. MOOC(7)- case依赖、读取json配置文件进行多个接口请求-openpyxl读取excel(14)

    从excel中读取数据 # -*- coding: utf-8 -*- # @Time : 2020/2/12 17:23 # @File : do_excel_openpyxl_14.py # @A ...

  3. 4k高分屏下,chm帮助文档,api文档打开后字体过小的解决

    如图所示: 4k分辨率下,chm文件的正文部分的字体过小,这是这些网页可能使用了CSS维持字体dpi, 在普通分辨率下,可以显示正常,但在高分屏下就会显示得过小,这时我们就需要调整显示网页 的显示效果 ...

  4. [洛谷P4720] [模板] 扩展卢卡斯

    题目传送门 求组合数的时候,如果模数p是质数,可以用卢卡斯定理解决. 但是卢卡斯定理仅仅适用于p是质数的情况. 当p不是质数的时候,我们就需要用扩展卢卡斯求解. 实际上,扩展卢卡斯=快速幂+快速乘+e ...

  5. Java实用教程系列之对象的转型

    体现: 父类的引用可以指向子类的对象接口的引用可以指向实现类的对象转型: 向上转型由子类类型转型为父类类型,或者由实现类类型转型为接口类型向上转型一定会成功,是一个隐式转换向上转型后的对象,将只能访问 ...

  6. 20181026_队测_Brick Game

    题目描述 给出一个\(n\)行\(m\)列的矩阵,矩阵中每个格子有一个非负整数,现在要求你去除其中的个格子,使得剩下的格子中的数的总和最大.另外,去除\(k\)个格子后,剩下的格子必须满足以下几个性质 ...

  7. AI能帮我们造出一个无肉的世界吗?

    AI听起来很遥远,其实已经渗透到我们的日常工作和生活中.在不远的未来,互联网.大数据.硬件的发展和软件的优化,乃至全社会的参与,人工智能将真正从实验室走进生活,它将成为改变我们生活的一部分.我们吃的肉 ...

  8. 数据库事务(Transaction)

    事务(Transaction)是访问并可能更新数据库中各种数据项的一个程序执行单元(unit). 事务四大特性(ACID): 原子性(Atomicity):个事务是一个不可分割的工作单位,事务中包括的 ...

  9. MYSQL对数据库和表的基本操作

    CREATE DATABASE testdb CHARSET=UTF8 创建一个数据库 名字叫做testdb USE testdb; 选择数据库 CREATE TABLE testTable1( ) ...

  10. Spring AOP使用方式

    AOP:全称是Aspect Oriented Programming,面向切面编程 Spring AOP的作用和优势: 作用:在程序运行期间,不修改源码对已有方法进行增强 优势:减少重复代码:提高开发 ...