检测子弹和砖墙的磕碰

  • 子弹具有进犯的才能
  • 砖墙具有受进犯的才能
  • 什么时候发生进犯和被进犯

不要乱想了。开端写代码

创立两个接口Attackable、Sufferable

/**
 * 进犯的才能
 */
interface Attackable : View {
    //判别是否磕碰
    fun isCollision(suffer:Sufferable):Boolean
    //告诉
    fun notifyAttack(suffer: Sufferable)
}
/**
 * 遭受进犯的才能
 */
interface Sufferable : View {
    fun notifySuffer(attackable: Attackable)
}

GameWindow的业务逻辑中添加

class GameWindow :
    ......
    override fun onRefresh() {
        ......
        //检测有进犯才能和被进犯才能的物体间是否发生了磕碰
        //1)过滤具有进犯才能的
        views.filter { it is Attackable }.forEach{attack->
            attack as Attackable
            //2)具有受进犯才能的
            views.filter { it is Sufferable }.forEach sufferTag@ {suffer->
                suffer as Sufferable
                //3)判别是否发生磕碰
                if(attack.isCollision(suffer)){
                    //发生磕碰,找到磕碰者
                    //告诉进犯者发生磕碰了
                    attack.notifyAttack(suffer)
                    //告诉被进犯者发生磕碰
                    suffer.notifySuffer(attack)
                    return@sufferTag
                }
            }
        }
    }
}

子弹完成Attackable,完成两个办法,其中是否磕碰isCollision咱们在Tank中写过,直接拿过来,由于是重复代码,咱们能够把它放到View中

interface View {
    ......
    //显现
    fun draw()
    fun checkCollision(x1:Int,y1:Int,w1:Int,h1:Int,
                       x2:Int,y2:Int,w2:Int,h2:Int):Boolean{
        //两个物体的坐标x,y,w,h的比较
        return when {
            y2 + h2 <= y1 -> //假如阻挠物在运动物上方 不磕碰
                false
            y1 + h1 <= y2 -> //假如阻挠物在运动物下方 不磕碰
                false
           x2+ w2 <= x1 -> //假如阻挠物在运动物左面 不磕碰
                false
            else -> x1 + w1 > x2
        }
    }
}

那么Tank中的磕碰办法能够修改为

class Tank(override var x: Int, override var y: Int) : Moveable {
    ......
    override fun willCollision(block: Blockable): Direction? {
        //将要磕碰时,用未来的坐标
        var x = this.x
        var y = this.y
        when (currentDirection) {
            Direction.up -> y -= speed
            Direction.down -> y += speed
            Direction.left -> x -= speed
            Direction.right -> x += speed
        }
        //检测下一步是否磕碰磕碰
        //kotlin写法
       /* val collision:Boolean = when {
            block.y + block.height <= y -> //假如阻挠物在运动物上方 不磕碰
                false
            y + height <= block.y -> //假如阻挠物在运动物下方 不磕碰
                false
            block.x + block.width <= x -> //假如阻挠物在运动物左面 不磕碰
                false
            else -> x + width > block.x
        }*/
        var collision = checkCollision(x, y, width, height, block.x, block.y, block.width, block.height)
        return if(collision) currentDirection else null
    }
    ......
}

现在传8个参数,是有点麻烦,咱们能够在View中对办法进行重载。但是假如View的代码已经写死了,咱们能够为View进行扩展,创立ViewExt

【Kotlin】坦克大战6:攻和受

import com.errol.gradle.model.View
fun View.checkCollision(view: View): Boolean {
    return checkCollision(x, y, width, height, view.x, view.y, view.width, view.height)
}

Bullet中

class Bullet(override val currentDirection: Direction, create: (width: Int, height: Int) -> Pair<Int, Int>) : AutoMoveable,Destroyable,Attackable {
    ......
    override fun isCollision(suffer: Sufferable): Boolean {
        return checkCollision(suffer)
    }
    override fun notifyAttack(suffer: Sufferable) {
        println("子弹接遭到磕碰")
    }
}

让Wall完成Sufferable

class Wall(override var x: Int, override var y: Int) :Blockable,Sufferable{
    ......
    override fun notifySuffer(attackable: Attackable) {
        println("砖墙接遭到进犯")
    }
}

运行程序,当子弹磕碰到砖墙,会有告诉打印

磕碰后子弹消失

磕碰后子弹应该消失,咱们看到Bullet具有毁掉的才能,但是代码中咱们只是让给它越界后消失,这里咱们添加一个变量isDestroyed

