Programming Games

Class 10 - Platformers

Play homework

Ennuigi

Celeste 2 (Pico-8) by Maddy Thorson, Noel Berry, Lena Raine, Kevin Regamey

Topics

Description

We’ll pull together all of our skills thus far to build a platformer. We’ll introduce physics and build complex levels for the player to explore.

Class notes

Character movement

The solution of making the player move is to change the players x and y.

To do that we need to make 2 variables named x and y, player = {x=0,y=0} after we do that we need to actually draw the sprite so we need to make a draw function.

player = {x=0,y=0}

function _draw()
 spr(1,player.x,player.y)
end

Now we need to change the player’s x and y position.

player = {x=0,y=0}

function _update()
 if btn(→) then
    player.x += 1
 end
 
 if btn(←) then
    player.x -= 1
 end
 
 if btn(↑) then
    player.y -= 1
 end
 
 if btn(↓) then
    player.y += 1
 end

function _draw()
 spr(1,player.x,player.y)
end

That’s it!

Ennuigi game

Spend some time with a depressed, laconic Luigi as he chain smokes and wanders through a crumbling Mushroom Kingdom, ruminating on ontology, ethics, family, identity, and the mistakes he and his brother have made.

Controls:

    left/right: walk around
    up: ruminate
    down: smoke

About the game:

This is a shot at a collection of ideas I had a few years ago, about looking critically at the universe of Super Mario Bros. in light of the total lack of explicit narrative in the original game in particular. Who are these strange men? What motivates them? By what right do they wreak the havoc they do on this strange place? What do they feel about where they are and what they’re doing?

And so, this is one lens through which to look at all that, with Luigi, the second brother, the also-ran, as a complicit onlooker, wandering now through some fractured, rotting liminal place in this strange world, reflecting on it all in scattered fragments.

Random

rnd( [limit or table])

Random generates a random number under the given limit, or returns a random element from a sequence.

The limit is non-inclusive (i.e. the random value will top out below the limit number). The default limit is 1.0.

The rnd() function returns a random number in a range. The result ranges from 0.0 up the given limit, but will never be the limit value itself. For instance, rnd(10) can return from 0.0 up to 9.99999, but will never return 10.

The random number includes a fractional part. To get a random integer, use flr(rnd(...)).

If a sequence-style table is passed instead of a numerical *limit>, the rnd() function will return a random element from the sequence.

If no limit or table is supplied, it will default to a limit of 1.0. Values returned will range from 0.0 up to 0.99999 (0x0.ffff).

The random number generator is initialized with an unpredictable seed value when PICO-8 starts. The generator may be initialized with an explicit seed using srand(), which permits a program to execute multiple times and always receive the same sequence of random numbers.

Working with ranges that don’t start at 0

To select a random number between a minimum and a maximum, call rnd() with the size of the range, then add the minimum:

scale    = rnd(20) - 10     -- a random number between -10 and 10
bomb.x   = 32 + rnd(64)     -- a random number between 32 and 96
die_roll = flr(rnd(6)) + 1  -- a random integer between 1 and 6

To select a random fraction with a fixed number of decimal digits, multiply the range by the number of values between whole numbers (e.g. 100 for two decimal digits), and then divide the result by the same amount:

earnings = flr(rnd(50 * 100)) / 100  -- between 0.00 and 50.00

Examples

print(rnd(20))       -- for example, 3.837

print(flr(rnd(20)))  -- for example, 17

-- note this is the same as rnd(0xffff)
print(rnd(-1))       -- for example, -1734.56 or 13744.63

print(rnd(split("hello",""))) -- h, e, o have 20% probability, l has 40% probability

