且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

《C++游戏编程入门(第4版)》——2.10 随机数的生成

更新时间:2022-09-15 20:37:50

本节书摘来自异步社区出版社《C++游戏编程入门(第4版)》一书中的第2章,第2.10节,作者:【美】Michael Dawson(道森),更多章节内容可以访问云栖社区“异步社区”公众号查看。

2.10 随机数的生成

C++游戏编程入门(第4版)
不可预测性可以增加游戏的刺激性。无论是RTS游戏中计算机对手策略上的突变还是FPS游戏中从任意一扇门中蹦出的外星生物,玩家都会感到某种程度的意外。随机数的生成是实现这种意外的一种方式。

2.10.1 Die Roller程序简介
Die Roller程序模拟投掷六面骰子。计算机通过生成随机数来进行投掷。程序运行结果如图2.12所示。
《C++游戏编程入门(第4版)》——2.10 随机数的生成

图2.12 骰子的投掷过程是基于程序生成的随机数来模拟的

从Cengage Learning网站(www.cengageptr.com/downloads)上可以下载到该程序的代码。程序位于Chapter 2文件夹中,文件名为die_roller.cpp。

// Die Roller
// Demonstrates generating random numbers

#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
int main()
{
   srand(static_cast<unsigned int>(time(0))); //seed random number generator
   int randomNumber = rand(); //generate random number

   int die = (randomNumber % 6) + 1; // get a number between 1 and 6
   cout << "You rolled a " << die << endl;

   return 0;
}```
###2.10.2 调用rand()函数
程序首先包含了一个新文件:

`#include <cstdlib>`
文件cstdlib包含(除了其他内容以外)处理随机数生成的函数。因为包含了该文件,所以可以***调用其中的函数,包括rand()。主函数中就有该函数的调用:

  ` int randomNumber = rand(); //generate random number`
如第1章所述,函数是能完成某项任务并有一个返回值的一些代码块。我们可以通过在函数名后面加上一对括号来调用函数。如果函数有返回值,该值可以赋给一个变量,如上面代码中的赋值语句所示。rand()的返回值(一个随机数)赋给了randomNumber。

提示
 函数rand()生成从0到至少32767之间的随机数。具体的上界取决于所使用的C++实现。该上界存储在cstdlib定义的常量RAND_MAX中。因此,如果想知道rand()能生成的最大随机数,那么就把RAND_MAX发送给cout。
函数也可以使用传递给它们的数值。在函数名后面的括号中放入这些数值并用逗号隔开,就可以供函数使用。这些值称为实参。在为函数提供这些数值时,通过实参传递给函数。rand()没有接受任何数值传递,因为该函数不需要任何实参。

###2.10.3 为随机数生成器确定种子
计算机基于数学公式生成的是伪随机数,而不是真正的随机数。可以把这想象成让计算机从一本包含许多预定数字的书中读取数字。通过从该书中读取,计算机可以表现得像是在产生随机数序列。

但是这样存在一个问题:计算机总是从书的起始点开始读取数字。因此,计算机在程序中总是产生相同的“随机数”序列。这在游戏中是不希望见到的。例如,我们不希望每次玩游戏时,骰子掷出的都是同样的数字。

这个问题的一个解决方法是当游戏程序开始时,告诉计算机从书的某个任意位置开始读取数字。该过程称为为随机数生成器确定种子。游戏程序员为随机数生成器提供一个叫作种子的数来确定伪随机数序列的起始位置。

下面的代码为随机数生成器确定种子:

  ` srand(static_cast<unsigned int>(time(0))); //seed random number generator`
这行代码看起来相当晦涩,但是它的作用其实很简单。它基于当前日期与时间为随机数生成器确定种子。这样做非常合适,因为当前日期与时间在每次程序运行时都不同。

实际上是函数srand()为随机数生成器确定种子,只需将一个unsigned int型的值作为种子传递给它。这里传递给函数的是time(0)的返回值—— 一个基于当前系统日期和时间的数字。代码static_cast<unsigned int>只是将这个值转换为unsigned int型。现在不必急于弄懂该行所有难懂的地方。如果希望程序在每次运行时生成不同的随机数序列,至少应该在调用rand()前让程序执行一次这一行代码。

提示
 本书不全面解释各种形式的数值类型转换。
###2.10.4 在一定范围内计算
生成随机数后,randomNumber存储的数值在0~32767之间(基于本书所使用的C++实现)。但是我们需要一个1~6之间的数,因此程序用取模运算符生成一个在此范围内的数。

  ` int die = (randomNumber % 6) + 1; // get a number between 1 and 6`
任意正数除以6的余数都在0~5之间。在上面的代码中,程序将余数加1,就有了范围1~6——这正是我们希望的。可以使用这种技术将随机数转换为所需范围内的数。

陷阱
 用取模运算符从随机数生成某个范围内的数的方法可能导致不均匀的结果。范围内某些数可能比其他数更有可能出现。然而,对于简单的游戏,这不是什么问题。