class Bullet(override val currentDirection: Direction, create: (width: Int, height: Int) -> Pair<Int, Int>) : AutoMoveable,Destroyable,Attackable {
    private var isDestroyed:Boolean = false
    ......
    override fun isDestroyed(): Boolean {
        if(isDestroyed) return true
        //子弹脱离屏暗地需要被毁掉
        if(x<-width) return true
        if(x>Config.gameWidth) return true
        if(y<-height) return true
        if(y>Config.gameHeight) return true
        return false
    }
    ......
    override fun notifyAttack(suffer: Sufferable) {
        println("子弹接遭到磕碰")
        //子弹碰到砖墙后,应该毁掉
        isDestroyed = true
    }
}

墙遭到进犯后损失

咱们界说能遭受进犯的物体都有一个生命值,修改Sufferable,添加一个生命值

interface Sufferable : View {
    //生命值
    var blood:Int
    fun notifySuffer(attackable: Attackable)
}

给具有进犯才能的物体,一个进犯力。一个普通坦克可能有1点进犯力。一个超级坦克可能有10点进犯力

interface Attackable : View {
    //进犯力
    var attackPower:Int
    //判别是否磕碰
    fun isCollision(suffer:Sufferable):Boolean
    //告诉
    fun notifyAttack(suffer: Sufferable)
}

Bullet咱们给它1点进犯力

override var attackPower: Int = 1

砖墙没血的时候应该被毁掉,因此完成Destroyable

/**
 * 砖墙
 * 具有阻塞才能
 * 具有受进犯才能
 * 具有毁掉才能
 */
class Wall(override var x: Int, override var y: Int) :Blockable,Sufferable,Destroyable{
    override var blood: Int = 3
    ......
    override fun notifySuffer(attackable: Attackable) {
        println("砖墙接遭到进犯")
        //砖墙掉血
        blood -= attackable.attackPower
    }
    override fun isDestroyed(): Boolean = blood<=0
}

运行

【Kotlin】坦克大战6:攻和受

爆破物显现

现在作用不够劲爆,添加音效

class Wall(override var x: Int, override var y: Int) :Blockable,Sufferable,Destroyable{
    ......
    override fun notifySuffer(attackable: Attackable) {
        ......
        //声音播放
        Composer.play("snd/hit.wav")
    }
    ......
}

添加爆破特效,咱们能够对Sufferable中的notifySuffer进行修改,让它回来一个或许多个View,能够为空

interface Sufferable : View {
    ......
    fun notifySuffer(attackable: Attackable):Array<View>?
}

Wall被进犯后发生爆破作用

class GameWindow :
 	......
    override fun onRefresh() {
        ......
        //检测有进犯才能和被进犯才能的物体间是否发生了磕碰
        //1)过滤具有进犯才能的
        views.filter { it is Attackable }.forEach{attack->
            attack as Attackable
            //2)具有受进犯才能的
            views.filter { it is Sufferable }.forEach sufferTag@ {suffer->
                suffer as Sufferable
                //3)判别是否发生磕碰
                if(attack.isCollision(suffer)){
                    //发生磕碰,找到磕碰者
                    //告诉进犯者发生磕碰了
                    attack.notifyAttack(suffer)
                    //告诉被进犯者发生磕碰
                    var sufferView:Array<View>? = suffer.notifySuffer(attack)
                    sufferView?.let {
                        //显现被进犯的作用
                        views.addAll(sufferView)
                    }
                    return@sufferTag
                }
            }
        }
    }
}

新建Blast

/**
 * 爆破物
 */
/**
 * 爆破物
 */
class Blast(override val x: Int, override val y: Int) :Destroyable {
    override val width: Int = Config.block
    override val height: Int = Config.block
    private var index:Int = 0
    private val imagePaths = arrayListOf<String>()
    init{
        (1..32).forEach {
            imagePaths.add("img/blast_${it}.png")
        }
    }
    override fun draw() {
        val i:Int = index%imagePaths.size
        Painter.drawImage(imagePaths[i],x,y)
        index++
    }
    override fun isDestroyed(): Boolean {
        return index>=imagePaths.size
    }
}

Wall

class Wall(override var x: Int, override var y: Int) :Blockable,Sufferable,Destroyable{
    ......
    override fun notifySuffer(attackable: Attackable):Array<View>? {
        ......
        Composer.play("snd/hit.wav")
        return arrayOf(Blast(x,y))
    }
    ......
}

运行

【Kotlin】坦克大战6:攻和受