<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="/feed.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" /><updated>2024-09-22T11:45:39+00:00</updated><id>/feed.xml</id><title type="html">LANCE EWING</title><subtitle>Lance Ewing&apos;s personal blog, mainly about technical topics, such as HTML5 game programming, js13kgames, gamedev.js, and Sierra On-line AGI graphic adventure games.  Occassionally I will also blog about 8-bit computer emulation, computer chip reverse engineering, digital electronics,  and in general about how computers work.</subtitle><author><name>Lance Ewing</name></author><entry><title type="html">The Space Quest II Master Disk Blunder</title><link href="/blog/sierra/agi/sq2/2024/05/22/do-you-own-this-space-quest-2-disk.html" rel="alternate" type="text/html" title="The Space Quest II Master Disk Blunder" /><published>2024-05-22T05:00:00+00:00</published><updated>2024-05-22T05:00:00+00:00</updated><id>/blog/sierra/agi/sq2/2024/05/22/do-you-own-this-space-quest-2-disk</id><content type="html" xml:base="/blog/sierra/agi/sq2/2024/05/22/do-you-own-this-space-quest-2-disk.html"><![CDATA[<p>There is nothing unusual about the outside of these disks, but there is something unique about the data that is stored on them, something that Sierra On-Line would have been totally unaware of and certainly wouldn’t have wanted them to include.</p>

<p><img src="/images/Space_Quest_2.0D_Disk_1_small.jpg" width="100%" /></p>

<p>If you happen to have one of these 720KB floppy disks lurking somewhere in your Sierra adventure game collection, then chances are you are not alone. Versions 2.0D and 2.0F of Space Quest II were not exactly uncommon.</p>

<h2 id="a-simple-directory-listing">A simple directory listing</h2>

<p>Listing the files on the disk doesn’t reveal anything unusual:</p>

<p><img src="/images/sq2_directory_listing.png" width="100%" /></p>

<p>The listing shown is from version 2.0D. There are no mysterious additional files, in fact it looks like any other Sierra game disk. Nothing is out of place.</p>

<p>The timestamps show that the game’s main data files, i.e. PICDIR, LOGDIR, VIEWDIR, SNDDIR, VOL.0 and VOL.1, were built on the 14th March 1988. The .OVL files are dated the 15th March 1988, and the AGI interpreter code itself is dated the 18th March 1988. These file timestamps capture a week of activity during which someone in the Sierra office was focused on preparing the 2.0D version of Space Quest II.</p>

<p>One thing that is a little curious about the directory listing above is that the “free” space on the disk is greater than the used space. 302,918 bytes are used and 402,432 bytes are supposedly free.</p>

<h2 id="using-a-hex-editor">Using a hex editor</h2>

<p>To take a closer look at what is on the disk, and to peek into that supposedly unused part, we need a tool called a hex editor. Back in the 1980s, a commonly used tool for this came with Norton Utilities. These days a great modern equivalent is the excellent HxD Hex Editor written by Maël Hörz.</p>

<p>For a freshly formatted DOS floppy disk, we’d expect all of the unused sectors to be filled with the 0xF6 byte value (i.e. DOS’s default format ‘filler’ byte), which is indeed the case for Disk 2 of Space Quest II version 2.0D, as shown below:</p>

<p><img src="/images/Fully_Formatted_Sector.png" width="100%" /></p>

<p>There isn’t, however, a single unused sector on Disk 1 that is filled with the 0xF6 byte, in fact the longest string of consecutive 0xF6 bytes on Disk 1 is only two. Given that over half the disk is “free” space, then clearly the disk had not been formatted prior to being prepared as a SQ2 master disk. The following shows an example of a section of the SQ2 Disk 1 disk that is marked as unused:</p>

<p><img src="/images/Space_Quest_2_C_Code_Sector.png" width="100%" /></p>

<p>Rather than being filled with the 0xF6 byte, it is instead filled with something that looks like C source code. This strongly suggests that the master disk was used for some other purpose prior to being the SQ2 Disk 1 master disk. The files were then deleted, but the disk was not properly formatted afterwards. Due to the way the DOS FAT file system works, deleting a file does not actually remove the data. Instead it simply marks the sectors as no longer being in use, so that in the future they could be used for a new file. If those sectors are never used again for a new file, then they will keep what they previously had stored on them, and so it was important that people fully formatted a floppy disk after storing sensitive files on them.</p>

<h2 id="agi-interpreter-source-code">AGI interpreter source code</h2>

<p>It is difficult to see exactly what the above data looks like when viewed in a hex editor, but it clearly looks like text, so let’s copy and paste the ASCII text on the right hand side into a text editor to see what we have:</p>

<p><img src="/images/AGI_Interpreter_Source_Code.png" width="100%" /></p>

<p>As we can see, it certainly does look like C source code. There are two functions defined, one called DisplayStatusLine and another called StatusLineOn. In the case of DisplayStatusLine, it appears to be displaying a line of text that includes the current score and whether the sound is on or off. Does this look familiar? You bet it does. Take a look at the top of the Space Quest II screen shot below:</p>

<p><img src="/images/SQ2.png" width="100%" /></p>

<p>That particular piece of C source code is responsible for displaying the white status bar at the top of the game screen, with the black text that shows the user what their current score is. This source code is a part of the AGI interpreter itself!</p>

<p>This is just one small example. Scrolling through more of the unused sectors in the hex editor reveals that there is a large amount of such source code. Not only that but the code is stored over consecutive sectors, i.e. it is not fragmented as you might expect, so it is relatively easy to extract this data from the disk and then split it up into separate C source files. The split points are easy to determine, since each file has a comment at the top saying what the name of the source file is. Splitting into the separate files gives us 93 files in total, made up of 75 C source files, 16 assembly language source files, and 2 DOS BAT files:</p>

<p><img src="/images/List_of_AGI_Source_Files.png" width="100%" /></p>

<p>In total, there are over 15000 lines of code and most of these files are complete.</p>

<p>It turns out that this Space Quest 2 game disk contains about 70% of the source code of Sierra On-Line’s AGI interpreter, complete with comments and change history. We will look at how that 70% figure was calculated later in the article.</p>

<p>When we say “the AGI interpreter”, what we are referring to is the file named “AGI” in the directory listing that was shown earlier, along with the the .OVL files. The “AGI” file is the executable that performs the actual interpretation of the game data contained in the LOGDIR, PICDIR, VIEWDIR, SNDDIR, OBJECT, WORDS.TOK and VOL files.</p>

<h2 id="source-file-change-history">Source file change history</h2>

<p>Some of the source files contain a change history within the header comment at the top. The following is an example from the ANIMATE.C source file:</p>

<p><img src="/images/Source_File_Header_Comment.png" width="100%" /></p>

<p>The header comment starts by stating the name of the source file. It then gives a short description of what it does, in this case to “Handle one cycle of animation in an adventure game”. Next we have a line that says “compile: MWC”. This appears to state the name of the C compiler that was used to compile the C source. MWC was a C compiler from the Mark Williams company that was very popular in those days. After that, the header has a “Change History” section and this makes for very interesting reading. It mentions the date, time, initials of the person making the change, and a description of the change.</p>

<p>The programmers have identied themselves using their initials, including their middle name initial. JAS is none other than Jeff Stephenson, the main programmer who was working on the AGI interpreter code, and DCI is Chris Iden. If you look at the game credits for the AGI games from 1985-89, you will see that Jeff and Chris were always credited with working on the game development system.</p>

<p><img src="/images/Space_Quest_2_Credits.png" width="100%" /></p>

<p>Robert Heitman is also listed, but his focus was mainly on the graphics tools (i.e. the Picture Editor (PE) and View Editor (VE)), whereas Jeff and Chris worked primarily on the interpreter code. So it isn’t surprising to see their initials in the change history for the C source code of the AGI interpreter.</p>

<h2 id="agiexe-memory-map">AGI.EXE memory map</h2>

<p>In addition to the 93 AGI interpreter source code files, the SQ2 2.0D 720KB Disk 1 disk also contains over 2000 lines of a Memory Map of the AGI.EXE executable. For production releases of AGI games, the interpreter executable is called simply AGI, and is not directly executable, but during the development of the games, Sierra On-Line used a version of the interpreter that had the .EXE extension and could be run directly. Someone at Sierra generated a memory map of the AGI.EXE executable, i.e. the AGI interpreter, on the 7th October 1987. A small section of it is shown below:</p>

<p><img src="/images/AGI_Memory_Map.png" width="100%" /></p>

<p>This gives a potential date to the collection of source code files. It ties in with the most recent change history comment in the code, which is from September 1987.</p>

<p>The memory map also gives us a fairly complete list of the modules and source files that make up the AGI interpreter. Each module specifies the file that it is defined in. If we count the number of distinct source files mentioned, it comes to 98. The number of those source files that are present in full on the SQ2 disk is 71. This is where we get the figure of roughly 70% for the amount of AGI interpreter source code that is on the SQ2 disk. For some of the modules, only the C header file is included, and so those modules are not included in this calculation.</p>

<h2 id="sierras-intellectual-property">Sierra’s intellectual property</h2>

<p>In 1984, Sierra On-Line was lucky to survive as a business. Some time around the release of King’s Quest, in May/June of that year, Ken Williams had to lay off around 100 of his employees, reducing the head count from approximately 130 down to about 30. Although it was a struggle, part of what turned their fortunes around was the success of the AGI adventure game system and the games that were built using it. By the end of 1984, King’s Quest had entered the top 20 on the software sales charts for computer games. It would stay there for the next half year, right up until the release of King’s Quest II, and it wasn’t long until King’s Quest II was also in the charts. This was helped a lot by the deal that Sierra made with Tandy Radio Shack to sell Tandy versions of the games in the Radio Shack stores.</p>

<p>The AGI games continued to be best sellers from 1985 to 1988. Over those years, the AGI interpreter was Sierra On-Line’s primary means of making money and therefore a core part of their intellectual property. They not only invented the 3D animated graphic adventure game genre but also had a head start on their competitors that lasted several years. It is safe to say that the source code for the AGI interpreter is something that Sierra would have preferred didn’t fall into the hands of their competitors.</p>

<p>For 70% of the source code to end up being copied en masse and sent out to tens, if not hundreds of thousands of their customers, was a big blunder.</p>

<h2 id="how-did-this-happen">How did this happen?</h2>

<p>When a new release version of a game was prepared by Sierra, it involved creating a “production copy” master disk to be used by the FormMaster disk duplication machine. This machine did not simply copy files from the the master disk to each copy but instead copied every byte of every sector of the disk, regardless of whether the sectors were currently being used or not. What this meant in the case of the version 2.0D and 2.0F SQ2 Disk 1 disks is that it also copied the supposedly unused 402,432 bytes, despite there being no reason to. This is why the preparation of a master disk involved ensuring that it started out fully formatted before the game was copied on to it, and for the most part, Sierra On-Line got this step right. Most original Sierra game disks were formatted before use. It would seem that someone forgot to do this for the Space Quest II version 2.0D Disk 1 disk, and the same disk was then also used for version 2.0F of SQ2.</p>

<p>This meant that potentially hundreds of thousands of SQ2 disks sent to customers and retail stores had a copy of 70% of the AGI interpreter source code hidden on them.</p>

<h2 id="dodged-a-bullet">Dodged a bullet</h2>

<p>It was almost certainly an unintentional mistake. Surprisingly, no one appeared to have noticed that this happened, not Sierra, not their competitors or their customers, and it was only discovered decades later, the first known discovery of it by online user NewRisingSun in October 2016.</p>

<p>It is also fortuitous that this happened at the end of the AGI era. In March 1988, Sierra had already developed their SCI adventure game system and were about to release the first game using it, which would be King’s Quest IV. So accidentally leaking the AGI interpreter source code into the public, should someone have noticed it, may not have been the issue it could have been only a year or two before that.</p>

<p>It makes for an interesting digital archeaology story though, and it allows us, 36 years later, to see how our heros at Sierra On-Line wrote their AGI interpreter.</p>

<p><br /></p>

<hr />
<p><em>For those who are interested, I have uploaded the extracted AGI interpreter source code into a github repo: <a href="https://github.com/lanceewing/agi">https://github.com/lanceewing/agi</a></em></p>

<p><em>Check out <a href="https://agi.sierra.games/">AGILE</a>, the web based AGI interpreter, whose implementation was guided in part by the original AGI source code.</em></p>

<hr />]]></content><author><name>Lance Ewing</name></author><category term="blog" /><category term="sierra" /><category term="agi" /><category term="sq2" /><summary type="html"><![CDATA[There is nothing unusual about the outside of these disks, but there is something unique about the data that is stored on them, something that Sierra On-Line would have been totally unaware of and certainly wouldn’t have wanted them to include.]]></summary></entry><entry><title type="html">AGILE - Celebrating 40 years of King’s Quest</title><link href="/blog/sierra/agi/agile/kq1/2024/05/10/agile-celebrating-40-years-of-kings-quest.html" rel="alternate" type="text/html" title="AGILE - Celebrating 40 years of King’s Quest" /><published>2024-05-10T05:00:00+00:00</published><updated>2024-05-10T05:00:00+00:00</updated><id>/blog/sierra/agi/agile/kq1/2024/05/10/agile-celebrating-40-years-of-kings-quest</id><content type="html" xml:base="/blog/sierra/agi/agile/kq1/2024/05/10/agile-celebrating-40-years-of-kings-quest.html"><![CDATA[<p>The 10th May 2024 marks the 40th anniversary of King’s Quest, the game that started a whole new genre: the animated graphic adventure. To celebrate the event, and as a tribute to the game, I am today releasing <a href="https://agi.sierra.games">AGILE</a>, a way to play Sierra On-Line’s AGI adventure games directly in the web browser!</p>

<p><img src="/images/kq1_web_desktop.jpg" alt="King's Quest Screenshot" /></p>

<p>🎂 <strong>Happy 40th Anniversary to King’s Quest!!</strong> 🥂</p>

<p>Give it <a href="https://agi.sierra.games">a try</a>!</p>

<p>Note that this isn’t a conversion of the original games into another form, so is quite different to <a href="http://www.sarien.net/">sarien.net</a> in that regard. AGILE is instead a nearly 100% accurate implementation of the <a href="https://en.wikipedia.org/wiki/Adventure_Game_Interpreter">AGI interpreter</a> that was used by Sierra to run those games, and being so, it will run the games from the original game files that you may still have lying around somewhere from when you first played them back in the 80s!</p>

<p>Most of the original Sierra On-Line games are still available for purchase online, so for legal reasons, AGILE does not come prepackaged with those games. You must have your own copy and use the import feature to load the game into AGILE. It supports importing from both a folder containing the game, or a ZIP file containing the game. After it has been imported, it will remain in your browser’s storage so that you won’t have to import it again. To purchase the original games online (should you no longer have them on your bookshelf), check out <a href="https://www.gog.com/">gog.com</a> and <a href="https://store.steampowered.com/">Steam</a>.</p>

<h2 id="how-to-run-the-games">How to run the games</h2>

<p>Start by going to <a href="https://agi.sierra.games">https://agi.sierra.games</a></p>

<p>AGILE should run on most modern web browsers. It does, however, rely on some browser APIs that are relatively recent, such as the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer">SharedArrayBuffer</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system">Origin Private File System</a>. It has been tested on Chrome, Edge, Firefox and Safari, both the desktop and mobile versions. If it doesn’t work for you, then check to make sure that you have updated your browser to the latest version.</p>

<p>It starts by showing the title page, as shown below:</p>

<p><img src="/images/title_page_web_desktop.jpg" alt="AGILE Title Page" /></p>

<p>There is a small question mark icon in the top right that pops up a dialog with a brief description. And take note of the pagination indicator dots at the bottom of the screen. The screen works in a very similar way to the user interface of a mobile device. If you are accessing the website on a touch screen device, then you can swipe to the right to get to the next page. If you are on desktop, you can use the right arrow key, or drag/fling with your mouse, or click on the small right arrow at the bottom of the screen.</p>

<p>The first page to the right has thumbnails for the original Sierra On-Line games. Notice that they are all faded out:</p>

<p><img src="/images/games_page_web_desktop_faded_out.jpg" alt="Sierra Games Page" /></p>

<p>This is to indicate that they are not imported yet. As mentioned above, this is for legal reasons. If you click on one of these, it will open a dialog telling you that you must import your own copy of the game for legal reasons. It also asks you if you would like to import from a ZIP file or a folder. After completing the import process, the thumbnail for the game will no longer be faded out. The game is now imported into the OPFS storage in your browser. If you click on the thumbnail again, it will run the game!</p>

<p>In addition to supporting the import of original Sierra games, AGILE comes pre-packaged with over 100 fan-made AGI games and demos; simply swipe again to the right to see several pages of them:</p>

<p><img src="/images/games_page_2_web_desktop.jpg" alt="Fan-made Games Page" /></p>

<p>These are games that fans of Sierra On-Line built themselves to run on the same AGI interpreter system and so they will therefore also run on AGILE. I have included them so that they and their authors can share in the 40th anniversary celebration. For those games that do not have in-game credits, please refer to the following web page to see who the authors were:</p>

<p><a href="http://agiwiki.sierrahelp.com/index.php/Fan_AGI_Release_List_(Sortable)">Fan AGI Release List</a></p>

<p>Be sure to check out <a href="https://agi.sierra.games/#/id/ltec">Let Them Eat Cake</a>, a new fan-made game written by Russ Danner to commemorate the 40th anniversary of King’s Quest! It’s well worth a play, and is a great tribute to the original game.</p>

<h2 id="how-does-it-work">How does it work?</h2>

<p>The story of AGILE begins several years ago when I first wrote a version of the <a href="https://github.com/lanceewing/agile">interpreter in C#</a>. That version runs as a desktop application. Development on the new web version of AGILE began in November 2023, in hopes that it would be fully complete by the 10th May 2024, to be a tribute to, and to coincide with, the 40th anniversary of the release of King’s Quest.</p>

<p>It started out as a port of the C# AGILE Sierra AGI interpreter project to Java, using the <a href="https://libgdx.com/">libGDX</a> cross-platform development framework, targeting Desktop, Android and HTML5/GWT. The AGILE interpreter code itself is a straight conversion of the C# code to Java, but for the “AGI Library” part, it is instead using a stripped down version of the <a href="https://github.com/lanceewing/jagi">JAGI</a> project, which was already written in Java and already had code for loading AGI resources. JAGI was originally written by a mysterious author known as Dr Zoltan and was further extended by myself and Mark Yu.</p>

<p>The reason why the libgdx framework was chosen (and why the JAGI bit is stripped down to the bare minimum), was to get it working as a web app, by primarily targeting the GWT/HTML libgdx platform. <a href="https://www.gwtproject.org/">GWT</a> (i.e. the Google Web Toolkit) is used by libgdx to transpile the Java code into JavaScript, thus the reason why it is written mostly in Java but is able to run on the web. Some parts had to be written in native JavaScript, where libgdx and GWT didn’t provide an out of the box way of doing something.</p>

<p>JavaScript is by default single threaded, which isn’t compatible with how AGI blocks waiting for input in some scenarios. The browser tab would simply hang. To address that issue, a web worker was needed, as it allows code to be run outside of the browser’s main UI thread. Unfortunately, libgdx and GWT did not provide direct access to that, but by using a project called <a href="https://gitlab.com/ManfredTremmel/gwt-webworker">gwt-webworker</a> written by Manfred Trammel, I was able to get libgdx to support running the code in a web worker. I then used the SharedArrayBuffer as the primary means of communication between the browser UI thread and the web worker, by both sides having shared simultaneous access to certain parts of the AGI interpreter state.</p>

<p>For those who are interested, the source code can be seen here:</p>

<p><a href="https://github.com/lanceewing/agile-gdx">https://github.com/lanceewing/agile-gdx</a></p>

<p>I hope you all have fun <a href="https://agi.sierra.games">trying it out</a> and reminiscing about King’s Quest and the other AGI adventure games!</p>

<p>And finally, a big thank you to our heroes who 40 years ago created King’s Quest. To Ken &amp; Roberta Williams, Arthur Abraham, Charles Tingley, Greg Rowland, Ken MacNeill and Doug MacNeill: Thank you for bringing to life such an amazing and enduring concept, and a game and franchise that we will continue to remember for decades to come.</p>]]></content><author><name>Lance Ewing</name></author><category term="blog" /><category term="sierra" /><category term="agi" /><category term="agile" /><category term="kq1" /><summary type="html"><![CDATA[The 10th May 2024 marks the 40th anniversary of King’s Quest, the game that started a whole new genre: the animated graphic adventure. To celebrate the event, and as a tribute to the game, I am today releasing AGILE, a way to play Sierra On-Line’s AGI adventure games directly in the web browser!]]></summary></entry><entry><title type="html">Using Emojis in a Point and Click Adventure - Part 3</title><link href="/blog/js13k/2020/10/14/using-emojis-in-a-point-and-click-adventure-part-3.html" rel="alternate" type="text/html" title="Using Emojis in a Point and Click Adventure - Part 3" /><published>2020-10-14T21:47:55+00:00</published><updated>2020-10-14T21:47:55+00:00</updated><id>/blog/js13k/2020/10/14/using-emojis-in-a-point-and-click-adventure-part-3</id><content type="html" xml:base="/blog/js13k/2020/10/14/using-emojis-in-a-point-and-click-adventure-part-3.html"><![CDATA[<p>Emojis can be used in a variety of ways as part of creating a point and click adventure. We can use them to compose the background scenes, the game sprites, the user interface, and for mouse cursors. We could also use one for the favicon.</p>

<p>My goal for the 2020 js13kgames contest this year was to use them for as much as I could.</p>

<h2 id="scene-composition">Scene Composition</h2>

<p>Composing a scene <em>solely</em> with emojis isn’t something that is 100% achievable. We can use things like buildings, trees, and vehicles to build up a scene, but there are no emojis that we could use for the background; well, not ones that are consistent across all platforms. Perhaps the cityscape emojis could be useful as backgrounds in some situations, but what we really need is something more generic.</p>

<p>We have to start with some kind of default background. Since most of my locations were outside, and with buildings, I decided a red brick wall was something that most would have in common. I also added a blue sky, with a gradient, some green grass, a footpath and a road.</p>

<p><img src="/images/game_background.png" alt="image info" /></p>

<p>The above is all created with CSS, most of it as a single linear gradient. On top of this, I added the emojis that made up the current scene:</p>

<p><img src="/images/game_emojis_on_background.png" alt="image info" /></p>

<p>All of these larger sized emojis, that are used to compose the background scene itself, are drawn on individual canvas elements using the renderEmoji method shown in the <a href="/blog/js13k/2020/09/27/using-emojis-in-a-point-and-click-adventure-part-1.html">first part in this series</a>. The choice for drawing them on a canvas, rather than simply as text, was originally to support Windows 10 emojis, as discussed in that post.</p>

<p>As the game progressed, I discovered that there were other benefits to having them drawn on a canvas, which we will cover in a later section of this article entitled “Determing the thing”.</p>

<h2 id="different-shades">Different Shades</h2>

<p>Sometimes the default appearance of an emoji, such as a tree, gets a bit monotonous. In “The King’s Missing Page”, I use a row of trees in the background for most of the scenes. This looked a bit plain when there wasn’t some kind of variation:</p>

<p><img src="/images/emoji_variations_1.png" alt="image info" /></p>

<p>Applying a CSS filter to darken one of the rows of trees makes it look a lot more interesting:</p>

<figure class="highlight"><pre><code class="language-css" data-lang="css"><span class="nc">.dark</span> <span class="p">{</span>
  <span class="nl">filter</span><span class="p">:</span> <span class="n">brightness</span><span class="p">(</span><span class="m">0.4</span><span class="p">)</span> <span class="n">saturate</span><span class="p">(</span><span class="m">3</span><span class="p">);</span>
<span class="p">}</span></code></pre></figure>

<p><img src="/images/emoji_variations_2.png" alt="image info" /></p>

<p>The same technique could be used to apply some kind of variation to any of the emojis.</p>

<p>The unicode standard itself supports variations to certain types of emoji, such as those showing <a href="https://unicode.org/emoji/charts/full-emoji-modifiers.html">skin colour</a>, using something called “modifiers”, but for a tree, a building or a vehicle, there isn’t such a mechanism at the current time, which is why a CSS filter is useful to introduce a bit of variation.</p>

<h2 id="point-and-click-interface">Point and Click Interface</h2>

<p>“The King’s Missing Page” uses an old school graphic adventure point-and-click user interface, similar to those seen in some of the <a href="https://en.wikipedia.org/wiki/Sierra_Entertainment">Sierra On-line</a> games, such as King’s Quest 5 and 6. It is very easy to create such an interface using emojis, due to there being emoji characters that look virtually the same as the icons that were used in those old school games:</p>

<p>A walking person, an eye, a hand, and a speech bubble. These are all familiar to those who have played such classic games:</p>

<p><img src="/images/point_and_click_interface.png" alt="image info" /></p>

<p>I designed my game so that only five action verbs were required: “Walk to”, “Look at”, “Pick up”, “Talk to” and “Use”. Four of these are represented by the four icons on the left of the image above.</p>

<p>The rest of the “action” bar is taken up by the inventory, a scrollable area inside which emojis are also used. The unicode standard includes emojis that cover a wide range of different inventory items that could be found in an adventure game, and they all have the look that you’d expect from such a game. It’s a perfect source for item images.</p>

<p>For all of the emojis on the “action” bar, whether they be verb icons, or item icons, they are rendered simply as emoji text.</p>

<figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"commands"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;span</span> <span class="na">data-name=</span><span class="s">"Walk to"</span><span class="nt">&gt;</span>🚶<span class="nt">&lt;/span&gt;</span>
    <span class="nt">&lt;span</span> <span class="na">data-name=</span><span class="s">"Look at"</span><span class="nt">&gt;</span>👁️<span class="nt">&lt;/span&gt;</span>
    <span class="nt">&lt;span</span> <span class="na">data-name=</span><span class="s">"Pick up"</span><span class="nt">&gt;</span>🤚🏼<span class="nt">&lt;/span&gt;</span>
    <span class="nt">&lt;span</span> <span class="na">data-name=</span><span class="s">"Talk to"</span><span class="nt">&gt;</span>💬<span class="nt">&lt;/span&gt;</span>
<span class="nt">&lt;/div&gt;</span>

<span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"itemlist"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;span</span> <span class="na">data-name=</span><span class="s">"map"</span><span class="nt">&gt;</span>🗺️<span class="nt">&lt;/span&gt;</span>
    <span class="nt">&lt;span</span> <span class="na">data-name=</span><span class="s">"rose"</span><span class="nt">&gt;</span>🌹<span class="nt">&lt;/span&gt;</span>
    <span class="nt">&lt;span</span> <span class="na">data-name=</span><span class="s">"tulip"</span><span class="nt">&gt;</span>🌷<span class="nt">&lt;/span&gt;</span>
    <span class="nt">&lt;span</span> <span class="na">data-name=</span><span class="s">"briefcase"</span><span class="nt">&gt;</span>💼<span class="nt">&lt;/span&gt;</span>
    <span class="nt">&lt;span</span> <span class="na">data-name=</span><span class="s">"bank card"</span><span class="nt">&gt;</span>💳<span class="nt">&lt;/span&gt;</span>
    <span class="nt">&lt;span</span> <span class="na">data-name=</span><span class="s">"water pistol"</span><span class="nt">&gt;</span>🔫<span class="nt">&lt;/span&gt;</span>
    <span class="nt">&lt;span</span> <span class="na">data-name=</span><span class="s">"cash"</span><span class="nt">&gt;</span>💵<span class="nt">&lt;/span&gt;</span>
<span class="nt">&lt;/div&gt;</span></code></pre></figure>

<p>There is no need to render these ones on a canvas as was done for the scene composition. These action bar emojis are drawn a lot smaller, making the black outline seen on Windows emojis far less noticeable.</p>

<h2 id="mouse-cursors">Mouse Cursors</h2>

<p>Whenever a verb icon is clicked, the mouse cursor changes to match that verb icon. Likewise, when an inventory item is clicked <em>first</em> (i.e. not after clicking on a verb icon), then the “Use” verb is implied, and the mouse cursor changes to match the inventory item.</p>

<p>All of these mouse cursors can be rendered using emojis. When the game first starts up, it renders the various mouse cursors up front, using the same rendering code used for rendering the sprites on the screen (the renderEmoji code can be seen in <a href="/blog/js13k/2020/09/27/using-emojis-in-a-point-and-click-adventure-part-1.html">part 1 of this series</a>). A mouse cursor uses a much smaller emoji size though.</p>

<p>The following code shows how the mouse cursors are rendered:</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="c1">// Initalise the mouse cursors.</span>
<span class="c1">// Note: Firefox ignores custom cursors bigger than 32x32 when near the Window edge.</span>
<span class="kd">let</span> <span class="nx">cursorSize</span> <span class="o">=</span> <span class="nb">navigator</span><span class="p">.</span><span class="nx">userAgent</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="sr">/Firefox/</span><span class="p">)?</span> <span class="mi">32</span> <span class="p">:</span> <span class="mi">50</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">cursors</span> <span class="o">=</span> <span class="p">{};</span>

<span class="p">[</span><span class="dl">'</span><span class="s1">🚶</span><span class="dl">'</span><span class="p">,</span><span class="dl">'</span><span class="s1">🤚🏼</span><span class="dl">'</span><span class="p">,</span><span class="dl">'</span><span class="s1">⬆️</span><span class="dl">'</span><span class="p">,</span><span class="dl">'</span><span class="s1">💬</span><span class="dl">'</span><span class="p">,</span><span class="dl">'</span><span class="s1">⬇️</span><span class="dl">'</span><span class="p">,</span><span class="dl">'</span><span class="s1">⏳</span><span class="dl">'</span><span class="p">,</span><span class="dl">'</span><span class="s1">↖️</span><span class="dl">'</span><span class="p">,</span><span class="dl">'</span><span class="s1">👁️</span><span class="dl">'</span><span class="p">,</span><span class="dl">'</span><span class="s1">⬅️</span><span class="dl">'</span><span class="p">,</span><span class="dl">'</span><span class="s1">➕</span><span class="dl">'</span><span class="p">,</span><span class="dl">'</span><span class="s1">↗️</span><span class="dl">'</span><span class="p">,</span><span class="dl">'</span><span class="s1">🤏🏼</span><span class="dl">'</span><span class="p">,</span><span class="dl">'</span><span class="s1">➡️</span><span class="dl">'</span><span class="p">,</span><span class="dl">'</span><span class="s1">❔</span><span class="dl">'</span><span class="p">,</span><span class="dl">'</span><span class="s1">↙️</span><span class="dl">'</span><span class="p">,</span><span class="dl">'</span><span class="s1">🔍</span><span class="dl">'</span><span class="p">,</span><span class="dl">'</span><span class="s1">↘️</span><span class="dl">'</span><span class="p">]</span>
<span class="p">).</span><span class="nx">forEach</span><span class="p">((</span><span class="nx">c</span><span class="p">,</span><span class="nx">i</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="c1">// Calculate hotspot Y position for cursor.</span>
    <span class="kd">let</span> <span class="nx">hotspotY</span> <span class="o">=</span> <span class="p">[</span><span class="nx">cursorSize</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="nx">cursorSize</span><span class="o">/</span><span class="mi">2</span><span class="p">][</span><span class="nx">i</span> <span class="o">%</span> <span class="mi">2</span><span class="p">];</span>
    <span class="c1">// Render emoji at cursorSize and build CSS custom cursor definition.</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">cursors</span><span class="p">[</span><span class="nx">c</span><span class="p">]</span> <span class="o">=</span> <span class="s2">`url(</span><span class="p">${</span><span class="nx">Util</span><span class="p">.</span><span class="nx">renderEmoji</span><span class="p">(</span><span class="nx">c</span><span class="p">,</span> <span class="nx">cursorSize</span><span class="p">,</span> <span class="nx">cursorSize</span><span class="p">)[</span><span class="mi">0</span><span class="p">].</span><span class="nx">toDataURL</span><span class="p">()}</span><span class="s2">) </span><span class="p">${</span><span class="nx">cursorSize</span><span class="o">/</span><span class="mi">2</span><span class="p">}</span><span class="s2"> </span><span class="p">${</span><span class="nx">hotspotY</span><span class="p">}</span><span class="s2">, auto`</span><span class="p">;</span>
    <span class="c1">// Add custom property to body tag with name matching emoji char and value the CSS cursor value.</span>
    <span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">setProperty</span><span class="p">(</span><span class="s2">`--</span><span class="p">${</span><span class="nx">c</span><span class="p">}</span><span class="s2">`</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">cursors</span><span class="p">[</span><span class="nx">c</span><span class="p">]);</span>
<span class="p">});</span></code></pre></figure>

