目录
射击游戏需求:
所参与的角色: 英雄机、子弹、小敌机、大敌机、小蜜蜂、天空
角色间的关系:
设计规则:
设计类与游戏对象:
需求设计:
代码实现:
1) 英雄机发射子弹,分单倍火力和双倍火力
2) 子弹射击敌人(小敌机、大敌机、小蜜蜂),若射击到了:
2.1) 子弹直接消失、敌人先爆破再消失
2.2) 子弹打掉小敌机----------玩家得1分
子弹打掉大敌机----------玩家得3分
子弹打掉小蜜蜂----------英雄机得奖励(1条命、40火力值)
3) 敌人(小敌机、大敌机、小蜜蜂)可以和英雄机撞,若撞上了:
3.1) 敌人先爆破再消失
3.2) 英雄机减1条命,同时,清空火力值---英雄机命数为0时,游戏结束
4) 英雄机、子弹、小敌机、大敌机、小蜜蜂都在天空上飞
1. 将所有派生类共有的属性和行为,抽到超类中-------------抽共性
2.派生类的行为都一样,设计为普通方法派生类的行为都不一样,设计为抽象方法
3.将部分派生类共有的行为,抽到接口中接口是对继承单根型的扩展-------------实现多继承
对象: 英雄机、子弹、小敌机、大敌机、小蜜蜂、天空
对象类: Hero、Bullet、Airplane、BigAirplane、Bee、Sky
超类:FlyingObject超类,6个对象类继承超类
图片工具类:Images(封装与图片相关的操作/方法)
接口:Enemy(得分接口),Award( 奖励接口)
窗口:World类
1.构造法:在FlyingObject中设计2个构造方法,6个对象类分别调用
2.移动方法:在6个对象类中重写step()移动
3.访问控制:给类中成员添加访问控制修饰符
4.画窗口:在World类中,测试,调用即可.
5.画对象:
1) 想画对象得需要获取对象的图片,每个对象都能获取图片,意味获取图片为对象共有的行为,
所以将获取图片行为设计在超类中,每个对象获取图片的行为都是不一样的,所以设计为抽象方法,
在FlyingObject中设计抽象方法getImage()用于获取对象的图片
2) 获取图片时需要去考虑对象的状态,因为在不同状态下获取的图片是不一样的,
每个对象都有状态,意味着状态为对象所共有的属性,所以设计在超类中,
状态一般都是固定的,所以都设计为常量,同时设计一个 state变量来表示当前的状态
,在FlyingObject 中设计LIFE、DEAD、REMOVE三个常量,设计state变量表示当前状态
状态有了,还需要去判断状态,每个对象都得判断,意味着判断状态为共有行为,
所以设计在超类中,每个对象判断状态是方式都是一样的,所以设计为普通方法
,在FlyingObject中设计isLife()、isDead()、isRemove()来判断对象的状态
3) 派生类重写getImage()获取图片:
3.1) 天空Sky,直接返回sky图片即可
3.2) 子弹Bullet:
3.2.1) 若活着的,直接返回bullet图片即可
3.2.2) 若死了的,直接删除(不返回图片)
3.3) 英雄机Hero:
3.3.1) 若活着的,直接返回heros[0]和heros[1]的来回切换
3.4) 小敌机Airplane:
3.4.1) 若活着的,直接返回airs[0]图片即可
3.4.2) 若死的了,返回airs[1]到airs[4]的爆破图,4后删除(不返回图片)
3.5) 大敌机BigAirplane:
3.4.1) 若活着的,直接返回bairs[0]图片即可
3.4.2) 若死的了,返回bairs[1]到bairs[4]的爆破图,4后删除(不返回图片)
3.4) 小蜜蜂Bee:
3.4.1) 若活着的,直接返回bees[0]图片即可
3.4.2) 若死的了,返回bees[1]到bees[4]的爆破图,4后删除(不返回图片)
4) 图片有了就可以开画了,因为是需要往窗口上画,所以在窗口World类中重写paint()方法实现画对象
6.敌人入场:
1) 敌人对象是由窗口产生的,所以在World中设计nextOne()生成敌人对象
2) 敌人入场为定时发生的,所以在 run()中调用enterAction()实现敌人入场
在enterAction中:
每400毫秒,获取敌人对象obj, enemies扩容,将obj添加到enemies的最后一个元素上
不要忘记: 在run()中调用enterAction()的下面调用repaint()方法重画
7.子弹入场:
1) 子弹是由英雄机发射出来的,所以在Hero中设计shoot()生成子弹对象
2) 子弹入场为定时发生的,所以在run()中调用shootAction()实现子弹入场
在shootAction中:
每300毫秒,获取子弹数组对象bs,bullets扩容,将bs追加到bullets的末尾
8.飞行物移动:
1) 飞行物移动为派生类所共有的行为,所以在超类FlyingObject中设计抽象方法step()移动,派生类中重写
2) 飞行物移动为定时发生的,所以在run()中调用 stepAction()实现飞行物移动
在stepAction中:
天空动,遍历敌人敌人动,遍历子弹子弹动
9.英雄机随着鼠标移动:
1) 英雄机随着鼠标动为英雄机的行为,所以在Hero中设计moveTo()实现英雄机随着鼠标动
2) 英雄机随着鼠标移动为事件触发的,所以在侦听器中重写mouseMoved()鼠标移动事件
在mouseMoved()中:
获取鼠标的x和y坐标,调用Hero的moveTo()方法
10.删除越界的敌人和子弹:
1) 在FlyingObject中设计outOfBounds()检测敌人是否越界
在Bullet中重写outOfBounds()检测子弹是否越界
2) 删除越界的敌人和子弹为定时发生的,所以在run中调用outOfBoundsAction()删除越界的敌人和子弹
在outOfBoundsAction()中:
声明不越界敌人/子弹数组,遍历enemies/bullets数组,判断若不越界,则将对象装到不越界数组中
最后将不越界数组复制到enemies/bullets数组中
11.接口设计:
1)设计Enemy得分接口,Airplane和BigAirplane实现Enemy接口,击中小敌机/大敌机,得1分/3分.
2)设计Award奖励接口,Bee实现Award接口,击中小蜜蜂随机奖励:生命值加一/火力值加40双倍火力
12.敌人与子弹的碰撞:
1) 在 FlyingObject中设计hit()检测敌人与子弹的碰撞、goDead()飞行物去死
在Hero中设计addLife()增命、addFire()增火力
2) 敌人与子弹的碰撞为定时发生的,所以在run()中调用bulletBangAction()实现敌人与子弹的碰撞
在bulletBangAction()中:
遍历子弹得子弹,遍历敌人得敌人,判断若都活着并且撞上了:
子弹去死、敌人去死
判断若为Enemy分,则玩家得分
若为Award奖励,则英雄机得奖励
13.画分和画命:
1) 在Hero中设计getLife()获取英雄机的命数
2) 在World类的paint()中: 画分和画命
14.敌人与英雄机的碰撞:
1) 借用FlyingObject中的hit()碰撞检测、goDead()去死
在Hero中设计subtractLife()减命、clearFire()清空火力值
2) 敌人与英雄机的碰撞为定时发生的,所以在run()中调用heroBangAction()实现敌人与英雄机的碰撞
在heroBangAction中:
遍历敌人得敌人,判断若都活着并且撞上了:
敌人去死、英雄机减命、英雄机清空火力值
15.检测游戏结束:
1) 借助于Hero的getLife()获取命数
2) 检测游戏结束为定时发生的,所以在run()中调用checkGameOverAction()检测游戏结束
在checkGameOverAction()中:
判断若英雄机的命数<=0,表示游戏结束,则……
16.画状态:
1) 在World中设计四个状态常量,同时设计state变量表示当前状态,默认为START(启动状态)
在Images中设计start、pause、gameover三个状态图片,在static块中赋值
在World类的paint()中设计在不同的状态下画不同的图片
2) 设计run()中那一堆action,仅在运行状态时执行
设计英雄机随着鼠标动,仅在运行状态时执行
3) 重写mouseClicked()鼠标点击: 启动状态时变运行状态,游戏结束状态时先清理现场,再变启动状态
重写mouseExited()鼠标移出: 运行状态变为暂停状态
重写mouseEntered()鼠标移入: 暂停状态变为运行状态
(注:每一次功能的实现都要不断测试调试,防止报错.)
FlyingObject:
package cn.tedu.shoot;
import java.awt.image.BufferedImage;
import java.util.Random;
/**飞行物*/
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; //x 坐标
protected int y; //y 坐标
/**专门给小敌机 ,大敌机,小蜜蜂提供的 */
public FlyingObject(int width,int height){//传2个参是因为敌人只有width 和height 不一样
this.width=width;
this.height=height;
Random rand =new Random();//随机数对象
x =rand.nextInt(World.WIDTH-width);//0到(窗口宽-敌机宽)的随机数
y=-height;//负的小敌机的高
}
/**专门给 英雄机, 天空 ,子弹提供*/
public FlyingObject(int width,int height,int x,int y){//传四个参是因为他们的width,height,x,y都不一样
this.width=width;
this.height=height;
this.x=x;
this.y=y;
}
/**飞行物移动*/
public abstract void step();
/**获取对象的图片方法 */
public abstract BufferedImage getImage();
//公开 抽象 返回 值图片
/**判断对象是否活着*/
public boolean isLife(){
return state ==LIFE;//当前状态为LIFE,表示对象是活着的返回true 否则false
}
/**判断对象是否死了着*/
public boolean isDead(){
return state ==DEAD;//当前状态为DEAD,表示对象是死着的返回true 否则false
}
/**判断对象是否删除的*/
public boolean isRemove(){
return state ==REMOVE;//当前状态为REMOVE,表示对象是删除的返回true 否则false
}
/**检测敌人是否越界*/
public boolean outOfBounds(){
return this.y>= World.HEIGHT;//敌人的y大于等于窗口的 高 即为越界了
}
/**判断检测敌人 与 子弹/ 英雄机 this:敌人 other :子弹/英雄机*/
public boolean hit(FlyingObject other){
int x1 = this.x - other.width;//x1:敌人的x- 子弹/英雄机的宽
int x2 = this.x + this.width; //x2: 敌人的x+敌人的width
int y1 = this.y - other.height;//y1: 敌人的y - 子弹/英雄机的高
int y2 = this.y + this.height;// y2: 敌人的 y + 敌人的高
int x = other.x;
int y = other.y;
// x在x1 和 x2之间 并且 y在y1和y2之间 即为装上了
return x>=x1 && x<=x2 && y>=y1 &&y<=y2;
}
/**飞行物去死*/
public void goDead(){
state = DEAD;//将当前状态修改为 DEAD死
}
}
Airplane:
package cn.tedu.shoot;
import java.awt.image.BufferedImage;
/** 小敌机 是飞行物,也能得分*/
public class Airplane extends FlyingObject implements Enemy{
private int speed;//移动速度
/**构造方法*/ //Airplane a=new Airplane
public Airplane(){
super(48,50);
speed=2;
}
/**重写 step() 移动*/
public void step(){
y+=speed;
}
private int index=1;//爆破图的下标
/**重写 getImage() 获取图片方法*/
public BufferedImage getImage(){//每10毫秒走一次
if(isLife()){//活着的
return Images.airs[0];//返回airs第一张图片
}else if(isDead()){//死了的
BufferedImage img = Images.airs[index++];
if(index==Images.airs.length){
state = REMOVE;
}
return img;
/*
* index= 1;
* 10M airs[1] index=2 返回airs[1]
* 20M airs[2] index=3 返回airs[2]
* 30M airs[3] index=4;返回airs[3]
* 40M airs[4] index=5(REMOVE) 返回airs[4]
* 50M 因为REMOVE状态 ,所以不走isDead()里面了,直接返回null
*/
}
return null;//删除状态时,不返回图片
}
/**重写getScore()得分方法*/
public int getScore(){
return 1;//打掉小敌机,玩家得1分;
}
}
BigAirplane
package cn.tedu.shoot;
import java.awt.image.BufferedImage;
/** 大敌机 是飞行物,也能得分*/
public class BigAirplane extends FlyingObject implements Enemy{
private int speed;//移动速度
/**构造方法*/
public BigAirplane(){
super(66,89);
speed=2;
}
/**重写 step() 移动*/
public void step(){
y+=speed;//y+为(向下移动
}
private int index=1;//爆破图的下标
/**重写 getImage() 获取图片方法*/
public BufferedImage getImage(){//每10毫秒走一次
if(isLife()){//活着的
return Images.bairs[0];//返回bairs第一张图片
}else if(isDead()){//死了的
BufferedImage img = Images.bairs[index++];
if(index==Images.bairs.length){
state = REMOVE;
}
return img;
}
return null;//删除状态时,不返回图片
}
/**重写getScore()得分方法*/
public int getScore(){
return 3;//打掉小敌机,玩家得3分;
}
}
Bee:
package cn.tedu.shoot;
import java.awt.image.BufferedImage;
import java.util.Random;
/** 小蜜蜂 是飞行物, 也是奖励*/
public class Bee extends FlyingObject implements Award{
private int xSpeed;//x坐标移动速度
private int ySpeed;//y坐标移动速度
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;//x+(向左向右)
y+=ySpeed;//y+一定向下
//若x<=0 或者 x>=窗口宽-小蜜蜂宽,说明
if(x<=0||x>=World.WIDTH-this.width){
xSpeed*=-1;//切换方向(正变负 ,负变正)
}
}
private int index=1;//爆破图的下标
/**重写 getImage() 获取图片方法*/
public BufferedImage getImage(){//每10毫秒走一次
if(isLife()){//活着的
return Images.bees[0];//返回airs第一张图片
}else if(isDead()){//死了的
BufferedImage img = Images.bees[index++];
if(index==Images.bees.length){
state = REMOVE;
}
return img;
}
return null;//删除状态时,不返回图片
}
/**重写getAwardType()获取奖励类型*/
public int getAwardType(){
return awardType;//返回奖励类型
}
}
Hero:
package cn.tedu.shoot;
import java.awt.image.BufferedImage;
/**英雄机:是飞行物*/
public class Hero extends FlyingObject {
private int life;//生命
private int fire;//火力值
/**构造方法*/
public Hero(){
super(97,139,140,400);
life=3;
fire=0;
}
/**重写 step() 移动*/
public void step(){
}
private int index=0;
/**重写getImage方法*/
public BufferedImage getImage(){
if(isLife()){//若活着
return Images.heros[index++%Images.heros.length];//heros[0]和heros[1]之间来来回回切换
} //写活 数组长度
return null;////保证程序一定有返回值
}
/**英雄机发射子弹(生成子弹对象)*/
public Bullet[] shoot(){
int xStep = this.width/4;//1/4英雄级的宽
int yStep = 20; //固定20
if(fire>0){//双
Bullet[] bs= new Bullet[2];//2发子弹
bs[0]= new Bullet(this.x+1*xStep,this.y-yStep);
bs[1]= new Bullet(this.x+3*xStep,this.y-yStep);
fire-=2;//发射一次双倍火力 则火力值减2
return bs;
}else{//单倍火力
Bullet[] bs= new Bullet[1];//1发子弹
// x: 英雄级x+2/4英雄机的宽 y:英雄机的y-固定20
bs[0]= new Bullet(this.x+2*xStep,this.y-yStep);
return bs;
}
}
/**英雄机随着鼠标动 x/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 = 鼠标x- 1/2英雄机的高
}
/**英雄机增命*/
public void addLife(){
life++;//命数增1
}
/**获取英雄机命*/
public int getLife(){
return life;//获取命
}
/**英雄机减命*/
public void subtractLife(){
life--;//命数减1
}
/**英雄机增火力值*/
public void addFire(){
fire+=40;//火力值增40
}
/***/
public void clearFire(){
fire=0;//火力值归0
}
}
Sky:
package cn.tedu.shoot;
/**天空 是飞行物*/
import java.awt.Image;
import java.awt.image.BufferedImage;
public class Sky extends FlyingObject{
//成员变量
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;//y+向下
y1+=speed;//y1+向下
if(y>=World.HEIGHT){//y>=窗口高,说明移除窗口
y=-World.HEIGHT;//则设置y为负的窗口的高(挪到最上面去)
}
if(y1>=World.HEIGHT){//y1>=窗口高,说明移除窗口
y1=-World.HEIGHT;//则设置y1为负的窗口的高(挪到最上面去)
}
}
/**重写 getImage() 获取图片方法*/
public BufferedImage getImage(){
return Images.sky;
}
/**获取天空y1坐标*/
public int getY1(){
return y1;//返回y1坐标
}
}
Bullet:
package cn.tedu.shoot;
import java.awt.image.BufferedImage;
/** 子弹 是飞行物*/
public class Bullet extends FlyingObject {
private int speed;//移动速度
/**构造的方法*/
public Bullet(int x,int y){//Bullet b=new Bullet(100,200);
super(8,20,x,y);
speed=3;
}
/**重写 step() 移动*/
public void step(){
y-=speed;//y-为(向上移动
}
/**重写 getImage() 获取图片方法*/
public BufferedImage getImage(){
if(isLife()){
return Images.bullet;//活着
}else if(isDead()){
state=REMOVE;//若死了
}
return null;
}
/**重写outofBounds()--检测子弹是否越界*/
public boolean outOfBounds(){
return this.y<= - this.height;//敌人的y大于等于窗口的 高 即为越界了
}
}
Images:
package cn.tedu.shoot;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
/** 图片工具类*/
public class Images {
//公开的 静态的 数据类型 图片 变量名
public static BufferedImage sky;//天空图片
public static BufferedImage bullet;//子弹图片
public static BufferedImage[] heros;//英雄图片
public static BufferedImage[] airs;//小敌机图片
public static BufferedImage[] bairs;//大敌机图片
public static BufferedImage[] bees;//小蜜蜂图片
public static BufferedImage start;//启动图
public static BufferedImage pause;//暂停图
public static BufferedImage gameover;//游戏结束图
static{//初始化静态图片
sky = readImage("background.png");
bullet = readImage("bullet.png");
heros =new BufferedImage[2];//两张图片
heros[0]=readImage("hero0.png");
heros[1]=readImage("hero1.png");
airs=new BufferedImage[5];
bairs=new BufferedImage[5];
bees=new BufferedImage[5];
airs[0]=readImage("airplane1.png");
bairs[0]=readImage("bigairplane1.png");
bees[0]=readImage("bee1.png");
for(int i=1;i
Award:
package cn.tedu.shoot;
/**奖励接口*/
public interface Award {
public int FIRE = 0;//火力值 默认..实际public static final int FIRE
public int LIFE = 1;//命名 ..实际public static final int LIFE
/**获取奖励类型(0 或1)*/
public int getAwardType();
}
Enemy:
package cn.tedu.shoot;
/**得分接口*/
public interface Enemy {
/**得分方法*/
public int getScore();
}
World:
package cn.tedu.shoot;
import java.awt.event.MouseAdapter;//侦听器
import java.awt.event.MouseEvent;
import javax.swing.JFrame;//画框
import javax.swing.JPanel;//画板
import java.awt.Graphics;//绘制
import java.util.Random;
import java.util.Arrays;
import java.util.Timer;//定时器
import java.util.TimerTask;//定时任务
/** 整个窗口 */
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 Sky sky=new Sky();//天空对象
private Hero hero=new Hero();//英雄机对象
private FlyingObject[] enemies={};//敌人(小敌机,大敌机,小蜜蜂)数组
private Bullet[] bullets={};//子弹数组
/**创建敌人(小敌机 大敌机 小蜜蜂)对象*/
public FlyingObject nextOne(){
Random rand = new Random();//随机对象
int type = rand.nextInt(20);//0-19
if(type<5){//0-4时 返回小敌机
return new Bee();
}else if(type<12){//5-11返回小敌机
return new Airplane();
}else{//12到19生成大敌机
return new BigAirplane();
}
}
//敌人入场, 子弹入场 ,飞行物移动.
private int enterIndex= 0;
/**敌人(小敌机 大敌机 小蜜蜂)对象*/
public void enterAction(){//没10毫秒走一次
enterIndex++;//每10毫秒增1
if(enterIndex%40==0){//每400(40*10)毫秒走一次
FlyingObject obj = nextOne();//获取敌人数组
enemies = Arrays.copyOf(enemies, enemies.length+1);//扩容
enemies[enemies.length-1] = obj;//将敌人对象添加到最后一个元素上
}
}
private int shootIndex=0;
/**子弹入场*/
public void shootAction(){//每10毫秒走一次
shootIndex++;//每10毫秒增1
if(shootIndex%30==0){//每300(30*10)毫秒走一次
Bullet[] bs=hero.shoot();//获取英雄机发射出来的子弹对象
bullets = Arrays.copyOf(bullets, bullets.length+bs.length);//扩容(bs长度为几就扩大几个容量)
System.arraycopy(bs,0,bullets,bullets.length-bs.length,bs.length);//数组的追加
}
}
/**飞行物移动*/
public void stepAction(){//每10毫秒
sky.step();//天空动
for(int i=0;i
: