Impossible contact in SceneKit - ios

The physicsWorld(_:didBegin:) function should only be called when some node's category bit is set in another nodes contactTest bit mask. However, in my code, there is an impossible situation happening where a contact is occurring when neither of the contacting bodies have each other in their contactTest bit masks. Eg:
func physicsWorld(_ world: SCNPhysicsWorld, didBegin contact: SCNPhysicsContact) {
if (contact.nodeA.physicsBody!.categoryBitMask & contact.nodeB.physicsBody!.contactTestBitMask) != 0 {
print("All good")
} else if (contact.nodeA.physicsBody!.contactTestBitMask & contact.nodeB.physicsBody!.categoryBitMask) != 0 {
print("All good")
} else {
fatalError("Impossible") // << Execution reaches here
}
// .. Code handling the contact ...
}
I noticed this started happening when I added this line to the physicsWorld(_:didBegin:) function:
// The ball is the only node in the game that has a contactTestBitMask other than 0
let ball = contact.nodeA.name == "ball" ? contact.nodeA : contact.nodeB
ball.physicsBody!.contactTestBitMask = PhysicsCategory.dynamic
Regardless of what code I add to the function, that fatalError should never be reached. I don't know if changing bit masks within the didBeginContact method leads to undefined behaviour, but I still don't think the physicsWorld(_:didBegin:) should ever get called if categoryBitMask & contactTestBitMask == 0.
If I change (changing in both nodeA and nodeB) nodeA/B.physicsBody!.categoryBitMask to nodeA/B.categoryBitMask then it never crashes, but according to the documentation, a SCNNodes category bit mask property doesn't affect the SceneKit physics simulation.
If anyone has any suspicious of why this could happen please let me know as I'm convinced it's a bug.
Thanks

Related

Change texture of SKSpriteNode in SKScene

I've been trying to change the texture of an SKSpriteNode in the scene, but XCode keeps giving me the error "Value of type 'SKNode' has no member 'texture'". However, I put an if is statement, so it should read it as an SKSpriteNode. I'm wondering if there are any other solutions in order to be able to change the texture of an SKSpriteNode in the scene. Thank you!
for child in children {
if child.name == "Enemy" || child.name == "FinalEnemy" {
if child is SKSpriteNode {
child.texture = SKTexture.init(image: #imageLiteral(resourceName: "Ninja58"))
}
}
}
You are asking the runtime environment “is this SKNode actually a SKSpriteNode” but the compiler is still treating child as an SKNode.
You need to get a reference to a SKSpriteNode to make the compiler happy.
You can do that by changing
if child is SKSpriteNode
To
if let child = child as? SKSpriteNode
This second one uses optional binding to create a new variable named child which is valid inside the if block and is a SKSpriteNode.
If child is not a SKSpriteNode then this will pass over this if statement and the code won’t be run so you will have the behaviour you want.
The rest of your code should work by making this change.
Edit after Knight0fDragon's comment
You could actually embed the condition into the for loop here to make it a bit more compact...
for child in children where child.name == "Enemy" || child.name == "FinalEnemy" {
guard let child = child as? SKSpriteNode else { continue }
child.texture = ...
}
Hmm... this isn't as nice as I wanted it and I would like the optional binding in the for loop condition...
Maybe that's possible but this will do for now :)
Edit By Knight0fDragon due to comments being limited:
Please give this a try, should only work in XCode 9+, and the cast may be unnecessary, I do not have access to XCode to test this.
for child in ArraySlice<SKSpriteNode>(children) as [SKSpriteNode]
where ["Enemy","FinalEnemy"].contains(child.name ?? "") {
child.texture = ...
}

Cocos2d iPHone SDK ccPhysicsCollisionPreSolve crashing while accessing object properties

I am working on Cocos2d iphone SDK and stuck with an issues. Check my code here.
Obstacle Class
#objc class Obstacle: CCNode {
weak var __pipe: CCSprite!
var ignoreCollision:Bool = false
override init!() {
super.init()
//NSLog("init plain")
userInteractionEnabled = true
ignoreCollision = false
}
func didLoadFromCCB() {
...
}
}
The main scene where I have placed collision delegate methods. The method is called once the player object collides with obstacle object.
func ccPhysicsCollisionPreSolve(pair: CCPhysicsCollisionPair!, hero: Player!, platform: Obstacle!) -> ObjCBool {
if !isGameOn {
NSLog("PLATFORM: Game finished")
return false
}
if platform.ignoreCollision {
platform.ignoreCollision = !platform.ignoreCollision
// For score updates
hudLayer.updatePlatform(++scorePlatforms)
}
return true
}
Now here, I am just trying to use simple Bool property from platform object and what I get is a crash. My app crashes on the if... condition statement where I am using that property. I am unable to get what is with this as I am simply using a property from object.
I checked the object and found platform shows me of type Some instead ob Obstacle. I have tried using
var p: Obstacle = platform as Obstacle
and replaced all platform with p but yet I am facing the crash. I thought the type now shows me some random hex number which might be the issue.
Can anyone help me here as I am unable to find out how I should access property from this platform object in ccPhysicsCollisionPreSolve method?
Sorry guys for the trouble but it was my mistake. I was understanding the same incorrectly.
The Obstacle class represents the platform as well as its background layer having tripple height of the device screen. But my ball collides only with that __pipe sprite in Obstacle class and I am referring the whole Obstacle class which is wrong.
I used platform.parent!.ignoreCollision and problem is solved. :)
This little miss costed me 3-4 days of R&D and work extra.

