aboutsummaryrefslogtreecommitdiff
path: root/js/game.js
diff options
context:
space:
mode:
authorJuan J. Martinez <jjm@usebox.net>2023-12-28 19:43:05 +0000
committerJuan J. Martinez <jjm@usebox.net>2023-12-28 19:47:11 +0000
commited0e61af2c9fb7b016cb72a91d207b761859c676 (patch)
tree7a64f254ecb1b921acf561d3aebc47672d0d9714 /js/game.js
downloadjs-canvas-2023-ed0e61af2c9fb7b016cb72a91d207b761859c676.tar.gz
js-canvas-2023-ed0e61af2c9fb7b016cb72a91d207b761859c676.zip
Initial import
Diffstat (limited to 'js/game.js')
-rw-r--r--js/game.js251
1 files changed, 251 insertions, 0 deletions
diff --git a/js/game.js b/js/game.js
new file mode 100644
index 0000000..85b0f91
--- /dev/null
+++ b/js/game.js
@@ -0,0 +1,251 @@
+class Game {
+ constructor(canvas) {
+ this.canvas = canvas;
+ this.ctx = canvas.getContext("2d");
+
+ // override this for a differnt size
+ this.width = 320;
+ this.height = 240;
+
+ // override this with your data to be loaded:
+ //
+ // { id: "file.png" }
+ //
+ // Supports .png, .ogg and .json files.
+ //
+ // and also override dataSize with:
+ //
+ // this.dataSize = Object.keys(this.data).length
+ //
+ this.data = {};
+ this.dataSize = 0;
+
+ // override to change keys
+ this.controls = {
+ 80: "pause",
+ 83: "start",
+ 37: "left",
+ 38: "up",
+ 39: "right",
+ 40: "down",
+ 90: "a",
+ 88: "b"
+ };
+
+ this.scale = 1;
+
+ // will store the resouces indexed by id
+ this.res = {};
+ this.resSize = 0;
+
+ // override if you want to change the limit
+ // defaults to 8 "channels"
+ this.playLimit = 8;
+ this.playCount = 0;
+
+ // test to see if a key is down or not
+ this.keys = {
+ pause: false,
+ start: false,
+ left: false,
+ up: false,
+ right: false,
+ down: false,
+ a: false,
+ b: false
+ };
+
+ this.minFps = 60;
+ this.then = -1 / 60;
+
+ this.loadingError = undefined;
+ }
+
+ start() {
+ this.canvas.style.background = "rgb(21, 21, 21)";
+ this.resize();
+
+ window.onresize = (ev) => {
+ this.resize();
+ };
+
+ this.loader();
+ }
+
+ // override this
+ init() {}
+ // override this
+ update(dt) {}
+ // override this
+ draw() {}
+
+ _update(dt) {}
+ _draw() {}
+
+ resize() {
+ this.scale = Math.floor(window.innerHeight / this.height);
+ this.canvas.width = this.width * this.scale;
+ this.canvas.height = this.height * this.scale;
+
+ if (this.ctx != undefined) {
+ this.ctx.imageSmoothingEnabled = false;
+ }
+ }
+
+ loop(now) {
+ let dt = Math.min(1 / this.minFps, now - this.then);
+ this._update(dt);
+
+ this._draw();
+
+ this.then = now;
+ requestAnimationFrame((now) => {
+ this.loop(now)
+ });
+ }
+
+ loader() {
+ this._draw = this.drawLoading;
+ this.loop(0);
+
+ const onError = (ev) => {
+ console.log(ev);
+ this.loadingError = true;
+ };
+
+ for (const id in this.data) {
+ if (this.data[id].indexOf(".png") != -1) {
+ this.res[id] = new Image();
+ this.res[id].src = this.data[id];
+ this.res[id].onerror = onError
+ this.res[id].onload = (ev) => {
+ this.res[id].onload = null;
+ ev.currentTarget.removeEventListener("error", onError);
+ this.resSize += 1;
+ };
+ continue;
+ }
+ if (this.data[id].indexOf(".ogg") != -1) {
+ this.res[id] = new Audio();
+ this.res[id].src = this.data[id];
+ this.res[id].autoplay = false;
+ this.res[id].addEventListener("error", onError);
+ this.res[id].oncanplaythrough = (ev) => {
+ this.res[id].oncanplaythrough = null;
+ ev.currentTarget.removeEventListener("error", onError);
+ this.resSize += 1;
+ };
+ continue;
+ }
+ if (this.data[id].indexOf(".json") != -1) {
+ let xhr = new window.XMLHttpRequest();
+ xhr.open("GET", this.data[id]);
+ xhr.responseType = "json";
+ xhr.addEventListener("error", onError);
+ xhr.onload = (ev) => {
+ ev.currentTarget.removeEventListener("error", onError);
+ xhr.onload = null;
+ if (xhr.status == 200) {
+ this.res[id] = xhr.response;
+ this.resSize += 1;
+ } else {
+ console.log(ev);
+ this.loadingError = true;
+ }
+ };
+ xhr.send();
+ continue;
+ }
+ }
+ }
+
+ // erases the canvas and sets the scaling
+ drawStart() {
+ this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
+ this.ctx.save();
+ this.ctx.scale(this.scale, this.scale);
+ }
+
+ // restores the context
+ drawEnd() {
+ this.ctx.restore();
+ }
+
+ // won't use drawStart/drawEnd because may render regular fonts
+ drawLoading() {
+ this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
+ this.ctx.save();
+
+ // height of the loading bar
+ const h = 6;
+
+ if (this.loadingError == true) {
+ this.ctx.fillStyle = "rgb(255, 255, 255)";
+ this.ctx.font = "caption";
+ this.ctx.fillText(
+ "ERROR Loading Resources",
+ Math.floor(this.width * 0.1 * this.scale),
+ Math.floor((this.height / 2 - h - 2) * this.scale)
+ );
+ }
+
+ this.ctx.scale(this.scale, this.scale);
+
+ this.ctx.fillStyle = "rgb(128, 128, 128)";
+ this.ctx.fillRect(Math.floor(this.width * 0.1), Math.floor(this.height / 2) - h, this.width - Math.floor(this.width * 0.2), h);
+ this.ctx.fillStyle = "rgb(255, 255, 255)";
+ this.ctx.fillRect(Math.floor(this.width * 0.1), Math.floor(this.height / 2) - h, (
+ Math.floor(this.resSize * (this.width - Math.floor(this.width * 0.2)) / this.dataSize)
+ ), h);
+
+ this.ctx.restore();
+
+ // we don't do this on update because we want
+ // the progress bar to finish drawing
+ if (this.resSize == this.dataSize) {
+ console.log("Loader done");
+
+ document.addEventListener("keydown", (ev) => {
+ this.keyDown(ev)
+ }, false);
+ document.addEventListener("keyup", (ev) => {
+ this.keyUp(ev)
+ }, false);
+
+ this.init();
+ this._update = this.update;
+ this._draw = this.draw;
+ }
+ }
+
+ playSnd(playable, loop, clone) {
+ if (this.playCount < this.playLimit) {
+ this.playCount += 1;
+
+ if (clone || false) {
+ playable = playable.cloneNode(true)
+ }
+ playable.onended = (ev) => {
+ playable.onended = null;
+ this.playCount -= 1;
+ };
+ playable.loop = loop || false;
+ playable.play();
+ }
+ }
+
+ keyDown(ev) {
+ let key = this.controls[ev.keyCode];
+ if (key != undefined) {
+ this.keys[key] = true;
+ ev.preventDefault();
+ }
+ }
+
+ keyUp(ev) {
+ let key = this.controls[ev.keyCode];
+ if (key != undefined) {
+ this.keys[key] = false;
+ }
+ }
+}