<p>The rendered cursors are stored in two different places: One place is in an instance variable within the game, and the other place is as a CSS custom property on the document body tag. The latter case is so that CSS declarations can refer to these cursors.</p>

<h2 id="cursor-size-restrictions">Cursor Size Restrictions</h2>

<p>As mentioned in the comments included in the above code snippet, Firefox behaves strangely when a mouse cursor is greater than 32x32. This is for security reasons and is discussed in this <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1445844">Mozilla ticket</a>. The security concern is that large mouse cursors can be used to deliberately trick users when drawn over the native UI of the browser, something that tech support scammers have taken advantage of.</p>

<p>In Firefox, if the cursor is bigger than 32x32, and it moves close to the edge of the window, then the custom cursor disappears. When I say “close to”, it appears to happen when any part of the cursor starts moving outside of the window, rather than when the hotspot moves outside of the window. This is really quite noticeable. For a 50x50 custom mouse cursor, it switches to the default pointer cursor and its quite obvious that the hotspot of the pointer is still 25 pixels from the edge.</p>

<p>This is unfortunate, since rendering color mouse cursors at a size of 50x50 looks much nicer than 32x32.</p>

<p>Chrome also has <a href="https://bugs.chromium.org/p/chromium/issues/detail?id=880863">this ticket</a> related to the same issue, but they appear to have handled it in a different way. For cursors larger than 32x32 in size, the cursor doesn’t disappear until the hotspot moves outside of the window. This is more in line with what the user would expect. By playing the game, the user becomes familiar with where the cursor hotspot is, which is why the behaviour in Firefox is quite jarring.</p>

