时隔好久才来写技术博客,其实我写技术博客就是想提醒自己学会了一些什么东西,把我认为很重要的东西记录下来,如果我没有弄懂这个东西我是不会随便乱写的,因为我觉得写技术博客首先就是写给自己看,其次是给大家看,自己都没弄懂那你写的东西大家也会看不懂的。下面这个问题是我们最近学的游戏框架问题,主要就是为了解决规范化的书写代码,可以把你的代码供他人使用,就像我们平常用的东西就是其他人写好的供我们用,代码的好坏就是看代码够不够简洁,容不容易更改,可不可以打包发给其他人使用而不出问题。
本次要做的就是一个“打飞机”的游戏,就是飞机可以不断的移动并且子弹是自动发射,中途不断有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碰撞并发生爆炸,其中一方消失我还没有加上,等以后写出来会来加上。
?