简介:
运用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);
}