`
icrwen
  • 浏览: 261213 次
  • 性别: Icon_minigender_2
  • 来自: 济南
社区版块
存档分类
最新评论

用andengine做一款俄罗斯方块游戏

阅读更多
andengine 入门
AndEngine初步
作者: Me 日期: 2010/12/16 发表评论 (1)查看评论
AndEngine 是一个Android平台下基于OpenGL ES的免费(LGPL协议)2D游戏引擎。他的作者是德国人:Nicolas Gramlich

AndEngine 内置了Box2D物理引擎,你可以非常方便的在你的程序里面使用物理效果。



AndEngine相关资料

AndEngine的官方网站:http://www.andengine.org/

他的源码保存在google code 上面你可以从这里下载:http://code.google.com/p/andengine/

AndEngine的Wiki     http://wiki.andengine.org 你想知道AndEngine可以做哪些游戏的话请看:http://wiki.andengine.org/List_of_Apps_and_Games

注:签出引擎代码的时候你可以在你的Eclipse下安装 Mercurial 插件http://www.vectrace.com/mercurialeclipse/(推荐)或者直接下载Mercurial用命令行签出http://mercurial.selenic.com/downloads/ 。

这里是几篇比较有用的AndEngine英文教程(这些教程来自AndEngine论坛  http://www.andengine.org/forums/tutorials/):

Getting started with AndEngine  http://www.andengine.org/forums/tutorials/getting-started-with-andengine-t11.html

Eclipse, AndEngine and HelloWorld!!! http://www.andengine.org/forums/tutorials/eclipse-andengine-and-helloworld-t380.html

Mimminito’s Tutorial List http://www.andengine.org/forums/tutorials/mimminito-s-tutorial-list-t417.html

AndEngine功能强大并且易于使用,作者非常活跃。虽然AndEngine缺少文档,但是作者提供了非常多并且非常全面的示例程序,AndEngine Examples, http://code.google.com/p/andengineexamples/ 。里面把AndEngine的主要功能都做了演示,这些实例程序非常简洁,100多行的程序就能够实例很复杂的功能,虽然同样缺少文档。“好代码本身就是最好的文档。”(Steve McConnell,《代码大全》的作者) 。AndEngine的代码非常清晰。AndEngine缺少官方的手册和规范的文档,但是你可以从AndEngine 的 论坛http://www.andengine.org/forums/ 里面找到许多非常不错的英文教程,如果你遇到问题你可以先用论坛的搜索功能搜索,一般都能够发现别人之前的解决方案,如果你的问题没有得到解决的话,可以在论坛里面提问,论坛的成员和作者都非常热情并且活跃,能够在短时间内解决你棘手的问题。

在使用AndEngine前你需要具备一些Android平台的编程能力,Java的编程能力,这不是必须的,不过如果你不具备的话很可能你会遇到很多困难(你也可以同时学他们)。

下面的我将写如何使用AndEngine:

使用AndEngine写一个简单的程序,你首先得下载AndEngine的源码(http://code.google.com/p/andengine/)这个不是必须的,因为AndEngine引擎的jar 库你可以在 AndEngine Examples里面找到,你下载AndEngine后你可以自己做一些简单的修改,并且可以获取最新的AndEngine更新,建议下载。

下载AndEngine Examples  http://code.google.com/p/andengineexamples/ AndEngine Examples有apk版的,可以直接下载进手机里面运行。这一步对于刚刚接触AndEngine的人是必须的,你可以吧AndEngine Examples 看成是一个非常好的实例教程,AndEngine论坛里的某人曾经说过,他每天都要在手机里面运行 AndEngine Examples里面的所有例子两次以上以学习AndEngine ! 可见 AndEngine Examples 的重要性。


有了AndEngine Examples后,你就新建一个你自己的Android项目,比如HelloWrold,target platform 一般选1.6,建立一个你自己的activity 你可以从他的lib 目录里面找到 andengine.jar.



把andengine.jar 直接复制到你的项目的lib目录下,如果没有lib目录请自己创建一个,然后右键点击刚刚被复制过来的andengine.jar 然后点击Build Path -> Add To Build Path,这样就成功的把AndEngine添加到你的项目中了。

然后将你的HelloWorld.java 换成如下代码:(这里的代码出处是:http://www.andengine.org/forums/tutorials/eclipse-andengine-and-helloworld-t380.html)



public class HelloWorld extends BaseGameActivity {
// ===========================================================
// Constants
// ===========================================================
private static final int CAMERA_WIDTH = 720;
private static final int CAMERA_HEIGHT = 480;
// ===========================================================
// Fields
// ===========================================================
private Camera mCamera;
// ===========================================================
// Constructors
// ===========================================================
// ===========================================================
// Getter & Setter
// ===========================================================
// ===========================================================
// Methods for/from SuperClass/Interfaces
// ===========================================================
@Override
public Engine onLoadEngine() {
this.mCamera = new Camera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);
return new Engine(new EngineOptions(true, ScreenOrientation.LANDSCAPE,new RatioResolutionPolicy(CAMERA_WIDTH, CAMERA_HEIGHT), this.mCamera));
}
@Override
public void onLoadResources() {
}
@Override
public Scene onLoadScene() {
this.mEngine.registerUpdateHandler(new FPSLogger());
final Scene scene = new Scene(1);
scene.setBackground(new ColorBackground(0, 0, 0.8784f));
return scene;
}
@Override
public void onLoadComplete() {
}
// ===========================================================
// Methods
// ===========================================================
// ===========================================================
// Inner and Anonymous Classes
// ===========================================================
}


添加完这段代码之后,别忘了在你项目的AndroidManifest.xml 中添加

<uses-permission android:name=“android.permission.WAKE_LOCK”/>

现在运行这个程序你就可以得到一个蓝色的屏幕,里面除了一个蓝色的背景什么都没有,下面你所要做的是打开Android Examples项目,一面看里面例子的效果,一面看效果的实现代码,相信你很快就能够入门!


游戏描述: 

俄罗斯方块分为多个形状,每次在屏幕的上方的中间随机出现一个形状,然后这个形状以一定的速度向下落。

   下落过程中,可以使用键盘下左右控制方块的移动,按上设为改变方块的形状,当下落在屏幕的底或者碰到屏幕上已经堆积的方块时停下,以它自身的形状加入到屏幕上已堆积的方块 中。

堆积的方块中,若有其中有一行被所有方块填满,则这一行被消除。

当堆积的方块中其中有一列,超过屏幕顶端,则Game Over。

冲突检测:

1、 移动的冲突检测。

1) 当方块分别在最左边、最右边、最下面时,方块不能向左、右、下方移动。

2) 当方块左、右、下方有堆积的方块时,方块不能向左、右、下方移动。

2、 变换的冲突检测。

1) 当方块变换后的状态中存在一个坐标,使得这个坐标越界(超出上下左右边界),那么不能变换(该程序采用恢复到变换前的状态)。

2) 当方块变换后的状态中存在一个坐标,使得这个坐标和已堆积的方块的坐标重合,那么不能变换。

   判断停下:

                1、方块中存在一个坐标在屏幕的底时,则方块停下,加入已堆积的方块中。

                2、方块中存在一个坐标的纵坐标加+1和已经堆积方块中的一个坐标重合,则方块停下,加入已堆积的方块中。

                方块停下后,初始化新的方块。

判断消行:

            当方块停下来的时候,从方块最小纵坐标到最大纵坐标扫描已堆积的方块,若在堆积的方块中这一行已填满,则消除改行所有的方块,且将该行上面的方块向下移动一行。


代码思路提示:
整个场景是一个10*20的长方形空间,你要建立一个10*20的数组int num[20][10]模仿之,你可以想像这是200个小块,每个小块只有0和1两种状态,为0时相应该位置为空白,为1时相应该位置画一个小方块.

每一个图形由4个小方块组成,当他落到底停住时,你就要把数组num的相应数项由0改成1(num数组初始化全为0),同时在OnPaint(可能有时是OnDraw)函数中根据数组的0,1情况重绘,思路就是这样的.很简单


“俄罗斯方块”游戏设计

电子科技大学软件学院03级02班 周银辉
                               转载请注明出处

在这之前:
在这之前对“俄罗斯方块”游戏的编写尝试过两次,很不幸的是,都不理想了,原因是算法设计的不合理导致制作到中期的时候就导致某些实现显得异常复杂而夭折了,这也从侧面反映了开发方法的不合理,没有将“设计”与“生产”分开。这是第三次尝试,一个比较成功的设计,当然其也是建立在前两次失败的基础之上的。但其精彩的算法与漂亮的程序结构足以让人兴奋了。


平台说明:
开发平台    ms.net 2003 & msWindowsXPprofessional_sp2
开发语言    C# 1.0
开发日期    2006-1-9 - 2006-1-12


特殊名词说明:
3.1,象素坐标(Pixel Coordinate):以显示区域所在控件的Client Rectangle的左上角为坐标原点,一个象素点为单位1的坐标

3.2,网格坐标(Gird Coordinate):如果我们将显示区域分成m×n 的网格,那么其中某一个网格所在(列,行)组成的坐标,我们称之为网格坐标,在程序中网格坐标相关项以Gird或g或G开头

3.3,块(Block):一个小格子(比如游戏中的方块图形是由小格子组成的)称为一个块,将由块来组成游戏图形

3.4,形状(Shape):游戏中的由四个小格子组成的图形称为形状

3.5,基础点(Base Point):用于指示目前形状所在位置的一个(列,行)坐标点,以网格坐标为坐标。后面将详细说明其来历。

3.6,动态方块(Dynamic Block):用于构成运动的形状的Block,共4个

设计思想:
4.1,屏幕的组成:
     0 ⒈⒉⒊⒋⒌⒍⒎⒏⒐
  0  □□□□□□□□□□
  1  □□□□□□□□□□
  2  □□□□□□□□□□ 
--------------                     
  3  □□□□□□□□□□
  4  □□□□□□□□□□
  5  □□□□□□□□□□
  6  □□□□□□□□□□
  7  □□□□□□□□□□
  8  □□□□□□□□□□
  9  □□□□□□□□□□
10  □□□□□□□□□□
11  □□□□□□□□□□
12  □□□□□□□□□□
13  □□□□□□□□□□
14  □□□□□□□□□□
15  □□□□□□□□□□
16  □□□□□□□□□□
17  □□□□□□□□□□
18  □□□□□□□□□□
19  □□□□□□□□□□
20  □□□□□□□□□□
21  □□□□□□□□□□
22  □□□□□□□□□□
--------------
23  ■■■■■■■■■■
屏幕由23行10列的网格组成;其中0~2行:初始的形状将在这里形成然后下落,这三行用户不可见;3~22行:游戏显示区域;23行,其标记已到屏幕底部。

4.2,形状的组成:
  每一种形状都是由四个方块组成,比如■■■■由四个方块横向排列而成

4.3,形状的统一:
  ■■■■等共19种形状(旋转前后的形状归为不同的形状),虽然在玩游戏时我们会去将各种不同的形状命不同的命(比如“条子”,“方块”等),但在设计游戏是它们却是统一的,它们都是“形状”。这一点是游戏成功的基础。
     为了使各种不同的形状达到统一的设计,我设计了如下解决方案:将形状始终放在4×4的格子中,以该4×4格子的第一个格子为“基础点”,只要给出组成形状的四个块相对于该基础点的相对坐标,那么在基础点网格坐标的基础上就能求出各块的网格坐标。
★□□□   ★为基础点,形状各块的相对坐标是相对于这个基础点的
□□□□
□□□□
□□□□
那么■■■■在其中就如图:其四个方块相对于基础点的网格坐标就为(0,2)(1,2)(2,2)(3,2)
□□□□
□□□□
■■■■  02122232
□□□□ 
假设基础点的网格坐标是(gX, gY),那么此形状的坐标就为(gX+0,gY+2), (gX+1,gY+2), (gX+3,gY+2), (gX+3,gY+2)
我们将用一个int[8]记录下这四个相对坐标值(呵呵,用byte[8]就可以了哈)
同理:
□□□□
□□□□
■■□□
■■□□  02120313
这样,我们只要知道某个形状的相对坐标值数组,就可以轻松地求出它的各方块的排列方式,也就是其形状(样子)

4.4,移动与旋转的统一
   从上面我们可以看出形状的移动可以这样来实现: 移动基础点的网格坐标,然后组成形状的四个方块按照其与基础点坐标的相对值而改变网格坐标,则表现为移动。
   旋转与移动的原理一样:设旋转前的形状为A,旋转后的形状为B,组成形状A的四个方块按照B(而不是按照A)的相对于基础点坐标的相对值而改变网格坐标,则表现为旋转。
比如,
□□□□
□□□□
■■■■  02122232
□□□□ 
移动: 设其基础点网格坐标为(gX,gY),其各方块当前坐标(gX+0,gY+2), (gX+1,gY+2), (gX+3,gY+2), (gX+3,gY+2)。如果其向左移动一格,那么它的基础了坐标gX-=1; gY=gY; 其各方块移动后坐标 (gX+0,gY+2), (gX+1,gY+2), (gX+3,gY+2), (gX+3,gY+2)。
旋转:设其基础点网格坐标为(gX,gY),其各方块当前坐标(gX+0,gY+2), (gX+1,gY+2), (gX+3,gY+2), (gX+3,gY+2)。如果其旋转一次,旋转后的形状如图
□■□□
□■□□  10111213
□■□□
□■□□
那么其旋转后的各方块坐标 (gX+1,gY+0), (gX+1,gY+1), (gX+1,gY+2), (gX+1,gY+3)

如果我们将各形状编号,比如■■■■编号0,■ 编号1
                                         ■
                                         ■
                                         ■
那么0旋转目标为1,1的旋转目标为0
所以所有形状便得到了统一,如图:
     形状编号_相对坐标_旋转后的形状编号
□□□□
□□□□
■■■■  0_02122232_1
□□□□
 
□■□□
□■□□  1_10111213_0
□■□□
□■□□

□□□□
■□□□
■□□□  2_00010212_3
■■□□

□□□□
□□□□
■■■□  3_02122203_4
■□□□

□□□□
■■□□ 
□■□□  4_01111213_5
□■□□

□□□□
□□■□
■■■□  5_21021222_2
□□□□

□□□□
□□□□
■■□□
■■□□  6_02120313_6

□□□□
□■□□
■■□□  7_11021203_8
■□□□  

□□□□
■■□□
□■■□  8_01111222_7
□□□□

□□□□
□■□□
□■□□  9_11120313_10
■■□□

□□□□
■□□□
■■■□  10_01021222_11
□□□□

□□□□
□■■□
□■□□  11_11211213_12
□■□□

□□□□
□□□□
■■■□  12_02122223_9
□□■□

□□□□
■□□□
■■□□  13_01021213_14
□■□□

□□□□
□■■□  14_11210212_13
■■□□
□□□□

□□□□
□■□□
■■■□  15_11021222_16
□□□□

□□□□
□■□□
□■■□  16_11122213_17
□■□□

□□□□
□□□□
■■■□  17_02122213_18
□■□□                                                                                                                                                     

□□□□
■□□□
■■□□  18_11021213_15 
□■□□


数据结构
5.1,网格坐标:并没有提供这样一个类或结构,但提供了网格坐标与象素坐标的转换公式:
   pCoordinate = gCoordinate*(widthOfGird+distanceOfGird);
   象素坐标值 = 网格坐标值×(网格边长+网格间距)

5.2,网格点(GirdPoint):一个结构,它表示一个以网格坐标为坐标的点
public struct GirdPoint
{
  public int X;
  public int Y;
        //…各种构造函数
}

5.3,块(Block):继承于System.Windows.Forms.Label,用它来生成组成形状的块对象
public class Block: System.Windows.Forms.Label
{
  private GirdPoint gLocation;// 网格坐标
  //获取或设置 网格坐标值(若设置,那么其象素坐标会自动变化为相应值)
         public GirdPoint GLocation
     {
   get
   {
    return this.gLocation;
   }
   set
   {
    this.gLocation = value;
                   //其中Globals.ToPcoordinate()是一个将网格坐标值转换成象素坐标值的函数
    this.Location = new Point( Globals.ToPCoordinate(this.gLocation.X),
     Globals.ToPCoordinate(this.gLocation.Y));
   }
  }
        //……(各种构造函数)
}
对块位置的操作只需要指定其网格坐标,其象素坐标会自动变化为相应值

5.4,形状(Shape):一个结构,由它保存游戏中的形状的相关信息
public struct Shape
{
   //获取或设置该形状的索引值
   public int Index;
   //获取或设置一个数组,其为该形状的相对于基础点的相对网格坐标组
   public int[] DCoordinates;
  // 获取或设置该形状旋转后的新形状的索引
   public int EddiedIndex;
   public Shape(int index, int[] dCoordinates, int eddiedIndex)
   {
       this.Index = index;
       this.DCoordinates = dCoordinates;
      this.EddiedIndex = eddiedIndex;
}

5.5,背景方块数组(BackBlocks):一个Block[,],其维持着23行10列的Block对象,这些对象将作为屏幕的背景,当下落的形状下落到底部时,形状所在位置的背景方块将被点亮,这表现为下落的形状被固定住了

5.6,网格数组(GirdArray):一个int[,],其维持着整个屏幕的背景方块的状态,当该数组中的某个元素值由0变为1时,表示在屏幕中的该位置被某个方块占据,当由1变为0是,表示在屏幕中的该位置被释放而未被占据。被占据表现为屏幕上该位置已经有方块了。

5.7,动态方块数组(DynamicBlocksArray):一个Block[4],其维持着四个Block对象,游戏中的任何运动的形状都是由这四个Block对象变化位置而得到的

5.8,预览网格数组(ViewGirdArray):一个int[,],于GirdArray一样,只不过它维持着屏幕旁边的用于预览下一个即将出项的形状的小屏幕

5.9,预览方块数组(ViewDynamicBlocksArray):一个Block[4],其维持着四个Block对象,游戏中屏幕普遍的小屏幕显示了下一个即将出现的形状,而这个预览形状便是由这四个Block对象变化位置而得到的

5.10,Shapes:一个Shape[19],它维持着游戏中的19个形状
    public static readonly Shape[] Shapes = new Shape[19]
   {
    new Shape(0,new int[8]{0,2,1,2,2,2,3,2},1),
    new Shape(1,new int[8]{1,0,1,1,1,2,1,3},0),
    new Shape(2,new int[8]{0,0,0,1,0,2,1,2},3),
    new Shape(3,new int[8]{0,2,1,2,2,2,0,3},4),
    new Shape(4,new int[8]{0,1,1,1,1,2,1,3},5),
    new Shape(5,new int[8]{2,1,0,2,1,2,2,2},2),
    new Shape(6,new int[8]{0,2,1,2,0,3,1,3},6),
    new Shape(7,new int[8]{1,1,0,2,1,2,0,3},8),
    new Shape(8,new int[8]{0,1,1,1,1,2,2,2},7),
    new Shape(9,new int[8]{1,1,1,2,0,3,1,3},10),
    new Shape(10,new int[8]{0,1,0,2,1,2,2,2},11),
    new Shape(11,new int[8]{1,1,2,1,1,2,1,3},12),
    new Shape(12,new int[8]{0,2,1,2,2,2,2,3},9),
    new Shape(13,new int[8]{0,1,0,2,1,2,1,3},14),
    new Shape(14,new int[8]{1,1,2,1,0,2,1,2},13),
    new Shape(15,new int[8]{1,1,0,2,1,2,2,2},16),
    new Shape(16,new int[8]{1,1,1,2,2,2,1,3},17),
    new Shape(17,new int[8]{0,2,1,2,2,2,1,3},18),
           new Shape(18,new int[8]{1,1,0,2,1,2,1,3},15),
              }


功能的实现:
6.1,形状的生成:随机生成形状编号,并根据该编号生成相应的形状
   设形状编号为indexOfShape,组成形状的四个动态方块已存在DynamicBlockArray中,基础点为BasePoint,所有形状已存在Shapes中
void AssembleShape(int indexOfShape)
{
//重新安排四个动态块的位置以形成新形状
   DynamicBlocksArray[0].GLocation = new GirdPoint(
                  Shapes[indexOfShape].DCoordinates[0] + BasePoint.X,
               Shapes[indexOfShape].DCoordinates[1]+ BasePoint.Y);

   DynamicBlocksArray[1].GLocation = new GirdPoint(
    Shapes[indexOfShape].DCoordinates[2] + asePoint.X,
               Shapes[indexOfShape].DCoordinates[3]+ BasePoint.Y);

   DynamicBlocksArray[2].GLocation = new GirdPoint(
              Shapes[indexOfShape].DCoordinates[4] + BasePoint.X,
              Shapes[indexOfShape].DCoordinates[5] + BasePoint.Y);

   DynamicBlocksArray[3].GLocation = new GirdPoint(
       Shapes[indexOfShape].DCoordinates[6] + BasePoint.X,
       Shapes[indexOfShape].DCoordinates[7] + BasePoint.Y);
}

6.2,形状的移动,先试探能否向指定方向移动如果能,那么移动,否则不移动。试探,由于左右下三个方向移动的本质是一样的,所以它们可以由统一的函数来实现。移动,则是通过移动基础点位置,然后根据基础点位置重新安排一下四个动态方块的位置来实现,即,移动基础点,然后调用一次上面的AssembleShape函数。
     试探:设当前形状编号为indexOfShape,arg参数指定要试探的方向 ’L’,’R’,’D’ 分别为左,右,下;基础点为BascPoint,所有形状已存再Shapes中,CountOfTier为屏幕网格的列数。取得其试探位置的网格坐标,如果该位置已经超出屏幕或该位置已经被其他方块占据,则试探的位置不可达,如果组成形状的四个方块中有一个方块的试探位置不可达,那么试探函数返回false,说明不可向指定方向移动
private static bool canMove(int indexOfShape, char arg)
{
try
{
     GirdPoint tempBasePoint;

     switch(arg)
  {
   case 'L':
       tempBasePoint = new GirdPoint(BasePoint.X-1,BasePoint.Y);
      break;
   case 'R':
       tempBasePoint = new GirdPoint(BasePoint.X+1, BasePoint.Y);
      break;
   case 'D':
    tempBasePoint = new GirdPoint(BasePoint.X, BasePoint.Y+1);
    break;
           case 'E'://不移动,用于判断能否旋转,
                       //与判断移动不同的是传入的indexOfShape,
                       //判断旋转用的是旋转后的形状的索引,而移动用的是当前的
                    tempBasePoint = BasePoint;
                  break;
   default:
    MessageBox.Show("错误的参数"+arg+"\n应该为'L'或'R'或'D'");
    return false;
  }
   
  int gX0 = Shapes[indexOfShape].DCoordinates[0]+tempBasePoint.X;
  int gY0 = Shapes[indexOfShape].DCoordinates[1]+tempBasePoint.Y;
  int i =  GirdArray[gY0,gX0];
            
  int gX1 = Shapes[indexOfShape].DCoordinates[2]+tempBasePoint.X;
  int gY1 = Shapes[indexOfShape].DCoordinates[3]+tempBasePoint.Y;
  int j = GirdArray[gY1,gX1];

  int gX2 = Shapes[indexOfShape].DCoordinates[4]+tempBasePoint.X;
  int gY2 = Shapes[indexOfShape].DCoordinates[5]+tempBasePoint.Y;
  int m = GirdArray[gY2,gX2];

  int gX3 = Shapes[indexOfShape].DCoordinates[6]+tempBasePoint.X;
  int gY3 = Shapes[indexOfShape].DCoordinates[7]+tempBasePoint.Y;
  int n = GirdArray[gY3,gX3];

  //i,j,m,n为其即将到达的新位置的网格值,若为1,说明该网格已被占据 
  if(gX0<0 || gX0>= CountOfTier ||  i == 1 ||
       gX1<0 || gX1>= CountOfTier || j == 1 ||
   gX2<0 || gX2>=  CountOfTier || m == 1 ||
   gX3<0 || gX3>= CountOfTier || n == 1)
  {
   return false;
  }
 
    }
    catch
    {
     return false;
    }

    return true;
}
     移动:移动基础点,并重新组成形状,比如左移,
void ToLeft(int indexOfShape)
{
    if(canMove(indexOfShape,’L’)
    {
  BasePoint = new GirdPoint(BasePoint.X-1, BasePoint.Y);
  AssembleShape(indexOfShape);
    }
}

6.3,自动下落,设一个计时器timer,其每隔一定时间调用一次ToDown函数
void ToDown(int indexOfShape)
{
    if(canMove(indexOfShape,’D’)
    {
  BasePoint = new GirdPoint(BasePoint.X, BasePoint.Y+1);
  AssembleShape(indexOfShape);
    }
    else
    {
        //其他,比如固定下落的形状,判断得分,判断游戏是否已经结束等等
    }
}

6.4,下落形状的固定,当试探到不能下落时,就固定住当前形状。其实组成形状的四个块并没有被固定住,而是当它们当前位置对应的四个背景方块被点亮后,它们离开了当前位置而到屏幕的顶部(0~2行,不可见)组合新形状去了。对应的四个背景方块被点亮表现为形状被固定
void FixShape()
{
for(int i=0; i<4; i++)
{
        //将该位置的背景方块点亮(变为指定的颜色,假设为ColorOfFixedBlock)
       BackBlocks[DynamicBlocksArray[i].GLocation.Y, 
                 DynamicBlocksArray[i].GLocation.X].BackColor = ColorOfFixedBlock;
        //将对应的网格值设置为1,表示此网格已经被占据
       GirdArray[DynamicBlocksArray[i].GLocation.Y, DynamicBlocksArray[i].GLocation.X] = 1
     }
    //其他,比如判断是否应该消行加分等
    //将基础点移动到顶部,并产生新形状等等
}

6.5,旋转,与移动的原理一样。先试探能否旋转,如果能的话,取得旋转后的形状的索引,并按照此索引调用函数AssembleShape()。
    试探能否旋转:很巧妙地运用了试探能否移动的CanMove函数。与移动不同的是,试探能否旋转传入的是旋转后的形状的索引
bool CanEddy(indexOfShape)
    {
       int eddiedIndex = AllShapes.Shapes[indexOfShape].EddiedIndex;//旋转后的新形状的索引
       return canMove(eddiedIndex,’E’);
    }
   旋转:很巧妙地运用了移动时的AssembleShape()
   void Eddy(indexOfShape)
   {
       if(CanEddy(indexOfShape)
       {
          int eddiedIndex = AllShapes.Shapes[indexOfShape].EddiedIndex;//旋转后的新形状的索引
          AssembleShape(eddiedIndex);
       }
   }
这是激动人心的,程序巧妙地将左移、右移、下移、旋转高度地统一了起来,它们的实质都是调用CanMove()函数进行判断,然后再调用AssembelShape()函数移动四个动态方块的位置,而表现出各种效果。多么棒的设计啊(呵呵,自我欣赏了)

6.6,消行,当形状被固定后需要判断是否有满行,如果有,则消去已满的行
    扫描是否存在满行:没有必要扫描整个屏幕,而只需要扫描当前被固定的形状的基础点一下的四行(但不能超过第22行,因为第23行始终是满的)。对于某一行而言,如果该行的GirdArray对应值均为1,说明该行已被全部占据(或者说,如果该行的GirdArray对应值至少有一个为1,说明该行没有被全部占据),那么应该消去该行
    void ScanFullLines(int lineStart,int lineEnd)
    {
          int countOfFullLine = 0;//记录此次消除的行数,以便加分

       for(int i= lineStart; i<=lineEnd; i++)
       {
   bool isFull = true;//指示是否已被填满
   for(int j=0; j<CountOfTier; j++)//countOfTier:屏幕列数
   {
    if(GirdArray[i,j]==0)
    {
     isFull = false;
    }
   }

   if(isFull)
   {
    countOfFullLine++;
    DelLine(i);//消去第i行
          }
  
   switch(countOfFullLine)
              {
                 //根据所消行数加分
              }
       消去指定行:将该行上的背景方块熄灭(与点亮相反,变为另一种颜色,设为ColorOfBackBlock),并将该行以上的所有方块都下移一格。设背景方块数组为BackBlocks,网格数组为GirdArray
void DelLine(int indexOfLine)
{
//熄灭该行上的背景块,表现为移出了该行上的块
for(int i=0; i<CountOfTier; i++)
{
         //熄灭对应的背景方块
  BackBlocks[indexOfLine,i].BackColor = ColorOfBackBlock;
  //释放被占据的网格
  GirdArray[indexOfLine,i] = 0;
     }
//该行以上的所有行整体下落
for(int i=indexOfLine-1;i>=3; i--)
{
  for(int j=0; j<CountOfTier; j++)
  {
   if(GirdArray[i,j] == 1)
   {
    //此块熄灭,其正下方的块点亮,表现为下落了一格
    GirdArray[i,j] = 0;
    BackBlocks[i,j].BackColor =.ColorOfBackBlock;
    GirdArray[i+1,j] = 1;
    BackBlocks[i+1,j].BackColor = ColorOfFixedBlock;
   }
    }
    } 
}

6.7 游戏存档,当用户欲保存当前游戏以供以后继续时,使用这个功能。需要保存的数据有:目前的速度级别,目前的分数,目前的已屏幕上已被固定的各形状。存档文件仅需要一百多个字节就可以了,是这样做的:第一行写入游戏速度级别,第二行写入游戏分数,从第三行开始写入游戏中网格数组(GirdArray),网格数组中为1的对应的背景方块数组的方块即是被固定的(点亮的)。
bool SaveGame(string fileName)
{
try
{
          StreamWriter sWriter =    
              new StreamWriter(fileName,false,System.Text.Encoding.Default, 100);

  //写入速度级别
  sWriter.WriteLine(SpeedLevel.ToString());

  //写入分数
  sWriter.WriteLine(Score.ToString());

  //写入背景网格状态
  for(int i=0; i<CountOfRow; i++)
  {
   for(int j=0; j< CountOfTier; j++)
   {
    sWriter.Write(GirdArray[i,j].ToString());
   }
  }

  sWriter.Close();
}
catch(Exception ex)
{
  MessageBox.Show("未能保存!\n原因是:\n"+ex.Message);
  return false;
}

    return true;
}
比如我的一个游戏存档如下:
1
1500
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000010000000001100000
0001101000110110100111011111111101111111111111111111

6.8 加载游戏存档,将游戏存档读出,并将游戏中的各变量更改为存档中的值就可以了。
       bool LoadGame(string fileName)
       {
   try
   {
    StreamReader sReader =
     new StreamReader(fileName,System.Text.Encoding.Default,false,100);

                  //加载游戏速度
    string strSpeedLevel = sReader.ReadLine();
    Level = int.Parse(strSpeedLevel));

    //加载游戏分数
    string strScore = sReader.ReadLine();
    Score = int.Parse(strScore);
   
    //加载游戏网格状态
    char[] buffer = new char[1];
    for(int i=0; i<CountOfRow; i++)
    {
     for(int j=0; j<CountOfTier; j++)
     {
      sReader.Read(buffer,0,1);
      GirdArray[i,j] = int.Parse(buffer[0].ToString());
                           //如果对应值为1,则点亮对应的背景网格
      if(GirdArray[i,j]==1)
      {
       BackBlocks[i,j].BackColor = ColorOfFixedBlock;
      }
      else
      {
       BackBlocks[i,j].BackColor = ColorOfBackBlock;
      }
     }
    }

    sReader.Close();
   }
   catch(Exception ex)
   {
    MessageBox.Show("加载游戏失败\n原因是:\n"+ex.Message);
    return false;
   }

   return true;
  }


/************************************************/
面向对象课程设计:俄罗斯方块游戏修改版
制作人: 李梦松
时间:2007-5
单位: 湖工工程技术学院 工学系 软件(一)班
/************************************************/
#include<graphics.h>
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <dos.h>
#include <bios.h>
#include <time.h>
//常数定义区
#define UNIT 28
#define ROWS 16
#define COLUMNS 12
//枚举定义区
enum Boolean{false,true};
//全局变量
int speed=9;
int hard=9;
int numrow=0;
Boolean candown;
Boolean over;
//对象定义区
//Location对象:Location是所有图形的基类
class Location
{
protected:
int unit; //所有图形的基本单位长度。
public:
int x,y; //在容器中的逻辑坐标
int left,top; //在屏幕中的物理坐标
int color;
Location(int x,int y,int left,int top,int unit,int color);
};
Location::Location(int x,int y,int left,int top,int unit,int color)
{
this->x=x;
this->y=y;
this->left=left;
this->top=top;
this->unit=unit;
this->color=color;
}
//Brick对象:Brick是一个长方形的图形对象,由它可以组成俄罗斯方块所需要的各种形状。
class Brick:public Location
{
public:
Brick(int x,int y,int left,int top,int unit,int color);
void show();
void hide();
};
Brick::Brick(int x,int y,int left,int top,int unit,int color):Location(x,y,left,top,unit,color)
{ ;}
void Brick::show() //显示Brick
{
int tempColor;
tempColor=getcolor();
setfillstyle(1,color);
bar(left+1,top+1,left+unit-1,top+unit-1);
setfillstyle(0,color);
setcolor(tempColor);
}
void Brick::hide() //隐藏Brick
{
int tempColor;
tempColor=getcolor();
setfillstyle(1,getbkcolor());
bar(left+1,top+1,left+unit-1,top+unit-1);
setfillstyle(0,getbkcolor());
setcolor(tempColor);
}
//Figure对象:在Figure中有一个n*n的矩阵,在相应的位置上放置Brick对象就可以形成组合图形。
//每一种Figure图形由4个Brick对象组成。
class Figure:public Location
{
protected:
int shape;//shape取值0~6,分别代表7种不同的图形。
public:
int state;//state共有0,1,2,3四个值,分别表示图形的四种摆放角度。
int map[4][4][2];//map的下标依次表示:4个状态(state),4个Brick,x和y坐标
Brick *p_bk[4]; //表示组成Figure的4个Brick
Figure(int x,int y,int left,int top,int unit,int color,int shape,int state);
~Figure();
void setBricks();
void show();
void hide();
};
Figure::Figure(int x,int y,int left,int top,int unit,int color,int shape,int state):Location(x,y,left,top,unit,color)
{

this->shape=shape;
this->state=state;

//要想增加更多图形,只需扩充Brick的数目和相应的map的矩阵值。
if(this->shape==0) //长条形
{
map[0][0][0]=0;map[0][0][1]=1;
map[0][1][0]=1;map[0][1][1]=1;
map[0][2][0]=2;map[0][2][1]=1;
map[0][3][0]=3;map[0][3][1]=1;

map[1][0][0]=1;map[1][0][1]=0;
map[1][1][0]=1;map[1][1][1]=1;
map[1][2][0]=1;map[1][2][1]=2;
map[1][3][0]=1;map[1][3][1]=3;

map[2][0][0]=0;map[2][0][1]=1;
map[2][1][0]=1;map[2][1][1]=1;
map[2][2][0]=2;map[2][2][1]=1;
map[2][3][0]=3;map[2][3][1]=1;

map[3][0][0]=1;map[3][0][1]=0;
map[3][1][0]=1;map[3][1][1]=1;
map[3][2][0]=1;map[3][2][1]=2;
map[3][3][0]=1;map[3][3][1]=3;
}
else if(shape==1) //正方形...
{
map[0][0][0]=0;map[0][0][1]=0;
map[0][1][0]=1;map[0][1][1]=0;
map[0][2][0]=0;map[0][2][1]=1;
map[0][3][0]=1;map[0][3][1]=1;

map[1][0][0]=0;map[1][0][1]=0;
map[1][1][0]=1;map[1][1][1]=0;
map[1][2][0]=0;map[1][2][1]=1;
map[1][3][0]=1;map[1][3][1]=1;

map[2][0][0]=0;map[2][0][1]=0;
map[2][1][0]=1;map[2][1][1]=0;
map[2][2][0]=0;map[2][2][1]=1;
map[2][3][0]=1;map[2][3][1]=1;

map[3][0][0]=0;map[3][0][1]=0;
map[3][1][0]=1;map[3][1][1]=0;
map[3][2][0]=0;map[3][2][1]=1;
map[3][3][0]=1;map[3][3][1]=1;
}
else if(shape==2) //T字形...
{
map[0][0][0]=0;map[0][0][1]=1;
map[0][1][0]=1;map[0][1][1]=1;
map[0][2][0]=2;map[0][2][1]=1;
map[0][3][0]=1;map[0][3][1]=2;

map[1][0][0]=1;map[1][0][1]=0;
map[1][1][0]=0;map[1][1][1]=1;
map[1][2][0]=1;map[1][2][1]=1;
map[1][3][0]=1;map[1][3][1]=2;

map[2][0][0]=1;map[2][0][1]=0;
map[2][1][0]=0;map[2][1][1]=1;
map[2][2][0]=1;map[2][2][1]=1;
map[2][3][0]=2;map[2][3][1]=1;

map[3][0][0]=1;map[3][0][1]=0;
map[3][1][0]=1;map[3][1][1]=1;
map[3][2][0]=2;map[3][2][1]=1;
map[3][3][0]=1;map[3][3][1]=2;
}
else if(shape==3) //正Z形...
{
map[0][0][0]=0;map[0][0][1]=0;
map[0][1][0]=1;map[0][1][1]=0;
map[0][2][0]=1;map[0][2][1]=1;
map[0][3][0]=2;map[0][3][1]=1;

map[1][0][0]=1;map[1][0][1]=0;
map[1][1][0]=0;map[1][1][1]=1;
map[1][2][0]=1;map[1][2][1]=1;
map[1][3][0]=0;map[1][3][1]=2;

map[2][0][0]=0;map[2][0][1]=0;
map[2][1][0]=1;map[2][1][1]=0;
map[2][2][0]=1;map[2][2][1]=1;
map[2][3][0]=2;map[2][3][1]=1;

map[3][0][0]=1;map[3][0][1]=0;
map[3][1][0]=0;map[3][1][1]=1;
map[3][2][0]=1;map[3][2][1]=1;
map[3][3][0]=0;map[3][3][1]=2;
}
else if(shape==4) //反Z形...
{
map[0][0][0]=1;map[0][0][1]=0;
map[0][1][0]=2;map[0][1][1]=0;
map[0][2][0]=0;map[0][2][1]=1;
map[0][3][0]=1;map[0][3][1]=1;

map[1][0][0]=0;map[1][0][1]=0;
map[1][1][0]=0;map[1][1][1]=1;
map[1][2][0]=1;map[1][2][1]=1;
map[1][3][0]=1;map[1][3][1]=2;

map[2][0][0]=1;map[2][0][1]=0;
map[2][1][0]=2;map[2][1][1]=0;
map[2][2][0]=0;map[2][2][1]=1;
map[2][3][0]=1;map[2][3][1]=1;

map[3][0][0]=0;map[3][0][1]=0;
map[3][1][0]=0;map[3][1][1]=1;
map[3][2][0]=1;map[3][2][1]=1;
map[3][3][0]=1;map[3][3][1]=2;
}
else if(shape==5) //正L形...
{
map[0][0][0]=1;map[0][0][1]=0;
map[0][1][0]=1;map[0][1][1]=1;
map[0][2][0]=1;map[0][2][1]=2;
map[0][3][0]=2;map[0][3][1]=2;

map[1][0][0]=0;map[1][0][1]=1;
map[1][1][0]=1;map[1][1][1]=1;
map[1][2][0]=2;map[1][2][1]=1;
map[1][3][0]=0;map[1][3][1]=2;

map[2][0][0]=0;map[2][0][1]=0;
map[2][1][0]=1;map[2][1][1]=0;
map[2][2][0]=1;map[2][2][1]=1;
map[2][3][0]=1;map[2][3][1]=2;

map[3][0][0]=2;map[3][0][1]=0;
map[3][1][0]=0;map[3][1][1]=1;
map[3][2][0]=1;map[3][2][1]=1;
map[3][3][0]=2;map[3][3][1]=1;
}
else if(shape==6) //反L形...
{
map[0][0][0]=1;map[0][0][1]=0;
map[0][1][0]=1;map[0][1][1]=1;
map[0][2][0]=0;map[0][2][1]=2;
map[0][3][0]=1;map[0][3][1]=2;

map[1][0][0]=0;map[1][0][1]=0;
map[1][1][0]=0;map[1][1][1]=1;
map[1][2][0]=1;map[1][2][1]=1;
map[1][3][0]=2;map[1][3][1]=1;

map[2][0][0]=1;map[2][0][1]=0;
map[2][1][0]=2;map[2][1][1]=0;
map[2][2][0]=1;map[2][2][1]=1;
map[2][3][0]=1;map[2][3][1]=2;

map[3][0][0]=0;map[3][0][1]=1;
map[3][1][0]=1;map[3][1][1]=1;
map[3][2][0]=2;map[3][2][1]=1;
map[3][3][0]=2;map[3][3][1]=2;
}
int bk_x,bk_y,bk_left,bk_top;
for(int i=0;i<4;i++)
{
bk_x=map[state][i][0];
bk_y=map[state][i][1];
bk_left=left+bk_x*unit;
bk_top=top+bk_y*unit;
p_bk[i]=new Brick(bk_x,bk_y,bk_left,bk_top,unit,color);
}

}
Figure::~Figure()
{
delete(p_bk[0]);
delete(p_bk[1]);
delete(p_bk[2]);
delete(p_bk[3]);
}
void Figure::setBricks()//设置Brick的位置
{
int i;
for(i=0;i<4;i++)
{
p_bk[i]->x=map[state][i][0];
p_bk[i]->y=map[state][i][1];
p_bk[i]->left=left+(p_bk[i]->x)*unit;
p_bk[i]->top=top+(p_bk[i]->y)*unit;
}
}
void Figure::show() //显示Figure
{
for(int i=0;i<4;i++)
{
if (p_bk[i]->top<5) p_bk[i]->hide(); //如果超过容器顶部则把Brick隐藏
else p_bk[i]->show();
}

}
void Figure::hide()//隐藏Figure
{
for(int i=0;i<4;i++)
p_bk[i]->hide();
}
//GameBox对象:GameBox所有组合图形的容器
class GameBox:public Location
{
protected:
int rows;
int columns;
int flag[50][50];
int colors[50][50];
public:
Figure * p_fg;
GameBox(int x,int y,int left,int top,int unit,int color,int rows,int columns);
~GameBox();
void setFigure(int x,int y,int color,int shape,int state);
void show();
void hardBrick();
//以下的几个函数我曾经放到Figure里定义,但发现在这里定义更合理更简单。
Boolean canLeft();
void moveLeft();
Boolean canRight();
void moveRight();
void canDown();
void moveDown();
Boolean canRotate();
void rotate();
void delFigure();
void Over() ;
void hideRow(int rowNum);
void showRow(int rowNum);
void downRow(int rowNum);
void fresh();
void clear();
};
GameBox::GameBox(int x,int y,int left,int top,int unit,int color,int rows,int columns):Location(x,y,left,top,unit,color)
{
this->rows=rows;
this->columns=columns;
p_fg=NULL;
int i,j;
for(i=0;i<rows;i++)
for(j=0;j<columns;j++)
{
flag[j][i]=0;
colors[j][i]=0;
}
}
GameBox::~GameBox()
{
if(p_fg!=NULL) delete(p_fg);
}
void GameBox::setFigure(int x,int y,int color,int shape,int state) //在GameBox中构造新的Figure
{
int fg_left,fg_top;
fg_left=left+x*unit;
fg_top=top+y*unit-1; //此处TOP修改为TOP-1更合理,不然会挡住边框
p_fg=new Figure(x,y,fg_left,fg_top,unit,color,shape,state);
p_fg->setBricks();
}
void GameBox::show()//显示一个GameBox
{
int tempColor;
tempColor=getcolor();
setcolor(color);
rectangle(left-1,top-1,left+columns*unit+1,top+rows*unit+1);
setcolor(tempColor);

if(p_fg!=NULL)
p_fg->show();
}
Boolean GameBox::canLeft()
{
int i;
Boolean can=true;
int gb_x,gb_y;
for(i=0;i<4;i++)
{
gb_x=p_fg->x+p_fg->p_bk[i]->x-1;
gb_y=p_fg->y+p_fg->p_bk[i]->y;
if(gb_x<0)
{
can=false;
break;
}
else if(flag[gb_x][gb_y]>0)
{
can=false;
break;
}
}
return(can);
}
void GameBox::moveLeft()
{
p_fg->hide();
p_fg->x=p_fg->x-1;
p_fg->left=p_fg->left-unit;
p_fg->setBricks();
p_fg->show();
}
Boolean GameBox::canRight()
{
int i;
Boolean can=true;
int gb_x,gb_y;
for(i=0;i<4;i++)
{
gb_x=p_fg->x+p_fg->p_bk[i]->x+1;
gb_y=p_fg->y+p_fg->p_bk[i]->y;
if(gb_x>columns-1)
{
can=false;
break;
}
else if(flag[gb_x][gb_y]>0)
{
can=false;
break;
}
}
return(can);
}
void GameBox::moveRight()
{
p_fg->hide();
p_fg->x=p_fg->x+1;
p_fg->left=p_fg->left+unit;
p_fg->setBricks();
p_fg->show();
}
void GameBox::canDown()
{
int i;
int gb_x,gb_y;
for(i=0;i<4;i++)
{
gb_x=p_fg->x+p_fg->p_bk[i]->x;
gb_y=p_fg->y+p_fg->p_bk[i]->y+1;
if(gb_y>rows-1)
{
candown=false;
break;
}
else if(flag[gb_x][gb_y]>0)
{
candown=false;
break;
}
else candown=true; }
}
void GameBox::moveDown() //下落采用的是隐藏(hide)当前位置的Figure,在下方显示(show)同样的Figure
{
p_fg->hide();
p_fg->y=p_fg->y+1;
p_fg->top=p_fg->top+unit;
p_fg->setBricks();
p_fg->show();
}
Boolean GameBox::canRotate()
{
int i;
Boolean can=true;
int gb_x,gb_y;
int nextState;

nextState=(p_fg->state+1)%4;

for(i=0;i<4;i++)
{
gb_x=p_fg->map[nextState][i][0]+p_fg->x;
gb_y=p_fg->map[nextState][i][1]+p_fg->y;
if(gb_x<0||gb_x>columns-1||gb_y>rows-1)
{
can=false;
break;
}
else if(flag[gb_x][gb_y]>0)
{
can=false;
break;
}
}
return(can);
}
void GameBox::rotate()//变形
{
p_fg->hide();
p_fg->state=(p_fg->state+1)%4;
p_fg->setBricks();
p_fg->show();
}
void GameBox::delFigure() //Figure落下时,释放对该Figure的控制
{
int gb_l,gb_t;
int i,gb_x,gb_y;
for(i=0;i<4;i++)
{
gb_x=p_fg->x+p_fg->p_bk[i]->x;
gb_y=p_fg->y+p_fg->p_bk[i]->y;
flag[gb_x][gb_y]=1;
colors[gb_x][gb_y]=p_fg->color;
}
delete(p_fg);
p_fg=NULL;
}
/**********************************难度设计(pretty lee)************************************************/
void GameBox::clear() //清屏
{
for(int k=0;k<rows;k++)
hideRow(k);
}
void GameBox::hardBrick()//难度函数
{
int tempColor,color;
clear();
tempColor=getcolor();
for (int i=1;i<=9-hard;i++)
for (int j=1;j<=columns-6;j++)
{
int n=random(12);
color=random(15)+1;
flag[n][ROWS-i]=1;
colors[n][ROWS-i]=color;
setfillstyle(1,color);
bar(left+n*unit+1,top+(rows-i)*unit,left+(n+1)*unit-1,top+(rows-i+1)*unit-2);
setfillstyle(0,color);
}
setcolor(tempColor);
}
/**********************************************************************************************/
void GameBox::hideRow(int rowNum)
{
int tempColor;
tempColor=getcolor();
for(int i=0;i<columns;i++)
{
if(flag[i][rowNum]>0)
{
setfillstyle(1,getbkcolor());
bar(left+i*unit+1,top+rowNum*unit,left+(i+1)*unit-1,top+(rowNum+1)*unit-2);
flag[i][rowNum]=0;
colors[i][rowNum]=0;
setfillstyle(0,getbkcolor());
}
}
setcolor(tempColor);
}
void GameBox::showRow(int rowNum)
{
int tempColor;
tempColor=getcolor();
for(int i=0;i<columns;i++)
{
if(flag[i][rowNum]>0)
{
setfillstyle(1,colors[i][rowNum]);
bar(left+i*unit+1,top+rowNum*unit,left+(i+1)*unit-1,top+(rowNum+1)*unit-2);
setfillstyle(0,colors[i][rowNum]);
}
}
setcolor(tempColor);
}
void GameBox::downRow(int rowNum)
{
for(int i=0;i<columns;i++)
{
flag[i][rowNum+1]=flag[i][rowNum];
colors[i][rowNum+1]=colors[i][rowNum];
}
showRow(rowNum+1);
hideRow(rowNum);
}
void GameBox::fresh() //消除满行
{
int i,j,k,flagSum,ni=0;
for(i=rows-1;i>0;i--)
{
do
{
flagSum=0;
for(j=0;j<columns;j++)
flagSum=flagSum+flag[j][i];
if(flagSum>=columns) //如果一排满了则消行
{
for(k=i;k>0;k--)
{
hideRow(k);
downRow(k-1);
}
ni++;
numrow+=ni; //计算得分
if ((numrow-ni)%100>numrow%100) //当numrow+ni后,如果没满100,那么必有(numrow-ni)%100<numrow%100
{
if (speed<=1)
{
if (hard<=1)
{
hard=9;
hardBrick();
}
else
{
hard=hard-1;
hardBrick();
}
speed=9;
}
else speed=speed-1;
}
gotoxy(5, 5);
printf("scores: %d\r",numrow*10);
gotoxy(5,8);
printf("speed : %d\r",10-speed);
gotoxy(5,11);
printf("hard : %d\r",10-hard);
}
}
while(flagSum>=columns);
}
}
void GameBox::Over()
{
int j ;
int i,gb_x,gb_y;
for(i=0;i<4;i++)
{
gb_x=p_fg->x+p_fg->p_bk[i]->x;
gb_y=p_fg->y+p_fg->p_bk[i]->y;
flag[gb_x][gb_y]=1;
colors[gb_x][gb_y]=p_fg->color;
}
for(j=0;j<columns;j++)
{
if (flag[j][0]>0 && flag[j][0]>0 && candown==false)
{
over=true;
break;
}
else over=false;
}
}
int getKey() //系统自带函数bioskey(0)能达到几乎相同的功能
{
union REGS inReg,outReg;
int key_l;
inReg.h.ah = 0;
int86(22, &inReg, &outReg);
key_l=outReg.h.ah;
return(key_l);
}
//函数指针,用来保存原来时间中断函数的句柄。
void interrupt far (*OldIntHandler)(...);
//新的时间中断函数,最多1秒18次。
int ticker=0;
int timeCyc=speed;
Boolean ;
void far interrupt TimeInt(...) //如何让中断程序带参数?
{
ticker=(ticker+1)%timeCyc;
if(ticker==0 && onTime==false)
;
}
回答者: 不爱到爱 - 举人 四级   12-14 20:01
void main()
{
/*********************************欢迎界面(pretty lee)*********************************************/
char p1,p2;
int pp1,pp2;
candown=true;
over=false;
textbackground(WHITE);
gotoxy(30,8);
textbackground(BLUE); //将标题移到标题位置,然后输出标题
cprintf("Welcome to the GAME WORLD\r\n");
gotoxy(40,12);
textbackground(BLACK);
textcolor(WHITE);
cprintf("the speed is\r\n");
gotoxy(53,12);
textcolor(BLACK);
textbackground(WHITE);
cprintf("%d\r\n",10-speed);
gotoxy(40,15);
textbackground(BLACK);
textcolor(WHITE);
cprintf("the hard is\r\n");
gotoxy(53,15);
textbackground(BLACK);
textcolor(WHITE);
cprintf("%d\r\n",10-hard);
gotoxy(51,20);
textbackground(BLACK);
textcolor(WHITE);
cprintf("producer is pretty lee\r\n");
/*调节速度*/
re1: gotoxy(53,12);
textcolor(BLACK);
textbackground(WHITE);
p1=getch();
pp1=p1;
if (p1=='H' && speed<9)
{
speed=speed+1;
cprintf("%d\r\n",10-speed);
goto re1;
}
else if(p1=='P' && speed>1)
{
speed=speed-1;
cprintf("%d\r\n",10-speed);
goto re1;
}
else if(pp1!=13) goto re1;
else
{
textbackground(BLACK);
textcolor(WHITE);
cprintf("%d\r\n",10-speed);
}
/*****************/
/*调节难度*/
re2: gotoxy(53,15);//获得光标时背景变白色
textcolor(BLACK);
textbackground(WHITE);
cprintf("%d\r\n",10-hard);
gotoxy(53,15);//重新定输出位置
p2=getch();
pp2=p2;
if (p2=='H' && hard<9)
{

hard=hard+1;
cprintf("%d\r\n",10-hard);
goto re2;
}
else if(p2=='P' && hard>1)
{

hard=hard-1;
cprintf("%d\r\n",10-hard);
goto re2;
}
else if(pp2!=13) goto re2;
/*****************/
/******************************************************************************************/
int graphdriver = DETECT,graphmode; //初始化图形系统
initgraph(&graphdriver,&graphmode,"d:\\tc3\\bgi");
int key,nextnum1,nextnum2,nextnum3;
srand((unsigned)time(NULL));
timeCyc=speed;
OldIntHandler=getvect(0x1c);
disable();
setvect(0x1c,TimeInt); //中断矢量入口,相当如TIMER控件
enable();
clearviewport ();
/*****************************得分,速度,难度,预测下一个 四功能(pretty lee)*******************************************/
gotoxy(5,5);
printf("scores: 0\n");
gotoxy(5,8);
printf("speed : %d\n",10-speed);
gotoxy(5,11);
printf("hard : %d\n",10-hard);
GameBox gb(0,0,180,10,UNIT,getcolor(),ROWS,COLUMNS),nextgb(0,0,30,220,UNIT,getcolor(),4,4);
gb.show();
nextgb.show();
gb.hardBrick();
gb.p_fg=NULL;
nextnum1=random(15)+1;
nextnum2=random(7);
nextnum3=random(4);
nextgb.setFigure(0,0, nextnum1, nextnum2, nextnum3); //预测下一个Figure
nextgb.p_fg->show();

/*************************************************************************************************/
getch();
;
over=false;
candown=true;
while(true)
{
if(gb.p_fg==NULL)
{

gb.setFigure(5,-2,nextnum1,nextnum2,nextnum3);
gb.p_fg->show();
nextnum1=random(15)+1;
nextnum2=random(7);
nextnum3=random(4) ;
nextgb.p_fg->hide();
delete(nextgb.p_fg); //此处要释放nextgb.p_fg,不然会出现BUG
nextgb.setFigure(0,0, nextnum1, nextnum2, nextnum3);
nextgb.p_fg->show();
}
if(bioskey(1)) //kbhit()和bioskey(1)都是瞬间取值函数,有同样的效果。
{
key=getKey(); //系统自带函数bioskey(0)能达到几乎相同的功能
if(key==0x1c) // Return 暂停(继续游戏)
{
onTime=~onTime ;
stop: key=getKey();
if(key==0x1c) onTime=~onTime ;
else if(key==0x01 ){
onTime=~onTime ;
goto esc;}
else goto stop;
}
else if(key==0x01 )//ESC
{
esc:delete(gb.p_fg);
gb.p_fg=NULL;
clearviewport ();
closegraph ();//关闭图形系统
}
else if(key==0x4b )//LEFT
{
if(gb.canLeft())
gb.moveLeft();
}
else if(key==0x4d)//RIGHT
{
if(gb.canRight())
gb.moveRight();
}
else if(key==0x50 )//DOWN
{ gb.canDown();
if(candown==true)
gb.moveDown();
}
else if(key==0x48)//UP
{
if(gb.canRotate())
gb.rotate();
}
}

if(onTime==true)
{
gb.canDown();
if(candown==true)
{
gb.moveDown();
}
else
{
gb.Over();
if (over==false)
{
gb.delFigure();
gb.fresh();
timeCyc=speed;

}
else
{
gotoxy(40,12);
printf("GAME OVER!");
}
}
;
}
}
disable();
setvect(0x1c, OldIntHandler);
enable();
closegraph();
}

由于代码太长一个号发不完,这是我的小号发的
请把分加给不爱到爱那个号上.
以上代码是我自己写的,绝对可用
我的QQ空间里也有这代码 qq:252529013
回答者: lmslovegj - 试用期 一级  12-14 20:07
前 言
visual basic继承了basic语言易学易用的特点,特别适合于初学者学习windows系统编程。随着21世纪信息社会的到来,计算机在人们的工作和生活中的深入,要求我们越来越多地与计算机打交道,为了使用户在繁忙的日程工作中得到放松,于是出现了各种各样的休闲软件,如聊天工具,游戏等等。于是我们小组着手设计开始一个这样的游戏软件。通过这学期来Visual Basic的学习,我初步掌握了Visual Basic语言的最基本的知识,下动手用Visual Basic编写俄罗斯方块游戏。
我们之所以选择开发俄罗斯方块游戏,无可争议,《俄罗斯方块》是有史以来最伟大的游戏之一。 在曾经发布过的所有游戏中,《俄罗斯方块》还被认为是仅有的一个能够真正吸引广泛人群的作品。谁能说清楚,迄今为止人们究竟花了多少万个小时在这个游戏上?也许这些时间本来可以被花在更具生产力的活动上。某些批评家也许会声称,《俄罗斯方块》要比过去二十年间出现的任何东西都要浪费人们的时间。至于我们,则要欣然提名它为GameSpot评选出的历史上最伟大游戏之一。
为了怀念经典,也为了能够给大多的计算机用户在工作之余找到一个休闲、娱乐的一个方式,我们小组开始着手用VB语言开发一个经典的俄罗斯方块游戏。
工程概况
2.1 项目名称
俄罗斯方块游戏
2.2 设计平台
VB 全称Visual Basic,它是以Basic语言作为其基本语言的一种可视化编程工具。
Vb是microsoft公司于1991年退出的windows应用程序开发工具visual意思是“可视化的”。在它刚推出来时,自身还存在一些缺陷,功能也相对少一些。但是经过多年的开发研究。最近microsoft公司又推出了VB6.0版本
VB6.0运行环境:硬件,要求486以上的处理器、16MB以上内存,50MB 以上的硬盘,cd-rom驱动器,鼠标。软件:要求windows 95以上版本。
2.3程序设计思想
游戏是用来给大家娱乐的,所以要能在使用的过程中给大家带来快乐,消除大家的疲劳,所以我们在游戏中添加了漂亮的场景和动听的音乐,设置了过关升级的功能,激发大家的娱乐激情。
从游戏的基本玩法出发,主要就是俄罗斯方块的形状和旋转,我们在设计中在一个图片框中构造了一个4*4的网状小块,由这些小块组合成新的形状,每四个小块连接在一起就可以构造出一种造型,因此我们总共设计了7中造型,每种造型又可以通过旋转而变化出2到4种形状,利用随机函数在一个欲览窗体中提前展示形状供用户参考,然后将展示的形状复制到游戏窗体中进行摆放,在游戏窗体中用户就可以使用键盘的方向键来控制方块的运动,然后利用递归语句对每一行进行判断,如果有某行的方块是满的,则消除这行的方块,并且使上面的方块自由下落,其中,方块向下的速度是有时钟控件控制的,在游戏中,用户也可以使用向下键加快下落速度,定义一个变量,对消除的函数进行记录,最后就可以得出用户的分数,用if 语句对分数判断,达到一定的积分就可以升级到下一个档次。
俄罗斯方块游戏设计的主要步骤为以下10个方面:
(1)游戏界面的设计。
(2)俄罗斯方块的造型。
(3)俄罗斯方块的旋转。
(4)俄罗斯方块的运动情况(包括向左,向右和向下)。
(5)俄罗斯方块的自动消行功能。
(6)游戏级别的自由选择。
(7)游戏速度的自由选择。
(8)游戏得分的计算。
(9)游戏菜单选项的设计及功能实现。
(10)游戏的背景音乐及特效。
2.4运用的控件和主要对象
我们在设计过程中主要用到的控件有:command控件,image控件,picture控件,label控件,timer控件,text控件,windows media player控件等等。
2.5主要实现的功能
我们开发的俄罗斯方块游戏,主要实现了以下几种功能:
1.可以灵活控制方块在图形框中运动。
2.游戏过程中方块可以自由旋转。
3.当某一行的方块排列满时,将自动将这一行方块消除,然后将上面所有方块向下移动,可以支持连续消行。
4.游戏前可以选择游戏的速度和游戏的等级,游戏速度既为方块下落速度,游戏等级为初始游戏时在基层随机生成一定行数的无规律方块,生成的行数由你来选择,每行至少产生5个以上的无规律方块,这样增加了游戏难度,对于游戏高手来说,无疑不是一个新的挑战。
5.游戏的得分支持积分,并且按照公式:
得分 = 原来分数+ 100 * (2 ^ 同时消除的行数-1)
这样,你同一时间消除的行数越多,你的得分也就越高,当游戏积分到了一定时可以自动升级,这个升级指速度升级。
6.游戏中提供了一个漂亮的场景和动听的音乐,给你带来无限激情。
2.6开发人员
由于这次课程设计所选的题目太复杂,而时间又比较紧张,指导老师建议和同学分工完成。我们小组成员包括组长孙磊周,副组长邹海星,此游戏由我们两个人共同开发而成。
正文
3.1游戏设计的具体实现
在我们两个人共同努力下,此次设计,终于能够圆满完成。由于时间的紧促,在设计中,也许会有一些考虑不周之处,但其功能已经能够满足大多用户的需求,相信假以时日,一定能做出一个更经典,更完美的俄罗斯方块游戏,下面我们将对每一步的具体如何实现展示给大家。
3.1.1游戏界面的设计和背景音乐及特效的实现
俄罗斯方块游戏主要由两个界面构成,登陆界面和开始游戏界面,在登陆界面中我们可以首先看到圣诞节的晚上飘梅花的场景,梅花从窗体顶部做函数曲线的下落运动,在窗体中定义一个Image控件组,在通用中定义梅花X坐标变量动态数组,Y坐标变量动态数组,步距X的变量动态数组,步距Y的变量动态数组,以及振幅变量动态数组。然后在窗体form_load中可以定义梅花的数量,利用随机函数产生随机的梅花坐标,步距和振幅,Image控件在运行时候就调用梅花图片,Image控件就可以由时钟控件控制下落速度,可以自由调节,梅花按snow(i).Left = xp(i) + am(i) * Sin(dx(i))函数在做纵向的正玄函数轨迹运动,竖直方向上为自由下落运动,,有am(i)来控制梅花的左右移动振幅。因此,我们就可以看到一个梅花在空中自由飘舞的画面了。
背景画面是用photoshop软件处理的漂亮图案,原本画面中的动画效果都是由Image控件制作的,还有点击进入游戏的按钮是由Label控件实现的,因为Image控件没有置前置后功能,不能将下雪的场景体现完整性,所以将这些图案全部放在背景上,不影响雪花飘落的效果,当点击画面的时候一样可以进入游戏界面。
游戏的背景音乐是由一段代码调用系统播放器Windows Player播放背景音乐,由于本次设计主要是针对游戏如何设计的,所以在这里就不对播放背景音乐的功能做介绍了。
3.1.2俄罗斯方块的造型
相信朋友们都玩过俄罗斯方块,对这个游戏的玩法和方块形状都比较熟悉。我们这个游戏只选择了最基本的7中造型,包括长条型,正方型,正S型,反S型,正7型,反7型,T型。如果需要我们可以添加更多的造型。将游戏界面的游戏区图片框分割成10*20的小块,每个小块放置一个command控件,预览区图片框按同样比例分割成4*4的小块,同样有command控件构成,我们可以把预览区图片框看作是从游戏区图片框中选取的一个部分,游戏区的小方块编号和欲览区编号如下图:
0 1 2 3 4 5 6 7 8 9
… … … … … … … … … …
… … … … … … … … … …
90 91 92 93 94 95 96 97 98 99
3 4 5 6
13 14 15 16
23 24 25 26
33 34 35 36
游戏区编号 欲览区编号
利用Select将方块的7中造型列出,比如长条型的设计,在欲览区中分别有3.4.5.6和5.15.25.35四个方块构成两中形态,用数组为:
m(0) = 3: m(1) = 4: m(2) = 5: m(3) = 6: situation2 = 0
m(0) = 5: m(1) = 15: m(2) = 25: m(3) = 35: situation2 = 1
将它的形状编号为0和1,在后面方便调用,其他的方块造型同样的方法。
3.1.3俄罗斯方块的旋转
俄罗斯方块的旋转主要将方块的位置加以变换得到的,例如上述范例,长条型有两中样式,根据小方块的编号变动来实现整个造型的旋转,比如:
If n(0) - 18 >= 2 And n(3) + 9 <= 198 Then
If cmdfang(n(0) - 18).Visible = False And _
cmdfang(n(1) - 9).Visible = False And _
cmdfang(n(3) + 9).Visible = False Then
hidefang 0
n(0) = n(0) - 18
n(1) = n(1) - 9
n(3) = n(3) + 9
showfang 0
situation = 1
End If
End If
方块的造型在旋转的时候存在一个公式,当然首先要判断是否满足旋转的要求,以上是一个长条型由横着变成竖立状态的旋转,我们以它的造型中的第三个小方块n(3)为中心旋转,这样,在开始运动的时候,长条形要发生旋转最少要运动到第三行,才能由横着变成竖立状态,游戏区图形框中第三行的第一个方块的编号为20,所以长条造型的第一个小方块的编号n(0)必须要大于20。同样,长条型方块在下落到底部的时候也有限制。如果长条下落到最后一行也将无法由横着变成竖立状态。
3.1.4如何实现方块的运动和自动消除满行的方块
我们的这个俄罗斯方块游戏主要是利用command控件的visible属性完成效果的,其实在游戏区图形框可以看成是由许多的command小方块组成,方块运动的过程就是造型里方块显示或者隐藏,就像现在的霓虹灯效果一样,由时钟控件控制visible属性改变的速度,上一层的消失,下一层的显示,这样,从视觉效果可以看到方块的下落运动效果。
方块在下落的过程中会自动判断每一行方块的visible属性,如果全部为true时,就会将这一行小方块的visible属性全部变成false,在将上面的小方块向下移动,利用for语句进行循环判断,将所有这样情况的行改变小方块visible属性。当有多行同时出现这样情况时使用递归调用,实现连续消行。具体程序代码如下:
For i = 190 To 10 Step -10
If cmdfang(i).Visible = True And _
cmdfang(i + 1).Visible = True And _
cmdfang(i + 2).Visible = True And _
cmdfang(i + 3).Visible = True And _
cmdfang(i + 4).Visible = True And _
cmdfang(i + 5).Visible = True And _
cmdfang(i + 6).Visible = True And _
cmdfang(i + 7).Visible = True And _
cmdfang(i +.Visible = True And _
cmdfang(i + 9).Visible = True Then
For j = i + 4 To i Step -1
t = 1
cmdfang(j).Visible = False
cmdfang(2 * i + 9 - j).Visible = False
For k = 1 To 4000
DoEvents
Next
t = 0
Next
linenum = linenum + 1
For j = i - 1 To 0 Step -1
If cmdfang(j).Visible = True Then
cmdfang(j).Visible = False
cmdfang(j + 10).Visible = True
End If
Next
clearline '为了实现连消数行,这里使用递归调用
End If
Next
3.1.5游戏速度和游戏级别自由选择
游戏速度的自由选择无非就是改变时钟控件的频率,我们在菜单中添加了选择速度的功能,还有添加了考验功能,将欲览窗中的方块造型隐藏,给玩家提高了难度,如果你不愿意接受考验也可以点击显示还原成原来状态。
游戏级别的自由选择是让用户选择游戏开始时候,游戏区底部出现一定行数的随机方块,同样给玩家增加了难度,功能代码如下:
For i = 19 To 20 - Val(txthard.Text) Step -1
For j = i * 10 To i * 10 + 9
If Rnd >= 0.5 Then cmdfang(j).Visible = True
Next
Next
可以根据你选择的难度系数在底层的每一行随机产生超过半数(即5个以上)以上的小方块,这样适合喜欢高难度的玩家。
3.1.6游戏得分的计算和游戏菜单的编辑
游戏得分的计算主要是根据消除的行数来决定的,当然每一次同时消除的行数不一样,每一行的得分也不一样,如果你每次消除的行数为1,则最后得分是100分,如果同时消除2行,则最后得分是300分,同时消除3行,得分为700分,同时消除4行,得分为1500分,这由公式:得分 = 原来分数+ 100 * (2 ^ 同时消除的行数-1)。
游戏的编辑,读者可以参照下面的功能介绍。
3.2 游戏功能的介绍
文件-------开始:开始游戏。
继续:继续游戏。
暂停:暂时停止游戏,点击继续的时候可以继续游戏。
退出:退出游戏。
设置-------选择游戏级别。
选择游戏速度。
考验-------显示:显示欲览去方块。
隐藏:隐藏欲览去方块。
帮助-------操作提示以及版本信息和作者资料。





用户界面具体如图:

图—登陆界面 图—游戏界面

图—菜单编辑界面 图—游戏帮助界面
有关说明
经过两个多星期的
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics