2014-06-15 16 views
10

In un gioco iOS che utilizza il kit Sprite insieme al rilevamento dei contatti nel motore fisico di Sprite Kit, diminuisco di un numero le vite dell'eroe ogni volta che entra in contatto con un nemico. Questo viene fatto dal metodo didBeginContact. Tuttavia, sembra che quel metodo non venga chiamato solo una volta, quando il contatto inizia, ma chiamato continuamente finché l'Eroe e il nemico si sovrappongono: quando imposto un breakpoint in quel metodo, posso vedere che è l'esatto istanze del corpo fisico stesso esistenti come contact.bodyA e contact.bodyB. Il risultato è che l'Eroe perderà più vite, anche se passa solo un singolo nemico.Perché beginContact viene chiamato più volte?

Se l'Eroe incontra lo stesso nemico più tardi, dovrebbe ottenere un altro sottotitolo dal vivo, e quindi non posso semplicemente mantenere un hash seenEnemies impostato per affrontare il problema sopra.

La domanda è ora: come faresti a garantire che solo un live sia sottratto per ogni contatto Eroe/nemico?

+0

Vorrei assumere c'è una 'didEndContact' pure. – dandan78

+0

Le domande di aiuto per il debug ("perché non funziona questo codice?") Devono includere il comportamento desiderato, un problema specifico o un errore e il codice più breve necessario per riprodurlo nella domanda stessa. Le domande senza una chiara affermazione di problemi non sono utili agli altri lettori. – LearnCocos2D

+0

La documentazione di riferimento afferma che il metodo viene chiamato solo quando inizia il contatto. L'ho letto come "una sola volta per contatto". Apparentemente, non è questo il caso. La documentazione di riferimento è veramente sbagliata a questo punto? – someName

risposta

13

Ho avuto lo stesso problema (il punteggio aumenta più volte per un singolo nemico distrutto e più punti vita persi per una singola istanza di danno). Un utente nei forum Apple pensa che sia a bug in [SKPhysicsBody bodyWithTexture:size:] ma non credo che sia il caso, perché stava accadendo anche con altri costruttori.

Prima di tutto, il categoryBitMask e il contactTestBitMask sono molto importanti, ovviamente. Date un'occhiata a SpriteKit Physics Collisions sample code di Apple:

// I contatti sono spesso un problema di doppia spedizione; l'effetto che vuoi è basato sul tipo di entrambi i corpi nel contatto. Questo esempio questo in un modo forza bruta, controllando i tipi di ciascuno. Un esempio più complicato potrebbe utilizzare metodi sugli oggetti per eseguire il controllo dei tipi.

// I contatti possono apparire in entrambi gli ordini, quindi normalmente è necessario controllare l'uno contro l'altro.In questo esempio, i tipi di categoria sono ben ordinati, quindi il codice scambia i due corpi se sono fuori servizio. Ciò consente al codice di verificare solo le collisioni una volta.

Quello che ho fatto per risolverlo è stato impostare una bandiera dopo aver gestito ogni condizione. Nel mio caso, stavo testando se bodyA.node.parent era pari a didBeginContact, perché ho chiamato removeFromParent() sui nodi missile/nemico per distruggerli.

Penso che dovresti aspettarti che l'evento si attivi più volte e il tuo codice deve assicurarsi che venga elaborato una sola volta.

+1

Usare il costruttore di 'SKPhysicsBody (rectangleFrame: ') ha risolto il mio problema, grazie! Speriamo che risolvono questo bug –

1

Qui è un'opzione che rende il giocatore invulnerabile dopo essere stato colpito per un tempo stabilito:

A. Creare una variabile che rende il giocatore invulnerabile a perdere una vita dopo essere stato colpito per alcuni secondi.

  1. Creare una variabile booleana globale denominata isInvuln (impostata su FALSE) e un NSTimeInterval chiamato invulnTime.
  2. Nel metodo che gestisce il giocatore e il nemico che stabilisce il contatto, controlla se IsInvuln è Falso prima di prendere una vita. (se isInvuln è vero ... non fare nulla)
  3. Se isInvuln è falso, prendi una vita quindi imposta isInvuln su true.

    if(self.isInvuln == FALSE){ 
         self.player.lives-=1; 
         self.isInvuln = True;} 
    
  4. Aggiungi alla tua updateWithCurrentTime:

    if(self.isInvuln==True){ 
    self.invulnTime += timeSinceLast;} 
    
    if (self.invulnTime > 3) {    
        self.isInvuln = FALSE:} 
        self.invulnTime= 0; 
    

