JSFiddle - React, Tailwind, and code Playground

by Gwyn Milcote

HTML

Crappy map test. Use arrow keys or click-and-hold compass buttons to move.
<p>
    Map: <select id='ex-settings-level'>
        <option value='1'>One</option>
        <option value='2'>Two</option>
        <option value='3'>Three</option>
    </select> 
    <button id='ex-load-level'>Load</button> 
     &nbsp; &nbsp; &nbsp; Move speed: 
     <select id='ex-settings-speed'>
        <option value='100'>Snail</option>
        <option value='150'>Slow</option>
        <option value='200' selected>Normal</option>
        <option value='250'>Fast</option>
        <option value='300'>Cheetah</option>
    </select>
    <br>
    Display closest tile? 
    <select class='ex-settings-select' data-type='closestTile'>
        <option value='0'>No</option>
        <option value='1' selected>Yes</option>
    </select> 
     &nbsp; &nbsp; &nbsp; Display animated tiles? 
    <select class='ex-settings-select' data-type='animatedTile'>
        <option value='0'>No</option>
        <option value='1' selected>Yes</option>
    </select> 
     &nbsp; &nbsp; &nbsp; Display tile grid? 
    <select class='ex-settings-select' data-type='gridTile'>
        <option value='0' selected>No</option>
        <option value='1'>Yes</option>
    </select>
</p>

<p><button id='pause-btn'>Pause</button>  -  Image loading progress: <span id='loader-progress'></span> - Keys down: <span id='status-keys'></span>
<br>Direction moving: <span id='current-dir'>?</span></p>

<div id='ex-container'>

    <div id='canvas-border'>
    <div id='canvas-container'>
        <canvas id='game-canvas' height='360' width='480'></canvas>
        <div id='pause-overlay' style='display: none;'>
            <h1>Paused</h1>
        </div>
    </div>
    </div>
    
    <div id='ex-compass'>
            <div class='ex-arrow ex-n' data-key='W'>N</div>
            <div class='ex-arrow ex-s' data-key='S'>S</div>
            <div class='ex-arrow ex-e' data-key='D'>E</div>
            <div class='ex-arrow ex-w'...

CSS

div, img, button, p, select, input {
    box-sizing: border-box;
}

body {
    background-color: #ddd;
}

#canvas-border {
    border: 1px solid black;
    background-color: silver;
    padding: 10px;
}
#canvas-container {
    position: relative;
    width: 480px;
    height: 360px;
}
#canvas-container #game-canvas {
    position: absolute;
    top: 0;
    left: 0;
}
#canvas-container #pause-overlay {
    position: absolute;
    top: 0;
    left: 0;
    z-index: 2;
    width: 100%;
    height: 100%;
    background-color: rgba(255, 255, 255, 0.5);
    box-shadow: inset 0 0 50px 0px #fff;
    text-align: center;
    font-size: 40px;
    padding-top: 80px;
}

#ex-container {
    position: relative;
    width: 800px;
    height: 384px;
    margin: 20px auto;
    background-color: #fff;
    border: 1px solid silver;
}


#ex-compass {
    position: absolute;
    top: 10px;
    left: 520px;
    width: 170px;
    height: 170px;
    /*border: 1px solid grey;*/
}
#ex-compass .ex-arrow {
    width: 50px;
    height: 50px;
    position: absolute;
    border: 1px solid brown;
    background-color: gold;
    transition: 0.3s;
}
#ex-compass .ex-arrow:hover {
    cursor: pointer;
    background-color: ivory;
    transition: 0.3s;
}
.ex-n { top: 0; left: 60px; }
.ex-s { top: 120px; left: 60px; }
.ex-e { top: 60px; left: 120px; }
.ex-w { top: 60px; left: 0; }
.ex-ne { top: 0; left: 120px; }
.ex-nw { top: 0; left: 0; }
.ex-se { top: 120px; left: 120px; }
.ex-sw { top: 120px; left: 0; }
/*.ex-x { top: 40px; left: 40px; }*/

#ex-info-section {
    position: absolute;
    top: 190px;
    left: 520px;
    width: 260px;
    max-height: 100px;
    overflow: auto;
    border: 1px solid silver;
    padding: 5px;
}

JavaScript

/*
* Large pre-drawn images like trees. On top layer.
* Fixed at certain tile co-ords. Can reuse/repeat.
* Can be better than using lots of tiles!
* Need tiles for 'base'/low parts like trunks though,
* so those can appear behind the sprite.
*
* Need a standard size. And tiles are the top-left corners!
*/
const objects = [
    {id: 1, name: "Tree 1", url: "https://tse4.mm.bing.net/th?id=OIP.S1ADFIED7QQrOx8ug3LsewAAAA&pid=Api"},
    {id: 2, name: "Tree 2", url: "https://tse3.mm.bing.net/th?id=OIP.0xoU0azFfS2kphc4lZgUQgAAAA&pid=Api"},
    {id: 3, name: "Tree 3", url: "https://tse1.mm.bing.net/th?id=OIP.HI3bfmyChdtjauA7J-d1pwAAAA&pid=Api"},
    {id: 3, name: "Tree 4", url: "https://tse2.mm.bing.net/th?id=OIP.k9TIxKzt2vkidozGwyQg6QAAAA&pid=Api"},
    {id: 3, name: "Tree 5", url: "https://tse2.mm.bing.net/th?id=OIP._EmTWBR32M21ag6-z0JtdAAAAA&pid=Api"}
];

const db = {
    
    // Terrain layers, start coords...
    maps: [
        {
            id: 1, name: "Forest",
            startX: 5,
            startY: 7,
            cols: 16, rows: 16,
            // Fixed images
            objects: [
                //{x: 8, y: 6, image: "tree2"},
                //{x: 4, y: 10, image: "tree5"}
            ],
            // 2 layers of visual tiles to render.
            // Both below sprite. Graphical only.
            layers: [
                [
            [25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25],
            [25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25],
            [25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25],
            [25,25,25,25,25,25,25,25,25,25,25,12,25,25,25,25],
            [25,25,25,25,25, 3, 3, 3,25,25,25,12,12,25,25,25],
            [25,25,25,25,25, 3,25, 3,25,25,25,25,12,12,25,25],
            [25,25,25,25,25, 3, 3, 3,25,25,25,25,12,25,25,25],
            [25,25,25,25,25,25,25,25,25,25,25,12,12,25,25,25],
            [26,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25],
            [25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25],
         ...