我正在用Sprite-kit制作一个游戏,你可以转动一个轮子并需要躲避球,但每次我将场景从gameOverScene改为gameScene时,我都会得到一个EXC BAD ACCESS。当gameScene第一次加载时没有出现问题。
我收到此错误,因为我将'wheel'变量放在gameScene中的didMoveToView函数之外。我这样做了,所以你不必触摸方向盘就可以旋转它。
那么你们可以帮我解决这个问题吗?
gameScene:
import SpriteKit
//Collision bitmasks
let BallCategory : UInt32 = 0x1 << 0 // 00000000000000000000000000000001
let BorderCategory : UInt32 = 0x1 << 1 // 00000000000000000000000000000010
let WheelCategory : UInt32 = 0x1 << 2 // 00000000000000000000000000000100
//Score variable
var score:Int = 0
let scoreLabel = SKLabelNode()
//Drag rotating variables
var startingAngle:CGFloat?
var startingTime:NSTimeInterval?
//Wheel variable
let wheel = SKSpriteNode(imageNamed: "wheel") // THE ERROR IS PROBABLY HERE!
class GameScene: SKScene, SKPhysicsContactDelegate {
override func didMoveToView(view: SKView) {
super.didMoveToView(view)
//Border
let borderBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
self.physicsBody = borderBody
self.physicsBody?.categoryBitMask = BorderCategory
self.physicsBody?.restitution = 1
self.physicsBody?.friction = 0
//Ball
let ballNode = SKSpriteNode(imageNamed: "ball")
let scaleBall = frame.width / ballNode.frame.width * 0.035
ballNode.position = CGPoint(x: size.width * 0.5, y: size.height * 0.5)
addChild(ballNode)
ballNode.setScale(scaleBall)
let ballWidth = ballNode.size.width
ballNode.physicsBody = SKPhysicsBody(circleOfRadius: ballWidth * 0.5)
ballNode.physicsBody?.dynamic = true
ballNode.physicsBody?.categoryBitMask = BallCategory
ballNode.physicsBody?.contactTestBitMask = BorderCategory
ballNode.physicsBody?.restitution = 1
ballNode.physicsBody?.friction = 0
ballNode.physicsBody?.angularDamping = 0
ballNode.physicsBody?.linearDamping = 0
let delay = SKAction.waitForDuration(1.0)
ballNode.runAction(delay) {
let widthiPhone6 = CGFloat(375)
let heightiPhone6 = CGFloat(667)
let forceX = (self.frame.width / widthiPhone6) * (self.frame.width / widthiPhone6) * ((self.frame.width / widthiPhone6) / 1.5) * 3
let forceY = (self.frame.height / heightiPhone6) * (self.frame.height / heightiPhone6) * ((self.frame.width / widthiPhone6) / 1.5) * 2.3
print("Force: \(forceX), \(forceY)")
ballNode.physicsBody!.applyImpulse(CGVectorMake(forceX, forceY))
}
//Score
//scoreLabel.position = CGPoint(x: size.width * 0.1, y: size.height * 0.1)
//scoreLabel.fontColor = UIColor.blackColor()
addChild(scoreLabel)
//Background
backgroundColor = SKColor.whiteColor()
//physicsWorld
physicsWorld.gravity = CGVectorMake(0, 0)
physicsWorld.contactDelegate = self
//Wheel
//let wheel = SKSpriteNode(imageNamed: "wheel")
let scaleWheel = frame.width / wheel.frame.width * 0.8
print("Scale of the wheel: \(scaleWheel)\nFrame width: \(frame.width)\nWheel frame width: \(wheel.frame.width)")
wheel.name = "wheel"
wheel.position = view.center
//Scale constants
let sprite = SKSpriteNode()
sprite.position = CGPointMake(CGRectGetMidX(frame), CGRectGetMidY(frame))
let offsetX: CGFloat = sprite.frame.size.width * sprite.anchorPoint.x
let offsetY: CGFloat = sprite.frame.size.height * sprite.anchorPoint.y
//path
let path: CGMutablePathRef = CGPathCreateMutable()
CGPathMoveToPoint(path, nil, -142 - offsetX, 143 - offsetY)
CGPathAddLineToPoint(path, nil, -177 - offsetX, 161 - offsetY)
CGPathAddLineToPoint(path, nil, -149 - offsetX, 188 - offsetY)
CGPathAddLineToPoint(path, nil, -105 - offsetX, 215 - offsetY)
CGPathAddLineToPoint(path, nil, -57 - offsetX, 232 - offsetY)
CGPathAddLineToPoint(path, nil, -2 - offsetX, 238 - offsetY)
CGPathAddLineToPoint(path, nil, 57 - offsetX, 231 - offsetY)
CGPathAddLineToPoint(path, nil, 109 - offsetX, 213 - offsetY)
CGPathAddLineToPoint(path, nil, 146 - offsetX, 188 - offsetY)
CGPathAddLineToPoint(path, nil, 176 - offsetX, 162 - offsetY)
CGPathAddLineToPoint(path, nil, 142 - offsetX, 141 - offsetY)
CGPathAddLineToPoint(path, nil, 121 - offsetX, 160 - offsetY)
CGPathAddLineToPoint(path, nil, 92 - offsetX, 179 - offsetY)
CGPathAddLineToPoint(path, nil, 53 - offsetX, 194 - offsetY)
CGPathAddLineToPoint(path, nil, 0 - offsetX, 201 - offsetY)
CGPathAddLineToPoint(path, nil, -48 - offsetX, 195 - offsetY)
CGPathAddLineToPoint(path, nil, -88 - offsetX, 181 - offsetY)
CGPathAddLineToPoint(path, nil, -119 - offsetX, 163 - offsetY)
CGPathCloseSubpath(path)
//pathTwo
let pathTwo: CGMutablePathRef = CGPathCreateMutable()
CGPathMoveToPoint(pathTwo, nil, -241 - offsetX, 31 - offsetY)
CGPathAddLineToPoint(pathTwo, nil, -205 - offsetX, 17 - offsetY)
CGPathAddLineToPoint(pathTwo, nil, -205 - offsetX, -18 - offsetY)
CGPathAddLineToPoint(pathTwo, nil, -199 - offsetX, -56 - offsetY)
CGPathAddLineToPoint(pathTwo, nil, -184 - offsetX, -94 - offsetY)
CGPathAddLineToPoint(pathTwo, nil, -164 - offsetX, -127 - offsetY)
CGPathAddLineToPoint(pathTwo, nil, -140 - offsetX, -153 - offsetY)
CGPathAddLineToPoint(pathTwo, nil, -115 - offsetX, -175 - offsetY)
CGPathAddLineToPoint(pathTwo, nil, -86 - offsetX, -191 - offsetY)
CGPathAddLineToPoint(pathTwo, nil, -65 - offsetX, -200 - offsetY)
CGPathAddLineToPoint(pathTwo, nil, -64 - offsetX, -240 - offsetY)
CGPathAddLineToPoint(pathTwo, nil, -98 - offsetX, -228 - offsetY)
CGPathAddLineToPoint(pathTwo, nil, -129 - offsetX, -211 - offsetY)
CGPathAddLineToPoint(pathTwo, nil, -165 - offsetX, -184 - offsetY)
CGPathAddLineToPoint(pathTwo, nil, -195 - offsetX, -152 - offsetY)
CGPathAddLineToPoint(pathTwo, nil, -216 - offsetX, -117 - offsetY)
CGPathAddLineToPoint(pathTwo, nil, -233 - offsetX, -77 - offsetY)
CGPathAddLineToPoint(pathTwo, nil, -244 - offsetX, -25 - offsetY)
CGPathCloseSubpath(pathTwo)
//pathThree
let pathThree: CGMutablePathRef = CGPathCreateMutable()
CGPathMoveToPoint(pathThree, nil, 204 - offsetX, -3 - offsetY)
CGPathAddLineToPoint(pathThree, nil, 240 - offsetX, 21 - offsetY)
CGPathAddLineToPoint(pathThree, nil, 241 - offsetX, -30 - offsetY)
CGPathAddLineToPoint(pathThree, nil, 235 - offsetX, -66 - offsetY)
CGPathAddLineToPoint(pathThree, nil, 222 - offsetX, -104 - offsetY)
CGPathAddLineToPoint(pathThree, nil, 205 - offsetX, -136 - offsetY)
CGPathAddLineToPoint(pathThree, nil, 180 - offsetX, -168 - offsetY)
CGPathAddLineToPoint(pathThree, nil, 150 - offsetX, -196 - offsetY)
CGPathAddLineToPoint(pathThree, nil, 113 - offsetX, -220 - offsetY)
CGPathAddLineToPoint(pathThree, nil, 79 - offsetX, -233 - offsetY)
CGPathAddLineToPoint(pathThree, nil, 78 - offsetX, -194 - offsetY)
CGPathAddLineToPoint(pathThree, nil, 104 - offsetX, -181 - offsetY)
CGPathAddLineToPoint(pathThree, nil, 133 - offsetX, -161 - offsetY)
CGPathAddLineToPoint(pathThree, nil, 154 - offsetX, -140 - offsetY)
CGPathAddLineToPoint(pathThree, nil, 172 - offsetX, -115 - offsetY)
CGPathAddLineToPoint(pathThree, nil, 186 - offsetX, -89 - offsetY)
CGPathAddLineToPoint(pathThree, nil, 196 - offsetX, -63 - offsetY)
CGPathAddLineToPoint(pathThree, nil, 202 - offsetX, -36 - offsetY)
CGPathCloseSubpath(pathThree)
//Physics bodies
let bodyOne = SKPhysicsBody(edgeLoopFromPath: path)
let bodyTwo = SKPhysicsBody(edgeLoopFromPath: pathTwo)
let bodyThree = SKPhysicsBody(edgeLoopFromPath: pathThree)
wheel.physicsBody = SKPhysicsBody(bodies: [bodyOne, bodyTwo, bodyThree])
wheel.physicsBody!.angularDamping = 5
wheel.physicsBody?.pinned = true
wheel.physicsBody?.affectedByGravity = false
wheel.physicsBody?.restitution = 1
wheel.setScale(scaleWheel)
addChild(wheel)
}
func didBeginContact(contact: SKPhysicsContact) {
//Create local variables for two physics bodies
var firstBody: SKPhysicsBody
var secondBody: SKPhysicsBody
//Assign the two physics bodies so that the one with the lower category is always stored in firstBody
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
firstBody = contact.bodyA
secondBody = contact.bodyB
} else {
firstBody = contact.bodyB
secondBody = contact.bodyA
}
//React to the contact between ball and bottom
if firstBody.categoryBitMask == BallCategory && secondBody.categoryBitMask == BorderCategory {
score += 1
scoreLabel.text = "\(score)"
scoreLabel.position = CGPoint(x: size.width * 0.1, y: size.height * 0.1)
scoreLabel.fontColor = UIColor.blackColor()
print("SCORE: \(score)")
} else {
print("GAME OVER")
NSUserDefaults.standardUserDefaults().integerForKey("score")
NSUserDefaults.standardUserDefaults().setInteger(score, forKey: "score")
NSUserDefaults.standardUserDefaults().synchronize()
NSUserDefaults.standardUserDefaults().integerForKey("highscore")
//Check if score is higher than NSUserDefaults stored value and change NSUserDefaults stored value if it's true
if score > NSUserDefaults.standardUserDefaults().integerForKey("highscore") {
NSUserDefaults.standardUserDefaults().setInteger(score, forKey: "highscore")
NSUserDefaults.standardUserDefaults().synchronize()
}
score = 0
//removeChildrenInArray([wheel])
let newScene = GameOverScene(size: size)
newScene.scaleMode = .AspectFill
view?.presentScene(newScene)
}
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(self)
//let node = nodeAtPoint(location)
let node = wheel
let dx = location.x - node.position.x
let dy = location.y - node.position.y
// Store angle and current time
startingAngle = atan2(dy, dx)
startingTime = touch.timestamp
node.physicsBody?.angularVelocity = 0
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(self)
//let node = nodeAtPoint(location)
let node = wheel
let dx = location.x - node.position.x
let dy = location.y - node.position.y
let angle = atan2(dy, dx)
// Calculate angular velocity
// EDIT: Handle wrap at pi/-pi
var deltaAngle:CGFloat
if startingAngle == nil {
deltaAngle = CGFloat()
} else {
deltaAngle = angle - startingAngle!
}
if abs(deltaAngle) > CGFloat(M_PI) {
if (deltaAngle > 0) {
deltaAngle = deltaAngle - CGFloat(2*M_PI)
}
else {
deltaAngle = deltaAngle + CGFloat(2*M_PI)
}
}
if startingTime == nil {
startingTime = 0
}
let dt = CGFloat(touch.timestamp - startingTime!)
let velocity = deltaAngle / dt
node.physicsBody?.angularVelocity = velocity
// Update angle and time
startingAngle = angle
startingTime = touch.timestamp
}
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
startingAngle = nil
startingTime = nil
}
func offset(node: SKSpriteNode, isX: Bool)->CGFloat {
return isX ? node.frame.size.width * node.anchorPoint.x : node.frame.size.height * node.anchorPoint.y
}
func AddLineToPoint(path: CGMutablePath!, x: CGFloat, y: CGFloat, node: SKSpriteNode) {
CGPathAddLineToPoint(path, nil, (x * 2) - offset(node, isX: true), (y * 2) - offset(node, isX: false))
}
func MoveToPoint(path: CGMutablePath!, x: CGFloat, y: CGFloat, node: SKSpriteNode) {
CGPathMoveToPoint(path, nil, (x * 2) - offset(node, isX: true), (y * 2) - offset(node, isX: false))
}
}
gameOverScene:
import Foundation
import SpriteKit
class GameOverScene: SKScene {
//Create button
let button = SKLabelNode(text: "Tap to restart!")
override func didMoveToView(view: SKView) {
super.didMoveToView(view)
//Background
backgroundColor = SKColor.whiteColor()
//Button
button.fontColor = UIColor.blackColor()
button.fontSize = 50
button.position = view.center
addChild(button)
//Ruined
let youTotally = SKLabelNode(text: "You totally")
youTotally.fontSize = 50
youTotally.fontColor = UIColor.redColor()
youTotally.position = CGPoint(x: size.width * 0.5, y: size.height * 0.8)
addChild(youTotally)
let ruined = SKLabelNode(text: "Ruined")
ruined.fontSize = 60
ruined.fontColor = UIColor.redColor()
ruined.position = CGPoint(x: size.width * 0.5, y: size.height * 0.8 - 60)
addChild(ruined)
let it = SKLabelNode(text: "it!")
it.fontSize = 50
it.fontColor = UIColor.redColor()
it.position = CGPoint(x: size.width * 0.5, y: size.height * 0.8 - 110)
addChild(it)
//Score
let scoreLabelEnd = SKLabelNode(text: "Score: \(NSUserDefaults.standardUserDefaults().integerForKey("score"))")
scoreLabelEnd.position = CGPoint(x: size.width * 0.5, y: size.height * 0.1 + 40)
scoreLabelEnd.fontColor = UIColor.blackColor()
addChild(scoreLabelEnd)
//Highscore
let highscoreLabel = SKLabelNode(text: "Highscore: \(NSUserDefaults.standardUserDefaults().integerForKey("highscore"))")
highscoreLabel.position = CGPoint(x: size.width * 0.5, y: size.height * 0.1)
highscoreLabel.fontColor = UIColor.blackColor()
addChild(highscoreLabel)
NSUserDefaults.standardUserDefaults().integerForKey("score")
NSUserDefaults.standardUserDefaults().setInteger(0, forKey: "score")
NSUserDefaults.standardUserDefaults().synchronize()
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
print("GAME START")
let newScene = GameScene(size: size)
newScene.scaleMode = .AspectFill
view?.presentScene(newScene)
}
}