Sprite Kit never detect my collision - swift

When player body meet a monster body the function func didBeginContact(contact: SKPhysicsContact) is never called.
My class implement SKPhysicsContactDelegate
class GameScene: SKScene, SKPhysicsContactDelegate
the property collisionBitMask i commented it because i read that if we don't put it, node can collise with all node
I add my player like this :
bear.position = CGPoint(x:self.frame.width/20, y:self.frame.height/5)
bear.size = CGSize(width: self.frame.width/7, height: self.frame.height/5)
bear.physicsBody = SKPhysicsBody(rectangleOfSize: bear.frame.size)
bear.physicsBody?.dynamic = false;
bear.physicsBody?.categoryBitMask = PhysicsCategory.Player
bear.physicsBody?.contactTestBitMask = PhysicsCategory.Monster
//bear.physicsBody?.collisionBitMask = PhysicsCategory.Monster
I add monster like this:
let thePosition = CGPoint(x: positionX, y: (self.frame.height/7.4)*positionY)
let monster = SKSpriteNode(imageNamed: pattern as String)
monster.size = CGSize(width: self.frame.width/16, height: self.frame.height/11)
monster.name = name
monster.position = thePosition
monster.physicsBody = SKPhysicsBody(rectangleOfSize: monster.frame.size)
monster.physicsBody?.dynamic = false
monster.physicsBody?.categoryBitMask = PhysicsCategory.Monster // 3
monster.physicsBody?.affectedByGravity = false
monster.physicsBody?.contactTestBitMask = PhysicsCategory.Player // 4
//platformNode.physicsBody?.collisionBitMask = PhysicsCategory.Player // 5
addChild(monster)
my PhysicalCategory
struct PhysicsCategory {
static let None : UInt32 = 0
static let All : UInt32 = UInt32.max
static let Player: UInt32 = 0b1 // 1
static let Monster : UInt32 = 0b10 // 2
static let Projectile: UInt32 = 0b100 // 3
static let Plateforme: UInt32 = 0b1000 // 4
}
I have multiple Textures, for example when player walk i call this :
func walkingBear() {
bear.runAction( SKAction.repeatActionForever(SKAction.animateWithTextures(bearCoursFrames, timePerFrame: 0.1, resize: false, restore: true)), withKey:"cours")
}
I initlaise bearCoursFrames like this
var walkFrames = [SKTexture]()
var numImages : Int = bearAnimatedAtlas.textureNames.count
for var i=1; i<=numImages/2; i++ {
let bearTextureName = "cours\(i)"
walkFrames.append(bearAnimatedAtlas.textureNamed(bearTextureName))
}
bearCoursFrames = walkFrames
Where is my error please, i can't find a solution in other post
any help would be appreciated
---------------------------------EDIT------------------------------
Solution: I just add self.physicsWorld.contactDelegate = self and it works now.

The answer is simple. There is a rule which says that at least one body has to be defined as dynamic in order to register the contact.
Change either player or other body to be dynamic and contact will be detected.
As an addition, if you already haven't, enable physics visual representation ... It can be helpful because sometimes contact is not detected because the actual bodies are not intersecting each other eg. when node's anchor point is changed, so the texture and a body are misplaced (which is fixable, using appropriate initializers of SKPhysicsBody class, but this is a very common error).

I just add
self.physicsWorld.contactDelegate = self
and it works now.

Related

How do I create a collision with SpriteKit with an already created SKSpriteNode?

