【Kotlin】坦克大战3:移动和碰撞检测

移动

在接口中val,在实现的时候可以var

当按下WSAD这四个键时,坦克向上下左右移动,我们重写GameWindow的onKeyPressed方法

override fun onKeyPressed(event: KeyEvent) {
        //用户操作时
        when(event.code){
            KeyCode.W -> {
                tank.move(Direction.up)
            }
            KeyCode.S -> {
                tank.move(Direction.down)
            }
            KeyCode.A -> {
                tank.move(Direction.left)
            }
            KeyCode.D -> {
                tank.move(Direction.right)
            }
        }
    }

Tank文件中增加move方法来处理坦克的移动

class Tank(override var x: Int, override var y: Int) :View{
    override val width: Int = Config.block
    override val height: Int = Config.block

    //方向
    var currentDirection:Direction = Direction.up
    //速度
    var speed:Int = 8

    override fun draw() {
        //根据坦克类型进行绘制
        //方式一
        /*when(currentDirection){
            Direction.up -> Painter.drawImage("img/tank_u.gif",x,y)
            Direction.down -> Painter.drawImage("img/tank_d.gif",x,y)
            Direction.left -> Painter.drawImage("img/tank_l.gif",x,y)
            Direction.right -> Painter.drawImage("img/tank_r.gif",x,y)
        }*/
        //方式二
        val imagePath:String = when(currentDirection){
            Direction.up -> "img/tank_u.gif"
            Direction.down -> "img/tank_d.gif"
            Direction.left -> "img/tank_l.gif"
            Direction.right -> "img/tank_r.gif"
        }
        Painter.drawImage(imagePath,x,y)
    }

    //坦克移动方向
    fun move(direction: Direction){
        //当前的方向 和 希望移动的方向不一致时,只做方向改变
        if(this.currentDirection != direction){
            this.currentDirection = direction
            return
        }

        //坦克的坐标移动
        //根据方向改变坐标
        when(currentDirection){
            Direction.up -> y-=speed
            Direction.down -> y+=speed
            Direction.left -> x-=speed
            Direction.right -> x+=speed
        }

        //越界判断
        if(x<0) x=0
        if(x>Config.gameWidth-width){
            x = Config.gameWidth-width
        }
         if(y<0) y=0
        if(y>Config.gameHeight-height){
            y = Config.gameHeight-height
        }
    }
}

碰撞检测

在这里插入图片描述
创建两个接口
在这里插入图片描述
Moveable描述移动的能力,有移动的方向和速度。还有坐标,所以继承View。最后增加了一个是否碰撞的方法、一个通知方法

/**
 * 移动的能力
 */
interface Moveable: View {
    //可移动的物体存在方向
    val currentDirection:Direction
    //可移动的物体的速度
    val speed:Int
    /**
     * 判断移动的物体是否和阻塞物体发生碰撞
     * @return 要碰撞的方向 如果为Null,说明没有发生碰撞
     */
    fun willCollision(block:Blockable):Direction?

    /**
     * 通知碰撞
     */
    fun notifyCollision(direction: Direction?,block: Blockable?)
}

我们让坦克实现Moveable,由于Moveable继承了View,所以可以把Tank继承的View去掉,然后override两个属性和两个这个方法

class Tank(override var x: Int, override var y: Int) :Moveable{
    override val width: Int = Config.block
    override val height: Int = Config.block

    //方向
    override var currentDirection:Direction = Direction.up
    //速度
    override var speed:Int = 8
    //坦克不可以走的方向
    private var badDirection:Direction? = null

    override fun draw() {
        ......
    }

    //坦克移动方向
    fun move(direction: Direction){
        //判断是否是往要碰撞的方向走
        if(direction == badDirection){
            //不往下执行
            return
        }
        //当前的方向 和 希望移动的方向不一致时,只做方向改变
        if(this.currentDirection != direction){
            this.currentDirection = direction
            return
        }

        ......
    }

    override fun willCollision(block: Blockable): Direction? {
        //检测碰撞 暂时规定的上为不可移动方向
        return Direction.up
    }

    override fun notifyCollision(direction: Direction?, block: Blockable?) {
        //接收碰撞信息
        this.badDirection = direction
    }
}

Blockable描述阻塞的能力

/**
 * 阻挡阻塞的能力
 */
interface Blockable:View {
}

墙Wall实现Blockable

class Wall(override var x: Int, override var y: Int) :View,Blockable{
    override val width: Int = Config.block
    override val height: Int = Config.block

    ......
}

Steel、Water同样实现Blockable

修改GameWindow

class GameWindow :
    Window(title = "坦克大战", icon = "img/kotlin.jpg", width = Config.gameWidth, height = Config.gameHeight) {
    //管理元素的集合
    private val views = arrayListOf<View>()
    //晚点创建
    private lateinit var tank:Tank

    override fun onCreate() {
        ......
    }

    override fun onDisplay() {
       ......
    }

    override fun onKeyPressed(event: KeyEvent) {
       ......
    }

    override fun onRefresh() {
        //业务逻辑
        //判断运动的物体和阻塞物体发生碰撞
        //1)找到运动的物体
        views.filter { it is Moveable }.forEach{move->
            //2)找到阻塞的物体
            move as Moveable
            var badDirection: Direction? = null
            var badBlock:Blockable? = null

            views.filter { it is Blockable }.forEach blockTag@{block->
                //3)遍历集合,找到是否碰撞
                block as Blockable
                //获得碰撞方向
                val direction = move.willCollision(block)
                //不为空走
                direction?.let {
                    badDirection = direction
                    badBlock = block
                    //发现碰撞,跳出循环
                    return@blockTag
                }
            }

            //找到和move碰撞的阻塞块,找打会碰撞的方向
            //通知可以移动的物体会在哪个方向和哪个物体碰撞
            move.notifyCollision(badDirection,badBlock)
        }
    }
}

实现碰撞逻辑

接下来实现检测碰撞的逻辑
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

        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
        }

        return if(collision) currentDirection else null
    }

看代码中,开始用java写了碰撞逻辑,我们用kotlin写一遍。我们可以看到有虚线,说明代码可以优化,按alt+enter,进行优化

优化后

    override fun willCollision(block: Blockable): Direction? {
        //检测碰撞
        //kotlin写法
        val collision = when {
            block.y + block.height <= y -> //如果阻挡物在运动物上方 不碰撞
                false
            y + height <= block.y -> //如果阻挡物在运动物下方 不碰撞
                false
            block.x + block.width <= x -> //如果阻挡物在运动物左边 不碰撞
                false
            else -> x + width > block.x
        }
        return if(collision) currentDirection else null
    }

现在做完后运行的bug就是,当碰到障碍物时,再次移动,坦克就动不了了,因为之前检测的不是即将碰撞的位置,而是,碰撞后的位置

修改代码

在这里插入代码片

运行一下
在这里插入图片描述

发布了640 篇原创文章 · 获赞 143 · 访问量 54万+

猜你喜欢

转载自blog.csdn.net/u010356768/article/details/103471560