Issue
This is my code for my game. A brick falls and hits a side of a square. To differentiate what side is hit, four triangles are spawned that make the square. They are invisible from a low opacity. I am not receiving any contact between the brick node and the triangle node. The brick piece just falls right through the square. Any help?
Game Code
import SpriteKit
class GameScene: SKScene {
let basicTop = SKSpriteNode(imageNamed: "basic top");
let basicBottom = SKSpriteNode(imageNamed: "basic bottom");
let basicLeft = SKSpriteNode(imageNamed: "basic left");
let basicRight = SKSpriteNode(imageNamed: "basic right");
let brickTop = SKSpriteNode(imageNamed: "Top Side");
let brickBottom = SKSpriteNode(imageNamed: "Bottom Side");
let brickLeft = SKSpriteNode(imageNamed: "Left Side");
let brickRight = SKSpriteNode(imageNamed: "Right Side copy 2");
let basicBrick = SKSpriteNode(imageNamed: "Basic Brick")
override func didMove(to view: SKView) {
isUserInteractionEnabled = true
layoutScene()
}
func layoutScene() {
backgroundColor = UIColor(red: 10/255, green: 75/255, blue: 150/255, alpha: 1.0)
spawnBrick()
spawnBasicTop()
spawnBasicBottom()
spawnBasicLeft()
spawnBasicRight()
backgroundScene()
spawnBasicBrick()
setupPhysics()
}
func setupPhysics() {
physicsWorld.gravity = CGVector(dx: 0.0, dy: -0.1)
physicsWorld.contactDelegate = self
}
func spawnBrick() {
let randomFunc = [self.spawnbrickTop, self.spawnbrickBottom, self.spawnbrickLeft, self.spawnbrickRight]
let randomResult = Int(arc4random_uniform(UInt32(randomFunc.count)))
randomFunc[randomResult]()
}
func spawnbrickTop() {
brickTop.size = CGSize(width: 210, height: 105)
brickTop.name = "BrickTop"
brickTop.position = CGPoint(x: frame.midX, y: frame.maxY)
brickTop.zPosition = 1
//physics stuff begins here
brickTop.physicsBody = SKPhysicsBody(circleOfRadius: max(brickTop.size.width / 2,
brickTop.size.height / 2))
brickTop.physicsBody?.categoryBitMask = PhysicsCategories.brickTopCategory
brickTop.physicsBody?.contactTestBitMask = PhysicsCategories.basicTopCategory |
PhysicsCategories.basicBottomCategory |
PhysicsCategories.basicLeftCategory |
PhysicsCategories.basicRightCategory
brickTop.physicsBody?.collisionBitMask = PhysicsCategories.none
//bye bye physics
addChild(brickTop)
}
func spawnbrickBottom() {
brickBottom.size = CGSize(width: 150, height: 101)
brickBottom.name = "BrickBottom"
brickBottom.position = CGPoint(x: frame.midX, y: frame.maxY)
brickBottom.zPosition = 1
//physics stuff begins here
brickBottom.physicsBody = SKPhysicsBody(circleOfRadius: max(brickBottom.size.width / 2,
brickBottom.size.height / 2))
brickBottom.physicsBody?.categoryBitMask = PhysicsCategories.brickBottomCategory
brickBottom.physicsBody?.contactTestBitMask = PhysicsCategories.basicTopCategory |
PhysicsCategories.basicBottomCategory |
PhysicsCategories.basicLeftCategory |
PhysicsCategories.basicRightCategory
brickBottom.physicsBody?.collisionBitMask = PhysicsCategories.none
//bye bye physics
addChild(brickBottom)
}
func spawnbrickLeft() {
brickLeft.size = CGSize(width: 170, height: 80)
brickLeft.name = "BrickLeft"
brickLeft.position = CGPoint(x: frame.midX, y: frame.maxY)
brickLeft.zPosition = 1
//physics stuff begins here
brickLeft.physicsBody = SKPhysicsBody(circleOfRadius: max(brickLeft.size.width / 2,
brickLeft.size.height / 2))
brickLeft.physicsBody?.categoryBitMask = PhysicsCategories.brickLeftCategory
brickLeft.physicsBody?.contactTestBitMask = PhysicsCategories.basicTopCategory |
PhysicsCategories.basicBottomCategory |
PhysicsCategories.basicLeftCategory |
PhysicsCategories.basicRightCategory
brickLeft.physicsBody?.collisionBitMask = PhysicsCategories.none
//bye bye physics
addChild(brickLeft)
}
func spawnbrickRight() {
brickRight.size = CGSize(width: 140, height: 95)
brickRight.name = "BrickRight"
brickRight.position = CGPoint(x: frame.midX, y: frame.maxY)
brickRight.zPosition = 1
//physics stuff begins here
brickRight.physicsBody = SKPhysicsBody(circleOfRadius: max(brickRight.size.width / 2,
brickRight.size.height / 2))
brickRight.physicsBody?.categoryBitMask = PhysicsCategories.brickRightCategory
brickRight.physicsBody?.contactTestBitMask = PhysicsCategories.basicTopCategory |
PhysicsCategories.basicBottomCategory |
PhysicsCategories.basicLeftCategory |
PhysicsCategories.basicRightCategory
brickRight.physicsBody?.collisionBitMask = PhysicsCategories.none
//bye bye physics
addChild(brickRight)
}
func turnBasicTop() {
basicTop.run(SKAction.rotate(byAngle: .pi/2, duration: 0.25))
}
func turnBasicBottom() {
basicBottom.run(SKAction.rotate(byAngle: .pi/2, duration: 0.25))
}
func turnBasicLeft() {
basicLeft.run(SKAction.rotate(byAngle: .pi/2, duration: 0.25))
}
func turnBasicRight() {
basicRight.run(SKAction.rotate(byAngle: .pi/2, duration: 0.25))
}
func turnBasicBrick() {
basicBrick.run(SKAction.rotate(byAngle: .pi/2, duration: 0.25))
}
func gameOver() {
print("Game Over!")
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
turnBasicTop()
turnBasicBottom()
turnBasicLeft()
turnBasicRight()
turnBasicBrick()
}
func spawnBasicBrick() {
basicBrick.size = CGSize(width: 200, height: 177.6)
basicBrick.position = CGPoint(x: frame.midX, y: frame.minY + basicBrick.size.width)
basicBrick.zPosition = 1
basicBrick.physicsBody = SKPhysicsBody(rectangleOf: basicBrick.size)
basicBrick.physicsBody?.categoryBitMask = PhysicsCategories.basicBrickCategory
basicBrick.physicsBody?.isDynamic = false
basicBrick.physicsBody?.allowsRotation = true
addChild(basicBrick)
}
func spawnBasicTop() {
basicTop.size = CGSize(width: 400, height: 400)
basicTop.position = CGPoint(x: 230, y: 200)
basicTop.zPosition = 1
basicTop.alpha = 0.3
basicTop.name = "BasicTop"
//physics stuff begins here
basicTop.physicsBody = SKPhysicsBody(rectangleOf: basicTop.size)
basicTop.physicsBody?.categoryBitMask = PhysicsCategories.basicTopCategory
basicTop.physicsBody?.isDynamic = false
basicTop.physicsBody?.allowsRotation = true
//bye bye physics
addChild(basicTop)
}
func spawnBasicBottom() {
basicBottom.size = CGSize(width: 400, height: 400)
basicBottom.position = CGPoint(x: 230, y: 200)
basicBottom.zPosition = 1
basicBottom.alpha = 0.3
basicBottom.name = "BasicBottom"
//physics stuff begins here
basicBottom.physicsBody = SKPhysicsBody(rectangleOf: basicBottom.size)
basicBottom.physicsBody?.categoryBitMask = PhysicsCategories.basicBottomCategory
basicBottom.physicsBody?.isDynamic = false
//bye bye physics
addChild(basicBottom)
}
func spawnBasicLeft() {
basicLeft.size = CGSize(width: 400, height: 400)
basicLeft.position = CGPoint(x: 230, y: 200)
basicLeft.zPosition = 1
basicLeft.alpha = 0.3
basicLeft.name = "BasicLeft"
//physics stuff begins here
basicLeft.physicsBody = SKPhysicsBody(rectangleOf: basicLeft.size)
basicLeft.physicsBody?.categoryBitMask = PhysicsCategories.basicLeftCategory
basicLeft.physicsBody?.isDynamic = false
//bye bye physics
addChild(basicLeft)
}
func spawnBasicRight() {
basicRight.size = CGSize(width: 400, height: 400)
basicRight.position = CGPoint(x: 230, y: 200)
basicRight.zPosition = 1
basicRight.alpha = 0.3
basicRight.name = "BasicRight"
//physics stuff begins here
basicRight.physicsBody = SKPhysicsBody(rectangleOf: basicRight.size)
basicRight.physicsBody?.categoryBitMask = PhysicsCategories.basicRightCategory
basicRight.physicsBody?.isDynamic = false
//bye bye physics
addChild(basicRight)
}
func backgroundScene() {
let constructionSite = SKSpriteNode(imageNamed: "Background Image")
constructionSite.size = frame.size
constructionSite.position = CGPoint(x: frame.midX, y: frame.midY)
constructionSite.zPosition = -1
addChild(constructionSite)
}
}
extension GameScene: SKPhysicsContactDelegate {
func didBegin(_ contact: SKPhysicsContact) {
//01
//10
//11
let contactMask = contact.bodyA.categoryBitMask |
contact.bodyB.categoryBitMask
if contactMask == PhysicsCategories.brickTopCategory |
(PhysicsCategories.basicTopCategory) {
if let brickTop = contact.bodyA.node?.name == "BrickTop" ? contact.bodyA.node
as? SKSpriteNode : contact.bodyB.node as? SKSpriteNode {
if brickTop == basicTop {
print("Correct!")
brickTop.run(SKAction.fadeOut(withDuration: 0.05), completion: {
brickTop.removeFromParent()
self.spawnBrick()
})
}
}
else if contactMask == PhysicsCategories.brickBottomCategory |
(PhysicsCategories.basicBottomCategory) {
if let brickBottom = contact.bodyA.node?.name == "BrickBottom" ? contact.bodyA.node
as? SKSpriteNode : contact.bodyB.node as? SKSpriteNode {
if brickBottom == basicBottom {
print("Correct!")
brickBottom.run(SKAction.fadeOut(withDuration: 0.05), completion: {
brickBottom.removeFromParent()
self.spawnBrick()
})
}
}
else if contactMask == PhysicsCategories.brickLeftCategory |
(PhysicsCategories.basicLeftCategory) {
if let brickLeft = contact.bodyA.node?.name == "BrickLeft" ? contact.bodyA.node
as? SKSpriteNode : contact.bodyB.node as? SKSpriteNode {
if brickLeft == basicLeft {
print("Correct!")
brickLeft.run(SKAction.fadeOut(withDuration: 0.05), completion: {
brickLeft.removeFromParent()
self.spawnBrick()
})
}
}
else if contactMask == PhysicsCategories.brickRightCategory |
(PhysicsCategories.basicRightCategory) {
if let brickRight = contact.bodyA.node?.name == "BrickRight" ? contact.bodyA.node
as? SKSpriteNode : contact.bodyB.node as? SKSpriteNode {
if brickRight == basicRight {
print("Correct!")
brickRight.run(SKAction.fadeOut(withDuration: 0.05), completion: {
brickRight.removeFromParent()
self.spawnBrick()
})
}
}
else {
gameOver()
}
}
}
}
}
}
}
Settings File:
enum PhysicsCategories {
static let none: UInt32 = 0
static let brickCategory: UInt32 = 0x1//01
static let brickTopCategory: UInt32 = 0x1 //01
static let brickBottomCategory: UInt32 = 0x1//01
static let brickLeftCategory: UInt32 = 0x1//01
static let brickRightCategory: UInt32 = 0x1//01
static let basicTopCategory: UInt32 = 0x1 //10; shifts all bits to the left
static let basicBottomCategory: UInt32 = 0x1 //10; shifts all bits to the left
static let basicLeftCategory: UInt32 = 0x1 //10; shifts all bits to the left
static let basicRightCategory: UInt32 = 0x1 //10; shifts all bits to the left
static let basicBrickCategory: UInt32 = 0x1
}
Related
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate{
var picsArray = [String]()
var scoreLabel: SKLabelNode!
var editLabel: SKLabelNode!
var editingMode = false{
didSet{
if editingMode{
editLabel.text = "Done"
}
else{
editLabel.text = "Edit"
}
}
}
var score = 0 {
didSet{
scoreLabel.text = " score: \(score)"
}
}
override func didMove(to view: SKView) {
let fm = FileManager.default
let path = Bundle.main.resourcePath!
let items = try! fm.contentsOfDirectory(atPath: path)
for item in items {
if item.hasPrefix("ball"){
picsArray.append(item)
}
print(picsArray.count)
}
physicsWorld.contactDelegate = self
let background = SKSpriteNode(imageNamed: "background")
background.position = CGPoint(x: 512, y: 384)
background.blendMode = .replace
background.zPosition = -1
addChild(background)
scoreLabel = SKLabelNode(fontNamed: "chalkduster")
scoreLabel.text = "Score: 0"
scoreLabel.horizontalAlignmentMode = .right
scoreLabel.position = CGPoint(x: 980, y: 700)
addChild(scoreLabel)
editLabel = SKLabelNode(fontNamed: "chalkduster")
editLabel.text = "Edit"
editLabel.position = CGPoint(x: 80, y: 700)
addChild(editLabel)
physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
makeSlots(at: CGPoint(x: 128, y: 0), isGood: true)
makeSlots(at: CGPoint(x: 384, y: 0), isGood: false)
makeSlots(at: CGPoint(x: 640, y: 0), isGood: true)
makeSlots(at: CGPoint(x: 896, y: 0), isGood: false)
makeBouncer(at: CGPoint(x: 0, y: 0))
makeBouncer(at: CGPoint(x: 256, y: 0))
makeBouncer(at: CGPoint(x: 512, y: 0))
makeBouncer(at: CGPoint(x: 768, y: 0))
makeBouncer(at: CGPoint(x: 1024, y: 0))
// Get label node from scene and store it for use later
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else{return}
let location = touch.location(in: self)
let objects = nodes(at: location)
if objects.contains(editLabel){
editingMode.toggle()
}
else{
if editingMode{
let size = CGSize(width: Int.random(in: 16...128), height: 16)
let box = SKSpriteNode(color: UIColor(red: CGFloat.random(in: 0...1), green: CGFloat.random(in: 0...1), blue: CGFloat.random(in: 0...1), alpha: 1), size: size)
box.zRotation = CGFloat.random(in: 0...3)
box.position = location
box.physicsBody = SKPhysicsBody(rectangleOf: box.size)
box.physicsBody?.isDynamic = false
addChild(box)
}
else{
let ball = SKSpriteNode(imageNamed: "ballRed")
ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.size.width/2.0)
ball.physicsBody?.restitution = 0.4
ball.physicsBody?.contactTestBitMask = ball.physicsBody?.collisionBitMask ?? 0
ball.position = location
ball.name = "ball"
addChild(ball)
}
}
}
func makeBouncer(at position: CGPoint){
let bouncer = SKSpriteNode(imageNamed: "bouncer")
bouncer.position = position
bouncer.physicsBody = SKPhysicsBody(circleOfRadius: bouncer.size.width/2)
bouncer.physicsBody?.isDynamic = false
addChild(bouncer)
}
func makeSlots(at position: CGPoint, isGood: Bool){
var slotBase: SKSpriteNode
var slotGlow: SKSpriteNode
if isGood{
slotBase = SKSpriteNode(imageNamed: "slotBaseGood")
slotGlow = SKSpriteNode(imageNamed: "slotGlowGood")
slotBase.name = "good"
} else{
slotBase = SKSpriteNode(imageNamed: "slotBaseBad")
slotGlow = SKSpriteNode(imageNamed: "slotGlowBad")
slotBase.name = "bad"
}
slotGlow.position = position
slotBase.position = position
slotBase.physicsBody = SKPhysicsBody(rectangleOf: slotBase.size)
slotBase.physicsBody?.isDynamic = false
addChild(slotGlow)
addChild(slotBase)
let spin = SKAction.rotate(byAngle: .pi, duration: 1)
let spinforever = SKAction.repeatForever(spin)
slotGlow.run(spinforever)
}
func collision(between ball: SKNode, object: SKNode){
if object.name == "good"{
destroy(ball: ball)
score+=1
}
else{
if object.name == "bad"{
destroy(ball: ball)
score-=1
}
}
}
func destroy(ball: SKNode){
ball.removeFromParent()
}
func didBegin(_ contact: SKPhysicsContact) {
guard let nodeA = contact.bodyA.node else{return}
guard let nodeB = contact.bodyB.node else{return}
if nodeA.name == "ball"{
collision(between: nodeA, object: nodeB)
}
else if nodeB.name == "ball"{
collision(between: nodeB, object: nodeA)
}
}
}
I am trying to detect collision between 2 objects but it works only some times. Most of the time it doesn't work. Maybe you can help me out understanding what i am doing wrong.
static let soldier: UInt32 = 0x1 << 4
static let enemy: UInt32 = 0x1 << 5
function setting the physicsBody for the ground, the soldier and the enemy:
func addPhysicsBody(to sprite: SKSpriteNode, with name: String) {
let centerPoint = CGPoint(x:sprite.size.width / 2 - (sprite.size.width * sprite.anchorPoint.x), y:sprite.size.height / 2 - (sprite.size.height * sprite.anchorPoint.y))
sprite.physicsBody = SKPhysicsBody(rectangleOf: sprite.size, center: centerPoint)
sprite.physicsBody?.allowsRotation = false
sprite.physicsBody?.affectedByGravity = true
sprite.physicsBody?.restitution = 0.0
sprite.physicsBody?.friction = 0
if name.contains(GameConstants.StringConstants.soldierName) {
sprite.physicsBody?.categoryBitMask = GameConstants.PhysicsCategories.soldier
sprite.physicsBody?.contactTestBitMask = GameConstants.PhysicsCategories.enemy
sprite.physicsBody?.collisionBitMask = GameConstants.PhysicsCategories.ground
sprite.physicsBody?.isDynamic = true
} else if name.contains(GameConstants.StringConstants.enemyName) {
sprite.physicsBody?.categoryBitMask = GameConstants.PhysicsCategories.enemy
sprite.physicsBody?.contactTestBitMask = GameConstants.PhysicsCategories.soldier
sprite.physicsBody?.collisionBitMask = GameConstants.PhysicsCategories.ground
sprite.physicsBody?.isDynamic = true
} else if name == GameConstants.StringConstants.groundName {
sprite.physicsBody?.affectedByGravity = false
sprite.physicsBody?.categoryBitMask = GameConstants.PhysicsCategories.ground
sprite.physicsBody?.isDynamic = false
}
}
generating the soldier and the enemy and moving them:
func generateASoldier() {
let soldier = SKSpriteNode(texture: SKTexture(image: UIImage(named: "0\(GameConstants.StringConstants.idlePrefixKey)0")!), color: UIColor.darkGray, size: self.frame.size)
soldier.size = CGSize(width: castle.frame.size.height/4, height: castle.frame.size.height/4)
soldier.name = "\(GameConstants.StringConstants.soldierName)\(soldierCounter)"
soldierCounter+=1
addPhysicsBody(to: soldier, with: soldier.name!)
soldier.position = CGPoint(x: frame.minX-soldier.size.width*2, y: frame.midY/1.1)
soldier.zPosition = GameConstants.ZPositions.playerZ
addChild(soldier)
isMovingSoldier.append(true)
moveSoldier(soldier: soldier, index: isMovingSoldier.count-1)
}
func moveSoldier(soldier: SKSpriteNode, index: Int) {
soldier.physicsBody?.applyImpulse(CGVector(dx: charactersSpeed, dy: 0))
DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: {
if self.isMovingSoldier[index] {
self.moveSoldier(soldier: soldier, index: index)
}
})
}
func generateAnEnemy() {
let enemy = SKSpriteNode(texture: SKTexture(image: UIImage(named: "1\(GameConstants.StringConstants.idlePrefixKey)0")!), color: UIColor.darkGray, size: self.frame.size)
enemy.size = CGSize(width: -castle.frame.size.height/4, height: castle.frame.size.height/4)
enemy.name = "\(GameConstants.StringConstants.enemyName)\(enemyCounter)"
enemyCounter+=1
addPhysicsBody(to: enemy, with: enemy.name!)
enemy.position = CGPoint(x: frame.maxX-enemy.size.width*2, y: frame.midY/1.1)
enemy.zPosition = GameConstants.ZPositions.playerZ
addChild(enemy)
isMovingEnemy.append(true)
moveEnemy(enemy: enemy, index: isMovingEnemy.count-1)
}
func moveEnemy(enemy: SKSpriteNode, index: Int) {
enemy.physicsBody?.applyImpulse(CGVector(dx: -charactersSpeed, dy: 0))
DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: {
if self.isMovingEnemy[index] {
self.moveEnemy(enemy: enemy, index: index)
}
})
}
generating the ground:
func generateGround() {
ground = SKSpriteNode(color: .white, size: CGSize(width: frame.maxX*3, height: UIScreen.main.bounds.size.height/2.435))
ground.name = GameConstants.StringConstants.groundName
addPhysicsBody(to: ground, with: ground.name!)
ground.zPosition = GameConstants.ZPositions.playerZ
ground.position = CGPoint(x: frame.midX, y: frame.minY)
self.addChild(ground)
}
Using this code func didBegin(_ contact: SKPhysicsContact) is called a random amount of times and i am trying to make sure it will be called every time the soldier and the enemy collide
Edit:
I found out that the problem is detecting the collision using the applyImpulse function. Using gravity it detects the collision
Right now, I have a brick that falls down the screen. When it hits my square nothing happens. As you can see in the image below, I have four triangles that make up a square in the same spot, but with a low opacity. This is to help differentiate between what side of the square is contacted. However, nothing respawns or despawns win the brick hits the triange (the trianges that make up the square.)
Image of Game
func spawnBrick() {
let randomFunc = [self.spawnbrickTop, self.spawnbrickBottom, self.spawnbrickLeft, self.spawnbrickRight]
let randomResult = Int(arc4random_uniform(UInt32(randomFunc.count)))
randomFunc[randomResult]()
}
func spawnbrickTop() {
brickTop.size = CGSize(width: 210, height: 105)
brickTop.name = "BrickTop"
brickTop.position = CGPoint(x: frame.midX, y: frame.maxY)
brickTop.zPosition = 1.5
//physics stuff begins here
brickTop.physicsBody = SKPhysicsBody(circleOfRadius: max(brickTop.size.width / 2,
brickTop.size.height / 2))
brickTop.physicsBody?.categoryBitMask = PhysicsCategories.brickTopCategory
brickTop.physicsBody?.contactTestBitMask = PhysicsCategories.basicTopCategory |
PhysicsCategories.basicBottomCategory |
PhysicsCategories.basicLeftCategory |
PhysicsCategories.basicRightCategory
brickTop.physicsBody?.collisionBitMask = PhysicsCategories.none
//bye bye physics
self.addChild(brickTop)
}
func spawnbrickBottom() {
brickBottom.size = CGSize(width: 230, height: 101)
brickBottom.name = "BrickBottom"
brickBottom.position = CGPoint(x: frame.midX, y: frame.maxY)
brickBottom.zPosition = 1.5
//physics stuff begins here
brickBottom.physicsBody = SKPhysicsBody(circleOfRadius: max(brickBottom.size.width / 2,
brickBottom.size.height / 2))
brickBottom.physicsBody?.categoryBitMask = PhysicsCategories.brickBottomCategory
brickBottom.physicsBody?.contactTestBitMask = PhysicsCategories.basicTopCategory |
PhysicsCategories.basicBottomCategory |
PhysicsCategories.basicLeftCategory |
PhysicsCategories.basicRightCategory
brickBottom.physicsBody?.collisionBitMask = PhysicsCategories.none
//bye bye physics
self.addChild(brickBottom)
}
func spawnbrickLeft() {
brickLeft.size = CGSize(width: 170, height: 80)
brickLeft.name = "BrickLeft"
brickLeft.position = CGPoint(x: frame.midX, y: frame.maxY)
brickLeft.zPosition = 1.5
//physics stuff begins here
brickLeft.physicsBody = SKPhysicsBody(circleOfRadius: max(brickLeft.size.width / 2,
brickLeft.size.height / 2))
brickLeft.physicsBody?.categoryBitMask = PhysicsCategories.brickLeftCategory
brickLeft.physicsBody?.contactTestBitMask = PhysicsCategories.basicTopCategory |
PhysicsCategories.basicBottomCategory |
PhysicsCategories.basicLeftCategory |
PhysicsCategories.basicRightCategory
brickLeft.physicsBody?.collisionBitMask = PhysicsCategories.none
//bye bye physics
self.addChild(brickLeft)
}
func spawnbrickRight() {
brickRight.size = CGSize(width: 140, height: 95)
brickRight.name = "BrickRight"
brickRight.position = CGPoint(x: frame.midX, y: frame.maxY)
brickRight.zPosition = 1.5
//physics stuff begins here
brickRight.physicsBody = SKPhysicsBody(circleOfRadius: max(brickRight.size.width / 2,
brickRight.size.height / 2))
brickRight.physicsBody?.categoryBitMask = PhysicsCategories.brickRightCategory
brickRight.physicsBody?.contactTestBitMask = PhysicsCategories.basicTopCategory |
PhysicsCategories.basicBottomCategory |
PhysicsCategories.basicLeftCategory |
PhysicsCategories.basicRightCategory
brickRight.physicsBody?.collisionBitMask = PhysicsCategories.none
//bye bye physics
self.addChild(brickRight)
}
func spawnBasicBrick() {
basicBrick.size = CGSize(width: 200, height: 177.6)
basicBrick.position = CGPoint(x: frame.midX, y: frame.minY + basicBrick.size.width)
basicBrick.zPosition = 1
basicBrick.physicsBody = SKPhysicsBody(rectangleOf: basicBrick.size)
basicBrick.physicsBody?.categoryBitMask = PhysicsCategories.basicBrickCategory
basicBrick.physicsBody?.isDynamic = false
basicBrick.physicsBody?.allowsRotation = true
addChild(basicBrick)
}
func spawnBasicTop() {
basicTop.size = CGSize(width: 400, height: 400)
basicTop.position = CGPoint(x: 230, y: 200)
basicTop.zPosition = 1.5
basicTop.alpha = 0.3
basicTop.name = "BasicTop"
//physics stuff begins here
basicTop.physicsBody = SKPhysicsBody(rectangleOf: basicTop.size)
basicTop.physicsBody?.categoryBitMask = PhysicsCategories.basicTopCategory
basicTop.physicsBody?.isDynamic = false
basicTop.physicsBody?.allowsRotation = true
//bye bye physics
addChild(basicTop)
}
func spawnBasicBottom() {
basicBottom.size = CGSize(width: 400, height: 400)
basicBottom.position = CGPoint(x: 230, y: 200)
basicBottom.zPosition = 1.5
basicBottom.alpha = 0.3
basicBottom.name = "BasicBottom"
//physics stuff begins here
basicBottom.physicsBody = SKPhysicsBody(rectangleOf: basicBottom.size)
basicBottom.physicsBody?.categoryBitMask = PhysicsCategories.basicBottomCategory
basicBottom.physicsBody?.isDynamic = false
//bye bye physics
addChild(basicBottom)
}
func spawnBasicLeft() {
basicLeft.size = CGSize(width: 400, height: 400)
basicLeft.position = CGPoint(x: 230, y: 200)
basicLeft.zPosition = 1.5
basicLeft.alpha = 0.3
basicLeft.name = "BasicLeft"
//physics stuff begins here
basicLeft.physicsBody = SKPhysicsBody(rectangleOf: basicLeft.size)
basicLeft.physicsBody?.categoryBitMask = PhysicsCategories.basicLeftCategory
basicLeft.physicsBody?.isDynamic = false
//bye bye physics
addChild(basicLeft)
}
func spawnBasicRight() {
basicRight.size = CGSize(width: 400, height: 400)
basicRight.position = CGPoint(x: 230, y: 200)
basicRight.zPosition = 1.5
basicRight.alpha = 0.3
basicRight.name = "BasicRight"
//physics stuff begins here
basicRight.physicsBody = SKPhysicsBody(rectangleOf: basicRight.size)
basicRight.physicsBody?.categoryBitMask = PhysicsCategories.basicRightCategory
basicRight.physicsBody?.isDynamic = false
//bye bye physics
addChild(basicRight)
}
extension GameScene: SKPhysicsContactDelegate {
func didBegin(_ contact: SKPhysicsContact) {
//01
//10
//11
let contactMask = contact.bodyA.categoryBitMask |
contact.bodyB.categoryBitMask
if contactMask == PhysicsCategories.brickTopCategory |
(PhysicsCategories.basicTopCategory) {
if let brickTop = contact.bodyA.node?.name == "BrickTop" ? contact.bodyA.node
as? SKSpriteNode : contact.bodyB.node as? SKSpriteNode {
if contact.bodyA.node?.name == "BrickTop" &&
contact.bodyB.node?.name == "BasicTop" {
print("Correct!")
brickTop.run(SKAction.fadeOut(withDuration: 0.05), completion: {
self.brickTop.removeFromParent()
self.spawnBrick()
})
}
}
else if contactMask == PhysicsCategories.brickBottomCategory |
(PhysicsCategories.basicBottomCategory) {
if let brickBottom = contact.bodyA.node?.name == "BrickBottom" ? contact.bodyA.node
as? SKSpriteNode : contact.bodyB.node as? SKSpriteNode {
if contact.bodyA.node?.name == "BrickBottom" &&
contact.bodyB.node?.name == "BasicBottom" {
print("Correct!")
brickBottom.run(SKAction.fadeOut(withDuration: 0.05), completion: {
self.brickBottom.removeFromParent()
self.spawnBrick()
})
}
}
else if contactMask == PhysicsCategories.brickLeftCategory |
(PhysicsCategories.basicLeftCategory) {
if let brickLeft = contact.bodyA.node?.name == "BrickLeft" ? contact.bodyA.node
as? SKSpriteNode : contact.bodyB.node as? SKSpriteNode {
if contact.bodyA.node?.name == "BrickLeft" &&
contact.bodyB.node?.name == "BasicLeft" {
print("Correct!")
brickLeft.run(SKAction.fadeOut(withDuration: 0.05), completion: {
self.brickLeft.removeFromParent()
self.spawnBrick()
})
}
}
else if contactMask == PhysicsCategories.brickRightCategory |
(PhysicsCategories.basicRightCategory) {
if let brickRight = contact.bodyA.node?.name == "BrickRight" ? contact.bodyA.node
as? SKSpriteNode : contact.bodyB.node as? SKSpriteNode {
if contact.bodyA.node?.name == "BrickRight" &&
contact.bodyB.node?.name == "BasicRight" {
print("Correct!")
brickRight.run(SKAction.fadeOut(withDuration: 0.05), completion: {
self.brickRight.removeFromParent()
self.spawnBrick()
})
}
}
else {
gameOver()
}
}
}
}
}
}
}
enum PhysicsCategories {
static let none: UInt32 = 0
static let brickCategory: UInt32 = 1//01
static let brickTopCategory: UInt32 = 1 //01
static let brickBottomCategory: UInt32 = 1//01
static let brickLeftCategory: UInt32 = 1//01
static let brickRightCategory: UInt32 = 1//01
static let basicTopCategory: UInt32 = 1 //10; shifts all bits to the left
static let basicBottomCategory: UInt32 = 1 //10; shifts all bits to the left
static let basicLeftCategory: UInt32 = 1 //10; shifts all bits to the left
static let basicRightCategory: UInt32 = 1 //10; shifts all bits to the left
static let basicBrickCategory: UInt32 = 1
}
The problem is probably in lines like this:
brickTop.physicsBody?.contactTestBitMask = PhysicsCategories.basicTopCategory
brickTop.physicsBody?.contactTestBitMask = PhysicsCategories.basicBottomCategory
brickTop.physicsBody?.contactTestBitMask = PhysicsCategories.basicLeftCategory
brickTop.physicsBody?.contactTestBitMask = PhysicsCategories.basicRightCategory
In this code, the contact bitmask ends up just being basicRight. You want to combine them. This is assuming that you set up the categories correctly to begin with (as 1, 2, 4, 8, etc)
You want it more like this:
brickTop.physicsBody?.contactTestBitMask = PhysicsCategories.basicTopCategory |
PhysicsCategories.basicBottomCategory |
PhysicsCategories.basicLeftCategory |
PhysicsCategories.basicRightCategory
Here is how this is supposed to work
You have a bunch of nodes in your scene
You group them into categories that have similar behavior
You create a category enum/value for each category as a bit in a mask (1, 2, 4, etc), which is (0x1 << 0, 0x1 << 1, 0x1 << 2)
You put the nodes into categories (using | to combine the category values)
You set the contactTest and collisionDetect bit masks (using | to combine the category values)
I don't know how your game is supposed to work, but there are several things that feel weird about your code.
You use the same number for different categories
You don't combine them into bitmasks.
Imagine I have the game Pong. I want to detect when the ball hits
The sidewalls to bounce it
The top and bottom of the screen to record an out
The paddles to bounce it
Nodes
ball
left wall
right wall
bottom
top
top paddle
bottom paddle
player 1 score
player 2 score
Categories (this is just an example -- it's not the only way)
CategoryNone: 0
CategoryGamePiece: 1
CategoryOutDetector: 2
CategoryNonInteractive: 4 (use for the score nodes)
The ball bounces off the paddles and side walls. It also interacts with invisible out detectors on the top and bottom. It is in two categories (using |)
ball.categoryBitMask = CategoryGamePiece | CategoryOutDetector
ball.collisionBitMask = CategoryGamePiece
ball.contactTestBitMask = CategoryOutDetector
The paddle and side wall only interact with the ball as a game piece (colliding)
paddleTop.categoryBitMask = CategoryGamePiece
paddleTop.collisionBitMask = CategoryGamePiece
paddleTop.contactTestBitMask = CategoryNone
leftWall.categoryBitMask = CategoryGamePiece
leftWall.collisionBitMask = CategoryGamePiece
leftWall.contactTestBitMask = CategoryNone
The bottom only interacts with the ball as a contact test (doesn't change its motion)
bottomOut.categoryBitMask = CategoryOutDetector
bottomOut.collisionBitMask = CategoryNone
bottomOut.contactTestBitMask = CategoryOutDetector
import SpriteKit
import GameplayKit
class GameScene: SKScene, SKPhysicsContactDelegate {
let balls = [
SKSpriteNode(imageNamed: "blueball.png"),
SKSpriteNode(imageNamed: "greenball.png"),
SKSpriteNode(imageNamed: "realredball.png"),
]
let redRectangle = SKSpriteNode(imageNamed: "redrectangle.png")
let blueRectangle = SKSpriteNode(imageNamed: "bluerectangle.png")
let greenRectangle = SKSpriteNode(imageNamed: "greenrectangle.png")
let wall1 = SKSpriteNode(imageNamed: "drop_wall.png")
let wall2 = SKSpriteNode(imageNamed: "drop_wall.png")
let bottom = SKSpriteNode(imageNamed:"drop_bottom.png")
let top = SKSpriteNode(imageNamed:"drop_bottom.png")
let blueBallCategory :UInt32 = 0x1 << 0
let greenBallCategory :UInt32 = 0x1 << 1
let realRedBallCategory :UInt32 = 0x1 << 2
let redRectangleCategory : UInt32 = 0x1 << 3
let blueRectangleCategory : UInt32 = 0x1 << 4
let greenRectangleCategory : UInt32 = 0x1 << 5
override func didMove(to view: SKView) {
spawnBalls()
spawnRectangles()
physicsWorld.contactDelegate = self
moveRectangles()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for ball in balls{
ball.isUserInteractionEnabled = false
}
physics()
}
func didBegin(_ contact: SKPhysicsContact) {
let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
switch contactMask {
case blueBallCategory | blueRectangleCategory:
for ball in balls{
ball.removeFromParent()
}
print("Alive! Blue ball has hit blue rectangle.")
case greenBallCategory | greenRectangleCategory:
print("Alive! Green ball has hit green rectangle.")
case realRedBallCategory | redRectangleCategory:
print("Alive! Red ball has hit red rectangle.")
case blueBallCategory | redRectangleCategory:
print("dead")
case blueBallCategory | greenRectangleCategory:
print("dead")
case realRedBallCategory | blueRectangleCategory:
print("dead")
case realRedBallCategory | greenRectangleCategory:
print("dead")
case greenBallCategory | redRectangleCategory:
print("dead")
case greenBallCategory | blueRectangleCategory:
print("dead")
default:
print("missed")
}
}
func spawnRectangles() {
redRectangle.position = CGPoint(x: 0, y: -400)
redRectangle.size = CGSize(width: 200, height: 20)
blueRectangle.position = CGPoint(x: -300, y: -200)
blueRectangle.size = CGSize(width: 200, height: 20)
greenRectangle.position = CGPoint(x: 100, y: -550)
greenRectangle.size = CGSize(width: 200, height: 20)
self.addChild(redRectangle)
self.addChild(blueRectangle)
self.addChild(greenRectangle)
wall1.position = CGPoint(x: -367.04, y: 0)
wall1.size = CGSize(width: 20, height: 1350)
wall1.physicsBody = SKPhysicsBody(rectangleOf: wall1.size)
wall1.physicsBody?.isDynamic = false
wall1.physicsBody?.affectedByGravity = false
self.addChild(wall1)
wall2.position = CGPoint(x: 367.04, y: 0)
wall2.size = CGSize(width: 20, height: 1350)
wall2.physicsBody = SKPhysicsBody(rectangleOf: wall2.size)
wall2.physicsBody?.isDynamic = false
wall2.physicsBody?.affectedByGravity = false
self.addChild(wall2)
top.position = CGPoint(x: 0, y: 657)
top.size = CGSize(width: 765, height: 20)
top.physicsBody = SKPhysicsBody(rectangleOf: top.size)
top.physicsBody?.isDynamic = false
top.physicsBody?.affectedByGravity = false
self.addChild(top)
bottom.size = CGSize(width: 765, height: 20)
bottom.position = CGPoint(x: 0, y: -657)
bottom.physicsBody = SKPhysicsBody(rectangleOf: bottom.size)
bottom.physicsBody?.isDynamic = false
bottom.physicsBody?.affectedByGravity = false
self.addChild(bottom)
}
func physics(){
for ball in balls{
ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.size.height/2)
ball.physicsBody?.contactTestBitMask = blueRectangleCategory | greenRectangleCategory | redRectangleCategory
}
redRectangle.physicsBody = SKPhysicsBody(rectangleOf: redRectangle.size)
redRectangle.physicsBody?.affectedByGravity = false
redRectangle.physicsBody?.isDynamic = false
blueRectangle.physicsBody = SKPhysicsBody(rectangleOf: redRectangle.size)
blueRectangle.physicsBody?.affectedByGravity = false
blueRectangle.physicsBody?.isDynamic = false
greenRectangle.physicsBody = SKPhysicsBody(rectangleOf: redRectangle.size)
greenRectangle.physicsBody?.isDynamic = false
greenRectangle.physicsBody?.affectedByGravity = false
balls[0].physicsBody?.categoryBitMask = blueBallCategory
balls[1].physicsBody?.categoryBitMask = greenBallCategory
balls[2].physicsBody?.categoryBitMask = realRedBallCategory
redRectangle.physicsBody?.categoryBitMask = redRectangleCategory
blueRectangle.physicsBody?.categoryBitMask = blueRectangleCategory
greenRectangle.physicsBody?.categoryBitMask = greenRectangleCategory
}
func moveRectangles(){
let redMoveRight = SKAction.moveTo(x: 300, duration: 2)
let redMoveLeft = SKAction.moveTo(x: -280, duration: 2)
let redWholeMovement = SKAction.repeatForever(SKAction.sequence([redMoveRight,redMoveLeft]))
redRectangle.run(redWholeMovement)
let blueMoveRight = SKAction.moveTo(x: 300, duration: 2)
let blueMoveLeft = SKAction.moveTo(x: -280, duration: 1.5)
let blueWholeMovement = SKAction.repeatForever(SKAction.sequence([blueMoveRight,blueMoveLeft]))
blueRectangle.run(blueWholeMovement)
let greenMoveRight = SKAction.moveTo(x: 300, duration: 2)
let greenMoveLeft = SKAction.moveTo(x: -280, duration: 1.5)
let greenWholeMovement = SKAction.repeatForever(SKAction.sequence([greenMoveLeft,greenMoveRight]))
greenRectangle.run(greenWholeMovement)
}
func spawnBalls(){
let ball = balls[Int(arc4random_uniform(UInt32(balls.count)))]
ball.position = CGPoint(x: 0, y: 250)
ball.size = CGSize(width: 70, height: 70)
self.addChild(ball)
}
}
I want a new ball to spawn at the top of the screen if a ball hits a same colored rectangle. Right now when I run the app a randomly colored ball is spawned at the top and when the user clicks the ball drops. If the ball makes contact with a moving rectangle of the same color of the ball the game is supposed to keep going. But it just ends after. If anyone could help that would be great.Thanks!
You only spawn one ball in spawnBalls(). So when you touch only one ball appears. You should try to move spawnBalls() to touchesBegan().
I'm working on a game, and I'm using spritekit and Swift.
This is my code:
import SpriteKit
struct collision {
static let arrow:UInt32 = 0x1 << 1
static let runner:UInt32 = 0x1 << 2
static let target:UInt32 = 0x1 << 3
static let targetCenter:UInt32 = 0x1 << 4
}
class GameScene: SKScene, SKPhysicsContactDelegate {
var person = SKSpriteNode()
var box = SKSpriteNode()
var screenSize:CGSize!
var gameScreenSize:CGSize!
var gameStarted:Bool = false
var moveAndRemove = SKAction()
var boxVelocity:NSTimeInterval = 5.5
override func didMoveToView(view: SKView) {
self.physicsWorld.gravity = CGVectorMake(0, -1.0)
self.physicsWorld.contactDelegate = self
screenSize = self.frame.size
gameScreenSize = view.frame.size
createPerson()
}
func createPerson() -> Void {
person.texture = SKTexture(imageNamed:"person")
person.setScale(1.0)
person.size = CGSize(width: 80, height: 80)
person.position = CGPoint(x: screenSize.width / 2, y: 150)
person.physicsBody = SKPhysicsBody(rectangleOfSize: person.size)
person.physicsBody?.affectedByGravity = false
person.physicsBody?.dynamic = false
self.addChild(person)
}
func createTarget() -> Void {
box = SKSpriteNode()
box.size = CGSize(width: 70, height: 100)
box.setScale(1.0)
box.position = CGPoint(x: (screenSize.width / 3) * 2, y: screenSize.height + box.size.height)
box.texture = SKTexture(imageNamed: "box")
box.physicsBody? = SKPhysicsBody(rectangleOfSize: box.size)
box.physicsBody?.categoryBitMask = collision.target
box.physicsBody?.collisionBitMask = collision.arrow
box.physicsBody?.contactTestBitMask = collision.targetCenter
box.physicsBody?.affectedByGravity = false
box.physicsBody?.dynamic = true
box.physicsBody?.usesPreciseCollisionDetection = true
self.addChild(box)
let distance = CGFloat(self.frame.height - box.frame.height)
let moveTargets = SKAction.moveToY(-distance, duration: boxVelocity)
let removeTargets = SKAction.removeFromParent()
moveAndRemove = SKAction.sequence([moveTargets,removeTargets])
box.runAction(moveAndRemove)
}
func createBall() ->Void {
let ball = SKSpriteNode()
ball.size = CGSize(width: 20, height: 22)
ball.zPosition = 5
let moveToXY = CGPoint(x: self.size.width, y: self.size.height)
ball.texture = SKTexture(imageNamed: "ball")
ball.position = CGPointMake(person.position.x + ball.size.width, person.position.y + ball.size.height)
ball.physicsBody? = SKPhysicsBody(rectangleOfSize: ball.size)
ball.physicsBody?.categoryBitMask = collision.arrow
ball.physicsBody?.collisionBitMask = collision.target
ball.physicsBody?.affectedByGravity = false
ball.physicsBody?.dynamic = true
ball.physicsBody?.usesPreciseCollisionDetection = true
let action = SKAction.moveTo(moveToXY, duration: 1.5)
let delay = SKAction.waitForDuration(1.5)
ball.runAction(SKAction.sequence([action,delay]))
self.addChild(ball)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
if gameStarted == false {
gameStarted = true
let spawn = SKAction.runBlock { () in
self.createTarget()
}
let delay = SKAction.waitForDuration(1.5)
let spawnDelay = SKAction.sequence([spawn, delay])
let spanDelayForever = SKAction.repeatActionForever(spawnDelay)
self.runAction(spanDelayForever)
} else {
createBall()
boxVelocity -= 0.1
}
}
func didBeginContact(contact: SKPhysicsContact) {
print("Detect")
}
func didEndContact(contact: SKPhysicsContact) {
print("end detect")
}
override func update(currentTime: CFTimeInterval) {
}
}
But when I run the game, the collision between objects does not. I'm trying to solve a while, but found nothing. Can someone help me?
Project files.
Try with these to modifications:
box.physicsBody = SKPhysicsBody(rectangleOfSize: box.size)
box.physicsBody?.categoryBitMask = collision.target
box.physicsBody?.collisionBitMask = collision.arrow
box.physicsBody?.contactTestBitMask = collision.targetCenter
and
ball.physicsBody = SKPhysicsBody(rectangleOfSize: ball.size)
ball.physicsBody?.categoryBitMask = collision.arrow
ball.physicsBody?.collisionBitMask = collision.target
ball.physicsBody?.contactTestBitMask = collision.target
Note the absence of "?" while you init the physicsBody and the new contactTestBitMask