且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

在移动平台上移动节点

更新时间:2022-10-14 21:12:23

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 :