SKPhysicsJointLimit EXC_BAD_ACCESS? - ios

I'm using Sprite Kit (iOS), but whenever I try to add a SKPhysicsJointLimit to the physicsWorld, the app crashes with EXC_BAD_ACCESS (code=1, address=0xc0). Other joint types work fine, which is what's confusing me. Here's an example of what crashes:
var node1 = SKSpriteNode(color: SKColor.blueColor(), size: CGSize(width: 50, height: 50))
node1.physicsBody = SKPhysicsBody(rectangleOfSize: CGSize(width: 50, height: 50))
self.addChild(node1)
var node2 = SKSpriteNode(color: SKColor.blueColor(), size: CGSize(width: 50, height: 50))
node2.physicsBody = SKPhysicsBody(rectangleOfSize: CGSize(width: 50, height: 50))
self.addChild(node2)
var joint = SKPhysicsJointLimit()
joint.maxLength = 1000
joint.bodyA = node1.physicsBody
joint.bodyB = node2.physicsBody
self.physicsWorld.addJoint(joint)
When I replace SKPhysicsJointLimit() with SKPhysicsJointFixed() (and remove the line setting maxLength) or some other joint type, the code works as expected.
I'm new to Sprite Kit, any ideas on how to solve this?

The app is crashing because you're not setting the joint's anchor point properties. From the docs, anchorA is
A connection point on the first body in the scene’s coordinate system.
and anchorB is
A connection point on the second body in the scene’s coordinate system.
Here's an example of how to create a SKPhysicsJointLimit object with physics bodies and anchor points as arguments:
let joint = SKPhysicsJointLimit.jointWithBodyA(node1.physicsBody!, bodyB: node2.physicsBody!, anchorA: node1.position, anchorB: node2.position)
joint.maxLength = 1000
physicsWorld.addJoint(joint)
I'm not sure if you can't set the anchor points directly.

Related

SceneKit – Concave collision box

Im importing an object in DAE format and I'm wanting to use it as a collision object however it is concave, but I'm having trouble implementing this in code:
func addCollisionBox() {
let collisionBoxNode = SCNNode()
let collisionBox = importedCollisionBox.rootNode.childNodeWithName("pCube2", recursively: false)
collisionBoxNode.addChildNode(collisionBox!)
collisionBoxNode.position = SCNVector3Make(0, 0, 0)
collisionBoxNode.scale = SCNVector3Make(30, 20, 50)
//collisionBoxNode.physicsBody = SCNPhysicsBody.staticBody()
collisionBoxNode.physicsBody = SCNPhysicsBody.bodyWithType(SCNPhysicsShapeTypeConcavePolyhedron, shape: collisionBox) // This line errors
collisionBoxNode.physicsBody?.restitution = 0.8
collisionBoxNode.name = "collisionBox"
theScene.rootNode.addChildNode(collisionBoxNode)
}
Cant get the line of code to work which has this in it SCNPhysicsShapeTypeConcavePolyhedron as commented in the code.
A "concave" physics body, per SCNPhysicsShapeTypeConcavePolyhedron, must still be "solid" in some sense, and the implied behavior for that collision type is to keep other bodies outside the "solid" form of the body. As such, your cube still has a "solid" interior even if you set its shape type to concave.
(What the concave shape type does do for you is let you make a solid shape that is non-convex — for example, a cube where one of the faces bows inward to produce a bowl shape.)
If you want to confine other bodies to a box-shaped volume, you'll need to create walls: that is, put a few physics bodies around the volume you want to enclose. Here's a quick utility function for something like that:
func wallsForBox(box: SCNBox, thickness: CGFloat) -> SCNNode {
func physicsWall(width: CGFloat, height: CGFloat, length: CGFloat) -> SCNNode {
let node = SCNNode(geometry: SCNBox(width: width, height: height, length: length, chamferRadius: 0))
node.physicsBody = .staticBody()
return node
}
let parent = SCNNode()
let leftWall = physicsWall(thickness, height: box.height, length: box.length)
leftWall.position.x = -box.width / 2
parent.addChildNode(leftWall)
let rightWall = physicsWall(thickness, height: box.height, length: box.length)
rightWall.position.x = box.width / 2
parent.addChildNode(rightWall)
let frontWall = physicsWall(box.width, height: box.height, length: thickness)
frontWall.position.z = box.length / 2
parent.addChildNode(frontWall)
let backWall = physicsWall(box.width, height: box.height, length: thickness)
backWall.position.z = -box.length / 2
parent.addChildNode(backWall)
let topWall = physicsWall(box.width, height: thickness, length: box.length)
topWall.position.y = box.height / 2
parent.addChildNode(topWall)
let bottomWall = physicsWall(box.width, height: thickness, length: box.length)
bottomWall.position.y = -box.height / 2
parent.addChildNode(bottomWall)
return parent
}
This creates a node hierarchy containing visible walls as separate nodes, but you could easily modify it to create invisible walls and/or a single node with a compound physics body. (See SCNPhysicsShape.)

