add tiled background rendering and one ghosty boi

This commit is contained in:
Colin McMillen 2019-09-24 19:44:08 -04:00
parent d7a88b6d67
commit ce3ee42c4e
2 changed files with 214 additions and 5 deletions

View File

@ -21,13 +21,15 @@
<body> <body>
<!-- SNES resolution: 256x224. 4x scaled: 1024 x 896. --> <!-- SNES resolution: 256x224. 4x scaled: 1024 x 896. -->
<canvas id="canvas" width="256" height="224"></canvas> <canvas id="canvas" width="256" height="224"></canvas>
<div id="fps"></div>
<div id="debug"></div> <div id="debug"></div>
<button id="1x">1x</button> <button id="1x">1x</button>
<button id="2x">2x</button> <button id="2x">2x</button>
<button id="3x">3x</button> <button id="3x">3x</button>
<button id="4x">4x</button> <button id="4x">4x</button>
<button id="5x">5x</button> <button id="5x">5x</button>
<img src="resources/tf_atlantis_tiles.png" id="atlantis" style="display: none">
<img src="resources/ghost1.png" id="ghost" style="display: none">
<script src="main.js"></script> <script src="main.js"></script>
</body> </body>
</html> </html>

215
main.js
View File

@ -1,3 +1,10 @@
const Orientation = {
UP: 'up',
DOWN: 'down',
LEFT: 'left',
RIGHT: 'right'
}
class Input { class Input {
constructor() { constructor() {
this.left = false; this.left = false;
@ -13,6 +20,8 @@ class Input {
this.select = false; this.select = false;
this.start = false; this.start = false;
this.leftArrowPressed = false;
this.rightArrowPressed = false;
window.addEventListener('gamepadconnected', this.gamepadConnected); window.addEventListener('gamepadconnected', this.gamepadConnected);
window.addEventListener('gamepaddisconnected', this.gamepadDisconnected); window.addEventListener('gamepaddisconnected', this.gamepadDisconnected);
} }
@ -140,6 +149,16 @@ class Graphics {
this.ctx_.font = '' + size + 'px monospace'; this.ctx_.font = '' + size + 'px monospace';
this.ctx_.fillText(string, x, y); 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 { class FpsCounter {
@ -162,7 +181,8 @@ class FpsCounter {
} }
draw(gfx) { 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.state_ = null;
this.fpsCounter_ = new FpsCounter(); this.fpsCounter_ = new FpsCounter();
this.input_ = new Input(); 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(); this.gamepadRenderer_ = new GamepadRenderer();
} }
update(timestampMs) { update(timestampMs) {
this.fpsCounter_.update(timestampMs); this.fpsCounter_.update(timestampMs);
this.input_.update(); 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) { 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); 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 { class GamepadRenderer {
draw(gfx, input) { draw(gfx, input) {
const centerX = gfx.width / 2; const centerX = gfx.width / 2;
const centerY = gfx.height / 2; const centerY = gfx.height / 2;
gfx.fill('black');
// Select & Start // Select & Start
gfx.circle(centerX + 12, centerY, 8, input.start ? 'cyan' : 'grey'); gfx.circle(centerX + 12, centerY, 8, input.start ? 'cyan' : 'grey');
gfx.circle(centerX - 12, centerY, 8, input.select ? 'cyan' : 'grey'); gfx.circle(centerX - 12, centerY, 8, input.select ? 'cyan' : 'grey');