0

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

    }
}
Nimbahus
  • 477
  • 3
  • 16
  • This code doesn't make sense because after few created platforms ever next platform will be off-screen. Next thing is that SpawnPlatforms method is problematic, because you don't create platform node locally (or you accidentally missed that part of code). About the actual question...To detect contacts between platforms and a player you have to conform to SKPhysicsContactDelegate protocol as well as to set physicsWorld.contactDelegate property to be your scene. After that, you have to implement didBeginContact method. If you need a code example let me know. – Whirlwind Nov 11 '15 at 20:58
  • SpawnPlatforms will create nodes offscreen, because they will all move on the y axis downwards(camera is set). I know how to detect a collision between 2 object, the only problem lies in that I create 1000 nodes of SKSpriteNode, and I cant for e.g. change the color of the platform that collide with the player, and not all of them. – Nimbahus Nov 11 '15 at 22:22
  • Yeah but still its not a performant solution... Because off-screen nodes are still in a node tree and take up resources... Ideally you should spawn one platform above the top of the screen, then move all platforms downwards, then remove first platform from the bottom. You can change the color of a platform. You do that in didBeginContact method... – Whirlwind Nov 11 '15 at 22:30
  • You get access to the platform when the collision occurs. Why do you need to access them all at once? If you wanted to access them all at once for some reason, you could give them all the name "Platform" and enumerate children with the name "Platform". I think you're overthinking this. Just get the platform from the `SKPhysicsContact` you get in `didBeginContact`. – Ben Kane Nov 11 '15 at 23:10

1 Answers1

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.

Community
  • 1
  • 1
Whirlwind
  • 14,286
  • 11
  • 68
  • 157
  • First of all, sorry for the late reply. Secondly, now I can change the color of the platform that collided with the player to green, instead of changing the color from all platforms. But how can I achieve that the Player only collides with the platforms if it is above it? I edited my Code. – Nimbahus Nov 13 '15 at 20:32
  • @Albert You mean you want to change a color of a platform after player has landed on the surface of the platform ? – Whirlwind Nov 13 '15 at 20:35
  • No, I got everything working with the color. I want to have the Player & Platform intersection only when the Player is above the platform, so if the Player jumps from the bottom to the 1. platform(through it) it only collides after he passed it. – Nimbahus Nov 13 '15 at 20:43
  • @Albert Isn't that solved here already : http://stackoverflow.com/a/31454044/3402095 After player jumps from the first platform, he will go through the second platform and stay on it after landing... – Whirlwind Nov 13 '15 at 20:54
  • No, because this code doesn't work actually, after about 3-4 platforms the player doesn't collide anymore with anything, sometimes from the beginning. – Nimbahus Nov 13 '15 at 23:33
  • @Albert "this code doesn't work" Do you mean your code from this post doesn't work, or the code I wrote in that link doesn't work ? Because I just tried the code (from the link I've posted), added moving platforms and it works no mater how long I generate them. – Whirlwind Nov 13 '15 at 23:57
  • @Albert If code from the link doesn't work 100% correct, it's because is not tested as pointed there and the whole point of that answer was to show you how to make jumps based on press duration. But at the moment, I can't produce what you are saying... The code may have some flaws but it's pointed that you have to implement and test many things by yourself :) You should try to localize the problem, isolate the responsible code and ask a different question because it's a whole another topic in compare to original question. – Whirlwind Nov 14 '15 at 00:12
  • You are right, thanks for taking your time to help me! – Nimbahus Nov 14 '15 at 00:16