我正在处理一个我一直在努力修复的错误,
我的游戏包含一个与平台部分交互的玩家精灵……一个标准的平台游戏。我通过将薄物理块连接到更大的平台物理主体(大白块)来设置我的游戏
每个细块代表:
- A面 - 绿色
- 左墙 - 黄色
- 右墙 - 红色
我可以配置使用自定义类打开或关闭这些。
- 蓝色的水滴是杀死玩家的死区
错误描述
所以我现在正在处理一个有趣的错误。它发生的方式是如果我的玩家精灵击中了一个 deadZone 物理体(杀死一个角色的区域)。
通过 revive() 函数复活角色后会出现错误。似乎 didBeginContact 被调用并相信我的角色与墙壁接触。即使看不到墙壁物理体。
仅当我在角色复活后开始快速按下跳跃时才会出现该错误。在按下跳转之前等待一小段时间不会发生错误。
联系 deadZone 时,我的 GameScene 类中的 didBeginContact() 代码如下所示
@objc(didBeginContact:) func didBegin(_ contact: SKPhysicsContact) {
let firstBody: SKPhysicsBody
let secondBody: SKPhysicsBody
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
firstBody = contact.bodyA
secondBody = contact.bodyB
} else {
firstBody = contact.bodyB
secondBody = contact.bodyA
}
//MARK: PLAYER with DEADZOne
if ( firstBody.categoryBitMask == BodyType.player.rawValue && secondBody.categoryBitMask == BodyType.deadZone.rawValue) {
print("deadzone contact with Player")
if let somePlayer:Player = firstBody.node as? Player {
somePlayer.changeRotation(newAngle: 0)
killPlayer(somePlayer)
}
}
请注意该功能不完整...(缺少诸如 Player - Wall Left (见下文)、 Player - Wall Right 之类的内容)
我知道 didBeginContact 被调用是因为调试控制台发出了我在 Wall - Player 部分中编写的打印语句,当错误发生时。它打印"contact with wall didBegin",当您接触左墙或右墙时打印。
//MARK: PLAYER with PLATFORM WALL (LEFT)
if ( firstBody.categoryBitMask == BodyType.player.rawValue && secondBody.categoryBitMask == BodyType.wallLeft.rawValue){
if let somePlayer:Player = firstBody.node as? Player,
let theWall:Platform = secondBody.node as? Platform
{
if somePlayer.jumpedOccured {
print("contact with wall didBegin")
somePlayer.onRightWall = false
somePlayer.onLeftWall = true
if somePlayer.isWallJumping {
somePlayer.stopWallJump()
playerOnWall(somePlayer, platformNode: theWall)
} else {
print("contact with wall didBegin")
playerOnWall(somePlayer, platformNode: theWall)
}
}
}
}
我的玩家回到起点的方式是通过调用 killPlayer() 将他移回重生点的代码,即关卡起点处的不可见精灵,该函数在 GameScene 类中调用包含此代码的函数:
var startBackToLocation:CGPoint = CGPoint.zero
if (somePlayer == thePlayer) {
if ( respawnPointPlayer1 != CGPoint.zero) {
startBackToLocation = respawnPointPlayer1
}
let wait:SKAction = SKAction.wait(forDuration: somePlayer.reviveTime)
let move:SKAction = SKAction.move(to: startBackToLocation, duration:0)
let run:SKAction = SKAction.run {
somePlayer.playerIsDead = false;
somePlayer.revive()
}
let seq:SKAction = SKAction.sequence([wait, move, wait, run])
somePlayer.run(seq)
我可以通过简单地增加等待时间来防止错误发生,例如
let wait:SKAction = SKAction.wait(forDuration: 1)
但是,这会导致游戏的流畅性降低。
我不确定到底是什么问题?
如果我从一个关卡中移除所有墙体物理体,我也可以防止错误发生,只有表面物理体......
我的猜测是它与 SpriteKit 的渲染循环有关,首先调用 didEvaluateActions... 在调用 didStimulatePhysics ...
所以 didEvaluateActions 将 Player sprite 移回起点。也许玩家在移回墙壁时碰到了墙壁?
也许 didStimulatePhysics 还没有完全赶上?也许循环需要再次运行才能识别出玩家的物理身体已经移动?
我有点迷路了,一直试图修复它一段时间。
这是一个演示该错误的视频。 YouTube 链接
视频中的第一级
视频显示该错误不会发生在没有墙壁物理实体的关卡上。
第二级
然后视频显示它发生在具有墙壁物理体的下一个级别。
- 第一次死亡后不会出现错误
- 第二次死亡后出现错误
- 第三次死亡后出现错误
- 第四次死亡后出现错误
- 第五次死亡后出现错误
- 第六次死亡后出现错误
ETC
什么没有奏效
1. 与 deadZone 接触时将 isDynamic 设置为 false。
然后在 Player 类中使用 revive() 将其更改为 true
// On Contact with DeadZone this code is executed
self.physicsBody?.isDynamic = false
// On revive()
self.physicsBody?.isDynamic = true
2. 通过在 deadZone 接触上将其设置为 nil 来移除物理体,然后在 revive() 上重新创建它。奇怪的是,在测试过程中,我可以直观地看到在 deadZone 接触上移除的物理矩形......然后重新出现......它仍然没有解决问题。
somePlayer.physicsBody = nil
3. 尝试重置玩家的contactTestMask。通过在deadZone 联系上将其设置为零,然后在revive() 上将其重置为默认值。
例如在 DeadZone 联系人
somePlayer.physicsBody?.contactTestBitMask = 0
例如在 revive()
self.physicsBody?.contactTestBitMask = BodyType.platformSurface.rawValue | BodyType.platformCeiling.rawValue | BodyType.wallRight.rawValue | BodyType.wallLeft.rawValue | BodyType.pole.rawValue | BodyType.portal.rawValue | BodyType.deadZone.rawValue | BodyType.coin.rawValue | BodyType.player.rawValue | BodyType.zipLine.rawValue | BodyType.springBoard.rawValue
4. 为每个 Player 使用 return 语句 - 如果 playerIsDead,___ 联系人退出范围。
例如在左墙接触
if somePlayer.playerIsDead == true {return}
5.直接设置玩家位置,而不是在玩家死亡时使用SKAction.move。
let wait:SKAction = SKAction.wait(forDuration: somePlayer.reviveTime)
let move:SKAction = SKAction.run {
somePlayer.position = startBackToLocation
}
let run:SKAction = SKAction.run {
somePlayer.playerIsDead = false;
somePlayer.revive()
}
let seq:SKAction = SKAction.sequence([wait, move, wait, run])
somePlayer.run(seq)
是什么降低了错误的频率
- 我在 Player 类中尝试了一个“重置物理函数”......从 Player 类中的 revive() 函数调用。
同时仍然在DeadZone 联系人上设置somePlayer.physicsBody = nil
func resetPlayerPhysicsBody() {
let removePhysicsBody: SKAction = SKAction.run {
self.physicsBody = nil
self.canJumpAgain = false
}
let wait: SKAction = SKAction.wait(forDuration: self.reviveTime)
let resetToDefaults: SKAction = SKAction.run {
self.setUpPlayerPhysics() // Recreates initial physics body //
self.canJumpAgain = true
}
let seq:SKAction = SKAction.sequence([removePhysicsBody, wait, resetToDefaults])
self.run(seq)}
这实际上使错误......以低得多的频率发生......它几乎消除了它......但它也有一些奇怪的副作用,比如玩家在产卵后经常向右移动一点。
尽管添加了这些代码,但在我最新的视频中它只出现了 3 倍......
- 在视频中的 13 秒标记处死亡后
- 在视频中的 16 秒标记处死亡后
- 在视频中的 1 分 13 秒处死亡后
这是此尝试的视频。 YouTube 链接
有人对修复有任何想法吗?
我猜测某种方式可以“重置”或“刷新”SpriteKit 认为正在被联系的内容……但是这种情况发生的方式和原因也让我感到困扰。