Adding a gravitational field about SpriteNode - swift

I have a game with a character who is affected by normal gravity as he jumps across platforms. He collects coins and power ups. I'm making a power up that makes coins gravitate to the characterSpriteNode.
In the case that magneticPowerUp = true I want it to appear that a gravitational field has activated about the character that makes only the coins attract to the character, like a magnetic field.
I've been fideling with Epic Bytes SO answer here but i'm not having much luck as i don't have much experience with vectors and haven't got too deep into physicsBody.
So I just can't work out the part that will make the coins move once it has decided the coin is close enough to move to the char.
My thoughts are something like this:
override func update(currentTime: NSTimeInterval) {
if magneticPowerUp {
for coin in scoreNode.children {
let disp = CGVector(dx: coin.position.x-character.position.x, dy: coin.position.y-character.position.y)
let radius = sqrt(disp.dx*disp.dx+disp.dy*disp.dy) // Distance between character and coin
if radius < character.frame.height * 3 {
// Use physics methods here to push the coin to the character
let dt: CGFloat = 1.0/60.0
let strength: CGFloat = 10000
let m1 = character.physicsBody!.mass*strength
let m2 = coin.physicsBody!.mass*strength
let force = (m1*m2)/(radius*radius);
let normal = CGVector(dx: disp.dx/radius, dy: disp.dy/radius)
let impulse = CGVector(dx: normal.dx*force*dt, dy: normal.dy*force*dt)
// Something wrong with this?
coin.physicsBody!.velocity = CGVector(dx: coin.physicsBody!.velocity.dx + impulse.dx, dy: coin.physicsBody!.velocity.dy + impulse.dy)
}
}
}
}

SpriteKit has SKFieldNode for creating gravity and magnets that apply to bodies. Normally it deflects charged bodies instead of attracting them like ferromagnetism in the real world.
If you want a field that attracts things you'll need a radialGravityField() method around your hero. To attract specific things like your coins you would use categoryBitMask on the hero field and fieldBitMask on the coin sprites you want to attract.
With the electricField() method you could also have your hero attract different bodies with stronger or weaker forces. Or even attract and repel different bodies at the same time. You can use the charge property of physics bodies.
Code examples:
var field:SKFieldNode?
switch( name )
{
case .Electric:
var electric = SKFieldNode.electricField()
electric.strength = 100.0
bestBodyMass = 0.5
impulseMultiplier = 400
field = electric
case .Magnetic:
var magnetic = SKFieldNode.magneticField()
magnetic.strength = 1.0
bestBodyMass = 0.5
impulseMultiplier = 400
field = magnetic
}
The Apple documentation for SKFieldNode is awesome.
https://developer.apple.com/reference/spritekit/skfieldnode
Here's are two cool YT videos showing the effect.
https://www.youtube.com/watch?v=-mjRPgP0oAE
https://www.youtube.com/watch?v=JGk3agy-c50

Related

Swift UIKit dynamics - Vary UIPushBehavior force magnitude by distance from center of source

Can someone suggest how I can vary the UIPushBehavior magnitude force by distance from source. To mirror the affect of the force of wind from a fan on another object. So the closer the object is to the fan the stronger the force.
if String(describing: identifier) == "fan1" {
self.push = UIPushBehavior(items: [self.object], mode: .instantaneous)
self.push.setAngle(.pi/4, magnitude: 1)
self.animator.addBehavior(self.push)
self.push.addItem(self.object)
}
Use a UIFieldBehaviour to create a linear gravitational field in the direction of the fan. Then you can specify a falloff:
var forceField: UIFieldBehaviour!
// ...
forceField = UIFieldBehavior.linearGravityField(direction: CGVector(dx: 1, dy: -5))
// example values for a "fan" on the bottom left blowing mostly upwards:
forceField.position = CGPoint(x: view.bounds.minX, y: view.bounds.maxY)
forceField.region = UIRegion(radius: 3000)
forceField.minimumRadius = 100
forceField.falloff = 5
forceField.strength = 10
forceField.addItem(view1)
forceField.addItem(view2)
animator.addBehavior(forceField)
Have fun playing around with these values!
Adding collision, another gravity behaviour, and a dynamic item behaviour to two views, we get the following effect:
That feels like a fan on the bottom left to me!
You can also choose a radial gravitational field positioned at where the fan is, if your fan is in a corner and blows "radially", but note that you should use a negative value for strength in that case to say that the field repels rather than attracts.

