我正在使用Xcode开发应用程序,目前我面临有关SKSpriteNodes的问题。当有多个SKSpriteNode且触摸该节点时,不会删除该触摸的节点,但会删除另一个未触摸的节点。我还注意到,当屏幕上有多个节点时,只有来自屏幕顶部的最新节点被删除,而其他节点仍在向下移动,尽管它们被触摸了。有人可以帮忙确定为什么会发生这种情况以及防止此类错误的方法吗?
作为参考,我已经包含了该错误所在的类。
import SpriteKit
import GameplayKit
class GameScene: SKScene {
private var gameCounter: Int = 0
private var currentLevel: Int = 0
private var debug: SKLabelNode?
private var increasedTouchArea:SKSpriteNode?
private var generatedNode:SKSpriteNode?
private var moveAndRemove:SKAction?
private var moveNode:SKAction?
// Here we set initial values of counter and level. Debug label is created here as well.
override func didMove(to view: SKView) {
print(action(forKey: "counting") == nil)
gameCounter = 0
currentLevel = 1
//backgroundColor = SKColor.gray
debug = SKLabelNode(fontNamed: "ArialMT")
debug?.fontColor = SKColor.red
debug?.fontSize = 30.0
debug?.position = CGPoint(x: frame.midX, y: frame.midY)
debug?.text = "Counter : [ \(gameCounter) ], Level [ \(currentLevel) ]"
if let aDebug = debug {
addChild(aDebug)
}
}
//Method to start a timer. SKAction is used here to track a time passed and to maintain current level
func startTimer() {
print("Timer Started...")
weak var weakSelf: GameScene? = self
//make a weak reference to scene to avoid retain cycle
let block = SKAction.run({
weakSelf?.gameCounter = (weakSelf?.gameCounter ?? 0) + 1
//Maintaining level
if (weakSelf?.gameCounter ?? 0) < 5 {
//level 1
weakSelf?.currentLevel = 1
} else if (weakSelf?.gameCounter ?? 0) >= 5 && (weakSelf?.gameCounter ?? 0) < 10 {
//level 2
weakSelf?.currentLevel = 2
} else {
//level 3
weakSelf?.currentLevel = 3
}
weakSelf?.debug?.text = "Counter : [ \(Int(weakSelf?.gameCounter ?? 0)) ], Level [ \(Int(weakSelf?.currentLevel ?? 0)) ]"
})
run(SKAction.repeatForever(SKAction.sequence([SKAction.wait(forDuration: 1), block])), withKey: "counting")
}
//Method for stopping timer and reseting everything to default state.
func stopTimer() {
print("Timer Stopped...")
if action(forKey: "counting") != nil {
removeAction(forKey: "counting")
}
gameCounter = Int(0.0)
currentLevel = 1
debug?.text = "Counter : [ \(gameCounter) ], Level [ \(currentLevel) ]"
}
//Get current speed based on time passed (based on counter variable)
func getCurrentSpeed() -> CGFloat {
if gameCounter < 30 {
//level 1
return 1.0
} else if gameCounter >= 31 && gameCounter < 49 {
//level 2
return 2.0
} else {
//level 3
return 3.0
}
}
//Method which stop generating stones, called in touchesBegan
func stopGeneratingStones() {
print("STOPPED GENERATING STONES...")
if action(forKey: "spawning") != nil {
removeAction(forKey: "spawning")
}
}
func randomFloatBetween(_ smallNumber: CGFloat, and bigNumber: CGFloat) -> CGFloat {
let diff: CGFloat = bigNumber - smallNumber
return CGFloat(arc4random() % (UInt32(RAND_MAX) + 1)) / CGFloat(RAND_MAX) * diff + smallNumber
}
//Method for generating stones, you run this method when you want to start spawning nodes (eg. didMoveToView or when some button is clicked)
func generateStones() {
print("Generating Stones...")
let delay = SKAction.wait(forDuration: 1, withRange: 0.5) //Change forDuration: delay decreases as game progresses.
//randomizing delay time
weak var weakSelf: GameScene? = self
//make a weak reference to scene to avoid retain cycle
let block = SKAction.run({
let stone: SKSpriteNode? = weakSelf?.spawnNodes(withSpeed: weakSelf?.getCurrentSpeed() ?? 0.0)
stone?.zPosition = 20
if let aStone = stone {
weakSelf?.addChild(aStone)
}
})
run(SKAction.repeatForever(SKAction.sequence([delay, block])), withKey: "spawning")
}
func spawnNodes(withSpeed stoneSpeed: CGFloat) -> SKSpriteNode? {
let nodeSize = CGSize(width: 60, height: 60) //size of shape.
let initalNodePosition = CGPoint(x: randomFloatBetween(0.0, and: (self.view?.bounds.size.width)!) - 110, y: frame.maxY)
generatedNode = SKSpriteNode(color: SKColor.green, size: nodeSize)
generatedNode?.position = initalNodePosition
moveNode = SKAction.moveBy(x: 0, y: self.view!.scene!.frame.minY + self.view!.scene!.frame.minY , duration: 5.25)
generatedNode?.isUserInteractionEnabled = false //Allows users to touch shape.
increasedTouchArea = SKSpriteNode(color: UIColor.clear, size: CGSize(width: nodeSize.width * 1.35, height: nodeSize.height * 1.35))
increasedTouchArea?.name = "generatedNode"
increasedTouchArea?.isUserInteractionEnabled = false
generatedNode?.addChild(increasedTouchArea!)
moveNode?.speed = stoneSpeed
moveAndRemove = SKAction.sequence([moveNode!, SKAction.removeFromParent()])
generatedNode?.run(moveAndRemove!, withKey: "moving")
generatedNode?.name = "generatedNode"
return generatedNode
}
func deleteNode(){
moveAndRemove = SKAction.sequence([SKAction.removeFromParent()])
generatedNode?.run(moveAndRemove!, withKey: "moving")
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent? {
for touch in touches {
let locationTocuhed = touch.location(in: self)
let touchedNode : SKNode = self.atPoint(locationTocuhed)
if touchedNode.name == generatedNode?.name {
print("Node touched")
deleteNode()
}
}
if action(forKey: "counting") == nil {
print("Game started...")
startTimer()
generateStones()
} else {
print("Game paused...")
}
}
}
答案 0 :(得分:0)
您的方法deleteNode()在 generateNode 所指向的节点上而不是最后接触的节点上运行删除动画:
func deleteNode(){
moveAndRemove = SKAction.sequence([SKAction.removeFromParent()])
generatedNode?.run(moveAndRemove!, withKey: "moving")
}
如果要删除最后触摸的节点,请将对该节点的引用传递给deleteNode方法,然后在其中而不是generateNode上运行删除动画。