项目开设计(俄罗斯方块)

俄罗斯方块游戏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 俄罗斯方块设计文档

 

第二篇:俄罗斯方块程序设计原理

首先自我介绍下,如果您对我不感兴趣,请直接跳过这段。。我的真名叫李志刚,河南科技大学计算机05级,平时就喜欢写程序。大三上学完了C#,想找点东西练手,就写了这个程序,这是我第一次写超过了2K行的程序。如果您是个程序老手,那么,没必要在这听我罗嗦,我想的很简单,对您没什么参考价值。。但我相信,还是有很多和我一样的学生朋友。我想,全国的二流大学的计算机专业的情况应该是一样的(我真不想再多说,这些抱怨话在大一,大二时应该都讲完了,再说下去就没意思了,走好以后的路才重要),因为我也是这样学校中的一员,我就从没指望靠学校能找到工作。我只有靠自己的实力了,我没得选。。想和我做朋友,

请加我QQ 437004168

好了,不罗嗦了,我把我的设计原理尽量讲清:

首先,俄罗斯方块游戏中的基础是Block(方块),而Block(方块)又是由Brick(砖块)构成,所以,我在程序中就设计了这两个类:Class Block 和 Class Brick。

先说说Class Brick :

private class Brick //方块的基础:砖块..设砖块都为正方形

{

static private int side; //砖块的边长

private int x; //砖块的X坐标

private int y; //砖块的Y坐标

private bool filled; //砖块是否被填满

}

再来看Class Block:以下面的形状为例:

//□■□□

//■■■□

//□□□□

//□□□□

方块Block由一个二维数组4*4来存储形状,方块有极端位置,因为平移,旋转,下移都和这几个极端位置有关

//游戏中的基本元素---方块

private class Block

{

private Brick [,] Shape;//方块是由几个砖块构成的

//方块的最左,最右,下,上,都是一个平面内的点

private Point left; //方块的最左边

private Point right; //方块的最右边

private Point top; //方块的最顶部

private Point bottom; //方块的最底部

private int type; //方块的形状

public Block() //新生成一个方块,随机产生形状 public bool CanMoveDown() //方块是否能继续下移 public bool CanMoveLeft() //方块能左移动 public bool CanMoveRight() //方块能右移动 public bool CanRotate() //方块能否顺利旋转 public void DrawBlock(Graphics g) //画方块

public int GetBrickX(int i, int j)

public int GetBrickY(int i, int j) //查看方块中指定坐标的砖块的位置

public bool IsFilled(int i,int j) //查看方块中指定坐标的砖块是否被填充

public void MoveBlock(int KeyCode) //移动方块

public void MoveToCenterScreen() //把方块平移到屏幕中间,用于居中显示

}

每个方块,都有共同的行为:移动、画方块。

关键在移动方块,有左移、右移、下移、上移(旋转方块),而移动方块时,有要考虑这个方块能否移动,当方块处于边界时不能移,当方块下面有方块了也不能移。所以我就设计了如public bool CanMoveDown() 方法来判断方块能否移动,具体代码看原程序。

设计好这两个类后,这个程序也就基本出来了。

我把游戏的界面分成20*15的单元,用一个二维数组Game[20,15]存储,当Game[i,j]=1,说明这个单元格被一砖块填充。

□□□□□□□□□□□□□□□

□□□□□□□□□□□□□□□

□□□□□□□□□□□□□□□

□□□□□□□□□□□□□□□

□□□□□□□□□□□□□□□

□□□□□□□□□□□□□□□

□□□□□□□□□□□□□□□

□□□□□□□□□□□□□□□

□□□□□□□□□□□□□□□

□□□□□□□□□□□□□□□

□□□□□□□□□□□□□□□

□□□□□□□□□□□□□□□

□□□□□□□□□□□□□□□

□□□□□□□□□□□□□□□

□□□□□□□□□□□□□□□

□□□□□□□□□□□□□□□

□□□□□□□□□□□□□□□

□□□□□□□□□□□□□□□

□□□□□□□□□□□□□□□

□□□□□□□□□□□□□□□

在设计好结构后,再来看看程序的流程:

在事件Form1_Shown中,添加代码,NewGame()即新建游戏,让上面的单元格都为0,即Game[i,j]=0,然后是timer1.Start();

private void Form1_Shown(object sender, EventArgs e)

{

NewGame();

}

下面的代码是最重要的部分:定时器功能,每隔一段时间根据游戏的当前状态对整个屏幕进行重绘 private void timer1_Tick(object sender, EventArgs e)

{

//如果当前的方块不能再往下移动

if (!CurrentBlock.CanMoveDown())

{

for (int i = 0; i < 4; i++)

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

{

if (CurrentBlock.IsFilled(i, j))

Game[CurrentBlock.GetBrickX(i, j), CurrentBlock.GetBrickY(i, j)] = 1;

}

ClearLine(); //消去填满的行

//下一个方块变为当前活动方块

CurrentBlock = NextBlock;

CurrentBlock.MoveToCenterScreen();

//再新产生一个方块作为下一个方块

NextField.Refresh();

NextBlock = new Block();

NextBlock.DrawBlock(NextField.CreateGraphics());

}

if (CurrentBlock.CanMoveDown())

{

GameMainField.Refresh();

FullGameField();

CurrentBlock.DrawBlock(GameMainField.CreateGraphics());

CurrentBlock.MoveBlock(40);

}

if (GameIsOver()) //游戏结束

{

timer1.Stop();

FullGameField();

MessageBox.Show("Game Over");

}

}

如果当前活动的方块不能再住下移动if (!CurrentBlock.CanMoveDown()),就让数组

Game[CurrentBlock.GetBrickX(i, j), CurrentBlock.GetBrickY(i, j)] = 1;

同时消去被填满的行,下一个方块变为当前活动方块,再新产生一个方块作为下一个方块 如果当前活动的方块能再住下移动if (CurrentBlock.CanMoveDown()),让当前活动方块下移一个单元,再重绘游戏界面

如果游戏结束if (GameIsOver())即方块到了最顶层,那么游戏失败了。

整个程序的流程就这样,每部分的功能再看看源码吧。我想的方法比较笨,而且很耗时间,每当方块移动或旋转时,都要遍历数组,你可以自己再想别的办法来实现,我见别人写的这个程序,不到1K行代码就搞定了。

加我QQ吧,我也很想找个有共同爱好的人,多聊聊程序,多编程。。共同进步!!~~

相关推荐