스프라이트 애니메이션 파이프라인은(는) 무엇을 하나요?
순서가 지정된 프레임 또는 시트를 Godot 4, Unity 6 또는 Phaser 3으로 가져옵니다. 애니메이션 타이밍, 아틀라스 참조 및 피벗을 수정합니다.
프로젝트 규칙, 설치된 버전, 아키텍처, 아트 스타일 및 명시적인 사용자 선택이 우선합니다.
태스크가 타이밍만 변경하는 경우 작동하는 시트와 포맷을 유지합니다. 슬라이싱과 리패킹은 선택 사항입니다.
이 워크플로우는 새 애니메이션을 생성하는 대신 기존 아트를 사용합니다. 새 프레임은 pixo media generate --sheet <C>x<R> --frames(프롬프트의 미디어 섹션)에서 제공되며, 이미 하나의 캔버스에 기록됩니다.
빠른 경로
pixo asset info <path> --json으로 선택한 시트 또는 프레임을 검사하고, 현재 클립 정의를 읽습니다.- 셀 크기와 순서, 클립 범위, 기준선, 피벗 및 루프 또는 원샷에 대한 타이밍을 확인합니다.
pixo asset frames check <frames...> --json이 기준선과 드리프트를 측정해 줍니다. - 아래의 직접 시트 또는 팩 분기 중 하나를 선택하고, 작업의 실제 출력 경로를 읽습니다.
- 게임플레이 충돌을 이동하지 않고 엔진의 기존 애니메이션 오너를 통해 통합합니다.
pixo game verify --steps check,build,run --screenshot --json을 실행하고, 로그와 프레임을 검사한 다음, 전체 루프와 원샷을 대화식으로 시청합니다.
명령어는 PIXO_PROJECT_ID를 가정합니다. 그렇지 않으면 --project <id>를 추가하세요.
아래의 경로와 프레임 수는 예시입니다. 메타데이터가 없는 시트는 자체 클립을 식별하지 않습니다.
Unity에는 run 단계나 스크린샷이 없습니다. --steps check,build를 사용하고 에디터에서 클립을 시청하세요.
가장 작은 포맷 변경 선택
| 입력 또는 요청 | 선택 | 유지할 것 |
|---|---|---|
| 작동하는 일반 시트, 타이밍만 변경 | 기존 SpriteFrames, Unity 클립 또는 Phaser 시트 | 프레임 범위, 텍스처 키, 공유 기준선 |
| 일반 그리드가 별도 프레임 필요 | pixo asset slice --cell WxH 또는 --grid CxR 및 입력 경로 | 여백, 간격 및 예상 커버리지 |
| 프레임이 흔들리거나, 미끄러지거나, 크기가 다름 | pixo asset frames check, 그다음 frames align | 입력 순서, 모든 프레임에 하나의 스케일 |
| Godot용 순서가 지정된 PNG | pack --format godot-tres | 입력 순서, PNG와 TRES를 함께 유지 |
| Phaser용 순서가 지정된 PNG | pack --format texturepacker-json | 입력 순서, 실제 설명자 키 |
| Unity용 시트 또는 프레임 | Sprite로 가져와 Unity에서 슬라이스, pack에는 Unity 출력 없음 | 피벗, Pixels Per Unit, 기존 .meta |
| 사용자 정의 피벗이 있는 트리밍된 프레임 | 기존 메타데이터 인식 파이프라인 | 트림 오프셋과 피벗, 이미지만으로는 복구 불가 |
관련 계약 필드만 기록: 캔버스와 셀 크기, 행 우선 순서, 클립 범위, FPS, 프레임 홀드, 루프 및 발 기준선. 동일한 크기의 캔버스와 공유 앵커는 프레임 간 흔들림을 방지합니다. 불투명 경계는 피벗이 아닙니다.
슬라이싱 및 패킹 앵커
pixo asset slice public/assets/player.png --cell 32x32 --out derived/player-frames --dry-run --json
pixo asset slice public/assets/player.png --cell 32x32 --out derived/player-frames --json
pixo asset pack sprites/run/frame-0000.png sprites/run/frame-0001.png --format godot-tres --padding 1 --out derived/run-godot --json
pixo asset pack public/sprites/run/frame-0000.png public/sprites/run/frame-0001.png --format texturepacker-json --padding 1 --out public/atlases/run --json
슬라이스 보고서에서 반환된 PNG 경로를 사용하세요. 두 프레임 팩 명령어는 순서를 설명하기 위한 예시입니다.
--grid는 여백과 간격 후 정확한 분할이 필요합니다. --cell은 불완전한 가장자리 셀을 사용하지 않을 수 있습니다.
전체 커버리지와 프레임 수를 확인하세요. 다른 클립의 빈 셀과 프레임을 의도적으로 제외하세요.
results, extraOutputs, reportPath, 실패 및 originalsPreserved를 읽으세요. 기존 대상은 건너뜁니다.
패커 팩트:
- Stem은 첫 번째 입력에서 파생되며
--out아래의 상대 디렉토리를 유지합니다. 경로를 추측하지 말고 읽으세요. - 출력은
<stem>.atlas.png와.atlas.tres또는.atlas.json입니다. - 패킹은 공간 배치를 재배열할 수 있지만 설명자는 입력 순서를 유지합니다.
- 회전, 트림, 가장자리 돌출, 클립 이름 지정 또는 사용자 정의 피벗이 생성되지 않습니다.
- 패딩은 투명한 분리이며, 복제된 가장자리 픽셀이 아닙니다.
프레임 확인 및 정렬
pixo asset frames check public/assets/hero/hero-run-1.png public/assets/hero/hero-run-2.png public/assets/hero/hero-run-3.png --json
pixo asset frames align public/assets/hero/hero-run-*.png --canvas 64x64 --padding 1 --out derived/hero-run --json
프레임을 재생 순서대로 전달합니다(셸 글로브는 -10을 -2보다 먼저 정렬합니다. 10개 이상일 때는 목록으로 나열하세요).
frames check는 이미지를 쓰지 않습니다. 프레임별로 results[].detail에는 그려진 bounds, bottomGap(발 아래 행), feetX(가장 낮은 행의 중심, 휘두른 무기가 움직이지 않음), empty 및 touchesEdge가 있습니다.
summary는 세트를 판단합니다: baselineSpreadPx(발 깜빡임), feetXSpreadPx(슬라이딩), heightDriftPercent, emptyFrames, edgeFrames 및 문제당 하나의 평문 문장을 포함한 issues, 문제가 없으면 ok는 true입니다.
움츠리기, 스쿼시 또는 점프는 의도적으로 높이를 변경합니다. 같은 포즈가 다른 높이에 있으면 모델 드리프트입니다.
frames align은 각 프레임을 <out>/<input path>.png에 하나의 캔버스로 기록합니다. 발은 바닥에서 padding 위의 한 줄에, feetX는 중심선에, 모든 프레임에 하나의 스케일을 적용하여 움츠림이 서 있는 것보다 짧게 유지됩니다.
--canvas WxH는 모든 프레임을 동일한 계수로 스케일하여 맞춥니다. 없으면 캔버스는 모든 프레임을 담을 수 있는 가장 작은 크기입니다.
--match-height는 먼저 각 그림을 중간 높이로 스케일하여 드리프트만 수정합니다. --filter nearest는 픽셀 아트를 선명하게 유지합니다.
summary는 정렬된 프레임을 다시 확인합니다. 모든 에셋 작업과 마찬가지로 기존 출력은 건너뜁니다.
Godot 4: SpriteFrames 타이밍
godot-tres는 5 FPS에서 루프 default 클립을 사용하여 SpriteFrames 포맷=3을 출력합니다.
PNG와 TRES를 함께 유지하세요. 텍스처 참조는 상대적입니다. pixo game verify --steps check --json을 통해 새 이미지를 가져오세요.
보고된 경로를 대체한 후, 이 코드는 실제 4프레임 클립을 조정합니다:
func configure_run(sprite: AnimatedSprite2D) -> void:
var packed := load("res://sprites/run/run.atlas.tres") as SpriteFrames
if packed == null or packed.get_frame_count(&"default") != 4:
push_error("Expected four run frames")
return
var frames := packed.duplicate() as SpriteFrames
frames.rename_animation(&"default", &"run")
frames.set_animation_speed(&"run", 10.0)
frames.set_animation_loop(&"run", true)
frames.set_frame(&"run", 0, frames.get_frame_texture(&"run", 0), 2.0)
sprite.sprite_frames = frames
sprite.centered = true
sprite.offset = Vector2(0, -16) # 예: 32px 캔버스에서 하단 중앙 앵커
sprite.play(&"run")
프레임 지속 시간 2.0은 일반 프레임 지속 시간의 두 배를 의미하며, 2초가 아닙니다.
오프셋에 대해 합의된 기준선을 사용하세요. CharacterBody2D와 CollisionShape2D를 안정적으로 유지하세요.
원샷 클립은 loop=false가 필요합니다. animation_finished를 한 번 연결하고 활성 애니메이션으로 전환을 제어하세요.
참조: Godot 4.3 SpriteFrames.
Phaser 3: 시트 또는 아틀라스 타이밍
// preload(), 실제 32px 시트의 경우:
this.load.spritesheet('player', 'assets/player.png', { frameWidth: 32, frameHeight: 32 });
// create(), 프로젝트의 실제 실행 범위 사용:
if (!this.anims.exists('player-run')) {
this.anims.create({ key: 'player-run',
frames: this.anims.generateFrameNumbers('player', { start: 2, end: 5 }),
frameRate: 10, repeat: -1 });
}
player.setOrigin(0.5, 1).play('player-run', true);
Vite 공개 에셋은 URL에 public/ 없이 로드됩니다.
아틀라스의 경우 this.load.atlas(key, pngUrl, jsonUrl)가 반환된 쌍을 로드합니다.
TexturePacker 해시 키는 입력 프로젝트 상대 경로입니다. 그것들을 읽거나 getFrameNames()를 사용하세요.
이름이 순서를 인코딩하지 않는 경우 명시적인 정렬된 목록을 사용하세요.
// orderedNames는 검토된 설명자/클립 목록에서 가져옵니다.
this.anims.create({ key: 'hero-run', frameRate: 10, repeat: -1,
frames: orderedNames.map((frame, index) => ({
key: 'hero-atlas', frame, duration: index === 0 ? 100 : 0,
})),
}); // 10 FPS에서 100ms 추가로 첫 번째 프레임을 총 200ms 동안 유지
위와 같이 this.anims.exists로 전역 애니메이션 생성을 보호하세요. 키는 Scene 재시작에도 유지됩니다.
repeat: 0은 한 번 재생합니다. 소유한 원샷 완료 리스너를 사용하세요.
원점 변경은 바디 배치에 영향을 줍니다. 잘못된 피벗을 숨기기 위해 충돌을 이동하는 대신 크기와 오프셋을 검사하세요.
참조: Phaser 3.90 애니메이션 타입.
Unity 6: 임포터, 클립 및 Animator
Unity는 추가 패키지 없이 godot-tres나 TexturePacker JSON을 읽지 않습니다. 시트를 Assets/ 아래에 넣은 다음, Texture Type을 Sprite로, Sprite Mode를 Multiple로, 실제 Pixels Per Unit을 설정하세요. 픽셀 아트의 경우 Point 필터와 압축 없음을 추가하세요. Sprite Editor에서 셀 크기로 슬라이스하고, Bottom과 같은 공유 피벗을 사용하세요. 스크립트 슬라이싱은 SpriteDataProviderFactories의 ISpriteEditorDataProvider를 통해 이루어집니다. TextureImporter.spritesheet는 이전 경로입니다. com.unity.2d.aseprite는 아트가 거기서 시작될 때 .aseprite 파일을 클립과 함께 직접 가져옵니다.
클립은 스프라이트를 선택하고 Animation 창에 드래그 앤 드롭하거나, 많은 경우 스크립트를 통해 만들 수 있습니다. 이 에디터 전용 예제는 4프레임 루핑 클립을 만들고 첫 번째 프레임을 유지합니다:
using System;
using System.Linq;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEngine;
public static class HeroClips
{
[MenuItem("Tools/Create Hero Run Clip")]
public static void CreateRun()
{
const float frame = 0.1f; // 10 FPS.
// LoadAllAssetsAtPath는 순서가 보장되지 않으며, hero_10은 텍스트로 hero_2보다 먼저 정렬됩니다.
var sprites = AssetDatabase.LoadAllAssetsAtPath("Assets/Art/hero.png").OfType<Sprite>()
.OrderBy(sprite => int.Parse(Regex.Match(sprite.name, @"\d+$").Value))
.Skip(2).Take(4).ToArray();
if (sprites.Length != 4) throw new InvalidOperationException("Expected four run frames");
var keys = sprites.Select((sprite, index) => new ObjectReferenceKeyframe {
time = index == 0 ? 0f : (index + 1) * frame, value = sprite,
}).ToArray();
var clip = new AnimationClip { frameRate = 1f / frame };
AnimationUtility.SetObjectReferenceCurve(clip,
EditorCurveBinding.PPtrCurve("", typeof(SpriteRenderer), "m_Sprite"), keys);
var settings = AnimationUtility.GetAnimationClipSettings(clip);
settings.loopTime = true;
settings.stopTime = (sprites.Length + 1) * frame;
AnimationUtility.SetAnimationClipSettings(clip, settings);
AssetDatabase.CreateAsset(clip, "Assets/Animations/hero_run.anim");
}
}
키 시간은 초 단위이므로, 홀드는 키 사이의 더 넓은 간격입니다. 마지막 프레임이 전체 길이를 얻도록 stopTime을 설정하세요.
- Animator에서 스프라이트 트랜지션은 일반적으로 Has Exit Time을 끄고 Transition Duration을 0으로 설정해야 합니다. 그렇지 않으면 다음 포즈가 늦게 도착합니다.
animator.Play(hash)는 이미 재생 중인 상태를 그대로 두므로, 업데이트마다 호출해도 안전합니다. 원샷을 다시 재생하려면Play(hash, 0, 0f)가 필요합니다. 해시는Animator.StringToHash로 한 번 이름을 지정하세요.- 원샷의 종료를 감지하려면 Animation Event를 사용하거나,
IsInTransition(0)이 false이고 상태 해시가 일치할 때normalizedTime >= 1을 확인하세요. 그렇지 않으면 트랜지션이 이전 상태를 보고할 수 있습니다. - Animator와 SpriteRenderer를 자식에 넣고, Rigidbody2D와 콜라이더는 루트에 두고, Apply Root Motion을 끄세요.
flipX는 콜라이더나 자식 오브젝트를 뒤집지 않으며, 피벗을 중심으로 미러링하므로, 중앙이 아닌 피벗은 점프합니다. - 타이트한 스프라이트 메시는 프레임 위에 그려지는 효과를 클리핑할 수 있습니다. 셰이더가 윤곽선 너머를 샘플링할 때는 Full Rect를 사용하세요.
- Sprite Atlas는 별도로 가져온 프레임을 배칭합니다. 패딩을 주고, 픽셀 아트의 경우 Tight Packing과 Allow Rotation을 끄세요.
함정: 잘못된 것 → 올바른 것
| 잘못된 것 | 올바른 것, 그리고 이유 |
|---|---|
| 패딩되지 않은 파일 이름을 사전순으로 정렬 | 명시적 순서, 그렇지 않으면 frame-10이 frame-2보다 앞섬 |
| 각 프레임을 독립적으로 트림 | 발이 고정되도록 캔버스 또는 유효한 오프셋 유지 |
| 패킹된 기본 이름 추측 | 설명자 키와 반환된 출력 경로 읽기 |
| 모든 엔진에서 동일한 지속 시간 단위 사용 | Godot은 상대적 승수, Phaser 프레임 지속 시간은 밀리초 추가, Unity 키는 초 |
업데이트마다 play(key) 호출 | play(key, true)는 이미 활성화된 클립을 다시 시작하지 않음 |
| 시각적 클립 속도가 데미지나 물리를 암시적으로 변경하도록 함 | 명시적으로 결합되지 않는 한 게임플레이 타이밍을 권위적으로 유지 |
완료 조건
- 실제 프레임 수, 프레임 순서, 클립 타이밍, 루핑 및 원샷 동작이 요청과 일치합니다.
- 발과 피벗이 idle과 run 사이의 트랜지션, 미러링 및 재시작을 통해 안정적으로 유지됩니다.
pixo asset frames check는 각 상태에 대해ok를 보고하거나, 남은 문제가 의도적입니다. - 아틀라스 참조와 로드 경로가 실제입니다. 시각적 변경은 충돌 동작을 보존합니다.
-
pixo game run <projectId> --json또는 Unity의 Editor를 사용하여 완전한 루프와 원샷 완료를 시청합니다. 정지 이미지는 타이밍을 증명할 수 없습니다. 테스트된 타이밍과 해결되지 않은 문제를 보고합니다. -
pixo game verify --steps check,build,run --screenshot --json을 검사하고, 리소스 오류와 건너뛴 단계를 포함합니다.