feat(animation): 레이어 투명도 및 이펙트 5줄 확장
- AsciiLayer에 opacity 필드 추가 - AsciiCanvasPainter에서 레이어 투명도 렌더링 지원 - 배경 레이어 50% 투명으로 캐릭터 부각 - 모든 무기 이펙트 3줄→5줄로 확장 - 몬스터 공격 이펙트 5줄로 확장
This commit is contained in:
@@ -12,12 +12,14 @@ class _ParagraphCacheKey {
|
||||
required this.color,
|
||||
required this.fontSize,
|
||||
required this.cellWidth,
|
||||
required this.opacity,
|
||||
});
|
||||
|
||||
final String char;
|
||||
final AsciiCellColor color;
|
||||
final double fontSize;
|
||||
final double cellWidth;
|
||||
final double opacity;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
@@ -26,10 +28,11 @@ class _ParagraphCacheKey {
|
||||
char == other.char &&
|
||||
color == other.color &&
|
||||
fontSize == other.fontSize &&
|
||||
cellWidth == other.cellWidth;
|
||||
cellWidth == other.cellWidth &&
|
||||
opacity == other.opacity;
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(char, color, fontSize, cellWidth);
|
||||
int get hashCode => Object.hash(char, color, fontSize, cellWidth, opacity);
|
||||
}
|
||||
|
||||
/// ASCII Canvas 페인터 (CustomPainter 구현)
|
||||
@@ -124,7 +127,8 @@ class AsciiCanvasPainter extends CustomPainter {
|
||||
final x = gridX * cellWidth;
|
||||
final y = gridY * cellHeight;
|
||||
|
||||
_drawCell(canvas, cell, x, y, cellWidth, cellHeight, fontSize);
|
||||
// 레이어 투명도를 셀 렌더링에 전달
|
||||
_drawCell(canvas, cell, x, y, cellWidth, cellHeight, fontSize, layer.opacity);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -138,20 +142,22 @@ class AsciiCanvasPainter extends CustomPainter {
|
||||
double cellWidth,
|
||||
double cellHeight,
|
||||
double fontSize,
|
||||
double opacity,
|
||||
) {
|
||||
final cacheKey = _ParagraphCacheKey(
|
||||
char: cell.char,
|
||||
color: cell.color,
|
||||
fontSize: fontSize,
|
||||
cellWidth: cellWidth,
|
||||
opacity: opacity,
|
||||
);
|
||||
|
||||
// 캐시 히트 확인
|
||||
var paragraph = _paragraphCache[cacheKey];
|
||||
|
||||
if (paragraph == null) {
|
||||
// 캐시 미스: 새 Paragraph 생성
|
||||
final color = _getColor(cell.color);
|
||||
// 캐시 미스: 새 Paragraph 생성 (opacity 적용)
|
||||
final color = _getColor(cell.color).withValues(alpha: opacity);
|
||||
|
||||
final paragraphBuilder = ui.ParagraphBuilder(
|
||||
ui.ParagraphStyle(
|
||||
|
||||
@@ -9,6 +9,7 @@ class AsciiLayer {
|
||||
this.zIndex = 0,
|
||||
this.offsetX = 0,
|
||||
this.offsetY = 0,
|
||||
this.opacity = 1.0,
|
||||
});
|
||||
|
||||
/// 2D 셀 배열 [row][column]
|
||||
@@ -23,6 +24,9 @@ class AsciiLayer {
|
||||
/// Y 오프셋
|
||||
final int offsetY;
|
||||
|
||||
/// 레이어 투명도 (0.0 ~ 1.0, 배경 레이어 등에서 사용)
|
||||
final double opacity;
|
||||
|
||||
/// 레이어 높이 (줄 수)
|
||||
int get height => cells.length;
|
||||
|
||||
|
||||
@@ -99,7 +99,8 @@ class CanvasBattleComposer {
|
||||
}
|
||||
}
|
||||
|
||||
return AsciiLayer(cells: cells, zIndex: 0);
|
||||
// 배경 레이어는 50% 투명으로 캐릭터 부각
|
||||
return AsciiLayer(cells: cells, zIndex: 0, opacity: 0.5);
|
||||
}
|
||||
|
||||
/// 캐릭터 레이어 생성 (z=1)
|
||||
@@ -1107,24 +1108,24 @@ List<List<String>> _largeAlertFrames(MonsterCategory category) {
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 몬스터 공격 이펙트 (← 방향, Phase 8)
|
||||
// 몬스터 공격 이펙트 (← 방향, Phase 8) - 5줄
|
||||
// ============================================================================
|
||||
|
||||
/// 몬스터 공격 준비 프레임
|
||||
/// 몬스터 공격 준비 프레임 (5줄)
|
||||
const _monsterPrepareFrames = <List<String>>[
|
||||
[r' ', r' <', r' '],
|
||||
[r' ', r' <<', r' '],
|
||||
[r' ', r' ', r' < ', r' ', r' '],
|
||||
[r' ', r' _ ', r' << ', r' - ', r' '],
|
||||
];
|
||||
|
||||
/// 몬스터 공격 프레임
|
||||
/// 몬스터 공격 프레임 (5줄)
|
||||
const _monsterAttackFrames = <List<String>>[
|
||||
[r' ', r' <-- ', r' '],
|
||||
[r' ', r' <--- ', r' '],
|
||||
[r' ', r' <-----', r' '],
|
||||
[r' ', r' __ ', r' <-- ', r' -- ', r' '],
|
||||
[r' ', r' ___ ', r' <--- ', r' --- ', r' '],
|
||||
[r' ', r' ____ ', r' <----- ', r' ---- ', r' '],
|
||||
];
|
||||
|
||||
/// 몬스터 히트 프레임
|
||||
/// 몬스터 히트 프레임 (5줄)
|
||||
const _monsterHitFrames = <List<String>>[
|
||||
[r' *SLASH*', r' <-----', r' '],
|
||||
[r'*ATTACK*', r' <----', r' '],
|
||||
[r' *SLASH!* ', r' **** ', r' <----- ', r' **** ', r' '],
|
||||
[r'*ATTACK!* ', r' **** ', r' <---- ', r' **** ', r' '],
|
||||
];
|
||||
|
||||
@@ -47,95 +47,95 @@ WeaponEffect getWeaponEffect(WeaponCategory category) {
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 둔기류 - 휘두르기 (3줄)
|
||||
// 둔기류 - 휘두르기 (5줄)
|
||||
// ============================================================================
|
||||
const _bluntEffect = WeaponEffect(
|
||||
prepareFrames: [
|
||||
[r' _', r' /', r' /'],
|
||||
[r' _/', r' / ', r' / '],
|
||||
[r' ', r' _ ', r' / ', r' / ', r' / '],
|
||||
[r' ', r' _/ ', r' / ', r' / ', r' / '],
|
||||
],
|
||||
attackFrames: [
|
||||
[r' _/ ', r' / ', r'/ '],
|
||||
[r' /__ ', r'/ ', r' '],
|
||||
[r'/__ ', r' ', r' '],
|
||||
[r'/__=>', r' ', r' '],
|
||||
[r' ', r' _/ ', r' / ', r' / ', r' / '],
|
||||
[r' ', r' _/ ', r' / ', r' /________', r'/ '],
|
||||
[r' ', r' _/ ', r' /________', r'/ ', r' '],
|
||||
[r' ', r' _/____=> ', r'/ ', r' ', r' '],
|
||||
],
|
||||
hitFrames: [
|
||||
[r' *BASH* ', r'/__=> ', r' '],
|
||||
[r'*SMASH!*', r' /__ ', r' '],
|
||||
[r' *BASH* ', r' _/____=> ', r'/ ** ', r' ** ', r' '],
|
||||
[r' *SMASH!* ', r' _/____ ', r' / ** ', r' ** ', r' '],
|
||||
],
|
||||
hitSound: '*BASH*',
|
||||
effectHeight: 3,
|
||||
effectYStart: 2,
|
||||
effectHeight: 5,
|
||||
effectYStart: 1,
|
||||
);
|
||||
|
||||
// ============================================================================
|
||||
// 케이블류 - 채찍질 (3줄)
|
||||
// 케이블류 - 채찍질 (5줄)
|
||||
// ============================================================================
|
||||
const _cableEffect = WeaponEffect(
|
||||
prepareFrames: [
|
||||
[r' ', r'~ ', r' ~ '],
|
||||
[r' ', r'~~ ', r' ~ '],
|
||||
[r' ', r'~ ', r' ~ ', r' ~ ', r' ~ '],
|
||||
[r' ', r'~~ ', r' ~~ ', r' ~ ', r' ~ '],
|
||||
],
|
||||
attackFrames: [
|
||||
[r' ', r'~~~ ', r' ~~ '],
|
||||
[r' ', r'~~~~ ', r' ~~ '],
|
||||
[r' ', r'~~~~~> ', r' ~~ '],
|
||||
[r' ', r'~~~~~~> ', r' ~~'],
|
||||
[r' ', r'~~~ ', r' ~~~ ', r' ~~ ', r' ~ '],
|
||||
[r' ', r'~~~~ ', r' ~~~~ ', r' ~~', r' '],
|
||||
[r' ', r'~~~~~> ', r' ~~~~~', r' ', r' '],
|
||||
[r' ', r'~~~~~~> ', r' ~~~~', r' ', r' '],
|
||||
],
|
||||
hitFrames: [
|
||||
[r' *WHIP*', r'~~~~~~> ', r' ~~'],
|
||||
[r' *CRACK*', r'~~~~~> ', r' ~~ '],
|
||||
[r' *WHIP* ', r'~~~~~~> ', r' ~~~~', r' **', r' '],
|
||||
[r' *CRACK!* ', r'~~~~~> ', r' ~~~~~', r' **', r' '],
|
||||
],
|
||||
hitSound: '*WHIP*',
|
||||
effectHeight: 3,
|
||||
effectYStart: 2,
|
||||
effectHeight: 5,
|
||||
effectYStart: 1,
|
||||
);
|
||||
|
||||
// ============================================================================
|
||||
// 투척류 - 발사 (3줄)
|
||||
// 투척류 - 발사 (5줄)
|
||||
// ============================================================================
|
||||
const _projectileEffect = WeaponEffect(
|
||||
prepareFrames: [
|
||||
[r' ', r'[=] ', r' '],
|
||||
[r' ', r'[==] ', r' '],
|
||||
[r' ', r' ', r' [=] ', r' ', r' '],
|
||||
[r' ', r' _ ', r' [==] ', r' - ', r' '],
|
||||
],
|
||||
attackFrames: [
|
||||
[r' ', r' [> ', r' '],
|
||||
[r' ', r' [>', r' '],
|
||||
[r' ', r' [>', r' '],
|
||||
[r' ', r' [>', r' '],
|
||||
[r' ', r' . ', r' [> ', r" ' ", r' '],
|
||||
[r' ', r' . ', r' [> ', r" ' ", r' '],
|
||||
[r' ', r' . ', r' [>', r" ' ", r' '],
|
||||
[r' ', r' ', r' [>', r' ', r' '],
|
||||
],
|
||||
hitFrames: [
|
||||
[r' *CLANG*', r' [>', r' '],
|
||||
[r' *CRASH* ', r' [> ', r' '],
|
||||
[r' *CLANG!* ', r' ***', r' [>', r' ***', r' '],
|
||||
[r' *CRASH!* ', r' *** ', r' [> ', r' *** ', r' '],
|
||||
],
|
||||
hitSound: '*CLANG*',
|
||||
effectHeight: 3,
|
||||
effectYStart: 2,
|
||||
effectHeight: 5,
|
||||
effectYStart: 1,
|
||||
);
|
||||
|
||||
// ============================================================================
|
||||
// 에너지류 - 빔 발사 (3줄)
|
||||
// 에너지류 - 빔 발사 (5줄)
|
||||
// ============================================================================
|
||||
const _energyEffect = WeaponEffect(
|
||||
prepareFrames: [
|
||||
[r' ', r' <*> ', r' '],
|
||||
[r' == ', r' <**> ', r' == '],
|
||||
[r' ', r' == ', r' <**> ', r' == ', r' '],
|
||||
[r' ==== ', r' ====== ', r' <****> ', r' ====== ', r' ==== '],
|
||||
],
|
||||
attackFrames: [
|
||||
[r' ==== ', r'==<*>== ', r' ==== '],
|
||||
[r' ====== ', r'===<*>==', r' ====== '],
|
||||
[r'========', r'===<*>==', r'========'],
|
||||
[r'========', r'====<*>=', r'========'],
|
||||
[r' ====== ', r' ======== ', r'===<**>===', r' ======== ', r' ====== '],
|
||||
[r' ======== ', r'==========', r'===<**>===', r'==========', r' ======== '],
|
||||
[r'==========', r'==========', r'====<**>==', r'==========', r'=========='],
|
||||
[r'==========', r'==========', r'=====<**>=', r'==========', r'=========='],
|
||||
],
|
||||
hitFrames: [
|
||||
[r'==*ZAP*=', r'===<*>==', r'========'],
|
||||
[r'*BZZT!*=', r'====<*>=', r'========'],
|
||||
[r'===*ZAP*==', r'==========', r'====<**>==', r'==========', r'===*ZAP*=='],
|
||||
[r'==*BZZT!*=', r'==========', r'=====<**>=', r'==========', r'==*BZZT!*='],
|
||||
],
|
||||
hitSound: '*ZAP*',
|
||||
effectHeight: 3,
|
||||
effectYStart: 2,
|
||||
effectHeight: 5,
|
||||
effectYStart: 1,
|
||||
);
|
||||
|
||||
// ============================================================================
|
||||
@@ -162,23 +162,23 @@ const _cosmicEffect = WeaponEffect(
|
||||
);
|
||||
|
||||
// ============================================================================
|
||||
// 맨손 - 기본 펀치 (3줄)
|
||||
// 맨손 - 기본 펀치 (5줄)
|
||||
// ============================================================================
|
||||
const _unarmedEffect = WeaponEffect(
|
||||
prepareFrames: [
|
||||
[r' ', r' ', r' '],
|
||||
[r' ', r' > ', r' '],
|
||||
[r' ', r' ', r' > ', r' ', r' '],
|
||||
[r' ', r' _ ', r' -> ', r' - ', r' '],
|
||||
],
|
||||
attackFrames: [
|
||||
[r' ', r'-> ', r' '],
|
||||
[r' ', r'---> ', r' '],
|
||||
[r' ', r'-----> ', r' '],
|
||||
[r' ', r' __ ', r' ---> ', r' -- ', r' '],
|
||||
[r' ', r' ___ ', r' -----> ', r' --- ', r' '],
|
||||
[r' ', r' ____', r' ======>', r' ----', r' '],
|
||||
],
|
||||
hitFrames: [
|
||||
[r' *POW* ', r'-----> ', r' '],
|
||||
[r'*PUNCH*', r'----> ', r' '],
|
||||
[r' *POW!* ', r' ****', r' ======>', r' ****', r' '],
|
||||
[r' *PUNCH!* ', r' **** ', r' =====> ', r' **** ', r' '],
|
||||
],
|
||||
hitSound: '*POW*',
|
||||
effectHeight: 3,
|
||||
effectYStart: 2,
|
||||
effectHeight: 5,
|
||||
effectYStart: 1,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user