任何帮助将不胜感激。我对此比较陌生,只是觉得我开始了解编码。
我的问题-----
我很难尝试使用 SpriteKit 教程解决问题,我一直在增强该教程作为磨练和提高新手技能的一种方式。
当我的播放器“坠毁”在地面上时,我遇到了多次接触。
不止一个生命被移除,这导致我的游戏生成“尝试添加一个已经有父节点的 SKNode:”错误,将玩家添加回场景。我在代码中能想到的任何地方都尝试过 player.removeFromParent。
只要我在空中“撞”上“敌人”,游戏就完美无缺。一旦玩家接触到地面,一切就结束了。如果我在与地面接触时“杀死”玩家,没有问题,但我希望游戏继续,只要玩家仍然有生命,无论是接触地面还是与敌人接触。
我真的相信如果可以解决多接触问题,这个问题就会得到解决。
class GameScene: SKScene, SKPhysicsContactDelegate {
// Set up the Texure Atlases
var images = SKSpriteNode()
var textureAtlas = SKTextureAtlas()
var textureArray = [SKTexture]()
var touchingScreen = false
var obstacle = SKSpriteNode()
// Generates a Random number between -350 and 350 (the center of the axis being 0)
let rand = GKRandomDistribution(lowestValue: -350, highestValue: 350)
var timer: Timer?
// This method is called when the Game Launches
override func didMove(to view: SKView) {
// Adds a pixel perfect physicsBody to the player
player.physicsBody = SKPhysicsBody(texture: player.texture!, size: player.texture!.size())
player.physicsBody?.categoryBitMask = 1
player.position = CGPoint(x: -400, y: 275)
// Disables the affects of a collisions (+pushing, rotation etc.) on the player when a collision with another SKSpriteNode occurs
player.physicsBody?.allowsRotation = false
player.physicsBody?.collisionBitMask = 0
addChild(player)
physicsWorld.gravity = CGVector(dx: 0, dy: -2)
physicsWorld.contactDelegate = self
timer = Timer.scheduledTimer(timeInterval: 1.5, target: self, selector: #selector(createEnemy), userInfo: nil, repeats: true)
}
// This method is called when the User touches the Screen
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
touchingScreen = true
}
// This method is called when the User stops touching the Screen
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
touchingScreen = false
}
// This method is called before each frame is rendered
override func update(_ currentTime: TimeInterval) {
// Constrains the player to the scene area
if player.position.y > 275 {
player.position.y = 275
}
// Moves the player up when the screen is being touched
if touchingScreen {
player.physicsBody?.velocity = CGVector(dx: 0, dy: 200
)
}
}
func didBegin(_ contact: SKPhysicsContact) {
// Exits the method if either node is nil (doesn't exist)
guard let nodeA = contact.bodyA.node else { return }
guard let nodeB = contact.bodyB.node else { return }
// Check to see if either node is player and, if so, call the playerHit method and pass in the other node
if nodeA == player {
playerHit(nodeB)
} else if nodeB == player {
playerHit(nodeA)
}
}
func createEnemy() {
// Check for Bonus Creation
checkForBonusCreation()
// Choose a Random Enemy
let pickEnemy = Int(arc4random_uniform(3))
switch pickEnemy {
case 0:
obstacle = SKSpriteNode(imageNamed: "enemy-balloon")
animateBalloon()
case 1:
obstacle = SKSpriteNode(imageNamed: "enemy-bird")
animateBird()
case 2:
obstacle = SKSpriteNode(imageNamed: "enemy-plane")
animatePlane()
default:
return
}
// Positions the enemy
obstacle.zPosition = -2
obstacle.position.x = 768
obstacle.size = (CGSize(width: obstacle.size.width * 0.7, height: obstacle.size.width * 0.7))
// Prevents the obstacle from being spawned too low on the scene
if obstacle.position.y < -150 {
obstacle.position.y = -150
}
addChild(obstacle)
// Adds pixel perfect collision detection to the enemies
obstacle.physicsBody = SKPhysicsBody(texture: obstacle.texture!, size: obstacle.texture!.size())
// Then we set isDynamic to false so grivty will not affect the obstacles
obstacle.physicsBody?.isDynamic = false
// Assigns 1 to it's contactTestBitMask so that it know to detect a collision with the player
obstacle.physicsBody?.contactTestBitMask = 1
// Names the obstacle so we can track collisions properly
obstacle.name = "enemy"
// Spawn an enemy at a random y-axis
obstacle.position.y = CGFloat(rand.nextInt())
// Moves the obstacles across to and off the left hand side of the screen over 9 seconds athen removes thier nodes so they don't chew up memory
let move = SKAction.moveTo(x: -768, duration: 9)
let remove = SKAction.removeFromParent()
let action = SKAction.sequence([move, remove])
obstacle.run(action)
}
func playerHit(_ node: SKNode) {
if node.name == "enemy" {
player.removeFromParent()
node.removeFromParent()
lives -= 1
balloonPop()
showLivesRemaining()
} else if node.name == "ground" {
player.removeFromParent()
lives -= 1
balloonPop()
showLivesRemaining()
}
}
func balloonPop() {
player.removeFromParent()
}
func showLivesRemaining() {
if lives >= 3 {
lives = 3
} else if lives <= 0 {
lives = 0
player.removeFromParent()
}
}
// Jump to the restart or quit scene
func restartGame() {
player.removeFromParent()
let wait = SKAction.wait(forDuration: 2.0)
let showPlayer = SKAction.run {
player.position = CGPoint(x: -400, y: 275)
self.addChild(player)
}
let sequence = SKAction.sequence([wait, showPlayer])
run(sequence)
}
// Displays GAME OVER and takes the player to the "Restart or Quit" choice scene
func endGame() {
player.removeFromParent()
let gameOver = SKSpriteNode(imageNamed: "game-over")
gameOver.zPosition = 15
addChild(gameOver)
// Waits 2 seconds and fade into the Restart Scene
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
if let scene = RestartScene(fileNamed: "RestartScene") {
scene.scaleMode = .aspectFill
self.view?.presentScene(scene, transition: SKTransition.crossFade(withDuration: 1.5))
}
}
}
}