refactor(shared): animation, l10n, theme 모듈을 core에서 shared로 이동
- core/animation → shared/animation - core/l10n → shared/l10n - core/constants/ascii_colors → shared/theme/ascii_colors - import 경로 업데이트
This commit is contained in:
120
lib/src/shared/animation/canvas/canvas_walking_composer.dart
Normal file
120
lib/src/shared/animation/canvas/canvas_walking_composer.dart
Normal file
@@ -0,0 +1,120 @@
|
||||
import 'package:asciineverdie/src/shared/animation/background_data.dart';
|
||||
import 'package:asciineverdie/src/shared/animation/background_layer.dart';
|
||||
import 'package:asciineverdie/src/shared/animation/canvas/ascii_cell.dart';
|
||||
import 'package:asciineverdie/src/shared/animation/canvas/ascii_layer.dart';
|
||||
import 'package:asciineverdie/src/shared/animation/race_character_frames.dart';
|
||||
|
||||
/// Canvas용 걷기 애니메이션 합성기
|
||||
///
|
||||
/// 배경 스크롤 + 걷는 캐릭터
|
||||
/// Phase 4: 종족별 캐릭터 프레임 지원
|
||||
class CanvasWalkingComposer {
|
||||
const CanvasWalkingComposer({this.raceId});
|
||||
|
||||
/// 종족 ID (종족별 캐릭터 프레임 선택용)
|
||||
final String? raceId;
|
||||
|
||||
/// 프레임 상수
|
||||
static const int frameWidth = 60;
|
||||
static const int frameHeight = 8;
|
||||
|
||||
/// 레이어 기반 프레임 생성
|
||||
List<AsciiLayer> composeLayers(int globalTick) {
|
||||
return [
|
||||
_createBackgroundLayer(globalTick),
|
||||
_createCharacterLayer(globalTick),
|
||||
];
|
||||
}
|
||||
|
||||
/// 배경 레이어 생성 (z=0) - 숲 환경 기본
|
||||
AsciiLayer _createBackgroundLayer(int globalTick) {
|
||||
final cells = List.generate(
|
||||
frameHeight,
|
||||
(_) => List.filled(frameWidth, AsciiCell.empty),
|
||||
);
|
||||
|
||||
final bgLayers = getBackgroundLayers(EnvironmentType.forest);
|
||||
for (final layer in bgLayers) {
|
||||
// 스크롤 오프셋 계산 (걷기는 더 빠른 스크롤)
|
||||
final offset = (globalTick * layer.scrollSpeed * 2).toInt();
|
||||
|
||||
for (var i = 0; i < layer.lines.length; i++) {
|
||||
final y = layer.yStart + i;
|
||||
if (y >= frameHeight) break;
|
||||
|
||||
final pattern = layer.lines[i];
|
||||
if (pattern.isEmpty) continue;
|
||||
|
||||
for (var x = 0; x < frameWidth; x++) {
|
||||
final patternIdx = (x + offset) % pattern.length;
|
||||
final char = pattern[patternIdx];
|
||||
if (char != ' ') {
|
||||
cells[y][x] = AsciiCell.fromChar(char);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return AsciiLayer(cells: cells, zIndex: 0);
|
||||
}
|
||||
|
||||
/// 걷는 캐릭터 레이어 생성 (z=1)
|
||||
/// Phase 4: 종족별 프레임 지원
|
||||
AsciiLayer _createCharacterLayer(int globalTick) {
|
||||
final frameIndex = globalTick % 4; // 4프레임 루프
|
||||
List<String> charFrame;
|
||||
|
||||
// 종족별 프레임 사용 시도
|
||||
if (raceId != null && raceId!.isNotEmpty) {
|
||||
final raceData = RaceCharacterFrames.get(raceId!);
|
||||
if (raceData != null) {
|
||||
// idle 프레임을 기반으로 걷기 애니메이션 생성
|
||||
final idleFrame = raceData.idle[frameIndex % raceData.idle.length];
|
||||
charFrame = _animateWalking(idleFrame.lines, frameIndex);
|
||||
} else {
|
||||
charFrame = _walkingFrames[frameIndex];
|
||||
}
|
||||
} else {
|
||||
charFrame = _walkingFrames[frameIndex];
|
||||
}
|
||||
|
||||
final cells = _spriteToCells(charFrame);
|
||||
|
||||
// 화면 중앙에 캐릭터 배치 (25% 위치)
|
||||
const charX = 15;
|
||||
// 바닥 레이어(Y=7) 위에 서있도록
|
||||
final charY = frameHeight - cells.length - 1;
|
||||
|
||||
return AsciiLayer(cells: cells, zIndex: 1, offsetX: charX, offsetY: charY);
|
||||
}
|
||||
|
||||
/// idle 프레임 기반 걷기 애니메이션 생성
|
||||
/// 종족별 다리 모양을 유지 (idle 프레임이 4개라 자연스럽게 변화)
|
||||
List<String> _animateWalking(List<String> idleLines, int frameIndex) {
|
||||
// idle 프레임을 그대로 사용 (종족별 다리 모양 유지)
|
||||
// frameIndex에 따라 idle[0~3] 중 하나가 선택되어 자연스럽게 애니메이션됨
|
||||
return idleLines;
|
||||
}
|
||||
|
||||
/// 문자열 스프라이트를 AsciiCell 2D 배열로 변환
|
||||
List<List<AsciiCell>> _spriteToCells(List<String> lines) {
|
||||
return lines.map((line) {
|
||||
return line.split('').map(AsciiCell.fromChar).toList();
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 걷기 프레임 (4프레임 루프)
|
||||
// ============================================================================
|
||||
|
||||
const _walkingFrames = [
|
||||
// 프레임 1: 오른발 앞
|
||||
[r' o ', r' /|\ ', r' /| '],
|
||||
// 프레임 2: 모음
|
||||
[r' o ', r' /|\ ', r' |\ '],
|
||||
// 프레임 3: 왼발 앞
|
||||
[r' o ', r' /|\ ', r' /| '],
|
||||
// 프레임 4: 모음
|
||||
[r' o ', r' /|\ ', r' |\ '],
|
||||
];
|
||||
Reference in New Issue
Block a user