<p>Due to the Firefox behaviour, “The King’s Missing Page” deliberately renders mouse cursors in Firefox at a size of 32x32 to avoid the issue, whereas for all other browsers, it uses 50x50.</p>

<h2 id="cursor-changes">Cursor Changes</h2>

<p>The default verb action in the game is “Walk to”. Whenever a sentence is composed and execued, e.g. “Look at chipmunk” or “Pick up tulip”, the verb switches back to “Walk to”. This verb is a special verb, in that it behaves both as the “Walk to” verb, but also in some cases as if no action verb is selected, such as when the mouse is over the inventory items. The player obviously can’t “Walk to” an inventory item, so in this scenario if an inventory item is clicked on, the “Use” verb is assumed.</p>

<p>When the currently active verb is “Walk to”, then the cursor also changes to various arrows when the mouse passes over certain things on the screen, for example, changing to an upwards arrow when over a building, or downwards arrow when over the road. Certain parts of the footpath should change it to diagonal arrows or left/right arrows.</p>

<p>The mouse cursor also changes to an hour glass (⏳) when user input is disabled.</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="c1">// Update cursor. If input disabled, shown hour glass; otherwise verb icon.</span>
<span class="kd">let</span> <span class="nx">newCursor</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">cursors</span><span class="p">[</span><span class="k">this</span><span class="p">.</span><span class="nx">inputEnabled</span><span class="p">?</span> <span class="k">this</span><span class="p">.</span><span class="nx">verbIcon</span> <span class="p">:</span> <span class="dl">'</span><span class="s1">⏳</span><span class="dl">'</span><span class="p">];</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">newCursor</span> <span class="o">!=</span> <span class="k">this</span><span class="p">.</span><span class="nx">currentCursor</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// If the cursor has changed, update cursor on wrap element.</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">wrap</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">cursor</span> <span class="o">=</span> <span class="nx">newCursor</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">this</span><span class="p">.</span><span class="nx">currentCursor</span> <span class="o">=</span> <span class="nx">newCursor</span><span class="p">;</span></code></pre></figure>

