데미지 애니메이션의 구현
육상전 구현중입니다. 대항해시대인데 3편은 특이하게 육상전이 있습니다. 게임에서 가장 역동적인 컨텐츠라고 할 수 있습니다.
하지만 밋밋하게 가만히 있고 숫자만 뜨면 재미가 없겠죠. 그래서 타격감을 부여 해주어야 합니다.
공격 당하고 흔들림

데미지 표시
앞에서 맞았으면 흔들려 준 다음에 얼마의 피해가 있었는지도 시각적으로 보여줘야겠죠? 이미지가 떳다가 사라지면 밋밋하기 때문에 아래에서 떠오르는 효과로 4프레임에 걸쳐 구현 되어있습니다.

다음은 애니메이션을 C#으로 다시 구현한 부분입니다. WPF로 구현을 했구요 Redraw()를 호출 합니다.
// 숫자가 뜬 채로 넉 장 — 숫자는 장마다 <b>왼위로 떠오르고</b> 맞은 부대는 좌우로
// 흔들린다. 마지막 자리에 선 채로 다섯 눈금을 더 쉬고 나서 걷힌다.
for (int f = 0; f < Shakes.Length; f++)
{
var (dx, dy) = Rises[f];
foreach (var (glyph, left, top) in shown) At(glyph, left + dx, top + dy);
foreach (int slot in hurt) _shake[slot] = Shakes[f];
Redraw();
Rest(SwingMs);
}
foreach (int slot in hurt) _shake[slot] = 0;
Redraw();
Rest(RestMs);
foreach (var (glyph, _, _) in shown) _board.Children.Remove(glyph);
/// <summary>부대와 병사수를 다시 그린다.</summary>
private void Redraw()
{
for (int i = _board.Children.Count - 1; i >= 0; i--)
if (_board.Children[i] is FrameworkElement { Tag: UnitLayer })
_board.Children.RemoveAt(i);
Stand();
}
그러나 어색합니다. 맞고 흔들리고나서 데미지가 올라가야 하는데 같이 올라오니까 어색합니다.

분석을 해보니 제자리 왼쪽 오른쪽 오른쪽 제자리 이렇게 가네요
0 → −8 → 0 → +8 → 0 으로 움직이구요. 코드는 다음과 같이 수정되었습니다.
private void Flash(IReadOnlyList<LandFight.Line> bout)
{
var hurt = new List<int>();
var hits = new List<(int Slot, string Men)>();
foreach (var line in bout)
{
if (line.Damage <= 0) continue;
if (line.Target < 0 || line.Target >= StandAt.Length) continue;
if (!hurt.Contains(line.Target)) hurt.Add(line.Target);
hits.Add((line.Target, line.Damage.ToString()));
}
if (hits.Count == 0) return;
// ① 치는 몸짓이 끝나면 <b>한 장 멎었다가</b> 맞은 부대가 좌우로 흔들린다.
// 이 동안 숫자는 아직 안 뜬다.
Rest(SwingMs);
int drift = 0;
foreach (int step in Shakes)
{
// 표는 <b>자리가 아니라 걸음</b>이다 — 더해 가야 0·−8·0·+8·0 이 된다.
drift += step;
foreach (int slot in hurt) _shake[slot] = drift;
Redraw();
Rest(SwingMs);
}
foreach (int slot in hurt) _shake[slot] = 0;
Redraw();
// ② 흔들림이 멎고 나서 숫자가 뜬다.
var shown = new List<(FrameworkElement Glyph, int Left, int Top)>();
foreach (var (slot, men) in hits)
{
var (x, y) = StandAt[slot];
int left = x + (LandArt.DeployWidth - men.Length * Digit) / 2;
int top = y + (LandArt.DeployWidth - Digit) / 2;
foreach (char c in men)
{
if (Number(c - '0') is { } glyph)
{
Panel.SetZIndex(glyph, FlashDepth);
_board.Children.Add(At(glyph, left - Outline, top - Outline));
shown.Add((glyph, left - Outline, top - Outline));
}
left += Digit;
}
}
if (shown.Count == 0) { Rest(RestMs); return; }
// ③ 넉 장에 걸쳐 왼위로 떠오른다. 마지막 자리에 선 채로 다섯 눈금을 더 쉰다.
foreach (var (dx, dy) in Rises)
{
foreach (var (glyph, left, top) in shown) At(glyph, left + dx, top + dy);
Rest(SwingMs);
}
Rest(RestMs);
foreach (var (glyph, _, _) in shown) _board.Children.Remove(glyph);
}
결과

하지만 너무 계단이 지네요 비트맵 스케일 모드를 써서 이미지가 축소/확대되어도 깨지지 않고 깔끔하게 나오도록 설정 BitmapScalingMode.Fant을 적용 해줍니다.
private static Image Picture(uint[] bgra, int w, int h, int drawW = 0, int drawH = 0)
{
var bmp = BitmapSource.Create(w, h, 96, 96, PixelFormats.Pbgra32, null, bgra, w * 4);
bmp.Freeze();
var image = new Image
{
Source = bmp,
Width = drawW > 0 ? drawW : w,
Height = drawH > 0 ? drawH : h,
Stretch = Stretch.Fill,
};
RenderOptions.SetBitmapScalingMode(image, BitmapScalingMode.Fant);
return image;
}
조금 나아졌지만 아직 원본인 오리지널에는 미치지 못합니다.
일단은 여기까지입니다.