Swing编程-俄罗斯方块游戏

网友投稿 812 2022-11-22

Swing编程-俄罗斯方块游戏

Swing编程-俄罗斯方块游戏

1.游戏的面向对象分析与设计

涉及到几个对象(类)每个类中包含了什么方法

1.1.游戏的显示面板

GamePanel类

void display(); //显示

1.2.方块

Shap类

void moveLeft(); //左移void moveRight(); //右移void moveDown(); //下移void rotate(); //变形旋转void drawMe(); //显示定时向下移动(使用线程实现)

1.3.图形工厂

ShapeFactory类

//生产出不同形状的图形Shape getShape();

1.4.Ground

Ground类:

void accept()//把一个方块变成障碍物void drawMe()//显示障碍物

1.4.类之间的关系

1.4.设置事件监听

图形定时下落,没下落一次后都需要重新显示,图形移动和变形后,也需要重新显示。它又不能直接操作GamePannel,怎么实现呢?

可以通过事件监听建立联系

void shapeMoveDown(Shape);

2.游戏的初步框架

创建一个工程,搭起整个框架。敲一敲代码。从代码中去理解,去体会

2.1.创建一个工程TetrisGame

2.1.创建所需的类

Shape类:

package com.bruceliu.entity;/** * @Auther: bruceliu * @Classname Shape * @Date: 2020/2/26 14:17 * @Description: TODO */public class Shape { public void moveLeft(){ System.out.println("图形向左移"); } public void moveRight(){ System.out.println("图形向右移"); } public void moveDown(){ System.out.println("图形向下移"); } public void rotate(){ System.out.println("图形旋转"); } public void drawMe(){ System.out.println("图形画出自己"); } private class ShapeDriver implements Runnable{ @Override public void run() { // 定时向下落 while(true){ moveDown(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }}

ShapeFactory类:

/** * @Auther: bruceliu * @Classname ShapeFactory * @Date: 2020/2/26 14:18 * @Description: TODO */public class ShapeFactory { public Shape getShape(){ System.out.println("产生一个方块"); return new Shape(); } }

Ground类:

/** * @Auther: bruceliu * @Classname Ground * @Date: 2020/2/26 14:19 * @Description: TODO */public class Ground { public void accept(Shape shape){ System.out.println("把方块变成障碍物"); } public void drawMe(){ System.out.println("画出障碍物"); }}

GamePanel类

/** * @Auther: bruceliu * @Classname GamePanel * @Date: 2020/2/26 14:20 * @Description: TODO */public class GamePanel extends JPanel { private Ground ground; private com.bruceliu.entity.Shape shape; public void display(Ground ground,Shape shape){ System.out.println("GamePanel 显示"); this.ground=ground; this.shape=shape; this.repaint(); } @Override protected void paintComponent(Graphics g) { //重新显示 super.paintComponent(g); if(shape!=null && ground!=null){ shape.drawMe(); ground.drawMe(); } }}

Controller类

/** * @Auther: bruceliu * @Classname Controller * @Date: 2020/2/26 14:23 * @Description: TODO */public class Controller extends KeyAdapter { private Shape shape; private ShapeFactory shapeFactory; private Ground ground; private GamePanel gamePanel; @Override public void keyPressed(KeyEvent e){ super.keyPressed(e); switch (e.getKeyCode()) { case KeyEvent.VK_LEFT: shape.moveLeft(); break; case KeyEvent.VK_UP: shape.rotate(); break; case KeyEvent.VK_RIGHT: shape.moveRight(); break; default: break; } gamePanel.display(ground, shape); }}

创建ShapeListener-

/** * @Auther: bruceliu * @Classname ShapeListener * @Date: 2020/2/26 14:28 * @Description: TODO */public interface ShapeListener { void shapeMoveDown(Shape shape);}

在Shape下添加-

/** * @Auther: bruceliu * @Classname Shape * @Date: 2020/2/26 14:17 * @Description: TODO */public class Shape { private ShapeListener listener;// ** public void moveLeft(){ System.out.println("图形向左移"); } public void moveRight(){ System.out.println("图形向右移"); } public void moveDown(){ System.out.println("图形向下移"); } public void rotate(){ System.out.println("图形旋转"); } public void drawMe(){ System.out.println("图形画出自己"); } private class ShapeDriver implements Runnable{ @Override public void run() { // 定时向下落 while(true){ moveDown(); listener.shapeMoveDown(Shape.this);//** try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } public Shape(){//** new Thread(new ShapeDriver()).start(); } public void addShapeListener(ShapeListener l){//** if(null!=l){ this.listener=l; } }}

Controller类实现接口ShapeListener

/** * @Auther: bruceliu * @Classname Controller * @Date: 2020/2/26 14:23 * @Description: TODO */public class Controller extends KeyAdapter implements ShapeListener { private Shape shape; private ShapeFactory shapeFactory; private Ground ground; private GamePanel gamePanel; @Override public void keyPressed(KeyEvent e) { // TODO Auto-generated method stub super.keyPressed(e); switch (e.getKeyCode()) { case KeyEvent.VK_LEFT: shape.moveLeft(); break; case KeyEvent.VK_UP: shape.rotate(); break; case KeyEvent.VK_RIGHT: shape.moveRight(); break; default: break; } gamePanel.display(ground, shape); } @Override public void shapeMoveDown(Shape shape) { gamePanel.display(ground, shape); }}

为方块工厂产生的方块添加监听

/** * @Auther: bruceliu * @Classname ShapeFactory * @Date: 2020/2/26 14:18 * @Description: TODO */public class ShapeFactory { public Shape getShape(ShapeListener listener){//** System.out.println("产生一个方块"); Shape shape=new Shape(); shape.addShapeListener(listener);//** return shape; }}

Controller.java添加产生新游戏 的方法

public void newGame(){ shape=shapeFactory.getShape(this);}public Controller(ShapeFactory shapeFactory, Ground ground,GamePanel gamePanel){ this.shapeFactory=shapeFactory; this.ground=ground; this.gamePanel=gamePanel;}

设置GamePanel大小

public GamePanel(){ this.setSize(300,300);}

测试类GamePlay

/** * @Auther: bruceliu * @Classname GamePlay * @Date: 2020/2/26 14:32 * @Description: TODO */public class GamePlay { /** * @param args */ public static void main(String[] args) { ShapeFactory shapeFactory=new ShapeFactory(); Ground ground=new Ground(); GamePanel gamePanel=new GamePanel(); Controller controller=new Controller(shapeFactory, ground,gamePanel); JFrame frame=new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(gamePanel.getSize().width+10, gamePanel.getSize().height+30); frame.add(gamePanel); gamePanel.addKeyListener(controller); frame.setVisible(true); controller.newGame(); }}

3.游戏的图形绘制

3.1.图形和障碍物表示

3.2.图形状态的表示

图形变形就是一一个状态

3.4.图形绝对坐标和相对坐标

3.5.图形的移动

3.6.代码实现

在方块类Shape.java中添加二维标志,状态标志

private int[][] body;private int status;public void setBody(int body[][]){ this.body=body;}public void setStatus(int status) { this.status = status;}

在方块工厂ShapeFactory.java添加创建方块样式

/** * @Auther: bruceliu * @Classname ShapeFactory * @Date: 2020/2/26 14:18 * @Description: TODO */public class ShapeFactory { protected static int shapes[][][] = new int[][][] {//** /* 第一种 */{ /** ***** */ { 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, /* 第二种 */ { /** ********* */ { 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0 }, { 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 } }, /* 第三种 */ { /** ******* */ { 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, { 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0 } }, /* 第四种 */ { /** ******** */ { 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, } }, /* 第五种 */ { /** ******** */ { 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 } }, /* 第六种 */ { /** *********** */ { 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 }, { 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 } }, /* 第七种 */ { /** ********** */ { 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 } }, }; public Shape getShape(ShapeListener listener){ System.out.println("产生一个方块"); Shape shape=new Shape(); shape.addShapeListener(listener); int type=new Random().nextInt(shapes.length); shape.setBody(shapes[type]);//** shape.setStatus(0);//** return shape; }}

在Shape类中表示坐标的方法

/** * @Auther: bruceliu * @Classname Shape * @Date: 2020/2/26 14:17 * @Description: TODO */public class Shape { private int[][] body; private int status; private int left;//** private int top;//** public void setBody(int body[][]){ this.body=body; } public void setStatus(int status) { this.status = status; } private ShapeListener listener; public void moveLeft(){ System.out.println("图形向左移"); left--;//** } public void moveRight(){ System.out.println("图形向右移"); left++;//** } public void moveDown(){ System.out.println("图形向下移"); top++;//** } public void rotate() { System.out.println("图形旋转"); status = (status + 1) % body.length;//** } public void drawMe(){ System.out.println("图形画出自己"); } private class ShapeDriver implements Runnable{ @Override public void run() { // 定时向下落 while(true){ moveDown(); listener.shapeMoveDown(Shape.this);//** try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } public Shape(){//** new Thread(new ShapeDriver()).start(); } public void addShapeListener(ShapeListener l){//** if(null!=l){ this.listener=l; } }}

开始画出方块

画方阵标志为1的格子。标志为0的不画

修改Shape.java的drawMe()方法

public void drawMe(Graphics g) { System.out.println("图形画出自己"); g.setColor(Color.RED);//** for (int x = 0; x < 4; x++) {//** for (int y = 0; y < 4; y++) { if (getFlagByPoint(x, y)) { g.fill3DRect((left+x)* Global.CELL_SIZE, (top+y)*Global.CELL_SIZE, Global.CELL_SIZE, Global.CELL_SIZE, true); } } } } private boolean getFlagByPoint(int x, int y) {//** return body[status][y * 4 + x] == 1; }

/** * @Auther: bruceliu * @Classname Global * @Date: 2020/2/26 15:02 * @Description: TODO */public class Global { public static final int CELL_SIZE=20;}

修改GamePanel.java

@Overrideprotected void paintComponent(Graphics g) { //重新显示 super.paintComponent(g); g.fillRect(0, 0, 300, 300);//** g.setColor(new Color(0xcfcfcf));//** if(shape!=null && ground!=null){ shape.drawMe(g);//** ground.drawMe(); }}

public static void main(String[] args) { // TODO Auto-generated method stub ShapeFactory shapeFactory=new ShapeFactory(); Ground ground=new Ground(); GamePanel gamePanel=new GamePanel(); Controller controller=new Controller(shapeFactory, ground,gamePanel); JFrame frame=new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(gamePanel.getSize().width+10, gamePanel.getSize().height+30); frame.add(gamePanel); gamePanel.addKeyListener(controller); frame.addKeyListener(controller);//** frame.setVisible(true); controller.newGame(); }

4.游戏的完善

碰到游戏面板的边界时,要让方块做出反应。碰左壁,不能再向左移。碰右壁,不能再向右移

在Global.java添加两个常量

/** * @Auther: bruceliu * @Classname Global * @Date: 2020/2/26 15:02 * @Description: TODO */public class Global { public static final int CELL_SIZE=20; //格子的宽高 public static final int WIDTH=15; public static final int HEIGHT=15; }

修改GamePanel.java的大小

@Overrideprotected void paintComponent(Graphics g) { //重新显示 super.paintComponent(g); //g.fillRect(0, 0, 300, 300);//** g.fillRect(0, 0, Global.WIDTH*Global.CELL_SIZE,Global.HEIGHT*Global.CELL_SIZE); g.setColor(new Color(0xcfcfcf));//** if(shape!=null && ground!=null){ shape.drawMe(g);//** ground.drawMe(); }}

public GamePanel(){ this.setSize(Global.WIDTH*Global.CELL_SIZE,Global.HEIGHT*Global.CELL_SIZE); }

在Shape.java中添加几个方向常量

public static final int ROTATE = 0;public static final int LEFT = 1;public static final int RIGHT = 2;public static final int DOWN = 3;

在Shape.java中添加取得方块坐标的方法和判断是否自己成员的方法

public int getTop() { return top; } public int getLeft() { return left; } public boolean isMember(int x, int y, boolean rotate) { int tempStatus = status; //发生了旋转 if (rotate) { tempStatus = (status + 1) % body.length; } return body[tempStatus][y * 4 + x] == 1; }

在Ground.java中添加如下方法

public boolean isMoveable(Shape shape, int action) { int left = shape.getLeft(); int top = shape.getTop(); switch (action) { case Shape.LEFT: left--; break; case Shape.RIGHT: left++; break; case Shape.DOWN: top++; break; } //依次取出来图形中的点,判断是否超出显示区域 for (int x = 0; x < 4; x++) { for (int y = 0; y < 4; y++) { if (shape.isMember(x, y, action == Shape.ROTATE)) { if (top + y >= Global.HEIGHT || left + x < 0 || left + x >= Global.WIDTH) return false; } } } return true; }

在Controller.java下添加如下代码

@Overridepublic void keyPressed(KeyEvent e) { // TODO Auto-generated method stub super.keyPressed(e); switch (e.getKeyCode()) { case KeyEvent.VK_LEFT: if (ground.isMoveable(shape, Shape.LEFT)) shape.moveLeft(); break; case KeyEvent.VK_UP: if (ground.isMoveable(shape, Shape.ROTATE)) shape.rotate(); break; case KeyEvent.VK_RIGHT: if (ground.isMoveable(shape, Shape.RIGHT)) shape.moveRight(); break; case KeyEvent.VK_DOWN: if (ground.isMoveable(shape, Shape.DOWN)) shape.moveDown(); break; default: break; } gamePanel.display(ground, shape);}

/** * @Auther: bruceliu * @Classname ShapeListener * @Date: 2020/2/26 14:28 * @Description: TODO */public interface ShapeListener { void shapeMoveDown(Shape shape); boolean isShapeMoveDownable(Shape shape);}

修改实现该接口的类Controller.java

@Overridepublic boolean isShapeMoveDownable(Shape shape) { boolean result = ground.isMoveable(shape, Shape.DOWN); return result;}

修改Shape.java的定时下落

private class ShapeDriver implements Runnable { @Override public void run() { // 定时向下落 while (listener.isShapeMoveDownable(Shape.this)) { moveDown(); listener.shapeMoveDown(Shape.this);//** try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }

将图形变成障碍物和显示 修改Controller类的方法isShapeMoveDownable为同步。

@Override public synchronized boolean isShapeMoveDownable(Shape shape) { boolean result = ground.isMoveable(shape, Shape.DOWN); return result; }

修改Controller类的方法isShapeMoveDownable,图形到达底部,产生新图形

@Overridepublic synchronized boolean isShapeMoveDownable(Shape shape) { if(ground.isMoveable(shape, Shape.DOWN)){ return true; } //将图形变为障碍物 ground.accept(this.shape); //重新产生一个图形 this.shape = shapeFactory.getShape(this); return false;}

private int[][] obstacles = new int[Global.WIDTH][Global.HEIGHT]; public void accept(Shape shape) { System.out.println("把方块变成障碍物"); for (int x = 0; x < 4; x++) { for (int y = 0; y < 4; y++) { if (shape.isMember(x, y, false)) { obstacles[shape.getLeft() + x][shape.getTop() + y] = 1; } } } } public void drawMe(Graphics g) { System.out.println("画出障碍物"); for (int x = 0; x < Global.WIDTH; x++) { for (int y = 0; y < Global.HEIGHT; y++) { if (obstacles[x][y] == 1) { g.fill3DRect(x * Global.CELL_SIZE, y * Global.CELL_SIZE, Global.CELL_SIZE, Global.CELL_SIZE, true); } } } }

修改GamePanel.java

@Overrideprotected void paintComponent(Graphics g) { //重新显示 super.paintComponent(g); //g.fillRect(0, 0, 300, 300);//** g.fillRect(0, 0, Global.WIDTH*Global.CELL_SIZE,Global.HEIGHT*Global.CELL_SIZE); g.setColor(new Color(0xcfcfcf));//** if(shape!=null && ground!=null){ shape.drawMe(g);//** ground.drawMe(g); }}

当碰到障碍物的时候也停止,修改Ground下的

public boolean isMoveable(Shape shape, int action) { int left = shape.getLeft(); int top = shape.getTop(); switch (action) { case Shape.LEFT: left--; break; case Shape.RIGHT: left++; break; case Shape.DOWN: top++; break; } for (int x = 0; x < 4; x++) { for (int y = 0; y < 4; y++) { if (shape.isMember(x, y, action == Shape.ROTATE)) { if (top + y >= Global.HEIGHT || left + x < 0 || left + x >= Global.WIDTH||1==obstacles[left+x][top+y]) return false; } } } return true; }

消除满行的障碍物 这一行没有空白,则这一行就是满行了。上面所有的行,整体下移一行。

在Ground.java下添加

public void accept(Shape shape) { System.out.println("把方块变成障碍物"); for (int x = 0; x < 4; x++) { for (int y = 0; y < 4; y++) { if (shape.isMember(x, y, false)) { obstacles[shape.getLeft() + x][shape.getTop() + y] = 1; } } } deleteFullLine();}private void deleteFullLine() { for (int y = Global.HEIGHT - 1; y > 0; y--) { boolean full = true; for (int x = 0; x < Global.WIDTH; x++) { if (0 == this.obstacles[x][y]) { full = false; } } if (full) { deleteLine(y++); } }}private void deleteLine(int lineNum) { for (int y = lineNum; y > 0; y--) { for (int x = 0; x < Global.WIDTH; x++) { this.obstacles[x][y] = this.obstacles[x][y - 1]; } } for (int x = 0; x < Global.WIDTH; x++) { this.obstacles[x][0] = 0; }}

增加游戏结束功能 如果有的障碍物超出了上边界,就是游戏结束,第一行如果有障碍物,就是游戏结束

在Ground.java下添加

public boolean isFull() { for (int x = 0; x < Global.WIDTH; x++) { if (1 == this.obstacles[x][0]) { return true; } } return false;}

在Controller.java下判断

@Overridepublic synchronized boolean isShapeMoveDownable(Shape shape) { if (ground.isMoveable(shape, Shape.DOWN)) return true; ground.accept(this.shape); if(!ground.isFull()) { this.shape = shapeFactory.getShape(this); }else{ System.out.println("game over"); } return false;}

解决定时下落与按下键同时生成障碍物的问题

在controller.java中修改

@Override public void keyPressed(KeyEvent e) { // TODO Auto-generated method stub super.keyPressed(e); switch (e.getKeyCode()) { case KeyEvent.VK_LEFT: if (ground.isMoveable(shape, Shape.LEFT)) shape.moveLeft(); break; case KeyEvent.VK_UP: if (ground.isMoveable(shape, Shape.ROTATE)) shape.rotate(); break; case KeyEvent.VK_RIGHT: if (ground.isMoveable(shape, Shape.RIGHT)) shape.moveRight(); break; case KeyEvent.VK_DOWN: if(this.isShapeMoveDownable(shape)) shape.moveDown(); break; default: break; } gamePanel.display(ground, shape); }

@Overridepublic synchronized boolean isShapeMoveDownable(Shape shape) { if(this.shape!=shape){ return false; } if (ground.isMoveable(shape, Shape.DOWN)) return true; ground.accept(this.shape); if(!ground.isFull()) { this.shape = shapeFactory.getShape(this); }else{ System.out.println("game over"); } return false;}

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Commons-dbutils框架
下一篇:ElasticSearch专题(三)-ES术语
相关文章

 发表评论

暂时没有评论,来抢沙发吧~