一、项目对象如下:
ShootGame
|-- 英雄机 Hero
|-- 敌飞机 Airplane
|-- 蜜蜂 Bee
|-- 子弹 Bullet
二、游戏界面显示
一,新建工程和包
首先,新建名为shoot的Java工程;然后,在工程下的src目录下新建包com.cetc.shoot,将所需要图拷贝到该包下
二,创建抽象父类Flyingobject
分析出英雄机、敌飞机、子弹以及蜜蜂都有x, y,width以及height属性,因此,将这些属性抽象到父类Flyingobject中。另外,它们在界面上都以图片的形式显示,因此在父类Flyingobject中,添加image属性,表示它们的贴图,并提供上述5个属性的getter和setter方法, FlyingObject类的代码如下所示:
package com.cetc.shoot;
import java.awt.image.BufferedImage;
public abstract class FlyingObject {
protected int x; //x坐标
protected int y; //y坐标
protected int width; //宽
protected int height; //高
protected BufferedImage image; //图片
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public BufferedImage getImage() {
return image;
}
public void setImage(BufferedImage image) {
this.image = image;
}
/** 飞行物走一步 */
public abstract void step();
/**
* 检查当前飞行物体是否被子弹(x,y)击(shoot)中,
* @param bullet 子弹对象
* @return true表示击中
*/
public boolean shootBy(Bullet bullet) {
int x = bullet.x;
int y = bullet.y;
return this.x<x && x<this.x+width
&&
this.y<y && y<this.y+height;
}
/** 检查飞行物是否出界 */
public abstract boolean outOfBounds();
}
三,创建接口Enemy,实现接口的类为敌人
创建接口Enemy ,表示敌人。如果子弹击中敌飞机,英雄机可以获取分数,因此,在Enemy接口中提供获取分数的方法,代码如下所示:
package com.cetc.shoot;
/**
* 敌人 ,可以有分数
*
*/
public interface Enemy {
/**
* 敌人的分数
*/
public int getScore();
}
四,创建接口Award,实现接口的类表示奖励
public interface Award {
public int DOUBLE_FIRE = 0; //火力
public int LIFE = 1; //命
/** 获取奖励类型 0为火力 1为命 */
public int getType();
}
五,新建类Airplane,表示敌飞机
新建类Airplane ,表示敌飞机。敌飞机属于飞行物,因此,继承自Flyingobject类;敌飞机也属于敌人,因此,需要实现Enemy接口。敌飞机可以向下移动,因此有移动的速度作为属性,代码如下所示:
package com.cetc.shoot;
/** 敌机: 是飞行物,也是敌人 */
public class Airplane extends FlyingObject implements Enemy {
private int speed = 2; //走步的步数
/** 构造方法 */
public Airplane(){
image = ShootGame.airplane; //图片
width = image.getWidth(); //宽
height = image.getHeight(); //高
x = (int) (Math.random()*(ShootGame.WIDTH-this.width))
}
/** 重写getScore() */
public int getScore(){
return 5;
}
/** 重写step() */
public void step(){
y+=speed; //y加(向下)
}
/** 重写outOfBounds() */
public boolean outOfBounds(){
return this.y>=ShootGame.HEIGHT; //敌机的y>=屏幕的高,即为越界
}
}
六,新建类Bee,表示蜜蜂
新建类Bee ,表示蜜蜂。蜜蜂属于飞行物,因此,继承自Flyingobject类;击中蜜蜂可以获得奖励,因此,需要实现Award接口,并且有奖励类型作为属性。蜜蜂可以左右移动、向下移动,因此有移动的速度作为属性,代码如下所示:
package com.cetc.shoot;
import java.util.Random;
/** 小蜜蜂: 是飞行物,也是奖励 */
public class Bee extends FlyingObject implements Award {
private int xSpeed = 1; //x坐标走步步数
private int ySpeed = 2; //y坐标走步步数
private int awardType; //奖励类型
/** 构造方法 */
public Bee(){
image = ShootGame.bee; //图片
width = image.getWidth(); //宽
height = image.getHeight(); //高
y = -height; //y:负的蜜蜂的高
Random rand = new Random(); //创建随机数对象
x = rand.nextInt(ShootGame.WIDTH-this.width); //x:0到(屏幕宽-蜜蜂宽)之内的随机数
awardType = rand.nextInt(2); //奖励类型为0到1之间的随机数
/** 重写getType() */
public int getType(){
return awardType;
}
/** 重写step() */
public void step(){
x+=xSpeed; //x加(向左或向右)
y+=ySpeed; //y加(向下)
if(x>=ShootGame.WIDTH-this.width){ //x>=(屏幕宽-蜜蜂宽)时,x减(向左)
xSpeed = -1;
}
if(x<=0){ //x<=0时,x加(向右)
xSpeed = 1;
}
}
/** 重写outOfBounds() */
public boolean outOfBounds(){
return this.y>=ShootGame.HEIGHT; //蜜蜂的y>=屏幕的高,即为越界
}
}
七,新建类Bullet,表示子弹
新建类Bullet,表示子弹。子弹属于飞行物,因此,继承自Flyingobject类;子弹可以向上移动,因此有移动的速度作为属性,代码如下所示:
package com.cetc.shoot;
/** 子弹: 是飞行物 */
public class Bullet extends FlyingObject {
private int speed = 3; //走步步数
/** 构造方法 x:子弹的x坐标 y:子弹的y坐标*/
public Bullet(int x,int y){
image = ShootGame.bullet; //图片
this.x = x; //x坐标:与英雄机有关
this.y = y; //y坐标:与英雄机有关
}
/** 重写step() */
public void step(){
y-=speed; //y减(向上)
}
/** 重写outOfBounds() */
public boolean outOfBounds(){
return this.y<=-this.height; //子弹的y<=负的子弹的高,即为越界
}
}
八,新建Hero,表示英雄机
新建类Hero,表示英雄机。英雄机属于飞行物,因此,继承自Flyingobject类;英雄机发出子弹,击中蜜蜂可以获取双倍火力或增命,因此,将双倍火力的子弹数量和命的数虽作为该类的属性,代码如下所示:
package com.cetc.shoot;
import java.awt.image.BufferedImage;
/** 英雄机: 是飞行物 */
public class Hero extends FlyingObject {
private int life; //命
private int doubleFire; //火力值
private BufferedImage[] images = {}; //图片切换数组
private int index = 0; //协助图片切换
/** 构造方法 */
public Hero(){
image = ShootGame.hero0; //图片
width = image.getWidth(); //宽
height = image.getHeight(); //高
x = 150; //x坐标:固定的值
y = 400; //y坐标:固定的值
life = 3; //命数为3
doubleFire = 0; //火力值为0(单倍火力)
images = new BufferedImage[]{ShootGame.hero0,ShootGame.hero1}; //两张图片切换
}
/** 重写step() */
public void step() {
if(images.length>0) {
image = images[index++/10%images.length];
}
}
/** 英雄机发射子弹 */
public Bullet[] shoot(){
int xStep = this.width/4; //1/4英雄机的宽
int yStep = 20; //固定的值
if(doubleFire>0){ //双倍
Bullet[] bs = new Bullet[2]; //两发子弹
bs[0] = new Bullet(this.x+1*xStep,this.y-yStep); //x:英雄机的x+1/4英雄机的宽 y:英雄机的y-20
bs[1] = new Bullet(this.x+3*xStep,this.y-yStep); //x:英雄机的x+3/4英雄机的宽 y:英雄机的y-20
doubleFire-=2; //发射一次双倍火力时,火力值减2
return bs;
}else{ //单倍
Bullet[] bs = new Bullet[1]; //一发子弹
bs[0] = new Bullet(this.x+2*xStep,this.y-yStep); //x:英雄机的x+2/4英雄机的宽 y:英雄机的y-20
return bs;
}
}
/** 英雄机随着鼠标移动 x:鼠标的x坐标 y:鼠标的y坐标*/
public void moveTo(int x,int y){
this.x = x - this.width/2; //英雄机的x:鼠标的x-1/2英雄机的宽
this.y = y - this.height/2; //英雄机的y:鼠标的y-1/2英雄机的高
}
/** 英雄机增火力 */
public void addDoubleFire(){
doubleFire+=40; //火力值增40
}
/** 增命 */
public void addLife(){
life++; //命数增1
}
/** 获取命 */
public int getLife(){
return life; //返回命数
}
/** 减命 */
public void subtractLife(){
life--;
}
public void setDoubleFire(int doubleFire) {
this.doubleFire = doubleFire;
}
/** 重写outOfBounds() */
public boolean outOfBounds(){
return false; //永不越界
}
/** 检测英雄机与敌人的碰撞 this:英雄机 other:敌人 */
public boolean hit(FlyingObject other){
int x1 = other.x-this.width/2; //x1:敌人的x-1/2英雄机的宽
int x2 = other.x+other.width+this.width/2; //x2:敌人的x+敌人的宽+1/2英雄机的宽
int y1 = other.y-this.height/2; //y1:敌人的y-1/2英雄机的高
int y2 = other.y+other.height+this.height/2; //y2:敌人的y+敌人的高+1/2英雄机的高
int x = this.x+this.width/2; //x:英雄机的x+1/2英雄机的宽
int y = this.y+this.height/2; //y:英雄机的y+1/2英雄机的高
return x>x1 && x<x2
&&
y>y1 && y<y2; //x在x1和x2之间,并且,y在y1和y2之间,即为撞上了
}
}
九,新建ShootGame,加载图片
新建类ShootGame ,该类继承自JPanel ,在该类中,使用静态常量定义面板的宽和高并使用Imagero的read方法加载图片,代码如下所示:
在ShootGame类中添加以下属性和常量
在ShootGame类中添加checkGameoverAction方法,该方法用于判断游戏是否已经结束,如果已经结束,G设置为GAME-OVER
修改ShootGame类的action方法,添加鼠标点击、移入、退出等操作的状态处理
在ShootGame类中添加paintstate方法,画出START, PAUSE以及GAMEOVER
ShootGame类中paint方法中,调用paintstate方法
package com.cetc.shoot;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.Arrays;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask
import javax.imageio.ImageIO;
import javax.swing.JPanel;
//主程序类
public class ShootGame extends JPanel{
public static final int WIDTH = 400; //窗口宽
public static final int HEIGHT = 654; //窗口高
public static BufferedImage background; //背景图
public static BufferedImage start; //启动图
public static BufferedImage pause; //暂停图
public static BufferedImage gameover; //游戏结束图
public static BufferedImage airplane; //敌机
public static BufferedImage bee; //小蜜蜂
public static BufferedImage bullet; //子弹
public static BufferedImage hero0; //英雄机0
public static BufferedImage hero1; //英雄机1
private Hero hero = new Hero(); //英雄机对象
private FlyingObject[] flyings = {}; //敌人(敌机+小蜜蜂)数组
private Bullet[] bullets = {}; //子弹数组
private Timer timer; //定时器
private int intervel = 1000/100; //时间间隔(毫秒)
private int score = 0; //玩家的得分
private int state;
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; //游戏结束状态
public ShootGame(){
}
static{ //加载图片
try{
background = ImageIO.read(ShootGame.class.getResource("background.png"));
start = ImageIO.read(ShootGame.class.getResource("start.png"));
pause = ImageIO.read(ShootGame.class.getResource("pause.png"));
gameover = ImageIO.read(ShootGame.class.getResource("gameover.png"));
airplane = ImageIO.read(ShootGame.class.getResource("airplane.png"));
bee = ImageIO.read(ShootGame.class.getResource("bee.png"));
bullet = ImageIO.read(ShootGame.class.getResource("bullet.png"));
hero0 = ImageIO.read(ShootGame.class.getResource("hero0.png"));
hero1 = ImageIO.read(ShootGame.class.getResource("hero1.png"));
}catch(Exception e){
e.printStackTrace();
}
}
}