swift - juego de sprite kit rápido cuando disparo a un enemigo a veces la bala atraviesa al enemigo, ¿cómo puedo solucionarlo?
sprite-kit skphysicsbody (4)
Estoy haciendo un juego en el kit de sprites (2D)
.
Tengo este código:
meteor.physicsBody = SKPhysicsBody(rectangleOfSize: enemy.size)
y tengo una imagen de meteorito que necesitas destruir, pero a veces, cuando disparo en mi dispositivo al meteoro, la bala atraviesa el meteoro. ¿Es esto un error o hice algo mal? y ¿Cómo puedo solucionar este problema?
gracias por leer mi problema, ¡espero que alguien pueda ayudarme! si no entiendes mi pregunta, por favor comenta lo que no entiendes.
func fireBullet() {
let bullet = SKSpriteNode(imageNamed: "bullet")
bullet.name = "Bullet"
bullet.setScale(2.9)
bullet.position = player.position
bullet.zPosition = 1
bullet.physicsBody = SKPhysicsBody(rectangleOfSize: bullet.size)
bullet.physicsBody!.affectedByGravity = false
bullet.physicsBody!.categoryBitMask = PhysicsCategories.Bullet
bullet.physicsBody!.collisionBitMask = PhysicsCategories.None
bullet.physicsBody!.contactTestBitMask = PhysicsCategories.Meteor
self.addChild(bullet)
let moveBullet = SKAction.moveToY(self.size.height + bullet.size.height, duration: 1)
let deleteBullet = SKAction.removeFromParent()
let bulletSequence = SKAction.sequence([bulletSound, moveBullet, deleteBullet])
bullet.runAction(bulletSequence)
}
func spawnMeteor(){
let randomXStart = random(min: CGRectGetMinX(gameArea), max: CGRectGetMaxX(gameArea))
let randomXEnd = random(min: CGRectGetMinX(gameArea), max: CGRectGetMaxX(gameArea))
let startPoint = CGPoint(x: randomXStart, y: self.size.height * 1.2)
let endPoint = CGPoint(x: randomXEnd, y: -self.size.height * 0.2)
let Meteor = SKSpriteNode(imageNamed: "Meteor/(arc4random_uniform(2))")
Meteor.name = "Meteor"
Meteor.setScale(0.2)
Meteor.position = startPoint
Meteor.zPosition = 2
Meteor.physicsBody = SKPhysicsBody(rectangleOfSize: meteor.size)
Meteor.physicsBody!.affectedByGravity = false
Meteor.physicsBody!.categoryBitMask = PhysicsCategories.Meteor
Meteor.physicsBody!.collisionBitMask = PhysicsCategories.None
Meteor.physicsBody!.contactTestBitMask = PhysicsCategories.Player | PhysicsCategories.Bullet
self.addChild(Meteor)
let moveMeteor = SKAction.moveTo(endPoint, duration: 2)
let deleteMeteor = SKAction.removeFromParent()
let loseALifeAction = SKAction.runBlock(loseALife)
let MeteorSequence = SKAction.sequence([moveMeteor, deleteMeteor, loseALifeAction])
if currentGameState == gameState.inGame{
Meteor.runAction(MeteorSequence)
}
let dx = endPoint.x - startPoint.x
let dy = endPoint.y - startPoint.y
let amountToRotate = atan2(dy, dx)
enemy.zRotation = amountToRotate
}
func didBeginContact(contact: SKPhysicsContact) {
var body1 = SKPhysicsBody()
var body2 = SKPhysicsBody()
if contact.bodyA.collisionBitMask < contact.bodyB.categoryBitMask{
body1 = contact.bodyA
body2 = contact.bodyB
}
else{
body1 = contact.bodyB
body2 = contact.bodyA
}
if body1.categoryBitMask == PhysicsCategories.Player && body2.categoryBitMask == PhysicsCategories.Meteor{
//if the player has hit the meteor
if body1.node != nil {
spawnExplosion(body1.node!.position)
}
if body2.node != nil {
spawnExplosion(body2.node!.position)
}
body1.node?.removeFromParent()
body2.node?.removeFromParent()
runGameOver()
}
if body1.categoryBitMask == PhysicsCategories.Bullet && body2.categoryBitMask == PhysicsCategories.Meteor && body2.node?.position.y < self.size.height {
//if the bullet has hit the meteor
addScore()
if body2.node != nil{
spawnExplosion(body2.node!.position)
spawnPlusScore(body2.node!.position)
}
body1.node?.removeFromParent()
body2.node?.removeFromParent()
}
Creo que su problema está dentro del código de la bala:
let moveBullet = SKAction.moveToY(self.size.height + bullet.size.height, duration: 1)
let deleteBullet = SKAction.removeFromParent()
let bulletSequence = SKAction.sequence([bulletSound, moveBullet, deleteBullet])
bullet.runAction(bulletSequence)
Trate de reemplazar con bullet.physicsBody?.applyImpulse(CGVectorMake( 0, 20))
lugar de SKAction.moveToY
y elimine la viñeta si sale de la pantalla
Creo que una forma más clara de codificar didBeginContact es la siguiente:
func didBeginContact(contact: SKPhysicsContact) {
let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
switch contactMask
case PhysicsCategories.Player | PhysicsCategories.Meteor :
// The player and the meteor have contacted
spawnExplosion(contact.contactPoint) //Create explosion where they touch
contact.bodyA.node.removeFromParent()
contact.bodyB.node.removeFromParent()
runGameOver()
case PhysicsCategories.Bullet| PhysicsCategories.Meteor :
// The bullet and meteor have contacted
// body2.node?.position.y < self.size.height // ?? Not sure what this is
addScore()
spawnExplosion(contact.contactPoint) //Create explosion where they touch
contact.bodyA.node.removeFromParent()
contact.bodyB.node.removeFromParent()
default:
print("Other contact detected")
}
Cambié tu código para crear una explosión en el lugar donde tocan los dos cuerpos; si quieres 2 explosiones, cada una centrada en los nodos que tocan, usa:
spawnExplosion(contact.bodyA.node.position)
spawnExplosion(contact.bodyB.node.position)
o:
for node in [contact.bodyA.node, contact.bodyB.node] {
spawnExplosion(node.position)
}
En lugar de configurar tus collisionBitMasks a cero, intenta configurarlas así:
bullet.physicsBody!.collisionBitMask = PhysicsCategories.Meteor
Meteor.physicsBody!.collisionBitMask = PhysicsCategories.Bullet
Intenta cambiar el comienzo de tu método didBeginContact a esto:
var body1 = contact.bodyA.node as! SKSpriteNode
var body2 = contact.bodyB.node as! SKSpriteNode
if (body1.categoryBitMask == PhysicsCategories.Player && body2.categoryBitMask == PhysicsCategories.Meteor) || (body2.categoryBitMask == PhysicsCategories.Player && body1.categoryBitMask == PhysicsCategories.Meteor) {
//if the player has hit the meteor
spawnExplosion(body1.position)
spawnExplosion(body2.position)
body1.removeFromParent()
body2.removeFromParent()
runGameOver()
}
No puedo decir que la spawnExplosion funcionará porque no puedo ver lo que hace, pero esto debería eliminar los nodos.
SpriteKit no realiza una detección de colisión precisa porque tiene como objetivo lograr un rendimiento más rápido. Puedes hacer que la propiedad usesPreciseCollisionDetection
en true, así:
bullet.physicsBody!.usesPreciseCollisionDetection = true
https://developer.apple.com/reference/spritekit/skphysicsbody/1520014-usesprecisecollisiondetection
No sé si funciona, por favor avíseme.