-
Notifications
You must be signed in to change notification settings - Fork 0
perf: 뭉치 영상 생성 프레임 드랍 해결 #86
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
The head ref may contain hidden characters: "bugfix/-\uBB49\uCE58\uD0A4\uB9C1-\uC601\uC0C1\uCD94\uCD9C-\uC601\uC0C1-\uC885\uB8CC-\uC2DC\uC5D0-\uAE09\uACA9\uD55C-\uD504\uB808\uC784\uB4DC\uB78D-\uD655\uC778-\uC544\uB9C8-\uC2A4\uC719-\uC774\uD6C4\uC778\uB4EF"
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,13 +14,12 @@ import Lottie | |
| extension BundleVideoGenerator { | ||
|
|
||
| /// 파티클 재생 정보 | ||
| /// 현재 재생 중인 파티클을 렌더링하기 위해 필요한 모든 정보를 담고 있음 | ||
| /// 시작 시점에 모든 프레임을 프리렌더링하여 재생 중에는 캐시된 텍스처만 사용 | ||
| struct ParticlePlaybackInfo { | ||
| let displaySprite: SKSpriteNode // 화면에 표시되는 스프라이트 | ||
| let startedAtFrame: Int // 파티클 시작 프레임 | ||
| let particleId: String // 파티클 ID | ||
| let lottieRenderer: LottieAnimationView // Lottie → 이미지 변환 렌더러 | ||
| let animationData: LottieAnimation // Lottie 메타데이터 (총 프레임 수 등) | ||
| let displaySprite: SKSpriteNode // 화면에 표시되는 스프라이트 | ||
| let startedAtFrame: Int // 파티클 시작 프레임 | ||
| let particleId: String // 파티클 ID | ||
| let preRenderedTextures: [SKTexture] // 프리렌더링된 텍스처 배열 | ||
| } | ||
|
|
||
| /// 파티클 업데이트 | ||
|
|
@@ -43,60 +42,85 @@ extension BundleVideoGenerator { | |
| particleIndicesToRemove.forEach { playingParticles.removeValue(forKey: $0) } | ||
| } | ||
|
|
||
| /// 파티클 시작 | ||
| /// 파티클 시작 (모든 프레임을 프리렌더링) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 뭐든 미리 준비해두는게 빠르네요ㅋㅋㅋㅋ 굿! 확실히 많이 매끄러워졌어요. |
||
| private func startParticle(for keyringIndex: Int, particleId: String, at frameIndex: Int, scene: MultiKeyringScene) { | ||
| guard let animation = findParticleAnimation(particleId: particleId) else { | ||
| return | ||
| } | ||
|
|
||
| // Lottie 뷰 설정 | ||
| let config = LottieConfiguration(renderingEngine: .mainThread) | ||
| let lottieView = LottieAnimationView(animation: animation, configuration: config) | ||
| lottieView.frame = CGRect(origin: .zero, size: CGSize(width: scene.size.width, height: scene.size.height)) | ||
| lottieView.contentMode = .scaleAspectFit | ||
| lottieView.backgroundBehavior = .pauseAndRestore | ||
|
|
||
| // 모든 프레임을 프리렌더링 | ||
| let textures = preRenderAllFrames(lottieView: lottieView, animation: animation) | ||
|
|
||
| // 스프라이트 생성 | ||
| let sprite = SKSpriteNode() | ||
| sprite.size = CGSize(width: scene.size.width, height: scene.size.height) | ||
| sprite.position = CGPoint(x: scene.size.width / 2, y: scene.size.height / 2) | ||
| sprite.zPosition = 100 | ||
| sprite.alpha = 1.0 | ||
|
|
||
| // 첫 프레임 텍스처 설정 | ||
| if let firstTexture = textures.first { | ||
| sprite.texture = firstTexture | ||
| } | ||
|
|
||
| scene.addChild(sprite) | ||
|
|
||
| playingParticles[keyringIndex] = ParticlePlaybackInfo( | ||
| displaySprite: sprite, | ||
| startedAtFrame: frameIndex, | ||
| particleId: particleId, | ||
| lottieRenderer: lottieView, | ||
| animationData: animation | ||
| preRenderedTextures: textures | ||
| ) | ||
| } | ||
|
|
||
| /// 파티클 렌더링 | ||
| /// Lottie 애니메이션의 모든 프레임을 SKTexture로 프리렌더링 | ||
| private func preRenderAllFrames(lottieView: LottieAnimationView, animation: LottieAnimation) -> [SKTexture] { | ||
| let totalFrames = Int(animation.endFrame - animation.startFrame) | ||
| var textures: [SKTexture] = [] | ||
| textures.reserveCapacity(totalFrames) | ||
|
|
||
| // UIGraphicsImageRenderer를 한 번만 생성 (재사용) | ||
| let imageRenderer = UIGraphicsImageRenderer(bounds: lottieView.bounds) | ||
|
|
||
| for frameOffset in 0..<totalFrames { | ||
| let targetFrame = animation.startFrame + CGFloat(frameOffset) | ||
| lottieView.currentFrame = AnimationFrameTime(targetFrame) | ||
| lottieView.setNeedsDisplay() | ||
| lottieView.layer.displayIfNeeded() | ||
|
|
||
| let image = imageRenderer.image { context in | ||
| lottieView.layer.render(in: context.cgContext) | ||
| } | ||
| textures.append(SKTexture(image: image)) | ||
| } | ||
|
|
||
| return textures | ||
| } | ||
|
|
||
| /// 파티클 렌더링 (프리렌더링된 텍스처 사용) | ||
| private func updateActiveParticle(for keyringIndex: Int, at frameIndex: Int, scene: MultiKeyringScene) -> Bool { | ||
| guard let particleInfo = playingParticles[keyringIndex] else { | ||
| return false | ||
| } | ||
|
|
||
| let sprite = particleInfo.displaySprite | ||
| let offset = frameIndex - particleInfo.startedAtFrame | ||
| let targetFrame = particleInfo.animationData.startFrame + CGFloat(offset) | ||
|
|
||
| if targetFrame >= particleInfo.animationData.endFrame { | ||
| // 애니메이션 종료 체크 | ||
| if offset >= particleInfo.preRenderedTextures.count { | ||
| sprite.removeFromParent() | ||
| return true | ||
| } | ||
|
|
||
| particleInfo.lottieRenderer.currentFrame = AnimationFrameTime(targetFrame) | ||
| particleInfo.lottieRenderer.setNeedsDisplay() | ||
| particleInfo.lottieRenderer.layer.displayIfNeeded() | ||
|
|
||
| let imageRenderer = UIGraphicsImageRenderer(bounds: particleInfo.lottieRenderer.bounds) | ||
| let image = imageRenderer.image { context in | ||
| particleInfo.lottieRenderer.layer.render(in: context.cgContext) | ||
| } | ||
|
|
||
| sprite.texture = SKTexture(image: image) | ||
| // 프리렌더링된 텍스처 사용 (매우 빠름) | ||
| sprite.texture = particleInfo.preRenderedTextures[offset] | ||
|
|
||
| return false | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
영상 만드는데 그림자가 좀 무거웠나보네요 조금 아쉽긴 하지만 편의성을 위해서 조금 덜고 가는게 맞을 것 같네요.