前言
俄罗斯方块是一款风靡全球,从一开始到现在都一直经久不衰的电脑、手机、掌上游戏机产品,是一款游戏规则简单,但又不缺乏乐趣的简单经典小游戏,上手容易,适用范围广泛,人所共知。俄罗斯方块游戏简单的基本规则是有小方块组成的7种不同的规则图形陆续从屏幕上落下,玩家操作移动使其下落拼出一条完整的横条,随即会消失,玩家得分,游戏等级提高,当没有被消除的图形堆积到屏幕顶端时,游戏结束。
俄罗斯方块游戏最初是由俄罗斯人阿列克谢·帕基特诺夫在1984年开发的休闲小游戏,曾几何时它创造了无可匹敌的商业价值,影响了一代休闲小游戏的开发与产业链。随着信息时代科技时代的不断发展和变化,计算机已经普及到每一个人的生活中,在繁忙的工作生活中,这一类休闲小游戏给人们带来了些许的放松。
《俄罗斯方块升级版》游戏用java语言实现,采用了swing技术进行了界面化处理,设计思路用了面向对象思想。
主要需求
整个游戏系统会随机产生7种由四个小方块组成的不同形状的方块,经过旋转后得到28种状态,如果增加游戏难度,会增加六种不同形状的方块形状,同样经过旋转后增加24种状态。方块按一定的速度自由下落,玩家通过键盘上的上下左右按键控制方块的左右移动和旋转,将方块落下后放在合适的位置。当方块落下后,如果方块落下后有一整行被方块填满,那么该一整行消去。当一行被消去时,玩家得分增加10分,当得分达到100分后,玩家等级增加一级,速度加快一级,难度加大。如果当方块落下后整个游戏画布界面被占满,则方块不再下落,游戏宣告失败。游戏具体的功能需求有如下几个:
游戏界面需求:游戏的良好的界面会让玩家眼前一亮,更加能充分的感受到游戏带来的娱乐性,放松性。本游戏的默认背景色是深绿色,游戏主界面画面自定义为自己的所喜欢的图片,并可以更改,主界面方块默认用橘黄色,预显方块颜色默认为淡紫色。背景色、前景色对比鲜明,以达到让玩家眼前一亮的感觉,并能在游戏达到高等级状态,方块下落速度渐高的情况下使玩家能够清楚的分辨出下落方块的形状,增加游戏的刺激性。
游戏形状需求:用数组作为存储方块52种状态的数据结构,即初级等级长条形、Z字形、反Z形、田字形、7字形、反7形、T字型一共7种形状的向4个方向的旋转变形,和中级等级的三种方块12种不同的状态,高级等级的三种方块12种不同的状态。各个方块可以实现按逆时针的旋转方式旋转,并且方块能否旋转需要用条件加以判断,如果旋转后可能发生越界,则不能旋转,需要调整位置来保证他可以旋转。
主要设计
以下该游戏中类的设计:
MyFrame类:继承自JFrame类,作为游戏的主类,负责对游戏的主体全局进行控制,连接与指挥各个类的枢纽。
PreView类:继承自JPanel类,作为操作面板类,负责安放主游戏窗口,积分板,级别控制按钮等窗口用以控制游戏进程。
GameCanvas类:继承自JPanel类,ChangeBlockColor线程类动态改变画布类的方格颜色,画布类通过检查方格颜色来体现ChangeBlockColor方块的移动情况与消去状况。
Box类:方格类,组成方块的基本元素,主要表达方式为颜色。
Block类:作为操控方块的类,控制方块的移动,下落以及变形。
游戏主界面显示模块:
游戏主界面采用Swing组件开发,并且向其注册监听器,以实现各种控制功能,综合游戏窗体的设计,其上至少需要注册三个监听器,分别是动作监听器(ActionListener)、键盘监听器(KeyListener)、选项监听器(ItemListener)。
根据初步设计,可以确定客户端上所要用到的Swing组件对象有JFrame对象、JPanel对象,JLabel对象、JButton对象、JMenuBar对象、JMenu对象、JMenuItem对象、JTextField对象、JTextArea对象、JDialog对象等,至少十个Swing组件对象。
背景画布模块设计:该游戏的主背景画布是一个20行、12列的二维数组,方块显示是由相应颜色变化来标识,主窗体用颜色填充后可形成呈现出来背景样式和方块。本游戏用继承自JPanel的GameCanvas类控制背景画布的显示,用rows代表画布的行数,cols代表画布的列数,行数和列数决定着画布拥有方格的数目。
预览方块模块设计:方块和数据信息是游戏中最基本的功能模块。Box这个类方格类,是组成块的基本元素,用自己的颜色来表示块的外观 ,MyTask继承TimerTask类用来定时下落,用计数方式来实现速度的改变,MyListener类继承KeyAdapter类用来实现按键监听,控制方块的上下左右。定义一个4x4方阵,共16个小格。用“0”和“1”来表示每个方格是绘制新颜色还是保留底色。
方块移动、旋转模块设计:方块的翻转与移动比较容易实现,方块移动只需要改变方块的横坐标或纵坐标,然后重新绘制方块即可。方块翻转也只需要改变背景数组的值,重新绘制方即可。
本游戏方块下落时,进行动态绘制,实现Cloneable接口, 以指示 Object.clone() 方法可以合法地对该类实例进行按字段复制。方块的操作类BlockOperation继承Thread类,重写run()方法,以实现方块的动态正确下落。当然,在线程中要判定方块是处于moving状态还是pausing状态
功能截图
游戏开始主界面:
游戏菜单:
设计方块颜色:
代码实现
MyFrame
public class MyFrame extends JFrame{
JPanel jp_pan=new JPanel();
JPanel jp_pre=new JPanel();
JPanel jp_ctrl=new JPanel();
JPanel jp_scor=new JPanel();
Zidingyi zi;
JRadioButtonMenuItem jr1=new JRadioButtonMenuItem("初级",true);
JRadioButtonMenuItem jr2=new JRadioButtonMenuItem("中级");
JRadioButtonMenuItem jr3=new JRadioButtonMenuItem("高级");
JLabel jt9=new JLabel("得分:0" );
static JLabel jt10=new JLabel("等级:1" );
JMenu m1=new JMenu("游戏");
JMenu m2=new JMenu("帮助");
JCheckBox jc1;
JSlider jsl;
// Dialog dia;//创建对话框
static ImageIcon background = new ImageIcon(Constant.backGround1);
// 把背景图片加到label
static JLabel label = new JLabel(background);
// Dialog dia=new Dialog(this, "自定义", false);
int scor=0;//初始化分数为0
static int rank=0;//初始化等级为0
int highC=0;
boolean upspeed=false;
boolean isTime=true;
boolean runstop;
static boolean isRank=false;
static boolean changeBack=false;
public static boolean playing=false;
static boolean isMusic=true;
static boolean high=false;
PreView pv=new PreView();
JMenuItem ji1=new JMenuItem("开局");
GameCanvas gc=new GameCanvas(20, 12);//画出20行12列
private Block block=new Block();//当前块
private int newspeed=1000;//默认当前等级为1
MusicPlayer mp=new MusicPlayer();
Timer time=new Timer();
MyTask mytask;
int temp=1;
// 游戏主构造函数
public MyFrame(String str){
super(str);
this.setSize(450, 570);
Dimension scrSize =
Toolkit.getDefaultToolkit().getScreenSize();//获取屏幕尺寸
setLocation((scrSize.width - getSize().width) / 2,
(scrSize.height - getSize().height) / 2);//设置屏幕居中
this.setLayout(null);
//label的大小为jframe的大小
label.setBounds(0, 0, this.getWidth(), this.getHeight());
//把label加到jframe的最底层,比jframe上的那个panel还下面
this.getLayeredPane().add(label, new Integer(Integer.MIN_VALUE));
//label比jframe上的那个panel还下面,那需要把那个panel设为透明的,不然就盖住背景了
JPanel imagePanel = (JPanel) this.getContentPane();
imagePanel.setOpaque(false);
addMenu();
//游戏开始按钮
ji1.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
// 如果游戏已经开始,再按一次即显示游戏结束
if (playing == true) {
ji1.setText("开局");
if(isMusic==true)
{mp.playStart();}
gc.setGameOver(true);
gc.repaint();
MyFrame.rank=11-Constant.step;
MyFrame.jt10.setText("等级:"+MyFrame.rank);
runstop=true;
block.isAlive=false;
block=new Block();
mytask.cancel();
playing = false;
} else {
reset();
if(isMusic==true)
{mp.playStart();}
MyFrame.rank=11-Constant.step;
MyFrame.jt10.setText("等级:"+MyFrame.rank);
ji1.setText("结束游戏");
playing = true;
mytask=new MyTask();
time.schedule(mytask, 0, 100);//100毫秒执行一次
Thread thread = new Thread(new play());// 调用开始游戏的方法
thread.start();
}
};
});
this.add(gc);//添加游戏画布
addRight();//添加右边
this.setFocusable(true);//设置可获得焦点
this.requestFocus();//设置焦点
this.addKeyListener(new MyListener());
this.setVisible(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void setBackGround(){
label.setIcon(background);
}
private class MyTask extends TimerTask {
@Override
public void run() {
temp++;
if (temp % Constant.step == 0) {
block.newL = block.x;
block.newH = block.y + 1;
// block.yy();
if (block.pausing == true)
return;
if (high == true) {
block.earse();
highC++;
if (highC == 4) {
gc.addRow();
highC = 0;
}
}
if (block.isMoveAble(block.newH, block.newL)) {
block.earse();
block.y++;
block.display();
gc.repaint();
} else {
block.isAlive = false;
gc.repaint();
// cancel();
} // 取消定时器任务
temp = 1;
}
}
}
private class play implements Runnable {
public void run() {
int col = (int) (Math.random() * (gc.getCols() - 3));//随即位置生成列
int style = Constant.STYLES[(int) (Math.random() * Block.get_addl())][(int) (Math
.random() * 4)];
while (playing) {
if (block != null) {
//判断当前方块是否死亡
if (block.isAlive) {
try {
Thread.currentThread().sleep(100);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
continue;
}
}
isFullLine();
// if(upspeed)//判断是否加速,是的话则进行调用
// upLevel();
if(isGameOver()){
if(isMusic==true)
{mp.playGameOver();}
gc.setGameOver(true);
gc.repaint();
ji1.setText("开局");
mytask.cancel();
playing=false;
return;
}
block = new Block(style, -1, col, gc);
block.jixu();//防止在暂停时候重新开始游戏,暂停字还不消失
gc.repaint();// 将创建出来的方块显示出来
block.isAlive = true;
col = (int) (Math.random() * (gc.getCols() - 3));//随即位置生成列
style = Constant.STYLES[(int) (Math.random() * Block.get_addl())][(int) (Math
.random() * 4)];
pv.setStyle(style);
}
}
private void upLevel() {
if(Constant.step-1<1){
return;}
Constant.step=Constant.step-1; //速度增加一级
rank++;
jt10.setText("等级:"+rank);
upspeed=false;//将标志位至为false
}
private void isFullLine() {
// TODO Auto-generated method stub
for (int i = 0; i < 20; i++) {
int row = 0;
boolean flag = true;
for (int j = 0; j < 12; j++) {
if (!gc.getBox(i, j).isColorBox()) {
flag = false;
break;
}
}
if (flag == true) {
row = i;
gc.delete(row);//删除行
if(isMusic==true)
{mp.playEraseSound();}
addScor();//增加分数
if(scor%10==0)//设置为10分增加一个等级
upspeed=true;//将速度增加标志位至为true
if(upspeed==true)
upLevel();
}
}
}
private void addScor() {
scor=scor+10;
jt9.setText("得分:"+scor);
}
}
private boolean isGameOver() {
for (int i = 0; i < 12; i++) {
Box box = gc.getBox(0, i);
if (box.isColorBox())
return true;
}return false;
}
private void reset() {
scor=0;
rank=0;
jt10.setText("等级:"+rank);
jt9.setText("得分:"+scor);
upspeed=false;
playing=true;
runstop=false;
// block.pausing=false;
// isTime=true;
// block=new Block();
// block.isAlive=false;
gc.setGameOver(false);
gc.repaint();
gc.reset();
}
private class MyListener extends KeyAdapter{
@Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
int i = e.getKeyCode();
switch (i) {
case KeyEvent.VK_UP:
block.moveUp();
break;
case KeyEvent.VK_DOWN:
block.moveDown();
break;
case KeyEvent.VK_LEFT:
block.moveLeft();
break;
case KeyEvent.VK_RIGHT:
block.moveRight();
break;
case KeyEvent.VK_SPACE:
block.quickDown();
break;
case KeyEvent.VK_P:
block.pause();
break;
case KeyEvent.VK_C:
block.jixu();
break;
}
}
}
private void addMenu() {
// TODO Auto-generated method stub
JMenuBar jb1=new JMenuBar();
// m1.addKeyListener(new MenuKeyListener());
//监听Dialog对话框,如果有等级改变则改变选择
m1.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
// TODO Auto-generated method stub
int i=Block.get_addl();
if(i==7)
jr1.setSelected(true);
else if(i==10)
jr2.setSelected(true);
else
jr3.setSelected(true);
}
});
// JMenuItem ji1=new JMenuItem("开局(O)");
jr1.addActionListener(new MenuActionListener());
jr2.addActionListener(new MenuActionListener());
jr3.addActionListener(new MenuActionListener());
ButtonGroup bg=new ButtonGroup();
bg.add(jr1);
bg.add(jr2);
bg.add(jr3);
JMenuItem ji2=new JMenuItem("自定义");
ji2.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
zi=new Zidingyi(MyFrame.this,"自定义",false,block,gc);
zi.setVisible(true);
if(playing==true)
block.pause();
}
});
JMenuItem ji3=new JMenuItem("退出");
ji3.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
System.exit(1);//退出程序
}
});
JMenuItem ji4=new JMenuItem("关于");
ji4.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
JDialog dl=new Version(MyFrame.this,"版本信息",false);
}
});
//调用颜色对话框设置方块颜色
JMenuItem ji_color=new JMenuItem("方块颜色");
ji_color.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
Color newFrontColor = JColorChooser.showDialog(
MyFrame.this,"设置方块颜色",
gc.getBlockColor());
if (newFrontColor != null)
gc.setBlockColor(newFrontColor);
}
}
);
MyFrame.this.setJMenuBar(jb1);
jb1.add(m1);
jb1.add(m2);
m1.add(ji1);
m1.add(jr1);
m1.add(jr2);
m1.add(jr3);
m1.add(ji2);
m1.add(ji_color);
m1.add(ji3);
m2.add(ji4);
}
private void addRight() {
// TODO Auto-generated method stub
// JTextField jt1=new JTextField("下一块");
JLabel jt1=new JLabel("下一块");
jt1.setFont(new Font("华文行楷", Font.BOLD, 18));
jt1.setOpaque(false);
// jt1.setEditable(false);
jp_pre.setLayout(null);
jt1.setBounds(5, 0, 80, 20);
jp_pre.add(jt1);
pv.setBounds(10, 20, 102, 102);
jp_pre.add(pv);//添加预览窗口
jp_pre.setBounds(308, 5, 120, 125);//设置坐标
jp_pre.setOpaque(false);//设置背景为透明
MyFrame.this.add(jp_pre);
// JTextField jt2=new JTextField("功能键盘" );
JLabel jt2=new JLabel("功能键盘");
jt2.setFont(new Font("华文行楷", Font.BOLD, 23));
// jt2.setEditable(false);
jt2.setOpaque(false);
// JTextField jt3=new JTextField("快速向下:↓" );
JLabel jt3=new JLabel("快速向下:↓");
jt3.setFont(new Font("华文行楷", Font.BOLD, 15));
// jt3.setEditable(false);
jt3.setOpaque(false);
// JTextField jt4=new JTextField("旋转:↑" );
JLabel jt4=new JLabel("旋转:↑");
jt4.setFont(new Font("华文行楷", Font.BOLD, 15));
// jt4.setEditable(false);
jt4.setOpaque(false);
// JTextField jt5=new JTextField("向左:←" );
JLabel jt5=new JLabel("向左:←");
jt5.setFont(new Font("华文行楷", Font.BOLD, 15));
// jt5.setEditable(false);
jt5.setOpaque(false);
// JTextField jt6=new JTextField("向右:→" );
JLabel jt6=new JLabel("向右:→");
jt6.setFont(new Font("华文行楷", Font.BOLD, 15));
// jt6.setEditable(false);
jt6.setOpaque(false);
JLabel jt11=new JLabel("一键下落:空格");
jt11.setFont(new Font("华文行楷", Font.BOLD, 15));
// jt6.setEditable(false);
jt6.setOpaque(false);
// JTextField jt7=new JTextField("暂停:P" );
JLabel jt7=new JLabel("暂停:P");
jt7.setFont(new Font("华文行楷", Font.BOLD, 15));
// jt7.setEditable(false);
jt7.setOpaque(false);
// JTextField jt8=new JTextField("继续:C" );
JLabel jt8=new JLabel("继续:C");
jt8.setFont(new Font("华文行楷", Font.BOLD, 15));
// jt8.setEditable(false);
jt8.setOpaque(false);
jp_ctrl.setLayout(new GridLayout(8, 1, 0, 0));
// jp_ctrl.setBorder(BorderFactory.createBevelBorder(EtchedBorder.LOWERED));
jp_ctrl.add(jt2);
jp_ctrl.add(jt3);
jp_ctrl.add(jt4);
jp_ctrl.add(jt5);
jp_ctrl.add(jt6);
jp_ctrl.add(jt11);
jp_ctrl.add(jt7);
jp_ctrl.add(jt8);
jp_ctrl.setOpaque(false);
jp_ctrl.setBounds(310, 145, 120, 200);
MyFrame.this.add(jp_ctrl);
// jt9.setEditable(false);
jt9.setOpaque(false);
jt9.setForeground(Color.BLACK);
// jt10.setEditable(false);
jt10.setOpaque(false);
jt10.setForeground(Color.BLACK);
jp_scor.setLayout(new GridLayout(2, 1, 0, 20));
jp_scor.add(jt9);
jt9.setFont(new Font("华文行楷", Font.BOLD, 26));
jt10.setFont(new Font("华文行楷", Font.BOLD, 26));
jp_scor.add(jt10);
jt9.setBackground(Color.LIGHT_GRAY);
jt10.setBackground(Color.LIGHT_GRAY);
jp_scor.setOpaque(false);
jp_scor.setBounds(320, 360, 100, 140);
MyFrame.this.add(jp_scor);
}
private class MenuActionListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
//获取JMenuItem对象
JMenuItem j=((JMenuItem)e.getSource());
if(j==jr1){
// newLevel=Constant.LEVEL_1;
Block.set_addl(7);
}
if(j==jr2){
Block.set_addl(10);
}
if(j==jr3){
// high=true;
Block.set_addl(13);
}
}
}
public static void main(String[] args) {
new MyFrame("俄罗斯方块");
}
}
GameCanvas
public class GameCanvas extends JPanel {
private Color backColor = Color.GRAY, frontColor = Color.orange;
private int rows, cols, score = 0, scoreForLevelUpdate = 0;
private Box[][] boxes;
private int boxWidth=25, boxHeight=25;
private boolean gameOver=false;
public boolean pau=false;
public GameCanvas(int rows, int cols) {
this.rows = rows;
this.cols = cols;
this.setOpaque(false);
boxes = new Box[rows][cols];
for (int i = 0; i < boxes.length; i++) {
for (int j = 0; j < boxes[i].length; j++) {
boxes[i][j] = new Box(false);
}
}
setBounds(0, 0, 300, 500);//设置相对位置坐标
setBorder(new EtchedBorder(
EtchedBorder.RAISED, Color.white, new Color(148, 145, 140)));
}
public GameCanvas(int rows, int cols,
Color backColor, Color frontColor) {
this(rows, cols);
this.backColor = backColor;
this.frontColor = frontColor;
}
public void setBackgroundColor(Color backColor) {
this.backColor = backColor;
}
public Color getBackgroundColor() {
return backColor;
}
public void setBlockColor(Color frontColor) {
this.frontColor = frontColor;
}
public Color getBlockColor() {
return frontColor;
}
public int getRows() {
return rows;
}
public int getCols() {
return cols;
}
public int getScore() {
return score;
}
public int getScoreForLevelUpdate() {
return scoreForLevelUpdate;
}
public Box getBox(int row, int col) {
if (row < 0 || row > boxes.length - 1
|| col < 0 || col > boxes[0].length - 1)
return null;
return boxes[row][col];
}
public void delete(int row) {
//创建新Box存储新状态,不能直接赋值,赋值只是指向同一个对象
for (int i = row; i > 0; i--) {
for (int j = 0; j < 12; j++)
boxes[i][j] = new Box(boxes[i - 1][j].isColorBox());
}
repaint();
}
public void addRow() {
for (int i = 0; i <19 ; i++) {
for (int j = 0; j < 12; j++) {
boxes[i][j] = new Box(boxes[i + 1][j].isColorBox());
}
}
for (int i = 0; i < 12; i++) {
int a = (int) (Math.random() * 2);
if (a == 1)
boxes[19][i] = new Box(true);
else
boxes[19][i] = new Box(false);
}
repaint();
}
public void paint(Graphics g) {
super.paint(g);
if (gameOver) {
paintGame(g);
g.setColor(Color.RED);
g.setFont(new Font("黑体", Font.BOLD, 80));
// g.drawString("GAME\nOVER",10, 220);
g.drawString("GAME", 50, 220);
g.drawString("OVER", 50, 310);
} else if(pau==true){
paintGame(g);
g.setColor(Color.BLUE);
g.setFont(new Font("华文行楷", Font.BOLD, 40));
// g.drawString("GAME\nOVER",10, 220);
g.drawString("暂停", 100, 200);
g.drawString("按C继续!", 60, 310);
}else
paintGame(g);
}
public void paintGame(Graphics g) {
g.setColor(Color.BLUE);
g.draw3DRect(0, 0, 298, 498, true);
g.setColor(frontColor);
// int a;
// int key=0x8000;
// int y1=0;
for (int i = 0; i < boxes.length; i++) {
for (int j = 0; j < boxes[i].length; j++) {
if (boxes[i][j].isColorBox() == true) {
g.setColor(frontColor);// 获取方格颜色
g.fill3DRect(j * boxWidth, i * boxHeight, boxWidth,
boxHeight, true);// j,i是相反过来的}
}
}}
}
public boolean setGameOver(boolean go){
gameOver=go;
return gameOver;
}
public void reset() {
for (int i = 0; i < boxes.length; i++) {
for (int j = 0; j < boxes[i].length; j++)
boxes[i][j].setColor(false);
}
repaint();
}
}
总结
通过此次的《俄罗斯方块升级版》游戏实现,让我对swing的相关知识有了进一步的了解,对java这门语言也有了比以前更深刻的认识。
java的一些基本语法,比如数据类型、运算符、程序流程控制和数组等,理解更加透彻。java最核心的核心就是面向对象思想,对于这一个概念,终于悟到了一些。
以上就是Java实现经典游戏俄罗斯方块(升级版)的示例代码的详细内容,更多关于Java俄罗斯方块的资料请关注编程网其它相关文章!