collision avec SpriteKit

heliohelio Membre

Bonsoir à  tous,


 


je fais des tests avec SpriteKit et j'ai commencé à  essayer de réaliser un pong avec l'aide d'un tuto de raywenderlich.com, cependant les collisions ne fonctionnent qu'avec les côtés et le paddle de gauche, le paddle de droite n'est pas détecté, voici le code :



import SpriteKit


let ballCategory : UInt32 = 0x1 << 0 // 00000000000000000000000000000001
let cornerCategory : UInt32 = 0x1 << 1 // 00000000000000000000000000000010
let PaddleCategory : UInt32 = 0x1 << 2 // 00000000000000000000000000000100


class GameScene: SKScene, SKPhysicsContactDelegate {

var isPlaying = false
var ball = SKShapeNode()
var paddleLeft = SKShapeNode()
var paddleRight = SKShapeNode()
var startGameInfoNode = SKLabelNode()
var restartGameNode = SKLabelNode()
var isFingerOnPaddle = false

var speedupTimer = NSTimer()

override func didMoveToView(view: SKView) {
/* Setup your scene here */

self.backgroundColor = UIColor.darkGrayColor()

let score1 = "0"
let score2 = "0"

let labelSc1 = SKLabelNode()
labelSc1.text = score1
labelSc1.fontSize = 34
labelSc1.fontColor = SKColor.whiteColor()
labelSc1.position = CGPoint(x: (size.width/3), y: size.height-34)
addChild(labelSc1)


let labelSc2 = SKLabelNode()
labelSc2.text = score2
labelSc2.fontSize = 34
labelSc2.fontColor = SKColor.whiteColor()
labelSc2.position = CGPoint(x: (size.width)*(2/3), y: size.height-34)
addChild(labelSc2)

self.physicsWorld.gravity = CGVectorMake(0, 0)
self.physicsWorld.contactDelegate = self
let physicsBody = SKPhysicsBody (edgeLoopFromRect: self.frame)
self.physicsBody?.categoryBitMask = cornerCategory

self.physicsBody?.dynamic = false
self.physicsBody?.friction = 0
self.physicsBody = physicsBody


paddleLeft.path = UIBezierPath(roundedRect: CGRect(x: 0, y: -48, width: 16, height: 96), cornerRadius: 0).CGPath
paddleLeft.physicsBody = SKPhysicsBody(rectangleOfSize: paddleLeft.frame.size)

paddleLeft.position = CGPoint(x: 1, y: CGRectGetMidY(frame))
paddleLeft.fillColor = UIColor.brownColor()
paddleLeft.strokeColor = UIColor.whiteColor()
paddleLeft.physicsBody?.categoryBitMask = PaddleCategory
paddleLeft.lineWidth = 1
self.paddleLeft.physicsBody?.dynamic = false
addChild(paddleLeft)


paddleRight.path = UIBezierPath(roundedRect: CGRect(x: CGRectGetMaxX(self.frame)-(paddleRight.frame.width)-18, y: -48, width: 16, height: 96), cornerRadius: 0).CGPath
paddleRight.physicsBody = SKPhysicsBody(rectangleOfSize: paddleRight.frame.size)
paddleRight.position = CGPoint(x: 1, y: CGRectGetMidY(frame))
paddleRight.fillColor = UIColor.brownColor()
paddleRight.strokeColor = UIColor.whiteColor()
paddleRight.physicsBody?.categoryBitMask = PaddleCategory
paddleRight.lineWidth = 1
self.paddleRight.physicsBody?.dynamic = false
addChild(paddleRight)


let shape = SKShapeNode()
shape.path = UIBezierPath(roundedRect: CGRect(x: -3, y: 0, width: 6, height: self.frame.height), cornerRadius: 0).CGPath
shape.position = CGPoint(x: CGRectGetMidX(frame), y: 0)
shape.fillColor = UIColor.brownColor()
shape.strokeColor = UIColor.whiteColor()
shape.lineWidth = 1
addChild(shape)

startGameInfoNode = SKLabelNode()

startGameInfoNode.fontColor = SKColor.whiteColor()
startGameInfoNode.fontSize = 30.0
startGameInfoNode.position = CGPointMake(size.width / 2.0, size.height / 2.0);
startGameInfoNode.text = "Tap to start!"
addChild(startGameInfoNode)

}

func startGame () {

self.isPlaying = true
self.startGameInfoNode.hidden = true
self.restartGameNode.hidden = false

ball = SKShapeNode(circleOfRadius: 12) // Size of Circle

ball.strokeColor = SKColor.whiteColor()
// Circle.glowWidth = 1.0
ball.fillColor = SKColor.orangeColor()
ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.frame.size.height/2)
ball.physicsBody?.categoryBitMask = ballCategory
ball.physicsBody?.contactTestBitMask = cornerCategory | PaddleCategory
ball.physicsBody?.dynamic = true
ball.physicsBody?.restitution = 1.0
ball.physicsBody?.linearDamping = 0.0
ball.physicsBody?.angularDamping = 0.0
ball.physicsBody?.friction = 0.0

ball.position = CGPointMake(frame.midX, frame.midY) //Middle of Screen

self.addChild(ball)

ball.physicsBody?.velocity = CGVectorMake(150, -150)

speedupTimer = NSTimer.scheduledTimerWithTimeInterval(1.0, target:self, selector: #selector(GameScene.speedUpTheBall), userInfo: nil, repeats: true)

}

func restartGame () {

self.isPlaying = false
self.startGameInfoNode.hidden = false
self.restartGameNode.hidden = true

}

func speedUpTheBall () {
let velocityX = ball.physicsBody!.velocity.dx * 1.02
let velocityY = ball.physicsBody!.velocity.dy * 1.02
ball.physicsBody!.velocity = CGVectorMake(velocityX, velocityY)

}


override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */

isFingerOnPaddle = true
}

