繁体   English   中英

didBegin函数在IOS 11.3上无法正常工作

[英]didBegin function doesn't work well on IOS 11.3

我使用swift开发了一个游戏,在将系统更新为iPhone的IOS 11.3之前,它运行得非常好。 在我的游戏中,当子弹与敌人接触时,将立即删除两个SKSpriteNode,并且变量“ gameScore”将按预期方式加1。 但是现在,每当子弹与敌人接触时,“ gameScore”将被添加一个比1大得多的数字(取决于SKSpriteNode的速度)。

因此,我猜想在删除了SKSpriteNode之后,didBegin函数仍然被调用。 似乎didBegin功能存在时间延迟。 每个人都会遇到同样的问题吗?

    func didBegin(_ contact: SKPhysicsContact) {

    var body1 = SKPhysicsBody()
    var body2 = SKPhysicsBody()

    if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
        body1 = contact.bodyA
        body2 = contact.bodyB
    } else {
        body1 = contact.bodyB
        body2 = contact.bodyA
    }

    if body1.categoryBitMask == physicscategories.Player && body2.categoryBitMask == physicscategories.Bullet {

        gameScore += 1
        gameLabel1.text = "SCORE: \(gameScore)"
        body1.node?.removeFromParent()
        body2.node?.removeFromParent()

    }
}  

我找不到我对这个问题进行详尽解释的答案,我认为它已经存在于文档中,因此,如果有人找到重复的副本,请随时将其标记为此类。

首先,让我们尝试了解联系人的工作方式。

在物理阶段,将为您的节点创建一个池,该池列出了其接触的所有接触点。 该池将保留您的所有节点。

例如

let pool : [SKPhysicsContact] = [{node1.side1,node2.side1},{node1.side1,node2.side2}]

然后,我们遍历所有接触点并调用didBegin函数。

for contact in pool
{
   scene.didBegin(contact)
}

现在,我们输入您提供的代码:

func didBegin(_ contact: SKPhysicsContact) {

    var body1 = SKPhysicsBody()
    var body2 = SKPhysicsBody()

    if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
        body1 = contact.bodyA
        body2 = contact.bodyB
    } else {
        body1 = contact.bodyB
        body2 = contact.bodyA
    }

    if body1.categoryBitMask == physicscategories.Player && body2.categoryBitMask == physicscategories.Bullet {

        gameScore += 1
        gameLabel1.text = "SCORE: \(gameScore)"
        body1.node?.removeFromParent()
        body2.node?.removeFromParent()

    }
}  

如果我要将您的代码内联到for循环中,它将最终看起来像这样:

for contact in pool
{

    var body1 = SKPhysicsBody()
    var body2 = SKPhysicsBody()

    if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
        body1 = contact.bodyA
        body2 = contact.bodyB
    } else {
        body1 = contact.bodyB
        body2 = contact.bodyA
    }

    if body1.categoryBitMask == physicscategories.Player && body2.categoryBitMask == physicscategories.Bullet {

        gameScore += 1
        gameLabel1.text = "SCORE: \(gameScore)"
        body1.node?.removeFromParent()
        body2.node?.removeFromParent()

    }
}

现在您可以看到,调用body1.node?.removeFromParent()不会做任何事情来阻止for循环发生两次。 要做的所有事情就是将parent设置为nil,但是contact和node仍然存在,从而使下一个循环成功。

因此,我们需要做的是如何防止循环再次处理。

现在有几种方法可以做到这一点:

1)检查父母是否为零:

func didBegin(_ contact: SKPhysicsContact) {
    guard let _ = contact.bodyA.node.parent else {return}

这是可行的,但是如果bodyA.node的某个位置变为零,我们的代码将崩溃。

2)检查节点或父节点是否为零:

func didBegin(_ contact: SKPhysicsContact) {
    guard let _ = contact.bodyA.node?.parent else {return}

现在我们知道我们的代码是安全的。 哦,不,我们有两个不同的节点同时发生碰撞,并且该节点已从场景中移除,如何处理这两个节点,我只是移除了该节点?

3)将节点移到临时位置,检查节点是否在删除节点中,并在最终更新时进行清理:

let removalNode = SKNode()
func didBegin(_ contact: SKPhysicsContact) {
    var body1 : SKPhysicsBody!
    var body2 : SKPhysicsBody!

    if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
        body1 = contact.bodyA
        body2 = contact.bodyB
    } else {
        body1 = contact.bodyB
        body2 = contact.bodyA
    }


    if body1.categoryBitMask == physicscategories.Player && 
       body2.categoryBitMask == physicscategories.Bullet && 
       body1.node?.parent != removalNode{

           gameScore += 1
           gameLabel1.text = "SCORE: \(gameScore)"
           removalNode.addChild(body1)
           removalNode.addChild(body2)
    }  
}