How to create a ring with 3D effect using Sprite Kit?

I want to create a ring with a 3D effect using Sprite Kit. (SEE IMAGES)
I tried subclassing a SKNode and adding two nodes as children. (SEE CODE)
One node was a complete SKShapeNode ellipse, and the other was half ellipse using SKCropNode with a higher zPosition.
It looks good, but the SKCropNode increases the app CPU usage from 40% to 99%.
Any ideas on how to reduce the SKCropNode performance cost, or any alternative to create the same ring 3D effect?
class RingNode: SKNode {
let size: CGSize
init(size: CGSize, color: SKColor)
{
self.size = size
self.color = color
super.init()
ringPartsSetup()
}
private func ringPartsSetup() {
// LEFT PART (half ellipse)
let ellipseNodeLeft = getEllipseNode()
let leftMask = SKSpriteNode(texture: nil, color: SKColor.blackColor(), size: CGSize(
width: ellipseNodeLeft.frame.size.width/2,
height: ellipseNodeLeft.frame.size.height))
leftMask.anchorPoint = CGPoint(x: 0, y: 0.5)
leftMask.position = CGPoint(x: -ellipseNodeLeft.frame.size.width/2, y: 0)
let leftNode = SKCropNode()
leftNode.addChild(ellipseNodeLeft)
leftNode.maskNode = leftMask
leftNode.zPosition = 10 // Higher zPosition for 3D effect
leftNode.position = CGPoint(x: -leftNode.frame.size.width/4, y: 0)
addChild(leftNode)
// RIGHT PART (complete ellipse)
let rightNode = getEllipseNode()
rightNode.position = CGPoint(x: 0, y: 0)
rightNode.zPosition = 5
addChild(rightNode)
}
private func getEllipseNode() -> SKShapeNode {
let ellipseNode = SKShapeNode(ellipseOfSize: CGSize(
width: size.width,
height: size.height))
ellipseNode.strokeColor = SKColor.blackColor()
ellipseNode.lineWidth = 5
return ellipseNode
}
}
You've got the right idea with your two-layer approach and the half-slips on top. But instead of using a shape node inside a crop node, why not just use a shape node whose path is a half-ellipse? Create one using either CGPath or UIBezierPath API — use a circular arc with a transform to make it elliptical — then create your SKShapeNode from that path.
You may try converting your SKShapeNode to an SKSpriteNode. You can use SKView textureFromNode: (but we aware of issues with scale that require you to use it only after the node has been added to the view and at least one update cycle has run), or from scratch using an image (created programatically with a CGBitmapContext, of course).

How to Detect if a SKSpriteNode is in a SKShapeNode

