I have tree that is built out of a series of joints. The anchor is the base/stump of the tree. My hero is currently not able to walk through the anchor. Setting collisionBitMask = 0 isn't working for the anchor but that approach does work for the individual joint segments.
So, essentially I just want to avoid this collision. Here is the code:
//code for anchor
let chunkHolder = SKSpriteNode(imageNamed: ImageName.ChunkHolder)
chunkHolder.position = anchorPoint
chunkHolder.physicsBody = SKPhysicsBody(circleOfRadius: chunkHolder.size.width / 2)
chunkHolder.physicsBody?.isDynamic = false
chunkHolder.physicsBody?.categoryBitMask = PhysicsCategory.chunkAnchor.rawValue
chunkHolder.physicsBody?.collisionBitMask = 0
addChild(chunkHolder)
//individual tree segements where hero correctly passes through:
for i in 0..<length {
let treeSegment = SKSpriteNode(imageNamed: ImageName.ChunkTexture)
let offset = treeSegment.size.height * CGFloat(i + 1)
treeSegment.position = CGPoint(x: anchorPoint.x, y: anchorPoint.y - offset)
treeSegment.name = "tree" + String(i)
treeSegments.append(treeSegment)
addChild(treeSegment)
treeSegment.physicsBody = SKPhysicsBody(rectangleOf: treeSegment.size)
treeSegment.physicsBody?.collisionBitMask = 0
}
//joints
for i in 1..<length {
let nodeA = treeSegments[i - 1]
let nodeB = treeSegments[i]
let joint = SKPhysicsJointPin.joint(withBodyA: nodeA.physicsBody!, bodyB: nodeB.physicsBody!, anchor: CGPoint(x: nodeA.frame.midX, y: nodeA.frame.minY))
scene.physicsWorld.add(joint)
}
//for reference here is Hero's physics body:
self.physicsBody?.categoryBitMask = PhysicsCategory.hero.rawValue
self.physicsBody?.contactTestBitMask = PhysicsCategory.ground.rawValue
Also setting hero's collisionBitMask to 0 will not currently help because then hero will also fall through the ground / floor.
This was resolved by also setting the categoryBitMask to 0.
chunkHolder.physicsBody?.categoryBitMask = 0
chunkHolder.physicsBody?.collisionBitMask = 0
Related
I'm building a pong/breaker game with a ball and non-static blocks. I'd like the ball to never stop moving, but whenever it hits a block it loses velocity.
I have the restitusion = 1 for all sprites involved, I've tried setting the mass equal to each other and the density and the friction = 0. But, the ball still loses velocity on a bounce.
When the ball hits a block I'm removing it in the didBegin(contact:) function. I've also tried delaying the removal and it didn't help.
I'd like for the ball to have a constant velocity, but still be able to interact with the blocks as later I'd like to add blocks that can be hit without immediately being broken. So, the blocks can't be static but the ball needs to have a constant velocity.
My code for creating the ball node:
func ballNode(_ position: CGPoint?) -> SKSpriteNode {
let node = SKSpriteNode()
node.position = position == nil ? CGPoint(x: size.width/2, y: 100) : position!
node.size = CGSize(width: 17, height: 17)
//background
let background = SKShapeNode(circleOfRadius: 8.5)
background.fillColor = UIColor.white
node.addChild(background)
//physics
node.physicsBody = SKPhysicsBody(circleOfRadius: 8.5)
node.physicsBody?.allowsRotation = true
node.physicsBody?.friction = 0
node.physicsBody?.restitution = 1
node.physicsBody?.linearDamping = 0
node.physicsBody?.angularDamping = 0
node.physicsBody?.categoryBitMask = BallCategory
node.physicsBody?.contactTestBitMask = AddBlockBorderCategory | PaddleCategory
node.physicsBody?.collisionBitMask = PaddleCategory | BlockCategory | BorderCategory
return node
}
My code for creating the block node:
func createBlockNode() -> BlockNode {
let width = (size.width-CGFloat(6*layout[0].count))/CGFloat(layout[0].count)
let height = width*0.5
let nodeSize = CGSize(width: width, height: height)
let node = BlockNode(texture: nil, size: nodeSize)
let background = SKShapeNode(rectOf: nodeSize)
background.fillColor = .darkGray
background.strokeColor = .lightGray
//physics
node.physicsBody = SKPhysicsBody(rectangleOf: nodeSize)
node.physicsBody?.restitution = 1
node.physicsBody?.allowsRotation = true
node.physicsBody?.friction = 0
node.physicsBody?.categoryBitMask = BlockCategory
node.physicsBody?.contactTestBitMask = BallCategory
node.addChild(background)
return node
}
And a screen recording:
screen recording of the ball losing velocity
I'm starting the ball using this:
ball!.physicsBody?.applyForce(CGVector(dx: 0, dy: 50))
I have a ios 9 spritekit game. I am adding a i-beam or a wrecking ball that should swing from a jointed rope like a pendulum. My game requires gravity and I want the beam to react to gravity and sprites that jump on the beam or hit the beam from below. When I use the following code, the beam eventually slows down and comes to rest without any interaction with other sprites. The top node in the jointed node is fixed (i.e., modeling attachment to a crane or building) I start the beam swinging by applying an impulse on the bottom node in the jointed node. I have set the friction, linear dampening, and angular dampening to 0.
What I need the beam to do prior to interacting with any sprites is to swing back and forth where the maximum height on the left swing and right swing is nearly the same throughout time. I want the beam or wrecking ball to act like its swinging from a frictionless pivot. The beam or ball doesn't go in a full circle, so I cannot use a constant angular velocity.
I tried something similar to:
Constant speed orbit around point with SKNode but neither the linear nor angular velocity is constant after the initial impulse as the beam or ball will arc up, slow down, stop at the top of the arc, and then circle back in the other direction.
let (actionSceneObjectNode, jointArray) = ActionSceneObject.createActionJointedSceneObjectAtPosition(CGPoint(x: positionX, y: positionY), ofType: actionSceneObject.type!, withImage: actionSceneObject.imageName, withActionDirection: actionSceneObject.imageDirection)
foregroundNode.addChild(actionSceneObjectNode)
if let bottomNode = actionSceneObjectNode.childNodeWithName("bottomObject") {
bottomNode.physicsBody?.applyImpulse(CGVector(dx: 50000.0, dy: 0))
}
// add the joints
for joint in jointArray {
self.physicsWorld.addJoint(joint)
}
Function
class func createActionJointedSceneObjectAtPosition(position: CGPoint, ofType type: ActionSceneObjectType, withImage imageName: String, withActionDirection actionDirection: DirectionValue) -> (ActionSceneObjectNode, [SKPhysicsJoint]) {
let node = ActionSceneObjectNode()
node.position = position
node.name = SceneObjectType.Action.rawValue
node.actionType = type
node.actionDirection = actionDirection
var jointArray = [SKPhysicsJoint]()
var sprite: SKSpriteNode
////////
// adapted from https://stackoverflow.com/questions/20811931/how-to-create-a-rope-in-spritekit
let countJointElements:Int = 3
let texture = SKTexture(imageNamed: "Rope.png")
//let textureSize = CGSize(width: texture.size().width*SceneObjectSizeScale.ActionSceneObject, height: texture.size().height*SceneObjectSizeScale.ActionSceneObject)
let topAnchor = SKSpriteNode(texture: texture, size: texture.size())
topAnchor.name = "topAnchor"
//topAnchor.position = CGPointMake(position.x, position.y) // the node holds the joint start position
topAnchor.physicsBody = SKPhysicsBody(rectangleOfSize: texture.size())
topAnchor.physicsBody?.categoryBitMask = PhysicsCategoryBitmask.None
topAnchor.physicsBody?.affectedByGravity = false
topAnchor.physicsBody?.friction = 0.0
topAnchor.physicsBody?.restitution = 1.0
topAnchor.physicsBody?.linearDamping = 0.0
topAnchor.physicsBody?.angularDamping = 0.0
topAnchor.physicsBody?.mass = 10.0
node.addChild(topAnchor)
// by default, the joints build top to bottom
for index in 0 ..< countJointElements {
let item = SKSpriteNode(texture: texture, size: texture.size())
item.name = "ropeitem_" + String(index)
item.position = CGPointMake(0, 0 - CGFloat(index+1) * item.size.height)
item.physicsBody = SKPhysicsBody(rectangleOfSize: texture.size())
item.physicsBody?.categoryBitMask = PhysicsCategoryBitmask.None
item.physicsBody?.affectedByGravity = true
item.physicsBody?.friction = 0.0
item.physicsBody?.restitution = 1.0
item.physicsBody?.linearDamping = 0.0
item.physicsBody?.angularDamping = 0.0
item.physicsBody?.mass = 10.0
node.addChild(item)
var bodyA = SKPhysicsBody()
if (index == 0)
{
bodyA = topAnchor.physicsBody!;
}
else
{
let nameString = "ropeitem_" + String(index - 1)
let nodeItem = node.childNodeWithName(nameString) as! SKSpriteNode
bodyA = nodeItem.physicsBody!
}
// needs to in terms of the physics world - the item position in the node is already negative
let joint = SKPhysicsJointPin.jointWithBodyA(bodyA, bodyB: item.physicsBody!, anchor: CGPointMake(position.x, position.y + item.position.y + item.size.height/2))
jointArray.append(joint)
}
let nameString = NSString(format: "ropeitem_%d", countJointElements - 1)
let lastLinkItem = node.childNodeWithName(nameString as String)
let bottomObject = SKSpriteNode(imageNamed: "I-Beam.png")
bottomObject.name = "bottomObject"
bottomObject.setScale(SceneObjectSizeScale.Platform)
bottomObject.position = CGPointMake(0, 0 + lastLinkItem!.position.y - lastLinkItem!.frame.size.height/2.0 - bottomObject.frame.size.height/2.0)
bottomObject.physicsBody = SKPhysicsBody(rectangleOfSize: bottomObject.size)
bottomObject.physicsBody?.categoryBitMask = PhysicsCategoryBitmask.Platform
bottomObject.physicsBody?.affectedByGravity = true
bottomObject.physicsBody?.friction = 0.0
//bottomObject.physicsBody?.restitution = 1.0
bottomObject.physicsBody?.linearDamping = 0.0
bottomObject.physicsBody?.angularDamping = 0.0
bottomObject.physicsBody?.mass = 500.0
node.addChild(bottomObject)
let jointLast = SKPhysicsJointFixed.jointWithBodyA(lastLinkItem!.physicsBody!, bodyB: bottomObject.physicsBody!, anchor: CGPointMake(position.x, position.y + bottomObject.position.y + bottomObject.frame.size.height/2.0))
jointArray.append(jointLast)
///////
///////
sprite = SKSpriteNode(imageNamed: imageName)
//sprite.setScale(SceneObjectSizeScale.ActionSceneObject)
node.sprite = sprite
//node.addChild(sprite)
node.physicsBody = SKPhysicsBody(texture: texture, size: sprite.size)
node.physicsBody!.categoryBitMask = PhysicsCategoryBitmask.None
switch actionDirection {
case .Left:
node.sprite.zRotation = CGFloat(M_PI_2)
case .Right:
node.sprite.zRotation = CGFloat(-M_PI_2)
case .Down:
node.sprite.zRotation = CGFloat(M_PI)
case .Up, .Unknown:
break
}
node.physicsBody?.dynamic = true
node.physicsBody?.restitution = 0.0 // bounciness
node.physicsBody?.categoryBitMask = PhysicsCategoryBitmask.ActionSceneObject
node.physicsBody?.collisionBitMask = 0
return (node, jointArray)
}
please forgive me as I'm a bit of a Swift noob and am creating my first game :D
Ok, so I have two nodes... one is Guava and the other is Pepper. Guava moves from the bottom of the screen to the top. I want the Pepper to move from the top of the screen to the bottom. Here's the code I have, which works fine for the Guava.
// movement of guavas
let guavaToMove = CGFloat(self.frame.size.width + 2.0 * guavaNodeTexture.size().width)
let moveGuava = SKAction.moveByX(-guavaToMove, y: self.frame.size.width, duration: NSTimeInterval(0.01 * guavaToMove))
let removeGuava = SKAction.removeFromParent()
// movement of peppers
let pepperToMove = CGFloat(self.frame.size.width + 2.0 * pepperNodeTexture.size().width)
let movePepper = SKAction.moveByX(-pepperToMove, y: self.frame.size.width, duration: NSTimeInterval(0.01 * pepperToMove))
let removePepper = SKAction.removeFromParent()
GuavaMoveAndRemove = SKAction.sequence([moveGuava,removeGuava])
PepperMoveAndRemove = SKAction.sequence([movePepper,removePepper])
Now I have the Guava Node anchored to the bottom of the screen and it floats up perfectly. I have the Pepper Node anchored to the top of the screen and am trying to get it to float down in a similar fashion.
Here is the code for both functions, spawnGuava and spawnPepper where both nodes/textures are anchored:
func spawnPepper(){
//The way the Guava spawn and move
let pepperNode = SKNode()
let pepper = SKSpriteNode(texture: pepperNodeTexture)
pepper.setScale(0.30)
pepper.position = CGPointMake (self.size.width + 140, self.size.height * 0.75)
pepper.alpha = 0.75
pepper.physicsBody = SKPhysicsBody(rectangleOfSize: pepper.size)
pepper.physicsBody?.affectedByGravity = false
pepper.physicsBody?.dynamic = true
pepper.physicsBody?.categoryBitMask = PhysicsCatagory.Guava
pepper.physicsBody?.collisionBitMask = 1
pepper.physicsBody?.contactTestBitMask = PhysicsCatagory.Boognish
pepper.zPosition = 50
pepperNode.addChild(pepper)
pepper.runAction(PepperMoveAndRemove)
self.addChild(pepperNode)
if scoreIncreased == true {
}
//the way the pepper collide
}
func spawnGuava(){
//The way the Guava spawn and move
let guavaNode = SKNode()
let guava = SKSpriteNode(texture: guavaNodeTexture)
guava.setScale(0.75)
guava.position = CGPointMake (self.size.width - 40, self.size.height * 0.05)
guava.physicsBody = SKPhysicsBody(rectangleOfSize: guava.size)
guava.physicsBody?.affectedByGravity = false
guava.physicsBody?.dynamic = true
guava.alpha = 0.75
guava.physicsBody?.categoryBitMask = PhysicsCatagory.Guava
guava.physicsBody?.collisionBitMask = 1
guava.physicsBody?.contactTestBitMask = PhysicsCatagory.Boognish
guava.zPosition = 0
guavaNode.addChild(guava)
guava.runAction(GuavaMoveAndRemove)
self.addChild(guavaNode)
if scoreIncreased == true {
}
//the way the guava collide
}
Please help me configure this code accordingly so that the peppers fall from the sky :)
THANKS in advance. First post :)
FYI: I have tried modifying the code many times myself to no avail, so I have reverted it to the baseline you see now. IF I anchored the pepper to where the Guava is anchored then both would float up at the same time.
You're trying to move guavas and peppers in the opposite direction, but it seems like you are moving them in the same direction. Try removing or adding negative signs in movePepper:
let movePepper = SKAction.moveByX(-pepperToMove, y: -self.frame.size.width, duration: NSTimeInterval(0.01 * pepperToMove))
// movement of peppers
let pepperToMove = CGFloat(self.frame.size.width + 2.0 * pepperNodeTexture.size().width)
let movePepper = SKAction.moveByX(-pepperToMove, y: self.frame.size.height / -1, duration: NSTimeInterval(0.01 * pepperToMove))
let removePepper = SKAction.removeFromParent()
just has to divide the y value by a negative, derp... :D
Is there a way to give an SKNode its own physics? I have an SKShapeNode call "backGround" which I use for the parent node of most of my other nodes. I am constantly moving "background" to the left, to give the illusion that the player is moving forward. However, one of the objects that has "backGround" as a parent node is a pin with a rope hanging from it. When background accelerates to the left, is there a way to make it so the rope doesn't swing back and forth, as ropes tend to do when accelerating or decelerating?
EDIT: Here is my code:
func createRopeNode(pos: CGPoint) -> SKSpriteNode{
let ropeNode = SKSpriteNode(imageNamed: "Ball")
ropeNode.size = CGSize(width: 5, height: 5)
ropeNode.physicsBody = SKPhysicsBody(rectangleOfSize: ropeNode.size)
ropeNode.physicsBody?.affectedByGravity = true
ropeNode.physicsBody?.collisionBitMask = 0
ropeNode.alpha = 1
ropeNode.position = CGPoint(x: pos.x + 0, y: pos.y)
ropeNode.name = "RopePiece"
let text = SKSpriteNode(imageNamed: "RopeTexture")
ropeNode.zPosition = -5
text.runAction(SKAction.rotateByAngle(atan2(-dx!, dy!), duration: 0))
ropeNode.addChild(text)
return ropeNode
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
/* Called when a touch begins */
if (!playerIsConnected){
playerIsConnected = true
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
dx = pin.position.x - playerPoint!.x
dy = pin.position.y - playerPoint!.y
let length = sqrt(pow(dx!, 2) + pow(dy!, 2))
let distanceBetweenRopeNodes = 5
let numberOfPieces = Int(length)/distanceBetweenRopeNodes
var ropeNodes = [SKSpriteNode]()
//adds the pieces to the array at respective locations
for var index = 0; index < numberOfPieces; ++index{
let point = CGPoint(x: pin.position.x + CGFloat((index) * distanceBetweenRopeNodes) * sin(atan2(dy!, -dx!) + 1.5707), y: pin.position.y + CGFloat((index) * distanceBetweenRopeNodes) * cos(atan2(dy!, -dx!) + 1.5707))
let piece = createRopeNode(point)
ropeNodes.append(piece)
world.addChild(ropeNodes[index])
}
let firstJoint = SKPhysicsJointPin.jointWithBodyA(ropeNodes[0].physicsBody, bodyB: pin.physicsBody, anchor:
CGPoint(x: (ropeNodes[0].position.x + pin.position.x)/2, y: (ropeNodes[0].position.y + pin.position.y)/2))
firstJoint.frictionTorque = 1
self.physicsWorld.addJoint(firstJoint)
for var i = 1; i < ropeNodes.count; ++i{
let nodeA = ropeNodes[i - 1]
let nodeB = ropeNodes[i]
let middlePoint = CGPoint(x: (nodeA.position.x + nodeB.position.x)/2, y: (nodeA.position.y + nodeB.position.y)/2)
let joint = SKPhysicsJointPin.jointWithBodyA(nodeA.physicsBody, bodyB: nodeB.physicsBody, anchor: middlePoint)
joint.frictionTorque = 0.1
self.physicsWorld.addJoint(joint)
}
finalJoint?.frictionTorque = 1
finalJoint = SKPhysicsJointPin.jointWithBodyA(ropeNodes[ropeNodes.count - 1].physicsBody, bodyB: player.physicsBody, anchor:
CGPoint(x: (ropeNodes[ropeNodes.count - 1].position.x + playerPoint!.x)/2, y: (ropeNodes[ropeNodes.count - 1].position.y + playerPoint!.y)/2))
self.physicsWorld.addJoint(finalJoint!)
}
}
else{
physicsWorld.removeJoint(finalJoint!)
playerIsConnected = false
}
}
Anchor points are what you are looking for. Move the anchor point of the scene to only move the "camera" of the scene (what is displayed onscreen). This will not jostle the pin and rope. Keep in mind that the anchor point is on a slightly different scale from the scene.
Where the width of the scene could be 1024, the "width" of the of the anchor point for one scene length is 1 (basically counting as one width of the node). Same for the height, where it could be 768, the "height" would still be 1 in the anchor point coordinate space. So to move half a screen width, move the anchor point 0.5
The anchor point is a CGPoint, so you can go vertically as well. Here's a quick example:
var xValue : Float = 0.75
var yValue : Float = 0.0
self.scene?.anchorPoint = CGPointMake(xValue, yValue);
And for further reading, here's a link to the documentation on anchor points for sprites.
I'm trying to implement a rope in Swift SpriteKit and to add physics to it, the position is good for all, but they won't attach, when I hit play they all fall except the first one which is the "holder". Here is my code:
// create rope holder
let chainHolder = SKSpriteNode(imageNamed: "chainHolder")
chainHolder.position.y = self.frame.maxY - chainHolder.size.height
chainHolder.physicsBody = SKPhysicsBody(circleOfRadius: chainHolder.size.width / 2)
chainHolder.physicsBody?.dynamic = false
//chainHolder.physicsBody?.allowsRotation = true
chains.append(chainHolder)
addChild(chainHolder)
// add each of the rope parts
for i in 0...5 {
let chainRing = SKSpriteNode(imageNamed: "chainRing")
let offset = chainRing.size.height * CGFloat(i + 1)
chainRing.position = CGPointMake(chainHolder.position.x, chainHolder.position.y - offset)
chainRing.name = String(i)
chainRing.physicsBody = SKPhysicsBody(rectangleOfSize: chainRing.size)
//chainRing.physicsBody?.allowsRotation = true
chains.append(chainRing)
addChild(chainRing)
}
// set up joints between rope parts
for i in 1...5 {
var nodeA = chains[i - 1]
var nodeB = chains[i]
var joint = SKPhysicsJointPin.jointWithBodyA(nodeA.physicsBody, bodyB: nodeB.physicsBody,
anchor: CGPointMake(CGRectGetMidX(nodeA.frame), CGRectGetMinY(nodeA.frame)))
physicsWorld.addJoint(joint)
}
I found out that the problem was because I was setting the anchor point for the scene in (0.5, 0.5). If I leave it in (0, 0) everything is ok.