Richard's Arcade Tips #2 - Drawing custom stuff with Images

Hey folks!

It’s times for the second edition of Richard’s Arcade Tips! First off, I want to mention that I have created a new tag for this series of posts. If you’re interested in seeing all the entries, you can check out the page for the arcade-tips tag!

With that out of the way, let’s move on to today’s topic!

Programmatically drawing to images

Sprites are nice, but sometimes you want to draw directly to the screen. This can be tricky to do in Arcade, so I’m going to show off a few methods for doing this as well as some example programs that demonstrate why it’s useful. Let’s dive in!

Technique #1: Drawing to the background image

This is the easiest way to get something on the screen. Inside the “Scene” category, you will find a “background image” block. You can draw to this just like any other image using the blocks in the Image category.

For example, this code:

arcade-screenshot (10)

Draws this dumb picture of a grinning box:

image

Hurrah.

While this approach is easy to use, it has a major drawback in that it will always be drawn below everything else. The background image is essentially at a z-index of -Infinity, so it can never be above any Sprites in your game (or the tilemap for that matter). Because of this limitation, I never really use this approach for doing anything beyond little algorithmic drawing demos.

Let’s look at something more practical!

Technique #2: Use a Sprite

This approach is a little clunky to use, but it gets the job done! The idea here is to create a Sprite with a transparent image and draw to that image as if it were the screen. For example, let’s take our above example and use it to make a Sprite:

Now if we were to run that code, we get the same result as our original example:

image

However, the nice thing about this approach is that because you’re creating a Sprite, you can do Sprite things with it like controlling it’s z-index or making it bounce around:

draw-to-sprite

This makes this a good choice if you want to programmatically draw anything that should be subject to the physics engine. For example, this is how I created the player character in Rhombus Rush.

The downside of this approach is that because you’re creating an empty image for the sprite, you’re taking up extra memory for your game. For small sprites that won’t really matter, but if you’re creating a bunch of bigger sprites then you might get some out-of-memory errors on hardware. This is only really an issue on hardware (the browser doesn’t have memory issues).

Technique #3: Use the jwunderl/arcade-sprite-util extension

This third technique requires an extension, but it’s way more powerful than the first two.

Inside @jwunderl’s wonderful arcade-sprite-util extension there is a “render on z-index” block:

The “screen” parameter that you see in that block is a reference to the game screen. You can draw to it just like any other image and the z-index argument lets you control when the draw happens.

The benefit to this approach is that the screen is automatically cleared every frame of the game so you don’t need to worry about erasing things you already drew. Additionally, because this is called as part of the game render step, the screen will also have the pixels of everything below the given z-index already present when your code is invoked. That means you can do fun things like messing with the pixels on the screen (more on that below).

However, be aware that if you want to do anything with this block that is affected by the game camera, you’re going to have to do a little extra math to make it work (see the last example for tips on this)

Alright, with that out of the way, let’s take a look at some examples!

Example #1: Drawing fractals

The background image technique is only really useful for doing classic programmatic drawing examples like drawing fractals, so here’s one of those! Marvel at the symmetry!

Example #2: Screen effects

As mentioned above, one of the benefits of using the “render on z-index” block is that because your drawing code executes as part of the game loop’s render step, you can mess with the screen’s pixels in addition to drawing new things. That means you can do cool full screen effects!

To show this off, let’s create this effect which remaps the colors of part of the screen:

remap-screen

First, we’re going to create a function that takes in an image and changes the colors inside a rectangle:

To change all the pixels in the rectangle, we can use a couple of for-loops to loop over all of the xy coordinates:

(note: to change the variable in a for-index loop, create a new variable with the name you want and then right click on the variable in the loop and select the variable you created)

Inside that loop, we want to read the old color from our image and then remap it to a new color. To choose our new color, we’re just going to add 1 to whatever the old color was:

And finally, let’s call our function inside the “render on z-index” block:

Here’s the finished code:

This example is a really simplified version of the technique used by the shader and lantern extensions!

Example #3: Screen effects with z-index

Now, let’s say we have an effect that we want to apply to the background but not the sprites in our game. We can control which sprites are affected by our effect by using the z-index.

