我的一个精灵在游戏场景和场景中的游戏多次切换后冻结。一旦我死了并重新开始大约6-7次,我的敌人精灵就会出现。不再响应我的设备的倾斜(如果它,它需要很长时间)。使用屏幕操纵杆时,我的播放器仍能正常移动。我的FPS读取60的整个时间。
class GameScene: SKScene, SKPhysicsContactDelegate {
let joyStickSprite = SKSpriteNode(imageNamed: "flatLight09")
let playerSprite = SKSpriteNode(imageNamed: "p3_front")
let enemySprite = SKSpriteNode(imageNamed: "elementExplosive001")
let coinSprite = SKSpriteNode(imageNamed: "gold_1")
var left = false
var right = false
var enemyCount = 0
var randomNextCoin = Int(arc4random_uniform(5)+1)
var motionManager = CMMotionManager()
var destY:CGFloat = 0.0
struct CollisionCategoryBitmask {
static let Player: UInt32 = 0x00
static let Enemy: UInt32 = 0x01
static let Floor: UInt32 = 0x02
static let Coin: UInt32 = 0x03
}
var background = SKSpriteNode(imageNamed: "blue_land")
override func didMoveToView(view: SKView) {
self.physicsWorld.contactDelegate = self
createBackground()
createPlayer()
createJoyStick()
createScreenBorder()
createEnemy()
motionManager.accelerometerUpdateInterval = 0.1
motionManager.startAccelerometerUpdatesToQueue(NSOperationQueue.currentQueue(), withHandler: {
(accelerometerData: CMAccelerometerData!, error: NSError!) in
var currentY = self.enemySprite.position.y
let acceleration = accelerometerData.acceleration
self.destY = (CGFloat(acceleration.y) * 0.75) + (self.destY * 0.25)
})
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
let touchedNode = self.nodeAtPoint(location)
if let name = touchedNode.name
{
if name == "joyStick"
{
if location.x < joyStickSprite.position.x {
left = true
right = false
}
else {
right = true
left = false
}
}
}
}
}
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
left = false
right = false
}
func presentGameOver () {
removeAllChildren()
let newScene = GameScene(size: size)
newScene.scaleMode = scaleMode
let reveal = SKTransition.flipHorizontalWithDuration(0.5)
view?.presentScene(newScene, transition: reveal)
enemySprite.physicsBody?.dynamic = false
}
func createBackground () {
background.position = CGPoint(x: frame.size.width / 2, y: frame.size.height / 2)
background.size.height = self.size.height
addChild(background)
}
func createPlayer () {
playerSprite.position = CGPoint(x: self.size.width / 2, y: playerSprite.size.height/2)
playerSprite.physicsBody = SKPhysicsBody(circleOfRadius: playerSprite.size.width / 2)
playerSprite.physicsBody?.dynamic = false
playerSprite.physicsBody?.categoryBitMask = CollisionCategoryBitmask.Player
playerSprite.physicsBody?.collisionBitMask = 0
playerSprite.physicsBody?.contactTestBitMask = CollisionCategoryBitmask.Enemy | CollisionCategoryBitmask.Coin
addChild(playerSprite)
}
func movePlayerLeft () {
let moveLeft = SKAction.moveByX(-10, y: 0, duration: 1)
playerSprite.runAction(moveLeft)
}
func movePlayerRight () {
let moveRight = SKAction.moveByX(10, y: 0, duration: 1)
playerSprite.runAction(moveRight)
}
func createEnemy () {
var randomX = Int(arc4random_uniform(600))
var randomXCG = CGFloat(randomX)
enemyCount += 1
enemySprite.position = CGPoint(x: randomXCG, y: self.size.height)
enemySprite.physicsBody = SKPhysicsBody(circleOfRadius: enemySprite.size.width / 2)
enemySprite.physicsBody?.dynamic = true
enemySprite.physicsBody?.allowsRotation = true
enemySprite.physicsBody?.restitution = 0.0
enemySprite.physicsBody?.friction = 0.0
enemySprite.physicsBody?.angularDamping = 0.0
enemySprite.physicsBody?.linearDamping = 0.0
enemySprite.physicsBody?.affectedByGravity = true
enemySprite.physicsBody?.categoryBitMask = CollisionCategoryBitmask.Enemy
enemySprite.physicsBody?.collisionBitMask = CollisionCategoryBitmask.Floor
println("enemey count \(enemyCount)")
println("next coin \(randomNextCoin)")
addChild(enemySprite)
}
func createCoins () {
var randomX = Int(arc4random_uniform(600))
var randomXCG = CGFloat(randomX)
randomNextCoin = Int(arc4random_uniform(10))
enemyCount = 0
coinSprite.size.height = playerSprite.size.height/2
coinSprite.size.width = coinSprite.size.height
coinSprite.position = CGPoint(x: randomXCG, y: self.size.height)
coinSprite.physicsBody = SKPhysicsBody(circleOfRadius: enemySprite.size.width/2)
coinSprite.physicsBody?.dynamic = true
coinSprite.physicsBody?.affectedByGravity = true
coinSprite.physicsBody?.categoryBitMask = CollisionCategoryBitmask.Coin
addChild(coinSprite)
}
func createJoyStick () {
joyStickSprite.setScale(0.4)
joyStickSprite.position = CGPoint(x: self.size.width/1.1, y: joyStickSprite.size.height)
joyStickSprite.name = "joyStick"
joyStickSprite.userInteractionEnabled = false
addChild(joyStickSprite)
}
func updateEnemyPosition () {
if enemySprite.size.height > enemySprite.position.y {
enemySprite.position.x = enemySprite.position.x + destY*20
}
}
func didBeginContact(contact: SKPhysicsContact) {
let firstNode = contact.bodyA.node as! SKSpriteNode
let secondNode = contact.bodyB.node as! SKSpriteNode
if (contact.bodyA.categoryBitMask == CollisionCategoryBitmask.Player) &&
(contact.bodyB.categoryBitMask == CollisionCategoryBitmask.Enemy) {
let transition = SKTransition.revealWithDirection(SKTransitionDirection.Down, duration: 1.0)
let scene = SecondScene(size: self.scene!.size)
scene.scaleMode = SKSceneScaleMode.AspectFill
self.scene!.view!.presentScene(scene, transition: transition)
}
if (contact.bodyA.categoryBitMask == CollisionCategoryBitmask.Player) &&
(contact.bodyB.categoryBitMask == CollisionCategoryBitmask.Coin) {
coinSprite.removeFromParent()
}
}
func createScreenBorder () {
// 1. Create a physics body that borders the screen
let borderBody = SKPhysicsBody(edgeFromPoint: CGPointMake(0.0, 0.0), toPoint: CGPointMake(self.size.width, 0.0))
// 2. Set the friction of that physicsBody to 0
borderBody.friction = 0
borderBody.categoryBitMask = CollisionCategoryBitmask.Floor
// 3. Set physicsBody of scene to borderBody
self.physicsBody = borderBody
}
override func update(currentTime: CFTimeInterval) {
//detect where on joystick player is touching
if left == true {
movePlayerLeft()
}
if right == true {
movePlayerRight()
}
//move player to other side when going off screen
if playerSprite.position.x < -20.0 {
playerSprite.position = CGPoint(x: self.size.width + 20.0, y: playerSprite.position.y)
} else if (playerSprite.position.x > self.size.width + 20.0) {
playerSprite.position = CGPoint(x: -20.0, y: playerSprite.position.y)
}
//remove enemeny if off screen
if enemySprite.position.x < -20.0 || enemySprite.position.x > self.size.width + 20.0 {
self.enemySprite.removeFromParent()
createEnemy()
}
if randomNextCoin == enemyCount {
println("coin dropped")
coinSprite.removeFromParent()
createCoins()
}
updateEnemyPosition()
}
}
有人有任何建议吗?
答案 0 :(得分:2)
通过引用self.destY
闭包中的motionManager.startAccelerometerUpdatesToQueue
,您将创建一个强大的参考周期。来自文档,
如果为类实例的属性分配闭包,并且闭包通过引用实例或其成员来捕获该实例,则将在闭包和实例之间创建一个强引用循环。
此参考周期阻止您的场景被释放。由于您没有停止运动管理器,因此在转换场景时,旧管理器(复数)仍在运行。这可能导致当前场景在多次转换后冻结。
Swift使用捕获列表来避免强引用周期,其中捕获列表的格式为
{ [ /* weak or unowned + object, ...*/ ]
/* parameters */ in
}
您可以将闭包中的捕获定义为 unowned 或 weak 。来自文档,
当闭包和它捕获的实例将始终相互引用时,将闭包中的捕获定义为无主引用,并且将始终同时取消分配。
和
相反,当捕获的引用在将来的某个时刻变为零时,将捕获定义为弱引用。弱引用始终是可选类型,并且在它们引用的实例被释放时自动变为nil。这使您可以检查它们在闭包体内是否存在。
以下是如何向加速度计处理程序添加捕获列表的示例:
motionManager.accelerometerUpdateInterval = 0.1
motionManager.startAccelerometerUpdatesToQueue(NSOperationQueue.currentQueue()) {
[unowned self] accelerometerData, error in
var currentY = self.enemySprite.position.y
let acceleration = accelerometerData.acceleration
self.destY = (CGFloat(acceleration.y) * 0.75) + (self.destY * 0.25)
}
最后,在转换场景之前停止加速度计更新是一个好主意
override func willMoveFromView(view: SKView) {
motionManager.stopAccelerometerUpdates()
}