零基础游戏编程:完成重点的游戏逻辑,俄罗斯方块就差不多了

  解决了游戏的显示问题之后,我们就可以大刀阔斧地进行游戏逻辑方面的编程了。完成游戏逻辑方面的内容后,这个游戏算是完成大部分了。俄罗斯方块的游戏逻辑不难,但是涉及的点比较多。我们不要慌,慢慢来。

  一、添加游戏变量

  什么是游戏变量呢?就是游戏各种状态的值,这些值一定要保存在变量中,否则的话,游戏是无法进行下一帧计算的。

  上一节课中,我们已经把背景图片载入了,俄罗斯方块的叠放背景已经有了,这一节课的主要内容,是把俄罗斯方块这个主体添加到窗口中。

  在第5节课中,我们已经把俄罗斯方块的原理粗略地说了一下,其中最关键的,是用二维数组来代表20×10的叠放空间,所以,第一个变量就是叠放空间的二维数组:

  int block[20][10];//10X20的叠放空间

  除了叠放空间之外,俄罗斯方块还需要慢慢下落的正常方块,这些方块是由4个小方块随机组合而成的。在第5节课中,我们本想把俄罗斯方块所有的19种方块情况都画成图片,这个方法是可以的,只不过有点麻烦,还有点费事,所以,我们想要通过计算的方式来绘制这19种情况。

  但是,仔细一想之后发现,4个小方块的随机组合是一个比较复杂的数学问题,想要通过编程来解决,有点浪费时间了。如果是计算10个方块的随机组合,我们只能用算法来解决,因为10个方块的随机组合情况太多,要全部列举出来,工作量太大了。而4个方块的随机组合,只有19种情况,完全就是一个不大不小的工作量。干脆,我们把这19种情况抽象成数组来表示。

  由于是4个方块的随机组合,这个组合后的方块,完全可以放置在一个4×4的空间中,所以,我们就用4×4的二维数组来表示每一个方块。数组中,有方块的地方是1,没方块的地方是0,这样,全部的19种情况也没有多少数据,比绘制19张图片的数据量小多了。具体如何表示一个方块呢?我们可以看下图的例子:

  所以,第二个变量就是所有的方块类型:

  shape FK[7];//总共种情况O I L J T S Z

  shape是一个自己定义的变量类型,这种变量类型叫做结构。结构的格式和C++类的格式差不多,只不过是用struct这个关键字作为前缀。shape的定义如下:

  typedef struct

  {

  revolve state[4];//旋转状态信息

  } shape;

  一般而言,struct和typedef是一起使用的。typedef的是把后面的内容重新定义一个名字,上面语句的意思,是把struct和后面{}括起来的内容重新取个名字,这个名字就叫shape。为什么要这样呢?因为struct是一个整体,也就是说,想要用这个自定义类型去声明变量,需要这样写:

  struct

  {

  revolve state[4];

  } 新变量名称;

  由于有了typedef这个“取名”关键字,struct那一大串的内容,就可以用shape来代表了,再声明新变量的时候,就可以这样写了:

  shape 新变量名称;

  我们再捋一捋。shape代表的是俄罗斯大类的方块,比如S型方块或者是Z型方块。而一个S型方块,并非只有一种状态,它还可以旋转,每次旋转是90°,所以,每种形状都有4种状态,分别是0°、90°、180°和270°的状态。这样,shape中定义的state[4]这个数组的意思就能理解了。

  然后,就是revolve的定义了。由于4种状态是最终的状态,所以,revolve就是一个方块固定形态下需要记录的内容:

  typedef struct

  {

  int len;//该形状宽度

  int h;//该形状高度

  int s[4][4];//代表形状

  } revolve;

  第3行的定义应该能明白,一个4×4的数组来表示每一个固定的方块。原本这样就行了,但是,在游戏的帧计算中,我们需要知道每一个方块的长度和高度。方块的长度和高度是可以计算的,计算起来也不难,但是,这种简单的、不会变化的数学问题,建议不要计算。因为游戏的帧计算是非常宝贵的,不能把大量的CPU时间浪费在这些不需要计算的问题上。很多游戏看起来不大,却对配置要求很高,很可能就是这种无意义的数学计算太多了。我们可以直接记录的,就直接记录。所以,每一种固定状态下的方块,我们都事先记录好它的长度和高度。

  而这个下路方块在叠放空间中是可以移动和旋转的,所以,我们必须记录这个下落方块的位置,这个位置,就以4×4空间的左上角(0,0)点为基准。这样,第三、第四的变量就是下落方块在叠放空间中的坐标:

  int x;

  int y; //当前在叠放空间中的位置

  我们对比其他的俄罗斯方块游戏,发现,界面中一般有两个方块,一个是当前的下落方块,另一个是显示在信息栏中下一个要掉下来的方块。所以,我们需要把这两个方块都记录下来。看我们的shape结构,确定一个固定方块,需要知道方块的大类和当前旋转的情况,所以,这4个变量都是整数:

  int FKType;//当前是什么形状

  int FKRevo;//当前旋转状态

  int NextFKType;//下一个形状

  int NextFKRevo;//下一个旋转状态

  除此之外,我们还有下面几个变量:

  bool isdown;//当前块是否到底

  int delay;

  bool isRevo;//是否按住了旋转键

  int Score;//分数

  bool stop;//暂停

  第一个是一个标记,如果下落方块到底了,它的值就是真;第三个也是标记,如果玩家一直按着旋转键不放,它的值就是真,这个变量的意义,是防止下落方块一个劲地旋转;第四个记录的是游戏分数,目的是让玩家有那么一点点的激情;第五个变量控制游戏是否暂停。

  这里要说一下第二个变量delay。它的作用,是防止玩家控制的下落方块移动过快。由于计算机运行速度快,只要玩家一操作了控制键,下落方块就会响应,出现的情况就是,一按下左右方向键,下落方块因为移动太快,直接移动到了边上。为了防止这种情况发生,delay这个变量,是故意降低移动速度的。

  二、初始化19种方块

  shape是一个结构类型的数组,我们为了计算的方便,把每个方块在4个角度下的状态都记录了下来,记录的方法,就是上面图片中的方法。废话不多说,直接初始化。由于初始化的内容比较多,7个方块,4种旋转状态,总共是28份数据,还是单独写在一个函数里吧。

  首先是Tetris类声明成员函数:

  void Init(void);

  然后在CreateWnd函数中添加Init函数进行方块的初始化:

  bool Tetris::CreateWnd(HINSTANCE hIns, int nCmdShow)

  {

  //这里创建窗口

  //先创建D3D9

  CreateD3D9();

  //初始化方块

  Init();

  SetWindowLong(mhWnd, GWL_USERDATA, (LONG)this);

  return true;

  }

  然后,是Init函数的实现代码:

  void Tetris::Init(void)

  {

  memset(FK,0,sizeof(FK));

  //O字形四种旋转情况一样

  FK[0].state[0].s[0][0] = 1;

  FK[0].state[0].s[0][1] = 1;

  FK[0].state[0].s[1][0] = 1;

  FK[0].state[0].s[1][1] = 1;

  FK[0].state[0].len = 2;

  FK[0].state[0].h = 2;

  FK[0].state[1].s[0][0] = 1;

  FK[0].state[1].s[0][1] = 1;

  FK[0].state[1].s[1][0] = 1;

  FK[0].state[1].s[1][1] = 1;

  FK[0].state[1].len = 2;

  FK[0].state[1].h = 2;

  FK[0].state[2].s[0][0] = 1;

  FK[0].state[2].s[0][1] = 1;

  FK[0].state[2].s[1][0] = 1;

  FK[0].state[2].s[1][1] = 1;

  FK[0].state[2].len = 2;

  FK[0].state[2].h = 2;

  FK[0].state[3].s[0][0] = 1;

  FK[0].state[3].s[0][1] = 1;

  FK[0].state[3].s[1][0] = 1;

  FK[0].state[3].s[1][1] = 1;

  FK[0].state[3].len = 2;

  FK[0].state[3].h = 2;

  //I字形1、两种旋转情况一样,、两种旋转情况一样

  FK[1].state[0].s[0][0] = 1;

  FK[1].state[0].s[0][1] = 1;

  FK[1].state[0].s[0][2] = 1;

  FK[1].state[0].s[0][3] = 1;

  FK[1].state[0].len = 4;

  FK[1].state[0].h = 1;

  FK[1].state[1].s[0][0] = 1;

  FK[1].state[1].s[1][0] = 1;

  FK[1].state[1].s[2][0] = 1;

  FK[1].state[1].s[3][0] = 1;

  FK[1].state[1].len = 1;

  FK[1].state[1].h = 4;

  FK[1].state[2].s[0][0] = 1;

  FK[1].state[2].s[0][1] = 1;

  FK[1].state[2].s[0][2] = 1;

  FK[1].state[2].s[0][3] = 1;

  FK[1].state[2].len = 4;

  FK[1].state[2].h = 1;

  FK[1].state[3].s[0][0] = 1;

  FK[1].state[3].s[1][0] = 1;

  FK[1].state[3].s[2][0] = 1;

  FK[1].state[3].s[3][0] = 1;

  FK[1].state[3].len = 1;

  FK[1].state[3].h = 4;

  //L字形四种情况都不一样

  FK[2].state[0].s[0][0] = 1;

  FK[2].state[0].s[1][0] = 1;

  FK[2].state[0].s[2][0] = 1;

  FK[2].state[0].s[2][1] = 1;

  FK[2].state[0].len = 2;

  FK[2].state[0].h = 3;

  FK[2].state[1].s[0][0] = 1;

  FK[2].state[1].s[0][1] = 1;

  FK[2].state[1].s[0][2] = 1;

  FK[2].state[1].s[1][0] = 1;

  FK[2].state[1].len = 3;

  FK[2].state[1].h = 2;

  FK[2].state[2].s[0][0] = 1;

  FK[2].state[2].s[0][1] = 1;

  FK[2].state[2].s[1][1] = 1;

  FK[2].state[2].s[2][1] = 1;

  FK[2].state[2].len = 2;

  FK[2].state[2].h = 3;

  FK[2].state[3].s[0][2] = 1;

  FK[2].state[3].s[1][0] = 1;

  FK[2].state[3].s[1][1] = 1;

  FK[2].state[3].s[1][2] = 1;

  FK[2].state[3].len = 3;

  FK[2].state[3].h = 2;

  //J字形四种情况都不一样

  FK[3].state[0].s[0][1] = 1;

  FK[3].state[0].s[1][1] = 1;

  FK[3].state[0].s[2][0] = 1;

  FK[3].state[0].s[2][1] = 1;

  FK[3].state[0].len = 2;

  FK[3].state[0].h = 3;

  FK[3].state[1].s[0][0] = 1;

  FK[3].state[1].s[1][0] = 1;

  FK[3].state[1].s[1][1] = 1;

  FK[3].state[1].s[1][2] = 1;

  FK[3].state[1].len = 3;

  FK[3].state[1].h = 2;

  FK[3].state[2].s[0][0] = 1;

  FK[3].state[2].s[0][1] = 1;

  FK[3].state[2].s[1][0] = 1;

  FK[3].state[2].s[2][0] = 1;

  FK[3].state[2].len = 2;

  FK[3].state[2].h = 3;

  FK[3].state[3].s[0][0] = 1;

  FK[3].state[3].s[0][1] = 1;

  FK[3].state[3].s[0][2] = 1;

  FK[3].state[3].s[1][2] = 1;

  FK[3].state[3].len = 3;

  FK[3].state[3].h = 2;

  //T字形四种情况都不一样

  FK[4].state[0].s[0][1] = 1;

  FK[4].state[0].s[1][0] = 1;

  FK[4].state[0].s[1][1] = 1;

  FK[4].state[0].s[1][2] = 1;

  FK[4].state[0].len = 3;

  FK[4].state[0].h = 2;

  FK[4].state[1].s[0][0] = 1;

  FK[4].state[1].s[1][0] = 1;

  FK[4].state[1].s[1][1] = 1;

  FK[4].state[1].s[2][0] = 1;

  FK[4].state[1].len = 2;

  FK[4].state[1].h = 3;

  FK[4].state[2].s[0][0] = 1;

  FK[4].state[2].s[0][1] = 1;

  FK[4].state[2].s[0][2] = 1;

  FK[4].state[2].s[1][1] = 1;

  FK[4].state[2].len = 3;

  FK[4].state[2].h = 2;

  FK[4].state[3].s[0][1] = 1;

  FK[4].state[3].s[1][0] = 1;

  FK[4].state[3].s[1][1] = 1;

  FK[4].state[3].s[2][1] = 1;

  FK[4].state[3].len = 2;

  FK[4].state[3].h = 3;

  //S字形1、和、分别一样

  FK[5].state[0].s[0][1] = 1;

  FK[5].state[0].s[0][2] = 1;

  FK[5].state[0].s[1][0] = 1;

  FK[5].state[0].s[1][1] = 1;

  FK[5].state[0].len = 3;

  FK[5].state[0].h = 2;

  FK[5].state[1].s[0][0] = 1;

  FK[5].state[1].s[1][0] = 1;

  FK[5].state[1].s[1][1] = 1;

  FK[5].state[1].s[2][1] = 1;

  FK[5].state[1].len = 2;

  FK[5].state[1].h = 3;

  FK[5].state[2].s[0][1] = 1;

  FK[5].state[2].s[0][2] = 1;

  FK[5].state[2].s[1][0] = 1;

  FK[5].state[2].s[1][1] = 1;

  FK[5].state[2].len = 3;

  FK[5].state[2].h = 2;

  FK[5].state[3].s[0][0] = 1;

  FK[5].state[3].s[1][0] = 1;

  FK[5].state[3].s[1][1] = 1;

  FK[5].state[3].s[2][1] = 1;

  FK[5].state[3].len = 2;

  FK[5].state[3].h = 3;

  //Z字形1、和、分别一样

  FK[6].state[0].s[0][0] = 1;

  FK[6].state[0].s[0][1] = 1;

  FK[6].state[0].s[1][1] = 1;

  FK[6].state[0].s[1][2] = 1;

  FK[6].state[0].len = 3;

  FK[6].state[0].h = 2;

  FK[6].state[1].s[0][1] = 1;

  FK[6].state[1].s[1][0] = 1;

  FK[6].state[1].s[1][1] = 1;

  FK[6].state[1].s[2][0] = 1;

  FK[6].state[1].len = 2;

  FK[6].state[1].h = 3;

  FK[6].state[2].s[0][0] = 1;

  FK[6].state[2].s[0][1] = 1;

  FK[6].state[2].s[1][1] = 1;

  FK[6].state[2].s[1][2] = 1;

  FK[6].state[2].len = 3;

  FK[6].state[2].h = 2;

  FK[6].state[3].s[0][1] = 1;

  FK[6].state[3].s[1][0] = 1;

  FK[6].state[3].s[1][1] = 1;

  FK[6].state[3].s[2][0] = 1;

  FK[6].state[3].len = 2;

  FK[6].state[3].h = 3;

  //先生成下一个方块

  NextFKType = Ran(7);

  NextFKRevo = Ran(4);

  //初始化设置该变量以便在游戏开始时生成下一个方块

  isdown = true;

  x = 4;

  y = 0;

  memset(block,0,sizeof(block));

  delay = 0;

  isRevo = false;

  Score = 100;

  stop = false;

  }

  举报/反馈