Skip to main content

Drawing the Snake

Drawing the Body#

To draw the snake, you can take advantage of zig's for syntax. To make it a little easier, it's a good idea to use the rect function of WASM-4:

// rect draws a rectangle. It uses color 1 to fill and color 2 for the outlinefn rect(x: i32, y: i32, width: u32, height: u32) void;

With that out the way, let's see what a first draft could look like.

// Add this to the top of the fileconst w4 = @import("wasm4.zig");
// ...
pub const Snake = struct {    // ...
    pub fn draw(this: *@This()) void {        for(this.body.constSlice()) |part| {            w4.rect(part.x * 8, part.y * 8, 8, 8);        }    }}

Simply loop through the body and draw it at x * 8 and y * 8. 8 is the width and the height of a single part. On a 160x160 screen, it's big enough to fit snake that is 20*20=400 parts long. This is where the 400 in the BoundedArray declaration comes from: we cap the number of elements to 400 at comptime so we don't have to deal with dynamically allocating memory for the points.

That's all fine, but since we haven't initialized the snake, nothing can be drawn. To fix this, simply create a new variable in main and call its draw function:

const w4 = @import("wasm4.zig");const Snake = @import("snake.zig").Snake;
var snake = Snake.init();
export fn start() void {    w4.PALETTE.* = .{        0xfbf7f3,        0xe5b083,        0x426e5d,        0x20283d,    };}
export fn update() void {    snake.draw();}

This creating a global variable of a snake with some default values.

After that, simply call the draw function of the snake.

You should see some green blocks at the top.

Snake Body

Drawing the Head#

But where is the head? You can pick a side. Either position [0] or position [this.body.length - 1].

I think it's easier to pick [0].

Since the body is drawn, head is not much of a problem. Simply use the rect function again. But use a specific part instead:

    w4.rect(this.body.get(0).x * 8, this.body.get(0).y * 8, 8, 8)

The draw function should now look like this:

pub fn draw(this: *@This()) void {    for(this.body.constSlice()) |part| {        w4.rect(part.x * 8, part.y * 8, 8, 8);    }
    w4.rect(this.body.get(0).x * 8, this.body.get(0).y * 8, 8, 8);}

Notice the difference? Me neither.

The head should stand out a little. For this, you can use a different color:

w4.DRAW_COLORS.* = 0x0004;

You can set the colors with this variable. You can look at this variable like a table that is read from right to left.

The value for each digit can be 0 up to 4:

  • 0 = Use transparency
  • 1 = Use the 1st color from the color palette
  • 2 = Use the 2nd color from the color palette
  • 3 = Use the 3rd color from the color palette
  • 4 = Use the 4th color from the color palette

The snippet above reads like this: "Color 1 uses Color 4 of the color palette, Color 2 to Color 4 don't use any color." The basic drawing functions use "Color 1" to fill the shape and "Color 2" for the border.

If you change the source to

pub fn draw(this: *@This()) void {    for(this.body.constSlice()) |part| {        w4.rect(part.x * 8, part.y * 8, 8, 8);    }
    w4.DRAW_COLORS.* = 0x0004;    w4.rect(this.body.get(0).x * 8, this.body.get(0).y * 8, 8, 8);}

Result:

Changing Color

You'll see a change. The snake changed color. Not only the head, but the complete snake! Once you've set a color, it stays that way. So if you want to change only the head, you have to change Color 1 again. Right before you draw the body.

pub fn draw(this: *@This()) void {    w4.DRAW_COLORS.* = 0x0043;    for(this.body.constSlice()) |part| {        w4.rect(part.x * 8, part.y * 8, 8, 8);    }
    w4.DRAW_COLORS.* = 0x0004;    w4.rect(this.body.get(0).x * 8, this.body.get(0).y * 8, 8, 8);}

This changes the color back and adds the darker green as its outline.

Snake with outline