C++俄罗斯方块项目文档

C++俄罗斯方块项目文档

一、           问题描述

   要求支持键盘操作和7种不同类型方块的旋转变换,并且界面上显示操作介绍以及当前的玩家的得分,每消去一行得100分,每完成一个方块整体变换颜色,游戏失败后重新开始。

二、           功能分析

俄罗斯方块游戏需要解决的问题包括:

⑴随机产生七种固定类型方块并自动下移

⑵每次下落一种方块改变颜色

⑶用S键变换方块

⑷用  键和    键左右移动方块

⑸用  键使方块直接下移

⑹判断满行并消行、计分

⑺失败后重新开始

三、程序设计:

1、程序总体设计结构

开始游戏后,进入该程序最核心的部分——处理和实现进行过程中的各种事件和函数。在处理中判断游戏是否失败,如果没有失败,则重新开始游戏。

详解如下:

(1)、游戏方块控制功能。通过各种条件的判断,实现对游戏方块的左移、右移、快速下移、自由下落、旋转功能,以及行满消除行的功能。

(2)、游戏显示更新功能。在判断键值时,有左移、右移、下移、变形旋转键值的判断。当游戏方块左右移动、下落、旋转时,要清除先前的游戏方块,用新坐标重绘游戏方块。当消除满行时,要重绘游戏底板的当前状态,并改变颜色。

                        

 


 

                                                                                                                           

 

      游戏执行主流程图

2、界面设计

   分为左右两个部分:

   *左边为游戏面板

   *右边有两部分:操作提示、计分

3、重要数据的数据结构设计

1、Game

      游戏主类,负责游戏的全局控制。

    2、 MyRect

      方格类,是组成块的基本元素,用自己的颜色来表示块的外观。

    3、FourRects

       块类,由 4 * 4 个方格(MyRect)构成一个块控制块的移动、下落、变形.

    4、BattleField

       画布类,内有<行数> * <列数>个方格类实例。

四、部署方法

 1、config_h

数值   const int XRects=12;

           const int YRects=24;

           const int FieldLeft=60;

           const int FieldTop=60;

           const int SmallRectLength=30;

           const int FourRectsSize=19;

颜色

           const COLORREF FieldBoundColor=RGB(255,0,0);

           const COLORREF FieldLineColor=FieldBoundColor;

           const COLORREF FieldInnerColor=RGB(255,255,255);

           COLORREF RectInnerColor=RGB(0,0,0);

           const COLORREF RectBoundColor=FieldBoundColor;

键盘控制

           const WPARAM LeftKey=VK_LEFT;

           const WPARAM RightKey=VK_RIGHT;

           const WPARAM DownKey=VK_DOWN;

           const WPARAM ChangeKey1= 's';

           const WPARAM ChangeKey2= 'S';

速度控制

           const UINT Speed=300;

变色

           void ChangeRectInnerColor()

             

2、BattleField.h

定义布类 BattleField

布的显示void Show(HDC hdc)

                        

3、MyRect.h

定义类 MyRect

控制块

void DownOneLine()

void LeftOneLine()

void RightOneLine()

                        

4、FourRects.h

块的种类及变化

static const bool PosIndex[config::FourRectsSize][4][4]=

                         {

                            {{0,1,0,0},{0,1,0,0},{0,1,0,0},{0,1,0,0}},

                            {{0,0,0,0},{1,1,1,1},{0,0,0,0},{0,0,0,0}},

                            {{0,0,0,0},{0,1,1,0},{0,1,1,0},{0,0,0,0}},

                            {{0,0,0,0},{1,1,1,0},{0,0,1,0},{0,0,0,0}},

                            {{0,0,0,0},{0,1,1,0},{0,1,0,0},{0,1,0,0}},

                            {{0,0,0,0},{0,1,0,0},{0,1,1,1},{0,0,0,0}},

                            {{0,0,1,0},{0,0,1,0},{0,1,1,0},{0,0,0,0}},

                            {{0,0,0,0},{0,0,1,0},{1,1,1,0},{0,0,0,0}},

                            {{0,0,0,0},{0,1,1,0},{0,0,1,0},{0,0,1,0}},

                            {{0,0,0,0},{0,1,1,1},{0,1,0,0},{0,0,0,0}},

                            {{0,1,0,0},{0,1,0,0},{0,1,1,0},{0,0,0,0}},

                            {{0,1,0,0},{0,1,1,0},{0,0,1,0},{0,0,0,0}},

                            {{0,0,0,0},{0,1,1,0},{1,1,0,0},{0,0,0,0}},

                            {{0,0,0,0},{0,0,1,0},{0,1,1,0},{0,1,0,0}},

                            {{1,1,0,0},{0,1,1,0},{0,0,0,0},{0,0,0,0}},

                            {{0,0,1,0},{0,1,1,0},{0,0,1,0},{0,0,0,0}},

                            {{0,0,0,0},{0,1,1,1},{0,0,1,0},{0,0,0,0}},

                            {{0,0,1,0},{0,0,1,1},{0,0,1,0},{0,0,0,0}},

                            {{0,0,1,0},{0,1,1,1},{0,0,0,0},{0,0,0,0}}

                         };

