DrawFiddle▶ RunEdit in Fiddle
// Breakout — the brick wall spells DRAWN
const int cols = 34, rows = 20;
const double brickW = 10, brickH = 8;
const double wallTop = 26;
const double fieldW = cols * brickW;
const double fieldH = 440;
const double paddleW = 64, paddleH = 8, paddleY = 400;
const double ballR = 4;
const double baseSpeed = 260;

var glyphs = new Dictionary<char, string[]>();
glyphs['D'] = new[] { "1111.", "1...1", "1...1", "1...1", "1...1", "1...1", "1111." };
glyphs['R'] = new[] { "1111.", "1...1", "1...1", "1111.", "1..1.", "1...1", "1...1" };
glyphs['A'] = new[] { ".111.", "1...1", "1...1", "11111", "1...1", "1...1", "1...1" };
glyphs['W'] = new[] { "1...1", "1...1", "1...1", "1.1.1", "1.1.1", "11111", "1...1" };
glyphs['N'] = new[] { "1...1", "11..1", "11..1", "1.1.1", "1..11", "1..11", "1...1" };
var word = "DRAWN";

var strength = new int[cols, rows];
var alive = 0;
var score = 0;
var lives = 3;
var over = false;
var won = false;
double px = 0, py = 0, vx = 0, vy = 0;
double paddleX = fieldW / 2, paddleTo = fieldW / 2;
var keyDir = 0;   // -1 / +1 while an arrow key is held
var rnd = new Random();

SkiaLabel hud = null;
SkiaLabel status = null;

void BuildWall()
{
    for (int c = 0; c < cols; c++)
        for (int r = 0; r < rows; r++)
            strength[c, r] = 1;

    var wordCols = word.Length * 6 - 1;
    var c0 = (cols - wordCols) / 2;
    var r0 = 9;
    for (int i = 0; i < word.Length; i++)
    {
        var g = glyphs[word[i]];
        for (int gr = 0; gr < 7; gr++)
            for (int gc = 0; gc < 5; gc++)
                if (g[gr][gc] == '1')
                    strength[c0 + i * 6 + gc, r0 + gr] = 2;
    }
    alive = cols * rows;
}

void ResetBall()
{
    px = fieldW / 2;
    py = paddleY - 40;
    var a = (rnd.NextDouble() * 0.6 + 0.2) * Math.PI;
    vx = Math.Cos(a) * baseSpeed;
    vy = -Math.Abs(Math.Sin(a)) * baseSpeed;
}

void Restart()
{
    BuildWall();
    score = 0;
    lives = 3;
    over = false;
    won = false;
    paddleX = fieldW / 2;
    paddleTo = paddleX;
    ResetBall();
}

bool HitCell(double x, double y)
{
    var c = (int)Math.Floor(x / brickW);
    var r = (int)Math.Floor((y - wallTop) / brickH);
    if (c < 0 || c >= cols || r < 0 || r >= rows) return false;
    if (strength[c, r] <= 0) return false;
    strength[c, r]--;
    score += 10;
    if (strength[c, r] == 0) alive--;
    return true;
}

void Step(double dt)
{
    if (over || won) return;
    if (dt > 0.05) dt = 0.05;

    // held arrow key drives the target at a constant speed, key repeat is not involved
    if (keyDir != 0)
        paddleTo = Math.Clamp(paddleTo + keyDir * 380 * dt, paddleW / 2, fieldW - paddleW / 2);

    // pointer events land ~16/s, the canvas draws 60/s: chase the target so the paddle glides
    paddleX += (paddleTo - paddleX) * Math.Min(1.0, dt * 22);

    var dist = Math.Sqrt(vx * vx + vy * vy) * dt;
    var steps = Math.Max(1, (int)Math.Ceiling(dist / 2.0));
    var sdt = dt / steps;

    for (int s = 0; s < steps; s++)
    {
        px += vx * sdt;
        py += vy * sdt;

        if (px < ballR) { px = ballR; vx = Math.Abs(vx); }
        if (px > fieldW - ballR) { px = fieldW - ballR; vx = -Math.Abs(vx); }
        if (py < ballR) { py = ballR; vy = Math.Abs(vy); }

        if (vy > 0 && py + ballR >= paddleY && py - ballR <= paddleY + paddleH
            && px >= paddleX - paddleW / 2 - ballR && px <= paddleX + paddleW / 2 + ballR)
        {
            py = paddleY - ballR;
            var off = Math.Clamp((px - paddleX) / (paddleW / 2), -1.0, 1.0);
            var ang = off * 1.0;
            var sp = Math.Sqrt(vx * vx + vy * vy);
            vx = Math.Sin(ang) * sp;
            vy = -Math.Cos(ang) * sp;
        }

        if (HitCell(px + Math.Sign(vx) * ballR, py)) vx = -vx;
        else if (HitCell(px, py + Math.Sign(vy) * ballR)) vy = -vy;

        if (alive <= 0) { won = true; break; }

        if (py - ballR > fieldH)
        {
            lives--;
            if (lives <= 0) over = true;
            else ResetBall();
            break;
        }
    }
}

