5

I have a moving platform, but when the node is above the platform it doesnt move with the platform horizontally

In this article, the problem is explained: Moving Platform Hell

http://www.learn-cocos2d.com/2013/08/physics-engine-platformer-terrible-idea/

And in comment there is solutions for Box2D: Kinematic body

But what about SpriteKit ?

Update

I'm moving the platform using

let moveHPart1 = SKAction.moveByX(origW, y: 0, duration: moveDuration);
let moveHPart2 = SKAction.moveByX(-origW, y: 0, duration: moveDuration);

platform(SKAction.repeatActionForever(SKAction.sequence([moveHPart1, moveHPart2])));
grape1
  • 759
  • 2
  • 8
  • 19
  • @grape1 First of all you should update your question with relevant code. If you are moving a platform by using actions, then you are pulling it out of physics simulation. So, to move a body and platform together, you should apply force to the platform and set some properties like friction for both platform and a body... – Whirlwind Jul 23 '15 at 02:27
  • @Whirlwind I'm using SKAction.moveByX, but how to apply force to go left and to go back? – grape1 Jul 23 '15 at 04:37
  • 1
    Not sure but see if this helps at all http://stackoverflow.com/q/28030259/2158465 – Epic Byte Jul 23 '15 at 04:50
  • Yes that is a good solution, but since the platform move with SKAction its velocity is 0. Any ideas how to move to given x position? – grape1 Jul 23 '15 at 09:02
  • @EpicByte what technique you are using to keep moving platforms y position unchanged (after certain collisions) ? I guess your platforms are dynamic and not affected by gravity,right ? Or, I am wrong and you are using platforms with static physics bodies, and the only thing which is changing is player's velocity when successful landing on platform is detected? – Whirlwind Jul 23 '15 at 17:54
  • @Whirlwind I simply always manually set the pos/vel of the platform so that impulses applied from collisions, gravity etc are ignored. If you are having a problem with the platform moving you can try setting the pos in didFinishUpdate right before the scene is rendered. I don't use static because as you mentioned you can't set the velocity. However you could probably get it to work by using a custom velocity property but then the collisions between players and platforms would not take into account the custom velocity property. – Epic Byte Jul 23 '15 at 18:58
  • @Whirlwind I added an answer showing part of what I'm talking about here http://stackoverflow.com/a/31594249/2158465 – Epic Byte Jul 23 '15 at 19:04
  • @EpicByte Thanks, those are nice answer and cool solutions for all this. – Whirlwind Jul 23 '15 at 19:11

1 Answers1

9

Personally I am against of using physics for moving platforms because moving platform physics body has to be dynamic.

Static platforms

For static platforms setting physics body dynamic property to false is perfect solution. And this is how it is meant to be. Static bodies are not affected by forces but still give you a collision response. So, the problem is solved.

But you can't change position of static physics bodies by using forces. You can do this by using actions or manually setting its position. But, then you are removing a platform out of physics simulation.

In order to do all with physics, you have to keep the platform dynamic. But this can lead in other problems. For example when player lands on platform, he will push the platform down, because player has a mass. Even if platform has big mass it will go down as time passing. Remember, we cant just update platforms x position manually, because this can make a mess with physics simulation.

"Moving platform hell" as stated in that nice article of LearnCocos2d is probably the best description what can happen when using physics for this task :-)

Moving platform example

To show you some possibilities I made an simple example on how you can move a platform with applying a force to it, and make a character to stay on it.There are few things I've done in order to make this to work:

  • Changed a mass of platform. This will prevent platform from moving when player bumps in it from below.

  • Made an edge based physics body to prevent platform falling when player lands on it.

  • Played with properties like allows rotation and friction to get desired effect.

Here is the code :

import SpriteKit


class GameScene: SKScene,SKPhysicsContactDelegate
{
    let BodyCategory   : UInt32 = 0x1 << 1
    let PlatformCategory    : UInt32 = 0x1 << 2
    let WallCategory        : UInt32 = 0x1 << 3
    let EdgeCategory        : UInt32 = 0x1 << 4 // This will prevent a platforom from falling down
    let PlayerCategory       : UInt32 = 0x1 << 5

    let platformSpeed: CGFloat = 40.0

    let body = SKShapeNode(circleOfRadius: 20.0)

    let player = SKShapeNode(circleOfRadius: 20.0)

    let platform = SKSpriteNode(color: SKColor.greenColor(), size: CGSize(width:100, height:20))

    let notDynamicPlatform =  SKSpriteNode(color: SKColor.greenColor(), size: CGSize(width:100, height:20))