func didFinishUpdate(){
    removalNode.removeAllChildren()
}

现在让我们看一下我们在游戏中放置了一个令牌,并且我们的游戏规则是:如果玩家同时击中令牌和子弹,则玩家得分仍然会提高10。在当前设置下,我们现在可以这样做:

let removalNode = SKNode()
func didBegin(_ contact: SKPhysicsContact) {
    var body1 : SKPhysicsBody!
    var body2 : SKPhysicsBody!

    if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
        body1 = contact.bodyA
        body2 = contact.bodyB
    } else {
        body1 = contact.bodyB
        body2 = contact.bodyA
    }


    if body1.categoryBitMask == physicscategories.Player && 
       body2.categoryBitMask == physicscategories.Bullet && 
       body1.node?.parent != removalNode{

           gameScore += 1
           gameLabel1.text = "SCORE: \(gameScore)"
           removalNode.addChild(body1)
           removalNode.addChild(body2)
    }  
    if body1.categoryBitMask == physicscategories.Player && 
       body2.categoryBitMask == physicscategories.Token &&
       body2.node?.parent != removalNode{

           gameScore += 10
           gameLabel1.text = "SCORE: \(gameScore)"
           removalNode.addChild(body2)
    }  

}

func didFinishUpdate(){
    removalNode.removeAllChildren()
}

现在,让我们看一下先打子弹,然后打标记的示例。

联系->玩家命中子弹
确实开始了:
玩家还活着
玩家击中子弹=真
游戏得分提高
玩家死了
子弹死了
玩家命中令牌=错误
结束开始
联系->玩家命中令牌
确实开始了:
玩家死了
玩家击中子弹=错误
玩家击中令牌=真
Gamescore增加10
结束开始

现在游戏结束了,游戏总得分增加了11,这是我们游戏制定的规则。

但是,如果游戏规则是:如果玩家同时击中子弹并同时击中了代币,则分数不加? 您可能会认为“让我们检查一下玩家是否在removalNode中而不添加分数。那么您会错了,因为如果管道碰巧是令牌然后是子弹怎么办。

联系->玩家命中令牌
确实开始了:
玩家还活着
玩家击中子弹=错误
玩家击中令牌=真
Gamescore增加10
结束开始
联系->玩家命中子弹
确实开始了:
玩家还活着
玩家击中子弹=真
游戏得分提高
玩家死了
子弹死了
玩家命中令牌=错误
结束开始

分数现在是11,应该是1,这违反了我们的游戏规则。

我们该如何解决?

好吧,我们改为将得分转移到didFinishUpdate方法上,并使用userData标记令牌:

let removalNode = SKNode()
func didBegin(_ contact: SKPhysicsContact) {
    var body1 : SKPhysicsBody!
    var body2 : SKPhysicsBody!

    if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
        body1 = contact.bodyA
        body2 = contact.bodyB
    } else {
        body1 = contact.bodyB
        body2 = contact.bodyA
    }


    if body1.categoryBitMask == physicscategories.Player && 
       body2.categoryBitMask == physicscategories.Bullet && 
       body1.node?.parent != removalNode{

           removalNode.addChild(body1)
           removalNode.addChild(body2)
    }  
    if body1.categoryBitMask == physicscategories.Player && 
       body2.categoryBitMask == physicscategories.Token &&
       body2.node.parent != removalNode
       //Make sure userData is allocated
       if body1.userData = nil {body1.userData = [String:NSObject]()}
       // If body.userData[tokenScore] is nil, default to 0 then add 10
       body1.userData[tokenScore] = (body1.userData[tokenScore] ?? 0) + 10
       removalNode.addChild(body2)
    }  

}

func didFinishUpdate(){
    if player.parent = removalNode
    {
      gameScore += 1
    }
    else
    {
      //if userdata exists and token score has a value, then add it, otherwise add 0
      gameScore += player.usedData?["tokenScore"] ?? 0
    }
    gameLabel1.text = "SCORE: \(gameScore)"

    removalNode.removeAllChildren()
}

现在,我们的游戏规则规定,仅当玩家不在removeNode中时才添加令牌。

另外,如果您不想使用userData ,则可以始终检查“ removalNode”子代中有多少令牌,并相应地添加分数。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM