单机游戏直接【开始】 就行了
2.联机游戏 ① A 方游戏玩家建立游戏服务器端等待另一方加入游戏后才可以开始游戏 ②B方通过A方的IP加入A方创建的游戏,加入成功后B方能看到加入成功的提示 ③B方加入成功后A方看到B方已经加入了游戏,此时A方可以【开始】游戏,这样A B方就能同步地玩游戏了
游戏用例图
游戏用例图
v整个游戏要实现的功能
类图 (聚合关系)
类图(继承实现关系)
设计要点
矩阵-->游戏界面
private int[ ][ ] m_nField 10列 20行
类图 (聚合关系)
类图(继承实现关系)
设计要点
v实现两机互连
v矩阵à游戏界面
v方块移动和变形算法
v键盘控制
v游戏同步
v游戏网络协议
v菜单控制
实现两机互连
vMyServer类:继承的Thread类,在线程的Run()方法里面建立ServerSocket进行监听,这样做的目的是在没有客户连接请求时可以做其它的事情.负责接受客户端请求的套接字,然后获得按受到的套接字的输入流和输出流.输入流的读取交给了ReadThread线程.ReadThread线程取得输入流中的数据再让网络接口(自定义的接口,主要负责解析数据类型)去解析数据.
MyClient类:负责与服务器连接,指定IP,指定端口,同样要在连接成功之后,获得Socket的输入流和输出流.输入流的读取交给了ReadThread线程.ReadThread线程取得输入流中的数据再让网络接口(自定义的接口,主要负责解析数据类型)去解析数据.
private int[ ][ ] m_nField 10列 20行
v在Java动画技术里经常会使用到双缓冲。双缓冲技术是一种很有效的避免闪烁的方法。它的特点是:不是直接在屏幕上画图,而是对内存里的一个虚拟的图片进行操作,当复杂的画图过程结束后,直接将内存里的这张图,贴到屏幕上就行了。这样做的好处在于对屏幕操作的时间缩短,这样对图像的闪烁感就会减少。
v双缓冲的实现分两步:
v①首先建立一个Image对象
vImage img=createImage(width,height);
v在Java里Image类是一个虚类,不能直接创建出一个新的对象,不过可用Java里的createImage(int w,int h)创建出宽为w高为h的图片图像。
v②获取Image对象的Graphics对象,通过这个对象就可以在Image对象上画需要的图形。
vGraphics g=img.getGraphics();
v在g上画图就相当于在Image上画图。
v③画好图后,将img对象直接贴到游戏界面上
vpublic void paint(Graphics gg)
v{
v gg.draw(img,0,0,this);
v}
|
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
0 |
||||||||||
1 |
||||||||||
2 |
||||||||||
3 |
||||||||||
4 |
||||||||||
5 |
||||||||||
6 |
||||||||||
7 |
m_nField[20][10] | |||||||||
8 | ||||||||||
9 |
||||||||||
10 |
||||||||||
11 |
||||||||||
12 |
||||||||||
13 |
||||||||||
14 |
||||||||||
15 |
||||||||||
16 |
||||||||||
17 |
||||||||||
18 |
||||||||||
19 |
方块的定义
v在游戏里操纵的是一个个方块,应该编写一个类来封装这些方块。
v方块类的设计:
v一个方块有三个属性:X坐标,Y坐标和颜色
vint m_nColumn;
vint m_nRow;
int m_nColor;
int m_nColor;
对于方块类还有一些方法:
v判断两个方块是不是相等
public boolean IsEqual(Square s)
v判断这个方块是不是还在游戏的可玩的区域里
public boolean InBounds(int colMax, int rowMax)
v方块组的构成
形状索引 |
0 |
1 |
2 |
3 |
4 |
5 |
6 | |||||||||||||||||||||
形状 |
||||||||||||||||||||||||||||
1 |
0 |
2 |
3 |
|
|
|
2 |
|
|
1 |
|
2 |
|
1 |
0 |
2 |
2 |
0 |
1 |
| ||||||||
|
|
0 |
3 |
|
|
3 |
|
0 |
|
3 |
0 |
|
|
3 |
3 |
|
| |||||||||||
颜色索引 |
1 |
2 |
3 |
4 |
5 |
6 |
7 | |||||||||||||||||||||
颜色描述 |
红色 |
桔黄色 |
绿色 |
兰色 |
品红色 |
粉红色 |
青色 |
方块组移动和变形算法
v方块组的移动算法,从一个方块组移动到另一个方块组,如果可以移动,则返回真,并且修改m_nField[][]的值,不可以返回假
vpublic boolean moveSquares(Square from[],Square to[])
v{
v/**判断是否能移动*/
vouterlable:
vfor (int i = 0; i < to.length; i++)
v{
v/**如果不在可玩区域,则返回假*/
vif (to[i].InBounds(m_nCols,m_nRows+4) == false)
vreturn false;
vif (m_nField[to[i].m_nColumn][to[i].m_nRow] != 0)
v{
vfor (int j = 0; j < from.length; j++)
vif (to[i].IsEqual(from[j]))
vcontinue outerlable;
vreturn false;
v}
v}
v/**移动到to的位置,清除from位置*/
vfor (int i = 0; i < from.length; i++)
vif (from[i].InBounds(m_nCols,m_nRows+4))
vm_nField[from[i].m_nColumn][from[i].m_nRow] = 0;
vfor (int i = 0; i < to.length; i++)
vm_nField[to[i].m_nColumn][to[i].m_nRow] = to[i].m_nColor;
vthis.playMusic("dead.wav");//移动一次播放一次移动的声音
vreturn true;
v}
vFrom定义为没有移动之前的那个方块组,to定义为要移到的方块组。
v定理:对于两个非零的向量a,b,如果ab=0,那么a┴b;反之如果a┴b,那么ab=0。
v问题:已经向量a=(X,Y) 要使|b|=|a|,且b┴a,求向量 b?
v解:设向量b(X1,Y1)
X2+Y2= X12+Y12
X*X1+Y*Y1=0
v解出方程组的解为 x1=-y,y1=x; 或者 x1=y,y1=-x.
v如果要实现方块的逆时针方向转动 取第一种解: x1=-y,y1=x1203逆时针
v0,1,2,3表示m_curPiece [0], m_curPiece [1], m_curPiece [2],
m_curPiece [3]
v根据求解得出的公式
以m_curPiece [0]的坐标为相对坐标原点,依次求出m_curPiece [1],
m_curPiece [2], m_curPiece [3]的相对于m_curPiece [0]的坐标
int dx = m_curPiece[i].m_nColumn - m_curPiece[0].m_nColumn;
int dy = m_curPiece[i].m_nRow - m_curPiece[0].m_nRow;
/**
PieceBuffer是一个具有互斥功能的环状缓冲区,用于存放生产者生产出的游戏所需要的方块组索引值
v那么m_curPiece[i]逆时针旋转90度后的相对于m_curPiece[0]的坐标为
v(-dy,dx)那么m_curPiece[i]绝对坐标为
v(m_curPiece[0].m_nColumn-dy, m_curPiece[0].m_nRow+dx)
v则m_curPiece[i] 旋转90度后的的新方块为
vSquare(m_curPiece[0].m_nColumn-dy,
vm_curPiece[0].m_nRow+dx,
vm_curPiece[i].m_nColor)
v如果不旋转 左右移动则相应加或减去移动的步长
vnewpos[i] = new Square(m_curPiece[i].m_nColumn + nDx,
v
m_curPiece[i].m_nRow + nDy,
v
m_curPiece[i].m_nColor)
/**
v *移动方块
v *@param nDx 左右移动,向左为-1 向右为1 不左右为0
v *@param nDy 上下移动,向下为-1 不上下为0
v *@param bRotate 是否转动 true 为转动
v */
v public synchronized boolean moveCurPiece(
vint nDx,
vint nDy,
vboolean bRotate)
v{
vSquare newpos[] = new Square[4];
vfor (int i = 0; i < 4; i++)
v{
v if (bRotate) //变形
v{
v int dx = m_curPiece[i].m_nColumn - m_curPiece[0].m_nColumn;
v int dy = m_curPiece[i].m_nRow - m_curPiece[0].m_nRow;
v newpos[i] = new Square(m_curPiece[0].m_nColumn - dy,
v
m_curPiece[0].m_nRow + dx,
v
m_curPiece[i].m_nColor);
v }
v else //复制要移动到的位置
v {
v newpos[i] = new Square(m_curPiece[i].m_nColumn + nDx,
v
m_curPiece[i].m_nRow + nDy,
v
m_curPiece[i].m_nColor);
v }
v }
v if (moveSquares(m_curPiece, newpos) == false)
v//不能移到指定位置返回false
v return false;
v m_curPiece = newpos; //能移返回true
v return true;
v }
键盘控制
v给游戏添加键盘侦听器
v侦听器KeyHandler实现接口KeyListener
v重写KeyListener中的keyPressed这个方法(包含字母键和非字母键 )
v左移m_tTetrics.moveCurPiece( -1, 0, false)
v右移m_tTetrics.moveCurPiece( 1, 0, false)
v变形m_tTetrics.moveCurPiece( 0, 0, true)
v下降
vwhile (m_tTetrics.moveCurPiece(0, -1, false));
游戏同步
v要想实现游戏的同步①保证游戏双方所需要的方块形状出现的次序一样②游戏双方实时地进行游戏区域数据的交互
v实现方法:
v①游戏双方都有自己存放方块形状的缓冲区
v有了缓冲区当然就要有生产和消费,生产者有游戏服务器端去做,生产的方块添加到自己缓冲区并同时发送给客户端,客户端端收到后再添加到自己的缓冲区
v②一方的方块组每移动一步就要把自己的区域信息发送给另一方,另一方收到后,再更新对方的游戏区域矩阵m_nRivalField[][]数据, m_nRivalField[][]里数据变了,画出的图形也就变了
vPieceBuffer是一个具有互斥功能的环状缓冲区,用于存放生产者生产出的游戏所需要的方块组索引值
v方块组索引值加1后就是画图所用的颜色索引值,一值两用
PieceBuffer是一个具有互斥功能的环状缓冲区,用于存放生产者生产出的游戏所需要的方块组索引值
v方块组索引值加1后就是画图所用的颜色索引值,一值两用
游戏网络协议
v这样游戏的服务器端和客户端就能在连接成功之后相互通信了.既然能够通信,那么双方之间通信的数据到底是属于哪些数据?是游戏的状态数据,还是组织游戏的动作命令,或者是双方聊天的内容?这就需要事先归定发送的数据是属于什么数据,从协议的角度出发,在数据内容头部加一个数据类型的报头,这样另一方在收到数据时,只要剥取数据的头部,然后根据头部的内容来判断数据类型,然后再决定怎样对待这类型的数据.
v在网络接口里定义了这些网络协议报文头常量
v public final int STARTGAME=11; /**开始游戏*/
v public final int PAUSEGAME=12; /**暂停游戏*/
v public final int ENDGAME=13; /**结束游戏*/
v public final int EXITGAME=14; /**退出程序*/
v public final int GAMEDATA=21; /**游戏数据*/
v public final int GAMEOVER=22; /**游戏结束*/
v public final int GAMEREMOVELINE=23;/**游戏消行*/
v public final int GAMESCORE=24; /**游戏分数*/
v public final int GAMEPIECE=25; /**游戏的Piece*/
v public final int TALK=31; /**聊天内容*/
v 整个数据是一个字符串
v字符串前2位是数据类型
v如:"31" 表示该条数据是聊天内容
v字符串第3位是一个分隔符冒号”:”隔开数据类型和数据内容
v从第四位开始为数据内容
v如:str=”31:你好!”
3 |
1 |
: |
你好! |
v1位 2位 3位 4位~N位
菜单控制
菜单结构一览
主菜单项 |
子菜单项 |
描述 |
游戏
jMenuGame |
开始jMenuStartGame
暂停jMenuPauseGame
结束jMenuEndGame
关闭 jMenuExit |
开始游戏
暂停游戏
结束游戏
退出程序 |
设置
jMenuSet |
设置级别jMenuGameLevel
创建游戏 jMenuCreateGame
加入游戏jMenuAddGame
退出游戏jMenuExitGame |
创建server端等待client来连接
作为client端去连接server端
断开网络连接 |
关于
jMenuHelp |
关于jMenuSeeAbout
记录jMenuSeeRecord |
关于制作
查看记录分数 |
创建游戏时序图
开始游戏时序图
开始游戏时序图