SceneKit how to move a character on terrain with different height

I am working on a scene with a character and a platform with a stairs. Character must move up and down of the stairs and jump from the platform:
I move character by virtual d-pad. I am looking for a correct way to move a character. Now I have two ways, each has its advantages and disadvantages:
First way: ray test. This way works perfect: I can move character up and down of the stairs, but if I have a lot of objects near the pad, and I want to jump I need to do a lot of ray tests.
Second way: move physics body of the character. Using this way I couldn't understand how to move character down of the stairs. Each time it looks like a jump over the stairs.
So what is the correct way to move a character over terrain with different height?
To move a character over a terrain with different heights you should use collisions. To find out more about it, watch a part on collision meshes in Enhancements to SceneKit at WWDC 2015 (time 17:30) and also look at a code in Fox 2 game's sample.
Here's a code's snippet how to use collisions:
private func loadCharacter() {
let scene = SCNScene( named: "Art.scnassets/character/max.scn")!
model = scene.rootNode.childNode( withName: "Max_rootNode", recursively: true)
model.simdPosition = Character.modelOffset
characterNode = SCNNode()
characterNode.name = "character"
characterNode.simdPosition = Character.initialPosition
characterOrientation = SCNNode()
characterNode.addChildNode(characterOrientation)
characterOrientation.addChildNode(model)
let collider = model.childNode(withName: "collider", recursively: true)!
collider.physicsBody?.collisionBitMask = Int(([ .enemy, .trigger, .collectable ] as Bitmask).rawValue)
let (min, max) = model.boundingBox
let collisionCapsuleRadius = CGFloat(max.x - min.x) * CGFloat(0.4)
let collisionCapsuleHeight = CGFloat(max.y - min.y)
let collisionGeometry = SCNCapsule(capRadius: collisionCapsuleRadius, height: collisionCapsuleHeight)
characterCollisionShape = SCNPhysicsShape(geometry: collisionGeometry, options:[.collisionMargin: Character.collisionMargin])
collisionShapeOffsetFromModel = float3(0, Float(collisionCapsuleHeight) * 0.51, 0.0)
}

Grouping (without collision), adding and removing multiple bodies and polygons in pymunk?

