簡單的 Sprite Kit 專案顯示碰撞接觸觸控事件
這是一個簡單的 Sprite-Kit GameScene.swift。建立一個新的空 SpriteKit 專案,並用此替換 GameScene.swift。然後構建並執行。
單擊螢幕上的任何物件以使其移動。檢查日誌和註釋,看看哪些碰撞,哪些碰撞。
//
// GameScene.swift
// bounceTest
//
// Created by Stephen Ives on 05/04/2016.
// Copyright (c) 2016 Stephen Ives. All rights reserved.
//
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
let objectSize = 150
let initialImpulse: UInt32 = 300 // Needs to be proportional to objectSize
//Physics categories
let purpleSquareCategory: UInt32 = 1 << 0
let redCircleCategory: UInt32 = 1 << 1
let blueSquareCategory: UInt32 = 1 << 2
let edgeCategory: UInt32 = 1 << 31
let purpleSquare = SKSpriteNode()
let blueSquare = SKSpriteNode()
let redCircle = SKSpriteNode()
override func didMove(to view: SKView) {
physicsWorld.gravity = CGVector(dx: 0, dy: 0)
//Create an boundary else everything will fly off-screen
let edge = frame.insetBy(dx: 0, dy: 0)
physicsBody = SKPhysicsBody(edgeLoopFrom: edge)
physicsBody?.isDynamic = false //This won't move
name = "Screen_edge"
scene?.backgroundColor = SKColor.black
// Give our 3 objects their attributes
blueSquare.color = SKColor.blue
blueSquare.size = CGSize(width: objectSize, height: objectSize)
blueSquare.name = "shape_blueSquare"
blueSquare.position = CGPoint(x: size.width * -0.25, y: size.height * 0.2)
let circleShape = SKShapeNode(circleOfRadius: CGFloat(objectSize))
circleShape.fillColor = SKColor.red
redCircle.texture = view.texture(from: circleShape)
redCircle.size = CGSize(width: objectSize, height: objectSize)
redCircle.name = "shape_redCircle"
redCircle.position = CGPoint(x: size.width * 0.4, y: size.height * -0.4)
purpleSquare.color = SKColor.purple
purpleSquare.size = CGSize(width: objectSize, height: objectSize)
purpleSquare.name = "shape_purpleSquare"
purpleSquare.position = CGPoint(x: size.width * -0.35, y: size.height * 0.4)
addChild(blueSquare)
addChild(redCircle)
addChild(purpleSquare)
redCircle.physicsBody = SKPhysicsBody(circleOfRadius: redCircle.size.width/2)
blueSquare.physicsBody = SKPhysicsBody(rectangleOf: blueSquare.frame.size)
purpleSquare.physicsBody = SKPhysicsBody(rectangleOf: purpleSquare.frame.size)
setUpCollisions()
checkPhysics()
}
func setUpCollisions() {
//Assign our category bit masks to our physics bodies
purpleSquare.physicsBody?.categoryBitMask = purpleSquareCategory
redCircle.physicsBody?.categoryBitMask = redCircleCategory
blueSquare.physicsBody?.categoryBitMask = blueSquareCategory
physicsBody?.categoryBitMask = edgeCategory // This is the edge for the scene itself
// Set up the collisions. By default, everything collides with everything.
redCircle.physicsBody?.collisionBitMask &= ~purpleSquareCategory // Circle doesn't collide with purple square
purpleSquare.physicsBody?.collisionBitMask = 0 // purpleSquare collides with nothing
// purpleSquare.physicsBody?.collisionBitMask |= (redCircleCategory | blueSquareCategory) // Add collisions with red circle and blue square
purpleSquare.physicsBody?.collisionBitMask = (redCircleCategory) // Add collisions with red circle
blueSquare.physicsBody?.collisionBitMask = (redCircleCategory) // Add collisions with red circle
// Set up the contact notifications. By default, nothing contacts anything.
redCircle.physicsBody?.contactTestBitMask |= purpleSquareCategory // Notify when red circle and purple square contact
blueSquare.physicsBody?.contactTestBitMask |= redCircleCategory // Notify when blue square and red circle contact
// Make sure everything collides with the screen edge and make everything really 'bouncy'
enumerateChildNodes(withName: "//shape*") { node, _ in
node.physicsBody?.collisionBitMask |= self.edgeCategory //Add edgeCategory to the collision bit mask
node.physicsBody?.restitution = 0.9 // Nice and bouncy...
node.physicsBody?.linearDamping = 0.1 // Nice and bouncy...
}
//Lastly, set ourselves as the contact delegate
physicsWorld.contactDelegate = self
}
func didBegin(_ contact: SKPhysicsContact) {
let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
switch contactMask {
case purpleSquareCategory | blueSquareCategory:
print("Purple square and Blue square have touched")
case redCircleCategory | blueSquareCategory:
print("Red circle and Blue square have touched")
case redCircleCategory | purpleSquareCategory:
print("Red circle and purple Square have touched")
default: print("Unknown contact detected")
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let touchedNode = selectNodeForTouch(touch.location(in: self))
if let node = touchedNode {
node.physicsBody?.applyImpulse(CGVector(dx: CGFloat(arc4random_uniform(initialImpulse)) - CGFloat(initialImpulse/2), dy: CGFloat(arc4random_uniform(initialImpulse)) - CGFloat(initialImpulse/2)))
node.physicsBody?.applyTorque(CGFloat(arc4random_uniform(20)) - CGFloat(10))
}
}
}
// Return the sprite where the user touched the screen
func selectNodeForTouch(_ touchLocation: CGPoint) -> SKSpriteNode? {
let touchedNode = self.atPoint(touchLocation)
print("Touched node is \(touchedNode.name)")
// let touchedColor = getPixelColorAtPoint(touchLocation)
// print("Touched colour is \(touchedColor)")
if touchedNode is SKSpriteNode {
return (touchedNode as! SKSpriteNode)
} else {
return nil
}
}
//MARK: - Analyse the collision/contact set up.
func checkPhysics() {
// Create an array of all the nodes with physicsBodies
var physicsNodes = [SKNode]()
//Get all physics bodies
enumerateChildNodes(withName: "//.") { node, _ in
if let _ = node.physicsBody {
physicsNodes.append(node)
} else {
print("\(node.name) does not have a physics body so cannot collide or be involved in contacts.")
}
}
//For each node, check it's category against every other node's collion and contctTest bit mask
for node in physicsNodes {
let category = node.physicsBody!.categoryBitMask
// Identify the node by its category if the name is blank
let name = node.name != nil ? node.name! : "Category \(category)"
let collisionMask = node.physicsBody!.collisionBitMask
let contactMask = node.physicsBody!.contactTestBitMask
// If all bits of the collisonmask set, just say it collides with everything.
if collisionMask == UInt32.max {
print("\(name) collides with everything")
}
for otherNode in physicsNodes {
if (node.physicsBody?.dynamic == false) {
print("This node \(name) is not dynamic")
}
if (node != otherNode) && (node.physicsBody?.isDynamic == true) {
let otherCategory = otherNode.physicsBody!.categoryBitMask
// Identify the node by its category if the name is blank
let otherName = otherNode.name != nil ? otherNode.name! : "Category \(otherCategory)"
// If the collisonmask and category match, they will collide
if ((collisionMask & otherCategory) != 0) && (collisionMask != UInt32.max) {
print("\(name) collides with \(otherName)")
}
// If the contactMAsk and category match, they will contact
if (contactMask & otherCategory) != 0 {print("\(name) notifies when contacting \(otherName)")}
}
}
}
}
}