I put a circular attack oval on a blue space ship and, if any SKSpriteNode enters the SKShapeNode (attack oval) the blue space ship can fire on the SKSpriteNode. The problem is that I can't figure out how to do the detection. I have tried physical bodies but, I can't have contact without having a collision.
Here is an example. I want the purple/blue attack circle to detect A,B,C,D,E
Here is the code I think you will need. RSSprite is short for red ship sprite
blueShip.position = CGPoint(x: 800, y: 400)
attackCircle = SKShapeNode(ellipseOfSize: CGSize(width: 1000, height: 400))
attackCircle.position = CGPoint(x: blueShip.position.x, y: blueShip.position.y)
RSSprite!.position = CGPoint(x: 200, y: 700)
RSSprite!.physicsBody = SKPhysicsBody(rectangleOfSize: RSSprite!.size)
In the update method you can check for an intersection of 2 nodes like this:
if([nodeA intersectsNode:nodeB]) {
// you have a collision
}

cocos2d swift how to show a sprite?

So I have my scene presenting and am trying to display a sprite when I press a button. I know the function is called because of a NSLog but I can't get the sprite to show.
func ShowShip() {
var booster = CCBReader.load("ccbResources/Booster")
booster.position = CGPoint(x: 0, y: 0)
self.addChild(booster)
NSLog("created sprite")
}
The log is called but the sprite isn't displayed. I looked at the quickstart tutorial and couldn't see much difference.
edit: tried calling the .png resource directly but got unwrapping error
Try direct method:
//method_1 : read image from disk
var booster = CCSprite(imageNamed:"Booster.png")
booster.position = CGPoint(x: 50, y: 50)
self.addChild(booster, z:3)
//method_2 : read image from sprite sheet
var frame1 = CCSpriteFrameCache.sharedSpriteFrameCache().spriteFrameByName("Booster.png") as CCSpriteFrame
var booster = CCSprite(spriteFrame: frame1)
booster.position = CGPoint(x: 50, y: 50)
self.addChild(booster, z:3)

In SpriteKit on iOS, scaling a textured sprite produces an incorrect frame?

