边做科研任务边带着学习JAVA,学了将近20天的飞机大战项目终于做好了!
目录
思路
World
Sky
FlyingObject
Hero
AirPlane
BigAirPlane
Bee
Bullet
Enemy
Award
思路
我总结一下这个项目的思路:
首先,根据游戏成品来观察我们需要设计哪些类。这里,我观察到有AirPlane、BigAirPlane、Hero、Bee、Bullet这几个比较明显的物体,然后又考虑到背景在动,就加了Sky背景类,最后考虑到整个游戏需要main方法类,就设计了World游戏世界。
接着,设计完类,应该给类赋予基本的属性,比如长 宽 高 坐标等。设计时发现,代码重复,为了代码复用,设计了一个FlyingObject超类,并让飞行物和子弹继承该超类,在超类赋予属性,然后子类自动继承。设计好属性,继续设计基本方法,比如移动step()。设计完,继续在World类中设计游戏窗口的大小和位置。
设计完基本的属性方法,下面该画图了,画图用到了import javax.swing.JFrame;
import javax.swing.JPanel; import java.awt.Graphics; 画图先画游戏窗口,然后再画每个飞行物,在超类里面写了读取图片方法,但是每个飞行物读取图片的方式又不一样,所以需要重写画图的方法,除此,每个飞行物的移动方式也不同,也需要重写。 画完图,学到了修饰符与常量,又去改代码,将子类的属性设计成private修饰,然后又将Sky窗口设计成静态常量,因为是固定的。接着,设计了在游戏世界设计ACTION()方法来启动整个游戏。该方法里面调用各种子类的方法,比如画图、鼠标操作、得分、加命等。这里还用到了定时器,和鼠标的操作,import java.util.Timer; import java.util.TimerTask; 和 import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; 具体代码见下面,有点复杂。对于敌人和子弹会出现抛出窗口,所以要删除,避免占内存。所以设计了删除越界敌人和子弹的方法。接着,要画状态,因为游戏有启动、暂停、结束三种状态,所有的飞行物动态方法都是在启动运行时有效的。最后,游戏是通过打敌机得奖励和生命。所以,我设计了两个接口,以便于子类去继承。
具体代码如下:
World
package com.atjialiangExer;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Graphics;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Random;
import java.util.Arrays;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import java.awt.Graphics; /**整个游戏世界*/
public class World extends JPanel{
public static final int WIDTH = 400;
public static final int HEIGHT = 700;
public static final int START = 0;
public static final int RUNNING = 1;
public static final int PAUSE = 2;
public static final int GAME_OVER = 3;
private int state = START;//当前状态默认启动状态
private static BufferedImage start;
private static BufferedImage pause;
private static BufferedImage gameover;
static {
start = FlyingObject.loadImage("start.png");
pause = FlyingObject.loadImage("pause.png");
gameover = FlyingObject.loadImage("gameover.png");
}
private Sky sky = new Sky();
private Hero hero = new Hero();
//引用类型最好先赋值,不写就是null,避免空指针
private FlyingObject[] enemies = {};
private Bullet[] bullets = {};
/** 生成敌人 */
public FlyingObject nextOne() {
Random rand = new Random();
int type = rand.nextInt(20);
if(type < 5) {
return new AirPlane();
}else if(type < 12) {
return new BigAirPlane();
}else {
return new Bee();
}
}
/** 敌人入场 */
int enterIndex = 0; //敌人入场计数
public void enterAction() { //如何控制频率
enterIndex++;
if(enterIndex % 40 == 0) { //每40*10毫秒走一次
FlyingObject obj = nextOne();
enemies = Arrays.copyOf(enemies,enemies.length + 1); //数组的扩容
enemies[enemies.length - 1] = obj;
}
}
/** 子弹入场 */
int shootIndex = 0;
public void shootAction() {
shootIndex++;
if(shootIndex % 30 == 0) {
Bullet[] bs = hero.shoot();//获取子弹 对象
bullets = Arrays.copyOf(bullets,bullets.length + bs.length);
System.arraycopy(bs, 0,bullets,bullets.length - bs.length ,bs.length);
}
}
/** 飞行物移动*/
public void stepAction() {
sky.step();
for(int i = 0;i < enemies.length ;i++) {
enemies[i].step();
}
for(int i = 0;i < bullets.length ;i++) {
bullets[i].step();
}
}
/** 删除越界敌人和子弹 */
public void outOfBoundAction() {
int index = 0;
FlyingObject[] enemyLives = new FlyingObject[enemies.length];
for (int i = 0;i < enemies.length ;i++) {
FlyingObject f = enemies[i];
if(!f.outOfBounds()) {
enemyLives[index] = f;
index++;
}
}
enemies = Arrays.copyOf(enemyLives,index);//将不越界敌人复制过去
index = 0;
Bullet[] bulletLives = new Bullet[bullets.length];
for(int i = 0;i < bullets.length ;i++) {
Bullet b = bullets[i];
if(!b.outOfBounds()) {
bulletLives[index] = b;
index++;
}
}
bullets = Arrays.copyOf(bulletLives, index);
}
/** 子弹与敌人碰撞 */
int score = 0;//玩家得分
public void bulletBangAction() {
for(int i = 0;i < bullets.length ;i++) {
Bullet b = bullets[i];
for(int j = 0;j < enemies.length;j++) {
FlyingObject f = enemies[j];
if(f.isLife() && b.isLife() && f.hit(b)) {
b.goDead();
f.goDead();
if(f instanceof Enemy) {
Enemy e = (Enemy)f; //强转得分借口
score += e.getScore();
}
if (f instanceof Award) {
Award a = (Award)f;
int type = a.getAwardType();
switch(type) {
case 0 :
hero.addDoubleFire();
break;
case 1 :
hero.addlife();
break;
}
}
}
}
}
}
/** 英雄机与敌人碰撞 */
public void heroBangAction() {
for(int i = 0;i < enemies.length ;i++) {
FlyingObject f = enemies[i];
if(hero.isLife() && f.isLife() && f.hit(hero)) {
f.goDead();
hero.subtractlife();
hero.clearDoubleFire();
}
}
}
/** 检测游戏结束 */
public void checkGameOverAction() {
if(hero.LIFE <= 0) {
}
}
/** 启动程序 */
public void action() {//测试代码
MouseAdapter l = new MouseAdapter() {
/** 重写mouseMoved */
public void mouseMoved(MouseEvent e) {
if(state == RUNNING) {
int x = e.getX();
int y = e.getY();
hero.moveTo(x, y);
}
}
/** 鼠标点击 */
public void mouseClicked(MouseEvent e) {
switch (state) {
case START:
state = RUNNING;
break;
case GAME_OVER:
score = 0; //清理现场
sky = new Sky();
hero = new Hero();
enemies = new FlyingObject[0];
bullets = new Bullet[0];
state = START;
break;
}
}
/** 鼠标移出 */
public void mouseExited(MouseEvent e) {
if(state == RUNNING) {
state = PAUSE;
}
}
/** 鼠标移入 */
public void mouseEntered(MouseEvent e) {
if(state == PAUSE) {
state = RUNNING;
}
}
};
this.addMouseListener(l); //处理鼠标操作事件
this.addMouseMotionListener(l); //处理鼠标移动事件
Timer timer = new Timer();
int intervel = 10;//每10毫秒
timer.schedule(new TimerTask() {
public void run() {
if(state == RUNNING) {
enterAction();//敌人入场
shootAction();//子弹入场
stepAction(); //飞行物移动
outOfBoundAction(); //删除越界敌人
bulletBangAction(); //子弹敌人碰撞
checkGameOverAction();//检测是否游戏结束
}
repaint(); //重画敌人
}
},intervel,intervel);//第一个10,从程序启动开始到第一次触发的时间间隔
//第二个10,第一次触发到第二次触发时间间隔...
} /** 重写paint()画 */
public void paint(Graphics g) {
sky.paintObject(g);
hero.paintObject(g);
for(int i = 0;i < enemies.length ;i++) {
enemies[i].paintObject(g);
}
for(int i = 0;i < bullets.length ;i++) {
bullets[i].paintObject(g);
}
g.drawString("SCORE:" + score, 10, 25);
g.drawString("LIFE:" + hero.getLife(), 10, 45);
switch(state) {
case START:
g.drawImage(start,0,0,null);
break;
case PAUSE:
g.drawImage(pause,0,0,null);
break;
case GAME_OVER:
g.drawImage(gameover,0,0,null);
break;
}
} public static void main(String[] args) {
//画窗口
JFrame frame = new JFrame();
World world = new World();
frame.add(world);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(WIDTH,HEIGHT);
frame.setLocationRelativeTo(null);
frame.setVisible(true); //1设置窗口可见2尽快调用paint()方法
world.action();
}
}
Sky
package com.atjialiangExer;
import java.awt.Graphics;
import java.awt.image.BufferedImage;//装图片的类
public class Sky extends FlyingObject {
private static BufferedImage image;
static { //静态块给图片赋值
image = loadImage("background.png");
}
private int speed;
private int y1;//第二张图片的y坐标
/**构造方法 */
public Sky(){
super(World.WIDTH,World.HEIGHT,0,0);
speed = 1;
y1 = -World.HEIGHT;
}
/** 重写 step()移动 */
public void step() {
y += speed;
y1 += speed;
if(y >= World.HEIGHT) {
y = -World.HEIGHT;
}
if(y1 >= World.HEIGHT) {
y1 = -World.HEIGHT;
}
}
/**重写 getImage */
public BufferedImage getImage() {
return image; //直接返回image
}
/** 重写:画对象 g:画笔 */
public void paintObject(Graphics g) { //谁调用就画谁
g.drawImage(getImage(),x,y,null);
g.drawImage(getImage(),x,y1,null);
}
}FlyingObject
package com.atjialiangExer;
import java.util.Random;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import java.awt.Graphics;
/** 飞行器 (超类) */
public abstract class FlyingObject {
public static final int LIFE = 0;
public static final int DEAD = 1;
public static final int REMOVE = 2;
protected int state = LIFE;
protected int width;
protected int height;
protected int x;
protected int y;
/** 专门给小敌机、大敌机、小蜜蜂 */
public FlyingObject(int width,int height) {
this.width = width;
this.height = height;
Random rand = new Random();
x = rand.nextInt(World.WIDTH - this.width);
y = this.height;
}
/** 专门给英雄机、天空、子弹 */
public FlyingObject(int width,int height,int x,int y) {
this.width = width;
this.height = height;
this.x = x;
this.y = y;
}
/** 读取图片 */
public static BufferedImage loadImage(String fileName) {
try {//异常处理
BufferedImage img = ImageIO.read(FlyingObject.class.getResource(fileName));//同包图片读写
return img;
}catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
}
/** 飞行物移动 */
public abstract void step();
/** 获取对象的图片*/
public abstract BufferedImage getImage(); /** 判断是否活着 */
public boolean isLife() {
return state == LIFE;
}
public boolean isDead() {
return state == DEAD;
}
public boolean isRemove() {
return state == REMOVE;
}
/** 检查是否越界 */
public boolean outOfBounds() {
return this.y >= World.HEIGHT;
}
/** 检测是否碰撞 this:敌人 other:子弹、英雄机*/
public boolean hit(FlyingObject other) {
int x1 = this.x - other.width;
int x2 = this.x + this.width;
int y1 = this.y - other.height ;
int y2 = this.y + this.height ;
int x = other.x;
int y = other.y;
return x >= x1 && x <= x2 && y >= y1 && y <= y2;
}
public void goDead() {
state = DEAD;
}
/** 画对象 g:画笔 */
public void paintObject(Graphics g) { //谁调用就画谁
g.drawImage(getImage(),x,y,null);
}
}
Hero
package com.atjialiangExer;
import java.awt.image.BufferedImage;
public class Hero extends FlyingObject{
private static BufferedImage[] images;
static { //静态块给图片赋值
images = new BufferedImage[2];
images[0] = loadImage("hero0.png");
images[1] = loadImage("hero1.png");
}
private int life;
private int doubleFire;
public Hero(){
super(97,139,140,400);
life = 3;
doubleFire = 0;
}
/** 英雄机随着鼠标移动 x/y */
public void moveTo(int x, int y) {
this.x = x - this.width / 2;
this.y = y - this.height / 2;
}
/** 重写 step()移动 */
public void step() {
}
int index = 0;
/** 重写getImage */
public BufferedImage getImage() { //每10毫秒切换一次
if(isLife()) {
return images[index++ % images.length ]; //0和1的切换
}
return null;
}
/** 子弹生成 */
public Bullet[] shoot() {
int xStep = this.width / 4;
int yStep = 20; //间隔固定
if(doubleFire > 0) {
Bullet[] bs = new Bullet[2];
bs[0] = new Bullet(this.x + 1 * xStep,this.y - yStep);
bs[1] = new Bullet(this.x + 3 * xStep,this.y - yStep);
doubleFire -= 2;
return bs;
}else {
Bullet[] bs = new Bullet[1];
bs[0] = new Bullet(this.x + 2 * xStep,this.y - yStep);
return bs;
}
}
/** 增加生命 */
public void addlife() {
life++;
}
/** 获取生命值*/
public int getLife() {
return life;
}
/** 增加火力 */
public void addDoubleFire() {
doubleFire += 40;
}
/** 减少生命 */
public void subtractlife() {
life--;
}
/** 清除火力 */
public void clearDoubleFire() {
doubleFire = 0;
}
}AirPlane
import java.awt.image.BufferedImage;
import java.util.Random;public class AirPlane extends FlyingObject implements Enemy{
private static BufferedImage[] images;
static { //静态块给图片赋值
images = new BufferedImage[5];
for(int i = 0;i < images.length;i++) {
images[i] = loadImage("airplane" + i + ".png");
}
}
private int speed;
public AirPlane(){
super(49,36);
speed = 2;
}
/** 重写 step()移动 */
public void step() {
y += speed;
}
int index = 1;
/** 重写getImage */
public BufferedImage getImage() { //如果死了,每10毫秒返回一张图片
if(isLife()) {
return images[0];
}else if(isDead()) {
BufferedImage img = images[index++];
if(index == images.length ) {
state = REMOVE;
}
return img;
}
return null; //删除状态,返回null
}
/** 重写getScore */
public int getScore() {
return 1; //打掉小敌机
}
}
BigAirPlane
package com.atjialiangExer;
import java.awt.image.BufferedImage;
import java.util.Random;public class BigAirPlane extends FlyingObject implements Enemy{
private static BufferedImage[] images;
static { //静态块给图片赋值
images = new BufferedImage[2];
for(int i = 0;i < images.length;i++) {
images[i] = loadImage("bigplane" + i + ".png");
}
}
private int speed;
public BigAirPlane(){
super(66,89);
speed = 2;
}
/** 重写 step()移动 */
public void step() {
y += speed;
}
int index = 1;
/** 重写getImage */
public BufferedImage getImage() { //如果死了,每10毫秒返回一张图片
if(isLife()) {
return images[0];
}else if(isDead()) {
BufferedImage img = images[index++];
if(index == images.length ) {
state = REMOVE;
}
return img;
}
return null; //删除状态,返回null
}
/** 重写getScore */
public int getScore() {
return 3; //打掉大敌机
}
}Bee
package com.atjialiangExer;
import java.awt.image.BufferedImage;
import java.util.Random;
import java.util.Random;
public class Bee extends FlyingObject implements Award{
private static BufferedImage[] images;
static { //静态块给图片赋值
images = new BufferedImage[5];
for(int i = 0;i < images.length;i++) {
images[i] = loadImage("bee" + i + ".png");
}
}
private int xSpeed;
private int ySpeed;
private int awardType;//奖励类型:随机生成
public Bee(){
super(60,51);
xSpeed = 1;
ySpeed = 2;
Random rand = new Random();
awardType = rand.nextInt(2);//0或者1
}
/** 重写 step()移动 */
public void step() {
x += xSpeed;
y += ySpeed;
if(x <= 0 || x >= World.WIDTH - this.width) { //换方向
xSpeed *= -1;
}
}
int index = 1;
/** 重写getImage */
public BufferedImage getImage() { //如果死了,每10毫秒返回一张图片
if(isLife()) {
return images[0];
}else if(isDead()) {
BufferedImage img = images[index++];
if(index == images.length ) {
state = REMOVE;
}
return img;
}
return null; //删除状态,返回null
}
/** 重写getAwardType */
public int getAwardType() {
return awardType;
}
}
Bullet
package com.atjialiangExer;
import java.awt.image.BufferedImage;
public class Bullet extends FlyingObject{
private static BufferedImage image;
static { //静态块给图片赋值
image = loadImage("bullet.png");
}
private int speed;
public Bullet(int x,int y){
super(8,14,x,y);
speed = 3;
}
/** 重写 step()移动 */
public void step() {
y -= speed;
}
/** 重写getImage */
public BufferedImage getImage() {
if(isLife()) {
return image;
}else if(isDead()) {
state = REMOVE;
}
return null;
}
/** 重写子弹是否越界 */
public boolean outOfBounds() {
return this.y <= -this.height;
}
}
Enemy
package com.atjialiangExer;
/** 得分接口 */
public interface Enemy {
/** 得分 */
public int getScore();
}
Award
package com.atjialiangExer;
/** 奖励接口 */
public interface Award {
public int DOUBLE_FIRE = 0;
public int LIFE = 1;
/** 获取奖励类型 返回0或1 */
public int getAwardType();
}