Class 10 - Platformers
Play homework
Topics
- Pico-8 API
- Controlling a character
- Working with randomness
- Sprite editor
- Map editor
- A basic platformer
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.
- Create a character sprite in the sprite editor. At this point, you only need a single sprite to represent the character, but in the future you can add a walk cycle animation.
- Create background graphics sprites that will be used in your game. Things you may need: wall, floor, platform, exit (a flag?).
- Use the map editor to draw the level with your sprites
- Add your code to control the character and move through your level
- Add jumping and any other mechanics (gravity)
- Create an end state when the player reaches the goal
Credits
- Ennuigi by Josh Millard
- Celeste Classic by Noel Berry, Matt Thorson
- platformer code by mhughson CC BY SA