如何解决Swift / SpriteKit Collision产生不同的结果
我有一个小测试项目(我的第一个使用Swift / XCode)是为了使我脱离HTML5和Canvas进行游戏制作。
代码可以编译并正常运行。我将iPhone用作测试设备,而不是内置的模拟器。
问题的症状是
在某些情况下,碰撞可以正常进行,并产生外星爆炸,并从场景中移除外星精灵节点。
我将外星节点命名为“ alien”,将激光节点命名为“ laser”。 两者的contactTestBitMask设置为相同的值。
这是我的GameScene.swift代码:
import SpriteKit
class GameScene: SKScene,SKPhysicsContactDelegate {
var lastUpdateTime: TimeInterval = 0
var delta: TimeInterval = 0
var sp_player: SKSpriteNode!
var stars: SKSpriteNode!
var deeperstars: SKSpriteNode!
var laser: SKSpriteNode!
var alien: SKSpriteNode!
var explosionSplat1: SKSpriteNode!
var playerscore: UInt32!
struct PhysicsCategory {
static let base:UInt32 = 0x1 << 0
static let alien:UInt32 = 0x1 << 1
static let laser:UInt32 = 0x1 << 2
static let player:UInt32 = 0x1 << 3
}
override func didMove(to view: SKView) { // called when the scene is presented into view (happens only once)
playerscore = 0
physicsWorld.contactDelegate = self
physicsWorld.gravity = .zero
// BACKGROUND
backgroundColor = UIColor(red: 0/255,green: 0/255,blue: 48/255,alpha: 1.0)
print("Background color is set")
// WRAP THE STARFIELDS
// Front most layer of stars
let starsTexture = SKTexture(imageNamed: "stars.png")
let bgAnimation = SKAction.move(by: CGVector(dx: 0,dy: -starsTexture.size().height),duration: 5)
let bgReset = SKAction.move(by: CGVector(dx: 0,dy: starsTexture.size().height),duration: 0)
let bgConstantMotion = SKAction.repeatForever(SKAction.sequence([bgAnimation,bgReset]))
// Back layer of slower stars
let deeperStarsTexture = SKTexture(imageNamed: "stars-deeper.png")
let deeperStarsbgAnimation = SKAction.move(by: CGVector(dx: 0,dy: -deeperStarsTexture.size().height),duration: 8)
let deeperStarsbgReset = SKAction.move(by: CGVector(dx: 0,dy: deeperStarsTexture.size().height),duration: 0)
let deeperStarsbgConstantMotion = SKAction.repeatForever(SKAction.sequence([deeperStarsbgAnimation,deeperStarsbgReset]))
var i: CGFloat = 0
while i < 3
{
stars = SKSpriteNode(texture: starsTexture)
stars.position = CGPoint(x: frame.midX,y: starsTexture.size().height * i)
stars.size.height = frame.height
stars.run(bgConstantMotion)
stars.zPosition = -1
addChild(stars)
deeperstars = SKSpriteNode(texture: deeperStarsTexture)
deeperstars.position = CGPoint(x: frame.midX,y: deeperStarsTexture.size().height * i)
deeperstars.size.height = frame.height
deeperstars.run(deeperStarsbgConstantMotion)
deeperstars.zPosition = -1
addChild(deeperstars)
i += 1
}
// PLAYER
let playerTexture1 = SKTexture(imageNamed: "player-1.png")
let playerTexture2 = SKTexture(imageNamed: "player-2.png")
let playerAnimation = SKAction.animate(with: [playerTexture1,playerTexture2],timePerFrame: 0.2)
let constantAnimation = SKAction.repeatForever(playerAnimation)
sp_player = SKSpriteNode(texture: playerTexture1)
sp_player.position = CGPoint(x: frame.midX,y: (sp_player.size.height * 2))
sp_player.physicsBody = SKPhysicsBody(rectangleOf: sp_player.size)
sp_player.physicsBody!.isDynamic = false
sp_player.name = "player"
sp_player.run(constantAnimation)
addChild(sp_player)
// PLACE ALIENS
let alienTexture1 = SKTexture(imageNamed: "alien-1a.png")
let alienTexture2 = SKTexture(imageNamed: "alien-1b.png")
let alienAnimation = SKAction.animate(with: [alienTexture1,alienTexture2],timePerFrame: 0.4)
let constantAlienAnimation = SKAction.repeatForever(alienAnimation)
var x: CGFloat = 0,y: CGFloat = 0
while y < 6
{
while x < 6
{
alien = SKSpriteNode(texture: alienTexture1)
alien.position = CGPoint(x: 32 + (x * alien.size.width),y: (frame.size.height - (alien.size.height * 1.5) - (alien.size.height * y)))
print("Setting y to \(frame.size.height - (alien.size.height * y))")
alien.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: alien.size.width,height: alien.size.height))
alien.physicsBody!.isDynamic = false
alien.name = "alien"
alien.physicsBody!.contactTestBitMask = PhysicsCategory.laser
alien.run(constantAlienAnimation)
addChild(alien)
x += 1
}
y += 1
x = 0
}
print("Sprites added to scene")
spawnLasers()
}
func spawnLasers()
{
let delay1 = SKAction.wait(forDuration: 0.5)
let spawn = SKAction.run {
let laserTexture = SKTexture(imageNamed: "laser-1.png")
self.laser = SKSpriteNode(texture: laserTexture)
self.laser.position = CGPoint(x: self.sp_player.position.x,y: self.sp_player.position.y + self.sp_player.size.height)
self.laser.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: self.laser.size.width,height: self.laser.size.height))
self.laser.physicsBody!.isDynamic = true
self.laser.physicsBody!.lineardamping = 0
self.laser.physicsBody!.allowsRotation = false
self.laser.physicsBody!.contactTestBitMask = PhysicsCategory.laser
self.laser.name = "laser"
self.addChild(self.laser)
let shoot = SKAction.moveto(y: self.frame.size.height,duration: 1)
let killLaser = SKAction.removeFromParent()
let handleLaser = SKAction.sequence([shoot,killLaser])
self.laser.run(handleLaser)
}
let action = SKAction.sequence([delay1,spawn])
let constantLasers = SKAction.repeatForever(action)
self.run(constantLasers)
}
func didBegin(_ contact: SKPhysicsContact) {
var check: UInt32 = 0
if contact.bodyA.node != nil
{
check += 1
}
if contact.bodyB.node != nil
{
check += 1
}
if check == 2
{
if contact.bodyA.node!.name == "alien" && contact.bodyB.node!.name == "laser"
{
// EXPLOSION
let explosionSplatTexture1 = SKTexture(imageNamed: "explosion-1a.png")
let explosionSplatTexture2 = SKTexture(imageNamed: "explosion-1b.png")
let explosionSplatTexture3 = SKTexture(imageNamed: "explosion-1c.png")
let explosionSplatTexture4 = SKTexture(imageNamed: "explosion-1d.png")
let explosionSplatanimation = SKAction.animate(with: [explosionSplatTexture1,explosionSplatTexture2,explosionSplatTexture3,explosionSplatTexture4],timePerFrame: 0.1)
let killExplosion = SKAction.removeFromParent()
let explosionSequence = SKAction.sequence([explosionSplatanimation,killExplosion])
explosionSplat1 = SKSpriteNode(texture: explosionSplatTexture1)
explosionSplat1.name = "explosion"
explosionSplat1.position = CGPoint(x: contact.bodyA.node!.position.x,y: contact.bodyA.node!.position.y)
addChild(explosionSplat1)
explosionSplat1.run(explosionSequence)
self.playerscore += 1
print("score: \(self.playerscore!)")
contact.bodyA.node?.removeFromParent()
print("Alien named \(contact.bodyA.node?.name ?? "defaultAlienName") from scene")
contact.bodyB.node?.removeFromParent()
print("Laser named \(contact.bodyB.node?.name ?? "defaultLaserName") from scene")
}
}
}
func didEnd(_ contact: SKPhysicsContact) {
//print("Contact ended between \(contact.bodyA) and \(contact.bodyB)")
}
override func touchesBegan(_ touches: Set<UITouch>,with event: UIEvent?)
{
// if let touch = touches.first {
// let position = touch.location(in: view)
// storedTouch = position
// }
}
override func touchesMoved(_ touches: Set<UITouch>,with event: UIEvent?)
{
if let touch = touches.first {
let position = touch.location(in: view)
var playerpos: CGPoint!
playerpos = sp_player.position
let pl_move = SKAction.move(to: CGPoint(x: position.x,y: playerpos.y),duration: 0.1)
sp_player.run(pl_move)
}
}
override func touchesEnded(_ touches: Set<UITouch>,with event: UIEvent?)
{
/*
Need to figure out how to use storedTouch properly
to move player relative to the screen touch co-ordinates
*/
// if let touch = touches.first {
// let position = touch.location(in: view)
// }
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
if (lastUpdateTime > 0)
{
delta = currentTime - lastUpdateTime
} else {
delta = 0
}
lastUpdateTime = currentTime
}
}
游戏运行时会显示以下屏幕:
您可以在此处看到意外的激光行为: {{3}}
score: 1
Alien named defaultAlienName removed from scene
Laser named laser removed from scene
score: 2
Alien named defaultAlienName removed from scene
Laser named laser removed from scene
score: 3
Alien named defaultAlienName removed from scene
Laser named laser removed from scene
score: 4
Alien named defaultAlienName removed from scene
Laser named defaultLaserName removed from scene
score: 5
Alien named defaultAlienName removed from scene
Laser named defaultLaserName removed from scene
score: 6
Alien named defaultAlienName removed from scene
Laser named defaultLaserName removed from scene
这很可能是我完全不了解可选选项以及碰撞实际上是如何工作的。如有任何见解,我将不胜感激。
解决方法
在您的外来循环以及spawnLasers()中,您没有为子画面节点提供实际的PhysicsBody类别。为了让子画面能够检测彼此之间的联系,它们需要一个类别名称和一个联系人名称。
因此,在您的while循环(构建外星人)中,您需要具备以下条件:
alien.physicsBody!.categoryBitMask = PhysicsCategory.alien
alien.physicsBody!.contactTestBitMask = PhysicsCategory.laser
在spawnLasers()中,您希望添加此内容:
self.laser.physicsBody!.categoryBitMask = PhysicsCategory.laser
,然后将contactTestBitMask更改为Alien,而不是Laser:
self.laser.physicsBody!.contactTestBitMask = PhysicsCategory.alien
希望您能看到外星人类别想知道何时激光触摸,而激光类别想知道外星人何时触摸。
要在视觉上帮助您,请打开“显示物理”选项,这样您可以查看要处理的实际物理物体。 为此,请在您的GameViewController(或类似版本)中找到:
showsFPS = true
showsNodeCount = true
您要添加以下内容:
showsPhysics = true
这将有助于在屏幕上查看实际的物理物体。
在:
func didBegin(_ contact: SKPhysicsContact)
您仅测试BodyA是外星人,而BodyB是激光。 这是接触测试:
if contact.bodyA.node!.name == "alien" && contact.bodyB.node!.name == "laser"
我相信BodyA可以是激光,而BodyB可以是外星人。基本上,物理引擎的接触事件可以是“ A击中B”或“ B击中A”。
因此,一种快速而肮脏的解决方案是在当前语句的下面创建另一个if语句,但是更改主体名称,因此:
if contact.bodyA.node!.name == "laser" && contact.bodyB.node!.name == "alien" {
并从现有的if语句复制代码,并更改两个print语句。 这不是理想的方法,但是希望当您整理它时,您将了解物理接触的作用。
我希望一旦您实现了以上所述,您的状况就会好得多。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。