创建两个接口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
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
}
现在效果不够劲爆,增加音效
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))
}
......
}