俄罗斯方块游戏介绍
俄罗斯方块游戏介绍
public class Game extends java.awt.Frame
{
public Game()
{
setTitle("俄罗斯方块游戏 - Ghost Valley");
addWindowListener(new java.awt.event.WindowAdapter(){
public void windowClosing(java.awt.event.WindowEvent e){
dispose();
System.exit(0);
}
});
GameCanvas gameCanvas = new GameCanvas();
setLayout(new java.awt.BorderLayout());
add(gameCanvas,"Center");
setResizable(false);
setLocation((java.awt.Toolkit.getDefaultToolkit().getScreenSize().width-gameCanvas.getPreferredSize().width)/2,(java.awt.Toolkit.getDefaultToolkit().getScreenSize().height-gameCanvas.getPreferredSize().height)/2);
pack();
show();
}
public static void main(String args[]){ new Game(); }
}
class GameCanvas extends java.awt.Canvas implements Runnable
{
public static final int BKSIZE = 5;
public static final int BDWIDTH = 20;
public static final int BDHEIGHT = 30;
////// Inner class Block////////
private class Block
{
public boolean bkdata[][] = new boolean[BKSIZE][BKSIZE];
public int rpos,cpos;
public Block(final boolean bkdata1[][],int rpos,int cpos)
{
//this.bkdata = (boolean[][])bkdata1.clone();
for(int i=0; i
this.rpos = rpos;
this.cpos = cpos;
}
public Block(final Block bk)
{
//this.bkdata = (boolean[][])bk.bkdata.clone();
for(int i=0; i
this.rpos = bk.rpos;
this.cpos = bk.cpos;
}
public void doAction(int action)
{
switch(action)
{
case Message.MOVE_UP:
this.rpos--;
break;
case Message.MOVE_DOWN:
this.rpos++;
break;
case Message.MOVE_LEFT:
this.cpos--;
break;
case Message.MOVE_RIGHT:
this.cpos++;
break;
case Message.ROTATE_CLOCK:
{
final int x0 = BKSIZE/2;
final int y0 = BKSIZE/2;
//boolean bkdata1[][] = (boolean[][])this.bkdata.clone();
boolean bkdata1[][] = new boolean[BKSIZE][BKSIZE];
for(int i=0; i
for(int x=0; x
break;
}
case Message.ROTATE_ANTICLOCK:
{
final int x0 = BKSIZE/2;
final int y0 = BKSIZE/2;
//boolean bkdata1[][] = (boolean[][])this.bkdata.clone();
boolean bkdata1[][] = new boolean[BKSIZE][BKSIZE];
for(int i=0; i
for(int x=0; x
break;
}
}
}
}
////// Inner class Board /////
private class Board
{
public boolean bddata[][] = new boolean[BDHEIGHT][BDWIDTH];
public Board()
{
clear();
}
public Board(final Board board)
{
//this.bddata = (boolean[][])board.bddata.clone();
for(int i=0; i
}
public void clear()
{
for(int i=0; i
}
}
////// Inner class Message /////////
private class Message
{
public static final int MOVE_UP = 1;
public static final int MOVE_DOWN = 2;
public static final int MOVE_LEFT = 3;
public static final int MOVE_RIGHT = 4;
public static final int ROTATE_CLOCK = 5; //rotate clock-wise
public static final int ROTATE_ANTICLOCK = 6;//rotate anticlock-wise
public Message(int action)
{
this.action = action;
}
public int getAction()
{
return this.action;
}
private int action;
}
////// Inner class Queue ///////
//////////// fields of class Game ////////////////
private Board m_board = new Board(); //仅由消息处理器线程访问
private Block m_block = null; //当前存在的块
private MessageQueue m_msgQueue = new MessageQueue(); //由消息处理器和awt线程访问
private BlockDataPoolQueue m_blockDataPoolQueue = new BlockDataPoolQueue();
private int m_S = 0;
///// method run 主消息处理处理线程
public void run()
{
boolean bkdataPool[][][] ={
{{false,false,true,false,false},
{false,false,true,false,false},
{false,false,true,false,false},
{false,false,true,false,false},
{false,false,true,false,false}},
{{false,false,false,false,false},
{false,true,true,true,false},
{false,false,true,false,false},
{false,false,true,false,false},
{false,false,true,false,false},
{false,false,false,false,false}},
{{false,false,false,false,false},
{false,false,true,false,false},
{false,true,true,true,false},
{false,false,true,false,false},
{false,false,false,false,false}},
{{false,false,false,false,false},
{false,false,false,false,false},
{false,true,true,true,false},
{false,true,false,true,false},
{false,false,false,false,false}},
{{false,false,false,false,false},
{false,false,false,false,false},
{false,true,true,true,false},
{false,false,true,false,false},
{false,false,false,false,false}}};
repaintRequest();
while(true)
{
switch(m_S)
{
case 0:
try
{
Thread.currentThread().sleep(50);
}
catch (InterruptedException ex)
{
}
break;
case 1:
Block bk1 = new Block(m_blockDataPoolQueue.getNextBlockData(),0,(BDWIDTH-BKSIZE)/2);
if( ! isCollided(m_board,bk1)){
m_block = bk1;
m_S = 2;
}else
m_S = 3;
repaintRequest();
break;
case 2:
Message msg;
synchronized(m_msgQueue) //等待新的消息
{
while(m_msgQueue.empty()){
try{
m_msgQueue.wait();
}catch(InterruptedException e){
}
}
msg = (Message)m_msgQueue.dl();
}
Block bk2 = new Block(m_block);
bk2.doAction(msg.getAction());
if(! isCollided(m_board,bk2)){ //动作可以执行
m_block.doAction(msg.getAction());
m_S = 2;
repaintRequest();
}else if(msg.getAction()==Message.MOVE_DOWN){ //向下移动的动作不能执行
merge(m_board,m_block);
m_block = null;
packUpBoard(m_board);
m_S = 1;
repaintRequest();
}
break;
case 3:
try
{
Thread.currentThread().sleep(50);
}
catch (Exception ex)
{
}
break;
}
}
}
///// method isClllided /////
private boolean isCollided(final Block bk1,final Block bk2) //判断两方块是否重叠
{
for(int r1=0; r1
int r2 = r1+bk1.rpos-bk2.rpos;
if(r2>=0 && r2
for(int c1=0; c1
int c2 = c1+bk1.cpos-bk2.cpos;
if(c2>=0 && c2
return true;
}
}
}
return false;
}
private boolean isCollided(final Board board,final Block block) //判断某方块与某底板是否重叠
{
for(int i=0; i
return true;
}else if(block.bkdata[i][j])
return true;
}
}
return false;
}
private void merge(Board board,final Block block) //合并board <=== block
{
for(int i=0; i
board.bddata[block.rpos+i][block.cpos+j] |= block.bkdata[i][j];
}
private int packUpBoard(Board board) //消去满行
{
int linesDeleted = 0;
for(int i=BDHEIGHT-1; i>=0;){ //检测第i行
boolean full = true;
for(int j=0; j
full = false;
break;
}
}
if(full){ //第i行为满行
for(int k=i-1; k>=0; k--){
for(int c=0; c
}
for(int c=0; c
linesDeleted++;
}else
i--;
}
return linesDeleted;
}
//Constructor
final int X0 = 10, Y0 = 15;
final int CELLSIZE = 12;
public java.awt.Dimension getPreferredSize()
{
return new java.awt.Dimension(2*X0+CELLSIZE*BDWIDTH,2*Y0+CELLSIZE*BDHEIGHT);
}
public GameCanvas()
{
setBackground(java.awt.Color.black);
addKeyListener(new java.awt.event.KeyAdapter(){
public void keyPressed(java.awt.event.KeyEvent e)
{
switch(e.getKeyCode()){
case java.awt.event.KeyEvent.VK_LEFT:
postMessage(new Message(Message.MOVE_LEFT));
break;
case java.awt.event.KeyEvent.VK_RIGHT:
postMessage(new Message(Message.MOVE_RIGHT));
break;
case java.awt.event.KeyEvent.VK_DOWN:
postMessage(new Message(Message.MOVE_DOWN));
break;
case java.awt.event.KeyEvent.VK_UP:
postMessage(new Message(Message.ROTATE_CLOCK));
break;
case java.awt.event.KeyEvent.VK_ENTER:
postMessage(new Message(Message.ROTATE_ANTICLOCK));
break;
case java.awt.event.KeyEvent.VK_PAGE_UP:
postMessage(new Message(Message.MOVE_UP));
break;
case java.awt.event.KeyEvent.VK_SPACE:
if(m_S==0)
m_S = 1;
break;
case java.awt.event.KeyEvent.VK_F1:
if(m_S==3){
m_board.clear();
m_S = 0;
repaintRequest();
}
}
}
});
new Thread(){
public void run()
{
while(true){
try{
sleep(500);
}catch(InterruptedException e){
}
postMessage(new Message(Message.MOVE_DOWN));
}
}
}.start();
new Thread(this).start(); //消息处理线程
}
public void postMessage(Message msg)
{
if(m_S==1 || m_S==2){
synchronized(m_msgQueue){
m_msgQueue.en(msg);
m_msgQueue.notify();
}
}
}
public void repaintRequest()
{
repaint();
}
public void paint(java.awt.Graphics g)
{
//draw m_board
g.setColor(java.awt.Color.blue);
g.drawRect(X0,Y0,CELLSIZE*BDWIDTH-1,CELLSIZE*BDHEIGHT-1);
switch(m_S){
case 1:
case 2:
g.setColor(java.awt.Color.yellow);
break;
case 3:
g.setColor(java.awt.Color.lightGray);
break;
}
for(int i=0,y=Y0; i
g.fillRect(x,y,CELLSIZE-1,CELLSIZE-1);
}
}
//draw next block show
g.setColor(java.awt.Color.lightGray);
boolean nextBlockData[][] = m_blockDataPoolQueue.peekNextBlockData();
for(int i=0,y=Y0; i
g.drawRect(x+1,y+1,CELLSIZE-1,CELLSIZE-1);
}
}
//draw m_block
if(m_S==2){
g.setColor(java.awt.Color.red);
for(int i=0,y=Y0+m_block.rpos*CELLSIZE; i
g.fillRect(x,y,CELLSIZE-1,CELLSIZE-1);
}
}
}
g.setColor(java.awt.Color.red);
g.setFont(new java.awt.Font(g.getFont().getFontName(),g.getFont().getStyle(),16));
switch(m_S){
case 0:
g.drawString("按空格键开始游戏!",50,100);
break;
case 3:
g.drawString("游戏结束,按F1键重新开始!",20,100);
break;
}
}
private class BlockDataPoolQueue extends Queue
{
public BlockDataPoolQueue(){ this.currentBlockData = bkdataPool[ran.nextInt(bkdataPool.length)]; }
public boolean[][] peekNextBlockData()
{
return this.currentBlockData;
}
public boolean[][] getNextBlockData()
{
boolean[][] bkd = this.currentBlockData;
this.currentBlockData = bkdataPool[ran.nextInt(bkdataPool.length)];
return bkd;
}
private boolean[][] currentBlockData = null;
private java.util.Random ran = new java.util.Random();
private boolean bkdataPool[][][] ={
{{false,false,true,true,false},
{false,false,true,false,false},
{false,false,true,false,false},
{false,true,true,false,false},
{false,false,false,false,false}},
{{false,false,false,false,false},
{false,true,true,false,false},
{false,false,true,false,false},
{false,false,true,true,false},
{false,false,false,false,false}},
{{false,false,false,false,false},
{false,false,true,false,false},
{false,true,true,true,false},
{false,false,true,false,false},
{false,false,false,false,false}},
{{false,false,false,false,false},
{false,false,false,false,false},
{false,true,true,true,false},
{false,false,false,true,false},
{false,false,false,false,false}},
{{false,false,false,false,false},
{false,true,false,false,false},
{false,false,true,false,false},
{false,false,false,false,false},
{false,false,false,false,false}},
{{false,false,false,false,false},
{false,false,false,false,false},
{false,false,true,false,false},
{false,false,false,false,false},
{false,false,false,false,false}},
{{false,false,false,false,false},
{false,true,true,false,false},
{false,true,true,false,false},
{false,false,false,false,false},
{false,false,false,false,false}},
{{false,false,false,false,false},
{false,false,false,false,false},
{false,true,true,true,false},
{false,false,true,false,false},
{false,false,false,false,false}}};
}
private class Queue
{
private Node head;
private Node tail;
public Queue()
{
head = new Node();
tail = new Node();
head.prev = tail.next = null;
head.next = tail;
tail.prev = head;
}
public void en(Object item)
{
Node q = new Node();
q.data = item;
q.next = tail;
q.prev = tail.prev;
tail.prev.next = q;
tail.prev = q;
}
public Object dl()
{
if(! empty()){
Node p = head.next;
head.next.next.prev = head;
head.next = head.next.next;
return p.data;
}else
return null;
}
public Object peek()
{
if(! empty())
return head.next.data;
else
return null;
}
public boolean empty()
{
return (head.next==tail) || (tail.prev==head);
}
public void clear()
{
head.next = tail;
tail.prev = head;
}
public int elemNum()
{
int num = 0;
for(Node q=head.next; q!=tail; q=q.next)
num++;
return num;
}
private class Node
{
Object data;
Node prev;
Node next;
}
}
//inner class MessageQueue
private class MessageQueue
{
private Node head;
private Node tail;
public MessageQueue()
{
head = new Node();
tail = new Node();
head.prev = tail.next = null;
head.next = tail;
tail.prev = head;
}
public void en(Message msg)
{
Node p = new Node();
p.message = msg;
p.next = tail;
p.prev = tail.prev;
tail.prev.next = p;
tail.prev = p;
}
public Message dl()
{
if(! empty()){
Node p = head.next;
head.next.next.prev = head;
head.next = head.next.next;
return p.message;
}else
return null;
}
public Message peek() //应先使用empty来检查
{
if(! empty())
return head.next.message;
else
return null;
}
public boolean empty()
{
return (head.next==tail) || (tail.prev==head);
}
public void clear()
{
head.next = tail;
tail.prev = head;
}
private class Node
{
Message message;
Node prev;
Node next;
}
}
}
最主要的两个类是Board和Block
Board就是"底板",就是每个block落不下去时合并到的对象
Block就是每个"方块"
程序大体上是两个线程在运行,一个是awt-thread(系统线程),另一个是消息处理线程(运行在run())方法.
程序中设立了一个消息队列(MessageQueue m_msgQueue = new MessageQueue()).
awt-thread将player的输入动作(如向上动作(光标上键生成),向下动作(光标下键生成),旋转(Enter键生成))生成的动作封装成Message的对象,推入消息队列m_msgQueue中.
消息处理线程负责不断的从消息队列(m_msgQueue)中取得消息和处理消息,没有消息时就wait().
player的生存周期分为四个阶段(用有限状态自动机描述): 0(S_INIT)状态为初始状态,代表活动还未开始; 1(S_TO_GENERATE_BLOCK)代表要生成一个block了; 2(S_DROPPING)代表block处于下落状态; 3(S_DEAD)代表player已经"game over"了.
在S_TO_GENERATE_BLOCK时,尝试生成一个新的block,如果能生成的话(就是生成的block和"底板"不重叠),就赋值到m_block,代表一个新的block生成来,状态就转为下落状态(S_DROPPING),否则状态就转为S_DEAD.
在S_DROPPING状态时,对于每一个动作(Message对象的action),判断该动作能否执行,能执行就执行该动作.如果动作是MOVE_DOWN时如果不能执行就把该block合并到"底板"中,消去底板满行,状态转为S_TO_GENERATE_BLOCK.
我还有一个多人版的,本来不想公布出来的,但看到大家的学习兴趣这么好,就忍不住要公布出来.对初学者可能能学习一些多线程之间的同步与互斥的例子.
和传统的多人版的不同,这个多人版的俄罗斯方块游戏是所有人在一个"底板"中进行的(传统的是每个player是分开独立的的,大家比赛看是谁得分高),每个player都可以用自己的block推动别的player的block一起移动,players之间可以可以互相"捣乱",也可以互相配合(经常需要自己的block和别人的block合并在一起来动作).程序说明我就一并贴在这里吧:
interlace(player-p,action):
FOR action of player-p:
FOR ALL block of players i except player-p whick interlaced with block of player-p
interlace(player-i,action);
when all playerp-i is actable:
all block of player-i . perform action
使用递归的方法来处理.
目前网络部分还没有作完,所以只能在单机上多人玩.
先把单人版的源码看完多人版的就容易了.
程序太长了,在这里贴太麻烦,需要的留下email.