Java lecture 10 实现炸弹范围,攻击行为后潜艇和炸弹同样消失、生命、分数的显示及自增自减

一、在游戏中加入接口

在项目包下创建两个接口:

加分接口:

package cn.tedu.submarine;
​
/**
 * 加分的接口 :  目前 让侦察潜艇 和 鱼雷潜艇 来实现
 */
public interface EnemyScore {
​
    /**
     *  返回值为int类型  代表就是若被打到时 需要提要提供的分数
     */
    int getScore();
​
}

侦察潜艇,鱼雷潜艇实现:

  @Override   侦察潜艇类
    public int getScore() {
        return 10;
    }
​
  @Override   鱼雷潜艇类
    public int getScore() {
        return 40;
    }
​

加命接口:

package cn.tedu.submarine;
​
/**
 * 加命的接口
 */
public interface EnemyLife {
​
    /**
     *加命的接口,主要提供加命的行为,供实现类来进行实现并重写具体的细节。
     */
    int getLife();
​
}
​

水雷潜艇类实现:

 @Override
    public int getLife() {
        return 1;
    }

总结设计规则:

1.将所有概念统一的类,它们共性属性和行为 提取到一个父类中.过程叫做泛化.也是优化代码的冗余.

2.提取到父类中的共性行为里面的实现逻辑,若子类都能够或多个子类可以使用,则可以设计为普通方法.

若所有子类不能够使用父类提供的行为内的实现逻辑时,则应该设计为抽象方法!

3.接口:不同类别或同类别中的部分类别存在行为相同,我们可以使用接口来进行优化和复用。

二、编程中错误的一些分类

1.编译错误:

编译期间产生的错误 ----------全部都是语法的错误(练得少,多练!)

2.运行异常:

运行期间产生的错误 -----------空指针异常/数组下标越界异常..

3.程序没报错,但是现象就很奇怪.

解决方法:

3.1:锁定错误范围,通过现在来决定当前需要筛查的代码模块。

3.2:打桩, 就是用System.out.println(1);打印语句,检测该方法有没有执行。

------------------------------------------------------------------------------------------

面向对象的三大特性:封装,继承 ,多态。

多态:

比如:人类在睡觉的行为上有哪些多态:右侧睡,左侧睡,平躺睡,趴着睡...

人类在形体上有哪些多态:有的人高,有的人矮,有的人胖,有的人瘦.....

-------------------------------------------------------------------------------------------

程序上的多态 -----------行为的多态,一个父类型中的行为,子类有不同的实现。

引用类型的变量能打点调出什么,要看变量的类型!比如说下面调用p.cut()

也就是调用父类执行子类

举例:
 

人 p = new 理发师(); //声明父 new  子  向上造型
人 p1 = new 医生();
人 p2 = new 园丁();
p.cut();//p在调用的时候,用的是人类中cut,但是具体运行时执行p里面存储的对象的cut行为
p1.cut();//p在调用的时候,用的是人类中cut,但是具体运行时执行p里面存储的对象的cut行为
p2.cut();//p在调用的时候,用的是人类中cut,但是具体运行时执行p里面存储的对象的cut行为
class Person{
   abstract void cut();
}
class 理发师 extends Person{
    
    void cut(){
        System.out.println("剪发..");    
    }
}
class 医生 extends Person{
    void cut(){
        System.out.println("做手术..");    
    }
}
class 园丁 extends Person{
    void cut(){
        System.out.println("修剪树木..");    
    }
}

当一个对象 被造型为不同的类型时,具有不同的行为

我 me = new 我();
me.授课();
me.互相卷();
me.孝顺父母();
--------------------------------------
讲师 o1 = new  我();
//这里是向上造型,变成讲师。
o1.授课();
公司员工 o2 = new 我();
o2.互相卷();
父母的儿子 o3 = new 我();
o3.孝顺父母();
​
interface 讲师{
    void 授课();
}
interface 公司员工{
    void 互相卷();
}
interface 父母的儿子{
    void 孝顺父母();
}
​
class 我 implements 讲师,公司员工,父母儿子{
    void 授课(){ }
    void 互相卷(){ }
    void 孝顺父母(){   }
}

向上造型/引用类型中的自动类型转换

1.父 大 子 小 -----声明父 new 子(这时子类会被自动转换为大类型(父类))

2.能够向上造型成功, 父 new 子 , 接口 new 实现类

都是左边大(父类),右边小(子类)