<p>The above code shows the case where we simply update the cursor on the main wrapper element whenever it needs to change. It doesn’t show how the unique behaviour of the “Walk to” verb is handled.</p>

<h2 id="custom-css-property-fallback">Custom CSS Property Fallback</h2>

<p>I struggled for a while to get that particular part of the mouse cursor behaviour to work as I wanted it to. The problem was that I wanted every other verb icon (i.e. the eye, hand and speech bubble) to always be the mouse cursor regardless of what element on the screen the mouse was moving over. Those particular cursors had higher precedence. But for the walking icon, it had the lowest priority and the mouse cursor needed to instead respond to the properties defined in the CSS.</p>

<p>I knew that putting “cursor” CSS rules for the various elements I wanted the cursor to change on was a key part of the solution. But it needed to be conditional on what verb was currently active. I started out by thinking that I could have a single custom CSS property called “–c” that the Javascript could update based on the requirements I’ve described. There just didn’t seem to be a way to make that work though.</p>

<p>I then remembered that the CSS var function supported a fallback value. This got me thinking: If the “–c” custom CSS property was set only when the active verb icon was not “Walk to”, then I could make use of the CSS variable fallback mechanism.</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">verbIcon</span> <span class="o">!=</span> <span class="dl">'</span><span class="s1">🚶</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">wrap</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">setProperty</span><span class="p">(</span><span class="dl">'</span><span class="s1">--c</span><span class="dl">'</span><span class="p">,</span> <span class="nx">newCursor</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">wrap</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">removeProperty</span><span class="p">(</span><span class="dl">'</span><span class="s1">--c</span><span class="dl">'</span><span class="p">);</span>
<span class="p">}</span></code></pre></figure>

<p>With the above bit of Javascript in place, the CSS could then look like the following to achieve what I wanted. Each area of the screen that required this behaviour could define a “cursor” property value with “–c” as the overriding CSS property value, and a fallback to a specific cursor relevant only to that element:</p>

<figure class="highlight"><pre><code class="language-css" data-lang="css"><span class="nc">.building</span> <span class="p">{</span>
  <span class="nl">cursor</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--c</span><span class="p">,</span> <span class="n">var</span><span class="p">(</span><span class="n">--</span><span class="err">⬆️</span><span class="p">));</span>
<span class="p">}</span>
<span class="nc">.road</span> <span class="p">{</span>
  <span class="nl">cursor</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--c</span><span class="p">,</span> <span class="n">var</span><span class="p">(</span><span class="n">--</span><span class="err">⬇️</span><span class="p">));</span>
<span class="p">}</span>
<span class="nc">.left_path</span> <span class="p">{</span>
  <span class="nl">cursor</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--c</span><span class="p">,</span> <span class="n">var</span><span class="p">(</span><span class="n">--</span><span class="err">↗️</span><span class="p">));</span>
<span class="p">}</span>
<span class="nc">.right_path</span> <span class="p">{</span>
  <span class="nl">cursor</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--c</span><span class="p">,</span> <span class="n">var</span><span class="p">(</span><span class="n">--</span><span class="err">↖️</span><span class="p">));</span>
<span class="p">}</span>
<span class="nc">.left_path.down</span> <span class="p">{</span>
  <span class="nl">cursor</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--c</span><span class="p">,</span> <span class="n">var</span><span class="p">(</span><span class="n">--</span><span class="err">↙️</span><span class="p">));</span>
<span class="p">}</span>
<span class="nc">.right_path.down</span> <span class="p">{</span>
  <span class="nl">cursor</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--c</span><span class="p">,</span> <span class="n">var</span><span class="p">(</span><span class="n">--</span><span class="err">↘️</span><span class="p">));</span>
<span class="p">}</span></code></pre></figure>

<h2 id="determining-the-thing">Determining the ‘thing’</h2>

<p>“The King’s Missing Page” has a section of the screen that shows the sentence that is being constructed to tell “Detective Pip” what to do. The sentence is formed from the verb, e.g. “Look at”, and a thing, e.g. “tree”.</p>

<p>The “thing” part of that sentence is usually a sprite within the current scene. These sprites are instances of custom x-sprite elements and contain a canvas element. Both of these elements are rectangular in shape, which is true of most HTML elements.</p>

<p>A mouse move event handler is registered with each of the sprites. This event is fired whenever the mouse moves over the box within which the element exists. The idea of the event handler is to update the “thing” part of the sentence, e.g. “Look at” <strong>“tree”</strong> or “Walk to” <strong>“house”</strong>.</p>

<p>The problem with this is that the visual part of the sprite is never rectangular in shape. For something like a palm tree, quite a lot of that bounding box is transparent. It wouldn’t be correct to update the “sentence” to include the sprite until the mouse cursor is actually over the visual part of the sprite.</p>

<p><img src="/images/palm_tree_box.jpg" alt="image info" /></p>

<p>In some cases, there might be another sprite behind a sprite, e.g. a building or wall behind a tree, so rather than the sentence being updated to include “tree”, if the cursor is over the transparent part of the sprite, then it should instead fallback on the sprite behind it.</p>

<p>The following code shows how this is done:</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="cm">/**
 * Handles mouse move events, primarily so that the 'thing' property is updated 
 * when the mouse moves over objects on the screen. If the object has a canvas,
 * then it uses the image data to determine when exactly the mouse is over the
 * object. If the pixel is transparent at that position, then it falls back on
 * whatever is underneath the object.
 * 
 * @param {MouseEvent} e The MouseEvent for the mouse move event.
 */</span>
