我遇到了 SceneKit 的问题。
我正在开发一款3D手机游戏。我有一个角色 3D 模型和几个骨骼动画 CAAnimation。我以 *.dae 格式从 Maya 导入模型和动画。 角色的动画一个接一个地连续播放,每个新动画都是随机触发的。 通过设置 fadeInDuration 和 fadeOutDuration 属性,可以使动画之间的过渡变得平滑。这是代码示例:
import UIKit import QuartzCore import SceneKit
class TestAnimationController: UIViewController {
var bodyNode: SCNNode?
override func viewDidLoad() {
super.viewDidLoad()
let scnView = SCNView(frame: self.view.bounds)
scnView.backgroundColor = .black // Set your desired background color
scnView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
let scene = SCNScene(named: "art.scnassets/scene/Base_room/ROOM5.scn")!
bodyNode = collada2SCNNode(filepath: "art.scnassets/female/girl_body_races.dae")!
bodyNode?.renderingOrder = 10
scene.rootNode.addChildNode(bodyNode!)
playIdleAnimation()
scnView.scene = scene // Assign the scene to the SCNView
self.view.addSubview(scnView) // Add the SCNView to your main view)
}
func collada2SCNNode(filepath:String) -> SCNNode? { if let scene = SCNScene(named: filepath) { let node = scene.rootNode.childNodes[0] return node } else { return nil } }
func playIdleAnimation() {
let array = [
"art.scnassets/female/animations/idle/girl_idle_4.dae",
"art.scnassets/female/animations/idle/girl_idle1.dae",
"art.scnassets/female/animations/idle/girl_idle2.dae",
"art.scnassets/female/animations/idle/Girl_idle3.dae",
]
let animation = CAAnimation.animationWithSceneNamed(array.randomElement() ?? "")!
self.setAnimationAdd(
fadeInDuration: 1.0,
fadeOutDuration: 1.0,
keyTime: 0.99,
animation,
isLooped: false
) { [weak self] in
guard let self = self else { return }
try? self.playBoringAnimations()
}
}
func playBoringAnimations() {
let array = [
"art.scnassets/female/animations/boring/girl_boring1.dae",
"art.scnassets/female/animations/boring/girl_boring2.dae",
"art.scnassets/female/animations/boring/girl_boring3.dae",
"art.scnassets/female/animations/boring/girl_boring4.dae",
"art.scnassets/female/animations/boring/girl_boring5.dae",
"art.scnassets/female/animations/boring/girl_boring6.dae",
"art.scnassets/female/animations/boring/girl_boring8.dae"
]
let animation = CAAnimation.animationWithSceneNamed(array.randomElement() ?? "")!
self.setAnimationAdd(
fadeInDuration: 1.0,
fadeOutDuration: 1.0,
keyTime: 0.99,
animation,
isLooped: false
) { [weak self] in
guard let self = self else { return }
try? self.playIdleAnimation()
}
}
func setAnimationAdd(fadeInDuration : CGFloat, fadeOutDuration : CGFloat, keyTime : CGFloat, _ animation: CAAnimation, isLooped: Bool, completion: (() -> Void)?) {
animation.fadeInDuration = fadeInDuration
animation.fadeOutDuration = fadeOutDuration
if !isLooped {
animation.repeatCount = 1
} else {
animation.repeatCount = Float.greatestFiniteMagnitude
}
animation.animationEvents = [
SCNAnimationEvent(keyTime: keyTime, block: { _, _, _ in completion?() })
]
bodyNode?.addAnimation(animation, forKey: "avatarAnimation")
}
}
一切都很完美,直到我更新到 iOS 18。在物理设备上,动画现在突然过渡,没有早期 iOS 版本中出现的平滑混合。它们之间的切换非常明显,就好像 fadeInDuration 和 fadeOutDuration 参数被忽略一样。
但是,在 iOS 18 模拟器中,动画仍然像以前一样平滑过渡。
这里有两个示例视频 - IOS 17.5 和 IOS 18 https://youtube.com/shorts/jzoMRF4skAQ - IOS 17,5 流畅 https://youtube.com/shorts/VJXrZzO9wl0 - IOS 18 不流畅
我在 IOS 17.5 中尝试了这段代码,一切正常
我尝试将 CAanimatiom 对象转换为 SCNanimation。但没有效果。动画现在突然过渡,没有平滑混合。
这是代码
func setAnimationAddNew(fadeInDuration : CGFloat, fadeOutDuration : CGFloat, keyTime : CGFloat, _ animation: CAAnimation, isLooped: Bool, completion: (() -> Void)?) {
// Создаем анимационный плеер
let scnAnimation = SCNAnimation(caAnimation: animation)
// Создаем анимационный плеер
let animationPlayer = SCNAnimationPlayer(animation: scnAnimation)
animationPlayer.blendFactor = 1.0
// Настраиваем параметры анимации
animationPlayer.animation.blendInDuration = fadeInDuration
animationPlayer.animation.blendOutDuration = fadeOutDuration
if !isLooped {
animationPlayer.animation.repeatCount = 1
} else {
animationPlayer.animation.repeatCount = CGFloat.greatestFiniteMagnitude
}
animationPlayer.animation.animationEvents = [
SCNAnimationEvent(keyTime: keyTime, block: { _, _, _ in completion?() })
]
bodyNode?.addAnimationPlayer(animationPlayer, forKey: "avatarAnimation")
animationPlayer.play()
}