Restart();

return new SkiaStack()
{
    Spacing = 10,
    BackgroundColor = Color.Parse("#12161F"),
    Padding = new Thickness(16),
    Children = new List<SkiaControl>()
    {
        new SkiaLayer()
        {
            Children = new List<SkiaControl>()
            {
                new SkiaStack()
                {
                    Spacing = 2,
                    Children = new List<SkiaControl>()
                    {
                        new SkiaLabel("Breakout")
                        {
                            FontSize = 17,
                            FontWeight = 700,
                            TextColor = Color.Parse("#E8EDF6"),
                        },
                        new SkiaLabel("drag or use arrow keys")
                        {
                            FontSize = 11,
                            TextColor = Color.Parse("#6B7789"),
                        },
                    }
                }.StartX().CenterY(),

                new SkiaLabel("Score 0   Lives 3")
                {
                    FontSize = 12,
                    TextColor = Color.Parse("#FFD43B"),
                }
                .Assign(out hud)
                .EndX().CenterY(),
            }
        }.FillX(),

        // the frame paints the board background, the child layer paints the game on top
        new SkiaShape()
        {
            CornerRadius = 10,
            BackgroundColor = Color.Parse("#0B0E14"),
            WidthRequest = fieldW,
            HeightRequest = fieldH,
            Children = new List<SkiaControl>()
            {
                new SkiaLayer()
                {
                    Children = new List<SkiaControl>()
                    {
                        new SkiaLabel("")
                        {
                            FontSize = 14,
                            FontWeight = 700,
                            TextColor = Color.Parse("#E8EDF6"),
                            IsVisible = false,
                        }
                        .Assign(out status)
                        .Center(),
                    }
                }
                .Fill()
                .WhenPaint((me, ctx) =>
                {
                    var canvas = ctx.Context.Canvas;
                    var scale = ctx.Scale;
                    var dest = ctx.Destination;

                    float X(double v) => (float)(dest.Left + v * scale);
                    float Y(double v) => (float)(dest.Top + v * scale);
                    float S(double v) => (float)(v * scale);

                    using var paint = new SKPaint { IsAntialias = true, Style = SKPaintStyle.Fill };
                    var yellow = SKColor.Parse("#FFD43B");
                    var steel = SKColor.Parse("#4A5568");

                    for (int c = 0; c < cols; c++)
                    {
                        for (int r = 0; r < rows; r++)
                        {
                            var st = strength[c, r];
                            if (st <= 0) continue;
                            paint.Color = st > 1 ? steel : yellow;
                            canvas.DrawRect(new SKRect(
                                X(c * brickW + 1), Y(wallTop + r * brickH + 1),
                                X(c * brickW + brickW - 1), Y(wallTop + r * brickH + brickH - 1)), paint);
                        }
                    }

                    paint.Color = SKColor.Parse("#FF6B4A");
                    canvas.DrawRoundRect(new SKRect(
                        X(paddleX - paddleW / 2), Y(paddleY),
                        X(paddleX + paddleW / 2), Y(paddleY + paddleH)), S(4), S(4), paint);

                    paint.Color = SKColors.White;
                    canvas.DrawCircle(X(px), Y(py), S(ballR), paint);
                })
                .WithGestures((me, args, apply) =>
                {
                    if (args.Type == TouchActionResult.Down || args.Type == TouchActionResult.Panning)
                    {
                        if (over || won)
                        {
                            Restart();
                            return me;
                        }
                        var local = (args.Event.Location.X - me.DrawingRect.Left) / me.RenderingScale;
                        paddleTo = Math.Clamp(local, paddleW / 2, fieldW - paddleW / 2);
                        return me;
                    }
                    return null;
                })
                .Animate(1.0, (me, animator, value, dt) =>
                {
                    Step(dt);

                    var text = "Score " + score + "   Lives " + lives;
                    if (hud != null && hud.Text != text) hud.Text = text;

                    if (status != null)
                    {
                        var msg = won ? "YOU WIN — tap or press Space" : (over ? "GAME OVER — tap or press Space" : "");
                        if (status.Text != msg)
                        {
                            status.Text = msg;
                            status.IsVisible = msg.Length > 0;
                        }
                    }

                    me.Update();
                }, repeat: -1),
            }
        }
        .CenterX(),
    }
}
.OnKeyUp((me, key) => { })
.OnKeyDown((me, key) =>
{
    if (over || won)
    {
        if (key == InputKey.Space || key == InputKey.Enter) Restart();
        return;
    }
    if (key == InputKey.ArrowLeft) keyDir = -1;
    else if (key == InputKey.ArrowRight) keyDir = 1;
})
.OnKeyUp((me, key) =>
{
    if (key == InputKey.ArrowLeft && keyDir < 0) keyDir = 0;
    else if (key == InputKey.ArrowRight && keyDir > 0) keyDir = 0;
});