Close
0%
0%

HTML5 Retro Game Engine(s)

A Side project where I try and build various scriptable retro game engines in the browser.

Public Chat
Similar projects worth following
I have dabbled in interfaces for years for various projects, but for work, I have never really gotten into game development. Lately a project I'm working on started to incorporate more game like dynamics and attributes. This got me interested in the fundamentals of the machinery that underlay game logic.

In this project, I'll be describing my adventure to create adventures. I want to see how far I can get building a Sierra/Lucas SCUMM type of game engine completely in browser. Not an emulation of those two, but a completely new engine.

(A couple of the logs, I have writen in advance and only now have the time to upload them)

First off, Why? Well, because I think I can!

I really enjoyed (and still do) the 90's adventure game genre, especially the good old 320 by 200 pixel days. With retro being all the rage, why not jump on that bandwagon I thought. Last year I actually attempted building a game from scratch which really ended up nowhere. It was an exercise in using the <canvas> element to see how I could use that in various projects. It was a fun playground that quickly started to look like the beginnings of a game. I dubbed it Shovelization and thought about making it a strategy game about archaeology.

Shovelization basically was a isometric'ish tile based game where a player would get an assignment on a plot of land to dig up a site and find the answers to the questions associated with the level. I build the isometric engine, a couple of characters, the basic level system and some of the plumbing for a game like menuing systems and such. At many stages I felt I could really pull it off until reality hit me twice!

The first reality check was when I realized that the genre of real time strategy is about as hard to get right coding wise as anything. This is not scrolling platformer hard, this is properly difficult to get right. (How the 8-bit guy does it in 6502 assembly is remarkable!) The second was the fact that I completely got the fundamental design wrong. The relationship between game state, input and output was a mess. Building something on one size would break the other size. Fixing that would break something else. It resulted in unmanageable spaghetti code.

Realizing that implementing the gaming logic itself would be near impossible without a major rewrite of the core components, I laid that project to rest, perhaps to revisit it later. I did use what I learned in this experiment to build a small lightweight canvas based online image editor with some pretty advanced features.

But as my interest in gaming logic reappeared thanks to my current work, I had a choice. Retry Shovelization or perhaps try something else first.

script_manual.rtf

Version 1 for the game engine script language

Rich Text Format - 2.22 kB - 05/06/2019 at 16:41

