本章目的
- 解决坦克出界问题
- 让坦克发射多发炮弹问题
一、解决坦克出界问题
我们发现现在当前版本,如果控制这个坦克一直往左或者往右的话是会移出边界的
那么怎么解决这个问题呢?其实与子弹有异曲同工之处
public class Tank {
void move() {
//省略其他关键性代码.......
if (x < 0){ x = 0;}//若坦克x小于0则等于0
//小于30 是因为要考虑标题的宽度
if (y < 30){ y = 30;}//若坦克y小于30则等于30
//当坦克的x坐标加上坦克的宽度大于游戏窗口的宽度
if (x + Tank.WIDTH > TankClient.GAME_WINDTH ) {
x = TankClient.GAME_WINDTH - Tank.WIDTH;
}
//当坦克的y坐标加上坦克的高度大于游戏窗口的高度
if (y + Tank.HEIGHT > TankClient.GAME_HEIGHT ) {
y = TankClient.GAME_HEIGHT - Tank.HEIGHT;
}
}
//省略其他关键性代码.......
}
这时我们的坦克则无法再跑到外面了
但是我们发现当前这个版本只有我们一个坦克,那么我们能不能加点坦克进来玩呢?
二、添加机器坦克
一般我们玩坦克大战都会有一个阵营的标识符区分是好是坏的坦克
public class Tank {
//区分阵营好坏
private boolean camp = false;
public Tank(int x, int y, TankClient tc, boolean camp) {
this.x = x;
this.y = y;
this.tc = tc;
this.camp = camp;
}
//省略其他关键性代码.......
}
这时我们修改一下方法,并且创建多一个坦克
public class TankClient extends Frame {
//阵营:好的坦克
Tank mytank = new Tank(50,50,this,true);
//阵营:坏的坦克
Tank enemytank = new Tank(100,100,this,false);
//省略其他关键性代码.......
@Override
public void paint(Graphics g) {
//省略其他关键性代码.......
//画出 好的 坦克
mytank.draw(g);
//画出 坏的 坦克
enemytank.draw(g);
}
}
这时机器坦克就出来了,如果你觉得不好区分颜色,可以在绘制方法里区分
public class Tank {
//添加方法完成坦克的绘画
public void draw(Graphics g) {
//省略其他关键性代码.......
if(camp){
//将阵营好的坦克颜色为红色
g.setColor(Color.red);
}else{
//将阵营坏的坦克颜色为蓝色
g.setColor(Color.blue);
}
}
//省略其他关键性代码.......
}
步骤总结
- ✧加入区别敌我的变量camp
- ✧根据敌我的不同设置不同的颜色
- ✧更新Tank的构造函数,加入camp
- ✧TankClient中new 出敌人的坦克并画出
这时我们就有一个新的想法出来了:怎么将这个坦克给干掉呢?
三、击毙机器坦克
在我们发射炮弹的时候,那么如何检测到是否击中坦克呢?我们称为碰撞检测
游戏中的碰撞检测方式有很多,不同的算法之间主要是在精度和速度之间权衡
我们这里介绍一个类:Rectangle,它指的是坐标空间中的一块区域,通过坐标空间中Rectangle对象左上方的点 (x,y)
、宽度和高度可以定义这个区域
下面是其比较常用的几个方法:
//-->判定点(x,y)是否包含在指定区域内
boolean contains(int x,int y)
//-->判定指定区域是否在其指定区域内
boolean contains(int x,int y,int width,int height)
而我们只需要判断子弹与坦克之间是否碰撞到一块来确定是否击中
public class Tank {
//创建子弹的坐标空间区域
public Rectangle getRect(){
return new Rectangle(x,y,WIDTH,HEIGHT);
}
//省略其他关键性代码.......
}
class Missle{
//创建子弹的坐标空间区域
public Rectangle getRect(){
return new Rectangle(x,y,WIDTH,HEIGHT);
}
public boolean hitTank(Tank t){
if(this.getRect().intersects(t.getRect())){
return true;
}
return false;
}
//省略其他关键性代码.......
}
那么我们怎么让这个坦克消失呢?回想一下我们子弹是怎么消失的?
是不是有一个变量控制着存活状态呀?所以我们坦克也添加一个变量控制
public class Tank {
//用于判断控制坦克存活变量
private boolean live = true;
public boolean isLive() {
return live;
}
public void setLive(boolean live) {
this.live = live;
}
//添加方法完成坦克的绘画
public void draw(Graphics g) {
//死掉的坦克不用绘制
if(!live){
return;
}
//省略其他关键性代码.......
}
//省略其他关键性代码.......
}
同时当我们的子弹与坦克进行碰撞时,则将它修改为false状态即可
class Missle{
public boolean hitTank(Tank t){
if(this.getRect().intersects(t.getRect())){
t.setLive(false);
return true;
}
return false;
}
//省略其他关键性代码.......
}
当我们运行起来的时候,还需要调用该方法完成碰撞检测
public class TankClient extends Frame {
//省略其他关键性代码.......
@Override
public void paint(Graphics g) {
//画出容器里的子弹
for ( int i = 0; i < missles.size();i++){
Missle m = missles.get(i);
//将阵营坏的坦克放进来进行碰撞
m.hitTank(enemytank);
m.draw(g);
}
//省略其他关键性代码.......
}
}
我们发现子弹与坦克发生碰撞后,坦克是消失了但是打中坦克的子弹没有消失
这是因为我们没有对这颗子弹也进行消亡,移出
class Missle{
public boolean hitTank(Tank t){
if(this.getRect().intersects(t.getRect())){
t.setLive(false);
this.live = false;
return true;
}
return false;
}
//添加方法完成子弹的绘画
public void draw(Graphics g) {
if(!live){
//若消亡则进行移出
tc.missles.remove(this);
}
//省略其他关键性代码.......
}
//省略其他关键性代码.......
}
但是为什么这时我们打一堆子弹出去,也会一并消亡呢?为什么?
因为我们在判断碰撞的时候,并没有将坦克的存活状态考虑进去
class Missle{
public boolean hitTank(Tank t){
if(this.getRect().intersects(t.getRect()) && t.isLive()){
t.setLive(false);
this.live = false;
return true;
}
return false;
}
//省略其他关键性代码.......
}
这时我们打中坦克则完成了子弹移出、消亡效果
步骤总结
- ✧Missle中 加入hitTank(Tank)方法,返回布尔类型
- ✧碰撞检测的辅助类Rectangle
- ✧为Tank 和Missle都加入getRect方法
- ✧当击中敌人坦克时,坦克被打死,子弹也死去
- ✧增加控制Tank生死的量live
- ✧如果坦克死去就不画了
我们这里的碰撞检测采用的是最简单的方式,将子弹与坦克用Rectangle类生成的方框包装起来
判断这两个方框是否相交来判断证明是否击中,击中后进行相关处理
参考资料
尚学堂:坦克大战(马士兵老师)