I have 3 SKSpriteNodes in my Scene. One bird, one coin and a border around the scene. I don't want the coin and the bird to collide with each other but withe the border.
I assign a different collisionBitMask and categoryBitMask to every node:
enum CollisionType:UInt32{
case Bird = 1
case Coin = 2
case Border = 3
}
Like so:
bird.physicsBody!.categoryBitMask = CollisionType.Bird.rawValue
bird.physicsBody!.collisionBitMask = CollisionType.Border.rawValue
coin.physicsBody!.categoryBitMask = CollisionType.Coin.rawValue
coin.physicsBody!.collisionBitMask = CollisionType.Border.rawValue
But the coin and the bird still collide with each other.
What am I doing wrong?
The bitmask is on 32 bits. Declaring them like you did corresponds to :
enum CollisionType:UInt32{
case Bird = 1 // 00000000000000000000000000000001
case Coin = 2 // 00000000000000000000000000000010
case Border = 3 // 00000000000000000000000000000011
}
What you want to do is to set your border value to 4. In order to have the following bitmask instead :
enum CollisionType:UInt32{
case Bird = 1 // 00000000000000000000000000000001
case Coin = 2 // 00000000000000000000000000000010
case Border = 4 // 00000000000000000000000000000100
}
Keep in mind that you'll have to follow the same for next bitmask : 8, 16, ... an so on.
Edit :
Also, you might want to use a struct instead of an enum and use another syntax to get it easier (it's not mandatory, just a matter of preference) :
struct PhysicsCategory {
static let None : UInt32 = 0
static let All : UInt32 = UInt32.max
static let Bird : UInt32 = 0b1 // 1
static let Coin : UInt32 = 0b10 // 2
static let Border : UInt32 = 0b100 // 4
}
That you could use like this :
bird.physicsBody!.categoryBitMask = PhysicsCategory.Bird
bird.physicsBody!.collisionBitMask = PhysicsCategory.Border
coin.physicsBody!.categoryBitMask = PhysicsCategory.Coin
coin.physicsBody!.collisionBitMask = PhysicsCategory.Border
Related
I have two nodes, one "cat" and one "rat", but for some reason I can't get their collision to be detected. I'm using this method for masks:
enum CollisionTypes: UInt32 {
case holder = 1
case chef = 2
case powerups = 4
case ingredients = 8
case utensils = 16
case floor = 32
case bag = 64
case table = 128
case tip = 256
case rat = 512
case cat = 1024
}
Here is where I initialize their physics bodies:
// Cat physics body, the node's name is "cat"
public func initializeAt(position: CGPoint) {
sprite.position = position
sprite.zPosition = 5
sprite.name = "cat"
sprite.alpha = 0.7
scene.sceneContent.addChild(sprite)
sprite.physicsBody = SKPhysicsBody(rectangleOf: sprite.size)
sprite.physicsBody!.isDynamic = false
sprite.physicsBody!.categoryBitMask = CollisionTypes.cat.rawValue
sprite.physicsBody!.contactTestBitMask = CollisionTypes.rat.rawValue
sprite.physicsBody!.collisionBitMask = CollisionTypes.rat.rawValue
// Rat physics body, the nodes name is "rat"
init() {
node.name = "rat"
node.zPosition = 5
node.physicsBody = SKPhysicsBody(rectangleOf: node.size)
node.physicsBody!.isDynamic = false
node.physicsBody!.categoryBitMask = CollisionTypes.rat.rawValue
node.physicsBody!.contactTestBitMask = CollisionTypes.cat.rawValue
node.physicsBody!.collisionBitMask = CollisionTypes.cat.rawValue
setupFrames()
}
Here is my didBegin() method. However, neither of the if statements get executed and I don't know why because I am using this method for a number of other things in my project.
func didBegin(_ contact: SKPhysicsContact) {
if let node1 = contact.bodyA.node as? SKSpriteNode,
let node2 = contact.bodyB.node as? SKSpriteNode {
if node1.name == "rat" && node2.name == "cat" {
for rat in rats {
if node1 == rat.node {
rat.die()
}
}
Cat.shared.resetPosition()
return
}
else if node1.name == "cat" && node2.name == "rat" {
for rat in rats {
if node2 == rat.node {
rat.die()
}
}
Cat.shared.resetPosition()
return
}
If I try playing around with the contactTestBitMasks and making them something different like "ingredients", then I can see that the cat and rat are interacting with ingredients but it seems like they just wont interact with eachother.
rats and cats won't trigger contacts with each other because both have isDynamic set to false. At least one of them needs to be dynamic before a contact is triggered.
From https://developer.apple.com/documentation/spritekit/skphysicsbody
The isDynamic property controls whether a volume-based body is
affected by gravity, friction, collisions with other objects, and forces or impulses you directly apply to the object.
i want to make a struct where i declare different positions. I have many nodes which always use the same positions so instead of typing every time the same x-values and y-values i would like to just type positions.p1.
private struct Positions {
let P1 = myGameScene.frame.size.width * 0.05
let P2 = myGameScene.frame.size.width * 0.15
}
Now i get the error: Instance member frame cannot be used on type myGameScene. myGameScene is a SKScene.
If i try self. instead of myGameScene i get the error: Value of type NSObject -> () -> myGameScene has no member frame.
Need help :-(
It does work
class MyGameScene: SKScene { }
let myGameScene = MyGameScene()
struct XPositions {
let x0 = myGameScene.frame.size.width * 0.05
let x1 = myGameScene.frame.size.width * 0.15
}
print(XPositions().x0)
However let me say it's a weird way to solve the problem infact:
You are using a struct instead of an array
You need to create a value of the struct just to access the properties that are the same of every value of Positions
The properties (P1, P2) should be lowercase...
Suggestion
Why don't you just add a computed property to your scene?
class MyGameScene: SKScene {
lazy var positions: [CGFloat] = {
let factors: [CGFloat] = [0.15, 0.05]
return factors.map { $0 * self.frame.size.width }
}()
}
Use bounds instead of frame.
private struct Positions {
let P1 = myGameScene.bounds.size.width * 0.05
let P2 = myGameScene.bounds.size.width * 0.15
}
I am trying to make a platorm game. I have made a level in GameScene.sks but I couldn't figure out how to detect collisions in between the sis file and the player, so I decided to use a level node instead. I have the code for turning the nodes into 32 bit numbers, and I have it put into eh code before the self.addchild, but it does not seem to work. Any help?
struct collisionPhysics {
static let alien: UInt32 = 1 << 0
static let level: UInt32 = 1 << 1
}
alien.physicsBody?.categoryBitMask = collisionPhysics.alien
alien.physicsBody?.contactTestBitMask = collisionPhysics.level
alien.physicsBody?.collisionBitMask = collisionPhysics.level
level.physicsBody?.categoryBitMask = collisionPhysics.level
level.physicsBody?.contactTestBitMask = collisionPhysics.alien
level.physicsBody?.collisionBitMask = collisionPhysics.alien
James
I tried to make contact but it didn't work .
I create an enum:
enum object:UInt32{
case BB = 1
case TR = 2
case GAP = 3
}
Then I have 3 nodes:
balloon.physicsBody?.categoryBitMask = object.BB.rawValue
balloon.physicsBody?.contactTestBitMask = object.TR.rawValue
balloon.physicsBody?.collisionBitMask = object.GAP.rawValue
tree2.physicsBody?.categoryBitMask = object.TR.rawValue
tree2.physicsBody?.contactTestBitMask = object.BB.rawValue
gap.physicsBody?.categoryBitMask = object.GAP.rawValue
gap.physicsBody?.collisionBitMask = object.GAP.rawValue
gap.physicsBody?.contactTestBitMask = object.BB.rawValue
In DidBeginContact:
let result = contact.bodyA.categoryBitMask + contact.bodyB.categoryBitMask
switch result{
case object.BB.rawValue + object.TR.rawValue:
xoaBB(contact.bodyA.node as! SKSpriteNode, TR: contact.bodyB.node as! SKSpriteNode, toaDo: contact.contactPoint)
case object.BB.rawValue + object.GAP.rawValue:
score++
scorelabel.text = "\(score)"
default:
return
}
The problem is:
"case object.BB.rawValue + object.GAP.rawValue" didn't work
I want the ballon fly through the gap to score but it can't. Balloon just contact to gap and gets stuck there as shown here.
Can anyone help me, I really need "how to use Enum tutorial too"
As mentioned in my comment your bitmask-values doesn't quite make sense. How about something a little like this (I have not checked for typos):
enum MaskType : UInt32 {
case Balloon = 1
case Gap = 2
case Wall = 4
case None = 0
}
Then you need to decide what you are after. The following example does not register collisions, meaning you want have physical interactions happening between the objects.
balloon.physicsBody?.categoryBitMask = MaskType.Balloon.rawValue
balloon.physicsBody?.collisionBitMask = MaskType.None.rawValue
balloon.physicsBody?.contactTestBitMask = MaskType.Gap.rawValue | MaskType.Wall.rawValue
The balloon sprite will now register contacts with objects having either the .Gap or .Wall value as their categoryBitMask.
If you now setup your other spriteNode's physicsBodies appropriately you can do some nice things in the didBeginContact function. Perhaps something like this:
func didBeginContact(contact: SKPhysicsContact) {
let contactA = contact.bodyA
let contactB = contact.bodyB
func ballonHitType() -> Uint32 {
if contactA.categoryBitMask == MaskType.Baloon.rawValue {
return contactB.categoryBitMask
} else {
return contactA.categoryBitMask
}
}
// Then simply perform your wanted actions based on whatever ballonHitType you receive
}
I'm working with Swift, Xcode 6 and SpriteKit,
I want make a game where some sprites fall down from the top of the screen, but each sprite have a defined speed, position and activation time. I have this working code, but I really don't think that it's the most optimised way to do it:
class obstacles: SKSpriteNode
{
var isOnScreen = false
var type: Int = 0
var initTime: Int = 0
}
var obstacle = [obstacles]() // obstacle is an array of SKSpriteNode
// type : initTime : speed : position : xScale : yScale
var level1: [String] = ["0:120:3:0:1:1", "0:130:4:80:2:2","1:140:8:120:6:1","0:150:6:240:2:2"]
override func didMoveToView(view: SKView)
{
initObstacles()
}
func initObstacles()
{
for var x = 0; x < level1.count; x++ // for each obstacle
{
var index = 0
var type = String("")
var initTime = String("")
var speed = String("")
var position = String("")
var xScale = String("")
var yScale = String("")
var lastIndex = obstacle.count
for Character in level1[x] // we read each character one by one
{
if Character == ":" { index++ } // if it's a ":" we change the variable
else
{
switch index
{
case 0:
type += String(Character)
case 1:
initTime += String(Character)
case 2:
speed += String(Character)
case 3:
position += String(Character)
case 4:
xScale += String(Character)
case 5:
yScale += String(Character)
default:
break;
}
}
}
obstacle.append(obstacles(imageNamed: "Rectangle")) // we add an element to the array
obstacle[lastIndex].type = type.toInt()! // we init all the values
obstacle[lastIndex].initTime = initTime.toInt()!
obstacle[lastIndex].speed = CGFloat(speed.toInt()!)
obstacle[lastIndex].size.width = DEFAULT_OBSTACLE_SIZE * CGFloat(xScale.toInt()!)
obstacle[lastIndex].size.height = DEFAULT_OBSTACLE_SIZE * CGFloat(yScale.toInt()!)
obstacle[lastIndex].position = CGPointMake(CGFloat(position.toInt()!) - obstacle[lastIndex].size.width/2, CGRectGetMaxY(frame) + obstacle[lastIndex].size.height/2)
}
}
Do you know how could I manage to do the same thing, but more "clean" ?
I would suggest to create a class or struct that holds all necessary data for an obstacle and additionally change your type from a standard number to an enum, e.g.:
enum ObstacleType {
case Block, Tree, WhatEverObstaclesYouHave...
}
struct Obstacle {
var type: ObstacleType
var initializationTime: NSTimeInterval
var speed: Double
// and similarly for position, your scales and what you may need in future
}
and create them using, e.g.
Obstacle(type: .Block, initializationTime: 0, speed: 12.0, ...)
Advantage (obviously) is that you have no problems anymore parsing your string (there is no string anymore) and can provide all necessary information using the appropriate type directly. And you can easily use an enum for your type, which should be an enum, because your Obstacle is not a 1, but a Block, Tree or whatever.
Your level1 variable could then be defined like this:
var level1 : [Obstacle] = [
Obstacle(type: .Block, initializationTime: 120, speed: 3.0, ...),
Obstacle(type: .Block, initializationTime: 130, speed: 4.0, ...),
Obstacle(type: .Tree, initializationTime: 140, speed: 8.0, ...),
Obstacle(type: .Block, initializationTime: 150, speed: 6.0, ...)
]
To get rid of the labels in the initializer, just define your own initializer. Just add this to your struct:
init(_ type: ObstacleType, _ time: NSTimeInterval, _ speed: Double, ...) {
self.type = type
self.initializationTime = time
self.speed = speed
// other variables...
}
Then you can create every Obstacle like this:
Obstacle(.Block, 120, 3.0, ...)
But now you can not easily tell anymore which number has what meaning from reading the instantiation. I do not recommend this just to type less as autocomplete will present you with most of it.