I'm using code from the pymunk index_video to create a generic function that creates multiple cars which race each other and if they reach the right extreme of the screen, they are removed from Space and re-generated on the left extreme of the screen.
The problem is, that in the example code, each part of the car (chassis, pin joint, motor, wheels) is added to Space separately. I wanted to treat the entire car as a single body whose coordinates I can keep track of by storing the reference of the entire car body in a List and add or delete it to the Space easily.
Also, if the wheels are too close to the chassis, they collide with each other. I presume using a ShapeFilter can help avoid such collisions, but for that I need all parts of the car as a single body.
Please bear with me. I'm completely new to this jargon.
def car(space):
pos = Vec2d(100,200)
wheel_color = 52,219,119
shovel_color = 219,119,52
mass = 100
radius = 25
moment = pymunk.moment_for_circle(mass, 20, radius)
wheel1_b = pymunk.Body(mass, moment)
wheel1_s = pymunk.Circle(wheel1_b, radius)
wheel1_s.friction = 1.5
wheel1_s.color = wheel_color
space.add(wheel1_b, wheel1_s)
mass = 100
radius = 25
moment = pymunk.moment_for_circle(mass, 20, radius)
wheel2_b = pymunk.Body(mass, moment)
wheel2_s = pymunk.Circle(wheel2_b, radius)
wheel2_s.friction = 1.5
wheel2_s.color = wheel_color
space.add(wheel2_b, wheel2_s)
mass = 100
size = (50,30)
moment = pymunk.moment_for_box(mass, size)
chassi_b = pymunk.Body(mass, moment)
chassi_s = pymunk.Poly.create_box(chassi_b, size)
space.add(chassi_b, chassi_s)
vs = [(0,0),(25,45),(0,45)]
shovel_s = pymunk.Poly(chassi_b, vs, transform = pymunk.Transform(tx=85))
shovel_s.friction = 0.5
shovel_s.color = shovel_color
space.add(shovel_s)
wheel1_b.position = pos - (55,0)
wheel2_b.position = pos + (55,0)
chassi_b.position = pos + (0,-25)
space.add(
pymunk.PinJoint(wheel1_b, chassi_b, (0,0), (-25,-15)),
pymunk.PinJoint(wheel1_b, chassi_b, (0,0), (-25, 15)),
pymunk.PinJoint(wheel2_b, chassi_b, (0,0), (25,-15)),
pymunk.PinJoint(wheel2_b, chassi_b, (0,0), (25, 15))
)
speed = 4
space.add(
pymunk.SimpleMotor(wheel1_b, chassi_b, speed),
pymunk.SimpleMotor(wheel2_b, chassi_b, speed)
)
So this question is actually two questions.
A. How to make a "car object" that consists of multiple parts
There is no built in support for this, you have keep track of it yourself.
One way to do it is to create a car class that contains all the parts of the car. Something like this (not complete code, you need to fill in the full car)
class Car():
def __init__(self, pos):
self.wheel_body = pymunk.Body()
self.wheel_shape = pymunk.Circle()
self.chassi_body = pymunk.Body()
self.chassi_shape = pymunk.Poly()
self.motor = pymunk.SimpleMotor(wheel_body, chassi_body, 0)
def add_to_space(self, space)
space.add(self.wheel_body, self.wheel_shape, self.chassi_body, self.chassi_shape, self.motor)
def set_speed(self, speed)
self.motor.rate = speed
def car_position(self)
return self.chassi_body.position
B. How to make parts of the car to not collide with each other
This is quite straight forward, just as you already found the ShapeFilter is the way to go. For each "car", create a ShapeFilter and set a unique non-zero group on it. Then set that ShapeFilter as the filter property on each shape that makes up the car. It doesnt matter if the shapes belong to the same body or not, any shape with a ShapeFilter with a group set will not collide to other shapes with the same group set.

Stop objects from colliding using SpriteKit

