我正在处理一个我一直在努力修复的错误,
我的游戏包含一个与平台部分交互的播放器精灵......标准平台游戏。 我通过将薄物理块附加到更大的平台物理体(大白块)来设置我的游戏
每个瘦块代表:
我可以使用自定义类配置打开或关闭其中每个。
错误说明
所以我现在正在处理一个有趣的错误。 它发生的方式是我的玩家精灵击中一个死区物理体(杀死一个角色的区域)。
通过revive()函数恢复角色后会出现错误。 看来doBeginContact被调用并且相信我的角色与墙壁接触。即使看不到墙体物理体。
如果我在角色复活后开始快速按下跳跃,则只会出现错误。在按下跳转之前等待一段时间会导致错误发生。
联系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)
}
}
请注意,该功能尚未完成......(缺少播放器 - 墙左(见下文),播放器 - 墙右)
我知道didBeginContact正在被调用,因为调试控制台发出了一个print语句,我在发生错误时从函数的Wall - Player部分编写。它打印&#34;与墙壁doBegin&#34; 联系,当您联系左侧或右侧墙壁时打印。
//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类中调用包含此代码的函数: / p>
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)
然而,这使得游戏玩法变得不那么流畅。
我不确定究竟是什么问题?
如果我从一个级别移除所有墙体物理主体,只有表面物理实体,我也可以防止发生错误......
我的猜测是,在调用didStimulatePhysics之前,它与SpriteKit的渲染循环首次调用didEvaluateActions有关......
所以didEvaluateActions将Player精灵移回到开头。 也许玩家在搬回墙壁时会碰到墙壁?
也许刺激物理学还没有完全赶上? 也许循环需要再次运行才能识别出玩家的物理团体已经移动了吗?
我有点失落,并且一直试图修复它。
这是一个展示该错误的视频。 YouTube Link
视频中的第一级
视频显示没有墙体物理主体的水平上没有发生的错误。
第二级
然后视频显示它发生在下一个级别,它有墙体物理机构。
等
什么没有用
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()上重新创建它。奇怪的是,在测试过程中,我可以直观地看到在死区接触时移除的物理矩形......然后重新出现......它仍然没有解决问题。
somePlayer.physicsBody = nil
第3。尝试重置播放器的contactTestMask ..通过在deadZone联系人上将其设置为零,然后在revive()上将其重置为默认值。
e.g。关于DeadZone联系
somePlayer.physicsBody?.contactTestBitMask = 0
e.g。 on 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。对每个玩家使用return语句 - 如果是playerIsDead,则使用___联系人退出范围。
e.g。在左墙联系
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)
什么减少了错误的频率
,同时仍然设置 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倍......
以下是此次尝试的视频。 YouTube Link
任何人对修复都有任何想法吗?
我猜测某些方法&#34;重置&#34;或&#34;冲洗&#34; SpriteKit认为正在联系的是什么......但是这种情况发生的原因和原因也让我感到困扰。
答案 0 :(得分:0)
所以我终于找到了修复...... 它不是超级优雅的。
如果有人有更好的解决方案。请发布它们,我喜欢一个不那么混乱的解决方案,或许更有意义的东西,因为这个解决方案对我如何设计东西施加了一些限制。
class GameScene: SKScene, SKPhysicsContactDelegate {
var contactDetectionEnabled = true
var wallContactDetectionEnabled = true
var wallPhysicsOnNodePosition:CGPoint?
如果存在,则为wallPhysicsOnNodePosition分配一个值,否则它将保持为零。
我在didMove(to :)中为wallPhysicsOnNodePosition指定了一个值
self.enumerateChildNodes(withName: "//*") {
node, stop in
if (node.name == "WallPhysicsOnNode") {
self.wallPhysicsOnNodePosition = node.position
node.isHidden = true
print("assigning wallPhysicsOnNodePosition")
}
}
class Player: SKSpriteNode {
var jumpedOccured = false // turned on via update() while isJumping //
var fallingOccured = false // turned on via update() while isFalling //
@objc(didBeginContact:) func didBegin(_ contact: SKPhysicsContact) {
if contactDetectionEnabled {
@objc(didEndContact:) func didEnd(_ contact: SKPhysicsContact) {
if contactDetectionEnabled {
if wallContactDetectionEnabled {
//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{
if somePlayer.playerIsDead {
return
}
defer {
self.contactDetectionEnabled = false
self.wallContactDetectionEnabled = false
killPlayer(somePlayer)
}
}
}
override func update(_ currentTime: TimeInterval) {
/* Called before each frame is rendered */
if (transitionInProgress == false) {
thePlayer.update()
thePlayer2.update()
defer {
if thePlayer.jumpedOccured && !thePlayer.playerIsDead && thePlayer.touchOccured && thePlayer.fallingOccured && !contactDetectionEnabled {
contactDetectionEnabled = true
}
if thePlayer.jumpedOccured && !thePlayer.playerIsDead && thePlayer.touchOccured && thePlayer.fallingOccured && !wallContactDetectionEnabled {
if let wallOnNodePosUnwrapped = wallPhysicsOnNodePosition {
if thePlayer.position.x >= wallOnNodePosUnwrapped.x {
wallContactDetectionEnabled = true
print("Player passed wallPhysicsOnNodePosition ... so \(wallContactDetectionEnabled)")
}
} else if wallPhysicsOnNodePosition == nil {
wallContactDetectionEnabled = true
print("No wallPhysicsOnNodePosition ... but wallContactDetectionEnabled is \(wallContactDetectionEnabled)")
}
}
}
还有一点......但你几乎可以搞清楚......
使用Swifts defer {} 能力确实是我解决方案的关键。
几乎没有必要使用wallContactDetectionEnabled ......但我偶尔也会得到这个bug,所以我添加了那个部分并使用wallPhysicsOnNodePosition来控制它,以防bug仍然存在。
但是,对于所有级别,我都会在壁跳跃首次发生的位置添加一个WallPhysicsOnNode。
再一次,解决方案远非理想的......它有点混乱,我可能会在以后清理它......但它的工作正常。
希望有更好的东西......但是我尝试了很多逻辑应该有用的东西,但没有。直接添加上面的代码不应该是逻辑上必要的。我很震惊,我需要额外的if声明封装了墙 - 玩家联系方式。没有它就很难发生这个错误...但由于某些原因它偶尔也会发生。
希望有更优雅的东西......因为这是一个真正的胶带解决方案&#34;。但是,至少它是有效的。
如果有人有任何意见,请随时分享。 我的回答可能有效......但它只能防止这种情况发生。它在技术上并没有触及到底发生的事情。
这可能会在将来引发问题。