Reputation: 269
I'm new to Swift so apologies if I have made a rookie error. I am trying to get two boxes to disappear when they touch if the two boxes are the same color. I have the following code so far:
This code sets up the game:
import SpriteKit
import GameplayKit
class GameScene: SKScene, SKPhysicsContactDelegate {
override func didMove(to view: SKView) {
physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
physicsWorld.contactDelegate = self
let background = SKSpriteNode(imageNamed: "background.jpg")
background.size = self.frame.size;
background.position = CGPoint(x: 0, y: 0)
background.blendMode = .replace
background.zPosition = -1
addChild(background)
}
Code to generate a random color:
enum Color {
case ColorRed
case ColorGreen
case ColorBlue
public var color: UIColor {
switch self {
case .ColorRed: return UIColor(red: 255, green: 0, blue: 0, alpha: 1)
case .ColorGreen: return UIColor(red: 0, green: 255, blue: 0, alpha: 1)
case .ColorBlue: return UIColor(red: 0, green: 0, blue: 255, alpha: 1)
}
}
static var all: [Color] = [.ColorRed, .ColorGreen, .ColorBlue]
static var randomColor: UIColor {
let randomIndex = Int(arc4random_uniform(UInt32(all.count)))
return all[randomIndex].color
}
}
This is the part that matters - the actual contact between the objects:
func didBegin(_ contact: SKPhysicsContact) {
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
let firstBody = contact.bodyA.node as! SKSpriteNode!
let secondBody = contact.bodyB.node as! SKSpriteNode!
if firstBody!.color == secondBody!.color {
firstBody!.removeFromParent()
secondBody!.removeFromParent()
}
} else {
let firstBody = contact.bodyB.node as! SKSpriteNode!
let secondBody = contact.bodyA.node as! SKSpriteNode!
if firstBody!.color == secondBody!.color {
firstBody!.removeFromParent()
secondBody!.removeFromParent()
}
}
}
And finally the code for when the user touches the screen:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let location = touch.location(in: self)
let box = SKSpriteNode(color: UIColor.red, size: CGSize(width: 64, height: 64))
box.color = Color.randomColor
box.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 64, height: 64))
box.position = location
addChild(box)
}
}
I have provided all the code so you know the setup. Thank you in advance for your help.
Upvotes: 3
Views: 398
Reputation: 13675
You haven't set contactBitMask appropriately so no contacts were detected... By default, due to performance reasons a default value of this mask is zero:
When two bodies share the same space, each body’s category mask is tested against the other body’s contact mask by performing a logical AND operation. If either comparison results in a nonzero value, an SKPhysicsContact object is created and passed to the physics world’s delegate. For best performance, only set bits in the contacts mask for interactions you are interested in.
The default value is 0x00000000 (all bits cleared).
To fix this, set both contact and category bit masks to an appropriate values, like this:
class GameScene: SKScene,SKPhysicsContactDelegate {
override func didMove(to view: SKView) {
self.physicsWorld.contactDelegate = self
physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
physicsWorld.contactDelegate = self
}
func didBegin(_ contact: SKPhysicsContact) {
if let bodyA = contact.bodyA.node as? SKSpriteNode,
let bodyB = contact.bodyB.node as? SKSpriteNode{
//Of course this is simple example and you will have to do some "filtering" to determine what type of objects are collided.
// But the point is , when appropriate objects have collided, you compare their color properties.
if bodyA.color == bodyB.color {
bodyA.run(SKAction.removeFromParent())
bodyB.run(SKAction.removeFromParent())
}
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let location = touch.location(in: self)
let box = SKSpriteNode(color: UIColor.red, size: CGSize(width: 64, height: 64))
box.color = Color.randomColor
box.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 64, height: 64))
box.physicsBody?.contactTestBitMask = 0b1
box.physicsBody?.categoryBitMask = 0b1
box.position = location
addChild(box)
}
}
}
Now when a contact happen between two bodies, as said in docs, each body's category bit mask is tested agains the other body's contact mask, by performing logical AND operation. If a result is non-zero, a contact notification occurs. In this case this would be 1 & 1 = 1.
Upvotes: 4
Reputation: 7405
First, declare a struct
for the categoryBitMask
:
struct ColorMask {
static let Red: UInt32 = 0x1 << 0
static let Green: UInt32 = 0x1 << 1
static let Blue: UInt32 = 0x1 << 2
}
Second, change you enum declaration to the following:
enum Color {
case ColorRed
case ColorGreen
case ColorBlue
public var color: UIColor {
switch self {
case .ColorRed: return UIColor(red: 255, green: 0, blue: 0, alpha: 1)
case .ColorGreen: return UIColor(red: 0, green: 255, blue: 0, alpha: 1)
case .ColorBlue: return UIColor(red: 0, green: 0, blue: 255, alpha: 1)
}
}
static var all: [Color] = [.ColorRed, .ColorGreen, .ColorBlue]
static var randomColor: Color {
let randomIndex = Int(arc4random_uniform(UInt32(all.count)))
return all[randomIndex]
}
}
I just changed the above code to return Color
instead of UIColor
.
Third, modify didBegin
to:
func didBegin(_ contact: SKPhysicsContact) {
if contact.bodyA.categoryBitMask == contact.bodyB.categoryBitMask {
let firstBody = contact.bodyA.node as! SKSpriteNode!
let secondBody = contact.bodyB.node as! SKSpriteNode!
firstBody!.removeFromParent()
secondBody!.removeFromParent()
}
}
In the above code, just compare the categoryBitMask
is enough as I will set the body with same color with same categoryBitMask
later.
Lastly, set the categoryBitMask
to the box using the following code:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let location = touch.location(in: self)
let box = SKSpriteNode(color: UIColor.red, size: CGSize(width: 64, height: 64))
let color = Color.randomColor
box.color = color.color
box.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 64, height: 64))
if color == Color.ColorRed {
box.physicsBody?.categoryBitMask = ColorMask.Red
} else if color == Color.ColorGreen {
box.physicsBody?.categoryBitMask = ColorMask.Green
}else if color == Color.ColorBlue {
box.physicsBody?.categoryBitMask = ColorMask.Blue
}
box.physicsBody?.contactTestBitMask = ColorMask.Red | ColorMask.Green | ColorMask.Blue
box.position = location
addChild(box)
}
}
Upvotes: 0