8
votes

BOGUE: Test de frappe avec des nœuds de frère et la propriété UserActionAcédigActive dans le kit Sprite

Bug - hit-test ne fonctionne pas comme prévu lorsque les frères et sœurs se chevauchent:

Il y a 2 noeuds se chevauchant dans une scène qui ont le même parent (ie. Frères et sœurs)

Le noeud le plus haut a userInteractionEnabled = NO tandis que l'autre noeud a userInteractionEnabled = YES .

Si le recouvrement est touché, après le nœud le plus haut est testé hit et échoue (parce que userInteractionEnabled = NO ), au lieu du nœud inférieur étant le prochain à tester coup, il est sautée et le parent des 2 frères et sœurs est testé hit.

Que faut-il que le prochain frère (le nœud du bas) est frappé testé plutôt que le saut hit-test pour le parent.

Selon la documentation Sprite Kit:

"Dans une scène, quand Sprite Kit traite les événements tactiles ou de la souris, il marche la scène pour trouver le noeud le plus proche que veut accepter l'événement. Si ce nœud ne veut pas l'événement, Sprite contrôles Kit nœud suivant le plus proche, et ainsi au. L'ordre dans lequel le test-hit est traitée est essentiellement l'inverse de l'ordre de dessin. Pour un nœud à considérer lors d'essais à succès, sa propriété userInteractionEnabled doit être réglé sur OUI. La valeur par défaut est NO pour un noeud quelconque à l'exception d'un noeud de scène. "


Ceci est un bug comme frères et soeurs d'un noeud sont rendus avant que leurs parents - frères et soeurs devrait être le prochain à tester, et non son parent. De plus, si un nœud a userInteractionEnabled = NO , alors sûrement il devrait être « transparent » en ce qui concerne-test hit - mais ici, ce n'est pas car il se traduit par un changement de comportement en tant que nœud est sauté dans le test.

J'ai recherché en ligne, mais ne peut pas trouver quelqu'un ou relève également envoyer un message sur ce bug. Alors, dois-je le signaler?


Et la raison pour laquelle je l'ai posté ici parce que je voudrais une suggestion pour une « solution » de ce bug (ie. Une suggestion pour une mise en œuvre d'un code quelque part afin SpriteKit fonctionne dans le « but » de manière à-test de recherche)


Pour reproduire le bogue:

Utilisez le projet « Bonjour tout le monde » modèle fourni lorsque vous démarrez un nouveau « jeu » dans Xcode (il a « Bonjour tout le monde » et ajoute sprites à la roquette lorsque vous cliquez sur).

En option: [I également supprimé l'image de sprite de fusée du projet que le rectangle avec le X qui se produit lorsque l'image ne se trouve pas est plus facile de travailler avec pour le débogage, visuellement ]

Ajouter un SKSpriteNode à la scène avec userInteractionEnabled = YES (je vais parler comme le nœud A partir de maintenant).

Exécuter le code.

Vous remarquerez que lorsque vous cliquez sur le nœud A, pas de sprites roquettes sont pondus. (Comportement attendu depuis le hit-test doit arrêter après avoir réussit -. Il arrête car il réussit sur le nœud A)

Cependant, si vous engendrez quelques roquettes qui sont à côté de nœud A, puis cliquez sur un endroit où le nœud A et un chevauchement de roquettes, il est alors possible de faire apparaître une autre roquette sur le dessus du nœud A - mais shouldn » t être possible. Cela signifie qu'après le coup-test échoue sur le nœud le plus élevé (la fusée qui a userInteractionEnabled = NO par défaut), au lieu de tester le nœud A côté, il teste le parent de la fusée à la place qui est la scène.


Note:. J'utilise Xcode 7.3.1, Swift, iOS - Je ne l'ai pas testé pour voir si ce bug est universel, mais


détail supplémentaire. Je l'ai fait une déboguage (légère complication à la réplication ci-dessus) et a déterminé que le hit-test est envoyé au parent par la suite et donc pas nécessairement la scène


3 commentaires

J'apprécierais toute aide et toutes les aides avec ce problème que j'ai - je n'étais pas démarrer mon nouveau projet tant que cela est trié car toutes sortes de bogues peuvent survenir grâce à celle-ci lorsque des nœuds se chevauchent. D'autres d'autres ont-ils remarqué / expérimenté ce bug lorsqu'ils font des projets aussi, car il est assez fondamental?


