<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>shri-codes on despatches</title><link>https://icle.es/tags/shri-codes/</link><description>Recent content in shri-codes on despatches</description><generator>Hugo</generator><language>en</language><atom:link href="https://icle.es/tags/shri-codes/index.xml" rel="self" type="application/rss+xml"/><item><title>Building Pong with Zig and Raylib #6: Font Size, Collision Bugs, and Refactors</title><link>https://icle.es/2025/07/19/building-pong-with-zig-and-raylib-%236-font-size-collision-bugs-and-refactors/</link><pubDate>Sat, 19 Jul 2025 08:00:00 +0100</pubDate><guid>https://icle.es/2025/07/19/building-pong-with-zig-and-raylib-%236-font-size-collision-bugs-and-refactors/</guid><description>&lt;p>In this one, we’ll tidy up our Pong implementation by addressing three key
issues:&lt;/p>
&lt;ol>
&lt;li>The score font is too small&lt;/li>
&lt;li>The ball can get stuck inside a paddle&lt;/li>
&lt;li>Trigger score on edge (not the middle) of the ball going past.&lt;/li>
&lt;/ol>
&lt;p>We’ll also refactor score tracking to better match game logic structure.&lt;/p>
&lt;h2 id="-make-the-score-font-more-readable">🖋️ Make the Score Font More Readable&lt;/h2>
&lt;p>The score text was a little too small. If you&amp;rsquo;re using &lt;code>dvui&lt;/code>, here&amp;rsquo;s how to
adjust it:&lt;/p></description><content:encoded><![CDATA[<p>In this one, we’ll tidy up our Pong implementation by addressing three key
issues:</p>
<ol>
<li>The score font is too small</li>
<li>The ball can get stuck inside a paddle</li>
<li>Trigger score on edge (not the middle) of the ball going past.</li>
</ol>
<p>We’ll also refactor score tracking to better match game logic structure.</p>
<h2 id="-make-the-score-font-more-readable">🖋️ Make the Score Font More Readable</h2>
<p>The score text was a little too small. If you&rsquo;re using <code>dvui</code>, here&rsquo;s how to
adjust it:</p>
```zig
const font_size: f32 = 64;
var label_options: dvui.Options = .{
    .color_text = .white,
    .font_style = .title,
};
label_options.font = label_options.fontGet().resize(font_size);
dvui.label(@src(), "{d}", .{score}, label_options);
```
<p>Thanks to
<a href="https://ziggit.dev/t/building-pong-in-zig-with-raylib-part-1-paddles-and-a-ball/10768/12">code sample from milogreg</a></p>
<p>You may also need to adjust the width and height of the container if the larger
font gets clipped. In our case, font size of 64 felt about right.</p>
<h2 id="-fix-the-ball-getting-stuck-in-the-paddle">🧱 Fix the Ball Getting Stuck in the Paddle</h2>
<p>When the ball moves too far in one frame, it can land <em>inside</em> the paddle and
bounce back and forth infinitely.</p>
<p>This trap happens because we just multiply the x-velocity with <code>-1</code> to reverse
direction.</p>
<p>One way to fix this is to only trigger the bounce if the ball is moving <em>toward</em>
the paddle. For example:</p>
```zig
const crossing_x = switch (self.which) {
    .right => ball.vel.x > 0 and
        ball.pos.x + ball.r >= self.pos.x,
    .left => ball.vel.x < 0 and
        ball.pos.x - ball.r <= self.pos.x + size.x,
};
```
<p>Thanks again to
<a href="https://ziggit.dev/t/building-pong-in-zig-with-raylib-part-1-paddles-and-a-ball/10768/12">code sample from milogreg</a></p>
<p>This ensures we don’t apply the bounce logic when the ball is already inside the
paddle.</p>
<p>Another (potential) way to fix this would be to change the collision logic to
fix the x direction based on whether it&rsquo;s the left or right paddle.</p>
<h2 id="-trigger-a-score-when-the-balls-edge-crosses-the-screen">🧮 Trigger a Score When the Ball’s Edge Crosses the Screen</h2>
<p>Previously, we checked whether the ball’s <strong>center</strong> (<code>ball.x</code>) crossed the
screen edge. Particularly with the ball being bigger than the paddle, this
caused issues when the top/bottom of the paddle hit the ball.</p>
<p><a href="https://icle.es/games/pong/src/Game.zig">Game.zig</a></p>
```zig
if (self.ball.pos.x + self.ball.r > self.screen_width) {
    self.left_score += 1;
    self.ball.reset();
}

if (self.ball.pos.x < self.ball.r) {
    self.right_score += 1;
    self.ball.reset();
}
```
<p>This triggers the score as soon as the edge of the ball crosses the screen
bounds.</p>
<h2 id="-refactor-move-scores-out-of-the-paddle-struct">🔄 Refactor: Move Scores Out of the Paddle Struct</h2>
<p>Storing the score inside the <code>Paddle</code> struct is convenient but semantically odd</p>
<ul>
<li>paddles shouldn’t own scores. Instead, let&rsquo;s move them into your <code>Game</code>
struct:</li>
</ul>
<p>Then, pass the score explicitly to the rendering function.</p>
<p>You can find the updated code in
<a href="https://icle.es/games/pong/src/Game.zig">Game.zig</a>.</p>
<h2 id="-bonus-fix-make-score-display-use-paddle-play-area">✅ Bonus Fix: Make Score Display Use Paddle Play Area</h2>
<p>We now compute each paddle’s <em>play area</em> (a <code>dvui.Rect</code>) and use it to position
the score label, keeping layout logic more re-usable.</p>
<p>We add a <code>play_area</code> field or method to our <code>Paddle</code> struct that returns its
side of the screen. This makes the rendering logic clearer and more flexible.</p>
<h2 id="-whats-next">⏭️ What’s Next?</h2>
<p>Next episode: adding a <strong>pause menu</strong> to Pong, based on what I just built for my
other game, <em>triangle</em>. We’ll add basic Resume/Quit options and freeze game
state mid-play.</p>
<h2 id="links">Links</h2>
<ul>
<li><a href="https://icle.es/youtube/shri-codes/pong/pong-6.md">Watch Video</a></li>
<li><a href="https://icle.es/games/pong/">Source Code (at this point)</a></li>
<li>Prev: <a href="https://icle.es/5-ui.md">Smarter Collisions &amp; Cleaner Code</a></li>
<li>Next: Pause Menu</li>
</ul>
]]></content:encoded></item><item><title>Building Pong with Zig and Raylib #5: Show Score with dvui</title><link>https://icle.es/2025/07/15/building-pong-with-zig-and-raylib-%235-show-score-with-dvui/</link><pubDate>Tue, 15 Jul 2025 08:00:00 +0100</pubDate><guid>https://icle.es/2025/07/15/building-pong-with-zig-and-raylib-%235-show-score-with-dvui/</guid><description>&lt;p>In this episode, I finally add a score display to Pong using DVUI, a native Zig
UI framework. The scoring logic was already in place - now it&amp;rsquo;s time to show it
on screen.&lt;/p>
&lt;h2 id="extract-out-game-struct">Extract out &lt;code>Game&lt;/code> struct&lt;/h2>
&lt;p>One of the structural changes I wanted to make to tidy up the code was to pull
out the game logic into its own struct. This change helps to declutter the
&lt;code>main.zig&lt;/code> file, leading the way to add in the dvui scaffolding.&lt;/p></description><content:encoded><![CDATA[<p>In this episode, I finally add a score display to Pong using DVUI, a native Zig
UI framework. The scoring logic was already in place - now it&rsquo;s time to show it
on screen.</p>
<h2 id="extract-out-game-struct">Extract out <code>Game</code> struct</h2>
<p>One of the structural changes I wanted to make to tidy up the code was to pull
out the game logic into its own struct. This change helps to declutter the
<code>main.zig</code> file, leading the way to add in the dvui scaffolding.</p>
<p>This refactor pulls the update and render logic into a <code>Game</code> struct, which
helps declutter <code>main.zig</code> and sets up a better foundation for UI work.</p>
<p><a href="https://icle.es/games/pong/src/Game.zig">Game.zig</a></p>
```zig
pub fn update(self: *Game, dt: f32) void {
    self.ball.checkEdgeCollisions(self.screen_height);
    self.ball.update(dt);
    self.ball.checkPaddleCollision(&self.left_paddle);
    self.ball.checkPaddleCollision(&self.right_paddle);
    if (self.ball.pos.x > self.screen_width) {
        self.left_paddle.score += 1;
        std.debug.print("scores: l: {d}, r: {d}\n", .{ self.left_paddle.score, self.right_paddle.score });
        self.ball.reset();
    }

    if (self.ball.pos.x < 0) {
        self.right_paddle.score += 1;
        std.debug.print("scores: l: {d}, r: {d}\n", .{ self.left_paddle.score, self.right_paddle.score });
        self.ball.reset();
    }

    if (rl.isKeyDown(.w)) {
        self.left_paddle.moveUp(dt);
    }

    if (rl.isKeyDown(.s)) {
        self.left_paddle.moveDown(dt);
    }

    if (rl.isKeyDown(.e)) {
        self.right_paddle.moveUp(dt);
    }

    if (rl.isKeyDown(.d)) {
        self.right_paddle.moveDown(dt);
    }
}

pub fn render(self: *const Game) void {
    self.left_paddle.render();
    self.right_paddle.render();
    self.ball.render();

    showScore(self.screen_width * 0.25, self.left_paddle.score);
    showScore(self.screen_width * 0.75, self.right_paddle.score);
}
```
<h2 id="add-dvui-dependency">Add dvui dependency</h2>
<p>Let&rsquo;s fetch the dependency, adding it to <code>build.zig.zon</code>:</p>
<p><code>zig fetch --save git+https://github.com/david-vanderson/dvui.git</code></p>
<p>Then, in <code>build.zig</code>, we also need to declare it:</p>
```zig
const dvui_dep = b.dependency("dvui", .{
    .target = target,
    .optimize = optimize,
});

const dvui = dvui_dep.module("dvui_raylib");
```
<p>and then add it as a dependency:</p>
```zig
exe.root_module.addImport("dvui", dvui);
```
<h2 id="add-dvui-to-game-loop">Add <code>dvui</code> to game loop</h2>
<p>We also need to initialise dvui in the main loop.</p>
<p><a href="https://icle.es/games/pong/src/main.zig">main.zig</a></p>
<h3 id="import">Import</h3>
```zig
const dvui = @import("dvui");

const RaylibBackend = dvui.backend;
comptime {
    std.debug.assert(@hasDecl(RaylibBackend, "RaylibBackend"));
}
const ray = RaylibBackend.c;
```
<h3 id="initialise">Initialise</h3>
```zig
var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;
const allocator = gpa.allocator();
defer _ = gpa.deinit();

//--------------------------------------------------------------------------
// init Raylib backend
// init() means the app owns the window (and must call CloseWindow itself)
var backend = RaylibBackend.init(allocator);
defer backend.deinit();
backend.log_events = true;

// init dvui Window (maps onto a single OS window)
// OS window is managed by raylib, not dvui
var win = try dvui.Window.init(@src(), allocator, backend.backend(), .{});
defer win.deinit();
```
<h3 id="pre-render">Pre-render</h3>
```zig
// marks the beginning of a frame for dvui, can call dvui functions after this
try win.begin(std.time.nanoTimestamp());

// send all Raylib events to dvui for processing
_ = try backend.addAllEvents(&win);
```
<h3 id="post-render">Post-render</h3>
```zig
_ = try win.end(.{});

// cursor management
if (win.cursorRequestedFloating()) |cursor| {
    // cursor is over floating window, dvui sets it
    backend.setCursor(cursor);
} else {
    // cursor should be handled by application
    backend.setCursor(.arrow);
}
```
<h2 id="show-score">Show Score</h2>
<p>We can now show the score using <code>dvui.label</code> with positioning hardcoded to about
25% and 75% across the screen. There may be a better way to position it but it
works well enough for now.</p>
<p>I had to generate an id for the label so that they were unique. I generated it
the x-position using <code>@intFromFloat()</code>.</p>
<p><a href="https://icle.es/games/pong/src/Game.zig">Game.zig</a></p>
```zig
pub fn render(self: *const Game) void {
    self.left_paddle.render();
    self.right_paddle.render();
    self.ball.render();

    showScore(self.screen_width * 0.25, self.left_paddle.score);
    showScore(self.screen_width * 0.75, self.right_paddle.score);
}

fn showScore(xpos: f32, score: u8) void {
    const id: usize = @intFromFloat(xpos);
    var right = dvui.box(@src(), .horizontal, .{ .rect = .{ .x = xpos, .y = 50, .w = 50, .h = 50 }, .id_extra = id });
    defer right.deinit();

    dvui.label(@src(), "{d}", .{score}, .{ .color_text = .white, .font_style = .title });
}
```
<h2 id="closing">Closing</h2>
<p>As always, things took a bit longer than expected, but by the end:</p>
<ul>
<li>The game&rsquo;s structure is cleaner</li>
<li>DVUI is wired up properly</li>
<li>Scores now show up on screen</li>
</ul>
<p>Shoutout to <a href="https://ziggit.dev/u/milogreg/summary">milo_greg</a> on
<a href="https://ziggit.dev/">ziggit.dev</a> for loads of really valuable feedback and
tips. That kind of thoughtful review really helps.</p>
<h2 id="links">Links</h2>
<ul>
<li><a href="https://icle.es/youtube/shri-codes/pong/pong-5.md">Watch Video</a></li>
<li><a href="https://icle.es/games/pong/">Source Code (at this point)</a></li>
<li>Prev: <a href="https://icle.es/4-refactor.md">Smarter Collisions &amp; Cleaner Code</a></li>
<li>Next: <a href="https://icle.es/6-refactor.md">Font Size, Collision Bugs, and Refactors</a></li>
</ul>
]]></content:encoded></item><item><title>Building Pong with Zig and Raylib - Part 4: Smarter Collisions, Cleaner Code</title><link>https://icle.es/2025/07/10/building-pong-with-zig-and-raylib-part-4-smarter-collisions-cleaner-code/</link><pubDate>Thu, 10 Jul 2025 15:33:34 +0100</pubDate><guid>https://icle.es/2025/07/10/building-pong-with-zig-and-raylib-part-4-smarter-collisions-cleaner-code/</guid><description>&lt;p>Change of Plans&lt;/p>
&lt;p>I was going to dive into menus and UI, but after sharing the early version of
Pong on &lt;a href="https://ziggit.dev/">ziggit.dev&lt;/a>, I got a bunch of helpful feedback.
The feedback was the kind that makes you stop and think, &lt;em>ah, right&amp;hellip; I should
probably fix that before carrying on.&lt;/em>&lt;/p>
&lt;p>So that&amp;rsquo;s what this episode became: a collection of fixes, tweaks, and small
refactors that clean up the code and align things more closely with how things
&lt;em>should&lt;/em> be done in Zig (or at least, better than I had them before).&lt;/p></description><content:encoded><![CDATA[<p>Change of Plans</p>
<p>I was going to dive into menus and UI, but after sharing the early version of
Pong on <a href="https://ziggit.dev/">ziggit.dev</a>, I got a bunch of helpful feedback.
The feedback was the kind that makes you stop and think, <em>ah, right&hellip; I should
probably fix that before carrying on.</em></p>
<p>So that&rsquo;s what this episode became: a collection of fixes, tweaks, and small
refactors that clean up the code and align things more closely with how things
<em>should</em> be done in Zig (or at least, better than I had them before).</p>
<h2 id="-naming-matters">🧼 Naming Matters</h2>
<p>I’d originally named my files <code>paddle.zig</code> and <code>ball.zig</code> - lowercase,
snake-case. I had tried to find the guidelines around this, but turns out I only
got half the story. If a file implicitly defines a struct via top-level fields,
it should be named in PascalCase. So, <code>Paddle.zig</code>, not <code>paddle.zig</code>.</p>
<p>It’s a small thing, but one that helps be a bit more idiomatic - and avoids
confusion when others are reading it.</p>
<p>(now to make the same change across many more files in
<a href="https://icle.es/endeavours/triangle.md">triangle</a>)</p>
<h2 id="-default-field-initializers-and-when-not-to-use-them">🛠️ Default Field Initializers (and When <em>Not</em> to Use Them)</h2>
<p>Another thing I learned: structs that aren’t used as config objects shouldn’t
use default field values. Instead, they should have an <code>init</code> constant that
represents their starting state.</p>
<p>I’d missed this distinction, and both of my types were using default values
incorrectly. So I cleaned that up and added the values into the <code>init</code> method.
It’s a subtle change, but it keeps config objects and plain data objects
conceptually separate - and makes it clearer which parts of a struct are
supposed to be overridden.</p>
<h2 id="-rls-please">✨ RLS, Please</h2>
<p>One of the comments suggested I lean more into Zig’s Result Location Syntax -
where you define the type on the left-hand side and let Zig figure out the rest.</p>
<p>I’d been using a mix of styles. Nothing broke, but consistency helps. So I swept
through the code and updated those as well.</p>
```zig
//main.zig
var left_paddle: Paddle = .init(Paddle.size.x * 0.5, .left, screen_height);
var right_paddle: Paddle = .init(screen_width - Paddle.size.x * 1.5, .right, screen_height);
var ball: Ball = .init(.{ .x = screen_width * 0.5, .y = screen_height * 0.5 });
```
<h2 id="-fixing-paddle-collisions-on-the-y-axis">🎯 Fixing Paddle Collisions on the Y Axis</h2>
<p>Now for something more visible: my collision detection logic only checked the
center point of the ball along the y-axis. That meant if the ball clipped the
paddle at the edge, it was sneaking through.</p>
<p>The fix was simple: add/subtract the ball’s radius in the y-axis check. Much
better.</p>
```zig
const colliding = ball.pos.y + ball.r >= self.pos.y and ball.pos.y - ball.r <= self.pos.y + size.y;
```
<p>You can actually see the difference in-game - that satisfying little <em>thock</em> now
triggers when it should, even on corner hits.</p>
<p>(now I just need add some sounds - I&rsquo;d forgotten about that)</p>
<h2 id="-iscolliding-should-only-collide">🧽 <code>isColliding</code> Should Only Collide</h2>
<p>Previously, <code>isColliding</code> also handled coloring the paddle red when it detected
a hit - a debug leftover that had no place in the final function.</p>
<p>I stripped that out and left <code>isColliding</code> to do just one thing: return whether
there was a collision. If I want debug visuals again later, I’ll wrap this in
another function.</p>
```zig
// Paddle.zig
pub fn isColliding(self: *const Paddle, ball: *const Ball) bool {
    // which edge do we need to check
    const crossing_x: bool = switch (self.which) {
        .right => ball.pos.x + ball.r >= self.pos.x,
        .left => ball.pos.x - ball.r <= self.pos.x + size.x,
    };

    if (!crossing_x) {
        return false;
    }

    const colliding = ball.pos.y + ball.r >= self.pos.y and ball.pos.y - ball.r <= self.pos.y + size.y;

    return colliding;
}
```
<h2 id="-movement-logic-encapsulation">🔀 Movement Logic Encapsulation</h2>
<p>Paddle movement was scattered, and the logic for moving up/down lived inside
<code>main.zig</code>. I pulled that out into proper <code>moveUp</code> and <code>moveDown</code> methods on
<code>Paddle</code>.</p>
<p>It reads cleaner now:</p>
```zig
//Paddle.zig
pub fn moveUp(self: *Paddle, dt: f32) void {
    self.move(-100, dt);
}

pub fn moveDown(self: *Paddle, dt: f32) void {
    self.move(100, dt);
}
```
```zig
// main.zig
if (rl.isKeyDown(.w)) {
    left_paddle.moveUp(dt);
}

if (rl.isKeyDown(.s)) {
    left_paddle.moveDown(dt);
}

if (rl.isKeyDown(.e)) {
    right_paddle.moveUp(dt);
}

if (rl.isKeyDown(.d)) {
    right_paddle.moveDown(dt);
}
```
<p>…and it keeps the input logic in <code>main</code>, but the movement logic in the paddle -
where it belongs.</p>
<h2 id="-resolution-independence">📐 Resolution Independence</h2>
<p>Some values were hardcoded (like setting <code>y = 200</code> for paddle start position),
while others used <code>getScreenHeight()</code> and <code>getScreenWidth()</code>. I refactored to
make everything use actual screen dimensions, converting <code>i32</code> screen height
values to <code>f32</code> where needed.</p>
<p>It was a bit fiddly, but worth it. Now Pong should behave properly regardless of
window size.</p>
<h2 id="closing">Closing</h2>
<p>So, yeah - not the flashiest episode, but a satisfying one. Lots of small things
that feel better now that they’re fixed. And it&rsquo;s a reminder that sharing early
(even rough work) is usually a good idea. You never know what you&rsquo;ll learn.</p>
<p>Next time, we <em>will</em> get into UI. I’m planning to bring in
<a href="https://github.com/david-vanderson/dvui">DVUI</a> and show a basic score display,
a pause menu, and maybe some options for reset and quit.</p>
<p>Until then, thanks for reading (and watching) - see you in the next one.</p>
<h2 id="links">Links</h2>
<ul>
<li><a href="https://icle.es/youtube/shri-codes/pong/pong-4.md">Watch Video</a></li>
<li><a href="https://icle.es/games/pong/">Source Code (at this point)</a></li>
<li>Prev: <a href="https://icle.es/3-scoring.md">Ball Movement &amp; Paddle Collisions</a></li>
<li>Next: <a href="https://icle.es/5-ui.md">Integrate DVUI &amp; Show Score</a></li>
</ul>
]]></content:encoded></item><item><title>Building Pong in Zig with Raylib – Part 3: Edge Collisions, Scoring &amp; Player Input</title><link>https://icle.es/2025/07/07/building-pong-in-zig-with-raylib-part-3-edge-collisions-scoring-player-input/</link><pubDate>Mon, 07 Jul 2025 12:01:00 +0100</pubDate><guid>https://icle.es/2025/07/07/building-pong-in-zig-with-raylib-part-3-edge-collisions-scoring-player-input/</guid><description>&lt;p>In this part, we round out the core gameplay of our Pong clone in Zig using
raylib. By the end, we&amp;rsquo;ve got a working game - with a bouncing ball, paddle
controls, and basic scoring.&lt;/p>
&lt;h2 id="-edge-collisions">🧱 Edge Collisions&lt;/h2>
&lt;p>We already had paddle collision working, but we needed the ball to bounce off
the top and bottom edges of the screen. That meant checking if the ball&amp;rsquo;s
y-position was outside the visible range and inverting its vertical velocity
accordingly:&lt;/p></description><content:encoded><![CDATA[<p>In this part, we round out the core gameplay of our Pong clone in Zig using
raylib. By the end, we&rsquo;ve got a working game - with a bouncing ball, paddle
controls, and basic scoring.</p>
<h2 id="-edge-collisions">🧱 Edge Collisions</h2>
<p>We already had paddle collision working, but we needed the ball to bounce off
the top and bottom edges of the screen. That meant checking if the ball&rsquo;s
y-position was outside the visible range and inverting its vertical velocity
accordingly:</p>
```zig
pub fn checkEdgeCollisions(self: *Ball, screen_height: f32) void {
    if (self.pos.y < self.r or self.pos.y > screen_height - self.r) {
        self.vel.y *= -1;
    }
}
```
<p>There was a small detour where I realised <code>screen_height</code> wasn’t giving the
expected value - using <code>GetScreenHeight()</code> directly from raylib helped resolve
that.</p>
<h2 id="-scoring-system">🏁 Scoring System</h2>
<p>Once edge collisions were in place, it was time to detect goals. If the ball
passed the left or right edge of the screen, the opposing player got a point. We
also need a <code>reset()</code> method to the <code>Ball</code> struct to return it to the centre
after each goal.</p>
```zig
pub fn reset(self: *Ball) void {
    self.pos = self.home;
    self.vel = .{ .x = 250, .y = -50 };
}
```
<p>For now, let&rsquo;s print the scores via <code>std.debug.print</code>, but this sets the stage
for UI integration.</p>
```zig
if (ball.pos.x > screenWidth) {
    left_paddle.score += 1;
    std.debug.print("scores: l: {d}, r: {d}", .{ left_paddle.score, right_paddle.score });
    ball.reset();
}

if (ball.pos.x < 0) {
    right_paddle.score += 1;
    std.debug.print("scores: l: {d}, r: {d}", .{ left_paddle.score, right_paddle.score });
    ball.reset();
}
```
<h2 id="-player-input">⌨️ Player Input</h2>
<p>Finally, we wired up keyboard input to allow paddle movement. We used
<code>IsKeyDown</code> from raylib, and made sure movement was frame-rate independent by
scaling it with <code>dt</code>:</p>
```zig
pub fn move(self: *Paddle, y: f32, dt: f32) void {
    self.pos.y += y * dt;
}
```
<p>Left paddle uses <code>W/S</code>, right paddle uses <code>E/D</code>. Simple and responsive.</p>
```zig
if (rl.isKeyDown(.w)) {
    left_paddle.move(-100, dt);
}

if (rl.isKeyDown(.s)) {
    left_paddle.move(100, dt);
}

if (rl.isKeyDown(.e)) {
    right_paddle.move(-100, dt);
}

if (rl.isKeyDown(.d)) {
    right_paddle.move(100, dt);
}
```
<h2 id="-whats-working">✅ What&rsquo;s Working</h2>
<p>By the end of this episode, we’ve got:</p>
<ul>
<li>Ball bounces off top and bottom edges</li>
<li>Scoring when the ball passes a paddle</li>
<li>Ball reset after each goal</li>
<li>Both paddles are fully controllable</li>
</ul>
<p>All that’s missing now is a visible score, a win condition, and maybe a simple
menu.</p>
<h2 id="links">Links</h2>
<ul>
<li><a href="https://icle.es/youtube/shri-codes/pong/pong-3.md">Watch Video</a></li>
<li><a href="https://icle.es/games/pong/">Source Code (at this point)</a></li>
<li>Prev: <a href="https://icle.es/2-collisions.md">Ball Movement &amp; Paddle Collisions</a></li>
<li>Next: <a href="https://icle.es/4-refactor.md">Code Improvements from Review</a></li>
</ul>
]]></content:encoded></item><item><title>Building Pong in Zig with Raylib – Part 2: Ball Movement &amp; Paddle Collisions</title><link>https://icle.es/2025/07/04/building-pong-in-zig-with-raylib-part-2-ball-movement-paddle-collisions/</link><pubDate>Fri, 04 Jul 2025 12:01:00 +0100</pubDate><guid>https://icle.es/2025/07/04/building-pong-in-zig-with-raylib-part-2-ball-movement-paddle-collisions/</guid><description>&lt;p>In &lt;a href="https://icle.es/1-setup.md">Part 1&lt;/a> we set up the basics: a window, paddles, and a ball.
In this episode, we go one step further and get the ball moving 😉, add paddle
collisions, and make everything frame-rate independent.&lt;/p>
&lt;h2 id="ball-movement">Ball Movement&lt;/h2>
&lt;p>The first step was to give the ball some velocity and update its position every
frame.&lt;/p>
```zig
self.pos = rl.math.vector2Add(self.pos, self.vel);
```
&lt;p>We also fixed a small oversight: the ball and paddles were being recreated every
frame inside the game loop. Moving their initialization outside meant we could
actually observe state changes between frames.&lt;/p></description><content:encoded><![CDATA[<p>In <a href="https://icle.es/1-setup.md">Part 1</a> we set up the basics: a window, paddles, and a ball.
In this episode, we go one step further and get the ball moving 😉, add paddle
collisions, and make everything frame-rate independent.</p>
<h2 id="ball-movement">Ball Movement</h2>
<p>The first step was to give the ball some velocity and update its position every
frame.</p>
```zig
self.pos = rl.math.vector2Add(self.pos, self.vel);
```
<p>We also fixed a small oversight: the ball and paddles were being recreated every
frame inside the game loop. Moving their initialization outside meant we could
actually observe state changes between frames.</p>
<h2 id="frame-rate-independence">Frame-Rate Independence</h2>
<p>Raylib provides <code>GetFrameTime()</code> which returns the time in seconds since the
last frame. Multiplying the velocity by this <code>dt</code> value ensures that the ball
movement stays consistent across different frame rates:</p>
```zig
const vel_this_frame = rl.math.vector2Scale(self.vel, dt);
self.pos = rl.math.vector2Add(self.pos, vel_this_frame);
```
<p>With that, the ball now moves at a steady speed, no matter the frame rate.</p>
<h2 id="paddle-collision-x-axis">Paddle Collision (X-Axis)</h2>
<p>Next up: detecting collisions between the ball and paddles. I considered writing
a standalone collision checker, but ended up keeping the logic within the
<code>Paddle</code> struct itself.</p>
<p>To simplify which edge to check (left or right), I added a <code>which</code> field to
paddles - an enum with values <code>left</code> and <code>right</code>. That made the conditional
logic much cleaner.</p>
<p>To debug collisions, I added color switching: when a paddle detects a collision
on the x-axis, it flashes red.</p>
```zig
pub fn isColliding(self: *Paddle, ball: *const Ball) bool {
    // which edge do we need to check
    const crossing_x: bool = switch (self.which) {
        .right => ball.pos.x + ball.r >= self.pos.x,
        .left => ball.pos.x - ball.r <= self.pos.x + size.x,
    };

    self.colour = if (crossing_x) .red else .white;
    return crossing_x;
}
```
<h2 id="paddle-collision-y-axis">Paddle Collision (Y-Axis)</h2>
<p>After confirming horizontal collision detection, I added vertical bounds
checking. This just involved verifying the ball&rsquo;s y-position is within the
paddle’s vertical range.</p>
```zig
pub fn isColliding(self: *Paddle, ball: *const Ball) bool {
    // which edge do we need to check
    const crossing_x: bool = switch (self.which) {
        .right => ball.pos.x + ball.r >= self.pos.x,
        .left => ball.pos.x - ball.r <= self.pos.x + size.x,
    };

    if (!crossing_x) {
        self.colour = .white;
        return false;
    }

    const colliding = ball.pos.y >= self.pos.y and ball.pos.y <= self.pos.y + size.y;

    self.colour = if (colliding) .red else .white;
    return colliding;
}
```
<h2 id="bounce-logic">Bounce Logic</h2>
<p>With detection in place, we added bounce logic to the ball. If a collision with
a paddle is detected, we flip the x-component of the velocity vector:</p>
```zig
pub fn checkPaddleCollision(self: *Ball, paddle: *Paddle) void {
    if (paddle.isColliding(self)) {
        self.vel = rl.math.vector2Scale(self.vel, -1);
    }
}
```
<h2 id="whats-next">What’s Next</h2>
<p>That wraps up part 2. In the next episode, we’ll handle edge collisions (top and
bottom), scoring, and input management.</p>
<p>Thanks for following along!</p>
<h2 id="links">Links</h2>
<ul>
<li><a href="https://youtu.be/IoOLH1O_a7M">Watch Video</a></li>
<li><a href="https://github.com/drone-ah/wordsonsand/tree/shri-codes/pong/part-2/games/pong">Source Code (at this point)</a></li>
<li>Prev: <a href="https://icle.es/1-setup.md">Place Paddles &amp; Ball</a></li>
<li>Next: <a href="https://icle.es/3-scoring.md">Edge Collisions, Scoring &amp; Inputs</a></li>
</ul>
]]></content:encoded></item><item><title>Building Pong in Zig with Raylib – Part 1: Setup, Paddles, and Ball</title><link>https://icle.es/2025/06/28/building-pong-in-zig-with-raylib-part-1-setup-paddles-and-ball/</link><pubDate>Sat, 28 Jun 2025 20:38:29 +0100</pubDate><guid>https://icle.es/2025/06/28/building-pong-in-zig-with-raylib-part-1-setup-paddles-and-ball/</guid><description>&lt;p>Before continuing development on Triangle - my larger, arcade ARPG, factory
game, I wanted to take a step back and build something small and familiar. Pong
felt like the perfect choice: quick to prototype, easy to understand, and a good
warm-up before diving deeper into raylib again.&lt;/p>
&lt;p>This post goes alongside &lt;a href="https://youtu.be/ICq2D_na6zc">my video on YouTube&lt;/a>,
and walks through the early steps of the project: setting up the game, getting
something on screen, and implementing the paddles and the ball.&lt;/p></description><content:encoded><![CDATA[<p>Before continuing development on Triangle - my larger, arcade ARPG, factory
game, I wanted to take a step back and build something small and familiar. Pong
felt like the perfect choice: quick to prototype, easy to understand, and a good
warm-up before diving deeper into raylib again.</p>
<p>This post goes alongside <a href="https://youtu.be/ICq2D_na6zc">my video on YouTube</a>,
and walks through the early steps of the project: setting up the game, getting
something on screen, and implementing the paddles and the ball.</p>
<h2 id="why-pong">Why Pong?</h2>
<p>Sometimes, before getting deeper into a project, it helps to do something simple
just to get your hands moving again. I hadn’t written Zig in a couple of weeks,
and wanted a fast feedback loop to:</p>
<ul>
<li>Get back into using <code>raylib-zig</code></li>
<li>Have a bit of fun</li>
<li>Remind myself why I made certain decisions in the first place</li>
</ul>
<h2 id="project-setup">Project Setup</h2>
<p>I&rsquo;m using <a href="https://github.com/Not-Nik/raylib-zig"><code>raylib-zig</code></a> for this. You
can scaffold a new project with:</p>
```bash
./project_setup.sh <project-name>
```
<p>You could also use <code>zig init</code> and then add the dependency in manually if you
prefer.</p>
<h2 id="drawing-the-playground">Drawing the Playground</h2>
<p>The Pong &ldquo;arena&rdquo; is simple:</p>
<ul>
<li>Two paddles</li>
<li>A ball (currently static)</li>
<li>A center dividing line</li>
<li>Placeholder for scores</li>
</ul>
<h2 id="implementing-paddles">Implementing Paddles</h2>
<p>The paddles have:</p>
<ul>
<li>A position (top-left corner)</li>
<li>A fixed size</li>
<li>A simple render function</li>
</ul>
```zig
const std = @import("std");
const rl = @import("raylib");

const Paddle = @This();

pos: rl.Vector2,

pub fn init(x: f32) Paddle {
    return .{ .pos = .{
        .x = x,
        .y = 200,
    } };
}

pub const size = rl.Vector2{ .x = 25, .y = 100 };

pub fn render(self: Paddle) void {
    rl.drawRectangleV(self.pos, size, .white);
}
```
<p>It might have been nice to be able to have the paddle calculate more of its own
values, but the more I think about it, the more it makes sense to keep the logic
in paddle simpler.</p>
<h2 id="ball-placeholder">Ball Placeholder</h2>
<p>To wrap things up for this session, I added a static ball in the center of the
screen. It has a radius, position, and velocity fields ready to go. Rendering is
straightforward:</p>
```zig
const std = @import("std");
const rl = @import("raylib");

const Ball = @This();

pos: rl.Vector2,
r: f32 = 16,
vel: rl.Vector2 = .{ .x = 0, .y = 0 },

pub fn render(self: Ball) void {
    rl.drawCircleV(self.pos, self.r, .white);
}
```
<h2 id="whats-next">What&rsquo;s Next</h2>
<p>This was mostly about warming up, but the next episode will tackle:</p>
<ul>
<li>Adding velocity to the ball</li>
<li>Basic collision detection with paddles</li>
</ul>
<p>I&rsquo;ll try and think about simplifying paddle logic, especially the awkward
symmetry between left and right.</p>
<p>You can find the full source code
<a href="https://github.com/drone-ah/wordsonsand/tree/main/games/pong">on GitHub</a>.</p>
<p>See you in part 2!</p>
<h2 id="links">Links</h2>
<ul>
<li><a href="https://icle.es/youtube/shri-codes/pong/pong-1.md">YouTube Video</a></li>
<li><a href="https://github.com/drone-ah/wordsonsand/tree/shri-codes/pong/part-1/games/pong">Full Source Code (at this point)</a></li>
<li>Next: <a href="https://icle.es/2-collisions.md">Ball Movement &amp; Paddle Collisions</a></li>
</ul>
]]></content:encoded></item><item><title>Under the Hood of triangle</title><link>https://icle.es/2025/06/27/under-the-hood-of-triangle/</link><pubDate>Fri, 27 Jun 2025 08:01:28 +0100</pubDate><guid>https://icle.es/2025/06/27/under-the-hood-of-triangle/</guid><description>&lt;p>In &lt;a href="https://youtu.be/8nA-a5Z1IDc">this Let&amp;rsquo;s Code video&lt;/a> on
&lt;a href="http://www.youtube.com/@ShriCodesHere">&lt;strong>shri codes&lt;/strong>&lt;/a>, I walk through some of
the early systems in place for &lt;a href="https://icle.es/tags/triangle/">&lt;em>Triangle&lt;/em>&lt;/a> - my space
ARPG built in &lt;strong>Zig&lt;/strong> with &lt;strong>raylib&lt;/strong>.&lt;/p>
&lt;p>For the broader story behind the project - what &lt;em>Triangle&lt;/em> is, where it&amp;rsquo;s going,
and why I’m making it - check out the
&lt;a href="https://icle.es/endeavours/triangle.md">triangle endeavour&lt;/a> or the
&lt;a href="https://icle.es/tags/triangle/">devlog archive&lt;/a>. This post is about the &lt;em>how&lt;/em>.&lt;/p>
&lt;h3 id="-core-systems-so-far">🧩 Core Systems So Far&lt;/h3>
&lt;p>At this stage, Triangle supports:&lt;/p>
&lt;ul>
&lt;li>Basic player movement and shooting (Asteroids-style).&lt;/li>
&lt;li>Procedurally generated asteroid fields per &lt;em>sector&lt;/em>.&lt;/li>
&lt;li>Material drops (iron ore) from destroyed asteroids.&lt;/li>
&lt;li>Automatic recipe unlocking when new items are picked up.&lt;/li>
&lt;li>Smelting system that converts ore to ingots.&lt;/li>
&lt;li>Unlocking and queuing up &lt;em>constructors&lt;/em> using the crafting menu.&lt;/li>
&lt;li>A basic UI for crafting and inventory.&lt;/li>
&lt;li>Configurable keyboard mappings using TOML.&lt;/li>
&lt;/ul>
&lt;p>That’s the visible layer. Below the surface, the codebase has carved out some
space for more ambitious systems.&lt;/p></description><content:encoded><![CDATA[<p>In <a href="https://youtu.be/8nA-a5Z1IDc">this Let&rsquo;s Code video</a> on
<a href="http://www.youtube.com/@ShriCodesHere"><strong>shri codes</strong></a>, I walk through some of
the early systems in place for <a href="https://icle.es/tags/triangle/"><em>Triangle</em></a> - my space
ARPG built in <strong>Zig</strong> with <strong>raylib</strong>.</p>
<p>For the broader story behind the project - what <em>Triangle</em> is, where it&rsquo;s going,
and why I’m making it - check out the
<a href="https://icle.es/endeavours/triangle.md">triangle endeavour</a> or the
<a href="https://icle.es/tags/triangle/">devlog archive</a>. This post is about the <em>how</em>.</p>
<h3 id="-core-systems-so-far">🧩 Core Systems So Far</h3>
<p>At this stage, Triangle supports:</p>
<ul>
<li>Basic player movement and shooting (Asteroids-style).</li>
<li>Procedurally generated asteroid fields per <em>sector</em>.</li>
<li>Material drops (iron ore) from destroyed asteroids.</li>
<li>Automatic recipe unlocking when new items are picked up.</li>
<li>Smelting system that converts ore to ingots.</li>
<li>Unlocking and queuing up <em>constructors</em> using the crafting menu.</li>
<li>A basic UI for crafting and inventory.</li>
<li>Configurable keyboard mappings using TOML.</li>
</ul>
<p>That’s the visible layer. Below the surface, the codebase has carved out some
space for more ambitious systems.</p>
<h3 id="-code-structure-overview">🗂️ Code Structure Overview</h3>
<p>Here’s a quick tour of the key directories and their purpose:</p>
<h4 id="blit"><code>blit/</code></h4>
<p>Handles all the 2D rendering abstractions - bounding boxes, shapes (including
triangle fans 😉 and circles), and canvas drawing. Wraps <code>raylib</code> for better
namespacing and type control.</p>
<h4 id="phys"><code>phys/</code></h4>
<p>Physics system. <code>body.zig</code> implements basic movement, collisions, and inertia.
Still minimal, but usable.</p>
<h4 id="combat"><code>combat/</code></h4>
<p>Still early - only <code>bullet</code> is implemented, and is used to destroy asteroids.
Placeholder for upcoming systems like health, damage, and targeting.</p>
<h4 id="items"><code>items/</code></h4>
<p>This module forms the backbone of the crafting system. Items are tied to
recipes, and recipes are triggered via pickups or crafting actions. There&rsquo;s a
lot more to unpack here - likely its own devlog soon.</p>
<h4 id="notifier"><code>notifier/</code></h4>
<p>Manages in-game UI feedback. When you pick something up or unlock a new recipe,
this is what shows it. Notifiers are scoped - one for inventory, one for
crafting - and show up on either side of the screen.</p>
<h4 id="ship"><code>ship/</code></h4>
<p>This module contains the ship related logic, including movement, and the logic
for constructing smelters and other buildables. Eventually this will handle
assembly chains.</p>
<h4 id="asteroid"><code>asteroid</code></h4>
<p>These files should be refactored into its own module, and should contain the
asteroid itself, as well as sector, a chunk of space - asteroids, entities,
bullets, etc. This is the closest thing to a “level” in Triangle. It’s
procedurally generated, and you can eventually travel from one sector to the
next.</p>
<h4 id="diag"><code>diag/</code></h4>
<p>Code to support diagnostics &amp; logging</p>
<h4 id="game"><code>game/</code></h4>
<p>This module should get a bit of refactoring as well, moving some of the files
from the root into its own module</p>
<ul>
<li><code>config</code>: Takes care of user control bindings and input overrides via a TOML
config file.</li>
<li><code>notifier</code>: supports collecting notifications across multiple channels.</li>
<li><code>save</code>: Just scaffolding for now - no save/load logic yet, but it’s a
placeholder to group serializable components together.</li>
<li><code>context</code>: container struct to support dependency injection.</li>
</ul>
<h4 id="ui"><code>ui/</code></h4>
<p>Holds in-game panels like crafting and inventory. Needs cleanup and
consolidation.</p>
<h3 id="-whats-missing">🧪 What’s Missing</h3>
<p>A lot of the systems are stubbed out, but not yet wired in. For instance:</p>
<ul>
<li><code>power</code> is empty (future energy system)</li>
<li>The <em>canvas</em> currently wraps <code>raylib</code> without adding much - that will likely
change as UI complexity grows.</li>
</ul>
<h3 id="-threads-ill-pull-next">🧶 Threads I’ll Pull Next</h3>
<p>These are the next things I’ll either build or record:</p>
<ul>
<li>Improving the coding structure. I&rsquo;d like to improve organisation and am
considering extracting a re-usable library out of it, mainly so I can tinker
with other smaller game projects.</li>
<li>A <strong>rudimentary menu system</strong></li>
<li>A <strong>build+deploy system</strong> to generate early test builds</li>
<li>Possibly a <strong>contact/feedback</strong> form directly in-game or on the site</li>
</ul>
<p>I’ll be releasing this in what I’m calling a <a href="https://icle.es/sprout.md"><strong>&ldquo;Sprout&rdquo; build</strong></a> -
a playable seed, a form of extremely early access. Feedback and collaboration is
really important to me and I want to get this out there as soon as there is the
tiniest spark of &ldquo;life.&rdquo;</p>
<p>If you’d like to follow the journey, you can
<a href="https://youtu.be/8nA-a5Z1IDc">watch the video devlog</a></p>
]]></content:encoded></item></channel></rss>