Issue creating SKPhysicsBody - swift

I have a plank, which undergoes constant vertical velocity changes. Therefore setting it's dynamic property to false will not do since physicsBody.velocity will not work.
plank.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(plankWidth, plankHeight))
plank.physicsBody?.affectedByGravity = false
plank.physicsBody?.linearDamping = 0
I also have a ball that rests on the plank:
ball.physicsBody = SKPhysicsBody(circleOfRadius: frame.width*CGFloat(1/Float(NumberOfPlanks+1))*0.2)
ball.physicsBody.affectedByGravity = true
ball.physicsBody.linearDamping = false
This ball must be affected by gravity, rest on the plank, but not affect it's motion. This ball must also be able to undergo applyForce and applyImpulse.
Is there a simple way of accomplishing my goal? Thanks :)

Related

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).
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.
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.

Swift 3 (SpriteKit): Making a projectile move

I was wondering how to apply a force or thrust to a projectile (specifically an SKSpriteNode) to make it continuously move until it hits something. I have tried some ways like setting the velocity like this:
let node = SKSpriteNode(imageNamed: "node")
node.physicsBody = SKPhysicsBody(rectangleOf: node.size)
node.physicsBody.velocity = CGVector(dx: 5, dy: 5)
or instead of changing the node's velocity value at the end, I tried:
node.physicsBody?.applyImpulse(CGVector(dx: 5, dy: 5))
or
node.physicsBody?.applyForce(CGVector(dx: 5, dy: 5))
None of these actually move the projectile. I have even tried adding the same impulse and force forever each frame with an SKAction and there is still no movement. I would really appreciate any help :) Thanks.
Try adding a sure fire dynamism to the object:
let node = SKSpriteNode(imageNamed: "node")
node.physicsBody = SKPhysicsBody(rectangleOf: node.size)
// add this, to ensure dynamism:
node.physicsBody.isDynamic = true
Now all forces and impulses should work. Although I'd suggest using much larger numbers. Depends on the size and mass of the object, but you might need numbers a couple of orders of magnitude higher to get rapid movement.

Node is slowing down in Sprite Kit

I'm trying to create an arcade game, where a ball moves at a constant speed and is unaffected by gravity or friction. So I created the ball as an SKShapeNode and set its linearDamping and friction to 0. I also set the game scene to have no gravity. But when playing, if the ball hits another shape node (a circle) at a low angle, it can slow down. The ball's restitution is 1, and allowsRotation is false.
I am keeping the ball moving by applying one impulse at the beginning of the game, that is a random direction.
This might not be the most ideal fix but you could set the fixed speed of the object every update to a specific value which is your constant speed.
The other alternative way to solve this would be to set the fixed speed of the object under the collision delegate functions.
I had a similar problem that was resolved by setting physicsBody?.isDynamic = false on the node that the ball makes contact with.
For example if you have a ball and a brick:
ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.frame.width / 2) // (diameter / 2) = radius
ball.physicsBody?.categoryBitMask = ballCategoryBitMask
// Detect contact with the bottom of the screen or a brick
//
ball.physicsBody?.contactTestBitMask = bottomCategoryBitMask | brickCategoryBitMask
ball.physicsBody?.friction = 0
ball.physicsBody?.allowsRotation = false
ball.physicsBody?.linearDamping = 0
ball.physicsBody?.restitution = 1
ball.physicsBody?.applyImpulse(CGVector(dx: 10, dy: -10))
brick.physicsBody = SKPhysicsBody(rectangleOf: brick.frame.size)
brick.physicsBody?.linearDamping = 0
brick.physicsBody?.allowsRotation = false
brick.physicsBody?.isDynamic = false // Prevents the ball slowing down when it gets hit
brick.physicsBody?.affectedByGravity = false
brick.physicsBody?.friction = 0.0

Stop an SKPhysicsBody mid-motion

If an SKSpriteNode has had an impulse applied to its SKPhysicsBody so that it now has momentum, is there a way I can get it to immediately stop moving? Applying an equal and opposite impulse would probably work in theory, but I would like to know if there is a simpler way. Thanks (:
You can set its velocity to zero:
body.velocity = CGVectorMake(0, 0)
You might also want to set its angular velocity to zero:
body.angularVelocity = 0
And if you want it to become immune to forces and impulses, turn off dynamic:
body.dynamic = false

Turning off detection between sprites

I have two phyicsbodies in my spritekit game which are colliding and I am having a little difficulty getting them to stop.
I am posting the physics code for them below.
Problem is when the swarm touches my coins it pushes it, not exactly sure why since as far as my know my enemy is told to only contact with the player and my coin is told to only contact with the wall and the player (correct me if im wrong)
(P.S I commented out the collisionBitMask for the coin because when I dont my coins fall thru my walls )
Thanks
1St sprite
enemy = [SKSpriteNode spriteNodeWithImageNamed:#"Swarm"];
enemy.physicsBody =
[SKPhysicsBody bodyWithRectangleOfSize:enemy.size];
enemy.physicsBody.dynamic=NO;
enemy.name=#"Eagle";
enemy.physicsBody.categoryBitMask = PCFallersCategory;
// enemy.physicsBody.collisionBitMask =
// PCPlayerCategory;
enemy.physicsBody.contactTestBitMask = PCPlayerCategory;
enemy.physicsBody.restitution=0;
enemy.physicsBody.friction=0;
2nd Sprite.
self.name = #"coin";
CGFloat minDiam = MIN(self.size.width, self.size.height);
minDiam = MAX(minDiam-8, 8);
self.physicsBody =
[SKPhysicsBody bodyWithCircleOfRadius:minDiam/2.0];
self.physicsBody.dynamic=YES;
self.physicsBody.restitution =0;
self.physicsBody.friction = 0;
self.physicsBody.linearDamping = 0;
self.physicsBody.categoryBitMask = PCCollectableCategotry;
//self.physicsBody.collisionBitMask =PCPlayerCategory;
self.physicsBody.contactTestBitMask = PCPlayerCategory|PCWallCategory;
You need to explicitly set the collisionBitMask to 0.
enemy.physicsBody.collisionBitMask = 0;
According to the documentation
When two physics bodies contact each other, a collision may occur.
This body’s collision mask is compared to the other body’s category
mask by performing a logical AND operation. If the result is a
non-zero value, then this body is affected by the collision. Each body
independently chooses whether it wants to be affected by the other
body. For example, you might use this to avoid collision calculations
that would make negligible changes to a body’s velocity.
The default value is 0xFFFFFFFF (all bits set).
This means that by default, an SKPhysicsBody is configured to collide with all objects.