PICO-8



Main Menu Cartridge



This lesson continues where Main Menu Version 1 leaves off.

Version 1 introduces and explains what game states are and what a state machine is. So if you have questions about that, go back and watch that video:

Main Menu - Version 1

This version is more efficient than the first, so we actually recommend using this in your game, after understanding why it is better and how to use it.


After you made this main menu, change it up! Try some of the challenges below to spark some ideas!





Challenges!

After following the lesson, take on some of these challenges to test your knowledge and skills!

1. If you followed Version 1 already, copy that game, and convert it to use this version.

Improved Main Menu!




Explanation of Code!


  • 0. What Needs Improving?

      In Main Menu - Version 1, we introduced the simplest build of a state machine, and how to control the game states. This required only basic knowledge of programming so it is a good starting point for beginners. It is easy to read and understand, but not as easy for the code to run.

      Efficiency is what every programmer works towards. Efficiency is a quality of wasting the least amount of resources. In general, resources are whatever is used in any type of process.

      For example, if you are making a poster for a class project. Then you need to find or buy resources such as: poster board, glue, paper, scissors, etc. And you could buy 3 of everything, make 3 different posters, and choose the best one. But that will waste a lot of time and materials spent making the other 2. So that is not efficient.

      It would be much more efficient to plan out your poster and know exactly which materials you need and how much. Then you should only get what you need, and use it all in meaningful ways so you have no wasted materials or time.


      A good programmer will consider the use of the computer's resources and when we talk about a computer's resources, we usually mean Memory. There are basically 2 types of memory, stored memory and active memory.

      Stored Memory is the total amount of data that can be saved. Every file and folder you have on your computer takes up space. And every computer has a limit of how much space it has for saving data. PICO-8 limits our games to a very small space compared to modern video games, so we are forced to keep our games nice and simple.

      Active Memory is the amount of data that can be running. Every program you have open right now is using your computer's active memory. Some programs use a lot and you might notice your computer slow down and take time to load. While some programs use very little active memory. They are either small programs, or very efficient programs, and you will notice your computer loads and runs them easily.


      Now then, what does all that have to do with making games in PICO-8?

      Well there are so many ways to write code that does the same thing in the end. But the trick to being the best programmer is figuring out what is the most efficient way.






      Sometimes, we sacrifice the most efficient way for better readability and understanding. For example:

      s=1
      h=10
      a=5
      d=3
      

      Those are very efficient variable names because they are only 1 letter each. But what does it mean?! The person who wrote it might be able to understand it, but someone else will have to waste a lot of their time trying to figure it out.

      sprite=1
      health=10
      attack=5
      defence=3
      

      Now these variables are a tiny bit less efficient, but way more understandable! So most of the time, we should balance these two to find the best way, which isn't always the most efficient way.






      Version 1 is a good example of writing game states for understanding but not for efficiency. And here's what is really not efficient about it.

      --main update and draw
      function _update()
          if scene=="title" then
              update_title()
          elseif scene=="menu" then
              update_menu()
          elseif scene=="game" then
              update_game()
          elseif scene=="gameover" then
              update_gameover()
          end
      end
      
      function _draw()
          if scene=="title" then
              draw_title()
          elseif scene=="menu" then
              draw_menu()
          elseif scene=="game" then
              draw_game()
          elseif scene=="gameover" then
              draw_gameover()
          end
      end

      We don't quite get this far in the lesson, but if you continued to build your game this way, you would add each game state in another elseif statement. And this could get very long. So not only does it waste a lot of code which takes up storage memory, but it also takes a lot of active memory to constantly run each of these checks every tick of the game. So that gets expensive and uses too many resources.


      Luckily there are more efficient ways of doing the same thing and skipping those checks completely!






  • 1. How to Make it More Efficient?

      PICO-8 needs _update and _draw functions to run. So we wrote:

      function _update()
      end
      
      function _draw()
      end
      

      Then we used a variable to store the active game state name and check that variable to run the correct game state. Even though that is easy to follow, it is not efficient, but what it is doing looks something like this:

      So basically we are just setting _update to use our active game state's update function. And the same for draw.






      But here is something you might not have known:
      Function names are actually just variables that hold functions instead of just a word or a number.

      What does this mean??

      Well it means that we can treat function names just like variables, because that's what they are! (this is unique to the Lua programming language)

      So we can create an empty function:

      function myfunction()
      end
      

      And now myfunction is a variable, and we can set that variable to whatever we want, for example, a copy of another function's code.

      function myfunction()
      end
      
      function _init()
          myfunction=print --copies print() code into myfunction()
          myfunction("hello world")
      end
      
      --prints hello world
      

      You should already know the built in function print() and usually the first thing we all learn to do in any language is write "hello world" onto the screen. print("hello world") is how we normally do that in PICO-8, but because we copied the function from the variable print into our own variable myfunction, that means we can then write myfunction("hello world") to do exactly what print() would normally do!






      Understanding that is very important to understanding how this more efficient State Machine works.


      So now that we know how to copy functions from one function name (variable) to another function name (variable), we can skip the if elseif checks from Version 1 like this:

      We could start by writing the required update and draw functions.

      function _update()
      end
      
      function _draw()
      end
      

      But doing that, is really just creating two variables named _update and _draw. And we would normally just create variables by writing variable_name= right?

      So by just writing _update=, we can creating a variable named _update, which is even more efficient than above.

      Then we can jump right into setting the update and draw variables to hold the game state function code we want to run.

      _update=update_menu
      _draw=draw_menu
      

      Notice how we don't use any parentheses () because we are treating them as the variables they are, and not trying to run them as functions. In fact, when PICO-8 starts the game, it looks for variables named _update and _draw and tries to run them as functions automatically.


      It is really important to understand all of that, to be able to understand what we will do next. So if that all makes sense, let's start building the more efficient state machine!






  • 2. Initialize the First Game State

      function _init()
          --variables
          x=63
          y=63
      
          --first game state
          init_menu()
      end
      
      What is a variable?

      By setting up your variables inside of the _init() function, you can easily reset your game by calling _init(), and the variables here have access to any custom functions you write. That might come in handy later so it's a good practice.

      So far that is the same as Version 1, but the next part is new. We call a function that will be named init_menu meaning "initialize menu", but you can name it whatever you want. start_menu or open_menu are also good names.

      This function will be used to start the menu game state by setting _update and _draw to the code written in update_menu and draw_menu functions.

      function init_menu()
          _update = update_menu
          _draw = draw_menu
      end
      

      By doing it this way, we are able to skip the entire "main update and draw" tab from Version 1 that held the long if and elseif checks. To fully understand how this code works, read sections zero and one above.

      So now when we start this game, PICO-8 will first run the _init() function which will set the variables and then run the init_menu() function. That function will copy whatever code is saved in update_menu and draw_menu into the new variables _update and _draw. When PICO-8 finished _init(), it will then look for variables named _update and _draw, and run them as functions. And so they will run whatever code we write in update_menu() and draw_menu() functions.

      So that's next.


      But let's also create an init_game() function to change the main _update and _draw functions to run the game.

      function init_game()
          _update = update_game
          _draw = draw_game
      end
      

      And these init_something functions become our State Machine!

      So for each Game State you want to create, just write another one of these functions that change the main _update and _draw variables to hold your specific update_state and draw_state code.






  • 3. Update and Draw Functions for each Game State

      Here are the specific update and draw functions for each game state that we want to have. These are the exact same as Version 1.

      The only difference is how we change the active Game State. Instead of using the "scene" variable, now we just need to run the init_state function for the state you want to switch to.

      So in update_menu, when the user presses button X, then we run init_game.

      function update_menu()
        if btnp(❎) then
          init_game()
        end
      end
      
      function draw_menu()
          cls()
          print("press ❎ to start",30,63)
      end
      

      And in the update_game function, when something happens and we want to run a new Game State, then we run that init_state function. Just like Version 1, we are only giving an example of a Main Menu state and a Game state, so in the game state, we are also simply checking if the user presses the X button again, which will return to the Main Menu by running the init_menu() function.

      function update_game()
          if btn(⬅️) then x-=1 end
          if btn(➡️) then x+=1 end
          if btn(⬆️) then y-=1 end
          if btn(⬇️) then y+=1 end
      
          if btnp(❎) then
              init_menu()
          end
      end
      
      function draw_game()
          cls()
          spr(1,x,y)
      end
      

      You can organize your code in PICO-8 using the tabs however you would like. In the Full Code section below, we organized all of the init functions in the first tab, then all of the update functions in the second tab, and finally all of the draw functions in the third tab.






  • Full Code! (Template)

      NOTE: This is not the exact code created at the end of the video tutorials. This is more of a template that you can use to start building your game using this style of game states. We thought that would be more useful.

      --inits
      
      function _init()
          --global variables
          x=63
          y=63
      
          --first state
          init_menu()
      end
      
      function init_menu()
          --menu variables
      
          --set state
          _update=update_menu
          _draw=draw_menu
      end
      
      function init_game()
          --game variables
      
          --set state
          _update=update_game
          _draw=draw_game
      end
      
      -->8
      --updates
      
      function update_menu()
          if btnp(❎) then
              init_game()
          end
      end
      
      function update_game()
          if btn(⬅️) then x-=1 end
          if btn(➡️) then x+=1 end
          if btn(⬆️) then y-=1 end
          if btn(⬇️) then y+=1 end
      
          if btnp(❎) then
              init_menu()
          end
      end
      
      -->8
      --draws
      
      function draw_menu()
          cls()
          print("press ❎ to start",30,63)
      end
      
      function draw_game()
          cls()
          circfill(x,y,5,11)
      end
      

      -->8 is a new tab in Pico-8. They don't appear inside the Pico-8 code editor.






  • Example Video: Adding a Main Menu to an Existing Game


font