SpriteKit中SKSpriteNode匀速移动与特定节点检测实现方法
SpriteKit 两个常见问题解决方案
问题1:实现SKSpriteNode以恒定速度沿固定方向移动
你当前通过physicsBody.velocity设置初始速度后节点速度衰减,是因为缺少物理体阻尼、摩擦相关属性的配置,物理系统默认参数会对运动中的物体产生减速效果。
物理方案修正(需要物理碰撞特性时使用)
在原有代码基础上补充物理属性配置即可保证速度恒定:
let item = SKSpriteNode(color: .black, size: CGSize(width: 32, height: 16)) item.position = CGPoint(x: 600, y: self.heights[random]) item.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width:32, height: 16)) item.physicsBody?.affectedByGravity = false // 新增以下配置 item.physicsBody?.linearDamping = 0 // 关闭线性运动阻尼,默认值0.1会持续降低移动速度 item.physicsBody?.friction = 0 // 关闭接触摩擦,避免和其他物体接触时减速 item.physicsBody?.angularDamping = 0 // 关闭旋转阻尼,避免意外旋转影响移动方向 item.physicsBody?.restitution = 0 // 关闭碰撞反弹,避免碰撞后速度变化 item.physicsBody?.collisionBitMask = 0 // 不需要碰撞响应时直接设为空,从根源避免外力干扰速度 item.physicsBody?.velocity = CGVector(dx: -360, dy: 0) addChild(item)
无物理方案(不需要碰撞特性时优先使用)
如果节点不需要参与物理碰撞,直接用SKAction实现匀速移动性能更好,完全不会被物理系统干扰:
let item = SKSpriteNode(color: .black, size: CGSize(width: 32, height: 16)) item.position = CGPoint(x: 600, y: self.heights[random]) addChild(item) // 按速度计算移动时长:移动总距离/目标速度 = 时长,保证速度恒定为360pt/s let totalMoveDistance: CGFloat = 632 // 从x=600移动到x=-32(完全移出左屏幕)的总距离 let moveDuration = totalMoveDistance / 360 let moveAction = SKAction.moveBy(x: -totalMoveDistance, y: 0, duration: moveDuration) // 移动完成后自动移除节点,无需额外在update中清理 let removeAction = SKAction.removeFromParent() item.run(SKAction.sequence([moveAction, removeAction]))
问题2:在update()方法中获取符合条件的动态创建节点
最通用的实现方式是给动态创建的同类节点设置统一标识,在update中遍历筛选即可,不需要额外维护引用数组。
实现步骤
- 创建节点时给节点设置专属
name作为标识:
// 放在addChild之前即可 item.name = "movingBlock"
- 在
update方法中通过name遍历对应节点,判断条件后执行操作:
override func update(_ currentTime: TimeInterval) { // 遍历所有name为movingBlock的子节点 enumerateChildNodes(withName: "movingBlock") { node, _ in guard let targetNode = node as? SKSpriteNode else { return } // 写入自定义判断条件,示例为x坐标小于0时移除节点 if targetNode.position.x <= 0 { targetNode.removeFromParent() // 其他修改操作直接操作targetNode实例即可,比如修改颜色、调整速度 } } }
可选优化方案
如果需要高频操作大量同类型节点,可以单独维护一个数组存储动态节点的引用,创建节点时将节点加入数组,节点移除时同步从数组中删除,遍历数组判断条件即可,相比遍历所有子节点效率更高。注意数组中不要保留已经被移除出节点树的实例,避免内存泄漏。
如果使用前文提到的SKAction移动方案,不需要在update中编写检测逻辑,直接通过Action序列配置到达位置后的操作即可,能省掉每帧遍历的性能开销。
内容的提问来源于stack exchange,提问作者Joseph
相关产品推荐
相关产品推荐

