diff --git a/Snake.Avalonia/Views/AnimationHelper.cs b/Snake.Avalonia/Views/AnimationHelper.cs
index f11c590..0a5f2ce 100644
--- a/Snake.Avalonia/Views/AnimationHelper.cs
+++ b/Snake.Avalonia/Views/AnimationHelper.cs
@@ -26,7 +26,16 @@ public static class AnimationHelper
}
///
- /// Interpolates between two positions with given t value.
+ /// Ease-in cubic — mirror of EaseOutCubic, for tail interpolation.
+ ///
+ public static double EaseInCubic(double t)
+ {
+ t = Math.Clamp(t, 0.0, 1.0);
+ return t * t * t;
+ }
+
+ ///
+ /// Interpolates between two positions with given t value and easing mode.
/// Returns the visual position for rendering.
///
public static (double X, double Y) InterpolatePosition(
@@ -34,9 +43,14 @@ public static class AnimationHelper
Position to,
double t,
double cellSize,
- bool useEasing = false)
+ string easing = "linear")
{
- var eased = useEasing ? EaseOutCubic(t) : t;
+ var eased = easing switch
+ {
+ "easeOut" => EaseOutCubic(t),
+ "easeIn" => EaseInCubic(t),
+ _ => t
+ };
var x = from.X + (to.X - from.X) * eased;
var y = from.Y + (to.Y - from.Y) * eased;
return (x * cellSize + cellSize * 0.5, y * cellSize + cellSize * 0.5);
diff --git a/Snake.Avalonia/Views/SnakeRenderer.cs b/Snake.Avalonia/Views/SnakeRenderer.cs
index 06af464..b5b6598 100644
--- a/Snake.Avalonia/Views/SnakeRenderer.cs
+++ b/Snake.Avalonia/Views/SnakeRenderer.cs
@@ -172,28 +172,6 @@ public sealed class SnakeRenderer : Control
context.DrawGeometry(null, innerPen, bodyGeometry);
}
- // === Fading ghost circle at old tail position ===
- // Creates the illusion of smooth tail movement without affecting
- // the body path geometry (which stays grid-snapped with sharp corners).
- if (count >= 2 && PreviousSegments != null && PreviousSegments.Count > 0)
- {
- var opacity = 0.4 * (1.0 - t);
- if (opacity > 0)
- {
- var oldTailPos = PreviousSegments[PreviousSegments.Count - 1];
- var currentTailPos = segmentsList[count - 1];
-
- var (ghostX, ghostY) = AnimationHelper.InterpolatePosition(
- oldTailPos, currentTailPos, t, CellSize, useEasing: false);
-
- context.DrawEllipse(
- new SolidColorBrush(SnakeBodyBrush.Color, opacity),
- null,
- new Point(ghostX, ghostY),
- CellSize * 0.35, CellSize * 0.35);
- }
- }
-
// Draw head
DrawHead(context, t);
@@ -379,17 +357,22 @@ public sealed class SnakeRenderer : Control
private (double X, double Y) GetVisualPosition(Position pos, int segmentIndex, double t)
{
- // Head (index 0): interpolated with easing for smooth movement
- if (segmentIndex == 0)
- {
- var from = PreviousSegments != null && PreviousSegments.Count > 0
- ? AnimationHelper.GetPreviousPosition(0, Segments!, PreviousSegments, SnakeDirection)
- : pos;
+ if (Segments == null)
+ return (pos.X * CellSize + CellSize * 0.5, pos.Y * CellSize + CellSize * 0.5);
- return AnimationHelper.InterpolatePosition(from, pos, t, CellSize, useEasing: true);
- }
+ var isHead = segmentIndex == 0;
+ var isTail = Segments.Count > 1 && segmentIndex == Segments.Count - 1;
- // All other segments (body and tail): grid-snapped for sharp corners
- return (pos.X * CellSize + CellSize * 0.5, pos.Y * CellSize + CellSize * 0.5);
+ // Head and tail interpolate smoothly; middle segments stay grid-snapped
+ if (!isHead && !isTail)
+ return (pos.X * CellSize + CellSize * 0.5, pos.Y * CellSize + CellSize * 0.5);
+
+ var from = PreviousSegments != null && PreviousSegments.Count > 0
+ ? AnimationHelper.GetPreviousPosition(segmentIndex, Segments, PreviousSegments, SnakeDirection)
+ : pos;
+
+ // Head: ease-out forward. Tail: ease-in backward (mirrored).
+ var easingMode = isHead ? "easeOut" : "easeIn";
+ return AnimationHelper.InterpolatePosition(from, pos, t, CellSize, easing: easingMode);
}
}