简介:

运用JFrame窗口、Graphics绘图工具、Thread类等类的方法,结合多态类的写法,进行创作。

第一步:前期准备工作

第一:需要设定窗口的大小。此类数据对我们的代码的后继十分重要,并为了防止在后继的编写遗忘或不小心更改,我们设定了一个单独的包来储存。

/*
 * 固定常数
 */
public class Constant{
	public static final int GAME_WIDTH=500;
	public static final int GAME_HEIGHT=500;

}

第二步:创建父类__设定飞机__设定导弹

(1)创建GameObject父类

创建一个完善的父类会对我们后继的补写代码有着事半功倍的作用;在这里我们对父类创造了几个常用变量:坐标(x,y)、图像的宽度:weight、图像的长度:height 以及图像的运行速度:speed

package planeGame;
import java.awt.*;
/**
 * 游戏物体的父类
 * @author Lenovo
 *
 */
public class GameObject {
	Image image;//图像
	double x,y;
	int speed=6;
	int width,height;
	public void drawSelf(Graphics g) {//绘画自己
		g.drawImage(image, (int)x, (int)y,null);
	}

在这里有一个编写构造器的快捷技巧:alt+shift+s”,然后再按一下“o”会产生下面的有参数的代码:

public GameObject(Image image, double x, double y, int speed, int width, int height) {
		super();
		this.image = image;
		this.x = x;
		this.y = y;
		this.speed = speed;
		this.width = width;
		this.height = height;
	}

后我们建一个无参函数,快捷键为“alt+shift+s”,然后再按一下“c”:从超类中生成构造函数

public GameObject() {
		super();
		// TODO 自动生成的构造函数存根
	}

以上我们的父类初始化就完成了。接下来需要引入一点:物体的矩形的概念。在我们JFrame的窗口中,需要把所有的图像均以矩形的形式展现,在后面对碰撞的研究中,用来判断目标图像的位置和矩形范围来判断是否碰撞。所以我们还需要引入Rectangle类来返回矩形。

/**
	 * 返回物体所在的矩形  适用于物体碰撞的检测
	 * @return
	 */
	public Rectangle getRect() {
		return new Rectangle((int)x,(int)y,width,height);
	}

这样我们的父类就完美结束了!

(2)设定飞机

创建一个Plane包extends GameObject,同时创建构造器

/*
 * 飞机的类
 */
public class Plane extends GameObject{
public Plane(Image img,double x,double y) {
		this.image=img;
		this.y=y;
		this.x=x;
		this.width=img.getWidth(null);//图片宽
		this.height=img.getHeight(null);//图片高
	}
}

这里我们有一个疑问,飞机模型已经创造好了,可是这对我们的游戏要求远远不够。我们需要用键盘来操作他,所以我们引入了下一个知识点:键盘控制游戏物体原理。
首先我们需要在主类MyGame中创建按键函数和抬起函数;Key Event e就是我们摁键的变量赋值。

/*键盘控制游戏:
	 * 定义键盘键盘监听内部类
	 * 右击选源码->覆盖/实现方法->keyPressed(按键函数)和keyReleased(抬起函数)
	 */
	class KeyMonitor extends KeyAdapter {

		@Override
		public void keyPressed(KeyEvent e) {
			plane.addDirection(e);
		}

		@Override
		public void keyReleased(KeyEvent e) {
			plane.minusDirection(e);
		}		
	}

然后我们开始在Plane类创造摁键后的方法

boolean left,up,right,down;//这里的方向的类型是boolean,方便我们判断飞机的运行方向
boolean leave=true;//是否存活
	public void drawSelf(Graphics g) {
		if(leave) {
		if(left) {
			x-=speed;
			if(x<0)
				leave=false;
		}
		if(right) {
			x+=speed;
			if(x>Constant.GAME_WIDTH)
				leave=false;
		}
		if(up) {
			y-=speed;
			if(y<40)
				leave=false;
		}
		if(down) {
			y+=speed;
			if(y>Constant.GAME_HEIGHT)
				leave=false;
		}
		g.drawImage(image, (int)x, (int)y,null);
		}
		else {

		}
	}
		//按下某个键增加相反的方向
	public void addDirection(KeyEvent e) {
		switch(e.getKeyCode()) {
		case KeyEvent.*VK_LEFT*://按键“->”所对应的值
			left =true; 
			break;
		case KeyEvent.VK_UP:
			up =true; 
			break;
		case KeyEvent.VK_RIGHT:
			right =true; 
			break;
		case KeyEvent.VK_DOWN:
			down =true; 
			break;
		}
	}
	//取消某个键增加相反的方向
	public void minusDirection(KeyEvent e) {
		switch(e.getKeyCode()) {
		case KeyEvent.VK_LEFT://按键“->”所对应
			left =false; 
			break;
		case KeyEvent.VK_UP:
			up =false; 
			break;
		case KeyEvent.VK_RIGHT:
			right =false; 
			break;
		case KeyEvent.VK_DOWN:
			down =false; 
			break;
		}
	}

(3)设定导弹

我们这个1.0的飞机大战采用的是定点随机方向发射一定数量的导弹,并碰到四壁时会反弹,我们需要运用随机数和一定数学计算:
首先我们创建导弹Shall类

/*
 * 炮弹类
 */
public class Shall extends GameObject{
	double degree;
	public Shall() {
		x=200;y=200;
		width=10;height=10;//导弹的大小
		speed=3;//导弹的速度
		
		degree=Math.random()*Math.PI*2;//random生产0-1的随机数
	}
	public void draw(Graphics g) {//画出圆形黄色导弹
		Color c=g.getColor();
		g.setColor(Color.YELLOW);
		g.fillOval((int)x, (int)y, width, height);//填充颜色

		//炮弹方向   数学公式  可以自己推算
		x+=speed*Math.cos(degree);
		y+=speed*Math.sin(degree);
		
		//碰到边界反弹
		if(x<0||x>Constant.GAME_WIDTH-width)
		{
			degree=Math.PI-degree;
		}
		if(y<30||y>Constant.GAME_HEIGHT-height)
		{
			degree=-degree;
		}
		g.setColor(c);
	}
	