print(rnd({5,6,7,8,21,10,31,256})) -- returns a random value from the table between table[1] and table[#table]

Graphics

Palette

PICO-8 starts up with both the draw and screen palettes set to the following indices & colors, with index 0 flagged as transparent in sprites and maps:

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

The draw palette may be altered by calling pal(draw_palette_index, screen_palette_index). This allows remapping sprite and map colors.

The draw palette’s transparency flags may be altered by calling palt(draw_palette_index, true/false). This allows cookie-cutter style transparency in sprites and maps.

The screen palette may be altered by calling pal(screen_palette_index, system_palette_index, 1). This allows rearranging the default colors, or swapping in some of the undocumented alternate colors in the range 128-143:

128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143

See Palette for many more details, as well as the specific RGB values for each palette color, which may be useful in external editors or on the web.

Functions

camera([x,] [y])
circ(x, y, r, [col])
circfill(x, y, r, [col])
clip([x,] [y,] [w,] [h])
cls()
color(col)
cursor(x, y)
fget(n, [f])
fillp([pat])
fset(n, [f,] [v])
line(x0, y0, x1, y1, [col])
pal([c0,] [c1,] [p])
palt([c,] [t])
pget(x, y)
print(str, [x,] [y,] [col])
pset(x, y, [c])
rect(x0, y0, x1, y1, [col])
rectfill(x0, y0, x1, y1, [col])
sget(x, y)
spr(n, x, y, [w,] [h,] [flip_x,] [flip_y])
sset(x, y, [c])
sspr(sx, sy, sw, sh, dx, dy, [dw,] [dh,] [flip_x,] [flip_y])

Building a simple platformer

--     == platforming engine ==
--     ==in 100 lines of code==
           
--the goal of this cart is to 
--demonstrate a very basic
--platforming engine in under
--100 lines of *code*, while
--still maintaining an organized
--and documented game. 
--
--it isn't meant to be a demo of
--doing as much as possible, in
--as little code as possible.
--the 100 line limit is just 
--meant to encourage people 
--that "hey, you can make a game'
--with very little coding!"
--
--this will hopefully give new 
--users a simple and easy to 
--understand starting point for 
--their own platforming games.
--
--note: collision routine is
--based on mario bros 2 and 
--mckids, where we use collision
--points rather than a box.
--this has some interesting bugs
--but if it was good enough for
--miyamoto, its good enough for
--me!
           
--player
p1=
{
    --position
    x=72,
    y=16,
    --velocity
    dx=0,
    dy=0,
   
    --is the player standing on
    --the ground. used to determine
    --if they can jump.
    isgrounded=false,
   
    --tuning constants

    jumpvel=3.4,
}

--globals
g=
{
    grav=0.2, -- gravity per frame
}

-- called 30 times per second
function _update()

    --remember where we started
    local startx=p1.x
   
    --jump 
    --
   
    --if on the ground and the
    --user presses x,c,or,up...
    if (btnp(2)             or btnp(4) or btnp(5))
     and p1.isgrounded then
     --launch the player upwards
        p1.dy=-p1.jumpvel
    end
   
    --walk
    --
   
    p1.dx=0
    if btn(0) then --left
        p1.dx=-2
    end
    if btn(1) then --right
        p1.dx=2
    end
   
    --move the player left/right
    p1.x+=p1.dx
   
    --hit side walls
    --
   
    --check for walls in the
    --direction we are moving.
    local xoffset=0
    if p1.dx>0 then xoffset=7 end
   
    --look for a wall
    local h=mget((p1.x+xoffset)/8,(p1.y+7)/8)
    if fget(h,0) then
        --they hit a wall so move them
        --back to their original pos.
        --it should really move them to
        --the edge of the wall but this
        --mostly works and is simpler.
        p1.x=startx
    end
   
    --accumulate gravity
    p1.dy+=g.grav
   
    --fall
    p1.y+=p1.dy

    --hit floor
    --
   
    --check bottom center of the
    --player.
    local v=mget((p1.x+4)/8,(p1.y+8)/8)
   
    --assume they are floating 
    --until we determine otherwise
    p1.isgrounded=false
   
    --only check for floors when
    --moving downward
    if p1.dy>=0 then
        --look for a solid tile
        if fget(v,0) then
            --place p1 on top of tile
            p1.y = flr((p1.y)/8)*8
            --halt velocity
            p1.dy = 0
            --allow jumping again
            p1.isgrounded=true
        end
    end
   
    --hit ceiling
    --
   
    --check top center of p1
    v=mget((p1.x+4)/8,(p1.y)/8)
   
    --only check for ceilings when
    --moving up
    if p1.dy<=0 then
        --look for solid tile
        if fget(v,0) then
            --position p1 right below
            --ceiling
            p1.y = flr((p1.y+8)/8)*8
            --halt upward velocity
            p1.dy = 0
        end
    end
end

function _draw()
 cls() --clear the screen
 map(0,0,0,0,128,128) --draw map
    spr(1,p1.x,p1.y) --draw player
    print("v1.0 2016 - @matthughson",14,0,1)
end

Code homework

For homework this week you will play Pico-8 platformers and begin to make your own.

Credits