I'm learning SpriteKit game development for the fun of it & I've run across a seemingly simple problem that has me stumped.
Basically, after I scale a textured SKSpriteNode, the frame is NOT what I expect. I have figured out a few hacks to force it to what I want, but I'm trying to understand what is going on. Any ideas appreciated!
Here's my code WITHOUT SCALING:
func addSpaceship()
{
let spaceship = SKSpriteNode.init(imageNamed: "rocketship.png")
spaceship.name = "spaceship"
// spaceship.setScale(0.50)
let debugFrame = SKShapeNode.init(rect: spaceship.frame)
debugFrame.strokeColor = SKColor.greenColor()
spaceship.addChild(debugFrame)
spaceship.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame) - 150)
self.addChild(spaceship)
}
And my app looks like this:
Now, if I comment back in the line of code which scales it (spaceship.setScale(0.50)), I get this:
Notice that the spaceship is scaled down in the second image, but the frame is scaled even smaller. Why?
If I move the scaling line to after I add the spaceship to the scene, it does what I expect, but that seems wrong:
Here's the code with setScale called after adding the spaceship to the scene:
func addSpaceship()
{
let spaceship = SKSpriteNode.init(imageNamed: "rocketship.png")
spaceship.name = "spaceship"
let debugFrame = SKShapeNode.init(rect: spaceship.frame)
debugFrame.strokeColor = SKColor.greenColor()
spaceship.addChild(debugFrame)
spaceship.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame) - 150)
self.addChild(spaceship)
spaceship.setScale(0.50)
}
And here is what running my app looks like then:
So that works, but why is it necessary?
It has been suggested below, that this is a bug with SKShapeNode. But, replacing the SKShapeNode with an SKLabelNode has the same problem:
func addSpaceship()
{
let spaceship = SKSpriteNode.init(imageNamed: "rocketship.png")
spaceship.name = "spaceship"
spaceship.setScale(0.50)
let scoreNode = SKLabelNode(text: "100")
scoreNode.position = CGPointMake(CGRectGetMidX(spaceship.frame), CGRectGetMaxY(spaceship.frame))
scoreNode.fontColor = SKColor.redColor()
scoreNode.fontSize = 15.0
scoreNode.fontName = "Monaco"
scoreNode.zPosition = 10.0
spaceship.addChild(scoreNode)
spaceship.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame) - 150)
self.addChild(spaceship)
}
which gives us:
The intent is to have the score label (scoreNode) centered above the rocket, but as you can see it is on top of the top porthole. There is just something wrong with the spaceship's frame after I call spaceship.setScale.
I have made one additional discovery: the setScale call does not need to be after I add the spaceship to the scene. It just needs to be after I add the debugFrame/scoreNode to the spaceship. If I setScale AFTER that point, all is well:
func addSpaceship()
{
let spaceship = SKSpriteNode.init(imageNamed: "rocketship.png")
spaceship.name = "spaceship"
let scoreNode = SKLabelNode(text: "100")
scoreNode.position = CGPointMake(CGRectGetMidX(spaceship.frame), CGRectGetMaxY(spaceship.frame))
scoreNode.fontColor = SKColor.redColor()
scoreNode.fontSize = 15.0
scoreNode.fontName = "Monaco"
scoreNode.zPosition = 10.0
spaceship.addChild(scoreNode)
spaceship.setScale(0.50)
spaceship.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame) - 150)
self.addChild(spaceship)
}
which results in:
Your problem may be in the order of these two lines :
spaceship.setScale(0.50)
let debugFrame = SKShapeNode.init(rect: spaceship.frame)
You scaled down the spaceship and then calculate the size of the rectangle with the scaled spaceship. Then when rendering the rectangle is scaled down to half its size which is quarter of the original spaceship size.
If you swap the lines, it should work as expected.
In general, it is better to make compose in the real size and then scale the whole just before adding it to the scene.
First of all, let me preface with SKShapeNode is a really funky (maybe buggy class). At least it was in previous iterations of spritekit. If your goal is to add a debug rectangle for physics purposes. You can turn on showsPhysics on your SKView class inside of your GameViewController
Heres my experiment
let redbox = SKSpriteNode(color: SKColor.redColor(), size: CGSize(width: 100, height: 100))
redbox.position = CGPoint(x: self.size.width/2, y: self.size.height/2)
redbox.setScale(0.5)
let debugFrame = SKShapeNode(ellipseOfSize: redbox.size)
debugFrame.strokeColor = SKColor.greenColor()
self.addChild(redbox)
redbox.addChild(debugFrame)
looks same as yours. if i call setScale after i add the nodes then my circle fills up my red square.
Also if I keep everything the same, but I just add my debugframe to the scene directly it will be scaled the right way, weird huh??
ok another test. note I set greenbox to 50% of redboxes size so we can see the redbox beneath. If the bug was occuring here than greenbox would end up filling 25% of the redbox.
let redbox = SKSpriteNode(color: SKColor.redColor(), size: CGSize(width: 100, height: 100))
redbox.position = CGPoint(x: self.size.width/2, y: self.size.height/2)
redbox.setScale(0.5)
let greenbox = SKSpriteNode(color: SKColor.greenColor(), size: CGSize(width: 50, height: 50))
self.addChild(redbox)
redbox.addChild(greenbox)
Ok so i did the same thing using another SKSpriteNode, and it behaves the way we'd expect. So for whatever reason, when you use an SKShapeNode as a child.. setScale is being called twice on it; unless you set the scale after adding the nodes to the scene. But this doesnt happen with SKSpriteNode.
The answer is.. I don't think there's a good answer. It's probably a bug. SKShapeNode has a history of bugs. SpriteKit has a few bugs =/ Someone correct me if I'm wrong.

Resources