----------------------------------------------------------------------------------

向下转型/引用类型中的强制类型转换

能否强制成功,要看以下两个条件:

条件一:要强转的引用类型变量中的对象 就是转换的这个类型。

条件二:要强转的引用类型变量中的对象, 实现了要转换的这个接口,或者继承了该类。

需要引用类型的强转时,一定要在强制的语法前用if 通过instanceof来判断是否可以转换成功。

仔细看下面的代码例子,试着理解一下:
 

Aoo a1 = new Boo(); //向上造型。
​
Boo b1 = (Boo)a1;//可以完成强转操作,符合条件一:
Inter1 i1 = (Inter1)a1;//可以完成强转操作,符合条件二
Coo c1 = (Coo)a1; //强制转换失败的异常:class Cast Exception
​
interface Inter1{
    
}
class Aoo{
    
}
class Boo extends Aoo  implements Inter1{
    
}
class Coo extends Aoo{
    
}
package oo.day05;
​
/**
 *  类型转换的使用演示类:
 */
public class ClassCastDemo {
    public static void main(String[] args) {
        Aoo1 o1 = new Boo();//向上造型
        Boo b1 = (Boo) o1;//符合条件一:要强转的引用类型变量中的对象 就是转换的这个类型。
        Inter i1 = (Inter) o1;//符合条件二:要强转的引用类型变量中的对象, 实现了要转换的这个接口
        //
        if(o1 instanceof Coo) { //instanceof :判断 o对象 是否可以转换为 Coo类型
            Coo c1 = (Coo) o1;
            System.out.println("123");
        }
    }
}
interface Inter{
​
}
class Aoo1{
​
}
class Boo extends Aoo1  implements Inter{
​
}
class Coo extends Aoo1{
​
}

项目目前欠缺的:

深水炸弹要与潜艇碰撞:

雷与战舰进行碰撞:

因为碰撞的行为子类都有,所以要提取到父类中,做普通方法。根据碰撞的图可以计算出,雷和这些战舰的边界。所以以潜艇为参照物来进行坐标的设定。this代表潜艇、other代表深水炸弹 

public boolean isHit(SeaObject other){
        int x1 = this.x - other.width;
        int x2 = this.x + this.width;
        int y1 = this.y - other.height;
        int y2 = this.y + this.height;
        int x = other.x;//传入碰撞对象的x
        int y = other.y;//传入碰撞对象的y
        //返回 并判断  x是否在 x1 和x2 之间 并且 y是否在 y1 和y2 之间。
        return (x >= x1 && x <= x2) &&(y >= y1 && y <= y2); 
        //这里相当于规定出潜艇的领空
    
}
-------------------------------------------
isHit方法的逻辑其实都是一样的,所以不管谁碰谁:
1)潜艇对象.isHit(深水炸弹)--------------------this指代的是潜艇对象  other指代的是深水炸弹
2)深水炸弹.isHit(潜艇对象)--------------------this指代的是深水炸弹  other指代的是潜艇对象
3)雷.isHit(战舰)        --------------------this指代的是雷对象    other指代的是战舰
4)战舰.isHit(雷)        --------------------this指代的是战舰      other指代的是雷    

因为碰撞的行为是自动发生的,所以我们要在GameWorld类写一个方法bombBangAction,主要实现的是深水炸弹与潜艇的碰撞。

逻辑就是,把每个深水炸弹和每个潜艇的领空边界进行比较,看有没有碰上。

代码如下:
 

   /**
     * 深水炸弹与潜艇碰撞检测的逻辑实现。 -放run中调用
     */
    public void bombBangAction(){
        for (int i = 0; i < bombs.length; i++) {
            Bomb b = bombs[i];//获取当前深水炸弹数组中的对象
            for (int j = 0; j < submarines.length; j++) {
                SeaObject obj = submarines[j];//获取当前潜艇数组中的对象
                if(b.isLive() && obj.isLive()&&b.isHit(obj)){//深水炸弹.isHit(潜艇对象)
                   b.goDead(); //标记
                   obj.goDead();//标记
                }
            }
        }
    }

因为深水炸弹打到潜艇,两个都要消失,所以我们可以在SeaObject写一个goDead方法。哪个对象打点调用了goDead方法,哪个对象当前状态则被标记为死亡状态。

  /**
     * 哪个对象打点调用goDead方法,则被标记为死亡状态。
     */
    public void goDead(){
        this.currentState = DEAD;//将当前对象设置为死亡状态。
    }