I am testing out the features of SpriteKit and I ran into a problem. I was reading into bit masks, colliding, category, and contact. I get what they are, mostly at least, I don't get the point of category bitmasks, but I get colliding bitmasks which are the ones I need to solve my problem.
Ok so my problem is I have two different types of sprites: object and second. The names don't really make much sense but it is just for the sake of testing. I want second to have an impulse, and I want object to have a force. I was able to apply the respective vectors on the sprites, but I do not want them to collide with each other. I want them to pass right through and ignore the existence of each other.
I tried to solve that issue by assigning different collision bitmasks to each other:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let texture = SKTexture(imageNamed: "pokeball")
let object = SKSpriteNode(texture: texture)
object.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: object.size.width,height: object.size.height))
object.physicsBody?.affectedByGravity = false
object.yScale = 0.5
object.xScale = 0.5
for t in touches {
object.position = t.location(in: self)
}
self.addChild(object)
object.physicsBody?.collisionBitMask = UInt32(4)
object.physicsBody?.applyForce(CGVector(dx: 0, dy: 10))
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let texture = SKTexture(imageNamed: "purple")
let second = SKSpriteNode(texture: texture)
let impulse : Double = 20
let x = (impulse * Double(cosf(45)))
let y = Double(impulse * Double(sinf(45)))
let vector = CGVector(dx: x, dy: y)
second.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: second.size.width,height: second.size.height))
second.yScale = 1.5
second.xScale = 1.5
second.physicsBody?.isDynamic = true
for t in touches {
second.position = t.location(in: self)
}
self.addChild(second)
second.physicsBody?.collisionBitMask = UInt32(1)
second.physicsBody?.applyImpulse(vector)
}
So object has a bitmask of 4:
object.physicsBody?.collisionBitMask = UInt32(4)
And second has a bitmask of 1:
second.physicsBody?.collisionBitMask = UInt32(1)
I ran the simulator and they are still colliding with each other, so I went online and tried to look for some answers: I found one that says I must use numbers like:
these are bitmasks, you can't use arbitrary numbers 1,2,3,4,5 - you must use 1,2,4,8,16 and so on –
Can someone explain why? However, that wasn't the issue because I was using 1 and 4
Next question I ran into said that I had to use binary numbers (0100) and (0010), I tried them, same issue: still colliding.
I will leave a picture of the collisions:
Collisions
Does anyone know why this is happening? My apologies in advance if this is a really dumb mistake or something that has already been asked, I just couldn't find it.
There is a lot of documentation on these topics, but here is a practical example.
The power of categoryBitMasks
Pretend you have a collection of three nodes pool, basketball and bowlingball. Now, obviously, we want the basketball and bowlingball to collide with the each other. So you set the collisionBitMasks like so:
basketball.physicsBody?.collisionBitMask = UInt32(2)
bowlingball.physicsBody?.collisionBitMask = UInt32(2)
Great. Now, we want the bowlingball to sink to the bottom of the pool, and the basketball to collide with the pool (might be more of a splash, but bear with me). How would we do this? We could try:
pool.physicsBody?.collisionBitMask = UInt32(2) // ?
But wait, that would make the basketball AND the bowlingball collide with the pool. We only want the basketball to collide with the pool , whereas we want the bowlingball to ignore the pool and sink straight to the bottom with no collisions. This is where categoryBitMasks come in handy:
let basketballBitMask = UInt32(1)
let bowlingballBitMask = UInt32(2)
let poolBitMask = UInt32(4) // Why 4? See next section
basketball.physicsBody?.categoryBitMask = basketballBitMask
bowlingball.physicsBody?.categoryBitMask = bowlingballBitMask
pool.physicsBody?.categoryBitMask = poolBitMask
Because each object has a unique number assigned to it, you can now pick and choose which objects you'd like another object to collide with:
// basketball physics body collides with bowlingball(2) OR pool(4)
basketball.physicsBody?.collisionBitMask = bowlingballBitMask | poolBitMask
// ( '|' = logical OR operator)
// bowlingball physics body only collides with basketball(1)
bowlingball.physicsBody?.collisionBitMask = basketballBitMask
// pool physics body only collides with basketball(1)
pool.physicsBody?.collisionBitMask = basketballBitmask
If you're not sure what the strange '|' symbol is doing, I highly recommend the swift documentation on advanced operators to help you understand what's happening here.
Why not just use collisionBitMasks?
Okay so we've set some bit masks. But how are they used? If we only have two objects why can't we just compare collisionBitMasks?
Simply put, that's just not how it works. When a bowlingball comes into contact with the pool, the SpriteKit physics engine will AND ('&') together the bowlingball's categoryBitMask with the pool's collisionBitMask (or vice versa; the result is the same):
objectsShouldCollide = (bowlingball.physicsBody?.categoryBitMask &
pool.physicsBody?.collisionBitMask)
// objectsShouldCollide = (ob010 & 0b100) = 0b000
Because the bowlingball's categoryBitMask and the pool's collisionBitMask have zero bits in common, objectsShouldCollide is equal to zero, and SpriteKit will stop the objects from colliding.
But, in your case, you're not setting your objects' categoryBitMasks. So they have a default value of 2^32, or 0xFFFFFFFF (hexadecimal representation) or in binary, 0b11111111111111111111111111111111. So when an "object" hits a "second" object, SpriteKit does this:
objectsShouldCollide = (0b11111111111111111111111111111111 & // Default categoryBitMask for "object"
0b00000000000000000000000000000001) // collisionBitMask for "second" object
// = 0b00000000000000000000000000000001
So when you haven't defined the object's categoryBitMask, no matter what you set as the second object's collisionBitMask, objectsShouldCollide will never be zero, and they will always collide.
Note: you could set an object's collisionBitMask to 0; but then that object would never be able to collide with anything.
Using powers of 2 (0,1,2,4,8, etc.) for categoryBitMasks
Now let's say we wanted to include multiple bowlingballs that collided with each other. Easy:
bowlingball.physicsBody?.collisionBitMask = basketballBitMask | bowlingballBitMask
// bowlingball collision bit mask (in binary) = 0b10 | 0b01 = 0b11
// bowlingball collision bit mask (in decimal) = 2 | 1 = 3
Here you can see that if we had set the pools physicsCategory to UInt32(3), it would no longer be distinguishable from a bowlingball or basketball.
Further suggestions
Learn to name variables with purpose, even if you're just using them for testing (although, coincidentally, "object and second object" worked quite well).
Use a struct for bitmasks to simplify your code and improve readability:
struct PhysicsCategory {
static let Obj1 : UInt32 = 0b1 << 0
static let Obj2 : UInt32 = 0b1 << 1
static let Obj3 : UInt32 = 0b1 << 2
static let Obj4 : UInt32 = 0b1 << 3
}
obj1.physicsBody?.categoryBitmask = PhysicsCategory.Obj1 // etc
Why don't you download and play with this simple Sprite-Kit project? It creates various geometric shapes, sets some of them to collide, some to contact, uses a checkPhysics() function to show what's going to happen and then lets you flick the shapes around.
Attack button in SpriteKit
Any questions as to it's workings and I'll be more than happy to try and explain.