Je ne sais pas si je considérerais cela un bug ... dépend vraiment du contexte de jeu de manière préférable. Je pouvais facilement imaginer un jeu où si un sprite bloque l'autre, il est logique que celui-ci ne soit pas appelé, mais une action générique de scène est vérifiée.


@Gor non - ça doit être un bug. Le comportement va à l'encontre de ce que dit la documentation, ce qui signifie qu'il est inattendu. En outre, il ne fait aucun sens logique, car la propriété userActionAcédigAcédenAcled (code> est destinée à représenter la transparence en ce qui concerne le test de frappe, ce qui échoue à cause de ce comportement.


3 Réponses :


2
votes

La divergence semble se produire car l'instance SKVIEW SKVIEW a son IgnoressiblingOrderOrderner SET TO TRUE dans la mise en œuvre de ViewDidLoad sur gameviewController . Cette propriété est false par défaut.

de la DOCS:

Une valeur booléenne qui indique si les relations parent-enfant et la frère de sotage affectent l'ordre de rendu des nœuds dans la scène. La valeur par défaut est false , ce qui signifie que lorsque plusieurs nœuds partagent la même position Z, ces nœuds sont triés et rendus dans un ordre déterministe. Les parents sont rendus avant leurs enfants et les frères et sœurs sont rendus dans l'ordre du tableau. Lorsque cette propriété est définie sur true , la position des nœuds de l'arborescence est ignorée lors de la détermination de l'ordre de rendu. L'ordre de rendu des nœuds de la même position Z est arbitraire et peut changer chaque fois qu'un nouveau cadre est rendu. Lorsque le sœur et la commande parent sont ignorés, Spritekit applique des optimisations supplémentaires pour améliorer les performances de rendu. Si vous avez besoin de nœuds pour être rendu dans un ordre spécifique et déterministe, vous devez définir la position Z de ces nœuds.

Donc, dans votre cas, vous devriez simplement supprimer cette ligne pour obtenir le comportement habituel. Comme indiqué par @fujia dans les commentaires, cela ne devrait affecter que l'ordre de rendu et ne pas toucher les tests. < / p>

Comme UIKIT, les descendants directs de UIREPONTER Mettez probablement en œuvre ses méthodes de manipulation tactile afin de transférer des événements sur la chaîne du répondeur. Cette incohérence peut donc être causée par les implémentations remplacées de ces méthodes sur sknode . Si vous êtes raisonnablement sûr que le problème réside avec ces méthodes héritées, vous pouvez contourner la question en les priant avec votre propre logique de transfert d'événement. Si tel est le cas, il serait également agréable de produire un rapport de bogue avec votre projet de test.


7 commentaires

Ignoressiblingorder n'affecte que le comportement du rendu, ne pas toucher les tests.


Je suis d'accord avec @fujia - j'ai testé avec / sans Ignoressiblingorder et cela n'a fait aucune différence. Même si c'était le cas, et que l'ordre des frères et sœurs a été échangé, la survenue ci-dessus n'aurait pas eu lieu - le test Hit-Test n'aurait pas dû ignorer un nœud de frère de soeur de toute façon.


Dans ce cas, je vous recommanderais simplement de vérifier la mise en œuvre de votre nœud de votre enfant de la méthode uireponder . Si celles-ci sont correctes, cependant, cela peut en effet être un bug Sprantikit.


@Erikfoss Comment puis-je faire ça? Je n'ai plus ajouté de code.


Si vous ne faites pas plus de priorité, vous devriez obtenir le comportement attendu. En général, des descendants directs de uiresponder au sein de Uikit (qui devrait inclure sknode ) fournir des implémentations remplacées de ses méthodes de traitement des événements tactiles. Donc, si vous avez mis votre code à un exemple isolé et que vous voyez toujours le mauvais comportement, je vous recommanderais de déposer un radar avec celui-ci.


Oui - il n'y a plus rien au code que ce que j'ai dit pour reproduire le bogue. Cependant, d'après ce que vous avez dit, il est peut-être possible de remplacer le code dans la mise en œuvre de l'uiresponder directement pour résoudre ce problème?


Sûr. Bien que ce soit une solution de contournement, vous pourriez certainement les remplacer pour faire la commande vous-même. C'est à peu près l'approche fournie par la réponse de @ epsilon.



2
votes

Vous pouvez contourner le problème en écrasant votre scène Mousedown (ou des événements tactiles équivalents) comme ci-dessous. Fondamentalement, vous vérifiez les nœuds au point et trouvez celui qui possède la zosition la plus élevée et l'utilisateurInterActionAcédigraft. Cela fonctionne comme des retombées pour la situation lorsque vous n'avez pas de note aussi la position la plus élevée pour commencer.

override func mouseDown(theEvent: NSEvent) {
    /* Called when a mouse click occurs */
    let nodes = nodesAtPoint(theEvent.locationInNode(self))

    var actionNode : SKNode? = nil
    var highestZPosition = CGFloat(-1000)

    for n in nodes
    {
        if n.zPosition > highestZPosition && n.userInteractionEnabled
        {
            highestZPosition = n.zPosition
            actionNode = n
        }
    }

    actionNode?.mouseDown(theEvent)
}


3 commentaires

Merci. Je suppose que le tableau nœuds est commandé dans la commande qu'ils doivent être vérifiés (du parent Bottommost qui est la scène sur le nœud supérieur)? Cependant, cela ne fonctionne certainement pas bien: la scène MouseDown n'est pas appelée lorsqu'il y a des nœuds à un point car un clic ne "perce pas" à travers tout.


Bien sûr ça marche. S'il y a un nœud à l'emplacement qui a UsilInterActionAcédée, le MouseDown de ce nœud va être appelé. Si le plus haut noeud à cet emplacement est quelque chose qui a UsilInterActionAcédenabled = False, alors la scène est appelée comme vous l'avez remarquée et ainsi cette fonction. S'il n'y a pas de nœuds, l'emplacement, la scène est appelée aussi. Cela fonctionne, c'est essayer. Quant à la question de la commande des nœuds; C'est pourquoi les zositions sont comparées dans la boucle.


1. Lorsque les positions Z sont identiques, la commande de nœud doit être comparée par ordre de rendu - qui manque sûrement moins que nodesatpoint (...) donne le bon ordre. 2. Le problème que je pense, c'est que ce «patch» ne sera appelé que lorsque le MouseDown '/ code> est appelé. Si j'ajoute un nœud «couverture» couvrant la scène entière et ajustez le code afin que tout soit ajouté à cette «couverture» au lieu de la scène, le problème susmentionné se produira toujours. MOUSEDOWN n'est pas nécessairement appelé si le nœud le plus haut ne l'a pas comme le parent.



3
votes

Je soupçonne que c'est un bogue ou la documentation est incorrecte. De toute façon, voici une solution de contournement qui peut être ce que vous recherchez.

On dirait que vous souhaitez interagir avec un nœud qui peut être p>

  1. obscurci par un ou plusieurs nœuds qui ont userInterAcédigAcédenableD code> Configuration de la propriété sur False code> Li>
  2. Un enfant d'un "fond" nœud li>
  3. profond dans l'arborescence du nœud li> ol>

    nodesatpoint code> est un bon point de départ. Il renvoie une gamme de nœuds qui receverent le point de prise. Ajoutez ceci au touchesbegan code> et filtrez les nœuds qui n'ont pas userinterAcédenAcéder code> défini sur true code> par p> xxx

    À ce stade, vous pouvez trier la matrice de nœuds par zposition code> et em> profondeur de nœud. Vous pouvez utiliser l'extension suivante pour déterminer ces propriétés d'un nœud: p> xxx pré>

    et trier le tableau avec p> xxx pré>

    à Testez le code ci-dessus, définissez une sous-classe code> SKSpritenode code> qui permet une interaction utilisateur p> xxx pré>

    et ajoutez les gestionnaires tactiles suivants au skscene code > Sous-classe P>

    var selectedNode:SKNode?
    
    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        if let touch = touches.first {
            let location = touch.locationInNode(self)
            // Sort and filter nodes that intersect with location
            let nodes = nodesAtPoint(location)
                .filter {$0.userInteractionEnabled}
                .sort {$0.depth.z == $1.depth.z ? $0.depth.level > $1.depth.level : $0.depth.z > $1.depth.z}
            // Forward the touch events to the appropriate node
            if let first = nodes.first {
                first.touchesBegan(touches, withEvent: event)
                selectedNode = first
            }
        }
    }
    
    override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
        if let node = selectedNode {
            node.touchesMoved(touches, withEvent: event)
        }
    }
    
    override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
        if let node = selectedNode {
            node.touchesEnded(touches, withEvent: event)
            selectedNode = nil
        }
    }
    


0 commentaires