SpikeWars: refactor rendering out into a separate module. Finished with basic game loop.
This commit is contained in:
parent
72f0eede9d
commit
7d0b953fbe
@ -16,6 +16,7 @@
|
||||
<h1>Spike Wars!</h1>
|
||||
<canvas width=960 height=384></canvas>
|
||||
</div>
|
||||
<div><input id=pause type=checkbox checked=false>Pause</input>
|
||||
<script type='application/javascript' src="js/spike-wars-${version}.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,15 +1,12 @@
|
||||
// @flow
|
||||
import type Board from './game-types';
|
||||
import type GameState from './game-types';
|
||||
import SpikeWarsArtist from './spike-wars-artist';
|
||||
|
||||
function render(state: GameState, g: CanvasRenderingContext2D): void {
|
||||
var space = g.measureText(state.count + '');
|
||||
g.clearRect(100, 100, space.width, 50);
|
||||
g.fillText(state.count + '', 100, 100);
|
||||
function render(state: GameState, artist: SpikeWarsArtist): void {
|
||||
}
|
||||
|
||||
function update(state: GameState): GameState {
|
||||
state.count++;
|
||||
return state;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
export type Piece = "WALL" | "PLAYER" | "DEBUG";
|
||||
export type Board = Array<number>;
|
||||
export type GameState = {
|
||||
board: Board,
|
||||
count: number
|
||||
};
|
||||
|
52
spike-wars/src/main/js/spike-wars-artist.js
Normal file
52
spike-wars/src/main/js/spike-wars-artist.js
Normal file
@ -0,0 +1,52 @@
|
||||
// @flow
|
||||
type ConstructorOptions = {
|
||||
canvasSelector?: string,
|
||||
rows: number,
|
||||
columns: number };
|
||||
|
||||
export default class SpikeWarsArtist {
|
||||
canvas: HTMLCanvasElement;
|
||||
|
||||
// This is a reference to the drawing API: how we tell the computer to draw
|
||||
// on the canvas. It is a 2D context, meaning we draw in two-dimensions, like
|
||||
// on paper.
|
||||
canvas2d: CanvasRenderingContext2D;
|
||||
|
||||
rows: number; // How many rows are on our game board?
|
||||
cols: number; // How many columns are on our game board?
|
||||
tileWidth: number; // How wide is one of our game board spots?
|
||||
tileHeight: number; // How tall is one of our game bouard spots?
|
||||
|
||||
constructor({canvasSelector = 'canvas', rows, columns}: ConstructorOptions) {
|
||||
this.canvas = ((document.querySelector(canvasSelector): any): HTMLCanvasElement);
|
||||
|
||||
var c2d: ?CanvasRenderingContext2D = this.canvas.getContext('2d');
|
||||
if (c2d) this.canvas2d = c2d;
|
||||
else throw "Cannot get 2D rendering context!"; // TODO: better handling
|
||||
|
||||
this.rows = rows;
|
||||
this.cols = columns;
|
||||
|
||||
this.tileHeight = Math.ceil(this.canvas.height / this.rows);
|
||||
this.tileWidth = Math.ceil(this.canvas.width / this.cols);
|
||||
|
||||
console.log("SpikeWarsArtist: \n\trows: ", this.rows, "\tcols: ", this.cols,
|
||||
"\n\ttileWidth: ", this.tileWidth, "\ttileHeight: ", this.tileHeight);
|
||||
}
|
||||
|
||||
coord2idx(row: number, col: number): number { return (this.cols * row) + col; }
|
||||
idx2row(idx: number): number { return Math.floor(idx/this.cols); }
|
||||
idx2col(idx: number): number { return idx % this.cols; }
|
||||
|
||||
drawSolidColor(row: number, col: number, color: string): void {
|
||||
this.drawSolidColorIdx(this.coord2idx(row, col), color); }
|
||||
|
||||
drawSolidColorIdx(idx: number, color: string): void {
|
||||
this.canvas2d.fillStyle = color;
|
||||
|
||||
this.canvas2d.fillRect(
|
||||
(idx % this.cols) * this.tileWidth,
|
||||
Math.floor(idx / this.cols) * this.tileHeight,
|
||||
this.tileWidth, this.tileHeight); }
|
||||
|
||||
}
|
@ -1,15 +1,20 @@
|
||||
// @flow
|
||||
import Stats from 'stats.js';
|
||||
import SpikeWarsArtist from './spike-wars-artist';
|
||||
import {update, render} from './game-logic';
|
||||
|
||||
import type Board from './game-types';
|
||||
import type GameState from './game-types';
|
||||
import type Piece from './game-types';
|
||||
|
||||
type SpikeWarsOptions = {fps?: number};
|
||||
const WALL = "WALL";
|
||||
const DEBUG = "DEBUG";
|
||||
|
||||
type SpikeWarsOptions = {fps?: number, stats?: Object};
|
||||
|
||||
export default class SpikeWars {
|
||||
ROWS: number; // How many rows does our board have?
|
||||
COLS: number; // How many columns does our board have?
|
||||
TILE_SIZE: number; // How big is each square on our board?
|
||||
rows: number;
|
||||
cols: number;
|
||||
|
||||
// This is where we keep track of all the things that are happening in the
|
||||
// game.
|
||||
@ -18,46 +23,64 @@ export default class SpikeWars {
|
||||
nextGameTick: number; // At what time does the next game update happen?
|
||||
skipTicks: number; // How long does the game wait between updates?
|
||||
|
||||
// This is where we keep a reference to our canvas, what we are drawing on.
|
||||
canvas: HTMLCanvasElement;
|
||||
// This is the thing that knows how to draw stuff.
|
||||
artist: SpikeWarsArtist;
|
||||
|
||||
// This is a reference to the drawing API: how we tell the computer to draw
|
||||
// on the canvas. It is a 2D context, meaning we draw in two-dimensions, like
|
||||
// on paper.
|
||||
canvas2d: CanvasRenderingContext2D;
|
||||
stats: ?Object;
|
||||
pauseCheckbox: HTMLInputElement;
|
||||
paused: boolean;
|
||||
|
||||
// The constructor is the function that is run when somebody first creates a
|
||||
// new SpikeWars instance. This is where we set stuff up for the first time.
|
||||
constructor({fps = 30}: SpikeWarsOptions) {
|
||||
constructor({fps = 30, stats}: SpikeWarsOptions) {
|
||||
|
||||
// TODO: detect/set automatically instead of hard-coding
|
||||
this.ROWS = 8;
|
||||
this.COLS = 20;
|
||||
this.TILE_SIZE = 48;
|
||||
// Get our artist
|
||||
this.rows = 16;
|
||||
this.cols = 40;
|
||||
this.artist = new SpikeWarsArtist({rows: this.rows, columns: this.cols});
|
||||
|
||||
this.state = {board: this.newBoard(), count: 0}; // create a new board
|
||||
|
||||
// Find our canvas and ask for the reference to the drawing API.
|
||||
this.canvas = window.document.getElementsByTagName('canvas')[0];
|
||||
this.canvas2d = this.canvas.getContext('2d');
|
||||
this.stats = stats;
|
||||
|
||||
this.pauseCheckbox = window.document.getElementsByTagName('input')[0];
|
||||
this.pauseCheckbox.checked = false;
|
||||
this.pauseCheckbox.addEventListener('change', () => { this.toggleGamePaused() });
|
||||
this.paused = false;
|
||||
|
||||
this.skipTicks = Math.ceil(1000 / fps);
|
||||
// TODO: register event handlers on the canvas element.
|
||||
|
||||
}
|
||||
|
||||
// This function creates a new board.
|
||||
newBoard(): Board { return new Array(this.ROWS * this.COLS); }
|
||||
newBoard(): Board {
|
||||
var board = new Array(this.rows * this.cols);
|
||||
for (var i = 0; i < board.length; i++) {
|
||||
var col = i % this.cols;
|
||||
var row = Math.floor(i / this.cols);
|
||||
if (col === 0 || col === (this.cols - 1) ||
|
||||
row === 0 || row === (this.rows - 1)) board[i] = WALL; }
|
||||
return board; }
|
||||
|
||||
toggleGamePaused(): void {
|
||||
this.paused = this.pauseCheckbox.checked;
|
||||
|
||||
if (!this.paused) {
|
||||
this.nextGameTick = new Date().getTime();
|
||||
this.gameLoop(); }
|
||||
}
|
||||
|
||||
// The game loop is where the game actually runs. It is called a loop because
|
||||
// it runs over and over and over, in a loop until it is time to stop the
|
||||
// game.
|
||||
gameLoop(): void {
|
||||
// TODO: break the game loop upon exit conditions
|
||||
if (this.paused) return;
|
||||
|
||||
// We are going to ask the browser to call us again next time we have a
|
||||
// chance to draw. We're doing this first because we want to make sure we
|
||||
// reserve our place in line before we get busy doing stuff.
|
||||
window.requestAnimationFrame(this.gameLoop);
|
||||
window.requestAnimationFrame(() => { this.gameLoop(); });
|
||||
//window.setTimeout(() => { this.gameLoop(); });
|
||||
|
||||
// Now we need to check if it is time to do stuff. If our computer is
|
||||
// really fast the game loop might run really fast, or if our computer is
|
||||
@ -68,6 +91,8 @@ export default class SpikeWars {
|
||||
//
|
||||
// Is it time for the next game tick?
|
||||
if ((new Date()).getTime() > this.nextGameTick) {
|
||||
if (this.stats) this.stats.begin();
|
||||
|
||||
// Yes! So the first thing we need to do is figure out when the next tick
|
||||
// should be.
|
||||
this.nextGameTick += this.skipTicks;
|
||||
@ -76,13 +101,35 @@ export default class SpikeWars {
|
||||
this.state = update(this.state);
|
||||
|
||||
// And we re-draw the world.
|
||||
render(this.state, this.canvas2d); }
|
||||
render(this.state, this.artist);
|
||||
if (this.stats) this.stats.end(); }
|
||||
}
|
||||
|
||||
startGame(): void { this.gameLoop(); }
|
||||
startGame(): void {
|
||||
this.nextGameTick = new Date().getTime();
|
||||
this.drawBoard(this.artist, this.state.board);
|
||||
this.gameLoop(); }
|
||||
|
||||
drawBoard(artist: SpikeWarsArtist, board: Board): void {
|
||||
for (var i = 0; i < board.length; i++) {
|
||||
var color: string;
|
||||
switch (board[i]) {
|
||||
case WALL: color = 'black'; break;
|
||||
case DEBUG: color = 'red'; break;
|
||||
default: color = 'white';
|
||||
}
|
||||
this.artist.drawSolidColorIdx(i, color); } }
|
||||
}
|
||||
|
||||
|
||||
// instantiate the instance of SpikeWars
|
||||
window.spikeWars = new SpikeWars({fps: 30});
|
||||
window.spikeWars.startGame();
|
||||
if (false) {
|
||||
var stats = new Stats();
|
||||
stats.showPanel(0);
|
||||
document.body.appendChild(stats.dom);
|
||||
window.spikeWars = new SpikeWars({fps: 30, stats: stats}); }
|
||||
|
||||
else { window.spikeWars = new SpikeWars({fps: 30}); }
|
||||
|
||||
//window.spikeWars.startGame();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user