SceneKit: isResting never returns true

I am trying to make an app that uses 3D physics and detects when the various nodes in the scene have all come to rest. The logical way to do this would seem to be testing their isResting property on their physics body. However, no matter how stationary the objects are, this value is never set. I have checked that allowsResting is true and am testing the attribute as follows, but I have never seen even one object 'at rest' despite logging out its status on each update. I've also tried putting this in the didRender function. Anyone got any ideas as to why isResting never seems to be set?
var isTotallyAtRest = false
func renderer(aRenderer: SCNSceneRenderer, didSimulatePhysicsAtTime time: NSTimeInterval) {
for object in allObjects {
if let physics = object {
if physics.isResting == false {return}
}
}
isTotallyAtRest = true
}

SKSpriteNode subclass: method to remove all joints

I have created a subclass of SKSpriteNode. I connect instances of that class together with joints of type SKPhysicsJointLimit. I do this within my didEndContact(contact: SKPhysicsContact) in my GameScene:
var joint = SKPhysicsJointLimit.jointWithBodyA(contact.bodyA, bodyB: contact.bodyB, anchorA: pos1!, anchorB: pos2!)
self.physicsWorld.addJoint(joint)
This works well so far.
Then i come to the point where i want to release the node from the joint. According to the SKPhysicsBody docs there is a property called "joints" which is an array holding SKPhysicsJoint objects. I thought thats exactly what I need, but I am not able to iterate over an instance's joints and remove them from the physicsWorld. To do the job i added a method to my custom SKSpriteNode subclass.
func freeJoints(world: SKPhysicsWorld){
if let joints = self.physicsBody?.joints {
for joint in joints{
println("found a joint: \(joint)")
// example print:
//found a joint: <PKPhysicsJointRope: 0x7fbe39e95c50>
world.removeJoint(joint as SKPhysicsJoint)
}
}
}
Calling the method fails after the println() statement with the message "Swift dynamic cast failed". I would really appreciate your opinion in how to work with an SKPhysicsBody's joint property. More specifically: How to use (cast?) the items in the array to be able to remove them from a scene's SKPhysicsWorld.
I spent a little more time in investigating this. This is what I have come up with:
I decided to add an property to my SKSpriteNode subclass and manage the joints myself
var joints: [SKPhysicsJointLimit]
override init(){
...
self.joints = []
...
}
Everytime I add an joint to the scene's SKPHysicsWorld I also add it to the joints array of the SKNNode itself. Whilst iterating the SKPHysicsBody's joints-Array failed (see question) at the point I wanted to cast it to SKPhysicsJoint, removing items from the physics world works as intended when iterating the array of SKPhysicsJointLimit items:
func freeJoints(world: SKPhysicsWorld){
for item in self.joints{
println("removing item from physics world \(item)")
world.removeJoint(item)
}
self.joints.removeAll(keepCapacity: false)
}
}
This seems not to be the most elegant way to do the job, since there already is a framework managed array that promises to be same thing. But I was unable to utilize it and this works for now.

Implementing a life/score system in cocos2d

I am trying to implement a live/score system using cocos2d and box2d. my bodies are box2d bodies which collide as I want them to but the problem is that I have been trying to implement a score system where on each collision a life is removed or reduced from the lives left and after a certain number of collisions(say 3) the game is supposed to stop. On this occasion it doesn't but from the CCLOG I find out that it actually prints out the message I put in to display when the game manager is called but a weird thing is that it calls it forever(see the debugging window below)also it removes the object, "man" from the scene completely on starting the application.
I have a gameManager(singleton) class where in the .h file I have this declared
#interface GameManager : NSObject {
int lives;
}
-(void)removeLives : (int)val;
and in the gameManager.m file I have this method
-(void)removeLives : (int)val
{
lives -=val;
CCLOG(#"YOU HAVE LOST A LIFE MAN");
}
In my main gameLayer.mm file in the update method I have this code
if(kStateColliding){
if (lives > 0) {
man.visible = TRUE;
} else if (lives <= 0) {
[man stopAllActions];
man.visible = FALSE;
[[GameManager sharedGameManager] removeLives:1];
}
}
and the lives is initialised in gameManager init method thus
-(id)init { // 8
self = [super init];
if (self != nil) {
// Game Manager initialized
CCLOG(#"Game Manager Singleton, init");
lives = 3;
}
This is a screen shot of the debug console
Also note that I have a "Man" class which is Box2d class.
Am I doing the correct thing? Please can anyone help me out with how to go about implementing this system and where and how to make the appropriate calls.
Your call to [[GameManager sharedGameManager] removeLives:1] is within the else if (lives <= 0) block. It should only be called when lives > 0.
You will want to do something after the man has no lives left to prevent the if (kStateColliding) block from being called. Also, you will probably want to reset the mans position away from the object it is colliding with to prevent a single "collision" triggering multiple calls to removeLives.
Where are you declaring lives in gameLayer.mm? Make sure it is being set to the value stored in the singleton because with you current code, I dont see how it could possibly by set to 0 or below unless it is being initialized to 0 or you are changing its value elsewhere.
I would ask cocos2d questions like this in the cocos2d forums: http://www.cocos2d-iphone.org/forum/
There you will get answers more quickly, often within minutes.

Resources