定义块类 FourRects

块的旋转改变void Change()

                         {

                            ChangePosTable();

                            switch(Type)

                            {

                            case 1:

                                  Type=0;

                                  break;

                            case 2:

                                  Type=2;

                                  break;

                            case 6:

                                  Type=3;

                                  break;

                            case 10:

                                  Type=7;

                                  break;

                            case 12:

                                  Type=11;

                                  break;

                            case 14:

                                  Type=13;

                                  break;

                            case 18:

                                  Type=15;

                                  break;

                            default:

                                  ++Type;

                                  break;

                            }

                            UpdateMeFromPos();

                            return;

                         }

5、Game.h

定义主类class Game

获得分数GetScore()

判断是否产生新块bool IsAdapt(FourRects After)

判断是否行满bool IsLineFull(int LineY)

消行void RemoveLine(int LineY)

随机产生块FourRects RandomFourRects()

开始游戏void Start()

判断是否游戏结束bool IsGameOver()

判断是否能向下一行bool CanDownOneLine()

判断是否能向左一行bool CanLeftOneLine()

判断是否能向右一行bool CanRightOneLine()

判断是否能改变bool CanChange()

下键void OnDownKey()

左键void OnLeftKey()

右键void OnRightKey()

变化键void OnChangeKey()

重新开始void Restart(HWND hwnd)

结束void Over(HWND hwnd)

                        

6、WinMain.cpp

回调LRESULT CALLBACK GameProc

视窗应用接口int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd)

回调LRESULT CALLBACK GameProc

显示部分

sprintf(ScoreString,"你的分数是 %d",Test.GetScore());

TextOut(hdc,right+20,config::FieldTop,ScoreString,strlen(ScoreString));

          TextOut(hdc,right+20,config::FieldTop+100,"向左键向左,向右键向右,S键变形,向下键速下",strlen("向左键向左,向右键向右,S键变形,向下键速下"));

                            Test.Show(hdc);

                            EndPaint(hwnd,&ps);

                            break;

                         case WM_CHAR:

                            hdc=GetDC(hwnd);

键盘控制             switch(wParam)

                            {

                            case config::ChangeKey1:

                            case config::ChangeKey2:

                                  Test.OnChangeKey();

                                  break;

                            default:

                                  break;

                            }

输出及显示         Test.HideOldKeyRects(hdc);

                            Test.ShowKeyRects(hdc);

                            ReleaseDC(hwnd,hdc);

                            UpdateWindow(hwnd);

                            break;

                         case WM_KEYDOWN:

                            hdc=GetDC(hwnd);

键盘控制部分     switch(wParam)

                            {

                            case config::LeftKey:

                                  Test.OnLeftKey();

                                  break;

                            case config::RightKey:

                                  Test.OnRightKey();

                                  break;

                            case config::DownKey:

                                  Test.OnDownKey();

                                  Test.Show(hdc);

                                  break;

                            default:

                                  break;

                            }

                        

五、运行环境

5.1硬件环境:

 CPU:PIII 500以上, 内存:256M

5.2软件环境:

   Visual Studio2005 以上 

六、学习感想

上个学期学习C++的时候,主要是局限于读课本,记住课本上的一些死的知识和概念,编出来的东西也像是“死的”,很少自己亲自动手去编写代码,总是眼高手低,当真正的去编写这个俄罗斯游戏的时候就体会到编写代码,尤其是刚开始的构思的时候,根本智商不足。

