Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Advanced State

The State global variable that is created in usagi init and used in the examples is nothing special to Usagi. It’s just a global variable that’s a table that gets set in _init and persists across live reloads. You could call this variable whatever you want. You don’t have to use it either. But the big benefit of having a global variable in _init is that you can make changes to your game and keep the current level, the player’s location, etc. all in memory while changing the behavior of the game and rapidly testing your changes.

You will read in various places that global variables are bad news, and while that’s generally true, for smaller games, it’s actually a really convenient way to keep track of your game’s data. Especially since Usagi is single-threaded and intended for smaller games. By having a data structure that keeps track of your game’s data, you can make a clear separation in your game’s code that actually makes it less risky and easier to maintain. This recipe covers a few of the best practices I’ve used to make working with global State and Usagi’s live reload as enjoyable as possible.

Separate Data from Behavior

State is best used as a simple bucket for data: numbers, strings, booleans, and tables that consist of those. When you start assigning functions or instances of objects, the live reload doesn’t work because what’s in memory doesn’t refresh automatically with the changes.

If you have:

function _init()
  State = {
    player = {
      x = 10,
      y = 10
    }
  }
end

function _update()
  update_player()
end


function update_player()
  State.player.x += 1
end

You have your data (the player’s position) separate from the behavior (update_player). If you change 1 to 2, Usagi automatically picks that up. You might be tempted to even put 1 into speed so you reference it as State.player.speed. This is fine in practice, but you’d need to hard reload (Ctrl + R) to refresh that value if you change it in _init. When values stabilize, that’s a fine thing to do.

You might be tempted to do something like this:

function _init()
  State = {
    player = Player.new(10, 10)
  }
end

function _update()
  State.player:update()
end

But I am pretty sure Usagi’s live reload won’t pick up on the changes to Player if you revise the update() instance function. You’re mixing your data with your behavior and putting instances in State isn’t going to play nicely. I also think this is difficult to debug and reason about.

How I write my Usagi code is actually to pass around specific data structures and not rely on the global State whenever it’s possible. So something like:

function _init()
  State = {
    player = {
      x = 10,
      y = 10
    }
  }
end

function _update()
  update_player(State.player)
end


function update_player(player)
  player.x += 1
end

That way my code doesn’t have to reach into State to get what it knows. It makes the code more resilient for functions to just receive the data it needs versus all of it.