并且,被标记为死亡状态的对象

也需要被删除掉。所以这个时候补充到原来的deleteOutOfBounds方法中就好。

  public SeaObject[] deleteOutOfBounds(SeaObject[] objects) {
        for (int i = 0; i < objects.length; i++) {
            //2.判断数组中的每个对象是否越界
            if (objects[i].isOutBounds() || objects[i].isDead() ) {
                objects[i] = objects[objects.length - 1];//将数组最后一个对象赋值覆盖当前越界的对象空间里
                objects = Arrays.copyOf(objects, objects.length - 1);//删除最后一行
            }
        }
        return objects;//需要将当前缩容后的数组对象 返回出去
    }

如何实现雷与战舰的碰撞检测

在GameWorld类中,定义一个方法,方法命名为thunderBangAction,并放到run中调用。

内部逻辑: 遍历雷数组对象,依次与战舰对象去检测,如果碰到了雷对象 就goDead

/**
     * 雷与战舰碰撞的逻辑。
     */
    public void thunderBangAction() {
        for (int i = 0; i < thunders.length; i++) {
            if (thunders[i].isLive() && thunders[i].isHit(ship)) {
                thunders[i].goDead();
            }
        }
    }
​

所以到这一步之后,已经实现了炸弹攻击到潜艇后两者的消失。接下来就是加分逻辑和加命逻辑的写法了。

引入g.drawString用法(在paint方法里的)

用来将字符串显示在屏幕上

接受四个参数g.drawString("字符串"+参数值, x,y)

其中x和y表示字符串的坐标。

    private int score = 0; //默认游戏分为0 分
                g.drawString("Score:" + score, 200, 50);
                g.drawString("Life:" + ship.getLife(), 400, 50);

这个时候在潜艇类里加上life的setter和getter方法,方法公开化。

加上去的字体样式可以调。

g.setFont/g.setColor等等

                g.setFont(new Font("", Font.BOLD, 20));

双引号里写的是要选择的字体样式

20是字体大小

加分逻辑和加命逻辑写在哪里?

因为bombBangAction是炸弹爆炸的逻辑,炸弹爆炸就会让潜艇加分或加命,所以加在这里。

但是因为obj是父类,侦查潜艇加分,鱼类潜艇要加分,只有剩下的那个ship要加命,所以要用if-else来进行分类。

/**
     * 深水炸弹与潜艇碰撞检测的逻辑实现。 -放run中调用
     */
    public void bombBangAction() {
        for (int i = 0; i < bombs.length; i++) {
            Bomb b = bombs[i];//获取当前深水炸弹数组中的对象
            for (int j = 0; j < submarines.length; j++) {
                SeaObject obj = submarines[j];//获取当前潜艇数组中的对象
                if (b.isLive() && obj.isLive() && b.isHit(obj)) {//深水炸弹.isHit(潜艇对象)
                    b.goDead(); //标记
                    obj.goDead();//标记
                    if (obj instanceof EnemyScore) {//判断obj对象是否是 可以强转为 EnemyScore 接口
                        score += ((EnemyScore) obj).getScore();
                    } else if (obj instanceof EnemyLife) {
                        EnemyLife e = (EnemyLife) obj;
                        ship.setLife(e.getLife());
                    }
​
                    //加分加命的操作
                    //侦察潜艇要加分
                    //以下代码  复用性差,扩展性差,维护性也差   ----垃圾代码
//                    if (obj instanceof ObserverSubmarine) { //判断 obj 是否是 侦察潜艇类型
//                        ObserverSubmarine os = (ObserverSubmarine) obj;//强制转换
//                        score += os.getScore();//调用给分行为 获取分数
//                    } else if (obj instanceof TorpedoSubmarine) { //鱼雷潜艇要加分
//                        TorpedoSubmarine ts = (TorpedoSubmarine) obj;
//                        score += ts.getScore();
//                    } else if(obj instanceof MineSubmarine){   //水雷潜艇要加命
//                        MineSubmarine ms= (MineSubmarine)obj;
//                        ship.setLife(ms.getLife());
//                    }
​
​
                }
            }
        }
    }

战舰类提供减命的行为:

 public void subtractLife(){//提供减命的方法
        this.life--; //自减
    }

至此,完成了炸弹范围,攻击行为后潜艇和炸弹同样消失、生命、分数的显示及自增自减。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq030928

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值