直观的感觉俄罗斯方块如此简单,那编程自然轻松。但是细思恐极!想了半天,只是大体知道游戏流程,随着思考的深入,发现游戏是如何运作的却想不明白或是问题不知如何解决,看似简单的步骤都感觉下不了手,然后就陷入了死胡同。还要用编程实现,感觉压力山大。

只是简单地形象化的感受游戏却并不了解本身的机理和抽象的代码运作,所有的想法都只能停留在想法,自己实践起来思路不通,知识不足,错误不断。后来通过上网查询和请教一些编程比较好的同学后才大体了解整个游戏的运行机制,但这只是开始。自己又重新翻阅了一下课本,将课本上的知识又重新细致的复习了一遍,最终借助于网络和同学的帮助,确定了整个程序的步骤。

实施开始比较顺利,但到关键或连接处就暴露出基本功非常糟糕。思前想后开始写了,又发现只是写这一块就各种错误。没学好规范性,代码不忍卒读。更不用说编译测试部分,因为从来没有接触过这类编程,特殊和困难的方法自学结果千差万别,浏览了各种经验帖,才勉强找到能懂的技巧。编译不过家常便饭。最后终于拼出了一个能打开的小游戏。

对于关键技术和算法这一块,感到路漫漫其修远兮。可以想象创造性地设计出可行的思路和算法对于我们还是天方夜谭。在没有熟稔各种编程知识前,通过这个作业发现自己在编程方面还有很大的欠缺,编程能力很差劲,且无创造能力。这都归因于知识面的狭窄,思路的闭塞和实践的懒惰。

不过通过这个实验,也让我对上学期学习的C++知识有了一个很好很全面的复习,这是带给我最大的收获。必须要亲自动手多写才能游刃有余,多看代码才能吸取精华,从而真正的学好C++。

值得一提的是,过去重视编程所完成的作品的直观效果,倾向于华丽的表象,倾向于在一个使用者的角度浅尝辄止,三分钟热度。通过这一次,却发现了代码超越作品的神奇与玄妙,感受到每个程序思想背后的伟大艺术家的智慧。

 

第二篇:项目开设计(俄罗斯方块)

俄罗斯方块游戏JAVA版

项目开发设计文档

Ver 1.0

编制:骆华

                  审核:

20##年8月


一、项目介绍

俄罗斯方块是一款风靡全球的掌上游戏机和PC机游戏, 它造成的轰动与创造的经济价值可以说是游戏史上的一件大事。它由俄罗斯人阿列克谢·帕基特诺夫在1984年6月利用空闲时间所编写的游戏程序,故得此名。俄罗斯方块的基本规则是移动、旋转和摆放游戏自动输出的各种方块,使之排列成完整的一行或多行并且消除得分。它看似简单却变化无穷,俄罗斯方块上手极其简单,但是要熟练地掌握其中的操作与摆放技巧,难度却不低。作为家喻户晓老少皆宜的大众游戏,其普及程度可以说是史上任何一款游戏都无法相比的。相信大多数人都还记得为它痴迷得茶不思饭不想的那个俄罗斯方块的时代。由于俄罗斯方块具有的教学性、动态性与知名度,也经常拿来作为游戏程序设计的练习题材。

二、项目需求

  (一)功能需求

运行游戏后,可选择游戏难度,游戏开始后,由程序随机产生所定义的图形,右上角可看到下一个图形,无操作时,方块竖直缓慢下落,图形在接触障碍物之前,可以由键盘控制向左右下移动,可通过空格键进行翻转,可以进行暂停、重新开始游戏,当某一行被下落的方块填满后消除并计分,难度越大方块下落速度相对提高,另外可修改游戏窗体风格。

(二)性能需求

1、游戏在不同分辨率下应能适应各种分辨率保证不变形。

2、保证游戏性能,在双核512M内存下应能流畅运行。


三、涉及主要知识点

   (一)、开发环境搭建与配置

1、jdk安装配置

2、eclipse安装配置

   (二)、界面布局

1、AWT、Swing

    2、菜单使用

   (三)、游戏方块

   整个游戏中总共7种方块样式,每个方块有4种变形,通过线程控制由4 * 4 个方格构成的一个方块的移动、下落以及变形。

