[c语言编程]俄罗斯方块游戏(…

shape中的color表示形状的颜色,不同的形状有不同的颜色。七种形状及它们旋转后的变形体一共有19种形状,用一个全局数组表示。假定旋转的方向是逆时针方向(顺时针方向道理一样)。shape中的next就表示当前形状逆时针旋转后的下一个形状的序号。例如:第一种形状及其旋 
转变形的形状用结构表示如下。 

□□□□ □□□□ □□□□ □□□□ 
□■□□ □□□□ □■■□ □□□□ 
□■□□ □□■□ □□■□ ■■■□ 
□■■□ ■■■□ □□■□ ■□□□ 

suct shape shapes[19]= 

 
{ 0,-2, 0,-1, 0, 0, 1, 0, CYAN, 1},  
{-1, 0, 0, 0, 1,-1, 1, 0, CYAN, 2},  
{ 0,-2, 1,-2, 1,-1, 1, 0, CYAN, 3},  
{-1,-1,-1, 0, 0,-1, 1,-1, CYAN, 0},  

…… 

  游戏空间指的是整个游戏主要的界面(呵呵,这个定义我实在想不出更准确的,还请哪位大虾指点)。实际上是一个宽10格子、高20格子的 
游戏板。用一个全局数组board[12][22]表示。表示的时候:board[x][y]为1时表示游戏板上(x,y)这个位置上已经有方块占着了,board[x][y] 
为0表示游戏板上这位置还空着。为了便于判断形状的移动是否到边、到底,初始的时候在游戏板的两边各加一列,在游戏板的下面加一行,全 
部填上1,表示不能移出界。即board[0][y],board[11][y](其中y从0到21)初始都为1,board[x][21](其中x从1到10)初始都为1。 
1 2 3 4 5 6 7 8 910 
1□□□□□□□□□□ 
2□□□□□□□□□□ 
3□□□□□□□□□□ 
4□□□□□□□□□□ 
5□□□□□□□□□□ 
6□□□□□□□□□□ 
7□□□□□□□□□□ 
8□□□□□□□□□□ 
9□□□□□□□□□□ 
10□□□□□□□□□□ 
11□□□□□□□□□□ 
12□□□□□□□□□□ 
13□□□□□□□□□□ 
14□□□□□□□□□□ 
15□□□□□□□□□□ 
16□□□□□□□□□□ 
17□□□□□□□□□□ 
18□□□□□□□□□□ 
19□□□□□□□□□□ 
20□□□□□□□□□□ 

  prog6.c演示了用结构表示各种形状的方法。虽然程序稍长一些,但并不是特别复杂。其中游戏板初始化部分并没有真正用到,但是后面的程 
序会用到的。其中SIZE定义为16,这样将整个屏幕的坐标系由原来的640×480转换成40×30(640/16=40,480/16=30)。游戏中所有的坐标都是基于
40×30的坐标系的,这样有助于简化程序。坐标的转换在程序中由DrawBlock(int x,int y)来体现。 
 
 
 
  新的坐标系如下图所示: 
-8-7-6-5-4-3-2-1 0 1 2 3 4 5 6 7 8 910111213141516171819202122232425262728293031 
-4□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□ 
-3□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□ 
-2□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□ 
-1□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□ 
0□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□ 
1□□□□□□□□□■■■■■■■■■■□□□□□□□□□□□□□□□□□□□□□ 
2□□□□□□□□□■■■■■■■■■■□□□□□□□□□□□□□□□□□□□□□ 
3□□□□□□□□□■■■■■■■■■■□□□■■■■□□□□□□□□□□□□□□ 
4□□□□□□□□□■■■■■■■■■■□□□■■■■□□□□□□□□□□□□□□ 
5□□□□□□□□□■■■■■■■■■■□□□■■■■□□□□□□□□□□□□□□ 
6□□□□□□□□□■■■■■■■■■■□□□■■■■□□□□□□□□□□□□□□ 
7□□□□□□□□□■■■■■■■■■■□□□□□□□□□□□□□□□□□□□□□ 
8□□□□□□□□□■■■■■■■■■■□□□□□□□□□□□□□□□□□□□□□ 
9□□□□□□□□□■■■■■■■■■■□□□□□□□□□□□□□□□□□□□□□ 
10□□□□□□□□□■■■■■■■■■■□□□□□□□□□□□□□□□□□□□□□ 
11□□□□□□□□□■■■■■■■■■■□□□□□□□□□□□□□□□□□□□□□ 
12□□□□□□□□□■■■■■■■■■■□□□□□□□□□□□□□□□□□□□□□ 
13□□□□□□□□□■■■■■■■■■■□□□□□□□□□□□□□□□□□□□□□ 
14□□□□□□□□□■■■■■■■■■■□□□□□□□□□□□□□□□□□□□□□ 
15□□□□□□□□□■■■■■■■■■■□□□□□□□□□□□□□□□□□□□□□ 
16□□□□□□□□□■■■■■■■■■■□□□□□□□□□□□□□□□□□□□□□ 
17□□□□□□□□□■■■■■■■■■■□□□□□□□□□□□□□□□□□□□□□ 
18□□□□□□□□□■■■■■■■■■■□□□□□□□□□□□□□□□□□□□□□ 
19□□□□□□□□□■■■■■■■■■■□□□□□□□□□□□□□□□□□□□□□ 
20□□□□□□□□□■■■■■■■■■■□□□□□□□□□□□□□□□□□□□□□ 
21□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□ 
22□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□ 
23□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□ 
24□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□ 
25□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□ 
26□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□ 

  新坐标中最主要的是就是上面两块黑色的部分。左边那块大的就是游戏板(横坐标从1到10,纵坐标从1到20),右边那块小的就是显示
“下一个”形状的部分(横坐标从14到17,纵坐标从3到6)。这个新的坐标系是整个游戏的基础,后面所有的移动、变形等的计算都是基于这个坐标系的。 


游戏中怎么判断左右及向下移动的可能性? 

  看懂了前面的各种形状和游戏板等的表示,接下来的东西就都好办多了。先来看一下某个形状如何显示在游戏板当中。
假设要在游戏板中显示第一个形状。第一个形状在结构中的表示如下: 

suct shape shapes[19]= 

 
{ 0,-2, 0,-1, 0, 0, 1, 0, CYAN, 1}, 

…… 

  那么这个组成形状四个方块的坐标表示为(0,-2)、(0,-1)、(0,0)和(1,0)。这实际上是相对坐标。假形状的实际坐标指的是4x4方块中的第 
二列、第三行的方块的位置,设这个位置为(x,y)。那么组成这个形状的四个小方块的实际坐标(以第一个形状为例)就是(x 0,y-2)、(x 0,y-1)、(x 0,y 0)和(x 1,y 0)。由于所有的形状都可以在4x4的方块阵列中表示,这样就找到了一种统一的方法来表示所有的形状了。 

-1 0 1 2 
-3□□□□ 相对坐标 
-2□■□□ 
-1□■□□ 组成第一种形状的四个方块的相对坐标为(0,-2)、(0,-1)、(0,0)和(1,0)。 
0□■■□ 

让我们看看形状是如何显示在游戏板中的(以第一个形状为例)。 

1 2 3 4 5 6 7 8 910 
1□■□□□□□□□□ 形状的坐标为(2,3)。组成形状的四个方块的坐标由形状的 
2□■□□□□□□□□ 坐标加上这四个小方块各自的相对坐标得出。它们分别是: 
3□■■□□□□□□□ (2 0,3-2)、(2 0,3-1)、(2 0,3-0)和(2 1,3-0)。即: 
4□□□□□□□□□□ (2,1)、(2,2)、(2,3)和(3,3)。如左图所示。 
5□□□□□□□□□□ 
6□□□□□□□□□□ 
7■□□□□□□□□□ 形状的坐标为(1,9)。组成形状的四个方块的坐标分别是: 
8■□□□□□□□□□ (1 0,9-2)、(1 0,9-1)、(1 0,9-0)和(1 1,9-0)。即: 
9■■□□□□□□□□ (1,7)、(1,8)、(1,9)和(2,9)。如左图所示。 
10□□□□□□□□□□ 
11□□□□□□□□□□ 
12□□□□□□□□□□ 
13□□□□□□□□□□ 
14□□□□□□□□□□ 
15□□□□□□□□□□ 
16□□□□□□□□□□ 
17□□□□□□□□□□ 
18□□□□□□□□■□ 形状的坐标为(9,20)。组成形状的四个方块的坐标分别是: 
19□□□□□□□□■□ (9 0,20-2)、(9 0,20-1)、(9 0,20-0)和(9 1,20-0)。即: 
20□□□□□□□□■■ (9,18)、(9,19)、(9,20)和(10,20)。如左图所示。 

  从现在起,我不再举别的示例程序了。从现在开始所有的示例代码均来自于我写的"Russia.c"。为了记录游戏板的状态,用了一个全局数组board[12][22]。board[x][y](其中x从0到11,y从1到21)等于1表示(x,y)这个位置已经被填充了,组成形状的四个方块的坐标都不能为(x,y),否则将发生冲突。board[x][y](其中x从1到10,y从1到20)等于表示(x,y)这个位置还没有被填充。 

  游戏板初始化时,给board[0][y],board[11][y](其中y从1到21)都赋为1,给board[x][21](其中x从1到10)都赋为1。这相当于一开始就给游戏板左右和下方加了个“边”。所有的形状都不能够移入这个“边”,否则将发生冲突。 

  现在我们可以开始讨论如何判断一个形状向左、向右和向下移动的可能性了。先说个概念,“当前形状”是指那个正在下落还没有落到底的那个形状。如果当前形状向左移动,不与游戏板现有状态发生冲突,则可以向左移动。具体做法是:先假设当前形状已经向左移动了,判断此时是否与游戏板现有状态发生冲突。如果不发生冲突,则可以向左移动。否则,不可以向左移动。 

  判断索引号为ShapeIndex的形状在坐标(x,y)是否与游戏板当前状态发生冲突的代码如下。我把详细的说明加在这段代码中。 

enum bool Confilict(int ShapeIndex,int x,int y) 

int i; 

 
for (i=0;i<=7;i ,i )  

 
if (shapes[ShapeIndex].xy[i] x<1 || 
 
 shapes[ShapeIndex].xy[i] x>10) return True; 

 
if (shapes[ShapeIndex].xy[i 1] y<1) continue; 

 
if (board[shapes[ShapeIndex].xy[i] x][shapes[ShapeIndex].xy[i 1] y]) 
return True; 

 
return False; 

对以上代码附加说明如下: 
  shapes[ShapeIndex].xy[i](其中i等于0,2,4,6)表示组成索引号为ShapeIndex的形状的某个方块的x相对坐标。(i等于0时,表示第1个方块的x相对坐标;i等于2时,表示第2个方块的x相对坐标;i等于4时,表示第3个方块的x相对坐标;i等于6时,表示第4个方块的x相对坐标。) 

  shapes[ShapeIndex].xy[i](其中i等于1,3,5,7)表示组成索引号为ShapeIndex的形状的某个方块的y相对坐标。(i等于1时,表示第1个方块的y相对坐标;i等于3时,表示第2个方块的y相对坐标;i等于5时,表示第3个方块的y相对坐标;i等于7时,表示第4个方块的y相对坐标。) 

  shapes[ShapeIndex].xy[i] x(其中i等于0,2,4,6)表示索引号为ShapeIndex的形状的坐标为(x,y)时,组成该形状的某个方块的x实际坐标。(i等于0时,表示第1个方块的x实际坐标;i等于2时,表示第2个方块的x实际坐标;i等于4时,表示第3个方块的x实际坐标;i等于6时,表示第4个方块的x实际坐标。) 

  shapes[ShapeIndex].xy[i] y(其中i等于1,3,5,7)表示索引号为ShapeIndex的形状的坐标为(x,y)时,组成该形状的某个方块的y实际坐 
标。(i等于1时,表示第1个方块的y实际坐标;i等于3时,表示第2个方块的y实际坐标;i等于5时,表示第3个方块的y实际坐标;i等于7时,表示第4个方块的y实际坐标。) 

现在来看看这句是什么意思吧。 
board[shapes[ShapeIndex].xy[i] x][shapes[ShapeIndex].xy[i 1] y] 

可以这样理解,把上面一句分开来看:: 

ActualX=shapes[ShapeIndex].xy[i] x; 
表示某个方块实际的x坐标。 

ActualY=[shapes[ShapeIndex].xy[i 1] y; 
表示某个方块实际的y坐标。 

board[ActualX][ActualY]就是与某个方块坐标相同处的游戏板的标志。如果此标志不为0(为1),表示这个方块与游戏板发生冲突。如果此标志为0,表示这个方块没有与游戏板发生冲突。 

这段写的比较长,但是不是特别难理解。游戏中很多地方都用到了这种相对坐标向实际坐标的转换方式,看懂了这一段对理解其他部分的代码很有帮助。 


仔细看过这段代码后,你可能会提一个问题:不是已经在游戏板的左右两边都加了“边”了吗,为什么还要加下面这个对x坐标的判断呢? 

 
if (shapes[ShapeIndex].xy[i] x<1 || 
shapes[ShapeIndex].xy[i] x>10) return True; 

这是因为有一种特殊情况,如下图所示: 

■■ 
■ 2 3 4 5 6 7 8 910 
1■□□□□□□□□□ 这在当前形状刚出来的时候,是可能发生的。但是我们只给游戏板 
2□□□□□□□□□□ 加了一层“边”。对于这个形状的最左边的那个方块将失去判断, 
3□□□□□□□□□□ 如果不予理会,这个形状将会“挂”在游戏板的左上角!当初我也 
4□□□□□□□□□□ 没有想到这一点,后来发现会有形状“挂”在最顶层,而导致游戏 
5□□□□□□□□□□ 提前退出。发现了这个问题。 
6□□□□□□□□□□ 
7□□□□□□□□□□ 
8□□□□□□□□□□ 加了这个判断后,游戏板的左右两个“边”对冲突的判断就是去意 
 
 
 9□□□□□□□□□□ 义了。因为没有这两个“边”,对于冲突的判断也不会出错。不过 
10□□□□□□□□□□ 为了程序易于理解,还是保留了游戏板的左右两个“边”。 
11□□□□□□□□□□ 
12□□□□□□□□□□ 
13□□□□□□□□□□ 
14□□□□□□□□□□ 
15□□□□□□□□□□ 
16□□□□□□□□□□ 
17□□□□□□□□□□ 
18□□□□□□□□□□ 
19□□□□□□□□□□ 
20□□□□□□□□□□ 

  如果你对我上面提出的新问题及对于这个问题的解释不太明白,没关系,这并不重要。因为现在才刚刚开始,而且刚才所说的这个问题只 
有在特殊情况下才出现(当然,一旦发生上面说的问题,游戏就出错啦!^_^ ),对于理解整个程序的思路影响不大。看多了就会明白了(你 
会说:原来就这么简单!)。 
突,则整个形状在(x,y)处 
与游戏板当前状态冲突 */ 
if (board[shapes[ShapeIndex].xy[i] x][shapes[ShapeIndex].xy[i 1] y]) 
return True; 

 
return False; 

对以上代码附加说明如下: 
  shapes[ShapeIndex].xy[i](其中i等于0,2,4,6)表示组成索引号为ShapeIndex的形状的某个方块的x相对坐标。(i等于0时,表示第1个方块的x相对坐标;i等于2时,表示第2个方块的x相对坐标;i等于4时,表示第3个方块的x相对坐标;i等于6时,表示第4个方块的x相对坐标。) 

  shapes[ShapeIndex].xy[i](其中i等于1,3,5,7)表示组成索引号为ShapeIndex的形状的某个方块的y相对坐标。(i等于1时,表示第1个方块的y相对坐标;i等于3时,表示第2个方块的y相对坐标;i等于5时,表示第3个方块的y相对坐标;i等于7时,表示第4个方块的y相对坐标。) 

  shapes[ShapeIndex].xy[i] x(其中i等于0,2,4,6)表示索引号为ShapeIndex的形状的坐标为(x,y)时,组成该形状的某个方块的x实际坐标。(i等于0时,表示第1个方块的x实际坐标;i等于2时,表示第2个方块的x实际坐标;i等于4时,表示第3个方块的x实际坐标;i等于6时,表示第4个方块的x实际坐标。) 

  shapes[ShapeIndex].xy[i] y(其中i等于1,3,5,7)表示索引号为ShapeIndex的形状的坐标为(x,y)时,组成该形状的某个方块的y实际坐 
标。(i等于1时,表示第1个方块的y实际坐标;i等于3时,表示第2个方块的y实际坐标;i等于5时,表示第3个方块的y实际坐标;i等于7时,表示第4个方块的y实际坐标。) 

现在来看看这句是什么意思吧。 
board[shapes[ShapeIndex].xy[i] x][shapes[ShapeIndex].xy[i 1] y] 

可以这样理解,把上面一句分开来看:: 

ActualX=shapes[ShapeIndex].xy[i] x; 
表示某个方块实际的x坐标。 

ActualY=[shapes[ShapeIndex].xy[i 1] y; 
表示某个方块实际的y坐标。 

board[ActualX][ActualY]就是与某个方块坐标相同处的游戏板的标志。如果此标志不为0(为1),表示这个方块与游戏板发生冲突。如果此标志为0,表示这个方块没有与游戏板发生冲突。 

这段写的比较长,但是不是特别难理解。游戏中很多地方都用到了这种相对坐标向实际坐标的转换方式,看懂了这一段对理解其他部分的代码很有帮助。 


仔细看过这段代码后,你可能会提一个问题:不是已经在游戏板的左右两边都加了“边”了吗,为什么还要加下面这个对x坐标的判断呢? 

 
if (shapes[ShapeIndex].xy[i] x<1 || 
shapes[ShapeIndex].xy[i] x>10) return True; 

这是因为有一种特殊情况,如下图所示: 

■■ 
■ 2 3 4 5 6 7 8 910 
1■□□□□□□□□□ 这在当前形状刚出来的时候,是可能发生的。但是我们只给游戏板 
2□□□□□□□□□□ 加了一层“边”。对于这个形状的最左边的那个方块将失去判断, 
3□□□□□□□□□□ 如果不予理会,这个形状将会“挂”在游戏板的左上角!当初我也 
4□□□□□□□□□□ 没有想到这一点,后来发现会有形状“挂”在最顶层,而导致游戏 
5□□□□□□□□□□ 提前退出。发现了这个问题。 
6□□□□□□□□□□ 
7□□□□□□□□□□ 
8□□□□□□□□□□ 加了这个判断后,游戏板的左右两个“边”对冲突的判断就是去意 
9□□□□□□□□□□ 义了。因为没有这两个“边”,对于冲突的判断也不会出错。不过 
10□□□□□□□□□□ 为了程序易于理解,还是保留了游戏板的左右两个“边”。 
11□□□□□□□□□□ 
12□□□□□□□□□□ 
13□□□□□□□□□□ 
14□□□□□□□□□□ 
15□□□□□□□□□□ 
16□□□□□□□□□□ 
17□□□□□□□□□□ 
18□□□□□□□□□□ 
19□□□□□□□□□□ 
20□□□□□□□□□□ 

  如果你对我上面提出的新问题及对于这个问题的解释不太明白,没关系,这并不重要。因为现在才刚刚开始,而且刚才所说的这个问题只 
有在特殊情况下才出现(当然,一旦发生上面说的问题,游戏就出错啦!^_^ ),对于理解整个程序的思路影响不大。看多了就会明白了(你 
会说:原来就这么简单!)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值