Reputation: 316
I am working on a "Space Invaders" game, I have implemented CMMotionManager to move my heroShip on tilt(the game only runs in landscape), but for some reason my ship will move out of the view even though
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
should have prevented this by making a physics edge. Any ideas why?
import SpriteKit
import CoreMotion
class GameScene: SKScene,SKPhysicsContactDelegate{
let collisionBulletCategory: UInt32 = 0x1 << 0
let collisionHeroCategory: UInt32 = 0x1 << 1
let background = SKSpriteNode(imageNamed: "background")
let heroShip = SKSpriteNode(imageNamed: "heroShip")
let enemyShip = SKSpriteNode(imageNamed: "enemyShip")
let MotionManager = CMMotionManager()
override func didMoveToView(view: SKView) {
/* Setup your scene here */
self.physicsWorld.contactDelegate = self
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
background.position = CGPointMake(CGRectGetMidX(self.frame),CGRectGetMidY(self.frame))
heroShip.anchorPoint = CGPointMake(1.0, 0.5)
heroShip.physicsBody?.mass = 0.02
heroShip.physicsBody?.dynamic = true
heroShip.physicsBody = SKPhysicsBody(rectangleOfSize: heroShip.size)
heroShip.physicsBody?.affectedByGravity = false
heroShip.physicsBody?.categoryBitMask = collisionHeroCategory
heroShip.physicsBody?.contactTestBitMask = collisionBulletCategory
heroShip.physicsBody?.collisionBitMask = 0x0
heroShip.position = CGPointMake(self.size.width/6.0, self.size.height/2.0)
enemyShip.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))
enemyShip.zPosition = 1.0
self.heroShip.zPosition = 1.0
self.addChild(enemyShip)
self.addChild(background)
self.addChild(heroShip)
if MotionManager.accelerometerAvailable{
MotionManager.startAccelerometerUpdates()
}
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let bullet = SKSpriteNode(imageNamed: "bullet")
bullet.position = CGPointMake(heroShip.position.x, heroShip.position.y)
bullet.zPosition = 1.0
// Add physics body for collision detection
bullet.physicsBody = SKPhysicsBody(rectangleOfSize: bullet.frame.size)
bullet.physicsBody?.dynamic = true
bullet.physicsBody?.affectedByGravity = false
bullet.physicsBody?.categoryBitMask = collisionBulletCategory
bullet.physicsBody?.contactTestBitMask = collisionHeroCategory
bullet.physicsBody?.collisionBitMask = 0x0;
let action = SKAction.moveToX(CGRectGetMaxX(self.frame) + bullet.size.width, duration: 0.75)
self.addChild(bullet)
bullet.runAction(action, completion: {
bullet.removeAllActions()
bullet.removeFromParent()
})
}
func didBeginContact(contact: SKPhysicsContact) {
}
override func update(currentTime: CFTimeInterval) {
let data = MotionManager.accelerometerData
if data?.acceleration.x == nil{
print("nil")
}
else if fabs((data?.acceleration.x)!) > 0.2 {
self.heroShip.physicsBody?.applyForce(CGVectorMake(0.0, CGFloat(50 * (data?.acceleration.x)!)))
}
}
}
Upvotes: 2
Views: 122
Reputation: 1205
Adding to Whirlwind's answer, you probably also want to enable usesPreciseCollisionDetection
on your physics body. By setting this to true
, this will prevent your heroShip
from popping through the wall if it encounters a massive force from the accelerometer data.
Basically, the physics is calculated as where it is going to be. So if there is a massive force, it could say, "Oh, this massive force means I should be way over here outside the bounding box." If you have precise collision detection enabled, this will force it to calculate physics for multiple positions from the start position of this frame, to the calculated position. If it hits something before getting to the calculated position, then it adjusts so that it doesn't go through whatever it hit. For the most part, you don't have to worry about this, but it is better to be safe than sorry.
So put this line somewhere in your physics body configuration code after initialization of it:
heroShip.physicsBody?.usesPreciseCollisionDetection = true
Upvotes: 0
Reputation: 13665
According to the docs, this is how collision happen:
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 nonzero value, 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.
You have set player's collisionBitMask to something like 0x00000000
(all bits cleared). By default a categoryBitMask is set to 0xFFFFFFFF
(all bits set). Because you haven't stated otherwise, scene's physics body categoryBitMask is set to 0xFFFFFFFF
. When logical AND (&) operator is performed between those two, a result will be zero, means no collision.
To fix this, you can just remove this line:
heroShip.physicsBody?.collisionBitMask = 0x0
Or set it properly by defining a Wall category...
HINTS:
Anchor point defines how texture is drawn relative to the node's position. It has nothing with physics body. Physics body, by default is centered to the node's position. If you turn-on physics visual representation (skView.showsPhysics = true
) you will see, in your example, that physics body is not positioned as it should.
Setting up physics body before you have actually initialized it make no sense. You have to initialize physics body first.
Upvotes: 4