BenWiley4000 [Lexaloffle Blog Feed]https://www.lexaloffle.com/bbs/?uid=6654 Latin accent encode/print <p>If you port your PICO-8 game to any European languages other than English, you might want to include some character accents, which doesn't work by default since PICO-8 only supports ASCII characters (plus some special ones in the unused range between ASCII and ISO-8859-1).</p> <p>My work here is based off of <a href="https://www.lexaloffle.com/bbs/?tid=34306">Zep's post from a couple weeks ago</a> about Latin accent printing (by including special characters in front of plain ASCII characters to indicate accents). My code goes a step further by offering a way to save your strings with the real accent characters included, and then encode them so they can be printed properly. This way your text is a bit more readable in the source file.</p> <p>I slightly modified Zep's original print function by changing the <em><code>:</code></em> control character to <em><code>@</code></em> since I needed <em><code>:</code></em> in my printed text. The general rule, if you want to add new encoded characters to this system, is to pick a control character that won't be needed in the actual printed text.</p> <p>FUNCTIONS:</p> <ul> <li><strong><em><code>print_with_accents</code></em></strong> is the modified version of Zep's Latin accent string printing function. It does <em>not</em> print strings with Latin accent characters. It prints strings that have been processed already by <em><code>encode_accents</code></em>.</li> <li><strong><em><code>encode_accents</code></em></strong> accepts a string and returns the &quot;encoded&quot; version made up of plain ASCII characters.</li> <li><strong><em><code>encode_table</code></em></strong> takes a table full of string values (can be nested in subtables) and converts all the strings using <em><code>encode_accents</code></em>. It does this in-place and doesn't return any value.</li> </ul> <p>HANDLING ADDITIONAL ACCENT CHARACTERS:</p> <p>A bunch of cases are included in <em><code>encode_accents</code></em> to cover all the standard French accents, but if you need to handle another case (like the <em><code>&ntilde;</code></em> in Spanish) you can include it manually! For accents that haven't already been handled for any letters, you'll want to also include a new row in the dat table for <em><code>print_with_accents</code></em></p> <p>ACCOUNTING FOR SPECIAL PICO-8 CHARACTERS:</p> <p>The printer generally assumes that printed characters will take up 4 pixels of horizontal space, but the special upper range PICO-8 characters (e.g. 🅾️, ❎) actually take up 8 pixels, so we have to account for this in <em><code>print_with_accents</code></em>. If you need to include any of those characters in your printed text, make sure to add that check in the code.</p> <p>HOW TO USE FEWER TOKENS:</p> <p>If you're worried about the number of tokens you would need to include <em><code>encode_accents</code></em> and <em><code>encode_table</code></em>, you can use those functions in combination with a table serializer like <a href="https://lexaloffle.com/bbs/?pid=64777#p">pico8-table-string</a> to encode your table at build time in a separate Lua script.</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 print_with_accents(str,x,y,col) local dat={ [&quot;#&quot;] = {&quot;,&quot;, 0, 2}, [&quot;^&quot;] = {&quot;^&quot;, 0,-3}, [&quot;`&quot;] = {&quot;`&quot;, -1,-3}, [&quot;|&quot;] = {&quot;,&quot;, 1,-6}, [&quot;@&quot;] = {&quot;\&quot;&quot;, 0,-3} } local p = 1 while p &lt;= #str do local c=sub(str,p,p) if dat[c] then print( dat[c][1], x + dat[c][2], y + dat[c][3], col ) p += 1 c = sub(str,p,p) end print(c, x, y, col) x += 4 p += 1 if ( c == '🅾️' or c == '❎' or c == '♪' ) then x += 4 end end end function encode_accents(str) local new_str = &quot;&quot; local i = 0 while i &lt;= #str do -- two byte compare string local c = sub(str,i,i+1) -- one byte default local e = sub(str,i,i) -- cedille &cedil; if c == &quot;&ccedil;&quot; then e=&quot;#c&quot; -- aigu ˊ elseif c == &quot;&eacute;&quot; then e=&quot;|e&quot; -- circonflexe &circ; elseif c == &quot;&acirc;&quot; then e=&quot;^a&quot; elseif c == &quot;&ecirc;&quot; then e=&quot;^e&quot; elseif c == &quot;&icirc;&quot; then e=&quot;^i&quot; elseif c == &quot;&ocirc;&quot; then e=&quot;^o&quot; elseif c == &quot;&ucirc;&quot; then e=&quot;^u&quot; -- grave ˋ elseif c == &quot;&agrave;&quot; then e=&quot;`a&quot; elseif c == &quot;&egrave;&quot; then e=&quot;`e&quot; elseif c == &quot;&igrave;&quot; then e=&quot;`i&quot; elseif c == &quot;&ograve;&quot; then e=&quot;`o&quot; elseif c == &quot;&ugrave;&quot; then e=&quot;`u&quot; -- tr&eacute;ma &uml; elseif c == &quot;&euml;&quot; then e=&quot;@e&quot; elseif c == &quot;&iuml;&quot; then e=&quot;@i&quot; elseif c == &quot;&uuml;&quot; then e='@u' end new_str=new_str..e if e ~= sub(str,i,i) then i = i + 1 end i = i + 1 end return new_str end function encode_table(table) for k,v in pairs(table) do if type(v) == &quot;table&quot; then encode_table(v) else table[k]=encode_accents(v) end end end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> https://www.lexaloffle.com/bbs/?tid=34431 https://www.lexaloffle.com/bbs/?tid=34431 Mon, 03 Jun 2019 03:49:29 UTC pico8-table-string (for saving tokens) <p>I wrote this library to cut down on the number of tokens taken up by large tables with string data, e.g. for dialogue text/translations/etc. Most helpful if those tokens are using a lot of your tokens (i.e. more than 150), since the library itself takes up 139 tokens. But all your table declarations can be reduced to 5 tokens each!</p> <p>Here's an example of what your code can look like.</p> <p>Before:</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>my_table = { hello='world', nested={ some='data\'s nested', inside_of='here' }, 'and', 'indexed', 'data', { as='well' } }</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>After:</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 table_from_string(str) local tab, is_key = {}, true local key,val,is_on_key local function reset() key,val,is_on_key = '','',true end reset() local i, len = 1, #str while i &lt;= len do local char = sub(str, i, i) -- token separator if char == '\31' then if is_on_key then is_on_key = false else tab[tonum(key) or key] = val reset() end -- subtable start elseif char == '\29' then local j,c = i,'' -- checking for subtable end character while (c ~= '\30') do j = j + 1 c = sub(str, j, j) end tab[tonum(key) or key] = table_from_string(sub(str,i+1,j-1)) reset() i = j else if is_on_key then key = key..char else val = val..char end end i = i + 1 end return tab end my_table= table_from_string( '1�and�2�indexed�3�data�4�as�well��hello�world�nested�inside_of�here�some�data\'s nested��' )</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>Clearly it's more helpful if your data table is much larger than this. In my case, my data tables took up almost 200 tokens, and I saved about 50 using this technique. If I go to add more translations in the future, it will save even more.</p> <p><a href="https://github.com/benwiley4000/pico8-table-string">https://github.com/benwiley4000/pico8-table-string</a></p> https://www.lexaloffle.com/bbs/?tid=34342 https://www.lexaloffle.com/bbs/?tid=34342 Mon, 27 May 2019 07:03:40 UTC pico8-to-lua <p>Here's a simple tool I made for converting PICO-8-style Lua syntax into standard Lua syntax which allows you to run code analysis tools created for Lua. It's a thin wrapper around a converter function I took from PICOLOVE.</p> <p><a href="https://github.com/benwiley4000/pico8-to-lua">https://github.com/benwiley4000/pico8-to-lua</a></p> https://www.lexaloffle.com/bbs/?tid=34337 https://www.lexaloffle.com/bbs/?tid=34337 Sun, 26 May 2019 06:41:56 UTC Sk8Border - Lay That Wall To Waste! <p> <table><tr><td> <a href="/bbs/?pid=51656#p"> <img src="/bbs/thumbs/pico51655.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=51656#p"> Sk8Border - Lay That Wall To Waste! v1.0.0</a><br><br> by <a href="/bbs/?uid=6654"> BenWiley4000</a> <br><br><br> <a href="/bbs/?pid=51656#p"> [Click to Play]</a> </td></tr></table> </p> <p>Sk8Border is a collaboration by Leif Halld&oacute;r &Aacute;sgeirsson, Marc-Andr&eacute; Toupin and Ben Wiley for the <a href="https://itch.io/jam/antifa-game-jam"> Anti-Fascist Game Jam</a>.</p> <p>Works in mobile browsers!</p> <p>Hold Z or X to crouch, release to jump (Ollie). Do a tail or nose grind with Z or X. Rack up a nice combo to take down the wall!</p> <p>More details and play instructions are at <a href="https://sk8border.github.io/sk8border/">the game's official site</a>. We recommend playing it on mobile there since we have specially tailored controls, and vibration support!</p> <p>The game is &quot;finished,&quot; but comments are always welcome.</p> https://www.lexaloffle.com/bbs/?tid=31132 https://www.lexaloffle.com/bbs/?tid=31132 Sun, 15 Apr 2018 21:09:28 UTC P8 Messenger - Small GPIO library <p><a href="https://github.com/benwiley4000/pico8-messenger">Here's a repo with the code and more details on GitHub</a></p> <p>PICO-8 has a GPIO interface supporting 128 pins, each of which can store a 128-bit unsigned integer (0-255). However Raspberry PI and CHIP only support consuming each GPIO pin as a single bit (0 or 255), and only have pins for a small subset of the 128 virtual slots. The Pocket CHIP only has 6 fully-exposed pins (indices 2-7).</p> <p>This means that if you want to pass GPIO information that can be used easily with any platform that runs PICO-8, you only get six on-and-off slots, which doesn't sound that great. Until you consider that you can still use those 6 slots to encode an integer up to 6 bits (-32 to 31 signed, or 0 to 63 unsigned!). Even a 3-bit int (0-7 unsigned) can often be enough to encode meaningful state information for many games, which can be used to trigger vibrations, color lights, etc.</p> <p>The trouble is, taking a decimal value and encoding it as binary with PICO-8's built-in GPIO functions, then reading it again later, is not simple. PICO-8 Messenger provides utility functions which abstract away the bit-shifting and let you just read and write numbers.</p> <h3>Usage</h3> <p>Copy the functions you need from pico8-messenger.lua into your .p8 file.</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> -- included definition for write_gpio -- included definition for read_gpio -- write the number -1 to bits 2 through 4 write_gpio(-1, 2, 3) -- print out the number stored in bits 5 through 7 print(read_gpio(5, 3), 8, 8, 7) </pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>You can download pico8-messenger.js and include it in your page with a script tag:</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> &lt;script src=&quot;pico8-messenger.js&quot;&gt;&lt;/script&gt; &lt;script&gt; pico8_gpio = pico8_gpio || Array(128); // get some number stored in bits 2 through 4 var numFromPico8 = readFromGpio(pico8_gpio, 2, 3); // send the number 2 to pico-8 stored in bits 5 through 7 writeToGpio(pico8_gpio, 2, 5, 3); &lt;/script&gt; </pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <h3>API</h3> <p>For all of these functions:</p> <ul> <li><code>num</code> is the decimal integer to be stored</li> <li><code>pin_index</code> or <code>pinIndex</code> is index in the GPIO array (0-127) where storage for this number should begin (in other words, the location of the largest, left-most bit)</li> <li><code>bits</code> is the number of bits required to store the maximum value for this number</li> </ul> <h4>Lua</h4> <p>These functions wrap PICO-8's <code>peek</code> and <code>poke</code> functions to read and write data in the GPIO slots.</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 write_gpio(num, pin_index, bits) function write_gpio_unsigned(num, pin_index, bits) function read_gpio(pin_index, bits) function read_gpio_unsigned(pin_index,bits) </pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <h4>JavaScript</h4> <p>All of these functions assume <code>gpio</code> is a 128-length array filled with numbers that are either 0 or 255. Although these are intended for handling PICO-8 data, they can be used anywhere it could be useful to encode numbers in a binary array.</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 writeToGpio(gpio, num, pinIndex, bits) function writeToGpioUnsigned(gpio, num, pinIndex, bits) function readFromGpio(gpio, pinIndex, bits) function readFromGpioUnsigned(gpio, pinIndex, bits) </pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <h2>How many bits do I need?</h2> <p><a href="https://github.com/benwiley4000/pico8-messenger#how-many-bits-do-i-need">Check out this table on GitHub</a></p> https://www.lexaloffle.com/bbs/?tid=31098 https://www.lexaloffle.com/bbs/?tid=31098 Sun, 08 Apr 2018 06:15:40 UTC GPIO Listener JavaScript Library <p>The fact you can read and write PICO-8 GPIO values from the web wrapper is awesome - it means you can do things like emit vibrations according to game state, change the site theme to match different in-game levels, or maybe even develop a brand new input scheme.</p> <p>However the API for reading these values is a bit basic... PICO-8 writes GPIO values to an array, and it's up to you, the developer to check those values periodically to see if they're different. What if you could just tell JavaScript to let you know whenever the GPIO pins get updated with new values?</p> <p>Voil&agrave;:</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> var gpio = getP8Gpio(); var unsubscribe = gpio.subscribe(function(indices) { console.log( 'New values at indices ' + indices.join(', ') + ': ' + indices.map(function(i) { return gpio[i]; }).join(', ') ); }); // unsubscribe later if you want... unsubscribe(); </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 uses JavaScript setters under the hood to watch each index in the array, which means the watching part is shoved into a native background thread (== faster!). No need to write once-a-frame JS iterator loops or anything like that. Whenever PICO-8 writes to the GPIO array, you get notified immediately.</p> <p>Of course you can also write back to the GPIO array:</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> gpio[3] = 255; gpio[4] = 0; </pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <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> New values at indices 3, 4: 255, 0 </pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>Your listener will be notified about your own changes as well; it's up to you if you want to do anything with that.</p> <p>By default, listeners only get called if at least one value has changed in the last call stack. If you want to be notified about every update, new value or not, you can pass a second argument, <code>verbose</code>, which is a boolean:</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> gpio.subscribe(function(indices) { console.log( 'The values ' + indices.map(function(i) { return gpio[i]; }).join(', ') + ' at indices ' + indices.join(', ') + ' probably didn\'t change, but I am logging them anyway..' ); }, true); </pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>Here's the library code (~75 lines of JavaScript):</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> var getP8Gpio; (function() { getP8Gpio = _getP8Gpio; var size = 8; // extends Array prototype function PicoGpioArray() { Array.call(this, size); this._data = Array(size); this._listeners = []; this._pending = {}; this._pendingNew = {}; this._pendingTimeout = null; this.dispatchPending = this.dispatchPending.bind(this); Object.seal(this); } PicoGpioArray.prototype = Object.create(Array.prototype); PicoGpioArray.prototype.constructor = PicoGpioArray; // listener callback is required. second argument (verbose) is a boolean // and assumed to be false if not provided. PicoGpioArray.prototype.subscribe = function subscribe(listener, verbose) { listener.verbose = Boolean(verbose); this._listeners.push(listener); return (function unsubscribe() { this._listeners.splice(this._listeners.indexOf(listener), 1); }).bind(this); }; // alert listeners of all values changed during the last call stack PicoGpioArray.prototype.dispatchPending = function dispatchPending() { var pendingIndices = Object.keys(this._pending).map(Number); var pendingNewIndices = Object.keys(this._pendingNew).map(Number); for (var i = 0; i &lt; size; i++) { delete this._pending[i]; delete this._pendingNew[i]; } if (!pendingIndices.length) { return; } for (var l = 0; l &lt; this._listeners.length; l++) { var indices = this._listeners[l].verbose ? pendingIndices : pendingNewIndices; if (indices.length) { this._listeners[l](indices); } } }; // intercept assignments to each GPIO pin to notify listeners for (var i = 0; i &lt; size; i++) { (function(index) { Object.defineProperty(PicoGpioArray.prototype, index, { get: function() { return this._data[index]; }, set: function(value) { clearTimeout(this._pendingTimeout); this._pending[index] = true; if (this._data[index] !== value) { this._pendingNew[index] = true; } this._data[index] = value; this._pendingTimeout = setTimeout(this.dispatchPending); } }); })(i); } function _getP8Gpio() { // initialize only once window.pico8_gpio = window.pico8_gpio || new PicoGpioArray(); return window.pico8_gpio; } })(); </pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>Old version (API changed):</p> <p><div><div><input type="button" value=" Show " onClick="if (this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display != '') { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = ''; this.innerText = ''; this.value = ' Hide '; } else { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = 'none'; this.innerText = ''; this.value = ' Show '; }"></div><div><div style="display: none;"><br /> Voil&agrave;:</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> var gpio = getP8Gpio(); var unsubscribe = gpio.subscribe(function(index) { console.log('New value at index ' + index ' + ': ' + gpio[index]); }); // unsubscribe later if you want... unsubscribe(); </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 uses JavaScript setters under the hood to watch each index in the array, which means the watching part is shoved into a native background thread (== faster!). No need to write once-a-frame JS iterator loops or anything like that. Whenever PICO-8 writes to the GPIO array, you get notified immediately.</p> <p>Of course you can also write back to the GPIO array:</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> gpio[3] = 255; </pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>Your listener will be notified about your own changes as well; it's up to you if you want to do anything with that.</p> <p>By default, listeners only get called if a value has changed. If you want to be notified about every update, new value or not, you can pass a second argument, <code>verbose</code>, which is a boolean:</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> gpio.subscribe(function(index) { console.log( 'The value ' + gpio[index] + ' at index ' + index + ' probably didn't change, but I am logging it anyway..' ); }, true); </pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>Here's the library code (50 lines of JavaScript):</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> var getP8Gpio; (function() { getP8Gpio = _getP8Gpio; var size = 8; // extends Array prototype function PicoGpioArray() { Array.call(this, size); this._data = Array(size); this._listeners = []; Object.seal(this); } PicoGpioArray.prototype = Object.create(Array.prototype); PicoGpioArray.prototype.constructor = PicoGpioArray; // listener callback is required. second argument (verbose) is a boolean // and assumed to be false if not provided. PicoGpioArray.prototype.subscribe = function subscribe(listener, verbose) { listener.verbose = Boolean(verbose); this._listeners.push(listener); return (function unsubscribe() { this._listeners.splice(this._listeners.indexOf(listener), 1); }).bind(this); }; // intercept assignments to each GPIO pin to notify listeners for (var i = 0; i &lt; size; i++) { (function(index) { Object.defineProperty(PicoGpioArray.prototype, index, { get: function() { return this._data[index]; }, set: function(value) { var isNew = this._data[index] !== value; this._data[index] = value; this._listeners.forEach(function(listener) { if (isNew || listener.verbose) { listener(index); } }); } }); })(i); } function _getP8Gpio() { // initialize only once window.pico8_gpio = window.pico8_gpio || new PicoGpioArray(); return window.pico8_gpio; } })(); </pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p></div></div></div></p> https://www.lexaloffle.com/bbs/?tid=31084 https://www.lexaloffle.com/bbs/?tid=31084 Thu, 05 Apr 2018 01:10:46 UTC Tiny Touch UI JavaScript Library <p>I want my web export to support mobile. But if you're like me, you might find the API for controlling touch button inputs from a web page a bit esoteric, and not super easy to read/write. Why not write a tiny API wrapper that makes this much easier?</p> <p>If you have a page that looks 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> &lt;!-- ... cart stuff --&gt; &lt;button id=&quot;left&quot;&gt; &lt; &lt;/button&gt; &lt;button id=&quot;right&quot;&gt; &gt; &lt;/button&gt; &lt;button id=&quot;up&quot;&gt; /\ &lt;/button&gt; &lt;button id=&quot;down&quot;&gt; \/ &lt;/button&gt; &lt;button id=&quot;o&quot;&gt; O &lt;/button&gt; &lt;button id=&quot;x&quot;&gt; X &lt;/button&gt; &lt;! -- ... --&gt; </pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>Include this in your page...</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() { var __btns = {}; function update_btns(playerIndex) { pico8_buttons[playerIndex] = Object.keys(__btns[playerIndex]).reduce(function(val, btn) { return val | (__btns[playerIndex][btn] ? Math.pow(2, btn) : 0); }, 0); } function registerP8Btn(domElement, btnIndex, playerIndex) { playerIndex = playerIndex || 0; window.pico8_buttons = window.pico8_buttons || []; pico8_buttons[playerIndex] = pico8_buttons[playerIndex] || 0; __btns[playerIndex] = __btns[playerIndex] || {}; domElement.addEventListener('touchstart', function() { __btns[playerIndex][btnIndex] = true; update_btns(playerIndex); }); domElement.addEventListener('touchend', function() { __btns[playerIndex][btnIndex] = false; update_btns(playerIndex); }); } window.registerP8Btn = registerP8Btn; })(); </pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>Then later you can register buttons 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> registerP8Btn(document.getElementById('left'), 0); registerP8Btn(document.getElementById('right'), 1); registerP8Btn(document.getElementById('up'), 2); registerP8Btn(document.getElementById('down'), 3); registerP8Btn(document.getElementById('o'), 4); registerP8Btn(document.getElementById('x'), 5); </pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>Are you trying to support multiple players? Then you can do:</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> registerP8Btn(document.getElementById('x-P1'), 5, 0 /* player 1 */); registerP8Btn(document.getElementById('x-P2'), 5, 1 /* player 2 */); </pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>That's it!</p> https://www.lexaloffle.com/bbs/?tid=31082 https://www.lexaloffle.com/bbs/?tid=31082 Wed, 04 Apr 2018 15:46:51 UTC PICO-8 Timers API <p> <table><tr><td> <a href="/bbs/?pid=19693#p"> <img src="/bbs/thumbs/pico19773.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=19693#p"> Timers API 0.1.3</a><br><br> by <a href="/bbs/?uid=6654"> BenWiley4000</a> <br><br><br> <a href="/bbs/?pid=19693#p"> [Click to Play]</a> </td></tr></table> </p> <p> <table><tr><td> <a href="/bbs/?pid=19693#p"> <img src="/bbs/thumbs/pico19696.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=19693#p"> Timers API 0.1.2</a><br><br> by <a href="/bbs/?uid=6654"> BenWiley4000</a> <br><br><br> <a href="/bbs/?pid=19693#p"> [Click to Play]</a> </td></tr></table> </p> <p>As Scathe noted <a href="https://www.lexaloffle.com/bbs/?tid=3195">over here</a>, there's not a proper/easy-to-use Timers API built into PICO-8. Turns out it's not too hard to build one, though, so I took up the task.</p> <p>The cartridge which you can play above just counts to 10. I've reproduced all the code here:</p> <p><div><div><input type="button" value=" Show " onClick="if (this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display != '') { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = ''; this.innerText = ''; this.value = ' Hide '; } else { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = 'none'; this.innerText = ''; this.value = ' Show '; }"></div><div><div style="display: none;"></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> -- start timers code local timers = {} local last_time = nil function init_timers () last_time = time() end function add_timer (name, length, step_fn, end_fn, start_paused) local timer = { length=length, elapsed=0, active=not start_paused, step_fn=step_fn, end_fn=end_fn } timers[name] = timer return timer end function update_timers () local t = time() local dt = t - last_time last_time = t for name,timer in pairs(timers) do if timer.active then timer.elapsed += dt local elapsed = timer.elapsed local length = timer.length if elapsed &lt; length then if timer.step_fn then timer.step_fn(dt,elapsed,length,timer) end else if timer.end_fn then timer.end_fn(dt,elapsed,length,timer) end timer.active = false end end end end function pause_timer (name) local timer = timers[name] if (timer) timer.active = false end function resume_timer (name) local timer = timers[name] if (timer) timer.active = true end function restart_timer (name, start_paused) local timer = timers[name] if (not timer) return timer.elapsed = 0 timer.active = not start_paused end -- end timers code -- start app code function _update () update_timers() end function _init () init_timers() local last_int = 0 print(last_int) sfx(last_int) add_timer( &quot;timer1&quot;, 10, function (dt,elapsed,length) local i = flr(elapsed) if i &gt; last_int then print(i) sfx(i) last_int = i end end, function () print(&quot;done!&quot;) sfx(10) end ) end -- end app code </pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p></div></div></div></p> <p>The actual &quot;Timers API&quot; is between &quot;-- start timers code&quot; and &quot;-- end timers code.&quot; It might seem a bit verbose; take what you need and leave the rest.</p> <p>This should be robust enough to meet any typical needs. The main weakness right now is that you can't have multiple step or end callbacks, and you can't add callbacks after the timer is initialized.</p> <p>API specification:</p> <p><div><div><input type="button" value=" Show " onClick="if (this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display != '') { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = ''; this.innerText = ''; this.value = ' Hide '; } else { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = 'none'; this.innerText = ''; this.value = ' Show '; }"></div><div><div style="display: none;"></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> local timers -- CRITICAL -- this is a table that tracks your timers by name </pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <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> local last_time -- CRITICAL -- this is the last value of time() recorded. </pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <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_timers() -- CRITICAL -- run this at the start of your _init() function to make sure -- last_time is properly in-sync. </pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <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 add_timer (name, length, step_fn, end_fn, start_paused) -- CRITICAL -- use this function to track a new timer. -- PARAMS: -- * name: -- You can use &quot;timers[name]&quot; to access your timer later on, if you need to. -- type: string -- required? Yes. -- * length: -- How many seconds your timer should last. -- type: number -- required? Yes. -- * step_fn: -- A callback that gets called each time update_timers() is run. -- Receives (dt,elapsed,length,timer) as parameters. -- type: function -- required? No. -- * end_fn: -- A callback that gets called once after the timer has expired. -- Receives (dt,elapsed,length,timer) as parameters. -- type: function -- required? No. -- * start_paused: -- If present and truthy, makes the timer initialize as inactive. -- type: boolean -- required? No. </pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <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 update_timers() -- CRITICAL -- run this as part of your _update() function. </pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <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 pause_timer(name) -- OPTIONAL -- synactic sugar equivalent to 'timers[name].active = false.' -- leave out if you won't use this much or at all. -- fails silently if timer doesn't exist. </pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <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 resume_timer(name) -- OPTIONAL -- synactic sugar equivalent to 'timers[name].active = true.' -- leave out if you won't use this much or at all. -- fails silently if timer doesn't exist. </pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <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 restart_timer(name, start_paused) -- OPTIONAL -- synactic sugar equivalent to 'timers[name].elapsed = 0;timers[name].active = not start_paused.' -- leave out if you won't use this much or at all. -- fails silently if timer doesn't exist. -- PARAMS: -- * name: -- type: string -- required? Yes. -- * start_paused: -- If present and truthy, sets the timer to inactive. -- type: boolean -- required? No. </pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p></div></div></div></p> https://www.lexaloffle.com/bbs/?tid=3202 https://www.lexaloffle.com/bbs/?tid=3202 Sun, 10 Apr 2016 18:18:36 UTC Modifying music channel mask mid-playback <p>I was discussing with a friend how to deal with complex music arrangements (all 4 channels) alongside sound effects. He alleged that the GameBoy used all its channels for music at times but would cancel one of its channels (perhaps the percussive one) to play sound effects, then resume when sound effects had elapsed.</p> <p>Current PICO-8 configuration, as far as I understand, is to have music channel masks take precedence over sound effects. This is good in theory but can be bad if you want to do something like the above. A possible solution could be to allow music channel mask modification mid-playback - is this doable? It would require some way to save or lookup a currently playing track (not yet in the API as far as I know), and then we pass that track's value to a function that could reset the channel mask to fewer channels, before playing a sound effect. Then we would do it again with the previous channel mask and expect the muted track component to come back.</p> <p>I'm not familiar with the tech powering sound playback for PICO-8 and I don't know if this is the sort of thing that could have a simple implementation, or if trying to be able to modify variables for already-playing songs is totally out of the question. I can begin to imagine why it would be complicated or non-performant, but I'm not sure, so I thought I'd ask.</p> <p>Thanks!</p> https://www.lexaloffle.com/bbs/?tid=3188 https://www.lexaloffle.com/bbs/?tid=3188 Mon, 04 Apr 2016 09:57:00 UTC Swinging notes / end sound segment early <p>I want to write a song that's in 4, but has swinging notes - so I'm just writing tracks that fill up 24 of the slots instead of all 32. But in order to make it work in sequence I need the track to stop playing after the 24th slot and move on to the next segment in the song immediately. Is there a way to tell PICO-8 to do that? Of course I could just use all 32 slots and changing the measure's starting place depending on where I am, but I'd rather be more organized.</p> <img style="margin-bottom:16px" border=0 src="https://www.lexaloffle.com/bbs/files/6654/Screenshot-9.png" width=507 height=511 alt="" /> https://www.lexaloffle.com/bbs/?tid=3186 https://www.lexaloffle.com/bbs/?tid=3186 Mon, 04 Apr 2016 09:03:48 UTC Wrote Script for Making Webplayer Responsive <p><strong>UPDATE:</strong> Since I created this I've updated the script to apply all transforms with Python by default. The JavaScript method of shuffling elements is enabled only if 1) you lack the dependencies required by Python or 2) you specifically flag the program to use JavaScript. The advantage of doing it all with Python is that you don't have to wait until page content loads to see a responsive layout - it's already there. I've also created <a href="http://benwiley4000.github.io/pico8-responsive-webplayer-transform/pico8_responsive.html">a comparison of the responsive and default web player layouts</a>. And check out the <a href="https://github.com/benwiley4000/pico8-responsive-webplayer-transform/blob/master/README.md">README</a> to see what Python packages you'll need to do the full HTML transform in Python.</p> <p><em>~original post~</em></p> <p>The default webplayer page format that gets exported from PICO-8 is great, but not quite excellent, since it only provides one display resolution. I decided to work with the structure and style a bit (using flexbox styles and media breakpoints) to make the canvas render at 290px, 580px, or 1160px wide based on what real estate is available. Also, the buttons collapse into multiple rows once the display gets small enough.</p> <p>I came up with <a href="https://gist.github.com/benwiley4000/e269f51d4da1802154d33b74d7ec3927">this</a>.<br /> (replace &quot;[PATH_TO_YOUR_CARTRIDGE.js]&quot; with your actual cartridge js file).</p> <p>Try it out by resizing your browser window (if you open up the developer console you can use its border to make the window space even smaller). You won't get the highest resolution unless you're on a very high res display (e.g. 4k), since it checks for vertical space as well.</p> <p>The thing is, this won't be easy to use if you've already made content or style changes to your page, or when the next iteration of the HTML export page is included in a future version of PICO-8.</p> <p>So <a href="https://github.com/benwiley4000/pico8-responsive-webplayer-transform/blob/master/transform.py">I wrote a Python script</a> (click on &quot;Raw&quot; to download) that will, in one command, turn your existing webplayer page into a responsive one.</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> $ python transform.py cartridge.html </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 will give you a &quot;cartridge-responsive.html&quot; file.</p> <p>It works by inserting a new HTML style tag as a CSS override, and a bit of JavaScript that rearranges your page's elements immediately after the page loads. I was going to do that bit as part of the Python transform, but that would require people installing random Python libraries as dependencies. It doesn't really matter though, since the change happens as soon as the game is ready. The main disadvantage is that on a tiny display, the buttons will be overflowing the side of the screen until the game is finished loading.</p> <p><a href="https://github.com/benwiley4000/pico8-responsive-webplayer-transform">The full source is all on GitHub.</a></p> <p>Let me know what you think! If I did something wrong, feel free to discuss it here or submit a pull request.</p> <p>P.S. If you like it feel free to give the project a star on GitHub. :)</p> https://www.lexaloffle.com/bbs/?tid=3175 https://www.lexaloffle.com/bbs/?tid=3175 Wed, 30 Mar 2016 09:14:26 UTC Closure-Based Classes/Objects <p>So after completing the (very helpful) Squash/Pong tutorial in the first pico-8 zine, I decided to go about setting up a class-based object-oriented framework for implementing the same game - one where relevant data is contained within an object, and the object has callable methods that let the object act on its own data rather than other members reaching in and changing values. The _update() and _draw() functions in turn just call _update() and _draw() methods contained inside game objects.</p> <p>Here's what I came up with: <a href="http://hastebin.com/agasogijet.lua"><a href="http://hastebin.com/agasogijet.lua">http://hastebin.com/agasogijet.lua</a></a><br /> The code at the top is mine, and the section at the bottom contains the original code (more or less, I made a couple of changes).</p> <p>It's good if you follow the tutorial from the zine (<a href="https://sectordub.itch.io/pico-8-fanzine-1"><a href="https://sectordub.itch.io/pico-8-fanzine-1">https://sectordub.itch.io/pico-8-fanzine-1</a></a>), but if you don't, you should be able to paste the code into the pico-8 editor and run it. The health meter sprites won't show up, and you won't get any sound, but everything else will work.</p> <p>DISCUSSION:</p> <p>This turned out to be more complicated than I expected, entirely because I couldn't decide how I was going to implement classes. Even though this example only instantiates each class once (singletons), I wanted to have classes for future projects where I would want to be able to generalize across several instances.</p> <p>The common way of implementing classes in Lua is similar to what I'm used to with JavaScript - prototype inheritance, where a method lookup on an object falls back to another object which represents the class. In this case our &quot;objects&quot; are actually &quot;tables,&quot; but it's the same principle. I ended up writing this helper for creating a class in Lua:</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> -- this is possible in standard lua (try it here: [http://www.lua.org/cgi-bin/demo](http://www.lua.org/cgi-bin/demo)) -- class helper (include this once, use it for all your classes) function class (init) local c = {} c.__index = c function c.init (...) local self = setmetatable({},c) init(self,...) return self end return c end -- end class helper -- actual class definition local someclass = class(function (self, name) self.name = name end) function someclass:setname (name) self.name = name end -- end class definition local someclassinstance = someclass.init(&quot;monkey&quot;) print(someclassinstance.name) -- &quot;monkey&quot; someclassinstance:setname(&quot;banana&quot;) print(someclassinstance.name) -- &quot;banana&quot; </pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>However this requires the &quot;setmetatable()&quot; function which is part of the Lua standard library, which we don't have. As far as I know there isn't another way to pull off prototype-based classes, so I gave up on that. <a href="lua-users.org/wiki/ObjectOrientationTutorial">lua-users.org/wiki/ObjectOrientationTutorial</a></p> <p>The other accepted method (also discussed in the link above) is closure-based classes, which is also something people do in JavaScript (if they're confused). No need to use a helper function - we can just declare our object and its methods inside a function and return the result - it ends up being a lot like classes in Java and other object-oriented languages. Each instantiated class has its own set of methods rather than a shared set, so the memory cost can be substantially higher for many instances.</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> -- this works in pico-8! try it. -- class definition function someclass (name) local self = {} self.name = name function self.setname (name) self.name = name end return self end -- end class definition local someclassinstance = someclass(&quot;monkey&quot;) print(someclassinstance.name) -- &quot;monkey&quot; someclassinstance.setname(&quot;banana&quot;) print(someclassinstance.name) -- &quot;banana&quot; </pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>However, that's good enough, if a bit memory inefficient, and what we've got to work with right now. It's what I use throughout the Squash/Pong code I've shared.</p> <p>P.S. I also want to acknowledge this discussion which suggests copying objects and mutating them to simulate classes. <a href="https://www.lexaloffle.com/bbs/?tid=2951"><a href="https://www.lexaloffle.com/bbs/?tid=2951">https://www.lexaloffle.com/bbs/?tid=2951</a></a></p> <p>While some consider that a viable option (and it can get the job done), for me it slightly defeats the point of class-based programming.</p> https://www.lexaloffle.com/bbs/?tid=3172 https://www.lexaloffle.com/bbs/?tid=3172 Mon, 28 Mar 2016 07:20:20 UTC