(四)、游戏画布

 通过线程动态改变游戏画布的方格颜色,画布通过检查方格颜色来体现方块的移动

  (五)、事件

   键盘监听事件

  (六)、线程

   整个游戏由线程控制,一轮游戏过程,实现了Runnable接口,一轮游戏是一个大循环,在这个循环中,每隔100毫秒;检查游戏中的当前块是否已经到底了,如果没有,就继续等待。如果到底了,就看有没有全填满的行,如果有就删除它,并为游戏者加分,同时随机产生一新的当前块,让它自动下落。当新产生一个块时,先检查画布最顶上的一行是否已经

被占了,如果是,可以判断Game Over了。

四、概要设计

(采用开发模式、框架、模块划分、模块之间通讯设计等,本项目无)


五、数据库设计

  (概念数据模型、物理数据模型、表、字段、约束、存储过程、函数、视图等,本项目无)


六、界面设计

本游戏只包含1个主界面

     

游戏菜单下包括开始新游戏、设置游戏方块颜色、设置背景颜色、改变游戏难度等。

控制菜单下包括游戏的暂停、继续、重新开始以及停止。

窗体风格菜单下用来设置游戏窗体风格。

关于菜单下包括帮助和游戏简介

七、类设计

本项目包含ErsBlock、ErsBlocksGame、ControlPanel、ErsBox 以及GameCanvas 5个类,具体设计如下:

(一)ErsBlock类

   该类的主要功能为设计游戏方块类,继承自线程类(Thread),由 4 * 4 个方格(ErsBox)构成一个块,控制块的移动、下落、变形等。

  

(二)ErsBlocksGame类

该类的主要功能为游戏主类,继承自JFrame类,负责游戏的全局控制。内含

 1一个GameCanvas画布类的实例引用,

 2一个保存当前活动块(ErsBlock)实例的引用,

3一个保存当前控制面板(ControlPanel)实例的引用;

利用线程实现了一轮游戏过程,一轮游戏是一个大循环,在这个循环中,每隔100毫秒,检查游戏中的当前块是否已经到底了,如果没有,就继续等待。如果到底了,就看有没有全填满的行,如果有就删除它,并为游戏者加分,同时随机产生一个新的当前块,让它自动下落。

当新产生一个块时,先检查画布最顶上的一行是否已经被占了,如果是,可以判断Game Over了。

(三)ControlPanel类

该类的主要功能控制面板类,继承自JPanel.上边安放预显窗口、等级、得分、控制按钮,主

要用来控制游戏进程。

(四)ErsBox类

该类的主要功能是实现方格类,是组成块的基本元素,用自己的颜色来表示块的外观

 

(五)GameCanvas类

该类的主要功能是实现画布类,内有行数 * 列数个方格类实例。继承自JPanel类。 ErsBlock线程类动态改变画布类的方格颜色,画布类通过检查方格颜色来体现ErsBlock块的移动情况。


八、技术难点、解决方案和关键代码

1、游戏方块的旋转和移动

游戏中总共有7种方块类型,每种方块有4种翻转,因此我们在程序中利用点阵图,把4位16进制数按每位换算成一行4位2进制数的结果矩阵,理解为一个4*4的矩阵,比如说0x04e0转换完后就是

0000

0100

1110

0000

其中将0看作空的,1看作方块的块,就可以看出正好是长条形在4*4空间中的横竖切换

/**

        * 方块的样式数目为7

        */

       private final static int BLOCK_KIND_NUMBER = 7;

       /**

        * 每一个样式的方块的反转状态种类为4

        */

       private final static int BLOCK_STATUS_NUMBER = 4;

       /**

        * 分别对应对7种模型的28种状态

        */

       public final static int[][] STYLES = {// 共28种状态

              {0x0f00, 0x4444, 0x0f00, 0x4444}, // 长条型的四种状态

              {0x04e0, 0x0464, 0x00e4, 0x04c4}, // 'T'型的四种状态

              {0x4620, 0x6c00, 0x4620, 0x6c00}, // 反'Z'型的四种状态

              {0x2640, 0xc600, 0x2640, 0xc600}, // 'Z'型的四种状态

              {0x6220, 0x1700, 0x2230, 0x0740}, // '7'型的四种状态

              {0x6440, 0x0e20, 0x44c0, 0x8e00}, // 反'7'型的四种状态

              {0x0660, 0x0660, 0x0660, 0x0660}, // 方块的四种状态

       };

方块的移动分为向左、右和下3个方向,

/**

        * 块向左移动一格

        */

       public void moveLeft() {

              moveTo(y, x - 1);

       }

       /**

        * 块向右移动一格

        */

       public void moveRight() {

              moveTo(y, x + 1);

       }

       /**

        * 块向下落一格

        */

       public void moveDown() {

              moveTo(y + 1, x);

       }