I want the pacman to restart from its original position when it collides with blinky, that is moving.
How can I make them collide considering I have already declared them?
You move the pacman, but blinky moves alone. I want it to work like the pacman game.
public class PacmanScene: SKScene {
let playerSpeed: CGFloat = 40.0
var pacman: SKSpriteNode?
var playerTextures: [SKTexture] = []
var lastTouch: CGPoint? = nil
var blinky: SKSpriteNode?
var clyde: SKSpriteNode?
var inky: SKSpriteNode?
var pinky: SKSpriteNode?
override public init(size: CGSize) {
let pacmanTexture = SKTexture(imageNamed: "pacman01.png")
pacman = SKSpriteNode(texture: pacmanTexture)
pacman?.name = "pacman"
pacman?.position = CGPoint(x:30, y:30)
pacman?.zPosition = 1.0
pacman?.physicsBody = SKPhysicsBody(texture: pacmanTexture, size: CGSize(width: (pacman?.size.width)!, height: (pacman?.size.height)!))
pacman?.physicsBody?.allowsRotation = true
pacman?.physicsBody?.affectedByGravity = false
pacman?.physicsBody?.mass = 2
let blinkyTexture = SKTexture(imageNamed: "blinky.png")
blinky = SKSpriteNode(texture: blinkyTexture)
blinky?.name = "blinky"
blinky?.position = CGPoint(x: 15, y: 60)
blinky?.zPosition = 1.0
blinky?.physicsBody = SKPhysicsBody(texture: pacmanTexture, size: CGSize(width: (blinky?.size.width)!, height: (blinky?.size.height)!))
blinky?.physicsBody?.allowsRotation = false
blinky?.physicsBody?.affectedByGravity = false
blinky?.physicsBody?.mass = 1000
super.init(size: size)
addChild(pacman!)
addChild(blinky!)
override public func didMove(to view: SKView) {
let bmoveUp = SKAction.moveBy(x: 0, y: 450, duration: 4.0)
let bmoveRight = SKAction.moveBy(x:20, y:0, duration: 1.0)
let bmoveDown = SKAction.moveBy(x:0, y: -450, duration: 4.0)
let bmoveLeft = SKAction.moveBy(x:-20, y:0, duration: 1.0)
let bsequence = SKAction.sequence([bmoveUp, bmoveRight, bmoveDown, bmoveLeft])
let bendlessAction = SKAction.repeatForever(bsequence)
blinky?.run(bendlessAction)
}
If iv got this right you want your "blinky" to follow your "pacman" to do this you would have to work out the position of the pacman then add an SKAction to your blinky to move to that position.
Try something like this
//Speed blinky moves
let blinkySpeed = 100
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
updateBlinky()
}
func updateBlinky() {
//Set the point that blinky moves to
let point = CGPoint(x: pacman.position.x, y: pacman.position.y)
//Get the distance its got to travel
let distance = distanceBetweenPoints(first: pacman.position, second: blinky.position)
//Get the time is got to take from the speed and distance
let time = distance / blinkySpeed
//Create and run the action
let action = SKAction.move(to: point, duration: TimeInterval(time))
blinky.run(action)
}
//work out the distance between the sprites
func distanceBetweenPoints(first: CGPoint, second: CGPoint) -> Int {
return Int(hypot(second.x - first.x, second.y - first.y))
}
The end result would be something like this
Edit:
Okay I think from your question you already have "blinky" moving you just want to detect collisions.
First you need to add the SKPhysicsContactDelegate to your class
public class PacmanScene: SKScene, SKPhysicsContactDelegate {
Then you need to add a category bit mask to both the sprites then handle the collisions in didBegin(_ contact: SKPhysicsContact) method.
What I would do
//Create Physics category struct
struct PhysicsCategory {
static let pacman : UInt32 = 0x1 << 1
static var blinky : UInt32 = 0x1 << 2
}
Then where you are setting up pacman and blinky set the category bitmask
//Set the category bit mask for pacman
pacman?.physicsBody?.categoryBitMask = PhysicsCategory.pacman
//Set what categories you want to test contact
pacman?.physicsBody?.contactTestBitMask = PhysicsCategory.blinky
//Set what categories you want to collide with each other
pacman?.physicsBody?.collisionBitMask = PhysicsCategory.blinky
//Set the category bit mask for blinky
blinky?.physicsBody?.categoryBitMask = PhysicsCategory.blinky
//Set what categories you want to test contact
blinky?.physicsBody?.contactTestBitMask = PhysicsCategory.pacman
//Set what categories you want to collide with each other
blinky?.physicsBody?.collisionBitMask = PhysicsCategory.pacman
Then you would need to implement the didBegin(_ contact: SKPhysicsContact) method to handle the collisions
func didBegin(_ contact: SKPhysicsContact) {
//Work out which contact was pacman
let other = contact.bodyA.categoryBitMask == PhysicsCategory.pacman ? contact.bodyB : contact.bodyA
//Test what it hit
switch other.categoryBitMask {
case PhysicsCategory.blinky:
print("pacman hit blinky")
//Move pacman
pacman?.position = CGPoint(x:30, y:30)
default:
break
}
}
Hope this helps

How can I add a physics body to an SKAtlasTexture or create an animation through Images.xcassets?

I wanted to create a small animation of a car driving down the road, so I made an atlas of 9 different pictures. The car simply looks like its wheels are rotating and the car is bouncing a bit as it drives along. I already made an SKSpriteNode with an image and added a physics body on it so that it can jump and be affected by gravity.
So I was wondering how to add either a physics body to an SKAtlasTexture or create an animation through my image.xcassets folder. I tried to just change the SKSpriteNode to SKAtlasTexture, but that obviously didn't work as there are no physics bodies in SKAtlasTexture. So that's where I'm at. Any suggestions or solutions would be greatly appreciated.
Here some parts of my code:
class PlayScene: SKScene, SKPhysicsContactDelegate {
let road = SKSpriteNode(imageNamed: "road")
var origRoadPositionX = CGFloat(0)
var maxRoad = CGFloat(0)
var groundSpeed = 3
var carBaseLine = CGFloat(0)
let car = SKSpriteNode(imageNamed: "car")
enum ColliderType:UInt32{
case car = 1
case tower = 2
}
override func didMoveToView(view: SKView) {
self.backgroundColor = UIColor(hex: 0x80E8FF)
self.physicsWorld.contactDelegate = self
//Car
self.car.position = CGPointMake(CGRectGetMinX(self.frame)-20 + self.car.size.width, self.carBaseLine)
self.car.physicsBody = SKPhysicsBody (rectangleOfSize: self.car.size)
self.car.physicsBody?.allowsRotation = false
self.car.physicsBody?.affectedByGravity = false
self.car.physicsBody?.categoryBitMask = ColliderType.car.rawValue
self.car.physicsBody?.contactTestBitMask = ColliderType.tower.rawValue
self.car.physicsBody?.collisionBitMask = ColliderType.tower.rawValue
self.addChild(car)
If more code is needed in order to find a solution, let me know and i can supply more of it.
You can use atlas folder for performing animation with images.
Consider below example:
import SpriteKit
class GameScene: SKScene {
var bombFrames : [SKTexture]!
var bomb : SKSpriteNode!
let NoneCategory : UInt32 = 0x1 << 0
let ProjectileCategory : UInt32 = 0x1 << 2
let bombCategory : UInt32 = 0x1 << 7
override func didMoveToView(view: SKView) {
/* Setup your scene here */
runAction(SKAction.repeatActionForever(SKAction.sequence([SKAction.runBlock(addBomb), SKAction.waitForDuration(5.0)])))
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
func addBomb() {
let Name = "Bomb"
let AnimatedAtlas = SKTextureAtlas(named: Name)
var Framese = [SKTexture]()
let numImages = AnimatedAtlas.textureNames.count
for var i=1; i<=numImages; i++ {
let TextureName = "\(i)"
Framese.append(AnimatedAtlas.textureNamed(TextureName))
}
bombFrames = Framese
let firstFrame = bombFrames[0]
bomb = SKSpriteNode(texture: firstFrame)
let actualY = random(min: bomb.size.height/2, max: size.height - bomb.size.height/2)
bomb.position = CGPoint(x: size.width + bomb.size.width/2, y: actualY)
bomb.physicsBody = SKPhysicsBody(texture: bomb.texture, size: bomb.texture!.size())
bomb.physicsBody?.dynamic = true
bomb.physicsBody?.categoryBitMask = bombCategory
bomb.physicsBody?.contactTestBitMask = ProjectileCategory
bomb.physicsBody?.collisionBitMask = NoneCategory
bomb.physicsBody?.usesPreciseCollisionDetection = true
addChild(bomb)
let actualDuration = random(min: CGFloat(2.0), max: CGFloat(4.0))
let actionMove = SKAction.moveTo(CGPoint(x: -bomb.size.width/2, y: actualY), duration: NSTimeInterval(actualDuration))
let actionMoveDone = SKAction.removeFromParent()
bomb.runAction(SKAction.sequence([actionMove, actionMoveDone]))
playBombAnimation()
}
func random() -> CGFloat {
return CGFloat(Float(arc4random()) / 0xFFFFFFFF)
}
func random(#min: CGFloat, max: CGFloat) -> CGFloat {
return random() * (max - min) + min
}
func playBombAnimation() {
bomb.runAction(SKAction.repeatActionForever(SKAction.animateWithTextures(bombFrames, timePerFrame: 0.1, resize: false, restore: true)), withKey:"bombAnimation")
}
}
And don't forget to add atlas folder into your project navigator like this:
As you can see in code you can add physics body to your sprite. and if you want you can try this way.
Hope this will help.

didBeginContact not working properly

I created a struct PhysicsCatagory for each of the different objects I want interacting with each other
struct PhysicsCatagory {
static let Blade : UInt32 = 1
static let Laser : UInt32 = 2
}
above my class GameScene
class GameScene: SKScene, SKPhysicsContactDelegate {
and I initialized an SKSpriteNode blade with its physicsBody in the didMoveToView method
override func didMoveToView(view: SKView) {
physicsWorld.contactDelegate = self
Blade.position = CGPointMake(self.size.width / 2, (self.size.height / 14))
Blade.anchorPoint = CGPointMake(0.5, -0.13)
Blade.physicsBody = SKPhysicsBody(rectangleOfSize: Blade.size)
Blade.physicsBody?.categoryBitMask = PhysicsCatagory.Blade
Blade.physicsBody?.contactTestBitMask = PhysicsCatagory.Laser
Blade.physicsBody?.dynamic = false
self.addChild(Blade)
}
As well as a SKSpriteNode laser and its physicsbody in the method shootLaser
func shootLaser(){
var Laser = SKSpriteNode(imageNamed: "Laser.png")
Laser.position = Enemy.position
Laser.zPosition = -5
Laser.physicsBody = SKPhysicsBody(rectangleOfSize: Laser.size)
Laser.physicsBody?.categoryBitMask = PhysicsCatagory.Laser
Laser.physicsBody?.contactTestBitMask = PhysicsCatagory.Blade
Laser.physicsBody?.dynamic = false
let action = SKAction.moveBy(laserVector, duration: 0.7)
let actionDone = SKAction.removeFromParent()
Laser.runAction(SKAction.sequence([action,actionDone]))
self.addChild(Laser)
}
But when they collide in the simulation, the didBeginContact method is not called and "Hello" is not printed
func didBeginContact(contact: SKPhysicsContact) {
NSLog("Hello")
}
Why isn't the didBeginContact method being called when they collide? Thanks in advance (:
Sprite Kit does not check for contacts between non-dynamic physics bodies because they aren't expected to move. If you don't want your sprites to fall off the screen due to gravity, set the physics body's affectedByGravity property to false and set dynamic = true.

How to detect collisions between two fast moving objects?

Hey so I have a problem detecting collisions. I have this project I am making based on Ray Wenderlich's tutorial on How to Make a game like Mega Jump. So everything is works great at the beginning, where platforms and stars are static in one place. But as you go further in the game, stars and platforms start moving (I am using SKActions for this), tried to do this to make it a little harder. But when I get there, collision is NOT being detected at all, player just passes by through the objects like they weren't there. I've been reading everywhere online, I am using precise collision detection but still I see no difference. Any ideas on what can be wrong or what else can I do? Here is a bit of code on how I am doing this:
player = SKSpriteNode(texture: firstFrame)
player.position = (CGPoint(x: self.size.width / 2, y: 50.0))
player.physicsBody = SKPhysicsBody(circleOfRadius: player.size.width / 2)
player.physicsBody?.dynamic = true
player.physicsBody?.allowsRotation = false
player.physicsBody?.restitution = 1.0
player.physicsBody?.friction = 0.0
player.physicsBody?.angularDamping = 0.0
player.physicsBody?.linearDamping = 0.0
player.physicsBody?.usesPreciseCollisionDetection = true
player.physicsBody?.categoryBitMask = CollisionCategoryBitmask.Player
player.physicsBody?.collisionBitMask = 0
player.physicsBody?.contactTestBitMask = CollisionCategoryBitmask.Star | CollisionCategoryBitmask.Platform | CollisionCategoryBitmask.Monster
foregroundNode.addChild(player)
func createPlatformAtPosition(position: CGPoint, ofType type: PlatformType) -> PlatformNode {
let node = PlatformNode()
let thePosition = CGPoint(x: position.x * scaleFactor, y: position.y)
node.position = thePosition
node.name = "NODE_PLATFORM"
node.platformType = type
var sprite: SKSpriteNode
var spriteFrames : [SKTexture]!
if type == .Break {
let spriteAnimatedAtlas = SKTextureAtlas(named: "CloudBreak")
var cloudFrames = [SKTexture]()
spriteFrames = cloudFrames
let firstFrame = SKTexture(imageNamed: "Cloud02")
sprite = SKSpriteNode(texture: firstFrame)
let move = SKAction.moveToX(self.position.x - 160.0, duration:2.0)
let back = SKAction.moveToX(self.position.x, duration:2.0)
let sequence = SKAction.sequence([move, back, move, back])
sprite.runAction(SKAction.repeatActionForever(sequence))
} else {
let spriteAnimatedAtlas = SKTextureAtlas(named: "Cloud")
var cloudFrames = [SKTexture]()
spriteFrames = cloudFrames
let firstFrame = SKTexture(imageNamed: "Cloud")
sprite = SKSpriteNode(texture: firstFrame)
}
node.addChild(sprite)
node.physicsBody = SKPhysicsBody(rectangleOfSize: sprite.size)
node.physicsBody?.dynamic = false
node.physicsBody?.categoryBitMask = CollisionCategoryBitmask.Platform
node.physicsBody?.contactTestBitMask = CollisionCategoryBitmask.Player
node.physicsBody?.collisionBitMask = 0
return node
}
func didBeginContact(contact: SKPhysicsContact) {
var updateHUD = false
let whichNode = (contact.bodyA.node != player) ? contact.bodyA.node : contact.bodyB.node
let other = whichNode as GameObjectNode
updateHUD = other.collisionWithPlayer(player)
}
struct CollisionCategoryBitmask {
static let Player: UInt32 = 0x00
static let Star: UInt32 = 0x01
static let Platform: UInt32 = 0x02
static let Monster: UInt32 = 0x03
}
Slow the objects down and see if they collide. I had an issue where I was moving my nodes too fast and they were past the collision points when the new frame was rendered. If they move so fast they pass each other's physics body in the frame cycle they will not register a hit.
To detect a collision in that case, you can compare where they are in each frame, and if they have passed each other or their x/y plane values overlap, you can execute your collision code.
The collision is not detected because you didn't set the collisionBitMask.
player.physicsBody?.collisionBitMask = CollisionCategoryBitmask.Star | CollisionCategoryBitmask.Platform | CollisionCategoryBitmask.Monster
node.physicsBody?.collisionBitMask = CollisionCategoryBitmask.Player
Contact and Collision aren't the same thing. You can find more information online.
If it still doesn't work, please show us your CollisionCategoryBitmask to make sure you created it properly.
Edit :
struct CollisionCategoryBitmask {
static let Player: UInt32 = 0
static let Star: UInt32 = 0b1
static let Platform: UInt32 = 0b10
static let Monster: UInt32 = 0b100
}
Here is a table I've made for another question, that might help you too :
http://goo.gl/7D8EGY
Check out my collision physics engine, it's very simple but has support for continuous/predicting/bullet collisions. You might learn something from the code, and it's written in javascript so it should be easy to read.https://github.com/Murplyx/AAE---Axis-Aligned-Engine

iOS Swift didBeginContact not being called

I have been struggling for the past two days to get two SKSpriteNodes to register a collision and evoke didBegin#contact.
I've set their bit masks 'categoryBitMask', 'contactTestBitMask' and 'collisionTestBitMask' for both objects.
I've also set the 'dynamic' property for both to 'true'
initPhysics() seems to set up the physicsWorld okay.
All I'm expecting is that didBegin#Contact is called, but it is not
//Set up Physicsbody bit masks
let playerCarBitMask: UInt32 = 0x1 << 1
let slowCarBitMask: UInt32 = 0x1 << 2
//initPhysics
func initPhysics() {
println("(((((((((((((( Initiating Physicsbody ))))))))))))))")
self.physicsWorld.contactDelegate = self
self.physicsWorld.gravity = CGVector.zeroVector
println("self.physicsWorld.contactDelegate = \(self.physicsWorld.contactDelegate)")
}
//setupPlayer
func setupPlayer() {
car = SKSpriteNode(imageNamed: "redCarUp")
car.setScale(2.0)
car.position = CGPoint(x: 800, y: 400)
car.zPosition = 100
car.name = "car"
gameNode.addChild(car)
let carBody = SKPhysicsBody(
rectangleOfSize: car.frame.size, center: car.position)
carBody.dynamic = true
carBody.categoryBitMask = playerCarBitMask
carBody.contactTestBitMask = slowCarBitMask
carBody.mass = 5
carBody.collisionBitMask = slowCarBitMask
car.physicsBody = carBody
println("carBody = \(carBody)")
println("carBody.dynamic = \(carBody.dynamic)")
println("carBody.mass = \(carBody.mass)")
println("carBody.categoryBitMask = \(carBody.categoryBitMask)")
println("carBody.contactTestBitMask = \(carBody.contactTestBitMask)")
println("carBody.collisionBitMask = \(carBody.contactTestBitMask)")
slowCar = SKSpriteNode(imageNamed: "blueCarUp")
slowCar.setScale(2.0)
let slowCarScenePos = CGPoint(
x: 680,
y: 2048)
slowCar.position = gameNode.convertPoint(slowCarScenePos, fromNode: self)
println("slowCar.position = \(slowCar.position) ****")
slowCar.zPosition = 80
slowCar.name = "slowCar"
let slowCarBody = SKPhysicsBody(
rectangleOfSize: slowCar.frame.size, center: slowCar.position)
println("slowCar = \(slowCar) ****")
slowCarBody.dynamic = true
slowCarBody.categoryBitMask = slowCarBitMask
slowCarBody.contactTestBitMask = playerCarBitMask
slowCarBody.mass = 5
slowCarBody.collisionBitMask = playerCarBitMask
slowCar.physicsBody = slowCarBody
gameNode.addChild(slowCar)
}
func didBeginContact(contact: SKPhysicsContact!) {
println("*******************PhysicsContact********************")
}
'didBeginContact' has been changed to 'didBegin' in swift 3
func didBegin(_ contact: SKPhysicsContact) {
//stuff
}
I had a code from swift 2 and 'didBeginContact' was sitting there but wasn't being called. After quite a white I figured out that the function was changed. So, I thought my answer could help someone.
If you want make a contact between car and slowCar you have to init the categoryBitMask of both physicsBodies (I think you did). See the code below to get contact between two physicsBodies. When there is a contact it returns your display function :
//init your categoryBitMask :
let carCategory:UInt32 = 0x1 << 0
let SlowCarCategory:UInt32 = 0x1 << 1
//init car
car.physicsBody?.categoryBitMask = carCategory
car.physicsBody?.contactTestBitMask = slowCarCategory
//init slowCar
slowCar.physicsBody?.categoryBitMask = slowCarCategory
slowCar.physicsBody?.contactTestBitMask = CarCategory
// set your contact function
func didBeginContact(contact: SKPhysicsContact!)
{
var firstBody: SKPhysicsBody
var secondBody: SKPhysicsBody
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
{
firstBody = contact.bodyA
secondBody = contact.bodyB
}
else
{
firstBody = contact.bodyB
secondBody = contact.bodyA
}
if ((firstBody.categoryBitMask & carCategory) != 0 && (secondBody.categoryBitMask & slowCarCategory) != 0)
{
displayfunction(firstBody.node as SKSpriteNode, car: secondBody.node as SKSpriteNode)
}
}
func displayFunction (slowCar : SKSpriteNode, car : SKSpriteNode)
It turned out to be a simple problem. In my original code I was setting parameters for the SKPhysicsBody detection frame like so:
let carBody = SKPhysicsBody(
rectangleOfSize: car.frame.size, center: car.position)
Similarly I was doing the same for the second node that I was testing physics collisions for.
Simply removing the 'centre:' parameters like so:
let carBody = SKPhysicsBody(rectangleOfSize: car.frame.size)
for the two sprite nodes solved the problem and the nodes now crash into each other and push themselves aside as expected.
Please Note that contact will not be detected between two static bodies
(node.physicsBody?.isDynamic = false)