Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift 3 (SpriteKit): Locking the x axis of a SKSpriteNode and its physicsBody

I really need to know how to lock the x axis of an SKSpriteNode and its physicsBody. I need to keep the SKSpriteNode dynamic and affectedByGravity. The node is on a slope, so this is why it's x axis is moved due to gravity. However, I don't want the x axis of this SKSpriteNode to move due to gravity. Is there a way to lock the x axis in order to achieve this? Thanks for any help :D

Edit: I have tried to apply a constraint to the x value like this:

let xConstraint = SKConstraint.positionX(SKRange(constantValue: 195))
node.constraints?.append(xConstraint)

However this doesn't work and I'm not sure why and how to fix it. Anyone know of a solution?

Edit 2: SKPhysicsJointPin is actually looking more promising. In the comments of the first response/answer to this question, I have been trying to figure how to properly use it in my situation.

An example of my code:

let node = SKSpriteNode(imageNamed: "node")

enum collisionType: UInt32 {
    case node = 1
    case ground = 2
    case other = 4 //the other node is unrelated to the ground and node
}

class GameScene: SKScene, SKPhysicsContactDelegate {

override func didMove(to view: SKView) {
    //Setup node physicsBody
    node.physicsBody = SKPhysicsBody(rectangleOf: node.size)
    node.physicsBody?.categoryBitMask = collisionType.node.rawValue
    node.physicsBody?.collisionBitMask = //[other node that isn't the ground or the node]
    node.physicsBody?.contactTestBitMask = //[other node that isn't the ground or the node]
    node.physicsBody?.isDynamic = true
    node.physicsBody?.affectedByGravity = true
    addChild(node)

    //Physics Setup
    physicsWorld.contactDelegate = self
}

The node is on top of the ground, and the ground is composed of various SKSpriteNode lines that have a volumeBased physicsBody. The ground keeps adding new lines at the front, and removing the ones at the back, and changing the x value of each line by a negative (so the ground appears to moving - this process is performed in an SKAction). These lines (the parts of the ground) are on an angle which is why the node's x axis moves. I want the node to always be at the front of the ground (e.g. always on top of the newly created line). Currently, setting the position of the node like this locks the x axis (solving my issue):

override func didSimulatePhysics() {
    //Manage node position
    node.position.x = 195
    node.position.y = CGFloat([yPosition of the first line of the ground - the yPosition keeps changing]) 
}

Note: ^This^ function is inside the GameScene class

The x axis actually stays the same like this. However, the issue is that now the physicsBody of the node is lower than the centre of the node (which didn't happen before).

like image 653
J.Treutlein Avatar asked Jan 02 '17 06:01

J.Treutlein


2 Answers

A node's constraints property is nil by default. You'll need to create an array of one or more constraints and assign it to the property. For example

let xConstraint = SKConstraint.positionX(SKRange(constantValue: 195))
node.constraints = [xConstraint]

Update

You may want to use a camera node instead of moving the ground in the scene. With a camera node, you move the main character and the camera instead of the ground.

enter image description here

like image 143
0x141E Avatar answered Sep 28 '22 00:09

0x141E


I think you could set the linearDamping property to 0.0

The linearDamping is a property that reduces the body’s linear velocity.

This property is used to simulate fluid or air friction forces on the body. The property must be a value between 0.0 and 1.0. The default value is 0.1. If the value is 0.0, no linear damping is applied to the object.

You should pay attention also to the other forces applied to your SKSpriteNode. The gravitational force applied by the physics world for example where dx value, as you request , should be setted to 0.0:

CGVector(dx:0.0, dy:-4.9)

Remember also that when you apply other forces vectors like velocity you should maintain the dx property to 0.0 as constant if you want to block the x axis.

You could find more details to the official docs

Update (after your details to the comments below):

You could also anchored your sprite to the ground with an SKPhysicsJoint (I don't know your project so this is only for example):

var myJoint = SKPhysicsJointPin.joint(withBodyA: yourSprite.physicsBody!, bodyB: yourGround.physicsBody!, anchor: CGPoint(x: yourSprite.frame.minX, y: yourGround.frame.minY))

self.physicsWorld.add(myJoint)

You can work with the anchor property to create a good joint as you wish or adding more joints.

like image 37
Alessandro Ornano Avatar answered Sep 28 '22 01:09

Alessandro Ornano