(一)效果展示
这里因为作者提前做好了前面的部分,左上角的得分暂且忽略
(二)代码实现
1.添加敌机
老套路,先加载敌机的造型
Utils.java
import java.awt.*;
public class Utils {
public static Image BgSky = Toolkit.getDefaultToolkit().getImage("src/Images/Bg_sky.png");
public static Image[] Plane = {
Toolkit.getDefaultToolkit().getImage("src/Images/Plane/1.png"),
Toolkit.getDefaultToolkit().getImage("src/Images/Plane/2.png"),
Toolkit.getDefaultToolkit().getImage("src/Images/Plane/3.png"),
};
public static Image[] Bullet = {
Toolkit.getDefaultToolkit().getImage("src/Images/Bullet/1.png"),
Toolkit.getDefaultToolkit().getImage("src/Images/Bullet/2.png")
};
public static Image[] Enamy = {
Toolkit.getDefaultToolkit().getImage("src/Images/Enamy/1.png"),
Toolkit.getDefaultToolkit().getImage("src/Images/Enamy/2.png"),
Toolkit.getDefaultToolkit().getImage("src/Images/Enamy/3.png")
};
}
然后还记得上篇文章我们创建的Plan
类吗?创建Enamy
类继承它
跟子弹同样的道理,直接加上移动功能
这里为了不让敌机一排一排像板刷一样的出现,我们加上一个随机小范围调整速度让机群错落有致
Enamy.java
import java.awt.*;
import java.util.List;
public class Enamy extends Plane{
int hp;
/*
boss 飞机是横向移动的 所以区分 x,y speed 更加方便
*/
int speedx,speedy;
/*
跟子弹同理
*/
boolean delete = false;
Enamy(int x,int y,int level) {
this.rect = new Rectangle(x,y,64,64);
this.img = Utils.Enamy[level];
this.state = level + 10; // 标记是敌机的某种型号 + 10
if (level == 0 || level == 1) {
this.hp = (level + 1) * 2; // 根据等级确定血量
this.speedy = (level + 1) * (3 + (int)Math.round(Math.random() * (1 - 0)) + 0);
this.speedx = 0;
} else {
this.lstFire = -15;
this.hp = 30;
this.rect.setLocation(x,30);
this.speedy = 0;
this.speedx = 5;
}
}
void paintSelf(Graphics g) {
if (this.rect.y >= 700 || this.hp <= 0) this.delete = true;
else {
if (this.rect.x <= 20 || this.rect.x >= 450) this.speedx *= -1;
this.rect.translate(this.speedx,this.speedy);
}
super.paintSelf(g);
}
}
后来发现hp
是Enamy
和Player
共有的应该放进Plane
中大家可以自行修改一下。
敌机移动的代码就实现了,接下来还要实例化它,也就是基因有了,还要把他生出来。
GameWin.java
在游戏主循环中生成敌机
这里 小怪 经常出现,boss 极少出现且间隔超过15秒,所以我们给游戏加上一条时间线(实际上就是一个计数变量TimeStamp
)
另外用Math.random()
控制敌机种类的出现概率
import java.awt.*;
import javax.swing.*;
import javax.swing.event.MouseInputAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
public class GameWin extends JFrame {
int width = 500;
int height = 700;
Image offScreenImage;
Bg bg = new Bg();
Player player = new Player(this);
List<Bullet> bullets = new ArrayList<>();
// 同子弹
List<Enamy> enamies = new ArrayList<>();
int TimeStamp = 0,lstBoss = -100,Bossnum = 0,lstFire = -15;
void launch() {
this.setVisible(true);
this.setSize(width,height);
this.setLocationRelativeTo(null);
this.setTitle("飞机大战");
setDefaultCloseOperation(EXIT_ON_CLOSE);
this.addKeyListener(new MyKeyListener());
this.addMouseListener(new MouseInputAdapter() {
@Override
public void mouseClicked(java.awt.event.MouseEvent e) {
// TODO Auto-generated method stub
if (GameState == 0) {
GameState = 1;
}
super.mouseClicked(e);
}
});;
while (true) {
TimeStamp ++; // 时间线
// 个人习惯 用 {} 包裹一个整体 增强模块化逻辑
{
int num;
if (enamies.size() < 10 && enamies.size() >= 3) {
num = (int)(Math.random() * ((10 - enamies.size()) - 1)) + 1;
} else if(enamies.size() < 3) {
num = (int)(Math.random() * ((10 - enamies.size()) - 3)) + 3;
} else {
num = 0;
}
for(int i = 1;i <= num;i ++) {
double rd = Math.random();
int level;
if (rd <= 0.7) level = 0;
else if(rd <= 0.95 || (TimeStamp - lstBoss) <= 1500 || Bossnum != 0) level = 1;
else {
level = 2;
lstBoss = TimeStamp;
Bossnum ++;
}
this.enamies.add(new Enamy((int)(Math.random() * (450 - 20)) + 20,(int)(Math.random() * (25 - 0)) + 0,level));
}
}
repaint();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class MyKeyListener implements KeyListener {
@Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
if (e.getKeyCode() == KeyEvent.VK_LEFT) {
player.movedir = -1;
} else if(e.getKeyCode() == KeyEvent.VK_RIGHT) {
player.movedir = 1;
} else if(e.getKeyCode() == KeyEvent.VK_SPACE) {
player.Fire();
}
}
@Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
@Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
if (e.getKeyCode() == KeyEvent.VK_LEFT) player.movedir = 0;
else if(e.getKeyCode() == KeyEvent.VK_RIGHT) player.movedir = 0;
}
}
@Override
public void paint(Graphics g) {
// TODO Auto-generated method stub
offScreenImage = this.createImage(width,height);
Graphics gImage = offScreenImage.getGraphics();
bg.paintSelf(gImage);
player.paintSelf(gImage);
for(Iterator<Bullet> it = bullets.iterator();it.hasNext();) {
Bullet bl = it.next();
if (bl.delete) it.remove();
else bl.paintSelf(gImage);
}
for(Iterator<Enamy> it = enamies.iterator();it.hasNext();) {
Enamy en = it.next();
if (en.delete) {
if (en.state == 12) Bossnum --; // 控制最多有一个boss出现
it.remove();
}
else en.paintSelf(gImage);
}
gImage.setFont(new Font("黑体",Font.BOLD,25));
gImage.setColor(Color.red);
g.drawImage(offScreenImage,0,0,null);
}
public static void main(String[] args) throws Exception {
GameWin gameWin = new GameWin();
gameWin.launch();
}
}
敌机的问题就解决了
2.Boss升级
稍等,Boss还没有发射子弹,问题还没有结束
类似Player
设置Fire,但是我们还需要将GameWin
直接传参进去吗?
个人理解,这样做法不够好,占用更多的空间, 那么我们需要的其实只是bullets
列表
这里炮弹的形态,以及等级可以自选
void Fire(List<Bullet> bl) {
bl.add(new Bullet(rect.x,rect.y,1, 1));
bl.add(new Bullet(rect.x + rect.width - 5,rect.y,1, 1));
}
接下来就是发射了,许多小伙伴可能觉得按空格键和方向键不太方便,于是和Boss的子弹发射一起,我们做一个AutoFire
按E键启动
class MyKeyListener implements KeyListener {
@Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
if (e.getKeyCode() == KeyEvent.VK_LEFT) {
player.movedir = -1;
} else if(e.getKeyCode() == KeyEvent.VK_RIGHT) {
player.movedir = 1;
} else if(e.getKeyCode() == KeyEvent.VK_SPACE) {
player.Fire();
} else if(e.getKeyCode() == KeyEvent.VK_E) {
autofire = !autofire; // 设置自动开火模式
}
}
@Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
@Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
if (e.getKeyCode() == KeyEvent.VK_LEFT) player.movedir = 0;
else if(e.getKeyCode() == KeyEvent.VK_RIGHT) player.movedir = 0;
}
}
然后在主循环中按TimeStamp
与上一次lstFire
的间隔频率发射,添加
{
if (this.autofire && this.TimeStamp - this.lstFire >= 15){ // 150ms 一发
this.lstFire = this.TimeStamp;
player.Fire();
}
for(Enamy en: enamies) {
if (en.state == 12 && this.TimeStamp - en.lstFire >= 15){
en.lstFire = this.TimeStamp;
en.Fire(bullets);
}
}
}
运行,发现Boss可以开火了,今天的任务也就完成了
- 实现了添加敌机飞机
- 实现了不同种类的敌机错落有致
- 实现了Boss子弹发射
- 实现了游戏时间线
- 子弹碰撞检测 (由于结构变化,下期更新)
下期预告
子弹碰撞检测
游戏得分统计与玩家升级
游戏状态(输赢)
最后 撰写不易,做人,一定要善良; 点赞,好人,有好福
关注我,收藏 点赞 评论文章 更新速度更快哦