ilo Pikowe (Pico-8) la, mi pali e toki lawa ilo tawa ni: sina ken kepeken sitelen pona!! toki lawa ni li kepeken nimi Token 111 taso, li ken ante, li kepeken nimi P8SCII. sina li wile ala kepeken e sitelen Spritesheet !! ni li pona tawa sina la, o pana e ni tawa jan ante !!
I've made code for Pico-8 that allows you to use sitelen pona!! This code only uses 111 tokens total, is customizable, and uses P8SCII, meaning you won't have to use a single slot in your spritesheet for it!! If you liked it, please feel free to share!!
Today I’m going to cover the concept of state and how I handle it in PICO-8,
State Based Design is an approach to game development in which you define how an object or group of objects behaves in certain contexts. In the example above, my tetris game has a title menu, in which you can select three options, each of which can take you to different states, the third of which brings you to another menu where you can choose options for how the game state will be initialized.
Tetris was an early game for me, but I found the concept of state based design incredibly helpful when making games, as more complex games have even more complex states. So let’s break down a simpler example, and look at the code.
Here we have a simple menu/title which waits for any button to be pressed before starting the game (which simply moves a pixel in a circle!) In the corner we have a simple statistic display, which you’ll find common in my games. Now let’s look at the code to see what’s happening.
_ichor = { version = '1.0.0', order = {}, modules = {}, update = function() _ichor('update') end, draw = function() _ichor('draw') end, add = function(name,module) _ichor.modules[name] = module return _ichor end, set_order = function(state,order) _ichor.order[state] = order return _ichor end, set_state = function(key) _ichor('destroy') _ichor.state = key _ichor('init') return _ichor end, __call = function(this, call) for key in all(this.order[this.state]) do local module = this.modules[key] if module[call] then module[call](module) end end end, } setmetatable(_ichor,_ichor) _update = _ichor.update _draw = _ichor.draw _ichor .add('cls', { draw = cls }) .add('stats', { info = { function() return 'ichor version '.._ichor.version end, function() return 'state '.._ichor.state end, function() return 'mem '..(stat(0)/2048)..'%' end, function() return 'cpu '..(stat(1)*100)..'%' end, }, init = function(this) menuitem(1,'toggle stats', function() this.show = not this.show end) end, draw = function(this) if this.show then for i, call in pairs(this.info) do local label = call() local y = i*6-5 rectfill(1,y,#label*4+1,i*6+1,0) print(label,2,y+1,7) end end end }) .add('game',{ init = function(this) printh('initializing the game!') this.tick = 0 end, update = function(this) this.tick += .01 end, draw = function(this) pset(sin(this.tick)*10+64,cos(this.tick)*10+64,7) end }) .add('menu',{ update = function(this) if btnp() > 0 then _ichor.set_state('game') end end, draw = function(this) print('press any button to start!',12,64) end }) .set_order('game',{'cls','game','stats'}) .set_order('menu',{'cls','menu','stats'}) .set_state('menu') _ichor.modules.stats.show = true
Now I understand this is larger than my previous examples, but bear with me, it’s less intimidating than one may think. So let’s take this a chunk at a time.
First, we have the table _ichor. This handles state, and is a common staple among games I create. In it are a few more tables, modules will be a table of tables which may contain the methods init, update, and draw. order is also a table, with state names as our keys, and a table as our values, containing key names of our modules to be used when under the corresponding key.
That’s... a mouthful. But again bear with me here. Immediately after initializing _ichor, we bind PICO-8′s update and draw functions to _ichor’s, this is so that we can let _ichor decide what to draw. After that we get into actually using ichor.
The first thing we start doing with _ichor is start adding modules, and we do that using _ichor’s add function. We pass it a name, and a table. this table doesn’t necessarily need to define all of init, update, and draw, but it can. In the first case, we simply provide it draw, and give it cls, which is PICO-8′s internal function for clearing the screen.
Next, we give it a module named stats. This module has a table inside called info, this isn’t used by _ichor, but is used by itself. Because _ichor passes the module to itself when calling init, update, or draw, we have access to itself and can use this info table. In stats we utilize the init function to add a menuitem to PICO-8′s pause menu to toggle stats on and off. We don’t utilize update, because for a true statistics info, we’re gonna want those calls to execute last, in our draw step. so in draw, we do that.
Next is the actual menu and game the user interacts with. I won’t explain this as there’s not much new going on, so let’s skip over to those set_order calls.
The _ichor function set_order takes a state name, and a list of module names. it essentially declares and initializes a state. The table provided executes in that order.
After setting up the order and states, we now set a state in _ichor by calling set_state and passing a state key. _ichor will call init for each module and update its state variable so that they call on update and init.
I’m not entirely sure if I’ve explained this in enough detail but if you’ve got any questions feel free to leave me an ask and I can try to explain anything I’ve really missed.
An impossibly awesome and infinitely replayable Pico-8 Tetris/ Dungeon Builder/ Roguelike you're about to fall in love with. I'm proud if I'm the one introducing you to this for the first time. You're welcome.