fix(animation): ASCII 애니메이션 높낮이/공백 문제 수정
- walkingAnimation, townAnimation 4줄 → 3줄 통일 - character_frames.dart 모든 프레임 폭 6자로 통일 - _compose() 이펙트 Y 위치 동적 계산 (하드코딩 제거) - withShield() 3줄 캐릭터용으로 수정 (index 3 → index 1) - BattleComposer 캔버스 시스템 및 배경 합성 추가 - 무기 카테고리별 이펙트, 몬스터 크기/색상 시스템 구현
This commit is contained in:
178
lib/src/core/animation/character_frames.dart
Normal file
178
lib/src/core/animation/character_frames.dart
Normal file
@@ -0,0 +1,178 @@
|
||||
// 캐릭터 애니메이션 프레임 (8줄 Stone Story RPG 스타일)
|
||||
// 참조: Stone Story RPG - 상세하고 생동감 있는 ASCII 아트
|
||||
|
||||
/// 전투 페이즈
|
||||
enum BattlePhase {
|
||||
/// 대치 상태 (기본)
|
||||
idle,
|
||||
|
||||
/// 공격 준비
|
||||
prepare,
|
||||
|
||||
/// 공격 중
|
||||
attack,
|
||||
|
||||
/// 피격 (몬스터가 맞음)
|
||||
hit,
|
||||
|
||||
/// 복귀
|
||||
recover,
|
||||
}
|
||||
|
||||
/// 캐릭터 프레임 데이터
|
||||
class CharacterFrame {
|
||||
const CharacterFrame(this.lines);
|
||||
|
||||
/// 프레임 데이터 (3줄)
|
||||
final List<String> lines;
|
||||
|
||||
/// 방패 오버레이 적용
|
||||
/// 3줄 캐릭터: [0]=머리, [1]=몸통/팔, [2]=다리
|
||||
CharacterFrame withShield() {
|
||||
if (lines.length < 2) return this;
|
||||
final newLines = List<String>.from(lines);
|
||||
// 몸통 줄(1번줄, 팔 위치)에 방패 추가
|
||||
final bodyIdx = 1;
|
||||
if (newLines[bodyIdx].length >= 2) {
|
||||
// 첫 두 문자를 방패로 대체
|
||||
newLines[bodyIdx] = '[]${newLines[bodyIdx].substring(2)}';
|
||||
} else {
|
||||
newLines[bodyIdx] = '[]${newLines[bodyIdx]}';
|
||||
}
|
||||
return CharacterFrame(newLines);
|
||||
}
|
||||
}
|
||||
|
||||
/// 특정 페이즈와 서브프레임에 해당하는 캐릭터 프레임 반환
|
||||
CharacterFrame getCharacterFrame(BattlePhase phase, int subFrame) {
|
||||
final frames = switch (phase) {
|
||||
BattlePhase.idle => _idleFrames,
|
||||
BattlePhase.prepare => _prepareFrames,
|
||||
BattlePhase.attack => _attackFrames,
|
||||
BattlePhase.hit => _hitFrames,
|
||||
BattlePhase.recover => _recoverFrames,
|
||||
};
|
||||
|
||||
final index = subFrame % frames.length;
|
||||
return frames[index];
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 대기 프레임 (숨쉬기 애니메이션) - 4프레임, 심플 3줄 스타일, 폭 6자
|
||||
// ============================================================================
|
||||
const _idleFrames = [
|
||||
CharacterFrame([
|
||||
r' o ',
|
||||
r' /|\ ',
|
||||
r' / \ ',
|
||||
]),
|
||||
CharacterFrame([
|
||||
r' o ',
|
||||
r' /|\ ',
|
||||
r' | | ',
|
||||
]),
|
||||
CharacterFrame([
|
||||
r' o ',
|
||||
r' /|\ ',
|
||||
r' / \ ',
|
||||
]),
|
||||
CharacterFrame([
|
||||
r' O ',
|
||||
r' /|\ ',
|
||||
r' / \ ',
|
||||
]),
|
||||
];
|
||||
|
||||
// ============================================================================
|
||||
// 준비 프레임 (무기 들기) - 3프레임, 심플 3줄 스타일, 폭 6자
|
||||
// ============================================================================
|
||||
const _prepareFrames = [
|
||||
CharacterFrame([
|
||||
r' \o ',
|
||||
r' |\ ',
|
||||
r' / \ ',
|
||||
]),
|
||||
CharacterFrame([
|
||||
r' _ ',
|
||||
r' \o ',
|
||||
r' / \ ',
|
||||
]),
|
||||
CharacterFrame([
|
||||
r' \_ ',
|
||||
r' \o/ ',
|
||||
r' / \ ',
|
||||
]),
|
||||
];
|
||||
|
||||
// ============================================================================
|
||||
// 공격 프레임 (전진 + 휘두르기) - 5프레임, 심플 3줄 스타일
|
||||
// ============================================================================
|
||||
const _attackFrames = [
|
||||
CharacterFrame([
|
||||
r' \_/ ',
|
||||
r' o ',
|
||||
r' /| ',
|
||||
]),
|
||||
CharacterFrame([
|
||||
r' _/ ',
|
||||
r' o ',
|
||||
r' /|\ ',
|
||||
]),
|
||||
CharacterFrame([
|
||||
r' o-- ',
|
||||
r' /| ',
|
||||
r' / \ ',
|
||||
]),
|
||||
CharacterFrame([
|
||||
r' o ',
|
||||
r' /|-- ',
|
||||
r' / \ ',
|
||||
]),
|
||||
CharacterFrame([
|
||||
r' o ',
|
||||
r' /|\_ ',
|
||||
r' / \ ',
|
||||
]),
|
||||
];
|
||||
|
||||
// ============================================================================
|
||||
// 히트 프레임 (공격 명중) - 3프레임, 심플 3줄 스타일
|
||||
// ============================================================================
|
||||
const _hitFrames = [
|
||||
CharacterFrame([
|
||||
r' o ',
|
||||
r' /|-* ',
|
||||
r' / \ ',
|
||||
]),
|
||||
CharacterFrame([
|
||||
r' o ',
|
||||
r' /|=* ',
|
||||
r' / \ ',
|
||||
]),
|
||||
CharacterFrame([
|
||||
r' o ',
|
||||
r' /|~* ',
|
||||
r' / \ ',
|
||||
]),
|
||||
];
|
||||
|
||||
// ============================================================================
|
||||
// 복귀 프레임 - 3프레임, 심플 3줄 스타일
|
||||
// ============================================================================
|
||||
const _recoverFrames = [
|
||||
CharacterFrame([
|
||||
r' o ',
|
||||
r' /|\ ',
|
||||
r' | ',
|
||||
]),
|
||||
CharacterFrame([
|
||||
r' o ',
|
||||
r' /|\ ',
|
||||
r' / \ ',
|
||||
]),
|
||||
CharacterFrame([
|
||||
r' o ',
|
||||
r' /|\ ',
|
||||
r' / \ ',
|
||||
]),
|
||||
];
|
||||
Reference in New Issue
Block a user