## 潜艇游戏第一天:
1. 创建了6个类,创建World类并测试
## 潜艇游戏第二天:
1. 给6个类设计构造方法,并测试
## 潜艇游戏第三天:
1. 设计侦察潜艇数组、鱼雷潜艇数组、水雷潜艇数组、水雷数组、炸弹数组,并测试
2. 设计SeaObject超类,设计6个类继承SeaObject
3. 在超类SeaObject中设计两个构造方法,6个派生类分别调用
4. 将侦察潜艇数组、鱼雷潜艇数组、水雷潜艇数组统一组合成SeaObject数组,并测试
## 潜艇游戏第四天:
1. 在6个类中重写move()移动
2. 给类中成员添加访问权限
3. 设计Images图片类
## 潜艇游戏第五天:
1. 设计窗口的宽和高为常量,适用地方做修改
2. 画窗口:--
- import JFrame+JPanel
- 设计World类继承JPanel----------------这一步大家特别容易忘记
- main中代码CV大法
3. 画对象:
```java
1)想画对象需要获取对象的图片,每个对象都能得图片,
意味着获取图片的行为为共有行为,所以设计在SeaObject类中,
每个对象获取图片的代码都是不一样的,所以设计为抽象方法
----在SeaObject中设计抽象方法getImage()获取图片
2)在派生类中重写getImage()获取对象所对应的图片
----在6个类中重写getImage()
3)因为只有活着的对象才需要画到窗口中,所以需要设计对象的状态(活着还是死了),
每个对象都有状态,意味着状态为共有属性,所以设计在SeaObject超类中,
状态一般都设计为常量,同时再设计state变量表示当前状态
----在SeaObject中设计LIVE、DEAD常量,state变量表示当前状态
在后期的业务中经常需要判断对象的状态,每个对象都得判断,
意味着判断状态的行为为共有行为,所以设计在SeaObject超类中,
每个对象判断状态的代码都是一样的,所以设计为普通方法
----在SeaObject中设计isLive()、isDead()判断对象的状态
4)数据(状态、图片、x坐标、y坐标)都有了就可以开画了,每个对象都得画,
意味着画对象的行为为共有行为,所以设计在SeaObject超类中,
每个对象画的代码都是一样的,所以设计为普通方法
----在SeaObject中设计paintImage()画对象
5)画对象的方法写好了,在窗口World类中调用即可:
5.1)准备对象
5.2)重写paint()方法---在paint()中调用paintImage()即可---------不要求掌握
```
## 潜艇游戏第六天:---------要求:能够按照步骤写出来就OK
1. 潜艇入场:
- 潜艇对象是由窗口产生的,所以在World类中设计nextSubmarine()生成潜艇对象
- 潜艇入场为定时发生的,所以在run()中调用submarineEnterAction()实现潜艇入场
- 在submarineEnterAction()中:
- 每400毫秒,获取潜艇对象obj,submarines扩容,将obj添加到submarines最后一个元素上
> 注意:run()中调用submarineEnterAction()后,一定要调用repaint()来重画
2. 水雷入场(上半段):
- 水雷对象是由水雷潜艇发射出来的,所以在MineSubmarine中设计shootMine()生成水雷对象
- 水雷入场为定时发生的,所以在run()中调用mineEnterAction()实现水雷入场
- 在mineEnterAction()中:
- 每1000毫秒,......暂时搁置
3. 海洋对象移动:
- 海洋对象移动为共有的行为,所以在SeaObject中设计抽象方法move(),6个派生类中重写move()移动
- 海洋对象移动为定时发生的,所以在run()中调用moveAction()实现海洋对象移动
- 在moveAction()中:
- 遍历所有潜艇,潜艇移动。遍历所有水雷,水雷移动,遍历所有炸弹,炸弹移动。
## 潜艇游戏第七天:---------要求:能够按照步骤写出来就OK
1. 炸弹入场:
- 炸弹是由战舰发射出来的,所以在Battleship中设计shootBomb()生成炸弹对象
- 炸弹入场为事件触发的,所以在侦听器中重写keyReleased()按键抬起方法,方法中判断:
- 若抬起的是空格键,则:
- 获取炸弹对象obj,bombs扩容,将obj装到最后一个元素上
2. 战舰移动:
- 战舰移动为战舰的行为,所以在Battleship中设计moveLeft()左移、moveRight()右移
- 战舰移动为事件触发的,所以在侦听器的重写keyReleased()按键抬起方法中判断:
- 若抬起的是左箭头,则战舰左移
- 若抬起的是右箭头,则战舰右移
3. 删除越界的海洋对象:
- 在SeaObject中设计isOutOfBounds()检测潜艇越界,在Bomb和Mine中重写isOutOfBounds()检测炸弹和水雷越界
- 删除越界的海洋对象是定时发生的,所以在run()中调用outOfBoundsAction()删除越界的海洋对象
- 在outOfBoundsAction()中:
- 遍历所有潜艇/水雷/炸弹数组,判断若越界了,则:
- 将越界元素替换为最后一个元素,缩容
4. 设计EnemyScore分的接口,侦察潜艇与鱼雷潜艇实现分的接口
设计EnemyLife命的接口,水雷潜艇实现命的接口
## 潜艇游戏第八天:---------要求:能够按照步骤写出来就OK
1. 水雷入场(下半段):
- 水雷对象是由水雷潜艇发射出来的,所以在MineSubmarine中设计shootMine()生成水雷对象
- 水雷入场为定时发生的,所以在run()中调用mineEnterAction()实现水雷入场
- 在mineEnterAction()中:
- 每1000毫秒,遍历所有潜艇,判断若为水雷潜艇,则强转为水雷潜艇类型:
- 获取水雷对象obj,扩容,将obj添加到最后一个元素上
2. 炸弹与潜艇的碰撞:
- 在SeaObject中设计isHit()检测碰撞、goDead()海洋对象去死
在Battleship中设计addLife()增命
- 炸弹与潜艇的碰撞为定时发生的,所以在run()中调用bombBangAction()实现炸弹与潜艇碰撞
- 在bombBangAction()中:
- 遍历所有炸弹得炸弹,遍历所有潜艇得潜艇,判断若都活着并且还撞上了:
- 潜艇去死、炸弹去死
- 判断若是分,则强转为分的接口,玩家得分
- 判断若是命,则强转为命的接口,获取命数,战舰得命
3. 画分和画命:
- 在Battleship中设计getLife()获取命数
- 在paint()中:画分和画命---------------------不要求掌握
## 潜艇游戏第九天:---------要求:能够按照步骤写出来就OK
1. 水雷与战舰的碰撞:
- 在Battleship中设计subtractLife()减命
- 水雷与战舰的碰撞为定时发生的,所以在run()中调用mineBangAction()实现水雷与战舰碰撞
- 在mineBangAction()中:
- 遍历水雷获取水雷,判断若都活着并且还撞上了:
- 水雷去死、战舰减命
2. 检测游戏结束:
- 借用Battleship类的getLife()获取命数
- 检测游戏结束为定时发生的,所以在run()中调用checkGameOverAction()检测游戏结束
- 在checkGameOverAction()中:
- 若战舰的命数<=0,表示游戏结束,则......
3. 画状态:
- 在World类中设计RUNNING、PAUSE、GAME_OVER状态常量,state变量表示当前状态
- 在checkGameOverAction()中,若游戏结束,则将state修改为GAME_OVER游戏结束状态
- 在paint()设计:当游戏结束时画游戏结束图
- 设计run中的那一堆代码为仅在运行状态时执行
- 设计重写keyReleased()中的那一堆代码为仅在运行状态时执行
- 设计若抬起的是P键,则运行状态变为暂停状态,暂停状态变为运行状态
Battleship
package cn.tedu.submarine;
import javax.swing.ImageIcon;
/**
* 战舰类
*/
public class Battleship extends SeaObject {
private int life; //命
/**
* 构造方法
*/
public Battleship(){
super(66,26,270,124,20);
life = 5;
}
/**
* 重写move()移动
*/
public void move(){
//暂时搁置
}
/**
* 重写getImage()获取对象图片
* @return 返回战舰图片
*/
public ImageIcon getImage(){
return Images.battleship; //返回战舰图片
}
/**
* 发射炸弹----生成炸弹对象
* @return 炸弹对象
*/
public Bomb shootBomb(){
return new Bomb(this.getX(),this.getY()); //炸弹的初始坐标就是战舰的坐标
}
/**
* 战舰左移
*/
public void moveLeft(){
setX(getX()-getSpeed()); //x-(向左)
}
/**
* 战舰右移
*/
public void moveRight(){
setX(getX()+getSpeed()); //x+(向右)
}
/**
* 战舰增命
* @param num 所增命的数量
*/
public void addLife(int num){
life += num; //命数增num
}
/**
* 获取战舰的命数
* @return 返回战舰命数
*/
public int getLife(){
return life; //返回命数
}
/** 战舰减命 */
public void subtractLife(){
life--; //命数减1
}
}
Bomb
package cn.tedu.submarine;
import javax.swing.*;
/**
* 炸弹类
*/
public class Bomb extends SeaObject {
/**
* 构造方法
* @param x 炸弹的初始x坐标
* @param y 炸弹的初始y坐标
*/
public Bomb(int x,int y){ //因为炸弹的初始坐标,是根据战舰的坐标计算出来的,所以不能写死
super(9,12,x,y,3);
}
/**
* 重写move()移动
*/
public void move(){
setY(getY()+getSpeed()); //y+(向下)
}
/**
* 重写getImage()获取对象图片
* @return 返回炸弹图片
*/
public ImageIcon getImage(){
return Images.bomb; //返回炸弹图片
}
/**
* 重写isOutOfBounds()检测炸弹是否越界
* @return 若越界则返回true,否则返回false
*/
public boolean isOutOfBounds(){
return this.getY()>=World.HEIGHT; //炸弹的y>=窗口的高,即为越界了
}
}
EnemyLife 接口
package cn.tedu.submarine;
/**
* 命
*/
public interface EnemyLife {
/**
* 得命
* @return 命数
*/
public int getLife();
}
EnemyScore 得分
package cn.tedu.submarine;
/**
* 分
*/
public interface EnemyScore {
/**
* 得分
* @return 返回分数
*/
public int getScore();
}
Images 照片
package cn.tedu.submarine;
import javax.swing.ImageIcon;
//图片位置:点项目名称,右键New Directory,命名为img,将图片粘贴到img中
/**
* 图片类
*/
public class Images {
// 公开的 静态的 图片类型 变量名
public static ImageIcon sea;
public static ImageIcon gameover;
public static ImageIcon battleship;
public static ImageIcon obsersubm;
public static ImageIcon torpesubm;
public static ImageIcon minesubm;
public static ImageIcon mine;
public static ImageIcon bomb;
static{ //初始化静态图片
sea = new ImageIcon("img/sea.png"); //读取img下的sea.png到变量sea中
gameover = new ImageIcon("img/gameover.png");
battleship = new ImageIcon("img/battleship.png");
obsersubm = new ImageIcon("img/obsersubm.png");
torpesubm = new ImageIcon("img/torpesubm.png");
minesubm = new ImageIcon("img/minesubm.png");
mine = new ImageIcon("img/mine.png");
bomb = new ImageIcon("img/bomb.png");
}
public static void main(String[] args) {
//返回8表示图片读取成功,返回其余数字表示图片读取失败
System.out.println(sea.getImageLoadStatus()); //8
System.out.println(gameover.getImageLoadStatus());
System.out.println(battleship.getImageLoadStatus());
System.out.println(obsersubm.getImageLoadStatus());
System.out.println(torpesubm.getImageLoadStatus());
System.out.println(minesubm.getImageLoadStatus());
System.out.println(mine.getImageLoadStatus());
System.out.println(bomb.getImageLoadStatus());
}
}
Mine
package cn.tedu.submarine;
import javax.swing.*;
/**
* 水雷类
*/
public class Mine extends SeaObject {
/**
* 构造方法
* @param x 水雷的初始x坐标
* @param y 水雷的初始y坐标
*/
public Mine(int x,int y){ //因为水雷的初始坐标,是根据水雷潜艇的坐标计算出来的,所以不能写死
super(11,11,x,y,1);
}
/**
* 重写move()移动
*/
public void move(){
setY(getY()-getSpeed()); //y-(向上)
}
/**
* 重写getImage()获取对象图片
* @return 返回水雷图片
*/
public ImageIcon getImage(){
return Images.mine; //返回水雷图片
}
/**
* 重写isOutOfBounds()检测水雷是否越界
* @return 若越界则返回true,否则返回false
*/
public boolean isOutOfBounds(){
return this.getY()<=150-this.getHeight(); //水雷的y<=150-水雷的高,即为越界了
}
}
MineSubmarine
package cn.tedu.submarine;
import javax.swing.*;
/**
* 水雷潜艇类
*/
public class MineSubmarine extends SeaObject implements EnemyLife {
/**
* 构造方法
*/
public MineSubmarine(){
super(63,19);
}
/**
* 重写move()移动
*/
public void move(){
setX(getX()+getSpeed()); //x+(向右)
}
/**
* 重写getImage()获取对象图片
* @return 返回水雷潜艇图片
*/
public ImageIcon getImage(){
return Images.minesubm; //返回水雷潜艇图片
}
/**
* 发射水雷----生成水雷对象
* @return 返回水雷对象
*/
public Mine shootMine(){
//x:水雷潜艇的x+水雷潜艇的宽
//y:水雷潜艇的y-11
return new Mine(this.getX()+this.getWidth(),this.getY()-11); //this指的是水雷潜艇
}
/**
* 重写getLife()得命
* @return 返回命数
*/
public int getLife(){
return 1; //打掉水雷潜艇,得1条命
}
}
ObserveSubmarine
package cn.tedu.submarine;
import javax.swing.*;
/**
* 侦察潜艇类
*/
public class ObserveSubmarine extends SeaObject implements EnemyScore {
/**
* 构造方法
*/
public ObserveSubmarine(){
super(63,19);
}
/**
* 重写move()移动
*/
public void move(){
setX(getX()+getSpeed()); //x+(向右)
}
/**
* 重写getImage()获取对象图片
* @return 返回侦察潜艇图片
*/
public ImageIcon getImage(){
return Images.obsersubm; //返回侦察潜艇图片
}
/**
* 重写getScore()得分
* @return 返回分数
*/
public int getScore(){
return 10; //打掉侦察潜艇,得10分
}
}
SeaObject
package cn.tedu.submarine;
import javax.swing.ImageIcon;
import java.awt.*;
import java.util.Random;
/**
* 海洋对象类,是所有类的超类
*/
public abstract class SeaObject {
/**
* 活着的
*/
public static final int LIVE = 0;
/**
* 死了的
*/
public static final int DEAD = 1; //死了的
private int state = LIVE; //当前状态(默认为活着的)
private int width; //宽
private int height; //高
private int x; //x坐标
private int y; //y坐标
private int speed; //速度
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
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 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 getSpeed() {
return speed;
}
public void setSpeed(int speed) {
this.speed = speed;
}
//因为三种潜艇的width/height都是不一样的,所以数据不能写死,需传参写活
//因为三种潜艇的x/y/speed代码都是一样的,所以数据可以写死,不需要传参
/**
* 专门给侦察潜艇、鱼雷潜艇、水雷潜艇提供的构造方法
* @param width 潜艇的宽
* @param height 潜艇的高
*/
public SeaObject(int width,int height){
this.width = width;
this.height = height;
x = -width;
Random rand = new Random(); //创建随机数对象
y = rand.nextInt(World.HEIGHT-height-150+1)+150; //150到(窗口高-潜艇高)之内随机生成
speed = rand.nextInt(3)+1; //1到3之内随机生成
}
//因为三种对象的width/height/x/y/speed都是不一样的,所以数据不能写死,需传参写活
/**
* 专门给战舰、水雷、炸弹提供的构造方法
* @param width 宽
* @param height 高
* @param x x坐标
* @param y y坐标
* @param speed 速度
*/
public SeaObject(int width,int height,int x,int y,int speed){
this.width = width;
this.height = height;
this.x = x;
this.y = y;
this.speed = speed;
}
/**
* 移动
*/
public abstract void move();
/**
* 获取对象的图片
* @return 返回对象所对应的图片
*/
public abstract ImageIcon getImage();
/**
* 判断对象是否是活着的
* @return 返回true表示活着的,否则表示死了的
*/
public boolean isLive(){
return state==LIVE; //若state为LIVE,表示活着的,返回true,否则返回false
}
/**
* 判断对象是否是死了的
* @return 返回true表示死了的,否则表示活着的
*/
public boolean isDead(){
return state==DEAD; //若state为DEAD,表示死了的,返回true,否则返回false
}
/**
* 画对象
* @param g 画笔
*/
public void paintImage(Graphics g){
if(this.isLive()){ //若活着的
this.getImage().paintIcon(null,g,this.x,this.y); //----不要求掌握
}
}
/**
* 检测潜艇是否越界
* @return 若越界则返回true,否则返回false
*/
public boolean isOutOfBounds(){
return this.x>=World.WIDTH; //潜艇的x>=窗口的宽,即为越界了
}
/**
* 检测碰撞
* @param other 另一个对象 this表示一个对象
* @return 若撞上了则返回true,否则返回false
*/
public boolean isHit(SeaObject other){
//假设:this为潜艇,other为炸弹
int x1 = this.x-other.width; //x1:潜艇的x-炸弹的宽
int x2 = this.x+this.width; //x2:潜艇的x+潜艇的宽
int y1 = this.y-other.height; //y1:潜艇的y-炸弹的高
int y2 = this.y+this.height; //y2:潜艇的y+潜艇的高
int x = other.x; //x:炸弹的x
int y = other.y; //y:炸弹的y //练习-----------2:34继续
return x>=x1 && x<=x2
&&
y>=y1 && y<=y2; //x在x1与x2之间,并且,y在y1与y2之间,即为撞上了
}
/**
* 海洋对象去死
*/
public void goDead(){
state = DEAD; //将当前状态修改为DEAD死了的
}
}
TorpedoSubmarine
package cn.tedu.submarine;
import javax.swing.*;
/**
* 鱼雷潜艇类
*/
public class TorpedoSubmarine extends SeaObject implements EnemyScore {
/**
* 构造方法
*/
public TorpedoSubmarine(){
super(64,20);
}
/**
* 重写move()移动
*/
public void move(){
setX(getX()+getSpeed()); //x+(向右)
}
/**
* 重写getImage()获取对象图片
* @return 返回鱼雷潜艇图片
*/
public ImageIcon getImage(){
return Images.torpesubm; //返回鱼雷潜艇图片
}
/**
* 重写getScore()得分
* @return 返回分数
*/
public int getScore(){
return 40; //打掉鱼雷潜艇,得40分
}
}
World 整个游戏窗口
package cn.tedu.submarine;
import javax.swing.JFrame;
import javax.swing.JPanel; //1.
import java.awt.Graphics;
import java.util.Arrays;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Random;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
/**
* 整个游戏窗口
*/
public class World extends JPanel { //2.
/**
* 窗口的宽
*/
public static final int WIDTH = 641;
/**
* 窗口的高
*/
public static final int HEIGHT = 479;
/**
* 运行状态
*/
public static final int RUNNING = 0;
/**
* 暂停状态
*/
public static final int PAUSE = 1;
/**
* 游戏结束状态
*/
public static final int GAME_OVER = 2;
private int state = RUNNING; //当前状态(默认为运行状态)
//如下这一堆对象就是窗口中你所看到的对象
private Battleship ship = new Battleship(); //战舰对象
private SeaObject[] submarines = {}; //潜艇数组
private Mine[] mines = {}; //水雷数组
private Bomb[] bombs = {}; //炸弹数组
/**
* 生成潜艇(侦察潜艇、鱼雷潜艇、水雷潜艇)对象
* @return 返回潜艇
*/
private SeaObject nextSubmarine(){
Random rand = new Random(); //随机数对象
int type = rand.nextInt(20); //0到19之间
if(type<10){ //0到9时,返回侦察潜艇对象
return new ObserveSubmarine();
}else if(type<16){ //10到15时,返回鱼雷潜艇对象
return new TorpedoSubmarine();
}else{ //16到19时,返回水雷潜艇对象
return new MineSubmarine();
}
}
private int subEnterIndex = 0; //潜艇入场计数
/**
* 潜艇入场
*/
private void submarineEnterAction(){ //每10毫秒走一次
subEnterIndex++; //每10毫秒增1
if(subEnterIndex%40==0){ //每400(40*10)毫秒走一次
SeaObject obj = nextSubmarine(); //获取潜艇对象
submarines = Arrays.copyOf(submarines,submarines.length+1); //扩容
submarines[submarines.length-1] = obj; //将obj添加到submarines最后一个元素上
}
}
private int mineEnterIndex = 0; //水雷入场计数
/**
* 水雷入场
*/
private void mineEnterAction(){ //每10毫秒走一次
mineEnterIndex++; //每10毫秒增1
if(mineEnterIndex%100==0){ //每1000(100*10)毫秒走一次
for(int i=0;i