移动
在接口中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就是,当碰到障碍物时,再次移动,坦克就动不了了,因为之前检测的不是即将碰撞的位置,而是,碰撞后的位置
修改代码
在这里插入代码片
运行一下