调用SKAction.follow导致SKSpriteNode沿路径时逆时针旋转90度
问题原因
核心问题出在UIKit与SpriteKit的坐标系差异,以及SKAction.follow的orientToPath参数的旋转基准:
- UIBezierPath基于UIKit坐标系(Y轴向下),而SpriteKit使用Y轴向上的坐标系,直接将UIBezierPath的CGPath传入SpriteKit会导致路径的方向、切线角度出现90度偏移。
orientToPath=true时,SpriteKit会将节点的正X轴对齐路径的前进切线方向,但你的火车节点纹理大概率是以正Y轴为前进方向(比如火车头朝上),两者方向基准不匹配,导致节点出现90度旋转偏差。
解决方案
方案1:修正路径坐标系(推荐)
直接在SpriteKit坐标系下创建路径,避免UIKit与SpriteKit的坐标转换问题:
func createTrainPath() { // 确保trackRect使用SpriteKit的场景坐标(Y轴向上) trackRect = CGRect(x: tracksPosX - tracksWidth/2, y: tracksPosY, width: tracksWidth, height: tracksHeight) // 直接用CGPath创建椭圆,而非UIBezierPath trainPathCG = CGPath(ellipseIn: trackRect, transform: nil) } func startFollowTrainPath() { let trainAction = SKAction.follow( trainPathCG, asOffset: false, orientToPath: true, speed: theSpeed) myTrain.run(SKAction.repeatForever(trainAction), withKey: runTrainKey) }
如果必须使用UIBezierPath,可通过坐标变换适配SpriteKit:
func createTrainPath() { trackRect = CGRect(x: tracksPosX - tracksWidth/2, y: tracksPosY, width: tracksWidth, height: tracksHeight) let uiPath = UIBezierPath(ovalIn: trackRect) // 翻转Y轴,适配SpriteKit坐标系(假设scene是当前GameScene实例) let transform = CGAffineTransform(scaleX: 1, y: -1).translatedBy(x: 0, y: -scene.size.height) trainPathCG = uiPath.cgPath.copy(using: transform)! }
方案2:调整节点旋转基准
如果不想修改路径,可通过调整节点的初始旋转,让纹理的前进方向与orientToPath的X轴基准对齐:
// 在初始化火车节点时设置 myTrain.zRotation = -.pi / 2 // 顺时针旋转90度,将纹理的Y轴前进方向转为节点X轴方向
这样当orientToPath=true时,节点的X轴对齐路径切线,纹理的前进方向自然与路径方向一致,不会出现偏移。
方案3:自定义旋转逻辑(复杂路径适配)
如果后续使用S形等复杂路径,需要更精细的控制,可以禁用orientToPath,手动计算路径切线角度来设置节点旋转:
func startFollowTrainPath() { let followAction = SKAction.follow( trainPathCG, asOffset: false, orientToPath: false, speed: theSpeed) // 自定义旋转动作,实时计算路径切线角度 let rotateAction = SKAction.customAction(withDuration: followAction.duration) { node, elapsedTime in let progress = elapsedTime / followAction.duration guard let path = node.action(forKey: runTrainKey) as? SKAction else { return } // 获取当前路径点的切线角度 let tangentAngle = self.tangentAngle(at: progress, alongPath: self.trainPathCG) // 补偿纹理方向与节点X轴的差异 node.zRotation = tangentAngle - .pi / 2 } // 组合动作同时执行 let combinedAction = SKAction.group([followAction, rotateAction]) myTrain.run(SKAction.repeatForever(combinedAction), withKey: runTrainKey) } // 辅助方法:计算路径指定进度处的切线角度 func tangentAngle(at progress: CGFloat, alongPath path: CGPath) -> CGFloat { let pathLength = path.length let distance = progress * pathLength let point = path.point(at: distance) let nextPoint = path.point(at: min(distance + 1, pathLength)) let dx = nextPoint.x - point.x let dy = nextPoint.y - point.y return atan2(dy, dx) }
内容的提问来源于stack exchange,提问作者user22530416
相关产品推荐
相关产品推荐

