【问题标题】:Moving Node on top of a moving platform在移动平台上移动节点
【发布时间】:2025-12-14 01:25:01
【问题描述】:

我有一个移动平台,但是当节点在平台上方时,它不会随着平台水平移动

在这篇文章中,问题解释:移动平台地狱

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

在评论中有 Box2D 的解决方案:Kinematic body

但是 SpriteKit 呢?

更新

我正在使用移动平台

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 首先,您应该使用相关代码更新您的问题。如果您通过使用动作来移动平台,那么您就是将其从物理模拟中拉出来。因此,要同时移动物体和平台,您应该对平台施加力并为平台和物体设置一些属性,例如摩擦力......
  • @Whirlwind 我正在使用 SKAction.moveByX,但是如何施加力向左和返回?
  • 不确定但看看这是否有帮助*.com/q/28030259/2158465
  • 是的,这是一个很好的解决方案,但是由于平台使用 SKAction 移动,它的速度为 0。任何想法如何移动到给定的 x 位置?
  • @EpicByte 您使用什么技术来保持移动平台的 y 位置不变(在某些碰撞之后)?我猜你的平台是动态的,不受重力影响,对吧?或者,我错了,您使用的是具有静态物理体的平台,唯一改变的是检测到成功登陆平台时玩家的速度?

标签: swift sprite-kit physics


【解决方案1】:

我个人反对将物理用于移动平台,因为移动平台物理主体必须是动态的。

静态平台

对于静态平台,将物理体 dynamic 属性设置为 false 是完美的解决方案。这就是它的本意。静态物体不受力的影响,但仍会为您提供碰撞响应。这样,问题就解决了。

但是你不能通过力来改变静态物理物体的位置。您可以通过使用操作或手动设置其位置来做到这一点。但是,您正在从物理模拟中移除一个平台。

为了使用物理,您必须保持平台动态。但这可能会导致其他问题。例如,当玩家降落在平台上时,他会将平台向下推,因为玩家有质量。 即使平台质量很大,它也会随着时间的推移而下降。请记住,我们不能只手动更新平台 x 位置,因为这会干扰物理模拟。

LearnCocos2d 的 article 中提到的“移动平台地狱”可能是最好的描述当使用物理来完成这项任务时会发生什么:-)

移动平台示例

为了向您展示一些可能性,我做了一个简单的示例,说明如何通过对平台施加力来移动平台,并使角色停留在平台上。我做了一些事情来做到这一点工作:

  • 更改了大量平台。这将防止平台在玩家从下方撞到平台时移动。

  • 制作了一个基于边缘的物理主体,以防止玩家降落时平台掉落。

  • 使用允许旋转和摩擦等属性来获得所需的效果。

代码如下:

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))
        }
    }

}

结果如下:

【讨论】:

  • 有趣的解决方案,干得好!但是如果平台垂直移动就不行了,在一个平台游戏的spritekit课程的demo中,当平台向下移动而英雄在上面的时候,弹力很小……而且课程很贵!我使用 SKPhysicsJointFixed.jointWithBodyA 让英雄粘在平台上,但我不知道要删除那个关节,当英雄向右或向左移动时
  • 如果你想试试,把它放在里面 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) 如果你知道如何稍后点击删除(将英雄向右移动),请告诉我
  • 我认为您在发布的代码中缺少您的 contactDelegate 方法。
  • @EpicByte 我没有错过它们,因为我在这个例子中没有使用它们 ;-)(它们不仅仅用于碰撞模拟)
  • @Whirlwind 哦,我的错,我看到你的场景符合 SKPhysicsContactDelegate 所以我想也许你有一些你忘记包含的代码。