Author: lodomo

  • Devlog 4: Colliders Part 2 (Tilemap Cont’d)

    So that was a doozy to figure out. I never really considered that every single entity might have different reactions to collisions, especially if there’s not a true “physics” system. I don’t have any interest in making a physics engine for this game, everything can be handled at the object level.

    SO COLLISIONS. Let’s look at some of the code.

    Tilemap Check

    function TileMap:getTileAt(point)
        local row = math.floor((point.y - self.__origin.y) / self.__cell_size) + 1
        local col = math.floor((point.x - self.__origin.x) / self.__cell_size) + 1
    
        local cell_origin = Point(
            (col - 1) * self.__cell_size + self.__origin.x,
            (row - 1) * self.__cell_size + self.__origin.y)
    
        if self.__debug_draw then
            gfx.drawRect(
                cell_origin.x, cell_origin.y,
                self.__cell_size, self.__cell_size)
        end
    
        return self[row][col], cell_origin
    end

    This makes the tile a big target. Say our tile size is 8×8, that gives 64 different points that will return the same tile. Simple arithmetic, super fast. You can see I made a flag called “debug_draw” that will come in handy in a minute. That lets me see what tiles are being requested by any entity. It just draws an empty rectangle if that tile is polled.

    Points to check

    Each object is going to have slightly different rules, but lets look at something that might be a character or an enemy. I want to check for x movement, y movement, and then x/y movement. What do I mean by this?

    The movement above is exaggerated, we’re typically moving just a few pixels each frame. So I loop through the vertical line, and place points uniform tile-length apart. Then move those points in the direction of the velocity, and then remove points that wouldn’t be in the “green”

    Then loop through the horizontal, place points uniformly, move, cull what wouldn’t be magenta.

    Finally just take the velocity distance from the “moving” corner.

    When I do a final write up I’ll make this all much prettier with equations and stuff, but hopefully that makes sense.

    Resolving a Collision

    So we do our check. There’s 4 possible outcomes.

    • There was no collision, move the object.
    • There was a collision on the x-axis, wait to resolve
    • There was a collision on the y-axis, wait to resolve
    • There was no collision on the x or y axis and there was a diagonal collision.

    If there will be a collision on the x axis, for this example we got a little bit of work to do. We need to find the distance between our object and our tile. This has two possible collisions was the object moving left, or right? If it’s moving left, and our origin is in the top left, we need to move

    x of the tile origin + width of the tile – current x (before moving)

    tile origin = (10, 10)
    tile width = 8
    object origin = (20, 20)
    object width = 16
    object speed = 5
    
    10 + 8 - 20 = 2
    
    We need to move only 2 pixels to the left, not 5

    if we’re moving right…

    x of the tile origin – width of the object – current x (before moving)

    tile origin = (20, 20)
    tile width = 8
    object origin = (2, 2)
    object width = 16
    object speed = 5
    
    20 - 16 - 2 = 2
    
    We need to move only 2 pixels to the right, not 5

    The same logic applies for y movement. The real key is to resolve the collisions AFTER you check. Don’t move X then check Y. Check one, check the other, and then resolve.

    If there is a diagonal collision, apply both the X and the Y logic.

    If you want to peek at the code, this is the specific folder in the repository: https://github.com/lodomo/PlaydateResources/tree/main/Colliders/TileMapPart2b

    So far this only handles solid vs not-solid blocks. If there were say “spikes” that whole detection, resolve loop would need to be changed. This has turned out to be much trickier than I anticipated. The cool thing though is that it works.

    Here’s a little recording I did of it in action. The barriers are placed at random every time the program starts so I could test it.

    Legend:

    Dots: Where the player is polling to move
    Hollow Squares: Activated Tiles
    Solid Squares: Barriers
    Big Square: Player

    I think I need to turn this into a nice formal lesson/video/code along thing. This is mostly just a stream of consciousness again.

    Until Next Time,

    Lodomo

  • DevLog 3: Colliders Part 1

    Spoiler Warning: By the end of this first post, I still haven’t actually handled any collisions.

    This post isn’t going to be as straight forward as my previous posts. I’ve rewritten this a few times, and decided just a consciousness dump might be good for me, and good for others to understand that things aren’t always clear or easy. I might want to have a different tag for stream of consciousness posts like this.

    I’m going to start turning this whole project into a bunch of teeny programs to prove concepts. My project is getting cluttered with junk and it doesn’t even really do anything, yet. The good news is that there’s going to be a git repository that sort of grows and changes as the project goes on, and maybe turns into a tool someone can use to learn from, or at very least, something for me to look back on and be like “what was I thinking?”

    As I said in a previous post, I’m trying to implement as much as I can without using the Playdate SDK. If you’re here to learn how to use the Playdate’s built in colliders, head over to the official SDK. The link changes often so your best bet is to just go to https://play.date/dev/ and seach “Sprite collision detection”, and while you’re at it, you can look at the library they based their collision detection on. https://github.com/kikito/bump.lua . The SDK is wonderful, I’m here’s to learn how to do as much of this as I can myself.

    Now that we got that out of the way, let’s see how bad I am at implementing this myself. I’ve read a bulk of the bump library, and I’ve cried when reading Game Engine Architecture by Jason Gregory so I can probably do this. I also read the entire source code for Celeste on the Pico8.

    My original version of this game, the engine handled all this for me, so I’m feeling a little overwhelmed at having such a blank slate, freedom is scary.

    The Plan

    This game doesn’t require anything too fancy, so I’m going to start out with two types of collisions.

    Tilemap

    AABB (Axis-Aligned Bounding Box)

    That’s it.

    Tilemap

    Tilemap is going to work like this. The screen / level will be split into a grid of 8×8 pixels. Each tile will have a flag associated. These flags will be “solid”, “not-solid” to start out. I’ll likely add different things to have “disappears when player touches it”, and one way platforms, special static blocks and things like that, but we’ll burn that bridge when we get there.

    The benefit of a tilemap is having to do less collision checks.

    TILEMAP IMAGE

    Pros: It doesn’t matter if there’s a million tiles, or 1 tile, the amount of collision checks is constant. Now, that image is a little misleading, you only need to check for collisions in the direction you’re moving, and might need to check for more than 1 collision in a specific direction if the object is bigger than a tile. That being said, it’s still better than checking against every single possible thing in a scene.

    Cons: Objects can’t move (less than the size of a tile).

    Now take a minute, think about how you would implement this. If you haven’t read the post on bit, bytes and data types https://blog.lodomo.dev/2025/04/20/cs-bits-bytes-and-data-types-in-lua/ check it out. There’s loads of ways we can abstract the map.

    What if each tile just had a number? that gives us 4,294,967,296 options for each tile. If we could store it in chars, that would be nice, but iterating through strings in Lua isn’t very efficient. Let’s check out some tests.

    Let’s do a little test on a single screen.

    X_BOOL_50x30 = {}
    for i = 1, 30 do
        X_BOOL_50x30[i] = {}
        for j = 1, 50 do
            X_BOOL_50x30[i][j] = false
        end
    end
    
    X_NUM_50x30 = {}
    for i = 1, 30 do
        X_NUM_50x30[i] = {}
        for j = 1, 50 do
            X_NUM_50x30[i][j] = 0
        end
    end
    
    X_STRING_50x30 = {}
    for i = 1, 30 do
        X_STRING_50x30[i] = "00000000000000000000000000000000000000000000000000"
    end
    
    A screenshot of a dataset preview showing three labeled datasets:

    X_BOOL_50x30: 1530 items, total size 33,712

    X_NUM_50x30: 1530 items, total size 33,712

    X_STRING_50x30: 30 items, total size 3,082

    So now there’s 50 tiles * 30 tiles * 30 tables (one for each row) for the bool and num tables. That takes a whopping 33,712 bytes of memory for a single screen, just for flag data.

    The table of strings is just 30 items, 30 strings each one 50 chars long. It’s less than 1/10th of the size of the number/boolean tables and still has 255 possible tile options per tile.

    Let’s test how well lua can go through all these and see if that overhead is worth it or not.

    I loop through and get the value of every data member, and do nothing with it, this is purely just a table lookup. Then it prints to the screen what the average time for 1500 (50×30) lookups.

    The image shows a yellow handheld gaming device, the Playdate, with performance benchmark results displayed on its screen. The screen displays the following text:

