Skip to main content

Drawing the Snake

Drawing the Body#

We'll add a new function draw inside Snake implementation.

To make it a little easier, it's a good idea to use the rect function of WASM-4 that is already defined in src/lib/wasm4.rs:

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

Simply loop through the body and draw it at x * 8 and y * 8 because our snake is made of 8x8 squares.

// src/snake.rs inside `impl Snake {}` blockpub fn draw(&self) {    for &Point { x, y } in self.body.iter() {        wasm4::rect(x * 8, y * 8, 8, 8);    }}
Importing wasm4

Keep in mind you need to import wasm4, in case your editor doesn't do this for you.

use crate::wasm4;

That's all fine, but since there is no instance of the snake, nothing can be drawn. To fix this we are going to define a Game struct inside src/game.rs.

// src/game.rsuse crate::snake::{Point, Snake};use crate::wasm4;
pub struct Game {    snake: Snake,}
impl Game {    pub fn new() -> Self {      Self {          snake: Snake::new(),      }    }
    pub fn update(&self) {        self.snake.draw();    }}

We'll store a global Game instance using Mutex, provided by the standard library, and the macro lazy_static from crates.io.

// src/lib.rs#[cfg(feature = "buddy-alloc")]mod alloc;mod game;mod palette;mod snake;mod wasm4;use game::Game;use lazy_static::lazy_static;use std::sync::Mutex;
lazy_static! {    static ref SNAKE_GAME: Mutex<Game> = Mutex::new(Game::new());}
#[no_mangle]fn start() {    palette::set_palette([0xfff6d3, 0xf9a875, 0xeb6b6f, 0x7c3f58]);}
#[no_mangle]fn update() {    SNAKE_GAME.lock().expect("game_state").update();}
Using external libraries

lazy_static is an external library:

we have to declare inside the dependencies section of Cargo.toml, otherwise rustc does not know how to resolve it and will not compile our game.

[dependencies]buddy-alloc = { version = "0.4.1", optional = true }lazy_static = "1.4.0"

See also:

Drawing the Head#

But where is the head? You can pick a side. Either position 0 or position self.body.len() - 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:

// src/snake.r` inside `impl Snake {}` blockpub fn draw(&self) {    for &Point { x, y } in self.body.iter() {        wasm4::rect(x * 8, y * 8, 8, 8);    }
    wasm4::rect(self.body[0].x * 8, self.body[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.

We'll create a helper function inside src/palette.rs to confine the unsafe code.

// src/palette.rspub fn set_draw_color(idx: u16) {    unsafe { *wasm4::DRAW_COLORS = idx.into() }}

Then we'll invoke draw_color inside src/snake.rs:

// src/snake.rsuse crate::palette::set_draw_color;
// Inside inside `impl Snake {}` blockpub fn draw(&self) {    for &Point { x, y } in self.body.iter() {        wasm4::rect(x * 8, y * 8, 8, 8);    }
    set_draw_color(0x4);    wasm4::rect(self.body[0].x * 8, self.body[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.

// src/snake.rsuse crate::palette::set_draw_color;
// Inside inside `impl Snake {}` blockpub fn draw(&self) {    set_draw_color(0x43);
    for &Point { x, y } in self.body.iter() {        wasm4::rect(x * 8, y * 8, 8, 8);    }
    set_draw_color(0x4);    wasm4::rect(self.body[0].x * 8, self.body[0].y * 8, 8, 8);}

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

Snake with outline