潜艇游戏需求:
- 所参与的角色:
- 战舰、深水炸弹、侦察潜艇、鱼雷潜艇、水雷潜艇、水雷
- 角色间的关系:
- 战舰发射深水炸弹
- 深水炸弹可以打潜艇(侦察潜艇、鱼雷潜艇、水雷潜艇),若打中:
- 潜艇消失、深水炸弹消失
- 得东西:
- 打掉侦察潜艇,玩家得10分
- 打掉鱼雷潜艇,玩家得40分
- 打掉水雷潜艇,战舰得1条命
- 水雷潜艇可以发射水雷
- 水雷可以击打战舰,若击中:
- 水雷消失
- 战舰减1条命(命数为0时游戏结束)
潜艇游戏第一天:
- 根据参与的角色创建6个类,创建World类并测试;
潜艇游戏第二天:
- 给6个类添加构造方法,并测试;(以战舰类为例)
public class Battleship {
int width; //宽
int height; //高
int x; //x坐标
int y; //y坐标
int speed; //移动速度
int life; //命数
Battleship(){
width = 66;
height = 26;
x = 270;
y = 124;
speed = 20;
life = 5;
}
void move(){
System.out.println("战舰移动啦!");
}
}
潜艇游戏第三天:
- 在World类中创建侦察潜艇数组、鱼雷潜艇数组、水雷潜艇数组,水雷数组,炸弹数组,并测试;
public class World {
public static void main(String[] args) {
ObserveSubmarine[] oses = new ObserveSubmarine[3];
oses[0] = new ObserveSubmarine();
oses[1] = new ObserveSubmarine();
oses[2] = new ObserveSubmarine();
for(int i=0;i - 设计SeaObject超类,6个类继承超类;(以战舰类为例)
public class Battleship extends SeaObject {
int life; //命数
Battleship(){
super(66,26,270,124,20);
life = 5;
}
} - 在SeaObject中设计两个构造方法,6个派生类分别调用;
import java.util.Random;
public class SeaObject {
int width; //宽
int height; //高
int x; //x坐标
int y; //y坐标
int speed; //移动速度
//因为三种潜艇的width/height的值都是不一样的,所以数据不能写死,需传参写活
//因为三种潜艇的x/y/speed的值都是一样的,所以数据可以写死,不需要传参
SeaObject(int width,int height){
this.width = width;
this.height = height;
x = -width; //负的潜艇的宽
Random rand = new Random(); //随机数对象
y = rand.nextInt(479-height-150+1)+150; //150到(窗口高-潜艇高)之间的随机数
speed = rand.nextInt(3)+1; //1到3之内的随机数
}
//因为三种对象的width/height/x/y/speed都是不一样的,所以数据不能写死,需传参写活
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;
}
void move(){
System.out.println("海洋对象移动啦!");
}
}
潜艇游戏第四天:
- 将侦察潜艇数组、鱼雷潜艇数组、水雷潜艇数组统一组合为SeaObject超类数组,在World类中改写并测试;
public class World extends JPanel { //2.
public static void main(String[] args) {
ObserveSubmarine o1 = new ObserveSubmarine();
System.out.println("侦察艇初始数据: x:"+o1.x+", y:"+o1.y+", speed:"+o1.speed);
o1.move();
System.out.println("侦察艇移动后数据: x:"+o1.x+", y:"+o1.y+", speed:"+o1.speed);
Mine o2 = new Mine(100,200);
System.out.println("水雷初始数据: x:"+o2.x+", y:"+o2.y+", speed:"+o2.speed);
o2.move();
System.out.println("水雷移动后数据: x:"+o2.x+", y:"+o2.y+", speed:"+o2.speed);
SeaObject[] submarines = new SeaObject[5]; //潜艇数组
submarines[0] = new ObserveSubmarine(); //向上造型
submarines[1] = new ObserveSubmarine();
submarines[2] = new TorpedoSubmarine();
submarines[3] = new TorpedoSubmarine();
submarines[4] = new MineSubmarine();
for(int i=0;i - 在6个类中重写move()移动,并测试;(以战舰类为例)
public class Battleship extends SeaObject {
int life; //命数
Battleship(){
super(66,26,270,124,20);
life = 5;
}
void move(){
//暂时搁置
}
} - 在World类中画窗口:
import javax.swing.JFrame;
import javax.swing.JPanel; //1.第一步
public class World extends JPanel { //2.第二步
public static void main(String[] args) {
JFrame frame = new JFrame(); //3.第三步
World world = new World();
world.setFocusable(true);
frame.add(world);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(641+16, 479+39);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
潜艇游戏第五天:
- 给类中成员添加访问控制修饰符;(以战舰类为例) 访问控制修饰符可看:(13条消息) Java学习日记Day11(面向对象day05)_终为尘埃的博客-CSDN博客
public class Battleship extends SeaObject {
private int life; //命数
public Battleship(){
super(66,26,270,124,20);
life = 5;
}
public void move(){
//暂时搁置
}
} - 设计Images图片类;
import javax.swing.ImageIcon;
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{ //初始化静态图片
//将img中的sea.png读取到静态变量sea中
sea = new ImageIcon("img/sea.png");
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());
}
}
潜艇游戏第六天:
-
在World类中设计窗口的宽和高为常量,适当地方做修改,重写getImage()方法(以战舰类为例);
import javax.swing.ImageIcon;
public class Battleship extends SeaObject {
private int life; //命数
public Battleship(){
super(66,26,270,124,20);
life = 5;
}
public void move(){
//暂时搁置
}
public ImageIcon getImage(){
return Images.battleship; //返回战舰图片
}
}
-
画对象:
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()画对象即可
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.*;
import java.util.Arrays;
public class World extends JPanel {
public static final int WIDTH = 641; //窗口的宽
public static final int HEIGHT = 479; //窗口的高
//如下这一堆对象就是窗口中你所看到的对象了
private Battleship ship = new Battleship(); //战舰对象
private SeaObject[] submarines = {
new ObserveSubmarine(),
new TorpedoSubmarine(),
new MineSubmarine()
}; //潜艇数组
private Mine[] mines = {
new Mine(280,300)
}; //水雷数组
private Bomb[] bombs = {
new Bomb(200,250)
}; //炸弹数组
public void paint(Graphics g){
Images.sea.paintIcon(null,g,0,0); //画海洋图
ship.paintImage(g); //画战舰
for(int i=0;i
潜艇游戏第七天:
-
潜艇入场:
-
潜艇是由窗口产生的,所以在窗口World类中设计nextSubmarine()生成潜艇对象;
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();
}
} -
潜艇入场为定时发生的,所以在run()中调用submarineEnterAction()实现潜艇入场
在submarineEnterAction()中:
每400毫秒,获取潜艇对象obj,submarines扩容,将obj添加到最后一个元素上 ;
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 void action(){
Timer timer = new Timer(); //定时器对象
int interval = 10; //定时间隔(以毫秒为单位)
timer.schedule(new TimerTask() {
public void run() { //定时干的事---每10毫秒自动调用
submarineEnterAction(); //潜艇(侦察潜艇、鱼雷潜艇、水雷入场)入场
repaint(); //重画---系统自动调用paint()方法
}
}, interval, interval); //定时日程表
}
注意:在run()中调用submarineEnterAction()之后,一定要调用repaint()来重画
-
水雷入场:
-
水雷是由水雷潜艇发射出来的,所以在MineSubmarine中设计shootMine()生成水雷对象
-
水雷入场为定时发生的,所以在run()中调用mineEnterAction()实现水雷入场
在mineEnterAction()中:
每1000毫秒,
private int mineEnterIndex = 0; //水雷入场计数
private void mineEnterAction(){ //每10毫秒走一次
mineEnterIndex++; //每10毫秒增1
if(mineEnterIndex%100==0) { //每1000毫秒走一次
//暂时搁置
}
}
-
海洋对象移动:
-
海洋对象移动为共有行为,所以在SeaObject中设计抽象方法move()实现移动,派生类中重写
-
海洋对象移动为定时发生的,所以在run()中调用moveAction()实现海洋对象移动
在moveAction()中:
遍历所有潜艇--潜艇动,遍历所有水雷--水雷动,遍历所有炸弹--炸弹动
private void moveAction(){ //每10毫秒走一次
for(int i=0;i
潜艇游戏第八天:
-
炸弹入场:
-
炸弹是由战舰发射出来的,所以在Battleship类中设计shootBomb()生成炸弹对象;
public Bomb shootBomb(){
return new Bomb(this.x,this.y); //炸弹的初始坐标就是战舰的坐标
} -
炸弹入场为按空格键事件触发的,所以在侦听器中重写keyReleased()按键抬起事件,在抬起事件中:
-
判断若抬起的是空格键,则:
获取炸弹对象obj,bombs扩容,将obj添加到bombs的最后一个元素上
-
战舰移动:
- 战舰移动为战舰的行为,所以在Battleship中设计moveLeft()左移、moveRight()右移
- 战舰移动为事件触发的,所以在侦听器的重写keyReleased()按键抬起事件中:
- 判断若抬起的是左箭头,则战舰左移
- 判断若抬起的是右箭头,则战舰右移
//键盘侦听器
KeyAdapter k = new KeyAdapter() {
public void keyReleased(KeyEvent e) { //当按键抬起时会自动触发
if(e.getKeyCode()==KeyEvent.VK_SPACE){ //若抬起的是空格键
Bomb obj = ship.shootBomb(); //获取炸弹对象
bombs = Arrays.copyOf(bombs,bombs.length+1); //扩容
bombs[bombs.length-1] = obj; //将obj添加到最后一个元素上
}
if(e.getKeyCode()==KeyEvent.VK_LEFT){ //若抬起的是左箭头
ship.moveLeft(); //战舰左移
}
if(e.getKeyCode()==KeyEvent.VK_RIGHT) { //若抬起的是右箭头
ship.moveRight(); //战舰右移
}
}
};
this.addKeyListener(k); //添加侦听
-
删除越界的海洋对象:
-
在SeaObject中设计isOutOfBounds()检测潜艇是否越界,在Bomb和Mine中重写isOutOfBounds()检测炸弹和水雷是否越界;
-
删除越界海洋对象为定时发生的,所以在run()中调用outOfBoundsAction()删除越界海洋对象
在outOfBoundsAction()中:
遍历所有潜艇/水雷/炸弹,判断若越界了:
将越界元素替换为数组的最后一个元素,缩容;
private void outOfBoundsAction(){ //每10毫秒走一次
for(int i=0;i
-
设计EnemyScore得分接口,ObserveSubmarine和TorpedoSubmarine实现得分接口;
public interface EnemyScore {
public int getScore();
} 设计EnemyLife得命接口,MineSubmarine实现得命接口;
public interface EnemyLife {
public int getLife();
}
潜艇游戏第九天:
-
水雷入场:--------------后半段
-
水雷是由水雷潜艇发射出来的,所以在MineSubmarine中设计shootMine()生成水雷对象;
public Mine shootMine(){
//水雷的x:水雷潜艇的x+水雷潜艇的width
//水雷的y:水雷潜艇的y-水雷的高(11)
return new Mine(this.x+this.width,this.y-11); //this指水雷潜艇对象
} -
水雷入场为定时发生的,所以在run()中调用mineEnterAction()实现水雷入场
在mineEnterAction()中:
每1000毫秒,遍历所有潜艇,判断若为水雷潜艇,则强转为水雷潜艇类型,
获取水雷对象obj,mines扩容,将obj装到最后一个元素上;
private int mineEnterIndex = 0; //水雷入场计数
private void mineEnterAction(){ //每10毫秒走一次
mineEnterIndex++; //每10毫秒增1
if(mineEnterIndex%100==0) { //每1000毫秒走一次
for(int i=0;i
-
炸弹与潜艇的碰撞:
-
在SeaObject中设计isHit()检测碰撞、goDead()去死;
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
return x>=x1 && x<=x2 && y>=y1 && y<=y2; //x在x1与x2之间,并且,y在y1与y2之间,即为撞上了
}
public void goDead(){
state = DEAD; //将当前状态修改为DEAD死了的
} -
在Battleship中设计addLife()增命;
public void addLife(int num){
life += num; //命数增num
} -
炸弹与潜艇的碰撞为定时发生的,所以在run()中设计bombBangAction()实现炸弹与潜艇碰撞
在bombBangAction()中:
遍历所有炸弹得炸弹,遍历所有潜艇得潜艇,判断若都活着并且还撞上了:
炸弹去死、潜艇去死;
判断若是分,则强转为得分接口,玩家得分;
判断若是命,则强转为得命接口,获取命数,战舰得命;
private int score = 0 ; //玩家得分
private void bombBangAction(){ //每10毫秒走一次
for(int i=0;i
-
画分和画命:
- 在Battleship中设计getLife()获取命数;
public int getLife(){
return life; //返回命数
} - 在World的paint()中:画分和画命;
public void paint(Graphics g){ //每10毫秒走一次
Images.sea.paintIcon(null,g,0,0); //画海洋图
ship.paintImage(g); //画战舰
for(int i=0;i
潜艇游戏第十天:
-
水雷与战舰的碰撞:
-
在Battleship中设计subtractLife()减命;
public void subtractLife(){
life--; //命数减1
} -
水雷与战舰碰撞为定时发生的,所以在run()中调用mineBangAction()实现水雷与战舰的碰撞;
在mineBangAction()中:
遍历所有水雷,得水雷,判断若都活着并且还撞上了:
水雷去死、战舰减命;
private void mineBangAction(){ //每10毫秒走一次
for(int i=0;i
-
检测游戏结束:
-
借用Battleship的getLife()获取命数;
-
检测游戏结束为定时发生的,所以在run()中调checkGameOverAction()检测游戏结束
在checkGameOverAction()中:
判断若战舰的命数<=0,表示游戏结束了;
private void checkGameOverAction(){ //每10毫秒走一次
if(ship.getLife()<=0){ //若战舰的命数<=0,表示游戏结束了
state = GAME_OVER; //则将当前状态修改为GAME_OVER游戏结束状态
}
}
-
画状态:
-
在World类中设计RUNNING、PAUSE、GAME_OVER状态常量,state变量表示当前状态;
-
在checkGameOverAction()中,若游戏结束, 则将当前状态修改为GAME_OVER游戏结束状态;
-
在paint()中设计:若为游戏结束状态则画游戏结束图;
-
设计run()中那一堆代码,为仅在运行状态下执行;
-
设计键盘抬起时,按下空格键、左移键、右移键,也为仅在运行状态下执行;
-
在键盘抬起时,设计若按的是P键,则运行状态变暂停状态,暂停状态变运行状态。
另外还实现一个飞机小游戏,过程相似。结果如图:
如有需要,可评论区留下邮箱,后会发送。
- 战舰、深水炸弹、侦察潜艇、鱼雷潜艇、水雷潜艇、水雷
- 战舰发射深水炸弹
- 深水炸弹可以打潜艇(侦察潜艇、鱼雷潜艇、水雷潜艇),若打中:
- 潜艇消失、深水炸弹消失
- 得东西:
- 打掉侦察潜艇,玩家得10分
- 打掉鱼雷潜艇,玩家得40分
- 打掉水雷潜艇,战舰得1条命
- 水雷潜艇可以发射水雷
- 水雷可以击打战舰,若击中:
- 水雷消失
- 战舰减1条命(命数为0时游戏结束)
- 根据参与的角色创建6个类,创建World类并测试;
潜艇游戏第二天:
- 给6个类添加构造方法,并测试;(以战舰类为例)
public class Battleship {
int width; //宽
int height; //高
int x; //x坐标
int y; //y坐标
int speed; //移动速度
int life; //命数
Battleship(){
width = 66;
height = 26;
x = 270;
y = 124;
speed = 20;
life = 5;
}
void move(){
System.out.println("战舰移动啦!");
}
}
潜艇游戏第三天:
- 在World类中创建侦察潜艇数组、鱼雷潜艇数组、水雷潜艇数组,水雷数组,炸弹数组,并测试;
public class World {
public static void main(String[] args) {
ObserveSubmarine[] oses = new ObserveSubmarine[3];
oses[0] = new ObserveSubmarine();
oses[1] = new ObserveSubmarine();
oses[2] = new ObserveSubmarine();
for(int i=0;i - 设计SeaObject超类,6个类继承超类;(以战舰类为例)
public class Battleship extends SeaObject {
int life; //命数
Battleship(){
super(66,26,270,124,20);
life = 5;
}
} - 在SeaObject中设计两个构造方法,6个派生类分别调用;
import java.util.Random;
public class SeaObject {
int width; //宽
int height; //高
int x; //x坐标
int y; //y坐标
int speed; //移动速度
//因为三种潜艇的width/height的值都是不一样的,所以数据不能写死,需传参写活
//因为三种潜艇的x/y/speed的值都是一样的,所以数据可以写死,不需要传参
SeaObject(int width,int height){
this.width = width;
this.height = height;
x = -width; //负的潜艇的宽
Random rand = new Random(); //随机数对象
y = rand.nextInt(479-height-150+1)+150; //150到(窗口高-潜艇高)之间的随机数
speed = rand.nextInt(3)+1; //1到3之内的随机数
}
//因为三种对象的width/height/x/y/speed都是不一样的,所以数据不能写死,需传参写活
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;
}
void move(){
System.out.println("海洋对象移动啦!");
}
}
潜艇游戏第四天:
- 将侦察潜艇数组、鱼雷潜艇数组、水雷潜艇数组统一组合为SeaObject超类数组,在World类中改写并测试;
public class World extends JPanel { //2.
public static void main(String[] args) {
ObserveSubmarine o1 = new ObserveSubmarine();
System.out.println("侦察艇初始数据: x:"+o1.x+", y:"+o1.y+", speed:"+o1.speed);
o1.move();
System.out.println("侦察艇移动后数据: x:"+o1.x+", y:"+o1.y+", speed:"+o1.speed);
Mine o2 = new Mine(100,200);
System.out.println("水雷初始数据: x:"+o2.x+", y:"+o2.y+", speed:"+o2.speed);
o2.move();
System.out.println("水雷移动后数据: x:"+o2.x+", y:"+o2.y+", speed:"+o2.speed);
SeaObject[] submarines = new SeaObject[5]; //潜艇数组
submarines[0] = new ObserveSubmarine(); //向上造型
submarines[1] = new ObserveSubmarine();
submarines[2] = new TorpedoSubmarine();
submarines[3] = new TorpedoSubmarine();
submarines[4] = new MineSubmarine();
for(int i=0;i - 在6个类中重写move()移动,并测试;(以战舰类为例)
public class Battleship extends SeaObject {
int life; //命数
Battleship(){
super(66,26,270,124,20);
life = 5;
}
void move(){
//暂时搁置
}
} - 在World类中画窗口:
import javax.swing.JFrame;
import javax.swing.JPanel; //1.第一步
public class World extends JPanel { //2.第二步
public static void main(String[] args) {
JFrame frame = new JFrame(); //3.第三步
World world = new World();
world.setFocusable(true);
frame.add(world);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(641+16, 479+39);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
潜艇游戏第五天:
- 给类中成员添加访问控制修饰符;(以战舰类为例) 访问控制修饰符可看:(13条消息) Java学习日记Day11(面向对象day05)_终为尘埃的博客-CSDN博客
public class Battleship extends SeaObject {
private int life; //命数
public Battleship(){
super(66,26,270,124,20);
life = 5;
}
public void move(){
//暂时搁置
}
} - 设计Images图片类;
import javax.swing.ImageIcon;
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{ //初始化静态图片
//将img中的sea.png读取到静态变量sea中
sea = new ImageIcon("img/sea.png");
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());
}
}
潜艇游戏第六天:
-
在World类中设计窗口的宽和高为常量,适当地方做修改,重写getImage()方法(以战舰类为例);
import javax.swing.ImageIcon;
public class Battleship extends SeaObject {
private int life; //命数
public Battleship(){
super(66,26,270,124,20);
life = 5;
}
public void move(){
//暂时搁置
}
public ImageIcon getImage(){
return Images.battleship; //返回战舰图片
}
}
-
画对象:
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()画对象即可
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.*;
import java.util.Arrays;
public class World extends JPanel {
public static final int WIDTH = 641; //窗口的宽
public static final int HEIGHT = 479; //窗口的高
//如下这一堆对象就是窗口中你所看到的对象了
private Battleship ship = new Battleship(); //战舰对象
private SeaObject[] submarines = {
new ObserveSubmarine(),
new TorpedoSubmarine(),
new MineSubmarine()
}; //潜艇数组
private Mine[] mines = {
new Mine(280,300)
}; //水雷数组
private Bomb[] bombs = {
new Bomb(200,250)
}; //炸弹数组
public void paint(Graphics g){
Images.sea.paintIcon(null,g,0,0); //画海洋图
ship.paintImage(g); //画战舰
for(int i=0;i
潜艇游戏第七天:
-
潜艇入场:
-
潜艇是由窗口产生的,所以在窗口World类中设计nextSubmarine()生成潜艇对象;
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();
}
} -
潜艇入场为定时发生的,所以在run()中调用submarineEnterAction()实现潜艇入场
在submarineEnterAction()中:
每400毫秒,获取潜艇对象obj,submarines扩容,将obj添加到最后一个元素上 ;
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 void action(){
Timer timer = new Timer(); //定时器对象
int interval = 10; //定时间隔(以毫秒为单位)
timer.schedule(new TimerTask() {
public void run() { //定时干的事---每10毫秒自动调用
submarineEnterAction(); //潜艇(侦察潜艇、鱼雷潜艇、水雷入场)入场
repaint(); //重画---系统自动调用paint()方法
}
}, interval, interval); //定时日程表
}
注意:在run()中调用submarineEnterAction()之后,一定要调用repaint()来重画
-
水雷入场:
-
水雷是由水雷潜艇发射出来的,所以在MineSubmarine中设计shootMine()生成水雷对象
-
水雷入场为定时发生的,所以在run()中调用mineEnterAction()实现水雷入场
在mineEnterAction()中:
每1000毫秒,
private int mineEnterIndex = 0; //水雷入场计数
private void mineEnterAction(){ //每10毫秒走一次
mineEnterIndex++; //每10毫秒增1
if(mineEnterIndex%100==0) { //每1000毫秒走一次
//暂时搁置
}
}
-
海洋对象移动:
-
海洋对象移动为共有行为,所以在SeaObject中设计抽象方法move()实现移动,派生类中重写
-
海洋对象移动为定时发生的,所以在run()中调用moveAction()实现海洋对象移动
在moveAction()中:
遍历所有潜艇--潜艇动,遍历所有水雷--水雷动,遍历所有炸弹--炸弹动
private void moveAction(){ //每10毫秒走一次
for(int i=0;i
潜艇游戏第八天:
-
炸弹入场:
-
炸弹是由战舰发射出来的,所以在Battleship类中设计shootBomb()生成炸弹对象;
public Bomb shootBomb(){
return new Bomb(this.x,this.y); //炸弹的初始坐标就是战舰的坐标
} -
炸弹入场为按空格键事件触发的,所以在侦听器中重写keyReleased()按键抬起事件,在抬起事件中:
-
判断若抬起的是空格键,则:
获取炸弹对象obj,bombs扩容,将obj添加到bombs的最后一个元素上
-
战舰移动:
- 战舰移动为战舰的行为,所以在Battleship中设计moveLeft()左移、moveRight()右移
- 战舰移动为事件触发的,所以在侦听器的重写keyReleased()按键抬起事件中:
- 判断若抬起的是左箭头,则战舰左移
- 判断若抬起的是右箭头,则战舰右移
//键盘侦听器
KeyAdapter k = new KeyAdapter() {
public void keyReleased(KeyEvent e) { //当按键抬起时会自动触发
if(e.getKeyCode()==KeyEvent.VK_SPACE){ //若抬起的是空格键
Bomb obj = ship.shootBomb(); //获取炸弹对象
bombs = Arrays.copyOf(bombs,bombs.length+1); //扩容
bombs[bombs.length-1] = obj; //将obj添加到最后一个元素上
}
if(e.getKeyCode()==KeyEvent.VK_LEFT){ //若抬起的是左箭头
ship.moveLeft(); //战舰左移
}
if(e.getKeyCode()==KeyEvent.VK_RIGHT) { //若抬起的是右箭头
ship.moveRight(); //战舰右移
}
}
};
this.addKeyListener(k); //添加侦听
-
删除越界的海洋对象:
-
在SeaObject中设计isOutOfBounds()检测潜艇是否越界,在Bomb和Mine中重写isOutOfBounds()检测炸弹和水雷是否越界;
-
删除越界海洋对象为定时发生的,所以在run()中调用outOfBoundsAction()删除越界海洋对象
在outOfBoundsAction()中:
遍历所有潜艇/水雷/炸弹,判断若越界了:
将越界元素替换为数组的最后一个元素,缩容;
private void outOfBoundsAction(){ //每10毫秒走一次
for(int i=0;i
-
设计EnemyScore得分接口,ObserveSubmarine和TorpedoSubmarine实现得分接口;
public interface EnemyScore {
public int getScore();
} 设计EnemyLife得命接口,MineSubmarine实现得命接口;
public interface EnemyLife {
public int getLife();
}
潜艇游戏第九天:
-
水雷入场:--------------后半段
-
水雷是由水雷潜艇发射出来的,所以在MineSubmarine中设计shootMine()生成水雷对象;
public Mine shootMine(){
//水雷的x:水雷潜艇的x+水雷潜艇的width
//水雷的y:水雷潜艇的y-水雷的高(11)
return new Mine(this.x+this.width,this.y-11); //this指水雷潜艇对象
} -
水雷入场为定时发生的,所以在run()中调用mineEnterAction()实现水雷入场
在mineEnterAction()中:
每1000毫秒,遍历所有潜艇,判断若为水雷潜艇,则强转为水雷潜艇类型,
获取水雷对象obj,mines扩容,将obj装到最后一个元素上;
private int mineEnterIndex = 0; //水雷入场计数
private void mineEnterAction(){ //每10毫秒走一次
mineEnterIndex++; //每10毫秒增1
if(mineEnterIndex%100==0) { //每1000毫秒走一次
for(int i=0;i
-
炸弹与潜艇的碰撞:
-
在SeaObject中设计isHit()检测碰撞、goDead()去死;
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
return x>=x1 && x<=x2 && y>=y1 && y<=y2; //x在x1与x2之间,并且,y在y1与y2之间,即为撞上了
}
public void goDead(){
state = DEAD; //将当前状态修改为DEAD死了的
} -
在Battleship中设计addLife()增命;
public void addLife(int num){
life += num; //命数增num
} -
炸弹与潜艇的碰撞为定时发生的,所以在run()中设计bombBangAction()实现炸弹与潜艇碰撞
在bombBangAction()中:
遍历所有炸弹得炸弹,遍历所有潜艇得潜艇,判断若都活着并且还撞上了:
炸弹去死、潜艇去死;
判断若是分,则强转为得分接口,玩家得分;
判断若是命,则强转为得命接口,获取命数,战舰得命;
private int score = 0 ; //玩家得分
private void bombBangAction(){ //每10毫秒走一次
for(int i=0;i
-
画分和画命:
- 在Battleship中设计getLife()获取命数;
public int getLife(){
return life; //返回命数
} - 在World的paint()中:画分和画命;
public void paint(Graphics g){ //每10毫秒走一次
Images.sea.paintIcon(null,g,0,0); //画海洋图
ship.paintImage(g); //画战舰
for(int i=0;i
潜艇游戏第十天:
-
水雷与战舰的碰撞:
-
在Battleship中设计subtractLife()减命;
public void subtractLife(){
life--; //命数减1
} -
水雷与战舰碰撞为定时发生的,所以在run()中调用mineBangAction()实现水雷与战舰的碰撞;
在mineBangAction()中:
遍历所有水雷,得水雷,判断若都活着并且还撞上了:
水雷去死、战舰减命;
private void mineBangAction(){ //每10毫秒走一次
for(int i=0;i
-
检测游戏结束:
-
借用Battleship的getLife()获取命数;
-
检测游戏结束为定时发生的,所以在run()中调checkGameOverAction()检测游戏结束
在checkGameOverAction()中:
判断若战舰的命数<=0,表示游戏结束了;
private void checkGameOverAction(){ //每10毫秒走一次
if(ship.getLife()<=0){ //若战舰的命数<=0,表示游戏结束了
state = GAME_OVER; //则将当前状态修改为GAME_OVER游戏结束状态
}
}
-
画状态:
-
在World类中设计RUNNING、PAUSE、GAME_OVER状态常量,state变量表示当前状态;
-
在checkGameOverAction()中,若游戏结束, 则将当前状态修改为GAME_OVER游戏结束状态;
-
在paint()中设计:若为游戏结束状态则画游戏结束图;
-
设计run()中那一堆代码,为仅在运行状态下执行;
-
设计键盘抬起时,按下空格键、左移键、右移键,也为仅在运行状态下执行;
-
在键盘抬起时,设计若按的是P键,则运行状态变暂停状态,暂停状态变运行状态。
另外还实现一个飞机小游戏,过程相似。结果如图:
如有需要,可评论区留下邮箱,后会发送。
public class Battleship {
int width; //宽
int height; //高
int x; //x坐标
int y; //y坐标
int speed; //移动速度
int life; //命数
Battleship(){
width = 66;
height = 26;
x = 270;
y = 124;
speed = 20;
life = 5;
}
void move(){
System.out.println("战舰移动啦!");
}
} - 在World类中创建侦察潜艇数组、鱼雷潜艇数组、水雷潜艇数组,水雷数组,炸弹数组,并测试;
public class World { public static void main(String[] args) { ObserveSubmarine[] oses = new ObserveSubmarine[3]; oses[0] = new ObserveSubmarine(); oses[1] = new ObserveSubmarine(); oses[2] = new ObserveSubmarine(); for(int i=0;i - 设计SeaObject超类,6个类继承超类;(以战舰类为例)
public class Battleship extends SeaObject { int life; //命数 Battleship(){ super(66,26,270,124,20); life = 5; } } - 在SeaObject中设计两个构造方法,6个派生类分别调用;
import java.util.Random; public class SeaObject { int width; //宽 int height; //高 int x; //x坐标 int y; //y坐标 int speed; //移动速度 //因为三种潜艇的width/height的值都是不一样的,所以数据不能写死,需传参写活 //因为三种潜艇的x/y/speed的值都是一样的,所以数据可以写死,不需要传参 SeaObject(int width,int height){ this.width = width; this.height = height; x = -width; //负的潜艇的宽 Random rand = new Random(); //随机数对象 y = rand.nextInt(479-height-150+1)+150; //150到(窗口高-潜艇高)之间的随机数 speed = rand.nextInt(3)+1; //1到3之内的随机数 } //因为三种对象的width/height/x/y/speed都是不一样的,所以数据不能写死,需传参写活 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; } void move(){ System.out.println("海洋对象移动啦!"); } }
潜艇游戏第四天:
- 将侦察潜艇数组、鱼雷潜艇数组、水雷潜艇数组统一组合为SeaObject超类数组,在World类中改写并测试;
public class World extends JPanel { //2.
public static void main(String[] args) {
ObserveSubmarine o1 = new ObserveSubmarine();
System.out.println("侦察艇初始数据: x:"+o1.x+", y:"+o1.y+", speed:"+o1.speed);
o1.move();
System.out.println("侦察艇移动后数据: x:"+o1.x+", y:"+o1.y+", speed:"+o1.speed);
Mine o2 = new Mine(100,200);
System.out.println("水雷初始数据: x:"+o2.x+", y:"+o2.y+", speed:"+o2.speed);
o2.move();
System.out.println("水雷移动后数据: x:"+o2.x+", y:"+o2.y+", speed:"+o2.speed);
SeaObject[] submarines = new SeaObject[5]; //潜艇数组
submarines[0] = new ObserveSubmarine(); //向上造型
submarines[1] = new ObserveSubmarine();
submarines[2] = new TorpedoSubmarine();
submarines[3] = new TorpedoSubmarine();
submarines[4] = new MineSubmarine();
for(int i=0;i - 在6个类中重写move()移动,并测试;(以战舰类为例)
public class Battleship extends SeaObject {
int life; //命数
Battleship(){
super(66,26,270,124,20);
life = 5;
}
void move(){
//暂时搁置
}
} - 在World类中画窗口:
import javax.swing.JFrame;
import javax.swing.JPanel; //1.第一步
public class World extends JPanel { //2.第二步
public static void main(String[] args) {
JFrame frame = new JFrame(); //3.第三步
World world = new World();
world.setFocusable(true);
frame.add(world);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(641+16, 479+39);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
潜艇游戏第五天:
- 给类中成员添加访问控制修饰符;(以战舰类为例) 访问控制修饰符可看:(13条消息) Java学习日记Day11(面向对象day05)_终为尘埃的博客-CSDN博客
public class Battleship extends SeaObject {
private int life; //命数
public Battleship(){
super(66,26,270,124,20);
life = 5;
}
public void move(){
//暂时搁置
}
} - 设计Images图片类;
import javax.swing.ImageIcon;
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{ //初始化静态图片
//将img中的sea.png读取到静态变量sea中
sea = new ImageIcon("img/sea.png");
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());
}
}
潜艇游戏第六天:
-
在World类中设计窗口的宽和高为常量,适当地方做修改,重写getImage()方法(以战舰类为例);
import javax.swing.ImageIcon;
public class Battleship extends SeaObject {
private int life; //命数
public Battleship(){
super(66,26,270,124,20);
life = 5;
}
public void move(){
//暂时搁置
}
public ImageIcon getImage(){
return Images.battleship; //返回战舰图片
}
}
-
画对象:
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()画对象即可
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.*;
import java.util.Arrays;
public class World extends JPanel {
public static final int WIDTH = 641; //窗口的宽
public static final int HEIGHT = 479; //窗口的高
//如下这一堆对象就是窗口中你所看到的对象了
private Battleship ship = new Battleship(); //战舰对象
private SeaObject[] submarines = {
new ObserveSubmarine(),
new TorpedoSubmarine(),
new MineSubmarine()
}; //潜艇数组
private Mine[] mines = {
new Mine(280,300)
}; //水雷数组
private Bomb[] bombs = {
new Bomb(200,250)
}; //炸弹数组
public void paint(Graphics g){
Images.sea.paintIcon(null,g,0,0); //画海洋图
ship.paintImage(g); //画战舰
for(int i=0;i
潜艇游戏第七天:
-
潜艇入场:
-
潜艇是由窗口产生的,所以在窗口World类中设计nextSubmarine()生成潜艇对象;
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();
}
} -
潜艇入场为定时发生的,所以在run()中调用submarineEnterAction()实现潜艇入场
在submarineEnterAction()中:
每400毫秒,获取潜艇对象obj,submarines扩容,将obj添加到最后一个元素上 ;
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 void action(){
Timer timer = new Timer(); //定时器对象
int interval = 10; //定时间隔(以毫秒为单位)
timer.schedule(new TimerTask() {
public void run() { //定时干的事---每10毫秒自动调用
submarineEnterAction(); //潜艇(侦察潜艇、鱼雷潜艇、水雷入场)入场
repaint(); //重画---系统自动调用paint()方法
}
}, interval, interval); //定时日程表
}
注意:在run()中调用submarineEnterAction()之后,一定要调用repaint()来重画
-
水雷入场:
-
水雷是由水雷潜艇发射出来的,所以在MineSubmarine中设计shootMine()生成水雷对象
-
水雷入场为定时发生的,所以在run()中调用mineEnterAction()实现水雷入场
在mineEnterAction()中:
每1000毫秒,
private int mineEnterIndex = 0; //水雷入场计数
private void mineEnterAction(){ //每10毫秒走一次
mineEnterIndex++; //每10毫秒增1
if(mineEnterIndex%100==0) { //每1000毫秒走一次
//暂时搁置
}
}
-
海洋对象移动:
-
海洋对象移动为共有行为,所以在SeaObject中设计抽象方法move()实现移动,派生类中重写
-
海洋对象移动为定时发生的,所以在run()中调用moveAction()实现海洋对象移动
在moveAction()中:
遍历所有潜艇--潜艇动,遍历所有水雷--水雷动,遍历所有炸弹--炸弹动
private void moveAction(){ //每10毫秒走一次
for(int i=0;i
潜艇游戏第八天:
-
炸弹入场:
-
炸弹是由战舰发射出来的,所以在Battleship类中设计shootBomb()生成炸弹对象;
public Bomb shootBomb(){
return new Bomb(this.x,this.y); //炸弹的初始坐标就是战舰的坐标
} -
炸弹入场为按空格键事件触发的,所以在侦听器中重写keyReleased()按键抬起事件,在抬起事件中:
-
判断若抬起的是空格键,则:
获取炸弹对象obj,bombs扩容,将obj添加到bombs的最后一个元素上
-
战舰移动:
- 战舰移动为战舰的行为,所以在Battleship中设计moveLeft()左移、moveRight()右移
- 战舰移动为事件触发的,所以在侦听器的重写keyReleased()按键抬起事件中:
- 判断若抬起的是左箭头,则战舰左移
- 判断若抬起的是右箭头,则战舰右移
//键盘侦听器
KeyAdapter k = new KeyAdapter() {
public void keyReleased(KeyEvent e) { //当按键抬起时会自动触发
if(e.getKeyCode()==KeyEvent.VK_SPACE){ //若抬起的是空格键
Bomb obj = ship.shootBomb(); //获取炸弹对象
bombs = Arrays.copyOf(bombs,bombs.length+1); //扩容
bombs[bombs.length-1] = obj; //将obj添加到最后一个元素上
}
if(e.getKeyCode()==KeyEvent.VK_LEFT){ //若抬起的是左箭头
ship.moveLeft(); //战舰左移
}
if(e.getKeyCode()==KeyEvent.VK_RIGHT) { //若抬起的是右箭头
ship.moveRight(); //战舰右移
}
}
};
this.addKeyListener(k); //添加侦听
-
删除越界的海洋对象:
-
在SeaObject中设计isOutOfBounds()检测潜艇是否越界,在Bomb和Mine中重写isOutOfBounds()检测炸弹和水雷是否越界;
-
删除越界海洋对象为定时发生的,所以在run()中调用outOfBoundsAction()删除越界海洋对象
在outOfBoundsAction()中:
遍历所有潜艇/水雷/炸弹,判断若越界了:
将越界元素替换为数组的最后一个元素,缩容;
private void outOfBoundsAction(){ //每10毫秒走一次
for(int i=0;i
-
设计EnemyScore得分接口,ObserveSubmarine和TorpedoSubmarine实现得分接口;
public interface EnemyScore {
public int getScore();
} 设计EnemyLife得命接口,MineSubmarine实现得命接口;
public interface EnemyLife {
public int getLife();
}
潜艇游戏第九天:
-
水雷入场:--------------后半段
-
水雷是由水雷潜艇发射出来的,所以在MineSubmarine中设计shootMine()生成水雷对象;
public Mine shootMine(){
//水雷的x:水雷潜艇的x+水雷潜艇的width
//水雷的y:水雷潜艇的y-水雷的高(11)
return new Mine(this.x+this.width,this.y-11); //this指水雷潜艇对象
} -
水雷入场为定时发生的,所以在run()中调用mineEnterAction()实现水雷入场
在mineEnterAction()中:
每1000毫秒,遍历所有潜艇,判断若为水雷潜艇,则强转为水雷潜艇类型,
获取水雷对象obj,mines扩容,将obj装到最后一个元素上;
private int mineEnterIndex = 0; //水雷入场计数
private void mineEnterAction(){ //每10毫秒走一次
mineEnterIndex++; //每10毫秒增1
if(mineEnterIndex%100==0) { //每1000毫秒走一次
for(int i=0;i
-
炸弹与潜艇的碰撞:
-
在SeaObject中设计isHit()检测碰撞、goDead()去死;
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
return x>=x1 && x<=x2 && y>=y1 && y<=y2; //x在x1与x2之间,并且,y在y1与y2之间,即为撞上了
}
public void goDead(){
state = DEAD; //将当前状态修改为DEAD死了的
} -
在Battleship中设计addLife()增命;
public void addLife(int num){
life += num; //命数增num
} -
炸弹与潜艇的碰撞为定时发生的,所以在run()中设计bombBangAction()实现炸弹与潜艇碰撞
在bombBangAction()中:
遍历所有炸弹得炸弹,遍历所有潜艇得潜艇,判断若都活着并且还撞上了:
炸弹去死、潜艇去死;
判断若是分,则强转为得分接口,玩家得分;
判断若是命,则强转为得命接口,获取命数,战舰得命;
private int score = 0 ; //玩家得分
private void bombBangAction(){ //每10毫秒走一次
for(int i=0;i
-
画分和画命:
- 在Battleship中设计getLife()获取命数;
public int getLife(){
return life; //返回命数
} - 在World的paint()中:画分和画命;
public void paint(Graphics g){ //每10毫秒走一次
Images.sea.paintIcon(null,g,0,0); //画海洋图
ship.paintImage(g); //画战舰
for(int i=0;i
潜艇游戏第十天:
-
水雷与战舰的碰撞:
-
在Battleship中设计subtractLife()减命;
public void subtractLife(){
life--; //命数减1
} -
水雷与战舰碰撞为定时发生的,所以在run()中调用mineBangAction()实现水雷与战舰的碰撞;
在mineBangAction()中:
遍历所有水雷,得水雷,判断若都活着并且还撞上了:
水雷去死、战舰减命;
private void mineBangAction(){ //每10毫秒走一次
for(int i=0;i
-
检测游戏结束:
-
借用Battleship的getLife()获取命数;
-
检测游戏结束为定时发生的,所以在run()中调checkGameOverAction()检测游戏结束
在checkGameOverAction()中:
判断若战舰的命数<=0,表示游戏结束了;
private void checkGameOverAction(){ //每10毫秒走一次
if(ship.getLife()<=0){ //若战舰的命数<=0,表示游戏结束了
state = GAME_OVER; //则将当前状态修改为GAME_OVER游戏结束状态
}
}
-
画状态:
-
在World类中设计RUNNING、PAUSE、GAME_OVER状态常量,state变量表示当前状态;
-
在checkGameOverAction()中,若游戏结束, 则将当前状态修改为GAME_OVER游戏结束状态;
-
在paint()中设计:若为游戏结束状态则画游戏结束图;
-
设计run()中那一堆代码,为仅在运行状态下执行;
-
设计键盘抬起时,按下空格键、左移键、右移键,也为仅在运行状态下执行;
-
在键盘抬起时,设计若按的是P键,则运行状态变暂停状态,暂停状态变运行状态。
另外还实现一个飞机小游戏,过程相似。结果如图:
如有需要,可评论区留下邮箱,后会发送。
public class World extends JPanel { //2.
public static void main(String[] args) {
ObserveSubmarine o1 = new ObserveSubmarine();
System.out.println("侦察艇初始数据: x:"+o1.x+", y:"+o1.y+", speed:"+o1.speed);
o1.move();
System.out.println("侦察艇移动后数据: x:"+o1.x+", y:"+o1.y+", speed:"+o1.speed);
Mine o2 = new Mine(100,200);
System.out.println("水雷初始数据: x:"+o2.x+", y:"+o2.y+", speed:"+o2.speed);
o2.move();
System.out.println("水雷移动后数据: x:"+o2.x+", y:"+o2.y+", speed:"+o2.speed);
SeaObject[] submarines = new SeaObject[5]; //潜艇数组
submarines[0] = new ObserveSubmarine(); //向上造型
submarines[1] = new ObserveSubmarine();
submarines[2] = new TorpedoSubmarine();
submarines[3] = new TorpedoSubmarine();
submarines[4] = new MineSubmarine();
for(int i=0;i
public class Battleship extends SeaObject {
int life; //命数
Battleship(){
super(66,26,270,124,20);
life = 5;
}
void move(){
//暂时搁置
}
} import javax.swing.JFrame;
import javax.swing.JPanel; //1.第一步
public class World extends JPanel { //2.第二步
public static void main(String[] args) {
JFrame frame = new JFrame(); //3.第三步
World world = new World();
world.setFocusable(true);
frame.add(world);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(641+16, 479+39);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
- 给类中成员添加访问控制修饰符;(以战舰类为例) 访问控制修饰符可看:(13条消息) Java学习日记Day11(面向对象day05)_终为尘埃的博客-CSDN博客
public class Battleship extends SeaObject { private int life; //命数 public Battleship(){ super(66,26,270,124,20); life = 5; } public void move(){ //暂时搁置 } } - 设计Images图片类;
import javax.swing.ImageIcon; 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{ //初始化静态图片 //将img中的sea.png读取到静态变量sea中 sea = new ImageIcon("img/sea.png"); 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()); } }
潜艇游戏第六天:
-
在World类中设计窗口的宽和高为常量,适当地方做修改,重写getImage()方法(以战舰类为例);
import javax.swing.ImageIcon;
public class Battleship extends SeaObject {
private int life; //命数
public Battleship(){
super(66,26,270,124,20);
life = 5;
}
public void move(){
//暂时搁置
}
public ImageIcon getImage(){
return Images.battleship; //返回战舰图片
}
}
-
画对象:
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()画对象即可
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.*;
import java.util.Arrays;
public class World extends JPanel {
public static final int WIDTH = 641; //窗口的宽
public static final int HEIGHT = 479; //窗口的高
//如下这一堆对象就是窗口中你所看到的对象了
private Battleship ship = new Battleship(); //战舰对象
private SeaObject[] submarines = {
new ObserveSubmarine(),
new TorpedoSubmarine(),
new MineSubmarine()
}; //潜艇数组
private Mine[] mines = {
new Mine(280,300)
}; //水雷数组
private Bomb[] bombs = {
new Bomb(200,250)
}; //炸弹数组
public void paint(Graphics g){
Images.sea.paintIcon(null,g,0,0); //画海洋图
ship.paintImage(g); //画战舰
for(int i=0;i
潜艇游戏第七天:
-
潜艇入场:
-
潜艇是由窗口产生的,所以在窗口World类中设计nextSubmarine()生成潜艇对象;
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();
}
} -
潜艇入场为定时发生的,所以在run()中调用submarineEnterAction()实现潜艇入场
在submarineEnterAction()中:
每400毫秒,获取潜艇对象obj,submarines扩容,将obj添加到最后一个元素上 ;
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 void action(){
Timer timer = new Timer(); //定时器对象
int interval = 10; //定时间隔(以毫秒为单位)
timer.schedule(new TimerTask() {
public void run() { //定时干的事---每10毫秒自动调用
submarineEnterAction(); //潜艇(侦察潜艇、鱼雷潜艇、水雷入场)入场
repaint(); //重画---系统自动调用paint()方法
}
}, interval, interval); //定时日程表
}
注意:在run()中调用submarineEnterAction()之后,一定要调用repaint()来重画
-
水雷入场:
-
水雷是由水雷潜艇发射出来的,所以在MineSubmarine中设计shootMine()生成水雷对象
-
水雷入场为定时发生的,所以在run()中调用mineEnterAction()实现水雷入场
在mineEnterAction()中:
每1000毫秒,
private int mineEnterIndex = 0; //水雷入场计数
private void mineEnterAction(){ //每10毫秒走一次
mineEnterIndex++; //每10毫秒增1
if(mineEnterIndex%100==0) { //每1000毫秒走一次
//暂时搁置
}
}
-
海洋对象移动:
-
海洋对象移动为共有行为,所以在SeaObject中设计抽象方法move()实现移动,派生类中重写
-
海洋对象移动为定时发生的,所以在run()中调用moveAction()实现海洋对象移动
在moveAction()中:
遍历所有潜艇--潜艇动,遍历所有水雷--水雷动,遍历所有炸弹--炸弹动
private void moveAction(){ //每10毫秒走一次
for(int i=0;i
潜艇游戏第八天:
-
炸弹入场:
-
炸弹是由战舰发射出来的,所以在Battleship类中设计shootBomb()生成炸弹对象;
public Bomb shootBomb(){
return new Bomb(this.x,this.y); //炸弹的初始坐标就是战舰的坐标
} -
炸弹入场为按空格键事件触发的,所以在侦听器中重写keyReleased()按键抬起事件,在抬起事件中:
-
判断若抬起的是空格键,则:
获取炸弹对象obj,bombs扩容,将obj添加到bombs的最后一个元素上
-
战舰移动:
- 战舰移动为战舰的行为,所以在Battleship中设计moveLeft()左移、moveRight()右移
- 战舰移动为事件触发的,所以在侦听器的重写keyReleased()按键抬起事件中:
- 判断若抬起的是左箭头,则战舰左移
- 判断若抬起的是右箭头,则战舰右移
//键盘侦听器
KeyAdapter k = new KeyAdapter() {
public void keyReleased(KeyEvent e) { //当按键抬起时会自动触发
if(e.getKeyCode()==KeyEvent.VK_SPACE){ //若抬起的是空格键
Bomb obj = ship.shootBomb(); //获取炸弹对象
bombs = Arrays.copyOf(bombs,bombs.length+1); //扩容
bombs[bombs.length-1] = obj; //将obj添加到最后一个元素上
}
if(e.getKeyCode()==KeyEvent.VK_LEFT){ //若抬起的是左箭头
ship.moveLeft(); //战舰左移
}
if(e.getKeyCode()==KeyEvent.VK_RIGHT) { //若抬起的是右箭头
ship.moveRight(); //战舰右移
}
}
};
this.addKeyListener(k); //添加侦听
-
删除越界的海洋对象:
-
在SeaObject中设计isOutOfBounds()检测潜艇是否越界,在Bomb和Mine中重写isOutOfBounds()检测炸弹和水雷是否越界;
-
删除越界海洋对象为定时发生的,所以在run()中调用outOfBoundsAction()删除越界海洋对象
在outOfBoundsAction()中:
遍历所有潜艇/水雷/炸弹,判断若越界了:
将越界元素替换为数组的最后一个元素,缩容;
private void outOfBoundsAction(){ //每10毫秒走一次
for(int i=0;i
-
设计EnemyScore得分接口,ObserveSubmarine和TorpedoSubmarine实现得分接口;
public interface EnemyScore {
public int getScore();
} 设计EnemyLife得命接口,MineSubmarine实现得命接口;
public interface EnemyLife {
public int getLife();
}
潜艇游戏第九天:
-
水雷入场:--------------后半段
-
水雷是由水雷潜艇发射出来的,所以在MineSubmarine中设计shootMine()生成水雷对象;
public Mine shootMine(){
//水雷的x:水雷潜艇的x+水雷潜艇的width
//水雷的y:水雷潜艇的y-水雷的高(11)
return new Mine(this.x+this.width,this.y-11); //this指水雷潜艇对象
} -
水雷入场为定时发生的,所以在run()中调用mineEnterAction()实现水雷入场
在mineEnterAction()中:
每1000毫秒,遍历所有潜艇,判断若为水雷潜艇,则强转为水雷潜艇类型,
获取水雷对象obj,mines扩容,将obj装到最后一个元素上;
private int mineEnterIndex = 0; //水雷入场计数
private void mineEnterAction(){ //每10毫秒走一次
mineEnterIndex++; //每10毫秒增1
if(mineEnterIndex%100==0) { //每1000毫秒走一次
for(int i=0;i
-
炸弹与潜艇的碰撞:
-
在SeaObject中设计isHit()检测碰撞、goDead()去死;
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
return x>=x1 && x<=x2 && y>=y1 && y<=y2; //x在x1与x2之间,并且,y在y1与y2之间,即为撞上了
}
public void goDead(){
state = DEAD; //将当前状态修改为DEAD死了的
} -
在Battleship中设计addLife()增命;
public void addLife(int num){
life += num; //命数增num
} -
炸弹与潜艇的碰撞为定时发生的,所以在run()中设计bombBangAction()实现炸弹与潜艇碰撞
在bombBangAction()中:
遍历所有炸弹得炸弹,遍历所有潜艇得潜艇,判断若都活着并且还撞上了:
炸弹去死、潜艇去死;
判断若是分,则强转为得分接口,玩家得分;
判断若是命,则强转为得命接口,获取命数,战舰得命;
private int score = 0 ; //玩家得分
private void bombBangAction(){ //每10毫秒走一次
for(int i=0;i
-
画分和画命:
- 在Battleship中设计getLife()获取命数;
public int getLife(){
return life; //返回命数
} - 在World的paint()中:画分和画命;
public void paint(Graphics g){ //每10毫秒走一次
Images.sea.paintIcon(null,g,0,0); //画海洋图
ship.paintImage(g); //画战舰
for(int i=0;i
潜艇游戏第十天:
-
水雷与战舰的碰撞:
-
在Battleship中设计subtractLife()减命;
public void subtractLife(){
life--; //命数减1
} -
水雷与战舰碰撞为定时发生的,所以在run()中调用mineBangAction()实现水雷与战舰的碰撞;
在mineBangAction()中:
遍历所有水雷,得水雷,判断若都活着并且还撞上了:
水雷去死、战舰减命;
private void mineBangAction(){ //每10毫秒走一次
for(int i=0;i
-
检测游戏结束:
-
借用Battleship的getLife()获取命数;
-
检测游戏结束为定时发生的,所以在run()中调checkGameOverAction()检测游戏结束
在checkGameOverAction()中:
判断若战舰的命数<=0,表示游戏结束了;
private void checkGameOverAction(){ //每10毫秒走一次
if(ship.getLife()<=0){ //若战舰的命数<=0,表示游戏结束了
state = GAME_OVER; //则将当前状态修改为GAME_OVER游戏结束状态
}
}
-
画状态:
-
在World类中设计RUNNING、PAUSE、GAME_OVER状态常量,state变量表示当前状态;
-
在checkGameOverAction()中,若游戏结束, 则将当前状态修改为GAME_OVER游戏结束状态;
-
在paint()中设计:若为游戏结束状态则画游戏结束图;
-
设计run()中那一堆代码,为仅在运行状态下执行;
-
设计键盘抬起时,按下空格键、左移键、右移键,也为仅在运行状态下执行;
-
在键盘抬起时,设计若按的是P键,则运行状态变暂停状态,暂停状态变运行状态。
另外还实现一个飞机小游戏,过程相似。结果如图:
如有需要,可评论区留下邮箱,后会发送。
在World类中设计窗口的宽和高为常量,适当地方做修改,重写getImage()方法(以战舰类为例);
import javax.swing.ImageIcon;
public class Battleship extends SeaObject {
private int life; //命数
public Battleship(){
super(66,26,270,124,20);
life = 5;
}
public void move(){
//暂时搁置
}
public ImageIcon getImage(){
return Images.battleship; //返回战舰图片
}
}
画对象:
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()画对象即可
潜艇游戏第七天:
-
潜艇入场:
-
潜艇是由窗口产生的,所以在窗口World类中设计nextSubmarine()生成潜艇对象;
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();
}
} -
潜艇入场为定时发生的,所以在run()中调用submarineEnterAction()实现潜艇入场
在submarineEnterAction()中:
每400毫秒,获取潜艇对象obj,submarines扩容,将obj添加到最后一个元素上 ;
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 void action(){
Timer timer = new Timer(); //定时器对象
int interval = 10; //定时间隔(以毫秒为单位)
timer.schedule(new TimerTask() {
public void run() { //定时干的事---每10毫秒自动调用
submarineEnterAction(); //潜艇(侦察潜艇、鱼雷潜艇、水雷入场)入场
repaint(); //重画---系统自动调用paint()方法
}
}, interval, interval); //定时日程表
}
注意:在run()中调用submarineEnterAction()之后,一定要调用repaint()来重画
-
水雷入场:
-
水雷是由水雷潜艇发射出来的,所以在MineSubmarine中设计shootMine()生成水雷对象
-
水雷入场为定时发生的,所以在run()中调用mineEnterAction()实现水雷入场
在mineEnterAction()中:
每1000毫秒,
private int mineEnterIndex = 0; //水雷入场计数
private void mineEnterAction(){ //每10毫秒走一次
mineEnterIndex++; //每10毫秒增1
if(mineEnterIndex%100==0) { //每1000毫秒走一次
//暂时搁置
}
}
-
海洋对象移动:
-
海洋对象移动为共有行为,所以在SeaObject中设计抽象方法move()实现移动,派生类中重写
-
海洋对象移动为定时发生的,所以在run()中调用moveAction()实现海洋对象移动
在moveAction()中:
遍历所有潜艇--潜艇动,遍历所有水雷--水雷动,遍历所有炸弹--炸弹动
private void moveAction(){ //每10毫秒走一次
for(int i=0;i
潜艇游戏第八天:
-
炸弹入场:
-
炸弹是由战舰发射出来的,所以在Battleship类中设计shootBomb()生成炸弹对象;
public Bomb shootBomb(){
return new Bomb(this.x,this.y); //炸弹的初始坐标就是战舰的坐标
} -
炸弹入场为按空格键事件触发的,所以在侦听器中重写keyReleased()按键抬起事件,在抬起事件中:
-
判断若抬起的是空格键,则:
获取炸弹对象obj,bombs扩容,将obj添加到bombs的最后一个元素上
-
战舰移动:
- 战舰移动为战舰的行为,所以在Battleship中设计moveLeft()左移、moveRight()右移
- 战舰移动为事件触发的,所以在侦听器的重写keyReleased()按键抬起事件中:
- 判断若抬起的是左箭头,则战舰左移
- 判断若抬起的是右箭头,则战舰右移
//键盘侦听器
KeyAdapter k = new KeyAdapter() {
public void keyReleased(KeyEvent e) { //当按键抬起时会自动触发
if(e.getKeyCode()==KeyEvent.VK_SPACE){ //若抬起的是空格键
Bomb obj = ship.shootBomb(); //获取炸弹对象
bombs = Arrays.copyOf(bombs,bombs.length+1); //扩容
bombs[bombs.length-1] = obj; //将obj添加到最后一个元素上
}
if(e.getKeyCode()==KeyEvent.VK_LEFT){ //若抬起的是左箭头
ship.moveLeft(); //战舰左移
}
if(e.getKeyCode()==KeyEvent.VK_RIGHT) { //若抬起的是右箭头
ship.moveRight(); //战舰右移
}
}
};
this.addKeyListener(k); //添加侦听
-
删除越界的海洋对象:
-
在SeaObject中设计isOutOfBounds()检测潜艇是否越界,在Bomb和Mine中重写isOutOfBounds()检测炸弹和水雷是否越界;
-
删除越界海洋对象为定时发生的,所以在run()中调用outOfBoundsAction()删除越界海洋对象
在outOfBoundsAction()中:
遍历所有潜艇/水雷/炸弹,判断若越界了:
将越界元素替换为数组的最后一个元素,缩容;
private void outOfBoundsAction(){ //每10毫秒走一次
for(int i=0;i
-
设计EnemyScore得分接口,ObserveSubmarine和TorpedoSubmarine实现得分接口;
public interface EnemyScore {
public int getScore();
} 设计EnemyLife得命接口,MineSubmarine实现得命接口;
public interface EnemyLife {
public int getLife();
}
潜艇游戏第九天:
-
水雷入场:--------------后半段
-
水雷是由水雷潜艇发射出来的,所以在MineSubmarine中设计shootMine()生成水雷对象;
public Mine shootMine(){
//水雷的x:水雷潜艇的x+水雷潜艇的width
//水雷的y:水雷潜艇的y-水雷的高(11)
return new Mine(this.x+this.width,this.y-11); //this指水雷潜艇对象
} -
水雷入场为定时发生的,所以在run()中调用mineEnterAction()实现水雷入场
在mineEnterAction()中:
每1000毫秒,遍历所有潜艇,判断若为水雷潜艇,则强转为水雷潜艇类型,
获取水雷对象obj,mines扩容,将obj装到最后一个元素上;
private int mineEnterIndex = 0; //水雷入场计数
private void mineEnterAction(){ //每10毫秒走一次
mineEnterIndex++; //每10毫秒增1
if(mineEnterIndex%100==0) { //每1000毫秒走一次
for(int i=0;i
-
炸弹与潜艇的碰撞:
-
在SeaObject中设计isHit()检测碰撞、goDead()去死;
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
return x>=x1 && x<=x2 && y>=y1 && y<=y2; //x在x1与x2之间,并且,y在y1与y2之间,即为撞上了
}
public void goDead(){
state = DEAD; //将当前状态修改为DEAD死了的
} -
在Battleship中设计addLife()增命;
public void addLife(int num){
life += num; //命数增num
} -
炸弹与潜艇的碰撞为定时发生的,所以在run()中设计bombBangAction()实现炸弹与潜艇碰撞
在bombBangAction()中:
遍历所有炸弹得炸弹,遍历所有潜艇得潜艇,判断若都活着并且还撞上了:
炸弹去死、潜艇去死;
判断若是分,则强转为得分接口,玩家得分;
判断若是命,则强转为得命接口,获取命数,战舰得命;
private int score = 0 ; //玩家得分
private void bombBangAction(){ //每10毫秒走一次
for(int i=0;i
-
画分和画命:
- 在Battleship中设计getLife()获取命数;
public int getLife(){
return life; //返回命数
} - 在World的paint()中:画分和画命;
public void paint(Graphics g){ //每10毫秒走一次
Images.sea.paintIcon(null,g,0,0); //画海洋图
ship.paintImage(g); //画战舰
for(int i=0;i
潜艇游戏第十天:
-
水雷与战舰的碰撞:
-
在Battleship中设计subtractLife()减命;
public void subtractLife(){
life--; //命数减1
} -
水雷与战舰碰撞为定时发生的,所以在run()中调用mineBangAction()实现水雷与战舰的碰撞;
在mineBangAction()中:
遍历所有水雷,得水雷,判断若都活着并且还撞上了:
水雷去死、战舰减命;
private void mineBangAction(){ //每10毫秒走一次
for(int i=0;i
-
检测游戏结束:
-
借用Battleship的getLife()获取命数;
-
检测游戏结束为定时发生的,所以在run()中调checkGameOverAction()检测游戏结束
在checkGameOverAction()中:
判断若战舰的命数<=0,表示游戏结束了;
private void checkGameOverAction(){ //每10毫秒走一次
if(ship.getLife()<=0){ //若战舰的命数<=0,表示游戏结束了
state = GAME_OVER; //则将当前状态修改为GAME_OVER游戏结束状态
}
}
-
画状态:
-
在World类中设计RUNNING、PAUSE、GAME_OVER状态常量,state变量表示当前状态;
-
在checkGameOverAction()中,若游戏结束, 则将当前状态修改为GAME_OVER游戏结束状态;
-
在paint()中设计:若为游戏结束状态则画游戏结束图;
-
设计run()中那一堆代码,为仅在运行状态下执行;
-
设计键盘抬起时,按下空格键、左移键、右移键,也为仅在运行状态下执行;
-
在键盘抬起时,设计若按的是P键,则运行状态变暂停状态,暂停状态变运行状态。
另外还实现一个飞机小游戏,过程相似。结果如图:
如有需要,可评论区留下邮箱,后会发送。
潜艇入场:
-
潜艇是由窗口产生的,所以在窗口World类中设计nextSubmarine()生成潜艇对象;
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(); } } -
潜艇入场为定时发生的,所以在run()中调用submarineEnterAction()实现潜艇入场
在submarineEnterAction()中:
每400毫秒,获取潜艇对象obj,submarines扩容,将obj添加到最后一个元素上 ;
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 void action(){ Timer timer = new Timer(); //定时器对象 int interval = 10; //定时间隔(以毫秒为单位) timer.schedule(new TimerTask() { public void run() { //定时干的事---每10毫秒自动调用 submarineEnterAction(); //潜艇(侦察潜艇、鱼雷潜艇、水雷入场)入场 repaint(); //重画---系统自动调用paint()方法 } }, interval, interval); //定时日程表 }注意:在run()中调用submarineEnterAction()之后,一定要调用repaint()来重画
水雷入场:
-
水雷是由水雷潜艇发射出来的,所以在MineSubmarine中设计shootMine()生成水雷对象
-
水雷入场为定时发生的,所以在run()中调用mineEnterAction()实现水雷入场
在mineEnterAction()中:
每1000毫秒,
private int mineEnterIndex = 0; //水雷入场计数 private void mineEnterAction(){ //每10毫秒走一次 mineEnterIndex++; //每10毫秒增1 if(mineEnterIndex%100==0) { //每1000毫秒走一次 //暂时搁置 } }
海洋对象移动:
-
海洋对象移动为共有行为,所以在SeaObject中设计抽象方法move()实现移动,派生类中重写
-
海洋对象移动为定时发生的,所以在run()中调用moveAction()实现海洋对象移动
在moveAction()中:
遍历所有潜艇--潜艇动,遍历所有水雷--水雷动,遍历所有炸弹--炸弹动
private void moveAction(){ //每10毫秒走一次 for(int i=0;i
-
炸弹入场:
-
炸弹是由战舰发射出来的,所以在Battleship类中设计shootBomb()生成炸弹对象;
public Bomb shootBomb(){ return new Bomb(this.x,this.y); //炸弹的初始坐标就是战舰的坐标 } -
炸弹入场为按空格键事件触发的,所以在侦听器中重写keyReleased()按键抬起事件,在抬起事件中:
-
判断若抬起的是空格键,则:
获取炸弹对象obj,bombs扩容,将obj添加到bombs的最后一个元素上
-
-
-
战舰移动:
- 战舰移动为战舰的行为,所以在Battleship中设计moveLeft()左移、moveRight()右移
- 战舰移动为事件触发的,所以在侦听器的重写keyReleased()按键抬起事件中:
- 判断若抬起的是左箭头,则战舰左移
- 判断若抬起的是右箭头,则战舰右移
//键盘侦听器 KeyAdapter k = new KeyAdapter() { public void keyReleased(KeyEvent e) { //当按键抬起时会自动触发 if(e.getKeyCode()==KeyEvent.VK_SPACE){ //若抬起的是空格键 Bomb obj = ship.shootBomb(); //获取炸弹对象 bombs = Arrays.copyOf(bombs,bombs.length+1); //扩容 bombs[bombs.length-1] = obj; //将obj添加到最后一个元素上 } if(e.getKeyCode()==KeyEvent.VK_LEFT){ //若抬起的是左箭头 ship.moveLeft(); //战舰左移 } if(e.getKeyCode()==KeyEvent.VK_RIGHT) { //若抬起的是右箭头 ship.moveRight(); //战舰右移 } } }; this.addKeyListener(k); //添加侦听
-
删除越界的海洋对象:
-
在SeaObject中设计isOutOfBounds()检测潜艇是否越界,在Bomb和Mine中重写isOutOfBounds()检测炸弹和水雷是否越界;
-
删除越界海洋对象为定时发生的,所以在run()中调用outOfBoundsAction()删除越界海洋对象
在outOfBoundsAction()中:
遍历所有潜艇/水雷/炸弹,判断若越界了:
将越界元素替换为数组的最后一个元素,缩容;
private void outOfBoundsAction(){ //每10毫秒走一次 for(int i=0;i
-
-
设计EnemyScore得分接口,ObserveSubmarine和TorpedoSubmarine实现得分接口;
public interface EnemyScore { public int getScore(); }设计EnemyLife得命接口,MineSubmarine实现得命接口;
public interface EnemyLife { public int getLife(); }
潜艇游戏第九天:
-
水雷入场:--------------后半段
-
水雷是由水雷潜艇发射出来的,所以在MineSubmarine中设计shootMine()生成水雷对象;
public Mine shootMine(){
//水雷的x:水雷潜艇的x+水雷潜艇的width
//水雷的y:水雷潜艇的y-水雷的高(11)
return new Mine(this.x+this.width,this.y-11); //this指水雷潜艇对象
} -
水雷入场为定时发生的,所以在run()中调用mineEnterAction()实现水雷入场
在mineEnterAction()中:
每1000毫秒,遍历所有潜艇,判断若为水雷潜艇,则强转为水雷潜艇类型,
获取水雷对象obj,mines扩容,将obj装到最后一个元素上;
private int mineEnterIndex = 0; //水雷入场计数
private void mineEnterAction(){ //每10毫秒走一次
mineEnterIndex++; //每10毫秒增1
if(mineEnterIndex%100==0) { //每1000毫秒走一次
for(int i=0;i
-
炸弹与潜艇的碰撞:
-
在SeaObject中设计isHit()检测碰撞、goDead()去死;
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
return x>=x1 && x<=x2 && y>=y1 && y<=y2; //x在x1与x2之间,并且,y在y1与y2之间,即为撞上了
}
public void goDead(){
state = DEAD; //将当前状态修改为DEAD死了的
} -
在Battleship中设计addLife()增命;
public void addLife(int num){
life += num; //命数增num
} -
炸弹与潜艇的碰撞为定时发生的,所以在run()中设计bombBangAction()实现炸弹与潜艇碰撞
在bombBangAction()中:
遍历所有炸弹得炸弹,遍历所有潜艇得潜艇,判断若都活着并且还撞上了:
炸弹去死、潜艇去死;
判断若是分,则强转为得分接口,玩家得分;
判断若是命,则强转为得命接口,获取命数,战舰得命;
private int score = 0 ; //玩家得分
private void bombBangAction(){ //每10毫秒走一次
for(int i=0;i
-
画分和画命:
- 在Battleship中设计getLife()获取命数;
public int getLife(){
return life; //返回命数
} - 在World的paint()中:画分和画命;
public void paint(Graphics g){ //每10毫秒走一次
Images.sea.paintIcon(null,g,0,0); //画海洋图
ship.paintImage(g); //画战舰
for(int i=0;i
潜艇游戏第十天:
-
水雷与战舰的碰撞:
-
在Battleship中设计subtractLife()减命;
public void subtractLife(){
life--; //命数减1
} -
水雷与战舰碰撞为定时发生的,所以在run()中调用mineBangAction()实现水雷与战舰的碰撞;
在mineBangAction()中:
遍历所有水雷,得水雷,判断若都活着并且还撞上了:
水雷去死、战舰减命;
private void mineBangAction(){ //每10毫秒走一次
for(int i=0;i
-
检测游戏结束:
-
借用Battleship的getLife()获取命数;
-
检测游戏结束为定时发生的,所以在run()中调checkGameOverAction()检测游戏结束
在checkGameOverAction()中:
判断若战舰的命数<=0,表示游戏结束了;
private void checkGameOverAction(){ //每10毫秒走一次
if(ship.getLife()<=0){ //若战舰的命数<=0,表示游戏结束了
state = GAME_OVER; //则将当前状态修改为GAME_OVER游戏结束状态
}
}
-
画状态:
-
在World类中设计RUNNING、PAUSE、GAME_OVER状态常量,state变量表示当前状态;
-
在checkGameOverAction()中,若游戏结束, 则将当前状态修改为GAME_OVER游戏结束状态;
-
在paint()中设计:若为游戏结束状态则画游戏结束图;
-
设计run()中那一堆代码,为仅在运行状态下执行;
-
设计键盘抬起时,按下空格键、左移键、右移键,也为仅在运行状态下执行;
-
在键盘抬起时,设计若按的是P键,则运行状态变暂停状态,暂停状态变运行状态。
另外还实现一个飞机小游戏,过程相似。结果如图:
如有需要,可评论区留下邮箱,后会发送。
水雷入场:--------------后半段
-
水雷是由水雷潜艇发射出来的,所以在MineSubmarine中设计shootMine()生成水雷对象;
public Mine shootMine(){ //水雷的x:水雷潜艇的x+水雷潜艇的width //水雷的y:水雷潜艇的y-水雷的高(11) return new Mine(this.x+this.width,this.y-11); //this指水雷潜艇对象 } -
水雷入场为定时发生的,所以在run()中调用mineEnterAction()实现水雷入场
在mineEnterAction()中:
每1000毫秒,遍历所有潜艇,判断若为水雷潜艇,则强转为水雷潜艇类型,
获取水雷对象obj,mines扩容,将obj装到最后一个元素上;
private int mineEnterIndex = 0; //水雷入场计数 private void mineEnterAction(){ //每10毫秒走一次 mineEnterIndex++; //每10毫秒增1 if(mineEnterIndex%100==0) { //每1000毫秒走一次 for(int i=0;i
炸弹与潜艇的碰撞:
-
在SeaObject中设计isHit()检测碰撞、goDead()去死;
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 return x>=x1 && x<=x2 && y>=y1 && y<=y2; //x在x1与x2之间,并且,y在y1与y2之间,即为撞上了 } public void goDead(){ state = DEAD; //将当前状态修改为DEAD死了的 } -
在Battleship中设计addLife()增命;
public void addLife(int num){ life += num; //命数增num } -
炸弹与潜艇的碰撞为定时发生的,所以在run()中设计bombBangAction()实现炸弹与潜艇碰撞
在bombBangAction()中:
遍历所有炸弹得炸弹,遍历所有潜艇得潜艇,判断若都活着并且还撞上了:
炸弹去死、潜艇去死;
判断若是分,则强转为得分接口,玩家得分;
判断若是命,则强转为得命接口,获取命数,战舰得命;
private int score = 0 ; //玩家得分 private void bombBangAction(){ //每10毫秒走一次 for(int i=0;i
画分和画命:
- 在Battleship中设计getLife()获取命数;
public int getLife(){ return life; //返回命数 } - 在World的paint()中:画分和画命;
public void paint(Graphics g){ //每10毫秒走一次 Images.sea.paintIcon(null,g,0,0); //画海洋图 ship.paintImage(g); //画战舰 for(int i=0;i
-
水雷与战舰的碰撞:
-
在Battleship中设计subtractLife()减命;
public void subtractLife(){ life--; //命数减1 } -
水雷与战舰碰撞为定时发生的,所以在run()中调用mineBangAction()实现水雷与战舰的碰撞;
在mineBangAction()中:
遍历所有水雷,得水雷,判断若都活着并且还撞上了:
水雷去死、战舰减命;
private void mineBangAction(){ //每10毫秒走一次 for(int i=0;i
-
-
检测游戏结束:
-
借用Battleship的getLife()获取命数;
-
检测游戏结束为定时发生的,所以在run()中调checkGameOverAction()检测游戏结束
在checkGameOverAction()中:
判断若战舰的命数<=0,表示游戏结束了;
private void checkGameOverAction(){ //每10毫秒走一次 if(ship.getLife()<=0){ //若战舰的命数<=0,表示游戏结束了 state = GAME_OVER; //则将当前状态修改为GAME_OVER游戏结束状态 } }
-
-
画状态:
-
在World类中设计RUNNING、PAUSE、GAME_OVER状态常量,state变量表示当前状态;
-
在checkGameOverAction()中,若游戏结束, 则将当前状态修改为GAME_OVER游戏结束状态;
-
在paint()中设计:若为游戏结束状态则画游戏结束图;
-
设计run()中那一堆代码,为仅在运行状态下执行;
-
设计键盘抬起时,按下空格键、左移键、右移键,也为仅在运行状态下执行;
-
在键盘抬起时,设计若按的是P键,则运行状态变暂停状态,暂停状态变运行状态。
另外还实现一个飞机小游戏,过程相似。结果如图:
如有需要,可评论区留下邮箱,后会发送。
-



