feat(animation): 종족별 캐릭터 애니메이션 시스템 추가
- 21개 종족별 고유 ASCII 캐릭터 프레임 데이터 추가 - 각 종족당 5가지 상태 애니메이션: idle, prepare, attack, hit, recover - 종족 특성에 맞는 시각적 차별화 (마법사 ~, 기사 ♦, 언데드 ☠ 등) - 캐릭터 생성 화면 종족 미리보기 위젯 추가 - 프론트 화면 Hero vs Boss 애니메이션 개선 - 게임 플레이 화면 애니메이션 패널 연동 강화
This commit is contained in:
@@ -3,12 +3,13 @@ import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:askiineverdie/data/race_data.dart';
|
||||
import 'package:askiineverdie/src/core/animation/front_screen_animation.dart';
|
||||
|
||||
/// 프론트 화면용 Hero vs Glitch God ASCII 애니메이션 위젯
|
||||
///
|
||||
/// 작은 용사가 거대한 Glitch God에 맞서는 루프 애니메이션
|
||||
/// 항상 검은 배경에 흰색 텍스트, 특수 효과만 컬러로 표시
|
||||
/// RichText로 문자별 색상 적용, 랜덤 종족 변경 지원
|
||||
class HeroVsBossAnimation extends StatefulWidget {
|
||||
const HeroVsBossAnimation({super.key});
|
||||
|
||||
@@ -18,23 +19,30 @@ class HeroVsBossAnimation extends StatefulWidget {
|
||||
|
||||
class _HeroVsBossAnimationState extends State<HeroVsBossAnimation> {
|
||||
int _currentFrame = 0;
|
||||
Timer? _timer;
|
||||
Timer? _animationTimer;
|
||||
Timer? _raceChangeTimer;
|
||||
final Random _random = Random();
|
||||
|
||||
// 현재 종족 (랜덤 변경)
|
||||
String _currentRaceId = 'byte_human';
|
||||
int _raceIndex = 0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_startAnimation();
|
||||
_startRaceChangeTimer();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_timer?.cancel();
|
||||
_animationTimer?.cancel();
|
||||
_raceChangeTimer?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _startAnimation() {
|
||||
_timer = Timer.periodic(
|
||||
_animationTimer = Timer.periodic(
|
||||
const Duration(milliseconds: frontScreenAnimationIntervalMs),
|
||||
(_) {
|
||||
if (mounted) {
|
||||
@@ -47,6 +55,26 @@ class _HeroVsBossAnimationState extends State<HeroVsBossAnimation> {
|
||||
);
|
||||
}
|
||||
|
||||
/// 8초마다 랜덤 종족 변경
|
||||
void _startRaceChangeTimer() {
|
||||
_raceChangeTimer = Timer.periodic(
|
||||
const Duration(seconds: 8),
|
||||
(_) => _changeRace(),
|
||||
);
|
||||
}
|
||||
|
||||
void _changeRace() {
|
||||
if (!mounted) return;
|
||||
|
||||
final allRaces = RaceData.all;
|
||||
if (allRaces.isEmpty) return;
|
||||
|
||||
setState(() {
|
||||
_raceIndex = (_raceIndex + 1) % allRaces.length;
|
||||
_currentRaceId = allRaces[_raceIndex].raceId;
|
||||
});
|
||||
}
|
||||
|
||||
/// 글리치 효과: 랜덤 문자 대체
|
||||
String _applyGlitchEffect(String frame) {
|
||||
// 10% 확률로 글리치 효과 적용
|
||||
@@ -66,12 +94,52 @@ class _HeroVsBossAnimationState extends State<HeroVsBossAnimation> {
|
||||
return chars.join();
|
||||
}
|
||||
|
||||
/// 문자별 색상 결정
|
||||
Color _getCharColor(String char) {
|
||||
// 공격/이펙트 (시안)
|
||||
if ('><=!+'.contains(char)) return Colors.cyan;
|
||||
// 데미지/글리치 (마젠타)
|
||||
if ('*~@#\$%&'.contains(char)) return const Color(0xFFFF00FF);
|
||||
// 보스 눈 (빨강)
|
||||
if ('◈◉X'.contains(char)) return Colors.red;
|
||||
// 보스 타이틀 텍스트 (시안)
|
||||
if ('GLITCH'.contains(char) || 'GOD'.contains(char)) return Colors.cyan;
|
||||
// 기본 (흰색)
|
||||
return Colors.white;
|
||||
}
|
||||
|
||||
/// 문자열을 색상별 TextSpan으로 변환
|
||||
TextSpan _buildColoredTextSpan(String text) {
|
||||
final spans = <TextSpan>[];
|
||||
|
||||
for (final char in text.split('')) {
|
||||
final color = _getCharColor(char);
|
||||
spans.add(
|
||||
TextSpan(
|
||||
text: char,
|
||||
style: TextStyle(
|
||||
fontFamily: 'JetBrainsMono',
|
||||
fontSize: 10,
|
||||
height: 1.1,
|
||||
color: color,
|
||||
letterSpacing: 0,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return TextSpan(children: spans);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final frame = _applyGlitchEffect(
|
||||
frontScreenAnimationFrames[_currentFrame],
|
||||
);
|
||||
|
||||
// 현재 종족 이름 (UI 표시용)
|
||||
final raceName = RaceData.findById(_currentRaceId)?.name ?? 'Hero';
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 16),
|
||||
decoration: BoxDecoration(
|
||||
@@ -93,23 +161,26 @@ class _HeroVsBossAnimationState extends State<HeroVsBossAnimation> {
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
// ASCII 애니메이션 (흰색 텍스트)
|
||||
// ASCII 애니메이션 (컬러 적용)
|
||||
FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
child: Text(
|
||||
frame,
|
||||
style: const TextStyle(
|
||||
fontFamily: 'JetBrainsMono',
|
||||
fontSize: 10,
|
||||
height: 1.1,
|
||||
color: Colors.white,
|
||||
letterSpacing: 0,
|
||||
),
|
||||
child: RichText(
|
||||
text: _buildColoredTextSpan(frame),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
// 하단 효과 바 (컬러)
|
||||
_buildEffectBar(),
|
||||
const SizedBox(height: 4),
|
||||
// 현재 종족 표시
|
||||
Text(
|
||||
'♦ $raceName',
|
||||
style: TextStyle(
|
||||
fontFamily: 'JetBrainsMono',
|
||||
fontSize: 9,
|
||||
color: Colors.cyan.withValues(alpha: 0.7),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user