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

文章目录

  • 检测子弹和砖墙的碰撞
  • 碰撞后子弹消失
  • 墙受到攻击后损失
  • 爆炸物显示

检测子弹和砖墙的碰撞

【Kotlin】坦克大战6:攻和受_第1张图片
不要乱想了。开始写代码

创建两个接口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:攻和受_第2张图片

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:攻和受_第3张图片

爆炸物显示

现在效果不够劲爆,增加音效

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:攻和受_第4张图片

你可能感兴趣的:(Kotlin从零到一无所有,kotlin)