Download

  • Added scaling

    Timescale04/17/2020 at 15:54 0 comments

    I was pretty happy with my 3 transform rotate function, but in the adventure engine I had a feature that I had not implemented on this platform. The reason being that this grew from a testbed for platformers where I discovered that using an image map to directly use for the graphic assets resulted in a massive performance loss.

    Due to this, I stripped out some functionality of the drawing routines. This included the re-sampling function that also handled animations as these are generally part of the same asset.

    In this case, the number of graphic assets on screen are not as much as the platformer or the top down strategy engine, so I wanted to put back in the resampling and frame selection function in such a way that it would neatly work with the 3 transform rotation.

    Turns out, this was really easy. Almost a plug in addition to the engine. Of course it scales on one fixed corner, which has to be addressed, but in general it works really well. So, here is the demo!

  • Gamepad.IO

    Timescale04/14/2020 at 15:28 0 comments

    For a while now, I wanted to do some experiments with gamepads. These controller devices are ubiquitous and I often considered them an easy and cheap way to interact with, well, just about anything that needs simple input. Surely I can use these devices in my work, but how to implement them?

    I have a collection of input devices I play around with, but mostly I tinker with the more unique stuff like the Sony buzz quiz controllers or various Nintendo input devices. The generic gamepad not so much, but I do have a couple of them in my I/O hardware collection.

    My classic Gravis gamepad is of no use as it is a 15 pin din device. I am building an arduino based converter for various types of I/O which I should actually be covering on hackaday.io, but perhaps later I'll do that write-up.

    What I found in my bin was a logitech dual action USB gamepad. Just the thing! Nice and generic.


    I knew the HTML5 gamepad API was experimental, so I wanted an implementation that could roll with the times. This means I want the input of the controller to be freely mapped to any control scheme I have got going on. In order to make this, I chose my ArrType test platform as I had allready implemented a keyboard control scheme to move the little ship around.

    This scheme was based on readily available code examples and used an object in Javascript to map the event codes to names. The game can now, in its loop, check which buttons are pressed and call the appropriate function like rotate, move or shoot.

    var Key = {
          _pressed: {},
    
          P1_LEFT:65,
          P1_RIGHT:68,
          P1_UP:87,
          P1_DOWN:83,
          P1_SPACE: 32,
    
          isDown: function(keyCode) {
            return this._pressed[keyCode];
          },
    
          onKeydown: function(event) {
            this._pressed[event.keyCode] = true;
          },
    
          onKeyup: function(event) {
            delete this._pressed[event.keyCode];
          },
     
        };

    My idea is to map the buttons of a gamepad to the key names. This would not require any change in the game code that defines the actions and it means that remapping the keyboard controls from say the arrow keys to the A,S,W,D configuration would not affect the gamepad configuration.

    var padMapped = new Array();
        padMapped[0] = new Array();
        padMapped[0][14] = "P1_LEFT";
        padMapped[0][15] = "P1_RIGHT";
        padMapped[0][12] = "P1_UP";
        padMapped[0][13] = "P1_DOWN";
        padMapped[0][5] = "P1_SPACE";
    

    This array simply maps the necessary buttons to the names mapped to the keyboard input array. The upside of this method is twofold. Firstly, we do not have to define every button. Buttons pressed but not mapped, will be ignored without errors. The second benefit is that we actually can define keymaps that are outside the scope it can get via the keyboard. This means this setup is equally able to handle separate gamepad events. Multiple gamepads are supported.

    The code for the gamepad detection, enumeration and update function, I got from there : https://github.com/luser/gamepadtest

    It's a bit old, but it was short, it worked and I could easily fiddle my hooks into it. In any event, If/when I come across better/newer methods to do this, It won't be hard to take the old stuff out and put the new code in without actually having to worry about the underlying control scheme of the game/application itself.

    The difference between the keyboard event listener and the gamepad update function is that the first is triggered on a button press and the later is dependent on an function that checks the status of the gamepad X times per second. If there is an event driven gamepad API solution, I'd like to know about it!

    So basically all I have to do now is to trigger the keymapped object when a mapped gamepad button is pressed. That turned out to be quite simple.

         if (pressed) {
              padkey = Key[padMapped[j][i]];
              var padEvent = {
                  keyCode: padkey
              }
              controllerStatus[j][i] = true;
              Key.onKeydown(padEvent);
              console.log(i)
          } else {
              if (controllerStatus[j][i]){
                  controllerStatus[j][i] = false;
                padkey = Key[padMapped[j][i]];
                  var padEvent = {
                      keyCode: padkey
    ...
    Read more »

  • The shear revolution

    Timescale04/11/2020 at 16:34 0 comments

    While it might look like I’m easily distracted and roam from project to project without actually ever finishing any of them, you might have a point but there is some method to my madness. The main objective is to create a toolkit of functions that are easy to implement as building blocks for any sort of retro game. Simple functions like drawing lines or boxes to assets or the screen and functions that can do a whole lot more like scaling, animating “sprites” and other effects.

    These function have to be simple, fast and modular. If I build a effect in the adventure engine, it must also be usable in Shovelization and visa versa. Some game paradigms lend themselves better for certain functions than others and that is the reason why I have forked the project into several other game/app types. I now have the Adventure engine, the platform, the top down strategy platform, several audio and input experiments and an asteroids/city command type of environment.

    The asteroids platform named “ARRRtype” (Because I imagined something with space pirates) is based on a fire effect with multiple draw layers. I put a random spaceship in there and quickly made it “fly” and rotate. It was a natural evolution for this branche to have the ability to rotate graphics.

    The first rotation however wasn’t what I needed. I did a simple source to destination transformation that looked like this.

    x = Math.round((Math.cos(rotate) * (tileX - centerx)) - (Math.sin(rotate) * (tileY - centery)) + centerx);
    y = Math.round((Math.sin(rotate) * (tileX - centerx)) + (Math.cos(rotate) * (tileY - centery)) + centery);

     For every pixel, this calculation has to be made and what you get is a moire mess.. I knew this, but I thought I’d write something better later. I didn’t think it would be that difficult. I understand the principle of using “destination to source” transformation which always results in a pixel to be plotted. I use this type of transformation in a pinch-punch and swirl effect that I have in the same toolbox.

    Quickly I realised that for big graphics or many graphics at once, this was quite a costly operation. Then I stumbled on the notion of rotating by shearing the image. While it took me some time to wrap my head around it and adapt this method for full 360 rotation, it ended up pretty fast and versatile. You can read about the principle here : https://www.ocf.berkeley.edu/~fricke/projects/israel/paeth/rotation_by_shearing.html

    But the essence is that by shearing the image 3 times, you can rotate a bitmap with all pixels intact. At the beginning you need to calculate a couple of values and then use these in the drawing function to calculate the displacement of each pixel. This is the way I implemented the Alpha en Beta shear values:

    aShear = -Math.tan(rad/2).toFixed(2)
    bShear = Math.sin(rad).toFixed(2)

    And then for all pixels, the following cascade of transformation actually gives the new location.

    destX = tileX + Math.floor(aShear * tileY);
    destY = tileY + Math.floor(bShear * destX);
    destX = destX + Math.floor(aShear * destY);

    While on a per-pixel level, basically these are the operations that are done in order to rotate the original bitmap.

    Now there are some issues I found with this method. I could not do a full 360 with this method. -90 and 90 degrees (or -100 and 100 rad) worked fine, but out of those boundaries, the image would distort and completely shatter at the top end. This issue was easily resolved by simply mirroring the X and Y axis while doing the same rotation for the lower angles. I’m not sure how neat my solution here was, but for now it works.

    Then there was the problem of rotating around the center. The simple rotation method or a dest-to-source method automatically rotates around the center, which is what you’d want in many cases. The shear method however rotates around the top right corner of the image. To make it rotate around another point, you...

    Read more »

  • Editor and clean up on isle nine.

    Timescale03/30/2020 at 16:49 0 comments

    In my self-imposed isolation, I found it quite relaxing for the most part working on this pet-project again. There are no real dead-lines and there is nothing much else to do, so I can just chug along being all exited about my pointless and amateurish adventures in building retro games on non-retro platforms.

    It turns out that a game editor does much the same thing for insight as a CMS does for understanding the data itself. It makes things clearer and simplifies it. When I started drafting the spec for this concept, I imagined several object and data layers, but now I'm just down to 2.5 ish. I just need an map and an object layer and some method to project the characters and NPC's.

    Of course the underlying (literally) data must be represented in a map, but there is no need for those to have their own draw layer. You can only have a single level at a time. That works for the object level as well if I constrain those to 1 per tile.

    So what have I managed apart from new enlightenments? Well, The editor now stores the map. I can place tiles and objects anywhere per tile or per selection. I have a minimap of the entire field, which can be arbitrarily big and I did a bit of cleaning up the code.

    It could be more refined and elegant, but for the most part, I'm still experimenting of course. The line draw function could have been more advanced having just 1 function. Now it's 4. 2 for normal lines and 2 for the red-white pattern. I'll write a super functional line draw routine later, okay!!??

    Well, just to demonstrate the editor as it stands now, here is a short demonstration video.

  • Back to the beginning

    Timescale03/24/2020 at 19:44 0 comments

    So I have been busy with building stuff I get paid for. Some call it a job I guess, but with the current state of the world, this has been kinda shut down. With all this extra free time on my hands, I thought it a nice distraction to re-visit one or two of my dormant projects.

    The adventure engine thus far is not finished, but it is quite feature complete. It has animation, asset management, a scripting language and a powerful 2D retro graphics engine. I learned a metric faecal-ton of things about game logic during that experiment. Granted, It spiralled out of control when I got into writing buffers and filter and subsequently went nuts will all types of crazy effects that look nice and retro, but really have little to do with early 90's games.

    As I wrote in this projects introduction, this whole suite of experiments began with an attempt to write an archaeologically themes game. I build an engine with isometric tiles which was meant as a turn-bases strategy game a'la Civilization only not with countries but with archaeological sites. With these new tricks I learned, I think building that now is far more likely to succeed than my initial attempt.

    This time I'll keep it simple. Not an isometric playing field but simply top down. I can adapt my platformer experiment which was an adaptation of the engine of my adventure engine to do that. It has everything I need. It redraws the screen 30 times a second and has all the graphics function I need, including the ability to construct tiles of arbitrary sizes on the fly which will be handy for menus and panels.

    I have taken the system to the point where I am getting ready to split the main project in two. I need an editor to build the maps which are quite large even with an 80 by 80 grid. This is because there are at least 5 layers that need to be designed. Not just the map, but also the geology, archaeology, height info, objects. There could be more, but all these maps combined will inform the player function on how to interact with the environment.

    I have implemented the self updating menuing and panel system and the cursor system from the adventure engine and it looks something like this :

    The entire screen is redrawn 30 times a second pixel for pixel and I still have more then enough time per tick to burn for other operations and game logic.

  • Just one more!

    Timescale07/23/2019 at 17:53 0 comments

    I admit, I have bee indulging a bit with playing with my.. graphics functions... But just for fun, let me show you one more that perhaps shows a bit more potential when it comes to in game effects.

    This, seems to me, to be really handy for your typical bomber command clone!

  • Trying to think like a game dev.

    Timescale07/22/2019 at 21:17 0 comments

    In the Last log where I posted a link to a live version of my adventure engine, I mentioned that I was experimenting with various ways to build up a screen to figure out what works and what not and what methods are efficient and usable. It occurred to me that various game types must have different approaches on how to render a screen that make sense for that specific type and I wasn't sure if my approach was the right one.

    So I forked my adventure engine code and retooled it to be a continues side scrolling tile based platformer. The reason for the continues scrolling was because the engine already tries to draw the entire screen 30 times a second. A non scrolling game is easier on the CPU, but more difficult to code as all moving parts will need their own background redraw buffer. I know there are other options in HTML5 Canvas to make assets act more like real sprites, but I would like this project to be as basic as possible controlling every sub-pixel.

    The quick proof of concept I whipped up did yield some interesting results. Instead of going down a list of object to draw as I do in the adventure engine, the platformer just scans the 320 by 200 pixels and using offsets and a tile map, figures out which pixel from which tile to draw. It looks a bit like this :

    The snowy background just is random noise. The draw function also does not clip edges. What this demo does is simply take this data :

    <mapData>
        3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
        6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,
        6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,
        3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,
        2,2,2,2,2,2,2,2,2,2,5,5,5,5,5,5,
        1,1,1,1,1,1,1,1,1,4,4,4,4,4,4,4,
        1,1,1,1,1,1,1,1,4,4,4,4,4,4,4,4,
        1,1,1,1,1,1,1,4,4,4,4,4,4,4,4,4
    </mapData>
    

    And per pixel uses this to determine where the image data should come from. Now here is where it gets interesting because when I first implemented this I used a tilemap, or simply an image that held all the tiles in order.

    Using only the first tile was fine. Well under one third of a second to draw 30 frames, but as soon as I put in the code that moves the coordinates in the drawing routine to get the data from the second, third to sixth tile, the render loop slowed down by a factor of 10.

    This code wasn't that much different from the function I use in the adventure engine for animating the assets, but I never saw any drastic reduction in performance. In this case I did!

    It turned out to be a single multiplication in the line where the X and Y coordinates were mapped to the image object. Apparently the impact of that one multiplication, per pixel, either gave an enormous overhead or completely nullified any clever predictive execution methods that I am not aware of to make the whole thing become unusable.

    The solution was simple and crude. Instead of an image map, all the tiles were simply loaded into their own containers. The asset loader is quite capable of that, so I just cut them up and did it this way. Just passing the ID of the image data cause no slowdown and the engine now is perfectly capable of drawing the entire screen 30 times a second with a myriad of tiles and tile sized in fractions of a second.

    I found this really interesting and it made me realize that this to is an area where it helps to have some intuition from experience. This frame shifting method is fine for a system where only a couple of assets are animated in this way, but not for others.

    This set me out to explore other methods to build up a screen. I already named a few, but there obviously are more. Now what I'm doing is in no means using ASM on a 8-bit micro, racing the beam, but the general methods hold true.

    This made me experiment with various effects and methods which resulted in a strange screensaver result that I would like to share with you now :

    What this is, is the platformer code forked and made to draw the assets more like the adventure game, but also use the scanline function to do some effects. This turned out to be a real cheap way to get...

    Read more »

  • Try it yourself!

    Timescale07/19/2019 at 13:39 0 comments

    Having been busy with other (paid) projects, I did not have much time tinkering with the adventure engine for some time. I did experiment with the <canvas> some more and that yielded some interesting results.

    But in the meanwhile, I thought perhaps putting this out there is the right thing to do at this point in time. So here you have it !

    Adventure Engine Version 0.1b

    If you want to look at something, press the L key and to pick something up, press the P key. If you get stuck, you can move the character around with the arrow keys.

    There are some test functions available like the filter mode, fades and testing a part of the scripting functions.

    Nothing is obfuscated and you can download all the files with any extraction tool to have a go at it yourself. Apart from the "borrowed" graphic asset, all material/code is to be considered under the MIT license (version 2.0).

    If anybody wants a zip file, let me know.

  • Storyboard Script

    Timescale05/06/2019 at 11:15 0 comments

    It's time for some game logic! From the very first outline of this project, I wanted to have an engine that used an interpreted scripted narrative to run the game. It is time to start work on that.

    The last piece of the puzzle was to implement a global game state. This does a couple of things, but the most important thing is, it stores changed values per scene per object and when a scene (re)-loads, those values are put back. This means if you pick up an item in one scene, go to the next and then return, the item will still be picked up. Having a global game state also makes it easier to implement saved games and do quick testing of various game scenarios.

    But now I want something to do in the game. I have not yet made the inventory system, but that is a bolt on accessory that I do not need for this. What I want is for each scene to have a storyboard with an easily readable script that is able to define actions and subsequent reactions all with some logic thrown in there. For this I took some inspiration from other game scripting languages and general scripting languages.

    I want the least amount of syntax and maximum readability. That is why I chose to use an indent type of logic that interprets something that kinda resembles the verb based system SCUMM used, but not quite! Here is a snippet.

    <storyboard>
    
        use stick on bolder
            case bolder.pushCounter = 3
            say "enough already!"
            break
        esac
        set bolder.pushCounter ++
        move bolder add 10 0
        play grind
        say "Uuurrggg...."
        case bolder.pushCounter = 3
            say "Well, that went better than expected!"
            hide bolder
            break    
        esac
        use bag1 on bolder
            say "That does not seem to work."
        break
                            
        lookAt bag1
            say "That's my bag!"
        break
    
        lookAt stick
            say "It's a stick! Made of wood."
        break
                                            
    </storyboard>
    

    The main part here of course is the "use stick on bolder" line. In this case, it is not a description of what can be done, but a label of what to do when this happens. The game engine itself will generate that set of words and will then search the script for this occurance. If it is there, the script level goes up and interprets every line on that level until it breaks or the level goes down again.

    Specialized function like say, hide and play are abstracted versions of their actual engine counterparts. It is still possible to directly change game state via the set command. In fact, most commands could be done with set, only it would take more commands to do the same thing like move that would need several in this case as it not only sets an object x and y position, it adds to it which means that you'd need to use set about 6 times making the script less readable.

    Another example would be the hide command which does 2 things. First obviously it hides the object but more importantly it sets the global. The alternative version isn't much longer, but it is longer none the less.

    case bolder.pushCounter = 3
            set bolder.hidden true
            global bolder.hidden
            break
    esac    

    For commands like say, it would be even more involved as it would mean setting various object attributes and figuring out what object to do it to. The say functions just does that for me.

    At this moment, the script does indeed run and most functions are implemented. There are a few things I need to clean up and test, but I think this is looking very promising.

  • Setting up scenes

    Timescale05/05/2019 at 12:01 0 comments

    This weekend I got around to doing some work on the plumbing of the engine. A lot of the scene data was still hardcoded in the init function, but now nearly all data is loaded from the XML file. Asset information, object information and the scene data now make up the structure with which I want to drive the narrative.

    I realized that an engine is not so much a necessity as it is a luxury and therefor it should provide me with a level of comfort. It should make the minimum of demands on the data I feed into it and make the maximum amount of assumptions on data when I do not feed the data. Also, I want to be able to add/modify attributes at every point without having to dive into the code.

    So the minimum data required to get an item in a scene should be something like this.

    <scenes>
        <scene>
            <name>Intro</name>
            <character>PittRivers</character>
            <stage>scene</stage>
            <actors>
                <actor>
                    <objectName>PittRivers</objectName>
                    <x>35</x>
                    <y>165</y>
                </actor>
            </actors>
        </scene>
    </scenes>    
    

    This simply puts the character in the scene at those coordinates and defines it as the playable actor. All other attributes have either been set in the object definition of in code as defaults. At any stage can I add an attribute or overwrite one in the actor section. In fact, the object definition works just like this, only 1 step higher in the data structure hierarchy. This means that data like frame animations, anchor points or zoom types won't have to be set in every scene every time.

    There now is one layer that is missing and that is the global game state. This is where the game logic will exist in part. It is the bit that makes sure that when you picked up the stick and you re-enter this scene, the stick will remain hidden.

    Here is a demo video of the current state of the engine.

    Right now I'm selecting the options with the keyboard so L is "look at" and P is "pick up". This will later be selectable by clicking the main character.

    The text responses for the character are defined on the object level and the responses and action for items are right now defined on the stage level. There are also set distances at which certain actions can be done. These are also scene attributes.

    Another thing is audio. The asset loader now also loads and indexes ogg files that can be used for actions like picking up or using objects. This was a real simple addition. I'm still thinking on how to do background music. I love the idea of using MIDI for this and I am currently looking at existing in browser MIDI player options. If this does not work out, a simple looped ogg file will do just as well.

View all 18 project logs

Enjoy this project?

Share

Discussions

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates