问题
Let's say we add the same node around 1000 times like shown in the code below. Is there any way to have access of every single platform?
For e.g., if there is a collision between the PlatformCategory
and the PlayerCategory
, the Platform that had the collision should change its color to green and run a SKAction
.
Edit: Thanks to Whirlwind, I can get the platform that collides with the player change to green, while all other stay red. Now I need to set the collision properly, which means I only want the Player
to collide with Platform
when it is above it.
For e.g., the Player jumps from the 1. platform to the 2. platform. While jumping, it should not collide with the 2nd platform, only after it passed it.
class GameScene: SKScene, SKPhysicsContactDelegate {
let PlayerCategory : UInt32 = 0x1 << 1
let PlatformCategory : UInt32 = 0x1 << 2
var Platform: SKSpriteNode!
var Player: SKSpriteNode!
override func didMoveToView(view: SKView) {
/* Setup your scene here */
self.physicsWorld.contactDelegate = self
self.physicsBody?.velocity = CGVectorMake(0, 0)
.....
SpawnPlayer()
generatePlatforms()
}
func SpawnPlatforms(position: CGPoint)->SKSpriteNode{
Platform = SKSpriteNode(color: SKColor.redColor(), size: CGSize(width: self.frame.size.width, height: 25))
Platform.position = position
Platform.zPosition = 1
Platform.physicsBody = SKPhysicsBody(rectangleOfSize: CGSize(width: self.frame.size.width, height: 25))
Platform.physicsBody?.dynamic = false
Platform.physicsBody?.restitution = 0.0
Platform.physicsBody?.allowsRotation = false
Platform.physicsBody?.usesPreciseCollisionDetection = true
Platform.physicsBody?.categoryBitMask = PlatformCategory
Platform.physicsBody?.contactTestBitMask = PlayerCategory
Platform.physicsBody?.collisionBitMask = PlayerCategory
return Platform
}
func generatePlatforms(){
for i in 1...1000{
let position = CGPoint(x: self.frame.size.width / 2, y: 140 * CGFloat(i))
let platform = SpawnPlatforms(position)
self.addChild(platform)
}
}
func didBeginContact(contact: SKPhysicsContact) {
let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
let platform = contact.bodyB.node as! SKSpriteNode
switch(contactMask) {
case PlayerCategory | PlatformCategory:
//either the contactMask was the bro type or the ground type
print("Contact Made0")
if(Player.position.y >= platform.position.y && Player.position.y <= platform.position.y + Player.size.height){
Player.physicsBody?.collisionBitMask = PlatformCategory
platform.color = SKColor.greenColor()
}
default:
return
}
}
func didEndContact(contact: SKPhysicsContact) {
let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
switch(contactMask) {
case PlayerCategory | PlatformCategory:
//either the contactMask was the bro type or the ground type
print("Contact Made0")
default:
return
}
}
回答1:
As I said, to detect contact in order to change the platform's color you have to do few things:
- Conform to SKPhysicsDelegate protocol
- Set physicsWorld's contactDelegate property appropriately
- Implement didBeginContact method
Conforming to SKPhysicsDelegate protocol
The syntax for this task is easy:
class GameScene: SKScene,SKPhysicsContactDelegate
{//your GameScene properties and methods go here...}
Conforming to a protocol means that class has to implement required methods from that protocol. Methods specific for SKPhysicsDelegate protocol are didBeginContact and didEndContact. If you look into header file you will see this:
protocol SKPhysicsContactDelegate : NSObjectProtocol {
optional func didBeginContact(contact: SKPhysicsContact)
optional func didEndContact(contact: SKPhysicsContact)
}
Even if those methods are optional, we need them in order to get contact object which describes a contact.
Setting physicsWorld contact delegate property
The only code needed for this is:
self.physicsWorld.contactDelegate = self //self is a scene (eg. GameScene)
So when a physics contact occurs in a physics world we should be notified somehow. According to the docs, a contactDelegate property is a delegate which is called when that contact occurs. By setting the scene as a contact delegate we are delegating the responsibility of contact handling to it. And this where didBeginContact should be mentioned.
Here is how you should implement it (there are probably few more ways):
func didBeginContact(contact: SKPhysicsContact) {
var firstBody, 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 & CharacterCategory) != 0 &&
(secondBody.categoryBitMask & PlatformCategory != 0)) {
//secondBody would be a platform
}
}
}
Here, you can easily change the color of one platform like this:
let platform = secondBody.node as SKSpriteNode
platform.color = UIColor.redColor()
or you can change a color of all platforms:
self.enumerateChildNodesWithName("platform", usingBlock: {
node, stop in
var platformNode = node as SKSpriteNode
platformNode.color = UIColor.redColor()
})
Also I see you are using the code from this question :
https://stackoverflow.com/a/31454044/3402095
So I've edited it (changed didBeginContact) to show you on that example how to change sprite's color to red.
来源:https://stackoverflow.com/questions/33658734/swift-multiple-node-access