时隔好久才来写技术博客,其实我写技术博客就是想提醒自己学会了一些什么东西,把我认为很重要的东西记录下来,如果我没有弄懂这个东西我是不会随便乱写的,因为我觉得写技术博客首先就是写给自己看,其次是给大家看,自己都没弄懂那你写的东西大家也会看不懂的。下面这个问题是我们最近学的游戏框架问题,主要就是为了解决规范化的书写代码,可以把你的代码供他人使用,就像我们平常用的东西就是其他人写好的供我们用,代码的好坏就是看代码够不够简洁,容不容易更改,可不可以打包发给其他人使用而不出问题。
本次要做的就是一个“打飞机”的游戏,就是飞机可以不断的移动并且子弹是自动发射,中途不断有boss出现,boss也会发出子弹,背景是在星空。我们要做的是让代码看起来清晰、容易修改。
在这其中我们需要有
????????? 1.一个移动物体Moveable接口,在这个类中要有方法:移动方法move()、draw()画出自己模样的方法、canRemove()移除方法:当物体不再需要而进行移除;而飞机plane、boss、bullet(子弹)implements这个接口,实现其中的方法,规定自己的移动规则等。
????????
class="java">package Model2; import java.awt.Graphics; /** * 游戏里面的实体接口类 * 游戏中的每个实体,如人物、怪物,敌人等都要实现该接口 * */ public interface Moveable { /** * 物体移动的方法 * 每个物体的特性、移动规律等都不相同,要在具体的实现类中去实现 */ public void move(); /** * 物体绘制方法 * 将物体画到UI界面上,具体要怎么画,每个物体都不一样,都要在各自具体的实现类中去实现 * @param g 物体要画到哪个界面上去的画布 */ public void draw(Graphics g); /** * 判断物体是否可以被删除了 * @return */ public boolean canRemove(); }
?2.Data类,在这个类中存放的是整个游戏的所有数据,这是为了方便修改,然而这个类要写成单实例,所谓单实例就是只允许有一个类的对象,而实现这个就是要做一标识instance,有个getInstance()方法,如果instance是null,则新new? Data()否则返回instance此类中包括移动物体的图片,界面的宽、高获以及取这些数据的方法,还要有队列,因为每次画的物体都要保存到队列中具体类的内容如下
?
package com.sxf0912第二节课; import java.util.ArrayList; import java.util.List; import javax.swing.ImageIcon; import Model2.Moveable; import Model2.Plane; public class Data { private static Data intertance; private int ScreenWidth=400; private int ScreenHeight=400; private Plane plane; public Plane getPlane() { //System.out.print(planeImage.getImage().getHeight( null)); return plane; } public void setPlane(Plane plane) { this.plane = plane; //System.out.print(planeImage.getImage().getHeight( null)); } //定义一个线程安全队列保存所有移动的物体 private List<Moveable> moveList=java.util.Collections.synchronizedList(new ArrayList<Moveable>()); //背景图片、飞机图片、boss图片、子弹爆炸图片数组 private ImageIcon bgImage=new ImageIcon("image/BJ.png"); private ImageIcon planeImage=new ImageIcon("image/plane.png"); private ImageIcon bossImage=new ImageIcon("image/boss.png"); private ImageIcon[] bulletIcons = {new ImageIcon("image/0.PNG"),new ImageIcon("image/1.PNG"),new ImageIcon("image/2.PNG"),new ImageIcon("image/3.PNG"),new ImageIcon("image/4.PNG"),new ImageIcon("image/5.PNG"),new ImageIcon("image/6.PNG"),new ImageIcon("image/7.PNG")}; public ImageIcon getBossImage() { return bossImage; } public ImageIcon[] getBulletIcons() { return bulletIcons; } public ImageIcon getPlaneImage() { return planeImage; } public ImageIcon getBgImage() { return bgImage; } private Data(){ } public static Data getIntertance() { if(intertance==null){ intertance= new Data(); } return intertance; } public int getScreenWidth() { return ScreenWidth; } public int getScreenHeight() { return ScreenHeight; } public List<Moveable> getMoveList() { return moveList; } }
?3.control类,这是一个线程类,控制移动的物体,因为移动的物体有很多所以继承thread,move()方法是要把从Data类中的队列取出来,然后再调用队列中moveable的move()方法,具体代码如下
package com.sxf0912第二节课; import java.util.List; import Model2.Boss; import Model2.Moveable; //控制类, 控制游戏中所有物体的移动,及生成新的敌人等物体 public class Control extends Thread { private int n; public void run(){ while(true){ move(); try{ sleep(20); }catch(InterruptedException e){ e.printStackTrace(); } if(n++%500==0){ //出现boss Boss boss=new Boss(); Data.getIntertance().getMoveList().add(boss); } } } public void move(){ Data data=Data.getIntertance(); List<Moveable> moveList=data.getMoveList(); for(int i=0;i<moveList.size();i++){ Moveable moveable=moveList.get(i); if(moveable.canRemoved()){ ??? moveList.remove(moveable); ???? i--; ??? } else { ????moveable.move(); ?? ?} } } }
?4.UI类,界面类实现Runnable接口,其中方法initUI()是界面初始化,规定大小,结构,可见性等,drawbg()方法画背景的方法,从Data中取得背景图片,画两次,因为背景不断移动,为的是两次刚好接上,而且因为背景是动的,y值不断改变,所以每次调用此方法后要将y值变为0,drawMoveable():画出移动的物体,将存放移动物体的队列从Data中取出来,便利,在调用moveable的draw()方法进行画图在指定的画布上,draw()方法,为了让图片看起来是动的而不用画完擦除,采用缓冲的方法,首先获得缓冲图片的画布,将背景和移动的物体依次画在缓冲图片画布上,然后再将窗体画布上画上缓冲图片,还有键盘监听器,给飞机加上键盘监听器,因为这里需要用到飞机所以在Data类中要将飞机设置成属性具体代码如下:
package com.sxf0912第二节课; import java.awt.Graphics; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.image.BufferedImage; import java.util.List; import javax.swing.ImageIcon; import javax.swing.JFrame; import Model2.Moveable; import Model2.Plane; public class UI implements Runnable { private JFrame jf=new JFrame(); public void initUI(){ Data data =Data.getIntertance(); jf.setSize(data.getScreenWidth(),data.getScreenHeight()); jf.setLocationRelativeTo(null); jf.setResizable(false); jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); jf.setVisible(true); jf.addKeyListener(keylistener); } public void run() { Graphics graphics=jf.getGraphics(); while(true){ draw(graphics); try{ Thread.sleep(20); }catch(Exception e){ e.printStackTrace(); } } } public void draw(Graphics graphics){ //创建图片缓冲对象 BufferedImage image =new BufferedImage (jf.getWidth(), jf.getHeight(), BufferedImage.TYPE_INT_RGB); Graphics g=image.getGraphics(); //画背景在缓冲区的图片上 drawbg(g); //在缓冲区画移动的物体 drawMoveable(g); //将缓冲区的图片画到界面上 graphics.drawImage(image, 0, 0, jf); } private Data data=Data.getIntertance(); private int bgX,bgY; private void drawbg(Graphics g){ //g.setColor(jf.getBackground()); // g.fillRect(0, 0, jf.getWidth(), jf.getHeight()); ImageIcon image=data.getBgImage(); g.drawImage(image.getImage(), bgX, bgY, jf); g.drawImage(image.getImage(), bgX, bgY-image.getIconHeight(), jf); bgY++;//让图片动起来 if(bgY>jf.getHeight()){ bgY=0; } } private void drawMoveable(Graphics g){ List<Moveable> moveList=data.getMoveList(); for(int i=0;i<moveList.size();i++){ Moveable moveable =moveList.get(i); moveable.draw(g); } } KeyListener keylistener =new KeyListener(){ @Override public void keyTyped(KeyEvent e) { // TODO Auto-generated method stub } @Override public void keyPressed(KeyEvent e) { Plane plane =data.getPlane(); int code =e.getKeyCode(); switch (code){ case KeyEvent.VK_LEFT: plane.leftMove(true); break; case KeyEvent.VK_RIGHT: plane.rightMove(true); break; case KeyEvent.VK_UP: plane.upMove(true); break; case KeyEvent.VK_DOWN: plane.downMove(true); break; } } @Override public void keyReleased(KeyEvent e) { Plane plane =data.getPlane(); int code =e.getKeyCode(); switch (code){ case KeyEvent.VK_LEFT: plane.leftMove(false); break; case KeyEvent.VK_RIGHT: plane.rightMove(false); break; case KeyEvent.VK_UP: plane.upMove(false); break; case KeyEvent.VK_DOWN: plane.downMove(false); break; } } }; }
?
?5.plane类,实现Moveable,其中实现三个方法,还要有上下左右移动方法的设置标识方法,以及上下左右移动坐标变化的方法,具体代码如下:
package Model2; import java.awt.Graphics; import javax.swing.ImageIcon; import com.sxf0912第二节课.Data; public class Plane implements Moveable{ private int x,y; private boolean LEFT=false; private boolean RIGHT=false; private boolean UP=false; private boolean DOWN=false; private int speed=5; public Plane(){ x=Data.getIntertance().getScreenWidth()/2; y=Data.getIntertance().getScreenHeight()-100; //System.out.print(x); } int n=0; public void move() { if(LEFT){ x-=speed; //防止飞机超过窗体 if(x<0) x=0; } if(RIGHT){ x+=speed; //防止飞机超过窗体 if(x>Data.getIntertance().getScreenWidth()-Data.getIntertance().getPlaneImage().getIconWidth()) x=Data.getIntertance().getScreenWidth()-Data.getIntertance().getPlaneImage().getIconWidth(); } if(UP){ y-=speed; if(y<0) y=0; } if(DOWN){ y+=speed; if(y>Data.getIntertance().getScreenHeight()-Data.getIntertance().getPlaneImage().getIconHeight()) y=Data.getIntertance().getScreenWidth()-Data.getIntertance().getPlaneImage().getIconHeight(); } if(n++%20==0){ Bullet bullet=new Bullet(x+25,y,270); //System.out.print(x); Data.getIntertance().getMoveList().add(bullet); } } public void leftMove(boolean flag){ LEFT=flag; } public void rightMove(boolean flag){ RIGHT=flag; } public void upMove(boolean flag){ UP=flag; } public void downMove(boolean flag){ DOWN=flag; } @Override public void draw(Graphics g) { ImageIcon image=Data.getIntertance().getPlaneImage(); g.drawImage(image.getImage(), x, y, null); //System.out.print(x); } @Override public boolean canRemove() { // TODO Auto-generated method stub return false; } }
?6.boss类,同样实现Moveable接口,实现三个方法,移动,画,移除方法,然而boss要随机出现,具体代码如下:
package Model2; import java.awt.Graphics; import java.util.Random; import javax.swing.ImageIcon; import com.sxf0912第二节课.Data; public class Boss implements Moveable{ private int x,y; private int n; public Boss(){ Random rand =new Random(); y=-200; x=rand.nextInt(Data.getIntertance().getScreenWidth()-Data.getIntertance().getBossImage().getIconWidth()); } public void move() { y++; if(n++%100==0){ ImageIcon image=Data.getIntertance().getBossImage(); Bullet bullet =new Bullet(x+image.getIconWidth()/2,y+image.getIconHeight(),90); Data.getIntertance().getMoveList().add(bullet); } } public void draw(Graphics g) { ImageIcon image =Data.getIntertance().getBossImage(); g.drawImage( image.getImage(), x, y, null); } @Override public boolean canRemove() { if(y > Data.getInstance().getScreenHeight()) return true; return false; } }
?7.bullet类,同样实现了Moveable类,实现三个方法,具体代码如下
package Model2; import java.awt.Graphics; import javax.swing.ImageIcon; import com.sxf0912第二节课.Data; public class Bullet implements Moveable{ private int x,y; private int speed=3; private double angle; private int n; private int vx,vy; /** * 初始化子弹 * @param x 初始位置坐标 * @param y 初始位置坐标 * @param angle 角度 */ public Bullet(int x,int y,double angle){ this.x=x; this.y=y; this.angle=angle*Math.PI/180; this.vx=(int)(speed*(Math.cos(this.angle))); this.vy=(int)(speed*(Math.sin(this.angle))); } public void move() { x+=vx; y+=vy; } public void draw(Graphics g) { //子弹爆炸 if(++n>50){ int i=(n-50)/2; if(i>7) return; ImageIcon [] icons=Data.getIntertance().getBulletIcons(); int xx = x-icons[i].getIconWidth()/2; int yy = y-icons[i].getIconHeight()/2; g.drawImage(icons[i].getImage(),xx , yy, null); return; } //普通子弹 g.fillOval(x, y, 5, 5); } public boolean canRemove() { if(x<0||x>Data.getIntertance().getScreenWidth()||y<0||y>Data.getIntertance().getScreenHeight()){ return true; } return false; } }
?8.game类,控制游戏开始的类
package com.sxf0912第二节课; import Model2.Plane; public class Game { //游戏启动 public static void main(String[] args) { Plane plane =new Plane(); Data.getIntertance().setPlane(plane); Data.getIntertance().getMoveList().add(plane); //启动界面 UI ui =new UI(); ui.initUI(); new Thread(ui).start(); //启动移动控制线程 new Control().start(); } }
?综上就是打飞机游戏的简单功能,至于子弹与飞机以及boss碰撞并发生爆炸,其中一方消失我还没有加上,等以后写出来会来加上。
?