Collision Detection In Sprite Kit Swift - ios

This is my first time making a game in iOS so naturally I came across a couple of problems. I've managed to solve them but now that it comes to the collision part between mario and the pipes I'm having some trouble:
What I would like to do:
When mario collides with the pipes, he will have to jump over them to get to the other side. If he does not then the pipes will continue to 'push' him to the edge of the screen where the game will end.
What actually happens:
No collision is detected whatsoever.
Here is the code below:
import SpriteKit
import GameplayKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var mario = SKSpriteNode()
enum ColliderType: UInt32 {
case Mario = 1
case Object = 2
}
func addPipes() {
let pipetexture = SKTexture(imageNamed: "pipes")
let pipes = SKSpriteNode(texture: pipetexture)
let setScale = CGFloat(arc4random_uniform(19) + 15)
pipes.setScale(setScale) //min 15, max 19
pipes.position = CGPoint(x: self.frame.maxX, y: self.frame.minY)
let movePipes = SKAction.move(by: CGVector(dx: -5 * self.frame.width, dy: 0), duration: TimeInterval(self.frame.width/100))
let removePipes = SKAction.removeFromParent()
let moveAndRemovePipes = SKAction.sequence([movePipes, removePipes])
pipes.zPosition = +1
pipes.physicsBody = SKPhysicsBody(rectangleOf: pipes.size)
pipes.physicsBody!.isDynamic = false
pipes.physicsBody!.contactTestBitMask = ColliderType.Object.rawValue
pipes.physicsBody!.categoryBitMask = ColliderType.Object.rawValue
pipes.physicsBody!.collisionBitMask = ColliderType.Mario.rawValue
self.addChild(pipes)
pipes.run(moveAndRemovePipes)
}
func didBegin(_ contact: SKPhysicsContact) {
print("contact")
}
override func didMove(to view: SKView) {
self.physicsWorld.contactDelegate = self
_ = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(self.addPipes), userInfo: nil, repeats: true)
let marioTexture = SKTexture(imageNamed: "mariorun0.png")
let marioTexture2 = SKTexture(imageNamed: "mariorun1.png")
let marioTexture3 = SKTexture(imageNamed: "mariorun2.png")
let animation = SKAction.animate(with: [marioTexture, marioTexture2, marioTexture3], timePerFrame: 0.1)
let makeMarioRun = SKAction.repeatForever(animation)
mario = SKSpriteNode(texture: marioTexture)
mario.setScale(10)
mario.position = CGPoint(x: -(self.frame.size.width/3.5), y: -(self.frame.size.height/2.8))
mario.run(makeMarioRun)
mario.physicsBody = SKPhysicsBody(rectangleOf: mario.size)
mario.physicsBody!.isDynamic = false
mario.physicsBody!.contactTestBitMask = ColliderType.Object.rawValue
mario.physicsBody!.categoryBitMask = ColliderType.Mario.rawValue
mario.physicsBody!.collisionBitMask = ColliderType.Object.rawValue
self.addChild(mario)
addPipes()
mario.zPosition = +2
}
}
I have also implemented a jump function that works perfectly. My problem is that I cannot get a collision detection between the pipes and mario.
I have tried setting the z position for both sprites to the same value without any luck.
My initial worry was that the images were to small and hence a collision would be near impossible to detect. But since I am scaling up both nodes and using the physics body of the scaled up images then there wouldn't be a problem, but that doesn't help either.
Thanks for your help in advance.

physicsBody!.isDynamic = false means that no physics activity will happen to this body. Other bodies can interact with it, but it will not interact with anything.
This makes sense for a pipe to have, because a pipe never moves, so there is no reason to be dynamic. (Your world should be moving, not the pipe, if you are doing a flappy bird-esc game)
Now for Mario, this does not make sense. Mario is a moving entity, so he will be interacting with the physics world around in. To fix your issue, you need to make him dynamic with mario.physicsBody!.isDynamic = true

Related

Change SKSpriteNode.isHidden to false when hit by raycast, otherwise set it to true