	public static void main(String[] args) {
	}
}

设定好常数后,因为我们最后的成品是一个动画游戏,为了使画面更加流畅。可以采用多线程操作帮我们反复画窗口;

//帮助我们反复重画窗口
	class PaintThread extends Thread
	{//多线程操作
		@Override
		public void run() {
			while(true) {
				repaint();   //重画窗口
				try {Thread.sleep(40);//1s=1000ms  动画
				} 
				catch (InterruptedException e) {e.printStackTrace();}
			}
		}	
	}

第三步:爆炸

当我们的导弹与飞机产生碰撞后发生爆炸,需要连续的图片进行动画般的放映

/*
 * 爆炸类
 */
public class Explode {
	double x,y;//坐标
	static Image[] imges=new Image[16];
	static {
		for(int i=0;i<16;i++)
		{
		//图片数组
			imges[i]=GameUtil.getImage("images/explode/e"+(i+1)+".gif");
			imges[i].getWidth(null);
		}
	}
	int count;
	
	public void draw(Graphics g) {
		if(count<=15) {
			g.drawImage(imges[count],(int)x,(int)y,null);
			count++;
		}
	}
	public Explode(double x,double y) {
		this.x=x;
		this.y=y;
	}
	
}

第四步:创建窗口

首先我们需要准备事先找好的飞机图片并创建画笔
/*
	 * 图案飞机  炮弹
	 */
	Image planeImage =GameUtil.getImage("images/plane.png");
	Image bg =GameUtil.getImage("images/bg.jpg");     //背景
	
	Plane plane=new Plane(planeImage,200,350);//图片  位置
	Shall shall=new Shall();
	Shall[] shalls=new Shall[10];
	Explode explode;
画笔
public void paint(Graphics g) 
	{
		**//自动调用   g相当于画笔**
		Color c=g.getColor();//当我们改变笔的颜色时,最后先储存一下
		g.drawImage(bg, 0,0,this);
		plane.drawSelf(g);
		for(int i=0;i<shalls.length;i++)
		{
			shalls[i].draw(g);
			//飞机和炮弹的检测是否碰撞
			boolean peng=shalls[i].getRect().intersects(plane.getRect());
			if(peng||!plane.leave) {

				plane.leave=false;
				if(explode==null)//防止一直爆炸
				{
					explode=new Explode(plane.x,plane.y);
				}
				explode.draw(g);
			}
		}
		g.setColor(c);//返回颜色
	}
准备前面几步后工作后我们就可以开始创造JFrame窗口:
public void lanuchFrame() {
		this.setTitle("飞机大战");//窗口标题
		this.setVisible(true);//允许画图并显示
		this.setSize(Constant.GAME_WIDTH, Constant.GAME_HEIGHT);
		this.setLocation(600,300);//窗口位置
		//设置关闭方式
		this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

		new PaintThread().start();//启动重画窗口的线程
		addKeyListener(new KeyMonitor());//增加键盘的监听并接受
	}
为了让我们的游戏动画更加好看 我们采用多线操作的Thread类
//帮助我们反复重画窗口
	class PaintThread extends Thread
	{//多线程操作
		@Override
		public void run() {
			while(true) {
				repaint();   //重画窗口
				try {
					Thread.sleep(40);//1s=1000ms  动画
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}
若游戏出现双闪的情况,可以直接照抄下面代码
/*
	 * 解决双闪
	 */
	private Image offScreenImage=null;
	public void update(Graphics g) {
		if(offScreenImage==null)
			offScreenImage=this.createImage(Constant.GAME_HEIGHT,Constant.GAME_WIDTH);
		Graphics gOff=offScreenImage.getGraphics();
		paint(gOff);
		g.drawImage(offScreenImage, 0, 0, null);
	}
	public static void main(String[] args) {
		MyGame f=new MyGame();
		f.lanuchFrame();
		}

最后我们也可以添加计时系统Data类

Date startime=new Date();
	Date endtime; 
	int period;//游戏持续的时间
if(explode==null)
				{
					explode=new Explode(plane.x,plane.y);
				
					endtime=new Date();
					period=(int)((endtime.getTime()-startime.getTime())/1000);
				}
				explode.draw(g);
			}