/**

        * 将当前画移动到newRow/newCol所指定的位置

        * @param newRow int, 目的地所在行

        * @param newCol int, 目的地所在列

        * @return boolean, true-移动成功,false-移动失败

        */

       private synchronized boolean moveTo(int newRow, int newCol) {

              if (!isMoveAble(newRow, newCol) || !moving) return false;

              earse();

              y = newRow;

              x = newCol;

              display();

              canvas.repaint();

              return true;

       }

对于方块的翻转,

/**

        * 块变型

        */

       public void turnNext() {

              for (int i = 0; i < BLOCK_KIND_NUMBER; i++) {

                     for (int j = 0; j < BLOCK_STATUS_NUMBER; j++) {

                            if (STYLES[i][j] == style) {

                                   int newStyle = STYLES[i][(j + 1) % BLOCK_STATUS_NUMBER];

                                   turnTo(newStyle);

                                   return;

                            }

                     }

              }

       }

/**

        * 将当前块变成newStyle所指定的块样式

        * @param newStyle int,将要改变成的块样式,对应STYLES的28个值中的一个

        * @return boolean,true-改变成功,false-改变失败

        */

       private boolean turnTo(int newStyle) {

              if (!isTurnAble(newStyle) || !moving) return false;

              earse();

              int key = 0x8000;

              for (int i = 0; i < boxes.length; i++) {

                     for (int j = 0; j < boxes[i].length; j++) {

                            boolean isColor = ((newStyle & key) != 0);

                            boxes[i][j].setColor(isColor);

                            key >>= 1;

                     }

              }

              style = newStyle;

              display();

              canvas.repaint();

              return true;

       }

2、游戏中消行和结束

如果游戏画布中有全填满的行,我们就应该从画布中删除当行,根据最顶行是否被占来判断游戏是否结束。

/**

               * 检查画布中是否有全填满的行,如果有就删除之

               */

              public void checkFullLine() {

                     for (int i = 0; i < canvas.getRows(); i++) {

                            int row = -1;

                            boolean fullLineColorBox = true;

                            for (int j = 0; j < canvas.getCols(); j++) {

                                   if (!canvas.getBox(i, j).isColorBox()) {

                                          fullLineColorBox = false;

                                          break;

                                   }

                            }

                            if (fullLineColorBox) {

                                   row = i--;

                                   canvas.removeLine(row);

                            }

                     }

              }

       

/**

        * 当一行被游戏者叠满后,将此行清除,并为游戏者加分

        * @param row int, 要清除的行,是由ErsBoxesGame类计算的

        */

       public synchronized void removeLine(int row) {

              for (int i = row; i > 0; i--) {

                     for (int j = 0; j < cols; j++)

                            boxes[i][j] = (ErsBox) boxes[i - 1][j].clone();

              }

              score += ErsBlocksGame.PER_LINE_SCORE;

              scoreForLevelUpdate += ErsBlocksGame.PER_LINE_SCORE;

              repaint();

       }

              /**

               * 根据最顶行是否被占,判断游戏是否已经结束了。

               * @return boolean, true-游戏结束了,false-游戏未结束

               */

              private boolean isGameOver() {

                     for (int i = 0; i < canvas.getCols(); i++) {

                            ErsBox box = canvas.getBox(0, i);

                            if (box.isColorBox()) return true;

                     }

                     return false;

              }


九、   参考资料

1、http://www.docin.com/p-304555640.html 俄罗斯方块需求分析

2、http://wenku.baidu.com/link?url=e07lO7mcb4f_tzFRQXveVjow0R4otowq9ROZLL5aoANQBUG3VlE0z0kYpBOuxtowHUISeTL8lepul-hSyDoixn49aWnLq3HBvqsBs-a875S  基JAVA的俄罗斯方块游戏开发

3、http://image.baidu.com/i?ct=503316480&z=&tn=baiduimagedetail&ipn=d&word  多线编程

4、http://wenku.baidu.com/link?url=Hh_oYQfhalWzjDX2HHWbtOCHCTKkAb_1J2RuO2u_oLZNk5eTvEyFD-nneOCKFMIh_iscam9rtu6_aZo1RFiyEb7PdOuozLwwgYU4NLUGuMm 俄罗斯方块设计文档