    override func didMoveToView(view: SKView)
    {
        //Setup contact delegate so we can use didBeginContact and didEndContact methods
        physicsWorld.contactDelegate = self

        //Setup borders
        self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
        self.physicsBody?.categoryBitMask = WallCategory
        self.physicsBody?.collisionBitMask = BodyCategory | PlayerCategory


        //Setup some physics body object
        body.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
        body.fillColor = SKColor.greenColor()
        body.physicsBody = SKPhysicsBody(circleOfRadius: 20)
        body.physicsBody?.categoryBitMask = BodyCategory
        body.physicsBody?.contactTestBitMask = PlatformCategory
        body.physicsBody?.collisionBitMask = PlatformCategory | WallCategory
        body.physicsBody?.allowsRotation = false

        body.physicsBody?.dynamic = true
        self.addChild(body)

        //Setup player
        player.position = CGPoint(x: CGRectGetMidX(self.frame), y:30)
        player.fillColor = SKColor.greenColor()
        player.physicsBody = SKPhysicsBody(circleOfRadius: 20)
        player.physicsBody?.categoryBitMask = PlayerCategory
        player.physicsBody?.contactTestBitMask = PlatformCategory
        player.physicsBody?.collisionBitMask = PlatformCategory | WallCategory | BodyCategory
        player.physicsBody?.allowsRotation = false
        player.physicsBody?.friction = 1
        player.physicsBody?.dynamic = true

        self.addChild(player)




        //Setup platform

        platform.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame) - 100)
        platform.physicsBody = SKPhysicsBody(rectangleOfSize: platform.size)
        platform.physicsBody?.categoryBitMask = PlatformCategory
        platform.physicsBody?.contactTestBitMask = BodyCategory
        platform.physicsBody?.collisionBitMask = BodyCategory | EdgeCategory | PlayerCategory
        platform.physicsBody?.allowsRotation = false
        platform.physicsBody?.affectedByGravity = false
        platform.physicsBody?.dynamic = true
        platform.physicsBody?.friction = 1.0
        platform.physicsBody?.restitution = 0.0
        platform.physicsBody?.mass = 20


        //Setup edge

        let edge = SKNode()

        edge.physicsBody = SKPhysicsBody(edgeFromPoint: CGPoint(x: 0, y:-platform.size.height/2), toPoint: CGPoint(x: self.frame.size.width, y:-platform.size.height/2))

        edge.position = CGPoint(x:0, y: CGRectGetMidY(self.frame) - 100)
        edge.physicsBody?.categoryBitMask = EdgeCategory
        edge.physicsBody?.collisionBitMask = PlatformCategory

        self.addChild(edge)

        self.addChild(platform)

    }


    override func update(currentTime: NSTimeInterval) {



        if(platform.position.x <= platform.size.width/2.0 + 20.0 && platform.physicsBody?.velocity.dx < 0.0 ){

            platform.physicsBody?.velocity = CGVectorMake(platformSpeed, 0.0)


        }else if((platform.position.x >= self.frame.size.width - platform.size.width/2.0 - 20.0) && platform.physicsBody?.velocity.dx >= 0.0){

            platform.physicsBody?.velocity = CGVectorMake(-platformSpeed, 0.0)

        }else if(platform.physicsBody?.velocity.dx > 0.0){
             platform.physicsBody?.velocity = CGVectorMake(platformSpeed, 0.0)

        }else{
            platform.physicsBody?.velocity = CGVectorMake(-platformSpeed, 0.0)

        }


    }

    override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {

        let touch: AnyObject? = touches.anyObject()

        let location = touch?.locationInNode(self)


        if(location?.x > 187.5){

            player.physicsBody?.applyImpulse(CGVector(dx: 3, dy: 50))

        }else{
            player.physicsBody?.applyImpulse(CGVector(dx: -3, dy: 50))
        }
    }

}

Here is the result :

enter image description here

Whirlwind
  • 14,286
  • 11
  • 68
  • 157
  • interesting solution, good job! But if the platform is moving vertically won't work, in demo of a spritekit course about platform games when the platform is moving down and the hero is above, there is small bounciness... and the course is very expensive! I make the hero stick to the platform using SKPhysicsJointFixed.jointWithBodyA, but I don't know to delete that joint, when the hero will move right or left – grape1 Jul 23 '15 at 18:42
  • try it if you want, put it inside didBeginContact: let contactPoint: CGPoint = contact.contactPoint let joint = SKPhysicsJointFixed.jointWithBodyA(contact.bodyA, bodyB:contact.bodyB, anchor:CGPointMake(contactPoint.x, contactPoint.y)) self.physicsWorld.addJoint(joint) if you know how to delete later on tap (to move the hero right) let me know – grape1 Jul 23 '15 at 18:48
  • 1
    I think you are missing your contactDelegate method in the code you posted. – Epic Byte Jul 23 '15 at 19:08
  • @EpicByte I am not missing them, because I don't use them in this example ;-) (they are not needed just for collision simulations) – Whirlwind Jul 23 '15 at 19:13
  • @Whirlwind Oh my bad, I saw your scene was conforming to SKPhysicsContactDelegate so I thought maybe you had some code that you forgot to include. – Epic Byte Jul 23 '15 at 19:16
  • @grape1 I guess you can keep a reference to that joint, and then remove it when needed with removeJoint method: https://developer.apple.com/library/ios/documentation/SpriteKit/Reference/SKPhysicsWorld_Ref/index.html#//apple_ref/occ/instm/SKPhysicsWorld/removeJoint: – Whirlwind Jul 23 '15 at 19:17
  • What's interesting is how friction alone is keeping the nodes on the platform. Maybe Sprite Kit improved the friction simulation because when I first started using Sprite Kit the node's would slide even though I had friction set. – Epic Byte Jul 23 '15 at 19:18
  • @EpicByte Yeah you've noticed that one well, SKPhysicsContactDelegate is not needed there, but I left that to make everything ready if OP decide to use didBeginContact or didEndContact methods. – Whirlwind Jul 23 '15 at 19:19