<span class="nx">objMouseMove</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// Get's the element that the mouse is moving over.</span>
    <span class="kd">let</span> <span class="nx">target</span> <span class="o">=</span> <span class="nx">e</span><span class="p">.</span><span class="nx">currentTarget</span><span class="p">;</span>

    <span class="c1">// Only certain elements in the game have a canvas, i.e. the game sprites.</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">target</span><span class="p">.</span><span class="nx">canvas</span><span class="p">)</span> <span class="p">{</span>
        <span class="c1">// Map the mouse position to an X/Y position within sprite canvas.</span>
        <span class="kd">let</span> <span class="nx">rect</span> <span class="o">=</span> <span class="nx">target</span><span class="p">.</span><span class="nx">getBoundingClientRect</span><span class="p">();</span> 
        <span class="kd">let</span> <span class="nx">x</span> <span class="o">=</span> <span class="o">~~</span><span class="p">((</span><span class="nx">e</span><span class="p">.</span><span class="nx">clientX</span> <span class="o">-</span> <span class="nx">rect</span><span class="p">.</span><span class="nx">left</span><span class="p">)</span> <span class="o">/</span> <span class="k">this</span><span class="p">.</span><span class="nx">scaleX</span><span class="p">);</span>
        <span class="kd">let</span> <span class="nx">y</span> <span class="o">=</span> <span class="o">~~</span><span class="p">((</span><span class="nx">e</span><span class="p">.</span><span class="nx">clientY</span> <span class="o">-</span> <span class="nx">rect</span><span class="p">.</span><span class="nx">top</span><span class="p">)</span> <span class="o">/</span> <span class="k">this</span><span class="p">.</span><span class="nx">scaleY</span><span class="p">);</span>

        <span class="c1">// Get the pixel data from the canvas.</span>
        <span class="kd">let</span> <span class="p">{</span> <span class="nx">width</span><span class="p">,</span> <span class="nx">height</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">target</span><span class="p">;</span>
        <span class="kd">let</span> <span class="nx">ctx</span> <span class="o">=</span> <span class="nx">target</span><span class="p">.</span><span class="nx">canvas</span><span class="p">.</span><span class="nx">getContext</span><span class="p">(</span><span class="dl">'</span><span class="s1">2d</span><span class="dl">'</span><span class="p">);</span>
        <span class="kd">let</span> <span class="nx">imgData</span> <span class="o">=</span> <span class="nx">ctx</span><span class="p">.</span><span class="nx">getImageData</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">width</span><span class="p">,</span> <span class="nx">height</span><span class="p">);</span>

        <span class="c1">// If the pixel at the X/Y position is transparent, then get sprite underneath.</span>
        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">imgData</span><span class="p">.</span><span class="nx">data</span><span class="p">[(</span><span class="nx">y</span> <span class="o">*</span> <span class="p">(</span><span class="nx">width</span> <span class="o">&lt;&lt;</span> <span class="mi">2</span><span class="p">))</span> <span class="o">+</span> <span class="p">(</span><span class="nx">x</span> <span class="o">&lt;&lt;</span> <span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="mi">3</span><span class="p">])</span> <span class="p">{</span>
            <span class="c1">// Get all elements that have a canvas (i.e. sprites) in current mouse position.</span>
            <span class="kd">let</span> <span class="nx">elements</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">elementsFromPoint</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">clientX</span><span class="p">,</span> <span class="nx">e</span><span class="p">.</span><span class="nx">clientY</span><span class="p">).</span><span class="nx">filter</span><span class="p">(</span><span class="nx">s</span> <span class="o">=&gt;</span> <span class="nx">s</span> <span class="k">instanceof</span> <span class="nx">Sprite</span><span class="p">);</span>
            <span class="c1">// Index 0 is the currentTarget, so index 1 is the one immediately below.</span>
            <span class="nx">target</span> <span class="o">=</span> <span class="nx">elements</span><span class="p">[</span><span class="mi">1</span><span class="p">]?</span> <span class="nx">elements</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="p">:</span> <span class="kc">null</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">this</span><span class="p">.</span><span class="nx">thing</span> <span class="o">=</span> <span class="nx">target</span><span class="p">?</span> <span class="nx">target</span><span class="p">.</span><span class="nx">dataset</span><span class="p">.</span><span class="nx">name</span> <span class="p">:</span> <span class="dl">''</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure>

<h2 id="elements-from-point">Elements From Point</h2>

<p>The mouse position is mapped to a pixel position within the canvas. If that pixel is not transparent, then the “thing” will be the sprite that has received the event. If, however, the pixel is transparent, then the <a href="https://developer.mozilla.org/en-US/docs/Web/API/DocumentOrShadowRoot/elementsFromPoint">document.elementsFromPoint</a> function is invoked to get all elements at the current mouse position.</p>

<p>The elements are in Z order, so the first one will be the sprite that received the mouse move event. The next sprite element in the list will be the one immediately behind it, from which the name of the “thing” will be taken. The code above is looking only at the element immediately behind the current target, but it could be extended to continue looking back through the element list, so as to support more than two sprites overlapping.</p>

<h2 id="people">People</h2>

<p>One of the main limitations of using purely emojis to create an animated point-and-click adventure game is that all the emojis relating to people are of static poses. It isn’t really possible then to use the walking person emoji for your main character, since the movement wouldn’t have any kind of cycling animation, such as moving legs and arms.</p>

<p>I tried to avoid drawing attention to this by using the main character that I had used in my previous games, i.e. “Pip”, which isn’t an emoji. But it left me with a problem as to how to handle the other people in the game. There are plenty of different people emojis, but almost all of them are of the top part of the body.</p>

<p>This gave me an idea that I could use them in a “behind the counter” scene, where only that portrait view of the person is visible. The following is an example from my game of the bride in the church:</p>

<p><img src="/images/apple_church_bride.jpg" alt="image info" /></p>

<p>And this one is of the hotel clerk:</p>

<p><img src="/images/apple_hotel_clerk.jpg" alt="image info" /></p>

<p>This is how I handled inside locations, and I think it worked out quite well.</p>

<h2 id="favicon">Favicon</h2>

<p>As a final point, and although I didn’t do this for “The King’s Missing Page”, it would be trivial to use the same renderEmoji method shown in part one of this series to render and then set the favicon for the page. The following code is an example of how this could be done:</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">let</span> <span class="nx">favicon</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">'</span><span class="s1">favicon</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">favicon</span><span class="p">.</span><span class="nx">href</span> <span class="o">=</span> <span class="nx">Util</span><span class="p">.</span><span class="nx">renderEmoji</span><span class="p">(</span><span class="dl">'</span><span class="s1">👁️</span><span class="dl">'</span><span class="p">,</span> <span class="mi">32</span><span class="p">,</span> <span class="mi">32</span><span class="p">).</span><span class="nx">toDataURL</span><span class="p">();</span></code></pre></figure>]]></content><author><name>Lance Ewing</name></author><category term="blog" /><category term="js13k" /><summary type="html"><![CDATA[Emojis can be used in a variety of ways as part of creating a point and click adventure. We can use them to compose the background scenes, the game sprites, the user interface, and for mouse cursors. We could also use one for the favicon.]]></summary></entry><entry><title type="html">Using Emojis in a Point and Click Adventure - Part 2</title><link href="/blog/js13k/2020/10/04/using-emojis-in-a-point-and-click-adventure-part-2.html" rel="alternate" type="text/html" title="Using Emojis in a Point and Click Adventure - Part 2" /><published>2020-10-04T16:10:19+00:00</published><updated>2020-10-04T16:10:19+00:00</updated><id>/blog/js13k/2020/10/04/using-emojis-in-a-point-and-click-adventure-part-2</id><content type="html" xml:base="/blog/js13k/2020/10/04/using-emojis-in-a-point-and-click-adventure-part-2.html"><![CDATA[<p>In this post, I will be looking at the Emoji cross platform and cross browser issues I faced in creating my <a href="https://js13kgames.com/">js13kgames</a> entry. I hadn’t been working long on <a href="https://2020.js13kgames.com/entries/the-kings-missing-page">“The King’s Missing Page”</a> when I realised just how different the various emoji font files were across the different platforms and browsers.</p>

<p>I also learnt that the frequency at which different operating systems updated their emoji fonts to support new Unicode versions varied, so that some platforms are more likely to be 3 or 4 years behind the standard, whereas others are completely up to date.</p>

<h2 id="the-js13kgames-emoji-rule">The js13kgames emoji rule</h2>

<p>As this year’s contest was starting, I became aware of a js13kgames rule that I hadn’t previously realised was there. It reads as follows:</p>

<blockquote>
  <p>“you are allowed to ask users to live-load a web font to support some characters or emoji on devices that can’t display them properly, but you have to make sure your game will work without them”</p>
</blockquote>

<p>This intrigued me a bit. The <a href="https://github.com/twitter/twemoji">Twemoji</a> font was one such font that could be used. Others include <a href="https://github.com/googlefonts/noto-emoji">Noto Color Emoji</a>, <a href="https://openmoji.org/">OpenMoji</a>, and perhaps <a href="https://www.joypixels.com/">JoyPixels</a> (formerly <a href="https://github.com/eosrei/emojione-color-font">EmojiOne</a>). The above rule was a bit vague on what was allowed though. Could I <em>always</em> load and use the font? That would make the experience consistent across platforms, but it felt a little bit like cheating. The rule states that “you have to make sure your game will work without them”.</p>

<p>I think it is clear from the wording of the rule that you need to make an effort first to use the emoji font that is available to the platform that the game is running on. One solution would be to pop up a message asking the user to choose between the platform specific font or the live loaded font. I couldn’t help feeling like this would distract from the game itself and draw attention to the fact that an external resource was being used.</p>

<h2 id="detecting-the-supported-emoji-unicode-version">Detecting the supported emoji unicode version</h2>

<p>I decided instead to implement an automatic solution, one that would detect whether the platform supported the necessary emoji characters, and only if it didn’t would it “live load” the chosen fallback font. This felt like it was in the spirit of the rules.</p>

<p>The plan I had for my game initially depended on emojis up to and including unicode 12 being available, so I needed a way of detecting whether the platform it was running on supported that version. This seemed a bit tricky at first, but then I discovered that there was a unicode character (\uffff) that would always render the same as an unsupported character (￿). Different platforms render an unsupported emoji character in different ways, but a given platform will render all unsupported emoji characters in the same way.</p>

<p>I was therefore able to render the known unsupported character first and then compare that to the result of attempting to render the Unicode 12 character. If they matched, then Unicode 12 was not supported and it would automatically enable the fallback font.</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="cm">/**
 * Detects what Emoji Unicode version is available by default.
 */</span>
