2 votes

Comment choisir le nœud avec lequel je veux entrer en contact, lorsqu'une collision se produit avec deux nœuds à la fois avec SpriteKit ?

J'ai une application iOS codée en Swift 3 où une balle est tirée et rebondit sur des briques à l'écran. Si la brique est un PhysicsBody (un rectangle), je ne peux pas facilement déterminer quel côté/coin de la brique est touché. Ce que j'ai décidé de faire à la place, c'est que chaque côté de la brique soit un nœud distinct. Le problème que j'ai maintenant, c'est qu'une balle ne peut pas être en contact avec deux noeuds (disons le gauche et le bas) à la fois. Je diminue la valeur de la brique après chaque contact avec la balle, qui à son tour diminue la valeur de 2 pour ce seul contact. Comment puis-je faire en sorte que si une balle touche deux nœuds, le code ne soit exécuté que pour un seul contact ?

Parfois, le code ci-dessous est exécuté deux fois, et la balle entre en contact avec deux brickNodes à chaque fois.

func didBegin(_ contact: SKPhysicsContact) {
    var firstBody:SKPhysicsBody
    var secondBody:SKPhysicsBody

    let countPoint = true

    if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
        firstBody = contact.bodyA
        secondBody = contact.bodyB
    } else {
        firstBody = contact.bodyB
        secondBody = contact.bodyA
    }

    if (firstBody.categoryBitMask & ballCategory) != 0 {
        if (firstBody.node != nil && secondBody.node != nil){
            if (secondBody.categoryBitMask & brickCategory) != 0  {
                ballDidHitBrick(ballNode: firstBody.node as! SKShapeNode, brickNode: secondBody.node as! SKShapeNode, decreasePoint: countPoint)
            } else if (secondBody.categoryBitMask & roofCategory) != 0 || (secondBody.categoryBitMask & rightWallCategory) != 0 || (secondBody.categoryBitMask & leftWallCategory) != 0 || (secondBody.categoryBitMask & bottomCategory) != 0 {
                ballDidHitWall(ballNode: firstBody.node as! SKShapeNode, wallNode: secondBody.node as! SKShapeNode)
            } else {
                //Nothing as of yet
            }
        }
    }
}

1voto

D Gray Points 21

En suivant ce que Steve a dit plus haut, j'ai implémenté le code ci-dessous et je n'ai plus de double contact par mise à jour :

if !bricksHit.contains("\(secondBody.node?.name ?? ""), \(firstBody.node?.name ?? "")") {
    //If ball hasnt hit the object more than once
    bricksHit.append("\(secondBody.node?.name ?? ""), \(firstBody.node?.name ?? "")")

    ballDidHitBrick(ballNode: firstBody.node as! SKShapeNode, brickNode: secondBody.node as! SKShapeNode, decreasePoint: countPoint, contact: contact)
}

J'ai également ajouté l'élément ci-dessous à mon code, qui efface le bircksHit après chaque mise à jour :

override func didFinishUpdate() {
    bricksHit.removeAll()
}

0voto

Knight0fDragon Points 12881

Je supprimerais les nœuds multiples avec des corps multiples, cela donnerait des performances terribles si vous avez beaucoup de blocs.

Au contraire, vous devez traiter votre travail par étapes.

Pendant votre didBegin phase, vous devez garder la trace de l'endroit où se trouve le point de contact. Cela peut être fait avec userData

func didBegin(_ contact: SKPhysicsContact) {
    var firstBody:SKPhysicsBody
    var secondBody:SKPhysicsBody

    let countPoint = true

    if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
        firstBody = contact.bodyA
        secondBody = contact.bodyB
    } else {
        firstBody = contact.bodyB
        secondBody = contact.bodyA
    }

    if (firstBody.categoryBitMask & ballCategory) != 0,  (secondBody.categoryBitMask & brickCategory) != 0  {
        let userData = firstBody.node!.userData ??  [String,AnyObject]()
        let contactPoints = userData["contactPoints"] as? [CGPoint] ?? [CGPoint]()
        contactPoints.append(contact.contactPoint) //if need be add other info like the vector or relative to the node
       userData["contactPoints"] = contactPoints

    }
}

Puis dans un processus ultérieur, comme didSimulatePhysics Vous pouvez évaluer les nœuds qui ont été contactés, déterminer la priorité du contact (comme le fond l'emporterait sur les côtés, ou si la vitesse x > vitesse y, les côtés, tout ce que vous devez faire) et réagir de cette manière.

Notez que ce n'est qu'un exemple de code, il ne fonctionnera pas textuellement. Vous devrez le structurer dans votre code pour qu'il fonctionne correctement.

-1voto

Steve Ives Points 181

Oui, ça arrive. Le consensus de l'opinion semble être que s'il y a de multiples points de contact simultanés entre 2 corps physiques, SK appellera didBegin pour chaque point de contact, ce qui entraîne de multiples appels (dans la boucle du jeu Sam) pour la même paire de corps physiques.

La façon de gérer cela (vous ne pouvez pas faire en sorte que sprite-kit n'appelle PAS didBegin plusieurs fois dans certaines circonstances) est de vous assurer que votre code de contact s'adapte à cela et que la gestion du contrat plusieurs fois ne cause pas de problème (comme l'ajout au score plusieurs fois, la suppression de plusieurs vies, la tentative d'accéder à un nœud ou à un physicsBody qui a été supprimé, etc.)

Voici quelques exemples de ce que vous pouvez faire :

  • Si vous supprimez un nœud qui est contacté, vérifiez qu'il soit nul avant de avant de le supprimer (pour les contacts en double).

  • Ajoutez le nœud à un ensemble et supprimez ensuite tous les nœuds de l'ensemble en didFinishUpdate

  • Ajout d'un drapeau "inactif" aux données utilisateur du nœud.

  • Faites du noeud une sous-classe de SKSpriteNode et ajoutez une propriété 'inactive' (ou similaire).

  • Etc etc.

Voir cette question et réponse sur les appels de SK didBegin plusieurs fois pour un même contact :

Sprite-Kit enregistrant des collisions multiples pour un seul contact

De plus, SKPhysicsContact contient non seulement les détails des 2 corps physiques qui sont entrés en collision, mais aussi le point de contact. À partir de ce point et des propriétés de position des deux nœuds concernés, vous pouvez calculer le côté/coin de la brique qui est touché.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X