Wolfe3D [Lexaloffle Blog Feed]https://www.lexaloffle.com/bbs/?uid=49909 Building a PICO-8 Beatemup part 1 <h1>Building a PICO-8 Beatemup part 1</h1> <h3>Introduction and Aknowledgements</h3> <p>Hello and thanks for reading. This post will be an explanation of the basics of my Ninja Turtles beatemup engine. I'm going to try and explain every single step along the way so this will be a fairly long post. You should probably have some familiarity with basic development but I'll make this as understandable as possible across skill levels.</p> <p>This blog post obviously owes a lot to a lot of people but much of this specific workflow was derived from these two posts:</p> <p><a href="https://www.lexaloffle.com/bbs/?tid=45114">Saving Tokens</a> by <a href="https://www.lexaloffle.com/bbs/?uid=47206">drakeblue</a> </p> <p>and </p> <p><a href="https://www.lexaloffle.com/bbs/?tid=49047">Using _𝘦𝘯𝘷 in PICO-8</a> by <a href="https://www.lexaloffle.com/bbs/?uid=38916">slainte</a>.</p> <p>Please read both if you have not. Neither is a prerequisite, but you'll be glad you did.</p> <h3>Help Wanted</h3> <p>I&rsquo;m not a programmer by trade so I&rsquo;m looking forward to learning from the community by explaining what I&rsquo;ve created to people who are smarter than me, and receiving feedback on improvements. I hope this helps anyone along their own path. Please feel free to use this code or anything you find in any of my stuff, really. I'd love to play your game some day.</p> <h3>Blog Purpose:</h3> <p>What I&rsquo;m going to do is rebuild the core of my engine step by step to a basic working state. I won&rsquo;t go all the way through the ninja turtle specific stuff, odds are that you're making something different enough that you don't need those steps. So, we're going to focus on things that can or must go into action games of this type regardless of IP. </p> <p>Ideally, you will follow along, making little changes and coming up with ideas I never could have. But most of these games have biped characters who can walk around, so we're going to start with that.</p> <h3>In This Post:</h3> <p>In this post we&rsquo;ll set up our code and get a little guy walking around.</p> <h2>Basic loop and character sprite</h2> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>function _init() o={} --object table end function _update60() end function _draw() end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>I&rsquo;m going to start with the basic _init, _update60, _draw, and then we&rsquo;ll do some sprites.</p> <p>We&rsquo;re going to be creating an object setup and &quot;o&quot; is going to be the table variable that every object falls under. We&rsquo;ll have many types of objects, including the players, enemies, and any kind of powerup or effect or game feature you can think of adding.</p> <p>Now we need a few sprites to work with and so I will show you the default skeleton rig I came up with.</p> <p> <table><tr><td width=32> <img src="https://www.lexaloffle.com/bbs/gfxc/49909_1.png" width=32 height=64> </td> <td valign=bottom> <a style="cursor:pointer;font-size:8pt" onclick=' var el = document.getElementById("gfxcode_49909_1"); if (el.style.display == "none") el.style.display = ""; else el.style.display = "none"; microAjax("https://www.lexaloffle.com/bbs/gfxc/49909_1.txt", function (retdata){ var el = document.getElementById("gfxcode_49909_1"); el.innerHTML = retdata; el.focus(); el.select(); } ); '> [8x16]</a> </td></tr> <tr><td colspan=2> <textarea rows=3 class=lexinput id="gfxcode_49909_1" style="width:640px;background-color:#fed;display:none;overflow:hidden; font-size:6pt;"></textarea> </td> </tr> </table> </p> <p>I&rsquo;ve drawn a circle filling the first sprite tile and this will be my character&rsquo;s head. I&rsquo;ve drawn a stick figure body in a neutral pose. I then copied and pasted those two in two rows of 8, filling half of the first page of the sprite sheet.</p> <p> <table><tr><td width=256> <img src="https://www.lexaloffle.com/bbs/gfxc/49909_3.png" width=256 height=128> </td> <td valign=bottom> <a style="cursor:pointer;font-size:8pt" onclick=' var el = document.getElementById("gfxcode_49909_3"); if (el.style.display == "none") el.style.display = ""; else el.style.display = "none"; microAjax("https://www.lexaloffle.com/bbs/gfxc/49909_3.txt", function (retdata){ var el = document.getElementById("gfxcode_49909_3"); el.innerHTML = retdata; el.focus(); el.select(); } ); '> [64x32]</a> </td></tr> <tr><td colspan=2> <textarea rows=3 class=lexinput id="gfxcode_49909_3" style="width:640px;background-color:#fed;display:none;overflow:hidden; font-size:4pt;"></textarea> </td> </tr> </table> </p> <p>My template uses 8x8 heads and 8x8 bodies. I have 8 different poses, and I have one row for forward/backward and one for right/left. </p> <p>Arcade beatemups usually don&rsquo;t have the forward/backward versions of the poses, so you could certainly cut that out of your plans to save some sprite space.</p> <p>You could do this with fewer poses, but this is going to let me have a character that can move and animate in a variety of different directions.</p> <p> <table><tr><td width=32> <img src="https://www.lexaloffle.com/bbs/gfxc/49909_5.png" width=32 height=128> </td> <td valign=bottom> <a style="cursor:pointer;font-size:8pt" onclick=' var el = document.getElementById("gfxcode_49909_5"); if (el.style.display == "none") el.style.display = ""; else el.style.display = "none"; microAjax("https://www.lexaloffle.com/bbs/gfxc/49909_5.txt", function (retdata){ var el = document.getElementById("gfxcode_49909_5"); el.innerHTML = retdata; el.focus(); el.select(); } ); '> [8x32]</a> </td></tr> <tr><td colspan=2> <textarea rows=3 class=lexinput id="gfxcode_49909_5" style="width:640px;background-color:#fed;display:none;overflow:hidden; font-size:6pt;"></textarea> </td> </tr> </table> </p> <p>I gave the character some eyes. You can see I&rsquo;ve made the left/right version directly below it by shifting one eye over. The head will switch between these two rows depending on the player movement so I want the aligned ones to match.</p> <p> <table><tr><td width=32> <img src="https://www.lexaloffle.com/bbs/gfxc/49909_6.png" width=32 height=128> </td> <td valign=bottom> <a style="cursor:pointer;font-size:8pt" onclick=' var el = document.getElementById("gfxcode_49909_6"); if (el.style.display == "none") el.style.display = ""; else el.style.display = "none"; microAjax("https://www.lexaloffle.com/bbs/gfxc/49909_6.txt", function (retdata){ var el = document.getElementById("gfxcode_49909_6"); el.innerHTML = retdata; el.focus(); el.select(); } ); '> [8x32]</a> </td></tr> <tr><td colspan=2> <textarea rows=3 class=lexinput id="gfxcode_49909_6" style="width:640px;background-color:#fed;display:none;overflow:hidden; font-size:6pt;"></textarea> </td> </tr> </table> </p> <p>I&rsquo;m keeping two special head poses in the first column. The top one is for facing directly up and the bottom is for diagonal walking down. </p> <p> <table><tr><td width=64> <img src="https://www.lexaloffle.com/bbs/gfxc/49909_7.png" width=64 height=64> </td> <td valign=bottom> <a style="cursor:pointer;font-size:8pt" onclick=' var el = document.getElementById("gfxcode_49909_7"); if (el.style.display == "none") el.style.display = ""; else el.style.display = "none"; microAjax("https://www.lexaloffle.com/bbs/gfxc/49909_7.txt", function (retdata){ var el = document.getElementById("gfxcode_49909_7"); el.innerHTML = retdata; el.focus(); el.select(); } ); '> [16x16]</a> </td></tr> <tr><td colspan=2> <textarea rows=3 class=lexinput id="gfxcode_49909_7" style="width:640px;background-color:#fed;display:none;overflow:hidden; font-size:6pt;"></textarea> </td> </tr> </table> </p> <p>Now I&rsquo;m making two body poses. The first one is a resting pose with both feet together. I&rsquo;m using a darker shade for one foot already to help delineate them. Then on the right I&rsquo;m making a walking pose. You can flip this sprite horizontally with the F key to see the animation. I have posed the arms a bit and made the back arm the darker shade. </p> <p> <table><tr><td width=64> <img src="https://www.lexaloffle.com/bbs/gfxc/49909_8.png" width=64 height=64> </td> <td valign=bottom> <a style="cursor:pointer;font-size:8pt" onclick=' var el = document.getElementById("gfxcode_49909_8"); if (el.style.display == "none") el.style.display = ""; else el.style.display = "none"; microAjax("https://www.lexaloffle.com/bbs/gfxc/49909_8.txt", function (retdata){ var el = document.getElementById("gfxcode_49909_8"); el.innerHTML = retdata; el.focus(); el.select(); } ); '> [16x16]</a> </td></tr> <tr><td colspan=2> <textarea rows=3 class=lexinput id="gfxcode_49909_8" style="width:640px;background-color:#fed;display:none;overflow:hidden; font-size:6pt;"></textarea> </td> </tr> </table> </p> <p>Here are the sideways versions. I made the feet 2x2 blocks instead of L blocks because the flip animation looks better. One downside of this character method is that using the Xflip to animate can be limiting.</p> <p>So at this point, the first half of the first page of my sprite editor looks like this:</p> <p> <table><tr><td width=256> <img src="https://www.lexaloffle.com/bbs/gfxc/49909_9.png" width=256 height=128> </td> <td valign=bottom> <a style="cursor:pointer;font-size:8pt" onclick=' var el = document.getElementById("gfxcode_49909_9"); if (el.style.display == "none") el.style.display = ""; else el.style.display = "none"; microAjax("https://www.lexaloffle.com/bbs/gfxc/49909_9.txt", function (retdata){ var el = document.getElementById("gfxcode_49909_9"); el.innerHTML = retdata; el.focus(); el.select(); } ); '> [64x32]</a> </td></tr> <tr><td colspan=2> <textarea rows=3 class=lexinput id="gfxcode_49909_9" style="width:640px;background-color:#fed;display:none;overflow:hidden; font-size:4pt;"></textarea> </td> </tr> </table> </p> <p>This is all I need for now. I'll start filling in those empty poses when I start adding more actions, like jumps and attacks.</p> <h2>Object and player setup</h2> <p>I'm going to be writing some functions and like the sprites, I'm pasting them into this doc at various stages. If you're putting this into your code, be sure to read carefully before copy/pasting because some of these will be bad examples or halfway steps and that kind of thing. </p> <p>Now we&rsquo;re going to make the function that we use to create new objects.</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>function new_o(f) --creates a new object with function f local obj={} --local table created add(o,obj) --obj is added to table called o return obj --send obj back end </pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>This function is going to be called whenever we create a new object and &ldquo;f&rdquo; will be our first argument. It stands for &ldquo;function&rdquo; and is designed to run any given function just once at &quot;birth&quot; so that objects can have different inherent properties. I haven&rsquo;t actually implemented it yet, though. We&rsquo;ll get there in a sec.</p> <p>So the first thing we do is create a local table called &ldquo;obj&rdquo;. The reason is that we can accidentally overwrite our objects if we create these tables globally instead of locally. Making a local, empty table will ensure that this is an entirely new object and won't overwrite or be an instance of some previously created thing.</p> <p>Then we add the local obj to the &ldquo;o&rdquo; table. Keeping everything we create under this one variable means later we can just update every single object with one command instead of running through a bunch of lists of different things.</p> <p>We can add this new obj to other tables, but we&rsquo;ll decide to do that as we work. So we&rsquo;re going to return the obj and grab it later if we want.</p> <p>Now we're going to add one line to that function. </p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre> function new_o(f) --creates a new object with function f local obj={} --local table created setmetatable(obj,{__index=_𝘦𝘯𝘷}) --creates env setup for objects add(o,obj) --obj is added to table called o return obj --send obj back end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <h3>IMPORTANT:</h3> <p>The _𝘦𝘯𝘷 may or may not copy paste correctly so enable puny font with ctrl-p and type _env and ctrl-p back out if you need to.</p> <p>Essentially, what we're doing is changing the nature of these &quot;o&quot; objects at creation and before they are added to the big &quot;o&quot; table, so that when they execute little functions that we write for them, we don't have to waste a lot of token space differentiating between local and global variables. </p> <p>If that didn't make sense, don't worry. I'll explain where it comes in as we start writing some specific objects like the player. Before we do, we just need to bring in that &quot;f&quot; argument and call an init for all objects.</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>function new_o(f) --creates a new object with function f local obj={} --local table created setmetatable(obj,{__index=_𝘦𝘯𝘷}) --creates env setup for objects o_i(obj) --runs init on all objects if(f)f(obj) --runs a given init if called add(o,obj) --obj is added to table called o return obj --send obj back end </pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>First, we call a new function that we are about to create called o_i. It stands for object_init and it will declare all the basic attributes of any object we create. </p> <p>Next, we run the &quot;f&quot; function, if any has been sent. Now whenever we run &quot;new_o(foo)&quot;, an object will be created that will execute a function called &quot;foo&quot;.</p> <p>And because we are returning the obj in the function, we can set variable names as we create the objects like so:</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>function _init() o={} --object table p1=new_o() --creates player 1 end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>Now, at the bottom of our _init, we're creating an object and we're calling it p1 for player 1. I'm not passing any arguments yet, so we'll just start by creating a basic object that goes through our function called &quot;o_i&quot;.</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>function o_i(_𝘦𝘯𝘷) --all object init x=64 --sets x to 64 y=64 --sets y to 64 end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>Note the _𝘦𝘯𝘷 in the argument line. You will need that in most of your argument functions. But it's what makes the next step so cool. We declare x and y variables and put them in the middle of the screen. But because of all the business with the metatable and the _𝘦𝘯𝘷, that variable is now local to the object rather than a global variable. That means that the object can run functions that change those values while other objects are unaffected. </p> <p>Without the metatable line, you would have to do something like this:</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>function o_i(obj) --all object init obj.x=64 --sets x to 64 obj.y=64 --sets y to 64 end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>And that period between the &quot;obj&quot; and the x or y is an added token every time. So this is way has a bit of overhead but saves a lot of space. </p> <p>If you are planning a small demo, feel free to declare all your attributes in this manner. But if you're trying to jam a lot of content into your game, you'll want to start thinking about token optimization over readability. I'm going to be writing this with that kind of person in mind, and show you some of the things I've learned that will help.</p> <p>We can, for instance, optimize our use of &quot;=&quot; tokens buy declaring like this:</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>function o_i(_𝘦𝘯𝘷) --all object init x,y = 64,64 --x and y coords end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>A little less readable, but the token savings are important if you want to jam in more content later. </p> <p>Now we need to create default update and draw functions for the object.</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>function o_i(_𝘦𝘯𝘷) --all object init u=o_u --object update d=o_d --object draw x,y = 64,64 --x and y coords end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>And then if we put those new variables within the existing x and y setup, we get this:</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>function o_i(_𝘦𝘯𝘷) --all object init x,y,u,d = 64,64, --x and y coords o_u,o_d --update and draw functions end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>So we're using the letters &quot;u&quot; and &quot;d&quot; to represent update and draw. We're going to tell every object to run their &quot;u&quot; function during _update, and their &quot;d&quot; during _draw. And we're going to make those o_u and o_d functions next.</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>function o_u(_𝘦𝘯𝘷) --all object update end function o_d(_𝘦𝘯𝘷) --all object draw pset(x,y,14) --draws object pivot end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>I'm leaving the object update, o_u, empty for now. We'll be coming back soon. </p> <p>I've added one command to the object draw, o_d. We're using pset to draw one pink pixel at the object's x and y coord. Later we are going to be drawing sprites relative to this point, so I'm referring to it in the comments as a &quot;pivot&quot;. Making it pink will hopefully make it easier to see. We'll probably want to remove this line of code later in the process, but this will be a good visual indicator for the beginning of creating this character.</p> <p>Now we need to actually add the object update and draw to the main loop. Here's the 3 main functions written out:</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>function _init() o={} --object table p1=new_o() --creates player 1 end function _update60() foreach(o, function(o) --runs the following code on each object o:u() --runs the object's update end) --ends the foreach end function _draw() cls() foreach(o, function(o) --runs the following code on each object o:d() --runs the object's draw end) --ends the foreach end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>Update and draw have been updated to include a foreach command. We're going to run through every object (we currently just have 1) and run whatever function has been assigned to their &quot;u&quot; and &quot;d&quot; attribute. I've also added a command to clear the screen to the top of draw. If you run this code, you should get the pink pivot point drawn to the middle of the screen.</p> <h2>Bringing it all Together</h2> <p>Let's put our sprite work in by modifying the o_d function.</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>function o_d(_𝘦𝘯𝘷) --all object draw spr(16,x-4,y-7) --draws character body spr(1,x-4,y-15) --draws character head pset(x,y,14) --draws object pivot end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>When you run this code, the blue guy should be standing with the pivot at the base of his foot. Easy enough, let's add some temporary movement controls to o_u.</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>function o_u(_𝘦𝘯𝘷) --all object update if(btn(⬅️))x-=1 --temp movement if(btn(➡️))x+=1 if(btn(⬆️))y-=1 if(btn(⬇️))y+=1 end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>So now we're going to add some animation, and animation requires some kind of escalating frame count. I like to use ⧗ as a variable for an object's &quot;clock&quot;, so I'm going to add a couple lines to my object's init and update to get that going.</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>function o_i(_𝘦𝘯𝘷) --all object init ⧗, x,y,u,d = 0, --clock 64,64, --x and y coords o_u,o_d --update and draw functions end function o_u(_𝘦𝘯𝘷) --all object update if(btn(⬅️))x-=1 --temp movement if(btn(➡️))x+=1 if(btn(⬆️))y-=1 if(btn(⬇️))y+=1 ⧗+=1 end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>I've added ⧗ as a variable equal to 0 at the top of the variable list, and I'm incrementing that variable by 1 at the end of the object's update. Now every object I create will have their own clock that we can time our animations off of.</p> <p>Now let's get some code in to control and switch between sprites. Here's a new version of our object loop.</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>function o_i(_𝘦𝘯𝘷) --all object init ⧗, x,y,u,d = 0, --clock 64,64, --x and y coords o_u,o_d --update and draw functions body=17 head=1 end function o_u(_𝘦𝘯𝘷) --all object update if(btn(⬅️))x-=1 body=49 head=33 --temp movement and animation if(btn(➡️))x+=1 body=49 head=33 if(btn(⬆️))y-=1 body=17 head=0 if(btn(⬇️))y+=1 body=17 head=1 ⧗+=1 end function o_d(_𝘦𝘯𝘷) --all object draw spr(body,x-4,y-7) --draws character body spr(head,x-4,y-15) --draws character head pset(x,y,14) --draws object pivot end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>Pretty self explanatory here, but what's great is how easy it is to create, implement, and modify new attributes. We'll make this all a little more elegant in the next episode but let's cap this off with the walk cycle. One advantage to splitting the head from the body is that we can move and flip them independantly.</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>function o_u(_𝘦𝘯𝘷) --all object update bodyflip=false --reset flip walkcycle=⧗%16&lt;8 and true or false --modify clock into walk frames if(btn(⬅️))x-=1 bodyflip=walkcycle headflip=true body=49 head=33 --temp movement and animation if(btn(➡️))x+=1 bodyflip=walkcycle headflip=false body=49 head=33 if(btn(⬆️))y-=1 bodyflip=walkcycle body=17 head=0 if(btn(⬇️))y+=1 bodyflip=walkcycle body=17 head=1 ⧗+=1 end function o_d(_𝘦𝘯𝘷) --all object draw spr(body,x-4,y-7,1,1,bodyflip) --draws character body spr(head,x-4,y-15,1,1,headflip) --draws character head pset(x,y,14) --draws object pivot end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>So I'm now flipping the head and body sprites in the object draw with boolean variables called bodyflip and headflip. If you run this cart, you'll have a little guy who can walk around. </p> <p>I'm going to export out the work I've done so far and paste it at the bottom of this post. If you're having trouble getting your version to work, check against that code first. If you are looking for a challenge to complete, see if you can introduce sprite 32, aka diagonal head, to the walk animation. </p> <p>In the next entry of this blog, we're going to add some non player characters on to the screen and introduce a state machine to our objects that will let them switch between actions and animations. Thanks for reading!</p> <p> <table><tr><td> <a href="/bbs/?pid=122821#p"> <img src="/bbs/thumbs/pico8_wolfe3dblog_01-0.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=122821#p"> wolfe3dblog_01</a><br><br> by <a href="/bbs/?uid=49909"> Wolfe3D</a> <br><br><br> <a href="/bbs/?pid=122821#p"> [Click to Play]</a> </td></tr></table> </p> https://www.lexaloffle.com/bbs/?tid=50760 https://www.lexaloffle.com/bbs/?tid=50760 Thu, 22 Dec 2022 01:24:53 UTC Dammit <p> <table><tr><td> <a href="/bbs/?pid=122435#p"> <img src="/bbs/thumbs/pico8_dammit-0.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=122435#p"> dammit</a><br><br> by <a href="/bbs/?uid=49909"> Wolfe3D</a> <br><br><br> <a href="/bbs/?pid=122435#p"> [Click to Play]</a> </td></tr></table> </p> https://www.lexaloffle.com/bbs/?tid=50662 https://www.lexaloffle.com/bbs/?tid=50662 Thu, 15 Dec 2022 01:32:10 UTC Time After Time <p> <table><tr><td> <a href="/bbs/?pid=122295#p"> <img src="/bbs/thumbs/pico8_timeaftertime-0.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=122295#p"> timeaftertime</a><br><br> by <a href="/bbs/?uid=49909"> Wolfe3D</a> <br><br><br> <a href="/bbs/?pid=122295#p"> [Click to Play]</a> </td></tr></table> </p> https://www.lexaloffle.com/bbs/?tid=50627 https://www.lexaloffle.com/bbs/?tid=50627 Tue, 13 Dec 2022 01:59:19 UTC I Melt With You <p> <table><tr><td> <a href="/bbs/?pid=122218#p"> <img src="/bbs/thumbs/pico8_imeltwithyou-0.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=122218#p"> imeltwithyou</a><br><br> by <a href="/bbs/?uid=49909"> Wolfe3D</a> <br><br><br> <a href="/bbs/?pid=122218#p"> [Click to Play]</a> </td></tr></table> </p> https://www.lexaloffle.com/bbs/?tid=50605 https://www.lexaloffle.com/bbs/?tid=50605 Sun, 11 Dec 2022 03:52:15 UTC TMNT demo <p>Please see the main post here! </p> <p><a href="https://www.lexaloffle.com/bbs/?tid=48795">https://www.lexaloffle.com/bbs/?tid=48795</a></p> <p> <table><tr><td> <a href="/bbs/?pid=112776#p"> <img src="/bbs/thumbs/pico8_dusunopudu-3.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=112776#p"> dusunopudu</a><br><br> by <a href="/bbs/?uid=49909"> Wolfe3D</a> <br><br><br> <a href="/bbs/?pid=112776#p"> [Click to Play]</a> </td></tr></table> Progress update! Less of a &quot;game&quot; than the sparring demo but closer to what I want the final product to be. Hoping to have a full level 1 by the end of the summer.</p> <p> <table><tr><td> <a href="/bbs/?pid=112776#p"> <img src="/bbs/thumbs/pico8_dusunopudu-2.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=112776#p"> dusunopudu</a><br><br> by <a href="/bbs/?uid=49909"> Wolfe3D</a> <br><br><br> <a href="/bbs/?pid=112776#p"> [Click to Play]</a> </td></tr></table> Now a semi-playable sparring demo! Spar with a second player or against the computer!</p> <p> <table><tr><td> <a href="/bbs/?pid=112776#p"> <img src="/bbs/thumbs/pico8_dusunopudu-1.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=112776#p"> dusunopudu</a><br><br> by <a href="/bbs/?uid=49909"> Wolfe3D</a> <br><br><br> <a href="/bbs/?pid=112776#p"> [Click to Play]</a> </td></tr></table> Update!</p> <p> <table><tr><td> <a href="/bbs/?pid=112776#p"> <img src="/bbs/thumbs/pico8_dusunopudu-0.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=112776#p"> dusunopudu</a><br><br> by <a href="/bbs/?uid=49909"> Wolfe3D</a> <br><br><br> <a href="/bbs/?pid=112776#p"> [Click to Play]</a> </td></tr></table> This is rough but promising. May develop further.</p> https://www.lexaloffle.com/bbs/?tid=48047 https://www.lexaloffle.com/bbs/?tid=48047 Mon, 06 Jun 2022 05:56:41 UTC 2048 <p> <table><tr><td> <a href="/bbs/?pid=95413#p"> <img src="/bbs/thumbs/pico8_noyjejza-0.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=95413#p"> noyjejza</a><br><br> by <a href="/bbs/?uid=49909"> Wolfe3D</a> <br><br><br> <a href="/bbs/?pid=95413#p"> [Click to Play]</a> </td></tr></table> This is a no-frills port of the game 2048 that I made while my internet was down. There is a bug where the tile combinations sometimes cascade. I might revisit this and hunt it down but I'm going to post what I finished today because it's more or less playable.</p> https://www.lexaloffle.com/bbs/?tid=43985 https://www.lexaloffle.com/bbs/?tid=43985 Thu, 29 Jul 2021 04:46:55 UTC Destress Ball <p> <table><tr><td> <a href="/bbs/?pid=91959#p"> <img src="/bbs/thumbs/pico8_destressball-1.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=91959#p"> destressball</a><br><br> by <a href="/bbs/?uid=49909"> Wolfe3D</a> <br><br><br> <a href="/bbs/?pid=91959#p"> [Click to Play]</a> </td></tr></table> </p> <p>Something I made while learning to swap function variables more efficiently. Became a nice little time waster.</p> https://www.lexaloffle.com/bbs/?tid=42887 https://www.lexaloffle.com/bbs/?tid=42887 Sat, 15 May 2021 00:22:55 UTC New Kid Demo <p> <table><tr><td> <a href="/bbs/?pid=91410#p"> <img src="/bbs/thumbs/pico8_wskfogi-0.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=91410#p"> wskfogi</a><br><br> by <a href="/bbs/?uid=49909"> Wolfe3D</a> <br><br><br> <a href="/bbs/?pid=91410#p"> [Click to Play]</a> </td></tr></table> <br /> More progress! It's the end of my vacation so I won't have as much time for this going forward. Will try to sneak in a few hours here and there though.</p> <p> <table><tr><td> <a href="/bbs/?pid=91410#p"> <img src="/bbs/thumbs/pico8_gadsefah-0.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=91410#p"> gadsefah</a><br><br> by <a href="/bbs/?uid=49909"> Wolfe3D</a> <br><br><br> <a href="/bbs/?pid=91410#p"> [Click to Play]</a> </td></tr></table> <br /> Another progress build. Need to optimize and fine tune but pretty happy with this engine so I thought I'd share.</p> <p> <table><tr><td> <a href="/bbs/?pid=91410#p"> <img src="/bbs/thumbs/pico8_wuzuhikabo-0.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=91410#p"> wuzuhikabo</a><br><br> by <a href="/bbs/?uid=49909"> Wolfe3D</a> <br><br><br> <a href="/bbs/?pid=91410#p"> [Click to Play]</a> </td></tr></table> <br /> Progress build. Controls a little buggy and getting close to token limit so I'm going to do a teardown and optimize... again.</p> <p> <table><tr><td> <a href="/bbs/?pid=91410#p"> <img src="/bbs/thumbs/pico8_teruruzoyo-0.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=91410#p"> teruruzoyo</a><br><br> by <a href="/bbs/?uid=49909"> Wolfe3D</a> <br><br><br> <a href="/bbs/?pid=91410#p"> [Click to Play]</a> </td></tr></table> </p> <p>Early WIP of my school sim for Pico 8.</p> https://www.lexaloffle.com/bbs/?tid=42736 https://www.lexaloffle.com/bbs/?tid=42736 Mon, 03 May 2021 06:28:03 UTC Superman demo <p> <table><tr><td> <a href="/bbs/?pid=85761#p"> <img src="/bbs/thumbs/pico8_tiwajigha-0.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=85761#p"> tiwajigha</a><br><br> by <a href="/bbs/?uid=49909"> Wolfe3D</a> <br><br><br> <a href="/bbs/?pid=85761#p"> [Click to Play]</a> </td></tr></table> </p> <p>UPDATE: 7/11/21</p> <p>New engine, new intro, 50 rings to fly through. Check back for more updates!</p> https://www.lexaloffle.com/bbs/?tid=40916 https://www.lexaloffle.com/bbs/?tid=40916 Fri, 25 Dec 2020 05:56:29 UTC