Sprites following each other

Thanks in advance. I currently have a sprite controlled by the tilting of the device and would like every so often(NSTimer) for another sprite to appear behind it following it with the same physics. I have it set up, but i need the position to update every second so it follow behind it. How would I go about this? This is what I've tried/done.
let snakeBodyY = snakeHead.position.y - 5
let snakeBodyX = snakeHead.position.x - 5
snakeHead = SKSpriteNode(imageNamed: "snakeHead")
snakeHead.size = CGSize(width: 60, height: 60)
snakeHead.position = CGPoint(x: self.frame.midX, y: self.frame.midY - 50)
self.addChild(snakeHead)
snakeBody = SKSpriteNode(imageNamed: "snakeBody")
snakeBody.size = CGSize(width: 50, height: 50)
snakeBody.position.x = snakeBodyX
snakeBody.position.y = snakeBodyY
self.addChild(snakeBody)
Depends on how advance you need it,
if this is a free range 2D world you could assign the each new body to the tail so that the positions are relative to the snake, and always try to make the body parts move to the center of the previous body part up to the head.
Set up your sprite like this:
let snake = SKNode...
let head = SKSpriteNode...
let tail = SKSpriteNode...
//make sure you set up your positions for each body part, they should be relative to snake, not the screen
//setup collision so that tail and head can collide
head.physicsBody = SKPhysicsBody...
head.categoryBitMask = 1
head.collisionBitMask = 1
tail.physicsBody = SKPhysicsBody...
tail.categoryBitMask = 1
tail.collisionBitMask = 1
//setup restitution so that they do not bounce when colliding
head.restitution = 0
tail.restitution = 0
//add the parts to snake
snake.addChild(head)
snake.addChild(tail)
//Now you have a snake with a head and tail create an action to move the snake tail to the center of head
let moveToCenter = SKAction.moveTo(head.position, duration:0)
tail.runAction(moveToCenter)
//this will move the talk to try to get to the center of head, but collision physics will stop it from overlapping.
//Now everytime you want to add a new body
let body = SKSpriteNode...
body.physicsBody = SKPhysicsBody...
body.categoryBitMask = 1
body.collisionBitMask = 1
body.restitution = 0
snake.addChild(body)
let moveToCenter = SKAction.moveTo(tail.position, duration:0)
body.runAction(moveToCenter)
tail = body //lets make the tail the new piece to add
//Then on every update loop. you want to go through the children and do something like this
let previousChild = head
for child in snake.children
{
if child == head
{
continue;
}
child.removeAllActions()
let moveToCenter = SKAction.moveTo(previousChild.position, duration:0)
child.runAction(moveToCenter)
previousChild = child
}
Now if you find that this code may end up running slow for you, and don't mind a more stiff approach, you could also look up SKAction.followPath. Now I am not going to get into the details here, but basically what you need to do is record how your snake moves, then you generate a path with it, and use SKAction.followPath to make all the body parts move along the path.
If you are working in a 2d tiled world that only allows vertical and horizontal movement, you create an array of your sprites, and every time your snake makes a movement, you go down the snake and change its position to the previous body parts position