Let’s start with this heat wave effect:

desert-heat-simple

In arcade, higher z-indices are drawn on top of lower z-indices. For screen effects, that means that all sprites with a lower z-index will be affected by the effect while sprites with a higher z-index wont.

In the GIF below, we have three “sprites”

  • The house, which has a z-index of 49
  • The heat wave effect, which has a z-index of 50
  • The duck, which has a z-index of 51

desert-heat

Because the duck has a higher z-index, it isn’t subject to our wavy effect. The house’s z is lower, so it is affected.

Here’s a link to that code:

And here are some other useful z-indices to know about:

  • Background images are always drawn first (z = -Infinity)
  • Tilemaps are drawn at z = -1
  • The default z-index for sprites is z = 0
  • The HUD from the info category (scores, lives, countdown, etc.) is drawn at z = 100

Example #4: Respecting the camera

The “render on z-index” block is also super useful for drawing custom UI. For example, let’s say I have a game where I want to draw a line showing which direction a sprite is facing. This is super easy to do with “render on z-index” and a little trigonometry:

And here’s what that code looks like when it runs. Note the line that is showing the direction the apple is moving:

apple-follow

This works well for our little example game that doesn’t use the camera, but let’s say I add a tilemap to the game and have the camera follow the duck. Now we get this:

apple-camera

As soon as the camera starts scrolling, the line stops being drawn where the apple is.

To fix this issue, we need to take into account the current camera position. When you want to draw something to the screen that is affected by the camera, always subtract camera left from the x coordinates and camera top from the y coordinates.

Here’s what our adjusted render block looks like:

And here’s the finished code:

32 Likes

thats a lot of words

5 Likes

This is so cool! I love using these blocks to make lasers

9 Likes

Too Bad I’m Not Reading Them

3 Likes

this hurt brain but is cool :sunglasses:

5 Likes

I love the heatwave effect! So cinematic! :movie_camera:

4 Likes

Wait I can probably code a custom shader myself

3 Likes

@Luke that’s the idea!

5 Likes

what if i want to make multiple instances of an object? such as using tables in pico-8?

1 Like

@Jedi not entirely sure what you mean by that… can you be a little more specific?

3 Likes

so lets say i want to make a starfield, it would be practically impossible to write each pixel out for each star, so you would use a table in pico8 (basically an array for handling multiple objects) so would it be possible to do something like that in makecode?

1 Like

if you’re in javascript, you can use objects which are basically the equivalent of tables in lua:

const stars: any[] = [];

function makeStar() {
    let myStar = {
        x: randint(0, 160),
        y: randint(0, 120),
        speed: randint(20, 70),
        color: 1
    };

    return myStar;
}

for (let i = 0; i < 100; i++) {
    stars.push(makeStar());
}

scene.createRenderable(0, () => {
    for (const star of stars) {
        star.y -= star.speed * game.currentScene().eventContext.deltaTime

        screen.setPixel(star.x, star.y, star.color)

        if (star.y < 0) {
            star.y = 120;
        }
    }
})

or you can use classes, which are a little more efficient:

class Star {
    public x: number;
    public y: number;
    public speed: number;
    public color: number;

    constructor() {
        this.x = randint(0, 160);
        this.y = randint(0, 120)
        this.speed = randint(20, 70);
        this.color = 1;
    }
}

const stars: Star[] = [];

for (let i = 0; i < 100; i++) {
    stars.push(new Star());
}

scene.createRenderable(0, () => {
    for (const star of stars) {
        star.y -= star.speed * game.currentScene().eventContext.deltaTime

        screen.setPixel(star.x, star.y, star.color)

        if (star.y < 0) {
            star.y = 120;
        }
    }
})
2 Likes

in blocks, you can use the microsoft/arcade-block-objects extension (be aware though, it’s a bit of a monster)

3 Likes

There are two types of people:

or

5 Likes

ok, thank you!

2 Likes

next time you do a DVD Screensaver YOU MUST WAIT TILL IT HITS THE CORNER! It’s required

3 Likes

I’ve learned my lesson :skull:

1 Like