Bool: 3.384577 ms  
Num: 3.53881 ms  
String: 13.25907 ms

    Ooof. Welp, I’m a little concerned. For strings, that’s still a very very small amount of time per look up (0.0085~ ms) but it’s still about 4x slower than numbers.

    “The real problem is that programmers have spent far too much time worrying about efficiency in the wrong places and at the wrong times; premature optimization is the root of all evil (or at least most of it) in programming.” – Donald Knuth

    Look, Don, I just don’t want this slowing me down later that my tile maps are causing problems.

    I have 16,000,000 bytes to play with in RAM, a full screen tilemap is 33,000? maybe it’s worth the overhead to have faster lookups, and just be better about loading on the fly. The time to render each frame is around 20-30 milliseconds, and that is a much more precious resource.

    Now onto the collisions…

    The Code

    So I think in a final version the logic will be a bit different to keep objects to a more defined job they handle, but this will be something that works. Make it exist, then make it good.

    Creates a tile map that has colliders 1 tile in from the edge, all the way around.

        for row = 1, ROWS do
            TILE_MAP[row] = {}
            for col = 1, COLS do
                if row == 2 or row == ROWS - 1 or col == 2 or col == COLS - 1 then
                    TILE_MAP[row][col] = 1
                else
                    TILE_MAP[row][col] = 0
                end
            end
        end
    

    Color in those tiles

        for row = 1, #TILE_MAP do
    for col = 1, #TILE_MAP[row] do
    local tile = TILE_MAP[row][col]
    if tile == 1 then
    gfx.fillRect((col - 1) * CELL, (row - 1) * CELL, CELL, CELL)
    end
    end
    end

    Now we need a moving object.

    PLAYER = {
        x = 120,
        y = 120,
        width = 16,
        height = 16,
        velocity = {
            x = 1,
            y = 1,
        },
        draw = function(self)
            gfx.fillRect(self.x, self.y, self.width, self.height) -- x, y, width, height
        end,
        update = function(self)
            self.x = self.x + self.velocity.x
            self.y = self.y + self.velocity.y
        end,
    }
    

    Right now there is no tile checking, but I wanted to make sure this works. Test at every step.

        drawVelocity = function(self)
            local l_x = self.x
            local l_y = self.y
            local r_x = self.x + self.width
            local r_y = self.y + self.height
    
            gfx.drawLine(l_x, l_y, l_x + self.velocity.x, l_y + self.velocity.y)
            gfx.drawLine(r_x, l_y, r_x + self.velocity.x, l_y + self.velocity.y)
            gfx.drawLine(l_x, r_y, l_x + self.velocity.x, r_y + self.velocity.y)
            gfx.drawLine(r_x, r_y, r_x + self.velocity.x, r_y + self.velocity.y)
        end,
    

    Ok, so now these lines will be where the next frame will be. I made the speed 4 pixels per frame, going at a 45 degree angle down and right.

    Since we have the line of the direction were going it helps me visualize what I need to do, and then start to process that into logic. In psuedo code:

    If the velocity in the x direction is positive:
    check along the right side of the new position
    If the velocity in the x direction is negative:
    check along the left side of the new position
    If the velocity is the x direction is neutral:
    there cannot be new x-direction collision, do nothing.

    If the velocity in the y direction is positive:
    check along the bottom of the new position
    If the velocity in the y direction is negative:
    check along the top of the new position
    If the velocity in the y direction is neutral:
    there cannot be new y-direction collision, do nothing.

    AAAND this is where I think I’m going to end it this week. I’d rather give you all little updates as I finish them than nothing. Keeps me honest to keep working.

    Until Next Time,

    Lodomo

  • DevLog 2: Implementing Classes

    DevLog 2: Implementing Classes

    For a platformer, the most important element is how it feels. If the controls aren’t responsive or intuitive, nothing else matters. Good controls require solid physics. Good physics require accurate collisions. Accurate collisions require a well-structured architecture. Right now, I’m focused on building that structure from the ground up.

    Lua wasn’t taught at my university, so I’m learning it alongside the Playdate SDK. While the SDK is well-made, I’m intentionally minimizing my use of it. This is not a criticism of Panic’s work. I want to implement the systems myself to fully understand each part.

    Classes in Lua

    I’m using to using object-oriented programming, and Lua doesn’t have the idea of classes as part of it’s core structure. I need to abstract classes manually. It makes sense for most things in side the game to be objects. I don’t need to stick strictly to this paradigm, but I think it’s a great place to start.

    I initially wanted to just write closures to simulate object, wrapping everything into a function of functions. The code is super clean, there’s no silly colons. It would not matter at all on a modern system, but dealing with only 16mb of ram, I’d rather get rid of this overhead right away.

    I used object.lua from the Playdate SDK’s CoreLibs as a reference. It provides a working class system, but includes methods I either didn’t need or didn’t fully understand. I’ve kept the bare minimum and will push code up and down as needed.

    Class = {}
    Class.__index = Class
    
    function Class:new()
        local obj = setmetatable({}, self)
        return obj
    end
    
    function Class:print()
        for k, v in pairs(self) do
            print(k, v)
        end
    end

    So this makes a “Class” table in the global space. Using Class:new() will clone the data members of the table (in this case there are none), and give reference to the “print” method so that only gets created once.

    Every single class will derive from “class” so every single object will have a obj:print() to dump all the data members and methods if I need to use it for debugging.

    Point = {}
    Point.__index = Point
    setmetatable(Point, { __index = Class })
    
    -- Class Data Members
    function Point:new(x, y)
        local obj = Class:new()
        setmetatable(obj, self)
        obj.x = x or 0
        obj.y = y or 0
        return obj
    end
    
    -- Move a point along the x axis
    function Point:move_x(x)
        self.x = self.x + x
    end
    
    -- Move a point along the y axis
    function Point:move_y(y)
        self.y = self.y + y
    end
    
    -- Move along both axes
    function Point:move(x, y)
        self:move_x(x)
        self:move_y(y)
    end
    
    -- Draw the point as a single pixel, or define a radius.
    function Point:draw(radius)
        if radius == nil then
            return DrawPixel(self.x, self.y)
        end
    
        return FillCircleAtPoint(self.x, self.y, radius)
    end

    So far:

    • Class
      • DeltaTime (For counting time between frames / button presses, etc)
      • Point
        • Vector
        • Shapes
        • MainCharacter
      • ButtonState (For a single button)
      • Controller State (For the entire playdate controller)

    Lua’s dynamic typing makes passing functions down super easy. If “Point:move(x, y)” exists, it will be available to any derived class (like Vector, or Shape)

    Right now, MainCharacter is a point, (plus some more). I might introduce an “entity” class between the two to have enemies and NPCs, but until I need to pull that out I will keep it where it is.

    The “Template” project is free and open source, hosted here:
    https://github.com/lodomo/PlaydateResources

    This repository will continue to evolve.

    Extras

    The import script is carefully crafted. SDK CoreLibs are all imported at the top, commented out, and enabled as needed. No need to go hunting for what is available. Then I put some handy constants into the _G table, like PI.

    To streamline testing, I created a Makefile. Running “make run” compiles the game, and launches it directly into the Playdate Simulator. I work in the terminal with Neovim, and Tmux so having a terminal-based workflow is the best for me.

    Next time: Basic Collision Detection!

    -Lodomo

  • Shorty: The Beauty of Lua Comments.

    This will be short and sweet. Anyone who is new to coding might know about leaving comments in your code. Those who have coded for awhile know it’s vital to leave comments to your future self at the very least.

    Another great use for comments is blocking out code from running. This is where the absolute magic of Lua comments come in.

    -- Whole Line Comment
    x = 10 -- Inline Comment
    
    print(x)
    > 10

    Any time there is two dashes, the rest of the line becomes a comment.

    There’s also the [[ ]] operator. Use [[ ]] instead of “ “ to make a multi-line string.

    long_string = [[For instance, on the planet Earth, man had always assumed that he was more intelligent than dolphins because he had achieved so much—the wheel, New York, wars and so on—whilst all the dolphins had ever done was muck about in the water having a good time. But conversely, the dolphins had always believed that they were far more intelligent than man—for precisely the same reasons.]]
    
    print(long_string)
    >For instance, on the planet Earth, man had always assumed that he was more intelligent than dolphins because he had achieved so much—the wheel, New York, wars and so on—whilst all the dolphins had ever done was muck about in the water having a good time. But conversely, the dolphins had always believed that they were far more intelligent than man—for precisely the same reasons.

    Now, let’s combine these elements to make a multi-line comment

    --[[ This is a long comment
    anything in between these brackets is commented out
    x = 10
    and now we can end the long comment.]]
    
    print(x)
    > nil

    THE MAGIC

    --[[ Start your block comment with this
    and make the very last line look like this:
    --]] 

    Now why would we put dashes inside the comment block?

    --[[ This is code that will not run. It's a big block comment
    x = 10
    x = x + 10
    --]] 
    
    ---[[ This is code that WILL run.(Everything after -- is commented out)
    y = 10
    y = y - 10
    --]] This is just a blank commented line.
    
    print(x)
    > nil
    
    print(y)
    > 0

    You can create big chunks of blocks that “turn on and off” by adding/removing a SINGLE dash from the start of the block.

    Absolutely beautiful design.
    10/10
    no comments

    Until Next Time,

    Lodomo

  • Devlog 1: Inspiration, Planning, and Scope

    Devlog 1: Inspiration, Planning, and Scope

    I’ve had the idea for this game rattling around my head for years. It’s part of the reason I decided to go get my Computer Science degree. A little overkill, I know, but that’s how I roll.

    As soon as I saw the Playdate I knew that was the console it needed to be on.

    I made a proof of concept for a gamejam a few years ago. It takes place in the middle of the story, and is more of a “filler episode”

    You can play the original proof of concept here:
    https://lodomo.itch.io/bouldermagenightmare

    Inspiration

    The earliest memories I have of video games are with my cousin and playing The Legend of Zelda on NES. He also had a Turbografx. There was a game on there called “Parasol Stars” which was an odd little game I’ve never seen in the wild since. It was an off shoot of the game BubbleBobble but with some really neat mechanics.

    You were a boy with an umbrella, and you would strike enemies with it to stun them. Once stunned you could pickup and throw the enemies at other enemies. You could also grab water droplets to throw at enemies. If you pick up enough droplets you could flood the stage. Certain droplets had elemental characteristics.

    But, just another arcade game won’t be enough. Boulder Mage needs a story.

    I can’t stand games that take a long time to get going. The game needs to start almost immediately. In arcade games, that’s easy. You just throw a level at a player and they start to figure it out. For something story-driven it’s a very fine line of hand holding. In Legend Of Zelda: A Link to the Past, your call to action is almost immediate. You’re woken up, and need to go save the day.

    Game-feel is also very important. Maddy Thorsen has an AMAZING breakdown on the feel of Celeste. https://www.maddymakesgames.com/articles/celeste_and_forgiveness/index.html I plan on implementing every single one of these that applies to Boulder Mage.

    My last key inspiration for gameplay is the Chao Garden / Tamagotchi / Chocobos / Yoshi.

    I’ll get deeper into this as development goes. But what if you had to raise your own Yoshi-esque chickens? And then that became it’s own little sub-game? It might be a big idea to pull off for Playdate, but I think I can do it.

    Planning

    I’m a big planner. I’ve got notebooks all over the place for projects, daily tasks, and so far it’s treated me well. I’ve adopted something from my time learning as a Master Training Specialist to make solid plans to execute projects. Some might think it’s too much, but it really helps me when I don’t know what to do next.

    Here’s a template you can adapt to your own project: hop.lodomo.dev/plan-doc

    You don’t need to fill out every section. You don’t need all the answers. But get some of that out so when you’re getting further into development you always have a path forward.

    I won’t be releasing the entire Boulder Mage planning document because it has lots of secrets for the game. You’ve already read my inspiration sections. Here’s what my game features is starting to look like.

    Game Features

    • Main Abilities
      • Move horizontal
        • Ground Dash (Learned)
      • Jump – With some horizontal control after take off
        • Air Dash (Learned)
        • Double Jump (Learned)
      • Swing Staff
      • Pogo Staff (Learned)
      • Grab Item
        • Carry Item
        • Throw Item
      • Ground Pound (Learned)
    • Meditation Zones
      • Areas that require careful platforming to lead to an unlockable.If you look close you can see I drew this when I was going by “BathThief”
    • Collectables
      • Pythagoras Beans, Plant them to gain access to secretsPythagoras thought beans were human souls.
      • Elemental Gems, Not required to beat the game, but give extra perks to attacks
    • Fast Travel
      • Rufus (NPC) gives access to fast travel portals. Some are to anywhere, some are location specific. See map.

    Scope

    Most of the scope is going to be in my planning document, but I also like giving myself a clear picture of “what I want” and “what I can’t give up”

    What I can’t give up:

    • JUICY controls. If this isn’t in the game, it’s not worth making.
      • This is 100% what I’m coding first, before anything else. Can I get a square to move how I want it to feel.
    • 4 areas, 4 skills, 4 extra quests, 4 bosses,
    • Engaging puzzles
    • Chicken raising. One egg, One Mount minimum.
    • World building without force feeding the info to the player.

    What I want:

    • A world full of NPCs that give life to the world
    • Interactive environments
    • Full Chicken breeding, racing, Chao garden style mini-game

    Scope creep can DESTROY you. It’s either going to delay your project, burn you out, or make you hate everything you’ve been working on.

    It will make every project feel sisyphean. Sometimes you gotta let the rock roll.

    Why Playdate?

    I think it’s important to give yourself restrictions. I found my scope spiraling out of control with my other ideas. This has some really strict rules.

    • 2 Colors,
    • 400×240 (or smaller) Resolution,
    • 2 buttons,
    • A crank,
    • A d-pad,
    • An accelerometer,
    • A mic
    • and 16MB Ram.

    It will come with it’s own challenges, but thinks are less likely to grow out of control. Plus, how cool is the Playdate?

    Until next time,

    -Lodomo

  • CS: Bits, Bytes and Data Types (in Lua)

    CS: Bits, Bytes and Data Types (in Lua)

    I have an idea. No wait. I lost it. Wait. There it is, again.

    The mighty binary digit. (Shortened to “bit”). Physically, it is the state of a tiny switch. It’s the corner stone to tricking rocks to think for us.

    On it’s own, it doesn’t mean anything. However, we can use them to express different types of data. You can join them to represent anything from a single character to literally all of the internet.

    When you do this, you create a data type as in “What type of data should these bits represent?” We’re going to build, in binary, some of the core data types in Lua.

    If you wish to make an apple pie from scratch, you must first invent the universe. – Carl Sagan, 1980

    It might feel like I’m going full Carl Sagan on you, but having knowledge that these concepts exist will help you when you’re developing in the future. I promise.

    Every coding language has it’s own data types.
    Sometimes they’re obvious like in C where you declare something as an int (integer).
    Sometimes they’re a little more hidden like in Lua when you just say x = 10
    (that 10 is an integer just like in C)

    This article is going to cover Lua specific data types to help you understand the Playdate SDK (Software Development Kit). We’ll explore how these data types are built from their underlying structure, and some of the quirks of Lua for those who have some previous coding experience.

    If you’re here just to learn some information about Lua, jump to the TL;DR at the bottom.


    Boolean

    When you look at a single bit alone, we can start to look at our smallest data type, the boolean. A single bit is all you need for a boolean. They can be either true or false. It doesn’t seem like much at first, but the boolean is incredibly powerful. Let’s look at my favorite boolean value in real life (it involves candy).

    I hope they have full sized butterfingers – Dracula, probably

    The boolean in this case is the porch light. What information can you get from just this porch light being on?

    • The person who lives here is home.
    • The person who lives here celebrates Halloween.
    • They still have candy to give away.

    In code, it could look something like this:

    if date == "October 31" and porch_light == true then
        trick_or_treat()
    end

    Today isn’t going to cover coding, but there are 5 sneaky boolean values in the code above.

    1. porch_light is a boolean. It’s either true or false.
    2. true is a boolean.
    3. date == “October 31” resolves to a boolean. The statement is true or false.
    4. porch_light == true resolves to a boolean. The statement is true or false.

    In Lua, the Boolean type has 2 states denoted by true or false.

    Challenge!

    Comment if you find the 5th boolean!

    Leave a comment

    Special Note on Lua

    In Lua, boolean values are treated a little different than most other languages. If you’re coming from a coding background and are here to learn Lua, using comparative operators gets a little weird. When comparing values, they need to be of the same type, and boolean values are explicitly the value “true” or “false”

    > print(0 == false)
    false
    > print(0 == true)
    false

    “Is the number 0 equal to the boolean false? No”
    ”Is the number 0 equal to the boolean true? Also no.”

    Number

    In Lua, there are two sub-types of number. They’re either integers, or floats.

    • Integers are whole numbers.
    • Floats are any number with a decimal point in it.

    That’s all you REALLY need to know. But if you’d like to dive a little further and invent integers, read ahead! If not, skip down to Strings.

    Integers

    Let’s invent how to build an integer from scratch, so we can appreciate what Lua (and other coding languages) is doing for us under the hood. We have the power of the boolean at our disposal, how could we use that to count?

    Imagine, for a moment, you live in a world with a currency shortage, and you will go to jail if you own two of the same coin.

    • You mow your neighbor’s lawn, and she gives you a single coin.
    • You have one $1 coin.
      • That’s not enough to buy a Playdate. Let’s mow some more lawns.
    • You mow another lawn, and you get another $1 coin.
      • Uh-oh. You’re about to go to jail, so you go to the bank.
    • You give them your two $1 coins, and they give you one $2 coin.
      • You still don’t have enough for a Playdate. You mow another lawn.
    • You get a new $1 coin. That’s OK!
      • You own one $2 coin, and one $1 coin. Total Money: $3
    • You mow another lawn. You get another $1 coin.
      • Uh-oh. You have two $1 coins again.
    • Let’s make another trip to the bank.
      • You give your two $1 coins in and get a second $2 coin.
      • Uh-oh. You have two $2 coins.
      • You trade those in for a $4 coin.

    I need to mow 11000111 lawns! – You

    Congratulations! You now understand how to convert Base 10 counting into a Base 2 binary system.

    Each bit is assigned a number, and if it’s “on / true”, it adds that up to the total.

    001 = 0 + 0 + 1 = 1
    010 = 0 + 2 + 0 = 2
    011 = 0 + 2 + 1 = 3
    100 = 4 + 0 + 0 = 4

    Now, if we were to combine 8 bits into a byte, how high can you count with 8 binary digits? In other words, what is 11111111?

    Know the answer? Want help? Leave a comment!

    Still struggling with base 2? Imagine having to count in base 60, then base 60 again, then base 24, then base 7, then base 30 (except sometimes it’s base 31, or 29, or 28) Oh, and also base 12. And base 365. You already do this every day. Seconds, Minutes, Hours, Days, Weeks, Months, Years. If you can do that, you can do anything.

    Floats

    Representing floating point numbers is a little more complicated to represent with human readable binary. If you want to deep dive into it, let me know in the comments and I’ll do a follow up article.

    For a beginner, just know that floats are any number with a decimal point.
    3.14, 1.618, or 2.0 are all floats.

    Challenge!

    You know how to count in base 2 now, how would you count in base 16?

    Special Note on Numbers for Playdate

    Numbers are signed 32-bit in Playdate.
    The integer range is [-2,147,483,648 to 2,147,483,647]
    If you go outside this value, you will get overflow.

    String

    So far we’ve turned binary into boolean values. Boolean values into numbers. What if we take numbers, and turn them into characters?

    Let’s invent the character and assign them to numbers! It might make sense to start at 1 with familiar letters, and if you were going to recreate characters today, you might. But let’s look at how they’re actually implemented.

    The alphabet starts at 65

    A = 65 = 01000001
    B = 66 = 01000010
    …
    Z = 90 = 01011010

    Understanding this won’t be terribly useful for Playdate since there’s no keyboard. But if you end up journeying into the WASD territory for making games, these codes can be quite helpful.

    So, what is a string?

    Now that we have characters, we can combine them into words! It’s just a collection of characters. We define them in Lua by putting them in “double”, or ‘single’ quotes.

    Challenge!

    Use what you know about binary, and this ascii chart to decide the strings below:

    Easy Mode – Numbers → Characters → String
    110 111 119 32 116 97 99 107 108 101 32 104 97 114 100 32 109 111 100 101

    Hard Mode – Binary → Numbers → Characters → String
    01000001 01100011 01101000 01101001 01100101 01110110 01100101
    01101101 01100101 01101110 01110100 00100000 01010101 01101110
    01101100 01101111 01100011 01101011 01100101 01100100 00100001

    Leave a comment

    Special Note

    It’s important to know that the number 123, is not the same as the string “123”.
    123 in binary is 01111011
    ”123” in binary is 0110001 0110010 0110011

    123 does not equal “123”

    Intermission

    Rest and Reflect

    Wow, that was a lot. Take a seat by the fire for a moment.

    You’ve gone from a bit → boolean → numbers → characters → strings.

    You don’t need to carry this in your pack for the rest of your journey. Leave it in the chest at camp, and just know it’s there if you ever need it for the final boss.

    We’re done looking at binary. We’re done building the core data types. Translating into binary no longer makes sense from a human perspective. Trust the tiny magic rocks in your computer to do the work. The last section of this article is just a little taste of the final base data types in Lua, and then you can be on your way to greater things.

    UserData

    UserData is a complex one. It’s how Lua and the C language interact. Sometimes Lua isn’t fast enough to do the work we need it to do. You can write C code to do the heavy lifting.

    This is an advanced topic, but just understand it’s raw binary data that needs to be encoded, and/or decoded.

    Function

    A function is a block of code that is designed to complete a task. The way to define the rules of what a function should and shouldn’t do is up for debate, and has entire books written about it.

    For the sake of the beginner, a function makes it so you can do something again and again without copy and pasting code.

    function square(x)
        return x * x
    end
    y = square(10)
    print(y)
    > 100
    
    function bark()
        return print("WOOF!")
    end
    bark()
    > WOOF!

    Here we have two functions.

    1. The square function will return a number times itself.
    2. The bark function will print WOOF! to the screen

    Challenge!

    Play a game, or use a program on your computer.
    What are some functions you think are happening under the hood?

    Special Note on Functions for Lua

    Functions are treated like “first class” citizens. That means you can put functions into variables, you can return functions, you can treat them like any other data. That might not mean a lot to beginners, but as you get the hang of things, it’s a super powerful tool at your disposal.

    Table

    Tables are the beauty of Lua. Not only are they an extremely powerful data type, they are also the only data structure in Lua.

    We can use tables to be our very own custom data types. We can use tables to hold a bunch of similar values (like maybe all the names of your pets?).

    ALL Tables are a collection of “key:value” pairs.

    character = {
        name = "Boulder Mage",
        health = 3,
        has_hat = true,
        has_staff = false,
    }
    print(character.name)
    > Boulder Mage
    print(character["name"]
    > Boulder mage

    This table has 4 entries, a string, a number, and two boolean values. If you create a table with keys, you access those values by table.key or table[“key”]. If you create a table without keys, things become a little different.

    my_cats = {
        "John Wick",
        "Hugo",
        "Big Fat Freddy",
    }
    
    print(my_cats[3])
    > Big Fat Freddy
    

    In the example above, we create a table with no keys. We access the values by order, in a bracket[ ].

    Challenge

    Try to make a table that would be the data needed for a square.
    I bet you could think of a few different ways!

    Special Note on Tables for Lua

    I can’t know how to hear anymore about tables!

    Tables are NOT arrays. Tables are tables. When you don’t give a key for the table, it assigns keys in order from 1, 2, 3 and so on. They do not start at 0. There is no pointer arithmetic because it’s not an array. The data is not guaranteed to be contiguous. Don’t fret if this doesn’t make sense, yet. I will be doing a whole article just on tables.

    Nil

    Nil is something you probably haven’t come across before even if you’ve coded in other languages. It’s very specifically it’s own data type.

    It’s almost, but not quite, entirely unlike zero.

    • It is NOT zero.
    • It is NOT false.
    • It is NOT an empty string.
    • It is NOT an empty table.
    • It is NOT a function.
    • It is NOT anything.
    • It IS nothing.

    It is used to represent the absence of a value. You might use nil to check if a variable exists, or if a table contains a certain key. You also can set a variable to nil to remove it from memory.

    Special Note on Nil for Playdate

    If you set something to nil, the garbage collector will start to remove it from memory. Might happen right away, might not. Much like in real life, you can’t control the garbage collector. They march to the beat of their own drum. Setting things to nil can affect performance and cause games to lag because it’s cleaning up the mess.

    Premature optimization is the root of all evil — Donald Knuth.

    Don’t worry about it, just know it is there and that it might be a cause of your lag spikes and then you can worry about optimizing.

    Thanks for reading, be in touch!

    If you need clarification on anything, please comment! I’m happy to help.

    I wonder what would happen if rocks really could think on their own…

    TL;DR: Data Types

    • Boolean
      • Can be “true” or “false”
      • You cannot compare other data types to boolean values.
      • 0 does not equal false
    • Numbers
      • There’s one data type for Float and Integers. Floats have a decimal.
      • For Playdate specifically, they are 32 bit.
    • Strings
      • You can make a string with “double” and ‘single’ quotes.
    • UserData
      • This is the data type when you’re talking to the C language.
    • Functions
      • Make it so you can reuse code.
      • First class citizens. You can pass functions around just like any other data.
    • Tables
      • Tables are THE data structure in Lua. Access by key.value or key[index]
      • Tables are 1 indexed if no key is provided.
    • Nil
      • Nothing is nil except nil because nil is nothing.

    Lodomo.Dev is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.

    References

    Lua Docs – https://www.lua.org/docs.html
    Lua Reference Manual – https://www.lua.org/manual/
    Playdate SDK – https://play.date/dev/
    Ascii Table – https://www.asciitable.com/

  • Devlog 0: git commit -m “Initial Post”

    My name is Lorenzo. I’d love for you to join me on my journey deeper into game development, and I hope you learn something from me along the way as well.

    I’m at the end of my Bachelor’s degree in Computer Science this spring. Before going back to college, I was an instructor for four years. I’m deeply passionate about teaching, and learning.

    Lodomo.Dev is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.

    This Substack has a two main goals. The primary goal is a development log for Boulder Mage: The Parasol of Destiny, my upcoming game for PlayDate! Secondary, its a space for exploring story telling, game development, computer science, digital art, and more.

    If you’re interested in building games, deepening your understanding of programming, or just enjoy seeing ideas take shape from the ground up, you’ll feel right at home here.

    If you subscribe, you’ll get:

    • Weekly updates on Boulder Mage: The Parasol of Destiny (working title)
    • Lessons on Computer Science through the lens game development. From ‘What is a bit?’ to Polymorphism. Learn agnostic to coding language, and then see examples in Lua, and/or C.
    • Pixel art tips using Aseprite.
    • Open-source planning docs, design deep dives, and discussions on scope (and scope creep!)
    • The practice of wabi-sabi in design.
    • Thoughts on GenAI — where it helps, where it hurts, and when it’s ethical
      (To those who know, that em dash is intentional)
    • Who/What are my inspirations? Hint: Mostly Shigesato Itoi.
    • Book suggestions for further reading on all these topics
    • Discord announcements for when I’m streaming development.

    The format for these posts will be mostly text and images, but I will make the occasional video, and the more you support me, the more time I’ll be able to dedicate to video versions of all my posts.

    Why subscribe?

    Everything I create will always be open source. That doesn’t mean I won’t retain copyright on my IP, but you will be able to use most of the code with little restrictions. All licenses will be available for each project on GitHub.

    Subscribers get:

    • Bonus artwork
    • Aseprite key giveaways
    • Easier access to all code bases.
    • Deep discounts on the game when it launches on itch.io (or compile it yourself!)
    • Your name in the credits
    • Other stuff I haven’t come up with, yet!

    When Boulder Mage wraps up, I’ve already got the next project lined up.


    That was a lot, so what IS Boulder Mage?

    What if there was a boulder who is also a mage?

    At it’s heart, this game will be an homage to Parasol Stars. It’s going to be part metroidvania, part platformer, part Chao Garden, and all of my heart.

    If you want a taste, here’s my entry to LOSPEC Gamejam that won second place:

    https://lodomo.itch.io/bouldermagenightmare


    Thanks for reading. Now, let’s build cool stuff together.

    – Lorenzo