Questo farà in modo che quando un nemico e il giocatore si scontrano, il giocatore perde una vita e diventa invulnerabile 3 secondi. Dopo 3 secondi, il giocatore può subire ancora danni. Se il nemico contatta il giocatore entro i 3 secondi invulnerabili, il metodo di contatto non fa nulla. Spero che questo aiuti idee scintilla per affrontare il tuo problema.

+2

Ho finito per rendere l'eroe immune per un periodo al primo contatto, aggirando così il problema.Tuttavia, la domanda originale rimane senza risposta: è possibile che BeginContact venga licenziato continuamente finché c'è un contatto, e se questo è il caso, quale sarebbe una buona strategia per registrare solo il primo contatto con il pugno (e re-enigere le azioni di contatto di nuovo, una volta che l'eroe esce dalla zona di contatto)? – someName

+0

Prova a riattivare il contatto usando didEndContact (invece di usare un'ora) che farebbe esattamente quello che stai cercando. Non posso parlare di didEndContact perché non l'ho usato veramente. – meisenman

+0

Si potrebbe anche rendere l'oggetto che prende una vita in contatto con una sottoclasse di SKSpriteNode con un valore booleano che è possibile impostare su false dopo il primo contatto. Quando avviene il contatto, se il booleano è vero, prendi una vita e imposta il valore booleano su false. Se il booleano è falso ... non fare nulla – meisenman

5

Mi sono imbattuto nello stesso problema. Nel mio caso lo didBeginContact() è stato chiamato più volte (ho contato fino a 5 volte) per un contatto di un proiettile con il nemico. Dato che il proiettile è un semplice formato circolare, sono d'accordo con @SFX che non può essere un bug solo in Texture-Bodies. I test hanno dimostrato che non era possibile chiamare update() tra le chiamate didBeginContact(). Quindi la soluzione è semplice (Swift):

var updatesCalled = 0 
... 
internal update() { 
    updatesCalled ++ 
} 
... 
internal func didBeginContact(contact: SKPhysicsContact) { 
    NSLog("didBeginContact: (\(contact.contactPoint.x), \(contact.contactPoint.y)), \(updatesCalled)") 
    if(updatesCalled == 0) {return} // No real change since last call 
    updatesCalled = 0 
    ... your code here ... 
} 

ho provato didEndContact(), ma che non si chiamava affatto. Non ho investigato ulteriormente su questo.

BTW: Ho appena acceso da Android, e sono impressionato dalla facilità e la stabilità di questo sistema :-)

0

Nella mia esperienza, didEndContact & didBeginContact sono entrambi chiamato più volte, mentre gli oggetti si sovrappongono. Questo sta accadendo anche in SceneKit usando iOS 9, quindi devo presumere che sia un comportamento previsto.

1

ho capito facile soluzione:

basta cambiare il valore categoryBitMask sia del corpo di 0 o valore non utilizzato subito dopo ha rilevato il contatto.

Ad esempio:

if (firstBody.categoryBitMask == padCategory && secondBody.categoryBitMask == colorBallCategory) { 

     secondBody.categoryBitMask = 0; 

     // DO OTHER THING HERE 

} 
10

Il motivo per cui la didBeginContact viene licenziato più volte è perché si dispone di più punti di contatto che accadono su forme concave.

Se guardate l'immagine qui sotto, vedrete che ho 2 sprite, una stella nera e un rettangolo rosso. Quando la stella nera colpisce il rettangolo rosso, la colpisce su più punti, cerchiata in blu. Sprite Kit eseguirà quindi una chiamata per ogni intersezione di linea, in modo che lo sviluppatore possa utilizzare la variabile contactPoint per ciascuno di questi contatti.

enter image description here

+0

Buon punto, questo potrebbe accadere anche su forme semplici. Ad esempio quando un cerchio interseca una linea, nella maggior parte dei casi ci sono due punti di contatto. – salocinx

+0

LOL, chi ha votato questo – Knight0fDragon

+0

Non ho capito. Mi piace molto la tua illustrazione :) Anche se penso che ci siano almeno 4 punti di contatto nella tua illustrazione anziché solo 2 ;-p – salocinx

Problemi correlati