<span class="kd">static</span> <span class="nx">detectEmojiVersion</span><span class="p">()</span> <span class="p">{</span>
    <span class="c1">// The game requires at least Unicode 12 to work without Twemoji. We </span>
    <span class="c1">// work out whether it supports Unicode 12 by rendering a known </span>
    <span class="c1">// unsupported char and then compare it to what is rendered for a </span>
    <span class="c1">// Drop of Blood, which is part of Unicode 12.</span>
    <span class="kd">let</span> <span class="nx">NO_EMOJI</span> <span class="o">=</span> <span class="nx">Util</span><span class="p">.</span><span class="nx">renderEmoji</span><span class="p">(</span><span class="dl">'</span><span class="se">\</span><span class="s1">uffff</span><span class="dl">'</span><span class="p">,</span> <span class="mi">100</span><span class="p">,</span> <span class="mi">100</span><span class="p">).</span><span class="nx">toDataURL</span><span class="p">();</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">Util</span><span class="p">.</span><span class="nx">renderEmoji</span><span class="p">(</span><span class="dl">'</span><span class="s1">🩸</span><span class="dl">'</span><span class="p">,</span> <span class="mi">100</span><span class="p">,</span> <span class="mi">100</span><span class="p">).</span><span class="nx">toDataURL</span><span class="p">()</span> <span class="o">==</span> <span class="nx">NO_EMOJI</span><span class="p">))</span> <span class="p">{</span>
        <span class="nx">Util</span><span class="p">.</span><span class="nx">twemoji</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
        <span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="dl">'</span><span class="s1">twemoji</span><span class="dl">'</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>With that class added to the body, the game can then dynamically load the chosen fallback font. If there is a @font-face declaration block for the font, the browser will not automatically load it. But if one of the CSS selector blocks becomes active, perhaps in response to the “twemoji” class being added to the DOM in the example above, then the browser will download the font.</p>

<figure class="highlight"><pre><code class="language-css" data-lang="css"><span class="k">@font-face</span> <span class="p">{</span> 
  <span class="nl">font-family</span><span class="p">:</span> <span class="s1">"twemoji"</span><span class="p">;</span>
  <span class="nl">src</span><span class="p">:</span> <span class="sx">url('https://lanceewing.github.io/twemoji.ttf')</span><span class="p">;</span>
<span class="p">}</span>
<span class="c">/* The browser will only download the twemoji font if the twemoji class is present */</span>
<span class="nc">.twemoji</span> <span class="nf">#controls</span> <span class="p">{</span>
  <span class="nl">font-family</span><span class="p">:</span> <span class="s1">"twemoji"</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure>

<p>By using a technique like this, the font is only downloaded when the platform the game is running on doesn’t support the necessary emoji version.</p>

<p>In the example above, we used Twemoji as the fallback font. As mentioned earlier, there are several other options we could have gone with and perhaps Twemoji wasn’t necessarily the best one. In order to make the best choice, I had to learn more about the various available fonts and formats, and their pros and cons.</p>

<h2 id="emoji-fonts-and-formats">Emoji fonts and formats</h2>

<p>The automatic fallback mechanism described above is just that: a fallback mechanism. The normal case would hopefully be when the platform already supports the required unicode version. So let’s have a look at those “standard/default” fonts first, alongside some of the other alternatives.</p>

<p>The following image shows how a single emoji character appears differently depending on what font is used (images sourced from <a href="https://emojipedia.org/house-with-garden/">Emojipedia</a>):</p>

<p><img src="/images/different-emoji-fonts.png" alt="image info" /></p>

<p>The examples on the left are the default emoji fonts on particular operating systems, whereas those on the right are not associated with any particular OS. These eight are not the only Emoji fonts in use, but at the time of writing, they are probably the main ones that are still under active development. Some are available for free use, some for a fee, and others are fully proprietary and restricted solely to certain operating systems, apps or devices.</p>

<table>
  <thead>
    <tr>
      <th>Provider</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Apple</td>
      <td>Known as “Apple Color Emoji”. Used on Apple Macs, iPhones and iPads. Not available otherwise.</td>
    </tr>
    <tr>
      <td>Google</td>
      <td>Known as “Noto Color Emoji”. Used on most Android devices. Comes by default on many Linux distributions. Available for free use.</td>
    </tr>
    <tr>
      <td>Microsoft</td>
      <td>Known as “Segoe UI Emoji”. Used only on Windows machines. Not available otherwise.</td>
    </tr>
    <tr>
      <td>Samsung</td>
      <td>Used on Samsung Android devices. Not available otherwise.</td>
    </tr>
    <tr>
      <td>Twitter</td>
      <td>Known as “Twemoji”. Used on Twitter. Available for free use.</td>
    </tr>
    <tr>
      <td>Facebook</td>
      <td>Used on Facebook. Not available otherwise.</td>
    </tr>
    <tr>
      <td>JoyPixels</td>
      <td>Low-res version available for free use. Paid version at higher res. Formerly called EmojiOne.</td>
    </tr>
    <tr>
      <td>OpenMoji</td>
      <td>Free, open source Emoji font.</td>
    </tr>
  </tbody>
</table>

<p>Games submitted for the js13kgames competition are likely to encounter only the following emoji fonts: Segoe UI Emoji (Windows), Apple Color Emoji (Mac/iPhone/iPad), Noto Color Emoji (Android/Linux), and Samsung.</p>

<p>As we can see in the image above, the same emoji looks quite different across those four emoji fonts. The Apple, Noto, and Samsung fonts are all bitmap fonts that look like hand painted pictures. The Windows font, on the other hand, is a vector font with layers of flat colours, and has the thick black outline that was discussed at length in <a href="/blog/js13k/2020/09/27/using-emojis-in-a-point-and-click-adventure-part-1.html">part 1</a> of this series of articles.</p>

<p>These are not the only differences. Each vendor has drawn the house image itself in a different way. A different number of windows. Some with a chimney, some without. Even when they have the same features, those features don’t always align in the same places. These differences mean that a game can’t rely too much on what the emoji looks like. They have to be treated in a more generic way.</p>

<h2 id="multi-color-glyphs">Multi-color Glyphs</h2>

<p>In addition to a different appearance, there are several standards and formats for how Color Emojis are stored in a font file. Microsoft, Google, Apple, and Mozilla/Adobe all came up with different formats, and unfortunately they’re all being used.</p>

<p>They all do essentially the same thing, which is to extend the standard font file formats to include color image data, but they do this in different ways.</p>

<table>
  <thead>
    <tr>
      <th>Format</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>COLR/CPAL</td>
      <td>Created by Microsoft. Uses standard font glyphs, but with multiple layers of different colors. As a consequence, uses flat colors (i.e. no shading). Vector format (not SVG though), so scales well. Surprisingly quite widely supported. Examples: “Segoe UI Emoji”, “EmojiOneMozilla”, “TwemojiMozilla”</td>
    </tr>
    <tr>
      <td>CBDT/CBLC</td>
      <td>Created by Google. Replaces standard font glyphs with PNG bitmap format. Doesn’t scale well. Larger font sizes will blur pixels. Example: “Noto Color Emoji”</td>
    </tr>
    <tr>
      <td>SBIX</td>
      <td>Created by Apple. Emoji data stored as PNG bitmap data, so also doesn’t scale well. Not used outside the Apple ecosystem. Example: “Apple Color Emoji”</td>
    </tr>
    <tr>
      <td>OpenType-SVG</td>
      <td>Created by Mozilla and Adobe. Supports Vector and Bitmap formats.</td>
    </tr>
  </tbody>
</table>

<p>I realised after learning about these different formats that whichever “fallback” font I used, it would need to be in a format that worked on as many platforms and browsers as possible. All four formats are now incorporated into the OpenType spec, but support varies across the different browsers and platforms.</p>

<p>SBIX was ruled out early on, since only Apple was using it, and their font was not freely available. SBIX is supported by Safari, Chrome and Edge, but not Firefox. Even though the format is supported in several browsers, it is only useful on Apple devices.</p>

<h2 id="opentype-svg">OpenType-SVG</h2>

<p>OpenType-SVG was ruled out next, due to <a href="https://bugs.chromium.org/p/chromium/issues/detail?id=306078">Chrome’s refusal</a> to support it, mainly due to performance concerns. Now that Edge is using Chromium, Edge also no longer supports OpenType-SVG.</p>

<p>If you read up on OpenType-SVG, you’ll see that it was at one time mentioned as being well positioned to become the golden standard. It doesn’t seem like this is how things have panned out, in fact with Chromium having no plans to implement it, and Edge now using Chrominum, the browser support has actually regressed in recent times.</p>

<p>The following website is a good one for testing OpenType-SVG support on your browser:</p>

<p><a href="http://xerographer.github.io/reinebow/">http://xerographer.github.io/reinebow/</a></p>

<p>I tried the above web page while writing this article and can confirm that it was only in Firefox that the color fonts were displayed.</p>

<p>The following table shows what I believe the current state to be. It is a modified version of the table that appears in Ollie Williams’ article <a href="https://css-tricks.com/it-all-started-with-emoji-color-typography-on-the-web/">“It All Started With Emoji: Color Typography on the Web”</a> on CSS-Tricks. All I’ve changed is the OpenType-SVG in Edge, which is no longer available:</p>

<table style="text-align: center;">
  <tbody>
    <tr><td></td><th scope="col">Chrome</th><th scope="col">Safari</th><th scope="col">Edge</th><th scope="col">Firefox</th></tr>
    <tr><th scope="row">SVG-in-Opentype</th><td>❌</td><td>❌</td><td>❌</td><td>✅</td></tr>
    <tr><th scope="row">COLR/CPAL</th><td>✅</td><td>✅</td><td>✅</td><td>✅</td></tr>
    <tr><th scope="row">SBIX</th><td>✅</td><td>✅</td><td>✅</td><td>❌</td></tr>
    <tr><th scope="row">CBDT/CBLC</th><td>✅</td><td>❌</td><td>✅</td><td>❌</td></tr>
  </tbody>
</table>

<h2 id="cbdtcblc">CBDT/CBLC</h2>

<p>CBDT/CBLC didn’t have consistent support across all browsers either. I couldn’t make Noto Color Emoji load using @font-face in Firefox no matter what I tried. By default, the sanitizer in Firefox rejects CBDT/CBLC, but there is a preference (gfx.downloadable_fonts.keep_color_bitmaps) to turn on support. Unfortunately Noto Color Emoji characters still do not show even after enabling this preference.</p>

<p>I was also a bit skeptical about how good a bitmap format emoji font would look like when drawn at large pixel sizes, such as 400px. Would it look too blurry? The following is an example of using Noto Color Emoji:</p>

<p><img src="/images/noto_castle_scene.png" alt="image info" /></p>

<p>The castle in the above image appears quite blurry. Although not so noticeable on a mobile device, it becomes quite obvious on a desktop monitor. The bitmap image for the emoji has been stretched bigger than it is designed to be.</p>

<p>A vector format wouldn’t suffer from this.</p>

<h2 id="colrcpal">COLR/CPAL</h2>

<p>What did that leave me with? COLR/CPAL. It appeared to be supported by all browsers, and had been for some time. Microsoft had been quite cleaver in creating a format that utilised the glyph format already used in font files. It wasn’t as flexible as SVG, but it was a vector format and so did scale well. The only problem was that the Windows “Segoe UI Emoji” format was the only COLR/CPAL font that I’d heard of so far.</p>

<p>I couldn’t use the Windows emoji font as the fallback font. Technically though, I probably could, and I did think about it for a bit, but it wasn’t the right approach, because Microsoft didn’t allow this, so would violate the licensing section of the js13kgames rules.</p>

<p>Most of the free to use open source emoji fonts were not in the COLR/CPAL format. But after a bit of searching, I discovered that Mozilla had created COLR/CPAL versions of a couple of them.</p>

<h2 id="emojione-mozlla-font">EmojiOne Mozlla Font</h2>

<p>The first font they did this with was the EmojiOne font. Firefox used to ship with a COLR/CPAL version of EmojiOne, but Mozilla replaced this with with a COLR/CPAL version of the Twemoji font back in April 2018, in response to <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1358240">EmojiOne no longer being open source</a>.</p>

<p>For the same reason, I couldn’t really use the latest EmojiOne font (i.e. JoyPixels) either. Perhaps I could use the COLR/CPAL version that Mozilla produced before the EmojiOne licensing changed, since EmojiOne v2 was bound by an opensource license and therefore Mozilla’s conversion of v2 would also be free to use. The drawback with that though was that it would limit the game to Unicode 9, which would miss out on the past few years of new emoji characters.</p>

<h2 id="twemoji-colr-font">Twemoji COLR font</h2>

<p>The <a href="https://github.com/mozilla/twemoji-colr">Twemoji COLR/CPAL font</a>, on the other hand, appeared to be perfect for my needs. It ticked all the boxes and it looked good as well:</p>

<p><img src="/images/twemoji_game_screen.png" alt="image info" /></p>

<p>So this is what I choose to use as my fallback font for most of the time I was working on my 2020 js13kgames entry.</p>

<h2 id="cross-platform-testing">Cross Platform Testing</h2>

<p>I did some testing on Windows, Macbook, Android and a Samsung phone. Windows 10 was using Unicode 12. After a little research, it appeared that Windows kept its emoji font up to date through regular Windows updates.</p>

<p>The Android and Samsung phones, assuming they were on the latest Android release, also were on Unicode 12. The following video is a full playthrough of “The King’s Missing Page” on an Android phone:</p>

<div style="margin-bottom:15px">
  <div style="position:relative;padding-top:56.25%;">
    <iframe src="https://www.youtube.com/embed/_Y4EhzEDbEg?start=110" frameborder="0" allowfullscreen="" style="position:absolute;top:0;left:0;width:100%;height:100%;"></iframe>
  </div>
</div>

<p>What surprised me is that the Apple Macbook Pro I tested on was only at Unicode 9. It seemed that the MacOS was not updating its emojis as regularly as other operating systems were. This was unfortunate, because the Apple emojis were argubably the most visually appealing, despite being a bitmap format and therefore prone to the blurring issue.</p>

<p><img src="/images/apple_halloween_house.png" alt="image info" /></p>

<p>It was possible that there were a lot of Macbooks out there that were also several Unicode versions behind, which would mean that my game would fall back on the Twemoji font rather than showing the native Apple Color Emoji font.</p>

<p>I worried over this for a while, because it felt to me that potentially a rather high proportion of players out there would end up seeing the fallback Twemoji font.</p>

<p>I felt strongly enough about this that I decided to do a stock take of all of the emojis I was using from Unicode versions 10, 11 and 12. Perhaps I could replace those emojis with something similar from earlier Unicode versions, which is exactly what I ended up doing. Before long, the requirement to have Unicode 12 available had been reduced to Unicode 9.</p>

<h2 id="emojione-v2-revisited">EmojiOne V2 Revisited</h2>

<p>Out of curiosity more than anything, having reduced the required Unicode verson back to verson 9, I wondered what the game would look like with the EmojiOne COLR font.</p>

<p><img src="/images/emojione_game_screen.png" alt="image info" /></p>

<p>It didn’t look too bad, but it did show how emojs from different fonts can be drawn from different angles, which makes placing them on the screen, in a way that works across all possible fonts, a bit tricky. In the following example, the lorry on the left is not meant to be parked on the grass facing out. In all other emoji fonts that I’d seen, this emoji is drawn side on, similar to the vehicle on the right.</p>

<p><img src="/images/emojione_game_screen_2.png" alt="image info" /></p>

<h2 id="a-reversal">A Reversal</h2>

<p>Having been through the process of changing the game to use only Unicode 9 or below, in the end I felt that perhaps the fallback mechanism would no longer be needed, and I could save some precious bytes that could be used elsewhere.</p>

<p>So despite implementing the fallback mechanism described earlier in this article, and choosing a suitable font to use as that fallback, I ended up removing it from the final version of the game. To be honest, there was still a part of me that didn’t feel like it was in the spirit of the js13kgame contest to rely on a live loaded font file, so I felt happier that I didn’t need it afterall.</p>

<p>After the contest had ended, I received feedback from someone who played my game who said that certain emojis were not showing on his machine. So perhaps even Unicode 9 wasn’t old enough to justify removing the fallback mechanism. I had to draw the line somewhere though.</p>

<h2 id="takeaways">Takeaways</h2>

<p>The following are the key points that I took away from my time researching the various emoji fonts:</p>

<ul>
  <li>COLR/CPAL currently has the widest support across browsers and platforms.</li>
  <li>The Mozilla Twemoji COLR font is therefore a good fallback font, and is up to date and free to use.</li>
  <li>OpenType-SVG surprisingly has the worst support across the various browsers.</li>
  <li>Apple (SBIX) and Google (CBDT/CBLC) fonts do not scale particularly well at the larger sizes needed to render things like full size buildings in a game.</li>
  <li>If you’re not using a “live loaded” fallback font, then using newer emoji characters risks them not being rendered on some people’s machines.</li>
</ul>

<h2 id="part-3">Part 3…</h2>

<p>That is all for this part.</p>

<p>In the next part, I’ll be looking much closer at how I have used emojis in “The King’s Missing Page”, focusing primarily on the mouse interaction with the emojis.</p>]]></content><author><name>Lance Ewing</name></author><category term="blog" /><category term="js13k" /><summary type="html"><![CDATA[In this post, I will be looking at the Emoji cross platform and cross browser issues I faced in creating my js13kgames entry. I hadn’t been working long on “The King’s Missing Page” when I realised just how different the various emoji font files were across the different platforms and browsers.]]></summary></entry><entry><title type="html">Using Emojis in a Point and Click Adventure - Part 1</title><link href="/blog/js13k/2020/09/27/using-emojis-in-a-point-and-click-adventure-part-1.html" rel="alternate" type="text/html" title="Using Emojis in a Point and Click Adventure - Part 1" /><published>2020-09-27T12:47:15+00:00</published><updated>2020-09-27T12:47:15+00:00</updated><id>/blog/js13k/2020/09/27/using-emojis-in-a-point-and-click-adventure-part-1</id><content type="html" xml:base="/blog/js13k/2020/09/27/using-emojis-in-a-point-and-click-adventure-part-1.html"><![CDATA[<p>This year I entered the <a href="https://js13kgames.com/">js13kgames</a> contest for the 6th time with a game called <a href="https://2020.js13kgames.com/entries/the-kings-missing-page">“The King’s Missing Page”</a>. This article is part 1 in a series of blog posts that discuss some of the challenges I faced in creating this game.</p>

<p>For those who have seen my previous js13k entries, you will have seen that two of them are point and click graphic adventures. I also entered the inaugural gamedev.js jam earlier this year, with what was once again a point and click adventure.</p>

<p><a href="https://js13kgames.com/games/down-the-drain/index.html">
  <img src="/images/down-the-drain.png" width="49%" />
</a>
<a href="https://js13kgames.com/games/back-to-life-adventure/index.html">
  <img src="/images/back-to-life.png" width="49%" />
</a></p>

<p>Since I first heard of the js13kgames game jam back in 2013, I have wondered whether it would be possible to create a point and click adventure game that felt like a full and challenging game. Was 13k enough space to create something like the old Lucasfilm and Sierra On-line classics of the 80s and 90s?</p>

<p>My first two attempts (shown above) fell a little short of my vision, mainly because I ran out of time before I ran out of space. One thing became obvious through those attempts though, that if I wanted to add lots of rooms, they’d all end up looking nearly the same. Creating distinct looking scenes took up valuable bytes.</p>

<h2 id="emojis-as-an-art-asset">Emojis as an art asset</h2>

<p>Earlier this year a solution to creating more distinct looking scenes occurred to me. I had seen games in previous years of the js13kgames contest that had used emoji characters. Looking through the full set of emojis, I could see lots of different buildings, a few different trees, people, animals, and items. Could this be the way to create a more interesting graphic adventure game?</p>

<p><img src="/images/missing-page-black-edges-2.png" alt="image info" /></p>

<p>The main problem appeared to be how they are rendered on Windows 10. As shown in the image above, emojis on Windows are rendered with a relatively thick black outline. The bigger you draw the emoji, the thicker the black outline becomes. Buildings, vehicles and trees all have ugly outlines that look out of place.</p>

<h2 id="removing-the-black-outline">Removing the black outline</h2>

<p>Emojis are unicode characters rendered using a particular font. The Windows emoji font is called “Segoe UI Emoji”. It isn’t possible to style that font to remove the thick black outline, because its part of the graphics that make up the colour glyph of the character.</p>

<p>I spent some time playing around on codepen.io to see if could draw an emoji to an HTML canvas and then manipulate the pixels to remove the black outline. The idea I came up with was to apply a pixel fill algorithm starting at an area of the canvas outside of where the emoji is and then customising the fill algorithm to detect pixels that look like they are part of the black outline and to then remove them.</p>

<p>The following Pen shows the final code that I arrived at. You’ll need to view this on a Windows machine to  appreciate what it is doing:</p>

<p class="codepen" data-height="407" data-theme-id="dark" data-default-tab="js,result" data-user="lance_ewing" data-slug-hash="jOqgPGo" style="height: 407px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Emoji on Canvas">
  <span>See the Pen <a href="https://codepen.io/lance_ewing/pen/jOqgPGo">
  Emoji on Canvas</a> by Lance Ewing (<a href="https://codepen.io/lance_ewing">@lance_ewing</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async="" src="https://static.codepen.io/assets/embed/ei.js"></script>

<p>Why use a fill algorithm? This is mainly so that areas of black inside the emoji are not also removed. It is only the black around the edge of the emoji that we’re interested in removing. So the fill floods in towards the edges of the emoji and then stops when it gets to the other side of the black outline.</p>

<h2 id="detecting-the-black-outline">Detecting the black outline</h2>

<p>There is a bit more to it than what I described above. We might naively think that the whole of the black outline is pure black, by which I mean an RGB value of 0,0,0. Due to antialiasing with both the area outside the emoji, and the area within the emoji, the outline takes on various subtle shades and tints. This is what makes the outline look smooth when rendered, but it is a pain to try to remove.</p>

<p>The following code does a reasonable job:</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="c1">// Get the RGBA values for the pixel at the current position.</span>
<span class="kd">let</span> <span class="p">[</span><span class="nx">red</span><span class="p">,</span> <span class="nx">green</span><span class="p">,</span> <span class="nx">blue</span><span class="p">,</span> <span class="nx">alpha</span><span class="p">]</span> <span class="o">=</span> <span class="nx">imgData</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">slice</span><span class="p">(</span><span class="nx">pos</span><span class="p">,</span> <span class="nx">pos</span> <span class="o">+</span> <span class="mi">4</span><span class="p">);</span>

<span class="c1">// Calculate the perceived brightness of the pixel.</span>
<span class="kd">let</span> <span class="nx">brightness</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">round</span><span class="p">(</span><span class="nb">Math</span><span class="p">.</span><span class="nx">sqrt</span><span class="p">(</span>
    <span class="p">(</span><span class="nx">red</span> <span class="o">*</span> <span class="nx">red</span> <span class="o">*</span> <span class="mf">0.299</span><span class="p">)</span> <span class="o">+</span> 
    <span class="p">(</span><span class="nx">green</span> <span class="o">*</span> <span class="nx">green</span> <span class="o">*</span> <span class="mf">0.587</span><span class="p">)</span> <span class="o">+</span> 
    <span class="p">(</span><span class="nx">blue</span> <span class="o">*</span> <span class="nx">blue</span> <span class="o">*</span> <span class="mf">0.114</span><span class="p">)</span>
<span class="p">));</span>

<span class="kd">let</span> <span class="nx">spread</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">alpha</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// If the pixel is fully transparent, then let flood fill spread.</span>
    <span class="nx">spread</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">else</span> <span class="k">if</span> <span class="p">((</span><span class="nx">red</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="p">(</span><span class="nx">green</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="p">(</span><span class="nx">blue</span> <span class="o">==</span> <span class="mi">0</span><span class="p">))</span> <span class="p">{</span>
    <span class="c1">// If the pixel is pure black, then let flood fill spread, and </span>
    <span class="c1">// make pixel fully transparent.</span>
    <span class="nx">imgData</span><span class="p">.</span><span class="nx">data</span><span class="p">[</span><span class="nx">pos</span> <span class="o">+</span> <span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="nx">spread</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">brightness</span> <span class="o">&lt;</span> <span class="mi">70</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// If the RGB value of the pixel is below a certain perceived </span>
    <span class="c1">// brightness level, then adjust the alpha channel to match </span>
    <span class="c1">// and let the flood fill spread.</span>
    <span class="nx">imgData</span><span class="p">.</span><span class="nx">data</span><span class="p">[</span><span class="nx">pos</span> <span class="o">+</span> <span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">round</span><span class="p">(</span><span class="nx">brightness</span> <span class="o">*</span> <span class="p">(</span><span class="mi">255</span> <span class="o">/</span> <span class="mi">70</span><span class="p">));</span>
    <span class="nx">spread</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure>

<p>The first case, where the alpha channel is zero, is simply the fill flooding into the transparent background parts of the canvas. The second case, where RGB are all zero, is where the pixel is pure black and therefore deemed to be part of the black outline, and so the fill floods into that pixel and it is set to transparent.</p>

<p>If we stopped there, and didn’t have the third case, then the resulting image would have a jagged edge (see tree on the left in the image below):</p>

<p><img src="/images/tree-jagged-edges.png" alt="image info" /></p>

<p>To avoid this, the third case takes advantage of the existing antialiasing between the inside edge of the black outline and the colourful interior of the emoji. It detects this by calculating the perceived brightness of the pixel, and then if it is below a certain threshold, it adjusts the alpha channel of the pixel to match the brightness. This has the effect of making the edge of the emoji look smooth after removing the black outline.</p>

<p>The threshold of 70 was determined by trial and error after testing the code with various Windows emojis.</p>

<h2 id="resizing-after-removal">Resizing after removal</h2>

<p>If an emoji is drawn at a large font size, let’s say 300px, then removing the thick black outline results in an image noticeably smaller than the originally desired size.</p>

<p>Consider also that in order to allow the flood fill to spread the whole way around the emoji, the initial canvas size is deliberately made higher and wider than the desired height and width. Not too much though, otherwise time would be wasted filling into areas that are not needed.</p>

<p>As a consequence of the two factors mentioned above, after the black outline is removed there is quite a noticeable empty area beyond the four edges of the emoji that needs to be trimmed off. The canvas needs to be cropped so that its width and height exactly fits the emoji.</p>

<p><img src="/images/school-house-crop.png" alt="image info" /></p>

<p>With this in mind, the fill algorithm also keeps track of where the four visible edges of the emoji are as it is spreading around the emoji:</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="k">if</span> <span class="p">(</span><span class="nx">imgData</span><span class="p">.</span><span class="nx">data</span><span class="p">[</span><span class="nx">pos</span> <span class="o">+</span> <span class="mi">3</span><span class="p">])</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">x</span> <span class="o">&lt;</span> <span class="nx">minX</span><span class="p">)</span> <span class="nx">minX</span> <span class="o">=</span> <span class="nx">x</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">x</span> <span class="o">&gt;</span> <span class="nx">maxX</span><span class="p">)</span> <span class="nx">maxX</span> <span class="o">=</span> <span class="nx">x</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">y</span> <span class="o">&lt;</span> <span class="nx">minY</span><span class="p">)</span> <span class="nx">minY</span> <span class="o">=</span> <span class="nx">y</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">y</span> <span class="o">&gt;</span> <span class="nx">maxY</span><span class="p">)</span> <span class="nx">maxY</span> <span class="o">=</span> <span class="nx">y</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure>

<p>In the outline detection code shown earlier in the article, imgData.data[pos + 3] represented the alpha channel of the current pixel. Setting it to 0 makes it fully transparent. If after processing the current pixel it is determined to not be fully transparent, then it makes up a visible part of the emoji and therefore the current max and min X and Y values are updated. After processing the whole image, the minX, minY, maxX and maxY values give the left, top, right and bottom visible edges.</p>

<p>Simply cropping the canvas to this size is not enough though. The game requires an emoji to be displayed at a given width and height. So in addition to cropping, the canvas is resized in both directions:</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="c1">// Redraw the canvas, so that we can remove white space and add a shadow.</span>
<span class="kd">let</span> <span class="nx">emojiCanvas</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="dl">'</span><span class="s1">canvas</span><span class="dl">'</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">newWidth</span> <span class="o">=</span> <span class="p">((</span><span class="nx">maxX</span> <span class="o">-</span> <span class="nx">minX</span><span class="p">)</span> <span class="o">+</span> <span class="mi">3</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">newHeight</span> <span class="o">=</span> <span class="p">((</span><span class="nx">maxY</span> <span class="o">-</span> <span class="nx">minY</span><span class="p">)</span> <span class="o">+</span> <span class="mi">3</span><span class="p">);</span>
<span class="nx">emojiCanvas</span><span class="p">.</span><span class="nx">width</span> <span class="o">=</span> <span class="nx">width</span><span class="p">;</span>
<span class="nx">emojiCanvas</span><span class="p">.</span><span class="nx">height</span> <span class="o">=</span> <span class="nx">height</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">emojiCtx</span> <span class="o">=</span> <span class="nx">emojiCanvas</span><span class="p">.</span><span class="nx">getContext</span><span class="p">(</span><span class="dl">'</span><span class="s1">2d</span><span class="dl">'</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">newWidth</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="nx">newHeight</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">emojiCtx</span><span class="p">.</span><span class="nx">shadowColor</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">black</span><span class="dl">"</span><span class="p">;</span>
    <span class="nx">emojiCtx</span><span class="p">.</span><span class="nx">shadowBlur</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">flip</span><span class="p">)</span> <span class="p">{</span>
        <span class="nx">emojiCtx</span><span class="p">.</span><span class="nx">translate</span><span class="p">(</span><span class="nx">width</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
        <span class="nx">emojiCtx</span><span class="p">.</span><span class="nx">scale</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="nx">emojiCtx</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span>
        <span class="nx">canvas</span><span class="p">,</span>
        <span class="nx">minX</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="nx">minY</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="nx">newWidth</span><span class="p">,</span> <span class="nx">newHeight</span><span class="p">,</span>
        <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">width</span><span class="p">,</span> <span class="nx">height</span><span class="p">,</span>
    <span class="p">);</span>
<span class="p">}</span></code></pre></figure>