I am attempting to create a line of sight/field of view on a 2d game and am having some issues. I currently am trying a raycast as a solution. The idea is that by default all my enemy nodes have isHidden = true. When the raycast hits them it should change that value to be false and when the raycast is no longer hitting them it should change it back to true.
I've messed around with a lot of different options here. I have tried keeping track of the raycast hit variable like suggested here.
I currently have it set up in an if-else statement to handle it, but this ends up having the sprites flash in and out if the raycast is hitting and then will leave isHidden = false if it was true originally.
scene?.physicsWorld.enumerateBodies(alongRayStart: rayStart, end: rayEnd) { (body, point, normal, stop) in
let sprite = body.node as? SKSpriteNode
if body.categoryBitMask == 2 {
sprite?.isHidden = true
}else if body.categoryBitMask == 1 {
} else { sprite?.isHidden = true }
}
I have the above code as part of a function that I am then calling in the update() function.
I expect the sprite to have the value of isHidden = false only when currently being hit by the raycast. Unfortunately it isn't working that way.
I would first have all my enemies in a special node container in the scene, then before casting a ray, reset the enemies isHidden property by using a forloop.
enemyContainer.children.forEach{$0.isHidden = false}
scene?.physicsWorld.enumerateBodies(alongRayStart: rayStart, end: rayEnd) { (body, point, normal, stop) in
if let sprite = body.node as? SKSpriteNode,sprite.categoryBitMask & ~0b1 \\Replace 0b1 with your category bit mask constant if you created one
sprite.isHidden = true
}
}
This will allow you to immediately figure out if a light is touching an enemy, as opposed to E.Coms answer, which will only tell you when an enemy is hit during the physics update phase
if you do not want your enemies in a special container, then you can create an array and store only the ones that were touched the last time the ray was cast
//Global to class
var nodesInLight = [SKNode]()
.......
//in func
nodesInLight.forEach{$0.isHidden = false}
nodesInLight = [SKNode]() //Allow ARC to remove the previous container, instead of calling clear on it
scene?.physicsWorld.enumerateBodies(alongRayStart: rayStart, end: rayEnd) { (body, point, normal, stop) in
if let sprite = body.node as? SKSpriteNode,sprite.categoryBitMask & ~0b1 \\Replace 0b1 with your category bit mask constant if you created one
sprite.isHidden = true
nodesInLight.append(sprite)
}
}
An advance technique I would do is create an array of weak references, this way you do not accidentally create a retain cycle
In this case, you may give the ray a real physicsbody, and implement the contact delegate. In the DidBegin func, to turn the enemy on. and DidEnd func, turn the enemy off.
func didBegin(_ contact: SKPhysicsContact) {
print("begin")
}
func didEnd(_ contact: SKPhysicsContact) {
print("end")
}
override func didMove(to view: SKView) {
view.showsPhysics = true
let ray = SKShapeNode()
ray.path = UIBezierPath(rect: CGRect(x: 0, y: 0, width: 1000, height: 20)).cgPath
ray.fillColor = UIColor.red
addChild(ray)
ray.physicsBody = SKPhysicsBody(rectangleOf:(ray.path?.boundingBoxOfPath)!.size)
ray.physicsBody?.affectedByGravity = false
ray.run(SKAction.repeatForever(SKAction.rotate(byAngle: CGFloat(Float.pi * 2), duration: 3)))
ray.physicsBody?.contactTestBitMask = 1
ray.physicsBody?.collisionBitMask = 0
let enemy = SKSpriteNode(color: UIColor.green, size: CGSize.init(width: 100, height: 100))
enemy.physicsBody = SKPhysicsBody(rectangleOf: enemy.frame.size)
enemy.physicsBody?.affectedByGravity = false
enemy.position = CGPoint(x: 200, y: 200)
enemy.physicsBody?.contactTestBitMask = 1
enemy.physicsBody?.collisionBitMask = 0
addChild(enemy)
enemy.isHidden = true
physicsWorld.contactDelegate = self

Spritekit : how to make newly spawned enemy ships shoot bullets at player?

I need help with coming up with ways to make to make the newly spawned enemy ships all shoot bullets. The problem is that I don't actually know how I am going to do that. I tried using a variable timer but, it only shoots one straight line and I can't really control the location it spawns at.
Game Picture: There are more enemies spawning
What I Tried:
var EnemyTimer = Foundation.Timer.scheduledTimer(timeInterval: 0.3, target: self, selector: #selector(GameScene.spawnBullet3), userInfo: nil, repeats: true)
Also Tried:
}
func spawnBullet3(){
let Bullet = SKSpriteNode(imageNamed: "eBullet.png")
Bullet.setScale(1)
Bullet.zPosition = -1
Bullet.position = CGPoint(x: enemy.position.x, y: enemy.position.y)
let action = SKAction.moveTo(y: self.size.height + 100, duration: 0.5)
let actionDone = SKAction.removeFromParent()
Bullet.run(SKAction.sequence([action, actionDone]))
Bullet.physicsBody = SKPhysicsBody(rectangleOf: Bullet.size)
Bullet.physicsBody?.categoryBitMask = PhysicsCategories.Bullet
Bullet.physicsBody!.collisionBitMask = PhysicsCategories.None
Bullet.physicsBody?.contactTestBitMask = PhysicsCategories.Enemy
Bullet.physicsBody?.affectedByGravity = true
Bullet.physicsBody?.isDynamic = false
self.addChild(Bullet)
}
Well, for starters, (as LearnCocos2D explained wonderfully here) never user a Timer, performSelector:afterDelay:, or Grand Central Dispatch (GCD, ie any dispatch_... method.
Before anything, you must set up a couple of things:
First, replace add an argument to your spawnBullet3 function (If you use a custom class replace enemy:SKSpriteNode with enemy:yourCustomClass).
func spawnBullet3(enemy:SKSpriteNode){
let Bullet = SKSpriteNode(imageNamed: "eBullet.png")
Bullet.setScale(1)
Bullet.zPosition = -1
Bullet.position = CGPoint(x: enemy.position.x, y: enemy.position.y)
let action = SKAction.moveTo(y: self.size.height + 100, duration: 0.5)
let actionDone = SKAction.removeFromParent()
Bullet.run(SKAction.sequence([action, actionDone]))
Bullet.physicsBody = SKPhysicsBody(rectangleOf: Bullet.size)
Bullet.physicsBody?.categoryBitMask = PhysicsCategories.Bullet
Bullet.physicsBody!.collisionBitMask = PhysicsCategories.None
Bullet.physicsBody?.contactTestBitMask = PhysicsCategories.Enemy
Bullet.physicsBody?.affectedByGravity = true
Bullet.physicsBody?.isDynamic = false
self.addChild(Bullet)
}
Second, create a public enemies array (Again, if you use a custom class replace SKSpriteNode with your custom class name).
var enemies:[SKSpriteNode] = []
Next, every time you create a new enemy, append the enemy to the array
enemies.append(newEnemy)
Now you have two options:
Option 1: Use a SKAction
Use a SKAction timer to automate every enemy shooting every couple of seconds (Replace SKAction.waitForDuration(2.5) with SKAction.waitForDuration(yourAmountOfSeconds)).
for e in enemies
{
var wait = SKAction.waitForDuration(2.5)
var run = SKAction.runBlock {
spawnBullet3(e)
}
e.runAction(SKAction.repeatActionForever(SKAction.sequence([wait, run])))
}
Option 2: Use the update function
Use the currentTime from the update function (Replace currentTime.truncatingRemainder(dividingBy: 3) with currentTime.truncatingRemainder(dividingBy: yourAmountOfSeconds)).
override func update(_ currentTime: TimeInterval) {
[...]
if currentTime.truncatingRemainder(dividingBy: 3) == 0
{
for e in enemies
{
spawnBullet3(e)
}
}
}

Swift + Physics: Wrong angle calculation on collision

I have a simple SpriteKit App with walls and a ball. Both setup with a SKPhysicsBody.
When I apply a force in one direction, I expect the ball to reflect at the wall with the same angle when it collide but in opposite direction.
But sometimes I see the angle is weird. I played a lot with all the physicsBody properties, but was not able to fix it. Sometimes the first reflections look good, but then the third or sixth and sometimes the first reflection is at wrong angle.
I read from different posts, people are kinda self-calculating the "correct direction". But I can't imagine SpriteKits physic engine is not capable to do so.
Check my attached image to understand what I mean. Can anybody help on this? I don't want to start playing around with Box2d for Swift, since this looks like it will be too hard for me to integrate into Swift.
This is the physicsWorld init on my GameScene.swift file where all my elements are added to:
self.physicsWorld.gravity = CGVectorMake(0, 0)
Adding all my code would be way too much, so I add the important pieces. Hope its enough for analyze. All elements like the ball, walls are SKSpriteNode's
This is the physicsBody code for the ball:
self.physicsBody = SKPhysicsBody(circleOfRadius: Constants.Config.playersize+self.node.lineWidth)
self.physicsBody?.restitution = 1
self.physicsBody?.friction = 0
self.physicsBody?.linearDamping = 1
self.physicsBody?.allowsRotation = false
self.physicsBody?.categoryBitMask = Constants.Config.PhysicsCategory.Player
self.physicsBody?.collisionBitMask = Constants.Config.PhysicsCategory.Wall | Constants.Config.PhysicsCategory.Enemy
self.physicsBody?.contactTestBitMask = Constants.Config.PhysicsCategory.Wall | Constants.Config.PhysicsCategory.Enemy
This is the physicsBody for the walls:
el = SKSpriteNode(color: UIColor.blueColor(), size: CGSize(width: Constants.Config.wallsize, height: Constants.Config.wallsize))
el.physicsBody = SKPhysicsBody(rectangleOfSize: CGSize(width: Constants.Config.wallsize, height: Constants.Config.wallsize))
el.physicsBody?.dynamic = false
el.physicsBody?.categoryBitMask = Constants.Config.PhysicsCategory.Wall
el.physicsBody?.collisionBitMask = Constants.Config.PhysicsCategory.Player
el.physicsBody?.contactTestBitMask = Constants.Config.PhysicsCategory.Player
At the end I just call the applyImpulse function on the balls physicsBody to make it moving in the physics simulation.
Also check my attached second image/gif. It shows the edge collision problem with a simple skphysics app without any special parameterization. just a rectangle with a ball in it and one vector applied as impulse.
Heres my full non-working code using the solution from appzYourLife.
class GameScene: SKScene {
private var ball:SKShapeNode!
override func didMoveToView(view: SKView) {
let screen = UIScreen.mainScreen().bounds
let p = UIBezierPath()
p.moveToPoint(CGPointMake(0,0))
p.addLineToPoint(CGPointMake(screen.width,0))
p.addLineToPoint(CGPointMake(screen.width, screen.height))
p.addLineToPoint(CGPointMake(0,screen.height))
p.closePath()
let shape = SKShapeNode(path: p.CGPath)
shape.physicsBody = SKPhysicsBody(edgeLoopFromPath: p.CGPath)
shape.physicsBody?.affectedByGravity = false
shape.physicsBody?.dynamic = false
shape.strokeColor = UIColor.blackColor()
self.addChild(shape)
ball = SKShapeNode(circleOfRadius: 17)
ball.name = "player"
ball.position = CGPoint(x: 20, y: 20)
ball.fillColor = UIColor.yellowColor()
ball.physicsBody = SKPhysicsBody(circleOfRadius: 17)
ball.physicsBody?.angularDamping = 0
ball.physicsBody?.linearDamping = 0
ball.physicsBody?.restitution = 1
ball.physicsBody?.friction = 0
ball.physicsBody?.allowsRotation = false
self.addChild(ball)
self.ball.physicsBody?.applyImpulse(CGVector(dx: 2.4, dy: 9.7))
}
I just realized the mass and density of my ball sprite is nil. Why that? Even setting it keeps in nil.
Since i cannot imagine this is some unknown bug in SpriteKits physics engine,
i very hope someone can help me here as this is a full stopper for me.
It is a (known) bug in SpriteKits physic engine. But sometimes just playing with the values will make this bug disappear - for example, try changing the ball's speed (even by small value) every time it's hitting the wall.
Another solutions are listed here:
SpriteKit physics in Swift - Ball slides against wall instead of reflecting
(The same problem as yours, already from 2014, still not fixed..)
SpriteKit is working fine
I created a simple project and it is working fine.
The problem
As #Sulthan already suggested in a comment, I think the problem in your code is this line
el.physicsBody = SKPhysicsBody(rectangleOfSize: CGSize(width: Constants.Config.wallsize, height: Constants.Config.wallsize))
Here instead of creating the physical border for your scene, you are creating a rectangular physics body which overlap the physics body of the Ball producing unrealistic behaviours.
To create the physics border of my scene I simply used this
physicsBody = SKPhysicsBody(edgeLoopFromRect: frame)
Full code
By the way, this is the full code for my project
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
let ball = Ball()
ball.position = CGPoint(x:frame.midX, y:frame.midY)
addChild(ball)
physicsBody = SKPhysicsBody(edgeLoopFromRect: frame)
ball.physicsBody!.applyImpulse(CGVector(dx:50, dy:70))
physicsWorld.gravity = CGVector(dx:0, dy:0)
}
}
class Ball: SKSpriteNode {
init() {
let texture = SKTexture(imageNamed: "ball")
super.init(texture: texture, color: .clearColor(), size: texture.size())
let physicsBody = SKPhysicsBody(circleOfRadius: texture.size().width/2)
physicsBody.restitution = 1
physicsBody.friction = 0
physicsBody.linearDamping = 0
physicsBody.allowsRotation = false
self.physicsBody = physicsBody
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I also used scene.scaleMode = .ResizeFill inside GameViewController.swift.

Collisions in SpriteKit

I have an object kept between two bars. I have collisions set up but I'm not getting the print message "We have Contact" when making contact with either the top or bottom bar and I'm not sure why? I think it might have something to do with the boundary physicsBody. The image below is to give an example of the bars, where the object is kept between them.
var gameOver = false
override func didMoveToView(view: SKView) {
scene?.scaleMode = SKSceneScaleMode.AspectFill
self.physicsWorld.gravity = CGVectorMake(0.0, 2.0)
physicsWorld.contactDelegate = self
object = SKSpriteNode( imageNamed: "object")
object.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
object.physicsBody = SKPhysicsBody(circleOfRadius: object.size.width / 0.32)
object.physicsBody?.dynamic = true
object.physicsBody?.allowsRotation = true
self.addChild(object)
object.zPosition = 2
struct ColliderType {
static let object: UInt32 = 0x1 << 0
static let boundary: UInt32 = 0x1 << 1
}
let boundary = SKPhysicsBody(edgeLoopFromRect: CGRect(x: 253, y: 138, width:515, height:354))
self.physicsBody = boundary
self.physicsBody?.friction = 0
let push = CGVectorMake(10, 10)
object.physicsBody?.applyImpulse(push)
object.physicsBody?.categoryBitMask = ColliderType.object
object.physicsBody?.contactTestBitMask = ColliderType.boundary
object.physicsBody?.collisionBitMask = ColliderType.boundary
boundary.contactTestBitMask = ColliderType.object
boundary.categoryBitMask = ColliderType.boundary
boundary.collisionBitMask = ColliderType.object
func didBeginContact(contact:SKPhysicsContact) {
print("We have Contact")
gameOver = true
self.speed = 0
}
Try browsing the web and stackoverflow before asking a question because there is a lot of tutorials on the web and even stack overflow about this. This question might even be closed because collision detection is one of the basics in SpriteKit and has been asked a lot about on SO.
Anyway there is some errors/omissions in your code, so lets have a look.
1) You need to set the physics world contact delegate in your scenes "didMoveToView" method, so that it is actually listening for contacts. This is the number 1 thing people, including me, forget and than wonder for hours why nothing is working.
physicsWorld.contactDelegate = self
2) You are also not giving your sprites a physicsBody
object.physicsBody = SKPhysicsBody(...)
You have to always give them a physics body first and than set properties such as contactTestBitMasks etc.
3) Try using ? instead of ! when dealing with optionals to avoid nil crashes. Try to always do this even though you know that it is not nil. So say
...physicsBody?.contactTestBitMask...
instead of
...physicsBody!.contactTestBitMask...
You are sometimes doing it, and sometimes not.
In your current code you could get a crash because object has no physicsBody but you are force unwrapping it, essentially telling it that it does have a physics body, which is dangerous.
For example that line in your code should not work
object.physicsBody?.applyImpulse(push)
as there is no physics body on the object, but because it is not force unwrapping (?) nothing happens. Try changing that line using a ! and you should get a crash.
So always use ? when dealing with optionals and when the compiler allows you too.
Also that line should be after you have set up the object sprite not before.
4) Try to write your physics categories differently, your way will get confusing very quick when adding more categories because you cannot just increment the last number. So in your example the next collider type will have to be 4, 8, 16 etc.
Try this instead where you only need to increment the last number by 1.
struct ColliderType {
static let object: UInt32 = 0x1 << 0
static let boundary: UInt32 = 0x1 << 1
....
}
Than use it like so
...contactTestBitMask = ColliderType.object
...
You whole code should look like this
struct ColliderType {
static let object: UInt32 = 0x1 << 0
static let boundary: UInt32 = 0x1 << 1
}
class GameScene: SKScene, SKPhysicsContactDelegate {
var gameOver = false
var object: SKSpriteNode!
override func didMoveToView(view: SKView) {
scene?.scaleMode = SKSceneScaleMode.AspectFill
self.physicsWorld.gravity = CGVectorMake(0.0, 2.0)
physicsWorld.contactDelegate = self
object = SKSpriteNode( imageNamed: "Spaceship")
object.zPosition = 2
object.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
object.physicsBody = SKPhysicsBody(circleOfRadius: object.size.width / 0.32)
object.physicsBody?.dynamic = true
object.physicsBody?.allowsRotation = true
object.physicsBody?.categoryBitMask = ColliderType.object
object.physicsBody?.contactTestBitMask = ColliderType.boundary
object.physicsBody?.collisionBitMask = ColliderType.boundary
self.addChild(object)
let boundary = SKPhysicsBody(edgeLoopFromRect: CGRect(x: 253, y: 138, width:515, height:354))
self.physicsBody = boundary
self.physicsBody?.friction = 0
boundary.contactTestBitMask = ColliderType.object
boundary.categoryBitMask = ColliderType.boundary
boundary.collisionBitMask = ColliderType.object
let push = CGVectorMake(10, 10)
object.physicsBody?.applyImpulse(push)
}
func didBeginContact(contact:SKPhysicsContact) {
print("We have Contact")
gameOver = true
self.speed = 0
}
}
Hope this helps
You must be sure that the two collision shapes are in the same collision space, if they are not in the same space, then collision detection algorithm will be never invoked. About the comment of Seed12, the two objects do not need to be a rigid body (physicsBody) to be checked for the presence of collisions between them.

CoreDeviceMotion does not work to update physics world gravity

I am trying to get the device motion data and change the gravity direction, so that when I tilt my iPhone, the ball will move in the tilt direction. In this tutorial the method is getting acceleration data and applyForce on the node. However, I would like to achieve this goal by changing the gravity direction (no matter how I tilt the phone, the gravity is always along the real physical gravity direction).
Here is my code:
import SpriteKit
import CoreMotion
class GameScene: SKScene {
let ballName = "redBall"
var gravityDirection = CGVectorMake(0,-9.8)
let motionManager = CMMotionManager()
let motion = CMDeviceMotion()
func addBall(){
//Create the ball
var ball = SKShapeNode()
var path = CGPathCreateMutable()
CGPathAddArc(path, nil, 0, 0, 45, 0, CGFloat(M_PI * 2), true)
CGPathCloseSubpath(path)
ball.name = ballName
ball.path = path
ball.lineWidth = 2.0
ball.fillColor = SKColor(red: 0.95, green: 0.2, blue: 0.2, alpha: 0.9)
ball.position = CGPoint(x:size.width/2, y: size.height)
//Set the ball's physcial properties
ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.frame.width/2)
ball.physicsBody!.dynamic = true
ball.physicsBody!.mass = 0.1
ball.physicsBody!.friction = 0.2
ball.physicsBody!.restitution = 0.9
ball.physicsBody!.affectedByGravity = true
self.addChild(ball)
}
func gravityUpdated(){
let vector = CGVectorMake(CGFloat(motion.gravity.x), CGFloat(motion.gravity.y))
self.physicsWorld.gravity = vector
}
override func didMoveToView(view: SKView) {
physicsBody = SKPhysicsBody(edgeLoopFromRect: view.frame)
self.physicsWorld.gravity = gravityDirection
motionManager.startDeviceMotionUpdates()
addBall()
}
override func update(currentTime: CFTimeInterval) {
gravityUpdated()
}
}
In the above code I use CMDeviceMotion to get the gravity data, and give the gravity value to the physicsWorld.gravity. The app always crashes when I run it.
Xcode indicates there is a bug in gravityUpdate() function, but I can't find it. Hope someone can help me solve this problem. Or provide me a better way to simulate the gravity. Thanks!
I have figured it out myself. It's necessary to check if the motion data is available before give it value to physicsworld.gravity.
Here is the new code:
func gravityUpdated(){
if let data = motionManager.deviceMotion {
let gravity = data.gravity
self.physicsWorld.gravity = CGVectorMake(CGFloat(gravity.x), CGFloat(gravity.y))
}
}
Note that if no device-motion data is available, the value of the property is nil.
CMMotionManager Class Reference

Resources