You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SpriteKit中拖拽SKShapeNode如何避免穿越其他节点边界?

拖拽SKShapeNode小球时禁止穿越边界的解决方案

我需要实现拖拽代表玩家的圆形SKShapeNode小球时,不能穿越由复杂UIBezierPath创建的天花板、地板SKShapeNode边界,比如小球拖到天花板时不会穿墙。

游戏场景里,天花板和地板在update(_ currentTime: TimeInterval)中修改x坐标实现动画,已配置好碰撞检测与接触测试,整体运行正常,但两种拖拽方法都有问题:

方法一:直接修改position属性

在touchesMoved中直接更新小球position:

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
  let translation = CGVector(dx: location.x - previousLocation.x, dy: location.y - previousLocation.y)
        var newPosition = ball.position 
        newPosition.x += translation.dx
        newPosition.y += translation.dy
        ball.position = newPosition
}
  • 优点:拖拽流畅,和手指移动同步,慢速拖动时能遵守边界
  • 缺点:快速拖动时小球会直接穿过天花板或地板

方法二:使用applyImpulse施加冲量

通过给小球physicsBody施加冲量实现拖拽:

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
                guard let touch = touches.first, let location = touchLocation else { return }
                let newLocation = touch.location(in: self)
                let scalingFactor: CGFloat = 0.1
                let touchMovement = CGVector(dx: newLocation.x - location.x, dy: newLocation.y - location.y)
                touchForce = CGVector(dx: touchMovement.dx * scalingFactor, dy: touchMovement.dy * scalingFactor)
                touchLocation = newLocation
                ball.physicsBody?.applyImpulse(touchForce!)
}
  • 优点:能遵守边界
  • 缺点:有缓动效果,不符合预期的拖拽体验,也无法准确反映拖拽幅度

我需要兼顾两者优点的解决方案,以下是墙壁和小球的参考代码:

天花板/地板节点代码

class Wall: SKShapeNode {
    init(size: CGSize) {
        super.init()
        let bottomPath = UIBezierPath()
        bottomPath.move(to: CGPoint(x: 0, y: -19.99))
        bottomPath.addLine(to: CGPoint(x: 5723.5, y: -19.99))
        bottomPath.addLine(to: CGPoint(x: 5723.5, y: 35.51))
        bottomPath.addLine(to: CGPoint(x: 4915.5, y: 35.51))
        bottomPath.addCurve(to: CGPoint(x: 4892.87, y: 44.88), controlPoint1: CGPoint(x: 4907.01, y: 35.51), controlPoint2: CGPoint(x: 4898.87, y: 38.88))
       /// 更多路径节点

        bottomPath.close()
        self.path = bottomPath.cgPath
        strokeColor = UIColor.green
        fillColor = UIColor.clear
        lineWidth = 5
        lineCap = .round
        lineJoin = .round
        zPosition = 0
        name = "bottom"
        physicsBody = SKPhysicsBody(edgeLoopFrom: self.path!)
        physicsBody?.isDynamic = false
        physicsBody?.affectedByGravity = false
        physicsBody?.categoryBitMask = 1
        physicsBody?.contactTestBitMask = 2
        physicsBody?.collisionBitMask = 1
        physicsBody?.restitution = 0
        physicsBody?.usesPreciseCollisionDetection = true
        physicsBody?.density = 100
        physicsBody?.mass = 100
    }
}

小球节点代码

class Ball: SKShapeNode {
    convenience init(radius: CGFloat) {
        self.init(circleOfRadius: radius)
        
        fillColor = .red
        zPosition = 0
        physicsBody = SKPhysicsBody(circleOfRadius: radius)
        physicsBody?.affectedByGravity = false
        physicsBody?.categoryBitMask = 2
        physicsBody?.contactTestBitMask = 1
        physicsBody?.collisionBitMask = 1
        physicsBody?.restitution = 1
        physicsBody?.usesPreciseCollisionDetection = true
    }
}

游戏核心玩法是拖拽小球沿路径移动,统计耗时和碰壁次数,求合适的拖拽方案兼顾流畅性与边界约束。


内容的提问来源于stack exchange,提问作者user499846

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 10:04:54