今天是2015年6月18号,写博客的习惯也已经有了一年多的时间了,记得2014年初,刚开始接触
java语言那种兴奋和激动,看到面向对象设计语言如此美妙现实的作用,就暗地下给自己鼓劲一定要
把java语言学好。2014年暑假在家看了俄罗斯方块的视频,虽然线程和设计模式都不是很懂,但是还
是觉得很有感触。现在马上要大四了,也面临毕业就业的问题,看到市面上五花八门的项目,自己以
前的高中同学现在都在公司搞得有声有色,发现自己平常也有学习啊,怎么做不出来商业项目呢?现在
还是要静下心来学习巩固好以前的知识,体会编程的魅力。
上面都是题外话,只是发表一下自己的感受。现在开始步入正题:
步骤一:
预期:
俄罗斯方块都玩过,
对类的详细分析:java设计的原则,尽量在拥有数据的类里面定义好方法来操作数据。比如在黑板上画圆,要让圆来提供画圆的方法。
ShapeFactory.java
如何来描述六种不同的形状呢?不管是哪一种形状,都是在一个4*4的网格中。因此,我们可以用一个二维数组,用0表示4*4网格的某一个点没有被绘制,用1表示被绘制了。由于一共有六种图形是多个二维数组的组合,所以最外层是三维数组。
private int shapes[][][]=new int [][][]{ { {1,0,0,0, 1,1,1,0, 0,0,0,0, 0,0,0,0},//00 {1,1,0,0,1,0,0,0, 1,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 {0,1,0,0, 0,1,0,0, 0,1,1,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, 1,1,0,0, 0,0,0,0, 0,0,0,0}//00 表示正方形 },// 00 { {1,1,1,1,0,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} //0 // 0 // 0 }, { {1,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0},//0 {0,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0},//00 {0,1,0,0,1,1,0,0,0,1,0,0,0,0,0,0},//0 {1,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0} }, { {1,0,0,0,1,1,0,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} },//00 表示树状 { {0,1,0,0,1,1,0,0,1,0,0,0,0,0,0,0},// 0 {1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0} } };
ShapeFactory生产各式各样的Shape
public Shape getShape(ShapeListener listener) { System.out.println("ShapeFactory's getShape"); Shape shape=new Shape(); shape.addShapeListener(listener); int type=new Random().nextInt(shapes.length); shape.setBody(shapes[type]); shape.setStatus(new Random().nextInt(shapes[type].length)); return shape; }
实例化shape,添加监听器,产生6种形状里的一种。设置具体形状的状态,返回。
ShapeListener.java
package Listener; import entities.Shape; public interface ShapeListener { void shapeMoveDown(Shape shape); boolean isShapeMoveDownable(Shape shape); }
给形状设定的监听器,一个接口。类似于在android开发里有一个OnItemClickListener,表示组件的单击事件,组件比如button调用setOnItemClickListener(new OnItemClickListener()).最后调用onclick方法,
ShapeListener主要有ShapeMoveDown(),形状移动,isShapeMoveDownable()方法,形状能否移动(碰到障碍物了吗)。
这样理解:形状是一个”抽象“的,里面有具体的哪一种形状(柱状,正方形),所以用了接口。
组件是一个“抽象的”,具体有哪一种组件(TextView,ImageView,Button),所以也使用接口。
接下来就是Shape类了
Shape.java
package entities; import java.awt.Color; import java.awt.Graphics; import until.Global; import Listener.ShapeListener; public class Shape { //常量,不能改变,属性共有,静态的,表示是变形还是左右下 public static final int ROTATE=0; public static final int LEFT=1; public static final int RIGHT=2; public static final int DOWN=3; //每一种形状又有多种摆放的方法,比如柱体可以横或竖着放 private int[][]body; // private int status; private int left; private int top; private ShapeListener listener; public void moveLeft() { System.out.println("shape's moveleft"); left--; } public void moveRight() { System.out.println("shape's moveright"); left++; } public void moveDown() { System.out.println("shape's moveDown"); top++; } public void rotate() { System.out.println("shape's rotate"); //同时状态要改变 status=(status+1)%body.length; //} public void drawMe(Graphics g) { System.out.println("shape'drawme"); g.setColor(Color.BLUE); 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); } } } } //如果在4*4网格里有,就返回为true private boolean getFlagByPoint(int x,int y) { return body[status][y*4+x]==1; } 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; } private class ShapeDriver implements Runnable { public void run() { while(listener.isShapeMoveDownable(Shape.this)) { moveDown(); listener.shapeMoveDown(Shape.this); try{ Thread.sleep(400); } catch(InterruptedException e) { e.printStackTrace(); } } } } public Shape(){ new Thread(new ShapeDriver()).start(); } public void addShapeListener( ShapeListener l){ if(l!=null) listener=l; } public void setBody(int body[][]) { this.body=body; } public void setStatus(int status) { this.status=status; } public int getTop() { return top; } public int getLeft() { return left; } }
考虑到Shape是动一下休息一下在动这样主键下降的,因此我们要开启线程来控制这个,动完了以后休息一下再动。最后在构造方法里(最先被调用的方法里)来开辟这个线程。
现在有了工厂并且生产了Shape,接下来就要关心Ground类了
Ground.java
package entities; import java.awt.Graphics; import until.Global; public class Ground { //长宽分别为Global.HEIGHT,Global.WIDTH的Ground,如果 //被占据,是一个障碍,则设为一 private int[][] obstacles=new int[Global.WIDTH][Global.HEIGHT]; public void accept(Shape shape) { System.out.println("Ground's accept"); 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(); } //一行全为1才删 private void deleteFullLine() { for(int y=Global.HEIGHT-1;y>=0;y--){ boolean full=true; for(int x=0;x0;y--){ for(int x=0;x =Global.HEIGHT ||left+x<0||left+x>=Global.WIDTH|| obstacles[left+x][top+y]==1 ) return false; } } } return true; } public boolean isFull(){ for(int x=0;x
最后需要在GamePanel里面会好Ground和Shape
package view; import java.awt.Color; import java.awt.Graphics; import javax.swing.JPanel; import until.Global; import entities.Ground; import entities.Shape; public class GamePanel extends JPanel { private Ground ground; private Shape shape; //private GamePanel gamepanel; public void display(Ground ground,Shape shape) { System.out.println("GamePanel's display"); this.ground=ground; this.shape=shape; this.repaint(); } @Override protected void paintComponent(Graphics g) { // TODO Auto-generated method stub g.setColor(new Color(0xAABBEE)); g.fillRect(0,0,Global.WIDTH*Global.CELL_SIZE,Global.HEIGHT*Global.CELL_SIZE); if(shape!=null&&ground!=null) shape.drawMe(g); ground.drawMe(g); } public GamePanel(){ this.setSize(Global.WIDTH*Global.CELL_SIZE,Global.HEIGHT*Global.CELL_SIZE); } }
最后就是逻辑处理了
按键逻辑
package Controller; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import Listener.ShapeListener; import view.GamePanel; import entities.Ground; import entities.Shape; import entities.ShapeFactory; public class Controller extends KeyAdapter implements ShapeListener { private Shape shape; private ShapeFactory shapeFactory; private Ground ground; private GamePanel gamePanel; public void keyPressed(KeyEvent e){ switch(e.getKeyCode()){ case KeyEvent.VK_UP: if(ground.isMoveable(shape, Shape.ROTATE)) shape.rotate(); break; case KeyEvent.VK_DOWN: if( isShapeMoveDownable(shape)) shape.moveDown(); break; case KeyEvent.VK_LEFT: if(ground.isMoveable(shape, Shape.LEFT)) shape.moveLeft(); break; case KeyEvent.VK_RIGHT: if(ground.isMoveable(shape, Shape.RIGHT)) shape.moveRight(); break; } gamePanel.display(ground,shape); } public void shapeMoveDown(Shape shape){ gamePanel.display(ground,shape); } public synchronized boolean isShapeMoveDownable(Shape shape) { if(this.shape!=shape) return false; boolean result=ground.isMoveable(shape, Shape.DOWN); if(result) return true; ground.accept(this.shape); if(!ground.isFull()) this.shape=shapeFactory.getShape(this); return false; } public void newGame(){ shape=shapeFactory.getShape(this); } public Controller( ShapeFactory shapeFactory,Ground ground,GamePanel gamePanel) { this.shapeFactory=shapeFactory; this.ground=ground; this.gamePanel=gamePanel; } }
Panel逻辑
package Controller; import java.awt.Color; import javax.swing.*; import until.Global; public class ControlPanel extends JPanel{ public ControlPanel(){ this.setVisible(true); this.setSize(2*Global.WIDTH*(Global.CELL_SIZE),Global.HEIGHT*Global.CELL_SIZE); this.setBackground(Color.gray); } }
主方法
package test; import javax.swing.JFrame; import Controller.ControlPanel; import Controller.Controller; import until.Global; import view.GamePanel; import entities.Ground; import entities.ShapeFactory; public class Game { public static void main(String args[]) { ShapeFactory shapeFactory=new ShapeFactory(); Ground ground=new Ground(); GamePanel gamePanel=new GamePanel(); ControlPanel controlpanel=new ControlPanel(); Controller controller=new Controller(shapeFactory,ground,gamePanel); JFrame frame=new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(2*Global.WIDTH*Global.CELL_SIZE+10 , Global.HEIGHT*Global.CELL_SIZE+35); frame.add(gamePanel); frame.add(controlpanel); gamePanel.addKeyListener(controller); frame.addKeyListener(controller); frame.setVisible(true); controller.newGame(); } }
工具类
package until; public class Global { public static final int CELL_SIZE=20; public static final int WIDTH=15; public static final int HEIGHT=25; }
其实游戏开发不是很难吧,还挺有趣的,现在awt用的比较少了