用Swift做个游戏Lecture06 —— 碰撞的检测
前文已经为各个精灵新增了Physics Body,设置了三个掩码:
现在遗留的问题是如何检测碰撞?难道是在update()方法进行检测:遍历所有的节点,通过判断节点的位置是否有交集吗?天呐!这也太麻烦了。确实,如果通过自己实时检测实在过于劳累,何不让Sprite Kit来帮你代劳,每当物体之间发生碰撞了,立马通知你来处理事件。Bingo!! 显然这里要用协议+代理了,设置场景为代理,每当Sprite Kit检测到碰撞事件发生,就通知GameScene来处理,当前哪里事情都是在协议(Protocol)中声明了。 01.游戏状态在正式开始今天的碰撞检测课程之前,谈谈如何划分游戏各时的状态,仅以Flappy bird游戏为例,简单划分如下:
为此请打开Lecture05的完成工程,打开GameScene.swift文件,新增游戏状态的枚举声明到 enum GameState{
case MainMenu
case Tutorial
case Play
case Falling
case ShowingScore
case GameOver
}
当然,我们还需要声明一个变量用于存储游戏场景的状态,请找到GameScene类中 //1
var hitGround = false
//2
var hitObstacle = false
//3
var gameState: GameState = .Play
02.碰撞检测正如前面提及的协议+代理方式检测物体之间的碰撞情况。首先请使得类GameScene遵循 class GameScene: SKScene,SKPhysicsContactDelegate{...}
接着在didMoveToView()方法中设置代理为
分别用于反馈两个物体开始接触、结束接触两个时刻。本文采用第一个方法用户处理物体接触事件。 func didBeginContact(contact: SKPhysicsContact) {
let other = contact.bodyA.categoryBitMask == PhysicsCategory.Player ? contact.bodyB : contact.bodyA
if other.categoryBitMask == PhysicsCategory.Ground {
hitGround = true
}
if other.categoryBitMask == PhysicsCategory.Obstacle {
hitObstacle = true
}
}
一旦标志位设置之后,我们需要在update()方法中进行处理了! 03.根据游戏状态来处理事件请定位到 override func update(currentTime: CFTimeInterval) {
if lastUpdateTime > 0 {
dt = currentTime - lastUpdateTime
} else {
dt = 0
}
lastUpdateTime = currentTime
switch gameState {
case .MainMenu:
break
case .Tutorial:
break
case .Play:
updateForeground()
updatePlayer()
//1
checkHitObstacle() //Play状态下检测是否碰撞了障碍物
//2
checkHitGround() //Play状态下检测是否碰撞了地面
break
case .Falling:
updatePlayer()
//3
checkHitGround() //Falling状态下检测是否掉落至地面 此时已经失败了
break
case .ShowingScore:
break
case .GameOver:
break
}
}
其中1,2,3中三个方法均是通过状态标志位来处理碰撞事件,请添加 // 与障碍物发生碰撞
func checkHitObstacle() {
if hitObstacle {
hitObstacle = false
switchToFalling()
}
}
// 掉落至地面
func checkHitGround() {
if hitGround {
hitGround = false
playerVelocity = CGPoint.zero
player.zRotation = CGFloat(-90).degreesToRadians()
player.position = CGPoint(x: player.position.x,y: playableStart + player.size.width/2)
runAction(hitGroundAction)
switchToShowScore()
}
}
// MARK: - Game States
// 由Play状态变为Falling状态
func switchToFalling() {
gameState = .Falling
runAction(SKAction.sequence([
whackAction,SKAction.waitForDuration(0.1),fallingAction
]))
player.removeAllActions()
stopSpawning()
}
// 显示分数状态
func switchToShowScore() {
gameState = .ShowingScore
player.removeAllActions()
stopSpawning()
}
// 重新开始一次游戏
func switchToNewGame() {
runAction(popAction)
let newScene = GameScene(size: size)
let transition = SKTransition.fadeWithColor(SKColor.blackColor(),duration: 0.5)
view?.presentScene(newScene,transition: transition)
}
完成后自然你发现 OK,找到 ...
bottomObstacle.name = "BottomObstacle"
worldNode.addChild(bottomObstacle)
...
topObstacle.name = "TopObstacle"
worldNode.addChild(topObstacle)
...
现在来实现 func stopSpawning() {
removeActionForKey("spawn")
worldNode.enumerateChildNodesWithName("TopObstacle",usingBlock: { node,stop in
node.removeAllActions()
})
worldNode.enumerateChildNodesWithName("BottomObstacle",stop in
node.removeAllActions()
})
}
点击运行,我擦!还没来得及点就掉地上了……好吧,只能在游戏进入一瞬间先让Player向上蹦跶下。添加 点击运行,Nice!!Player顺利穿过了障碍,不小心碰到了障碍物,再点击,等等!怎么还能动…好吧,看来 override func touchesBegan(touches: Set<UITouch>,withEvent event: UIEvent?) {
switch gameState {
case .MainMenu:
break
case .Tutorial:
break
case .Play:
flapPlayer()
break
case .Falling:
break
case .ShowingScore:
switchToNewGame()
break
case .GameOver:
break
}
}
点击运行,失败重新开始游戏…等等貌似还有问题,怎么点击想重新开始游戏会突然掉落到地面上…好吧,请看lecture02中的时间间隔图,匆忙的你找找原因,试试解决吧。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |