From ce3ee42c4e0feadba53f92d70e261e3a3e642f8c Mon Sep 17 00:00:00 2001 From: Colin McMillen Date: Tue, 24 Sep 2019 19:44:08 -0400 Subject: [PATCH] add tiled background rendering and one ghosty boi --- main.html | 4 +- main.js | 215 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 214 insertions(+), 5 deletions(-) diff --git a/main.html b/main.html index e124205..0be84f1 100644 --- a/main.html +++ b/main.html @@ -21,13 +21,15 @@ +
+ + - diff --git a/main.js b/main.js index f1b766f..fd00db0 100644 --- a/main.js +++ b/main.js @@ -1,3 +1,10 @@ +const Orientation = { + UP: 'up', + DOWN: 'down', + LEFT: 'left', + RIGHT: 'right' +} + class Input { constructor() { this.left = false; @@ -13,6 +20,8 @@ class Input { this.select = false; this.start = false; + this.leftArrowPressed = false; + this.rightArrowPressed = false; window.addEventListener('gamepadconnected', this.gamepadConnected); window.addEventListener('gamepaddisconnected', this.gamepadDisconnected); } @@ -140,6 +149,16 @@ class Graphics { this.ctx_.font = '' + size + 'px monospace'; this.ctx_.fillText(string, x, y); } + + drawImage(image, dx, dy) { + const src = image[0]; + const sx = image[1]; + const sy = image[2]; + const width = image[3]; + const height = image[4]; + this.ctx_.drawImage( + src, sx, sy, width, height, dx, dy, width, height); + } } class FpsCounter { @@ -162,7 +181,8 @@ class FpsCounter { } draw(gfx) { - gfx.text('FPS: ' + Math.round(this.fps), 8, 16, 16, 'yellow'); + const fpsDiv = document.getElementById('fps'); + fpsDiv.innerText = 'FPS: ' + Math.round(this.fps); } } @@ -171,27 +191,214 @@ class World { this.state_ = null; this.fpsCounter_ = new FpsCounter(); this.input_ = new Input(); + this.player_ = new Player(); + + // TODO: move rendering stuff to a separate object. + this.resources_ = new Resources(); + this.tileRenderer_ = new TileRenderer(); + this.playerRenderer_ = new PlayerRenderer(); this.gamepadRenderer_ = new GamepadRenderer(); } update(timestampMs) { this.fpsCounter_.update(timestampMs); this.input_.update(); + if (this.input_.left) { + this.player_.moveLeft(); + } + if (this.input_.right) { + this.player_.moveRight(); + } + if (this.input_.up) { + this.player_.moveUp(); + } + if (this.input_.down) { + this.player_.moveDown(); + } } draw(gfx) { - this.gamepadRenderer_.draw(gfx, this.input_); + gfx.fill('black'); + this.tileRenderer_.draw(gfx, this.resources_.sprites); + this.playerRenderer_.draw(gfx, this.resources_.sprites, this.player_); + // this.gamepadRenderer_.draw(gfx, this.input_); this.fpsCounter_.draw(gfx); } } +class Resources { + constructor() { + const atlantis = document.getElementById('atlantis'); + const ghost = document.getElementById('ghost'); + const ts = 16; + this.sprites = { + 'ground0': [atlantis, 2 * ts, 1 * ts, 16, 16], + 'ground1': [atlantis, 3 * ts, 1 * ts, 16, 16], + 'ground2': [atlantis, 4 * ts, 1 * ts, 16, 16], + 'ground3': [atlantis, 5 * ts, 1 * ts, 16, 16], + 'ground4': [atlantis, 6 * ts, 1 * ts, 16, 16], + 'ground5': [atlantis, 7 * ts, 1 * ts, 16, 16], + 'ground6': [atlantis, 8 * ts, 1 * ts, 16, 16], + 'rock0': [atlantis, 1 * ts, 2 * ts, 16, 16], + 'rock1': [atlantis, 2 * ts, 2 * ts, 16, 16], + 'rock2': [atlantis, 3 * ts, 2 * ts, 16, 16], + 'anchor0': [atlantis, 21 * ts, 1 * ts, 16, 16], + 'seaweed0': [atlantis, 20 * ts, 2 * ts, 16, 32], + 'seaweed1': [atlantis, 16 * ts, 2 * ts, 16, 32], + 'coral0': [atlantis, 15 * ts, 9 * ts, 32, 16], + 'rockpile0': [atlantis, 17 * ts, 10 * ts, 32, 32], + + 'ghostdown0': [ghost, 0, 0, 24, 36], + 'ghostdown1': [ghost, 26, 0, 24, 36], + 'ghostdown2': [ghost, 52, 0, 24, 36], + 'ghostleft0': [ghost, 0, 36, 24, 36], + 'ghostleft1': [ghost, 26, 36, 24, 36], + 'ghostleft2': [ghost, 52, 36, 24, 36], + 'ghostright0': [ghost, 0, 72, 24, 36], + 'ghostright1': [ghost, 26, 72, 24, 36], + 'ghostright2': [ghost, 52, 72, 24, 36], + 'ghostup0': [ghost, 0, 108, 24, 36], + 'ghostup1': [ghost, 26, 108, 24, 36], + 'ghostup2': [ghost, 52, 108, 24, 36], + } + } +} + +class Player { + constructor() { + this.x = (256 - 26) / 2; + this.y = (224 - 36) / 2; + this.orientation = Orientation.DOWN; + } + + moveLeft() { + this.orientation = Orientation.LEFT; + this.x -= 2; + if (this.x < -4) { + this.x = -4; + } + } + + moveRight() { + this.orientation = Orientation.RIGHT; + this.x += 2; + if (this.x > 256 - 21) { + this.x = 256 - 21; + } + } + + moveUp() { + this.orientation = Orientation.UP; + this.y -= 2; + if (this.y < -7) { + this.y = -7; + } + } + + moveDown() { + this.orientation = Orientation.DOWN; + this.y += 2; + if (this.y > 224 - 36) { + this.y = 224 - 36; + } + } +} + +class PlayerRenderer { + constructor() { + this.frameNum = 0; + } + + draw(gfx, sprites, player) { + let spriteIndex = Math.floor((this.frameNum % 40) / 10); + if (spriteIndex == 3) { spriteIndex = 1; } + const spriteName = 'ghost' + player.orientation + spriteIndex; + gfx.drawImage(sprites[spriteName], player.x, player.y); + this.frameNum++; + } +} + +class TileRenderer { + draw(gfx, sprites) { + const tileSize = 16; + const rows = gfx.height / tileSize; + const columns = gfx.width / tileSize; + const layer1 = ["-,*-...*'.,-_'`o", + "_..'-_**,',_.'oo", + "-*-''_-'o,0O_```", + "o`0_._,*O'`--'-'", + "`0O-_'',`o*o*`-,", + "*,`'---o'O'_*''-", + "'-.**.'_'`.,'-.'", + ".O'``*``'`*,,_o`", + "_*_''*O'`_OO-_'o", + "0`0,*-,`_*'`O'*.", + ".o'-*.*_',`,,`.'", + "`o`O',.`OO,*-'**", + "-..*'-''',*'.'.O", + "*-_'-0.--__O`O`_", + "*-_,O_'*'`*'_._.", + "-.*,`OO'_`'*-0-O"]; + const layer2 = [" ", + " ", + " iil ", + " ", + " A ", + " ", + " ", + " ", + " ", + " i ", + " l ", + " ", + " c R ", + " "]; + const spriteLookup = { + '.': sprites.ground0, + ',': sprites.ground1, + '_': sprites.ground2, + '`': sprites.ground3, + '-': sprites.ground4, + '*': sprites.ground5, + "'": sprites.ground6, + 'o': sprites.rock0, + 'O': sprites.rock1, + '0': sprites.rock2, + 'A': sprites.anchor0, + 'i': sprites.seaweed0, + 'l': sprites.seaweed1, + 'c': sprites.coral0, + 'R': sprites.rockpile0, + }; + for (let j = 0; j < columns; j++) { + for (let i = 0; i < rows; i++) { + const dx = tileSize * j; + const dy = tileSize * i; + const sprite = spriteLookup[layer1[i][j]]; + if (sprite) { + gfx.drawImage(sprite, dx, dy); + } + } + } + for (let j = 0; j < columns; j++) { + for (let i = 0; i < rows; i++) { + const dx = tileSize * j; + const dy = tileSize * i; + const sprite = spriteLookup[layer2[i][j]]; + if (sprite) { + gfx.drawImage(sprite, dx, dy); + } + } + } + } + +} + class GamepadRenderer { draw(gfx, input) { const centerX = gfx.width / 2; const centerY = gfx.height / 2; - gfx.fill('black'); - // Select & Start gfx.circle(centerX + 12, centerY, 8, input.start ? 'cyan' : 'grey'); gfx.circle(centerX - 12, centerY, 8, input.select ? 'cyan' : 'grey');