EXC_BAD_ACCESS 在 removeFromParentNode() SceneKit
EXC_BAD_ACCESS at removeFromParentNode() SceneKit
我在游戏执行过程中随机弹出以下错误:
我正在随机创建一个 carNode 的多个实例,所有实例都有从场景中移除节点的操作。大多数时候这不是问题,但在某些情况下,应用程序会崩溃并显示照片中的错误。在这些情况下,我似乎会遇到错误:
一个对象没有被初始化
一个对象已经被释放
其他不太可能发生的事情
..除了第一个,我非常确定我没有提前释放对象..那会是什么?
这是完整的代码:
func spawnCarAtPosition(position: SCNVector3) {
// Create a material using the model_texture.tga image
let carMaterial = SCNMaterial()
carMaterial.diffuse.contents = UIImage(named: "assets.scnassets/Textures/model_texture.tga")
carMaterial.locksAmbientWithDiffuse = false
// Create a clone of the Car node of the carScene - you need a clone because you need to add many cars
var carNode: SCNNode!
let randomNumb = AppDelegate().randRange(0, upper: beachCarArray.count - 1)
let selectedObject = beachCarArray[randomNumb]
carNode = selectedObject.objectscene.rootNode.childNodeWithName(selectedObject.objectname, recursively: false)!.clone() as SCNNode
carNode.name = selectedObject.objectname
carNode.position = position
// Set the material
carNode.geometry!.firstMaterial = carMaterial
// x = length, y = height, z = width
let xscale = 0.6
let yscale = 0.8
let zscale = 0.5
carNode.scale = SCNVector3(xscale, yscale, zscale)
// Create a physicsbody for collision detection
let boundingBox = AppDelegate().sizeOfBoundingBoxFromNode(carNode)
let carPhysicsBodyShape = SCNPhysicsShape(geometry: SCNBox(width: CGFloat(boundingBox.width * Float(xscale)), height: CGFloat(boundingBox.height * Float(yscale)), length: CGFloat(boundingBox.depth * Float(zscale)), chamferRadius: 0.0), options: nil)
carNode.physicsBody = SCNPhysicsBody(type: SCNPhysicsBodyType.Kinematic, shape: carPhysicsBodyShape)
carNode.physicsBody!.categoryBitMask = PhysicsCategory.Car
carNode.physicsBody!.collisionBitMask = PhysicsCategory.Player
carNode.physicsBody?.contactTestBitMask = PhysicsCategory.Player
// Move the car
let moveDirection: Float = position.x > 0.0 ? -1.0 : 1.0
let moveDistance = levelData.gameLevelWidth()
let moveAction = SCNAction.moveBy(SCNVector3(x: moveDistance * moveDirection, y: 0.0, z: 0.0), duration: Double(AppDelegate().randomBetweenNumbers(6.0, secondNum: 7.0)))
let removeAction = SCNAction.runBlock { node -> Void in
node.removeFromParentNode()
}
carNode.runAction(SCNAction.sequence([moveAction, removeAction]))
// Rotate the car to move it in the right direction
if moveDirection > 0.0 {
carNode.rotation = SCNVector4(x: 0.0, y: 1.0, z: 0.0, w: 3.1415)
}
// add node to screen
rootNode.addChildNode(carNode)
}
编辑:
对象崩溃前的打印:
<SCNNode: 0x1374e5ba0 'BeachCar_2' pos(1.500000 0.035000 -4.800000) rot(0.000000 1.000000 0.000000 3.141500) scale(0.600000 0.800000 0.500000) | geometry=<SCNGeometry: 0x135fe80c0 'BeachCar_2'> | no child>
使用 SCNAnimation 删除操作:
编辑 2:
func createSpawnNode(gridPosition: SCNVector3, row: Int, parentNode: SCNNode) {
// Determine if the car should start from the left of the right
let startCol = row % 2 == 0 ? 0 : data.columnCount() - 1
let moveDirection : Float = row % 2 == 0 ? 1.0 : -1.0
// Determine the position of the node
var position = coordinatesForGridPosition(column: startCol, row: row)
position = SCNVector3(x: position.x, y: GameVariables.gamePlaneHeight / 2, z: position.z)
// Create node
let spawnNode = SCNNode()
spawnNode.position = position
var spawnAction: SCNAction!
var delayAction: SCNAction!
// Create an action to make the node spawn cars
spawnAction = SCNAction.runBlock({ node in
self.spawnDelegate!.spawnCarAtPosition(node.position)
})
delayAction = SCNAction.waitForDuration(3.0, withRange: 2.0)
spawnNode.runAction(SCNAction.repeatActionForever(SCNAction.sequence([spawnAction, delayAction])))
parentNode.addChildNode(spawnNode)
// record newly created node
recordNodeAtRow(spawnNode, row: row)
}
父节点是场景的根节点。
推测...也许内部某个东西持有对 carNode
的引用,该引用持续到您的 removeFromParentNode()
调用之后?看起来有一个保留循环:carNode
持有对 removeAction
的强引用,其闭包持有(强?)对 carNode
.
的引用
不过,我认为 removeFromParentNode
在 SCNAction
上的存在是一个非常重要的提示。如果不需要,它就不会在那里。而不是基于闭包的调用,try
let removeAction = SCNAction.removeFromParentNode()
carNode.runAction(SCNAction.sequence([moveAction, removeAction]))
只是为了确保:您是从主线程调用 spawnCarAtPosition()
,而不是后台线程,对吗?
在您的 EDIT 2 之后进行编辑:
createSpawnNode()
读起来有点混乱,因为你重用了 position
(两个 let
定义而不是一个 var
怎么样?)(另外两个 var
defs 会更直接,因为 let
,顺便说一句)。我没有看到任何我认为会导致崩溃的东西。
我不会复制源 Car 的 objectName
,而是给每个节点一个唯一的名称(可能是 NSDate().description
),看看您是否可以检测到一种模式。
我也会尝试将生成从 SCNAction 中移出,并移到渲染回调中(可能 renderer(_:updateAtTime:)
,以防你遇到内部 SceneKit 错误。你正在调用一个 SCNAction
在另一个中。文档没有说明是否允许这样做,但是由于您看到了不稳定的行为,因此简化它似乎是一件好事。
我假设您已经尝试过打开僵尸和设置异常断点的标准技巧。
我在游戏执行过程中随机弹出以下错误:
我正在随机创建一个 carNode 的多个实例,所有实例都有从场景中移除节点的操作。大多数时候这不是问题,但在某些情况下,应用程序会崩溃并显示照片中的错误。在这些情况下,我似乎会遇到错误:
一个对象没有被初始化 一个对象已经被释放 其他不太可能发生的事情
..除了第一个,我非常确定我没有提前释放对象..那会是什么?
这是完整的代码:
func spawnCarAtPosition(position: SCNVector3) {
// Create a material using the model_texture.tga image
let carMaterial = SCNMaterial()
carMaterial.diffuse.contents = UIImage(named: "assets.scnassets/Textures/model_texture.tga")
carMaterial.locksAmbientWithDiffuse = false
// Create a clone of the Car node of the carScene - you need a clone because you need to add many cars
var carNode: SCNNode!
let randomNumb = AppDelegate().randRange(0, upper: beachCarArray.count - 1)
let selectedObject = beachCarArray[randomNumb]
carNode = selectedObject.objectscene.rootNode.childNodeWithName(selectedObject.objectname, recursively: false)!.clone() as SCNNode
carNode.name = selectedObject.objectname
carNode.position = position
// Set the material
carNode.geometry!.firstMaterial = carMaterial
// x = length, y = height, z = width
let xscale = 0.6
let yscale = 0.8
let zscale = 0.5
carNode.scale = SCNVector3(xscale, yscale, zscale)
// Create a physicsbody for collision detection
let boundingBox = AppDelegate().sizeOfBoundingBoxFromNode(carNode)
let carPhysicsBodyShape = SCNPhysicsShape(geometry: SCNBox(width: CGFloat(boundingBox.width * Float(xscale)), height: CGFloat(boundingBox.height * Float(yscale)), length: CGFloat(boundingBox.depth * Float(zscale)), chamferRadius: 0.0), options: nil)
carNode.physicsBody = SCNPhysicsBody(type: SCNPhysicsBodyType.Kinematic, shape: carPhysicsBodyShape)
carNode.physicsBody!.categoryBitMask = PhysicsCategory.Car
carNode.physicsBody!.collisionBitMask = PhysicsCategory.Player
carNode.physicsBody?.contactTestBitMask = PhysicsCategory.Player
// Move the car
let moveDirection: Float = position.x > 0.0 ? -1.0 : 1.0
let moveDistance = levelData.gameLevelWidth()
let moveAction = SCNAction.moveBy(SCNVector3(x: moveDistance * moveDirection, y: 0.0, z: 0.0), duration: Double(AppDelegate().randomBetweenNumbers(6.0, secondNum: 7.0)))
let removeAction = SCNAction.runBlock { node -> Void in
node.removeFromParentNode()
}
carNode.runAction(SCNAction.sequence([moveAction, removeAction]))
// Rotate the car to move it in the right direction
if moveDirection > 0.0 {
carNode.rotation = SCNVector4(x: 0.0, y: 1.0, z: 0.0, w: 3.1415)
}
// add node to screen
rootNode.addChildNode(carNode)
}
编辑:
对象崩溃前的打印:
<SCNNode: 0x1374e5ba0 'BeachCar_2' pos(1.500000 0.035000 -4.800000) rot(0.000000 1.000000 0.000000 3.141500) scale(0.600000 0.800000 0.500000) | geometry=<SCNGeometry: 0x135fe80c0 'BeachCar_2'> | no child>
使用 SCNAnimation 删除操作:
编辑 2:
func createSpawnNode(gridPosition: SCNVector3, row: Int, parentNode: SCNNode) {
// Determine if the car should start from the left of the right
let startCol = row % 2 == 0 ? 0 : data.columnCount() - 1
let moveDirection : Float = row % 2 == 0 ? 1.0 : -1.0
// Determine the position of the node
var position = coordinatesForGridPosition(column: startCol, row: row)
position = SCNVector3(x: position.x, y: GameVariables.gamePlaneHeight / 2, z: position.z)
// Create node
let spawnNode = SCNNode()
spawnNode.position = position
var spawnAction: SCNAction!
var delayAction: SCNAction!
// Create an action to make the node spawn cars
spawnAction = SCNAction.runBlock({ node in
self.spawnDelegate!.spawnCarAtPosition(node.position)
})
delayAction = SCNAction.waitForDuration(3.0, withRange: 2.0)
spawnNode.runAction(SCNAction.repeatActionForever(SCNAction.sequence([spawnAction, delayAction])))
parentNode.addChildNode(spawnNode)
// record newly created node
recordNodeAtRow(spawnNode, row: row)
}
父节点是场景的根节点。
推测...也许内部某个东西持有对 carNode
的引用,该引用持续到您的 removeFromParentNode()
调用之后?看起来有一个保留循环:carNode
持有对 removeAction
的强引用,其闭包持有(强?)对 carNode
.
不过,我认为 removeFromParentNode
在 SCNAction
上的存在是一个非常重要的提示。如果不需要,它就不会在那里。而不是基于闭包的调用,try
let removeAction = SCNAction.removeFromParentNode()
carNode.runAction(SCNAction.sequence([moveAction, removeAction]))
只是为了确保:您是从主线程调用 spawnCarAtPosition()
,而不是后台线程,对吗?
在您的 EDIT 2 之后进行编辑:
createSpawnNode()
读起来有点混乱,因为你重用了 position
(两个 let
定义而不是一个 var
怎么样?)(另外两个 var
defs 会更直接,因为 let
,顺便说一句)。我没有看到任何我认为会导致崩溃的东西。
我不会复制源 Car 的 objectName
,而是给每个节点一个唯一的名称(可能是 NSDate().description
),看看您是否可以检测到一种模式。
我也会尝试将生成从 SCNAction 中移出,并移到渲染回调中(可能 renderer(_:updateAtTime:)
,以防你遇到内部 SceneKit 错误。你正在调用一个 SCNAction
在另一个中。文档没有说明是否允许这样做,但是由于您看到了不稳定的行为,因此简化它似乎是一件好事。
我假设您已经尝试过打开僵尸和设置异常断点的标准技巧。