Tutorial: Pumpkin Patch

October 1, 2020 garrison tutorial, game development, ims213 8 minutes, 32 seconds

Tutorial: Pumpkin-patch

Use "classes" and "objects" to make a pumpkin-patch

cartridge
download this code

Be sure to put the code, above, in your PICO-8 carts directory. You can reveal that directory from inside PICO8 by typing: folder on the command line.

The code shared below is relatively straight-forward, but there's no getting around the fact that it makes use of almost everything we've learned so far. See what you can do to make sense of it. Take it apart, simplify it, break it.

This code features:

  1. Pumpkins;
  2. A local array (pumpspr) to store the addresses of my pumpkin-sprites on the Pico Pumpkin Sprite Sheet;
  3. A local array (gardenrow) that stores the rows that I want to fill with pumpkins;
  4. An empty global array, pumpkinpatch, in which I'll store all of my pumpkin-objects;
  5. A pumpkin "class" that puts several different variables into every pumpkin I create, but also installs a single function ("method"): gourdshow(), which I call whenever I want that pumpkin to reveal itself;
  6. The use of a For-In-All-Do iterator. (For-In-All-Do is just a name I made up, but it fits.) Like a For/Do loop, For-In-All-Do takes advantage of PICO's penchant for repetitive tasks. But while For/Do lets you specify where to start counting and where to stop, For-In-All-Do is an iterator: A tool that automatically moves from the start to the end of a table. It makes working with table contents easier.

Table for One

Here's what it looks like: Imagine I've got a table called "High_Scores". It looks kinda like this:

High Scores
Peter 980
Lloyd 940
Dre 901

Each of those scores is really a Key-Value pair (KVP). For example, Peter: 980 means "Peter" is the key (the index, the sorting column, the ID, or whatever you care to call it) and "980" is the value, the data, or whatever you care to call it. Now, I'm keen to get all of those scores. Here's one way to do it:

FOR POINTS IN ALL (HIGH_SCORES) DO
        PRINT(POINTS)
END

Result: Prints three lines: 980,944,900.

That looks like a lot, but it is really minimally complex: HIGH_SCORES is the table you want to read; POINTS is the arbitrary name you give to the VALUE (as in KEY:VALUE) that PICO takes from every KEY it opens up. Remember that the computer doesn't understand the data you store -- it just holds on to it for you. So use a name that makes sense to you. This works, for example, but isn't really very helpful:

FOR BREAKFASTMUFFIN IN ALL (HIGH_SCORES) DO
        PRINT(BREAKFASTMUFFIN)
END

Result: Prints three lines: 980,944,900.


Right. Let's get to the code!

function _init()
 -- create a table to house
 -- all of our pumpkins-objects

 pumpkinpatch={}

 -- sprite addresses (16x16)
 local pumpspr={1,3,11,13,33,35}

 -- only show sprites on these rows
 local gardenrow={2,4,6}

 -- we'll do this loop twelve times,
 -- creating 12 pumpkins.  We'll add
 -- each pumpkin we create to the
 -- pumpkinpatch table.

 for n=1,12 do

        -- to keep things neater,
        -- I'll do the math and assign
        -- variables before I actually
        -- build my pumpkin.  As always,
        -- I don't have to break it down
        -- this far, but it can make it
        -- easier to read.

        --Start w. a random number 1-6,
        --then get that element number
        --from the `pumpspr` array.  There!
        --Now we've got a pumpkin sprite
        --address from the spritesheet.
        --Spritesheets are usually very
        --messy, so the luxury of just
        --picking 1,2,3,4,5 or 6 is rare.
        --Also:  Note that I'm using
        --16x16 sprites, so it would be
        --1,3,5,7,9, or 11 at best...

        local id=flr(rnd(1)*6)+1
        local myspr=pumpspr[id]

        --X location: random (1-8) x 16
        --(every sprite is 16 pixels wide,
        --this organizes things in a grid,
        --just an aesthetic choice.)

        local myx = flr(rnd(1)*8)*16

        -- random number (1-5) which
        -- is used to pick a `gardenrow`.
        -- I multiply the result by 16

        id = flr(rnd(1)*5)+1
        local myy = gardenrow[id]*16

        -- now assemble our pumpkin
        -- for eventual storage
        -- note that this part isn't
        -- "just" code to execute:  We're
        -- storing Key-Value pairs.  So
        -- the "sprite=" part of the first
        -- line specifies the KEY (the index).
        -- Same deal for the other lines.
        -- That's imperative.  It allows
        -- us to do this:
        -- print(mypumpkin.sprite) or
        -- print(mypumpkin["sprite"]) or
        -- print(mypumpkin.x), etc.

    mypumpkin =
        {
                sprite=myspr,
                x=myx,
                y=myy,

                -- start function
                -- section here

                showpumpkin=function(self)
                        -- shadow-making
                        color(5)
                        ovalfill(
                                self.x+7,
                                self.y+11,
                                self.x+21,
                                self.y+17
                                )
                        -- draw 16x16 sprite
                        spr(
                                self.sprite,
                                self.x,
                                self.y,
                                2, 2)
                --now we end the showpumpkin( ) function
                end
        }
        -- end of our pumpkin "class"

        -- now save that pumpkin into our little database
        -- so that we can return to build other pumpkins

        add(pumpkinpatch, mypumpkin)

        -- loop back and do it again, 12x
        end

        -- that's it.  We're out.
end

You'll need to include a function _update(), but I haven't used it all this time. But function _draw() does have some work to do.

pumpkin_draw.md

function _draw()

        -- clear the screen
        -- lay down a green background
        -- in case my map has holes, etc.
        cls(3)

        -- show my map
        map(0,0,0,0,16,16)

        -- now the big reveal:
        -- iterate over the twelve
        -- KVPs stored in my pumpkinpatch
        -- Each one contains a pumpkin
        -- object, stored in the exact same
        -- way, but with a few different
        -- values stored in their
        -- variables (sprite, x, and y)
        -- I "iterate" over the table,
        -- naming each pumpkin I pull out
        -- "gourd" (it doesn't matter what
        -- I call it), and then telling that
        -- gourd to call its built-in
        -- showpumpkin function to run.
        -- It will grab each and every
        -- pumpkin I've stored:  I don't
        -- need to tell it how many, etc.

        -- One tricky part:  Below, I use
        -- a COLON instead of a DOT because
        -- I need PICO8 to do all of the
        -- complicated stuff it does to
        -- restrict itself to operating
        -- on each gourd individually.  But
        -- don't worry about that.  Just
        -- remember when you ITERATE over a
        -- TABLE of OBJECTS, use a COLON
        -- instead of DOT SYNTAX.

        for gourd in all(pumpkinpatch) do
                gourd:showpumpkin()
        end

end

alt