<p>In the code above, the canvas variable is the original canvas that was manipulated to remove the black outline. The emojiCanvas is the new canvas that is set to the desired size of the emoji. The detected visible edges of the emoji on the original canvas are then used to specific what part to draw to the new emojiCanvas, which is resized in each direction to the desired size, and we’ve thrown in a flip variable in case we want the emoji to face the other direction.</p>

<p>Note that we also have a small shadow blur. This isn’t required, but it does give it a slightly smoother and more defined edge.</p>

<h2 id="performance">Performance</h2>

<p>There is certainly an overhead in performing this kind of image manipulation, so we need to keep it to a minimum. Sometimes the same emoji needs to be drawn many times in the same scene, such as a background of many trees of the same type and size. To address this, the game caches the image data for a given emoji and size, so that if the same combination is encountered again, in any scene, it uses the cached image data instead of performing the same image manipulation.</p>

<h2 id="the-end-result">The end result</h2>

<p>Removing the black outline from the Windows emojis in this way allows more attractive scenes to be created. If we render the same scene shown earlier in this post using the described approach, then the end result is as follows:</p>

<p><img src="/images/missing-page-no-edges.png" alt="image info" /></p>

<p>That’s much nicer!</p>

<p>Obviously this rendering technique is only of concern on a Windows machine, but it’s likely that many of the participants in the js13kgames contest are using Windows and will see the game like this, and since the participants are the voters, it is an important consideration.</p>

<p>Windows isn’t the only operating system that I had to cater for in choosing to use emojis. The topic of the next post in this series looks at some of the things I discovered about emoji fonts in relation to cross platform and browser compatibility.</p>]]></content><author><name>Lance Ewing</name></author><category term="blog" /><category term="js13k" /><summary type="html"><![CDATA[This year I entered the js13kgames contest for the 6th time with a game called “The King’s Missing Page”. This article is part 1 in a series of blog posts that discuss some of the challenges I faced in creating this game.]]></summary></entry></feed>