override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {

if isPlaying {

if isFingerOnPaddle {

let touch = touches.first! as UITouch
let touchLocation = touch.locationInNode(self)
let previousLocation = touch.previousLocationInNode(self)

var paddleY = paddleLeft.position.y + (touchLocation.y - previousLocation.y)

paddleY = max(paddleY, paddleLeft.frame.size.height/2)
paddleY = min(paddleY, size.height - paddleLeft.frame.size.height/2)

paddleLeft.position = CGPointMake(paddleLeft.position.x, paddleY)
}
} else {
self.startGame()
// print("startGame")
}
}

override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
isFingerOnPaddle = false
}


func didBeginContact(contact: SKPhysicsContact) {
var firstBody: SKPhysicsBody
var secondBody: SKPhysicsBody

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


if firstBody.categoryBitMask == ballCategory && secondBody.categoryBitMask == cornerCategory {
print("collision")
}

if firstBody.categoryBitMask == ballCategory && secondBody.categoryBitMask == PaddleCategory {
print("collision paddle ")

}

}

override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */

}


}


Auriez-vous une idée du problème ?


Merci.


Réponses

  • CéroceCéroce Membre, Modérateur
    La méthode didBeginContact est-elle appelée ?
    D'expérience, on se trompe souvent avec les masques.
  • heliohelio Membre
    mars 2016 modifié #3

    Oui elle est bien appelée, j'ai le message collision paddle dans la console mais uniquement quand le paddle de gauche rentre en contact avec la balle, et rien du côté du paddle de droite !


  • CéroceCéroce Membre, Modérateur
    Ma question est plutôt: "Est-elle appelée pour le paddle de droite ?".

    Inspecte le résultat AU DEBOGUEUR !!!
    Les logs ne te donnent qu'une information: que ça ne marche pas. Ce qu'il faut savoir, c'est si la méthode est appelée sur le paddle de droite. Si elle est appelée, alors ce sont tes tests sur les masques qui sont foireux. Si elle n'est pas appelée, alors tu as mal réglé les physics bodies pour le paddle de droite ou la balle.
  • LexxisLexxis Membre
    mars 2016 modifié #5

    Bonjour,


    je penses que tes deux pads sont à  la même position (regardes la propriété position de test deux pads). Le fait que ton pad droit soit affiché à  droite est tout à  fait normal puisque tu renseignes la propriété path avec les coordonnées ou tu veux que ton pad soit affiché (mais pas où ton objet est réellement positionné).

     


    La méthode convenience init(path path: CGPath,centered centered: Bool) permet de center le CGPath sur l'origine du node. Il est plus simple de le positionner par la suite:

     



    ...
    let path = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: 16, height: 96), cornerRadius: 0).CGPath
    ... 
    paddleRight = SKShapeNode(path: path, centered: true)
    paddleRight.position = <position de mon pad>

    SKView dispose d'un mode de debug pour l'utilisation de la "physique", il suffit de renseigner la propriété showsPhysics à  true (activante à  partie de la classe GameViewController si tu parts du template Apple).


     


    En tout cas je penses que cela peut être une piste.


  • heliohelio Membre

    OK merci,


    alors je n'ai pas la possibilité d'inspecter la code avant ce soir, mais effectivement comme le dit Lexxis les pads semblent bien être au même endroit ! j'ai l'application d'installée et j'ai remarqué qu'en mettant le pad gauche tout en haut, il y a un contact de la balle avec un objet transparent au milieu du côté gauche qui est en fait l'image du pad droit !


  • DrakenDraken Membre
    mars 2016 modifié #7

    J'ai testé ton source avec le simulateur, pour voir, sans plonger dedans en détail.


     


    Une chose me chiffonne : la balle rebondit en touchant les bords hauts et  bas, alors qu'elle disparait en touchant les bordures droites et gauches, pour réapparaà®tre quelques instants plus tard. A croire que le rectangle de collision est plus grand que l'écran, sur l'axe horizontal. L'effet se produit dans les deux orientations du device, mais il est plus perceptible en mode portrait. Bref, la collision avec les bords de l'écran est aussi à  revoir.


     


    Testé avec Xcode 7.3 sur le simulateur (iPhone 5, iPhone 6 et iPad 2).


     


    La courbe d'accélération de la sphère me semble un peu curieuse. Tu augmentes la vitesse de 2% chaque seconde sans aucune limite. Elle se traine au début, pour devenir plus rapide qu'une balle après un certain temps, ne laissant même aucune chance même à  un chat accro au café et boosté à  l'adrénaline. Tu devrais prévoir une valeur maximale à  la vélocité du projectile.

  • heliohelio Membre

    Pour l'instant c'était juste un test, mais effectivement il y a pas mal de choses à  revoir si je veux que cela soit jouable !


    Pour la collision avec les bords de l'écran, si tu mets ce code :



    scene.size = skView.bounds.size

    dans viewDidLoad de GameViewController.swift, il n'y a plus de problème.


     


    A+


  • DrakenDraken Membre
    mars 2016 modifié #9

    Tu ajustes manuellement la taille de la scène à  celle de l'écran physique au lancement de l'application. Oui, c'est logique. Je pensais que le template de SpriteKit le faisait automatiquement depuis plusieurs versions d'XCode. Manifestement non..


     


    EDIT : Je viens de modifier le source, avec un résultat inattendu. La balle se colle sur le coté droit et se déplace uniquement sur l'axe vertical.

Connectez-vous ou Inscrivez-vous pour répondre.