all repos — katbugjs @ bf245d2d2ab08c1a047ee87c7629e3d64348b069

side-scrolling infinite arcade game in Javascript with HTML5 Canvas

first commit
Iris Lightshard nilix@nilfm.cc
commit

bf245d2d2ab08c1a047ee87c7629e3d64348b069

A Catbug.js

@@ -0,0 +1,31 @@

+var catbug = {}; +catbug.x = 45; +catbug.y = 90; +catbug.vX = 0; +catbug.vY = 0; +catbug.frame = 0; +catbug.HP = 3; +catbug.maxHP = 3; +catbug.move = function() +{ + this.x += this.vX; + this.y += this.vY; + + if (this.x <= 0 || this.x >= 320) + { + this.x -= this.vX; + } + if (this.y <= 0 || this.y >= 180) + { + this.y -= this.vY; + } +} +catbug.isInRect = function(box) +{ + if (this.x >= box.x && this.x <= box.x + box.w + && this.y >= box.y && this.y <= box.y + box.h) + { + return true; + } + return false; +}
A Engine.js

@@ -0,0 +1,354 @@

+window.renderer = {}; +window.gameState = {}; +window.controller = {}; + +renderer.bgx = 0; +renderer.sprite = new Image(); +renderer.sprite.src = "assets/seethruzone.png"; +renderer.gfx = {}; + +renderer.loadingScreen = function() +{ + if (renderer.gfx.loadedCount < 8 && renderer.gfx.loadedCount > 1) + { + screen.drawImage(renderer.gfx.rawscreen, 0, 0) + } + if (renderer.gfx.loadedCount < 8) + { + requestAnimationFrame(loadingScreen); + } +} + +renderer.loadgfx = function() +{ + renderer.gfx.loadedCount = 0; + renderer.gfx.rawscreen = new Image(); + renderer.gfx.rawscreen.src = "assets/rawscreen.png"; + renderer.gfx.rawscreen.onload = renderer.gfx.loadedCount++; + renderer.gfx.bg = new Image(); + renderer.gfx.bg.onload = renderer.gfx.loadedCount++; + renderer.gfx.bg.src = "assets/seethruzone.png"; + renderer.gfx.title = new Image(); + renderer.gfx.title.onload = renderer.gfx.loadedCount++; + renderer.gfx.title.src = "assets/titletext.png"; + renderer.gfx.catbug = new Image(); + renderer.gfx.catbug.onload = renderer.gfx.loadedCount++; + renderer.gfx.catbug.src = "assets/catbug.png"; + renderer.gfx.peas = new Image(); + renderer.gfx.peas.onload = renderer.gfx.loadedCount++; + renderer.gfx.peas.src = "assets/sugarpeas.png"; + renderer.gfx.pbs = new Image(); + renderer.gfx.pbs.onload = renderer.gfx.loadedCount++; + renderer.gfx.pbs.src = "assets/peanutbuttersquare.png"; + renderer.gfx.taco = new Image(); + renderer.gfx.taco.onload = renderer.gfx.loadedCount++; + renderer.gfx.taco.src = "assets/taco.png"; + renderer.gfx.HP = new Image(); + renderer.gfx.HP.onload = renderer.gfx.loadedCount++; + renderer.gfx.HP.src = "assets/hp.png"; + requestAnimationFrame(renderer.loadingScreen); + +} + +renderer.bg = function() +{ + screen.drawImage(this.gfx.bg, this.bgx, 0); + screen.drawImage(this.gfx.bg, this.bgx + 1472, 0); + this.bgx -= 8; + if (this.bgx == -1472) this.bgx = 0; +} + +renderer.bgPaused = function() +{ + screen.drawImage(this.gfx.bg, this.bgx, 0); + screen.drawImage(this.gfx.bg, this.bgx + 1472, 0); +} + +renderer.drawCatbug = function() +{ + switch(catbug.frame) + { + case 0: + screen.drawImage(this.gfx.catbug, 0, 0, 43, 39, catbug.x - 22, catbug.y - 20, 43, 39); + if (!gameState.paused) + { + catbug.frame = 1; + } + break; + case 1: + screen.drawImage(this.gfx.catbug, 43, 0, 43, 39, catbug.x - 22, catbug.y - 20, 43, 39); + if (!gameState.paused) + { + catbug.frame = 0; + } + break; + } +} + +renderer.drawHP = function() +{ + var i; + for (i = 0; i < catbug.HP; i++) + { + screen.drawImage(this.gfx.HP, 10+(6*i), 10); + } +} + +renderer.drawPickups = function() +{ + var i; + for (i = 0; i < 10; i++) + { + if (!isEmptyObject(stuff[i])){ + screen.drawImage(stuff[i].sprite, stuff[i].x, stuff[i].y);} + } +} + +renderer.drawScore = function() +{ + screen.font = '8px kong'; + screen.textBaseline = 'top'; + screen.fillStyle = 'white'; + screen.fillText(gameState.points, 10, 20) +} + +renderer.drawPaused = function() +{ + screen.font = '8px kong'; + screen.textBaseline = 'top'; + screen.fillStyle = 'white'; + screen.fillText("paused", 10, 30); +} + +gameState.paused = false; +gameState.playing = false; +gameState.over = false; +gameState.points = 0; +gameState.threshold = 10; +gameState.ticker = new Date(); +gameState.frame = new Date(); + +controller.up = false; +controller.down = false; +controller.right = false; +controller.left = false; +controller.space = false; +controller.q = false; + +function isEmptyObject(obj) +{ + var name; + for (name in obj) + { + if (obj.hasOwnProperty(name)) + { + return false; + } + } + return true; +} + + +function scoreSummary() +{ + gameState.frame = thisFrame; + renderer.bg(); + screen.font = '16px kong'; + screen.textBaseline = 'top'; + screen.fillStyle = 'white'; + screen.fillText("SCORE:", 120, 80); + screen.fillText(gameState.points, 120, 105); +} + +function checkHP() +{ + if (catbug.HP <= 0) + { + gameState.paused = false; + gameState.over = true; + } +} + +renderer.titleScreen = function() +{ + renderer.bg(); + screen.drawImage(renderer.gfx.title, 0, 0); + if (controller.space == true) + { + resetGame(); + gameState.playing = true; + } +} + +function resetGame() +{ + catbug.maxHP = 3; + catbug.HP = 3; + catbug.x = 45; + catbug.y = 90; + catbug.vX = 0; + catbug.vY = 0; + gameState.points = 0; + gameState.threshold = 10; + stuff = [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}]; +} + +function loop() +{ + thisFrame = new Date(); + if (thisFrame - gameState.frame < 33.34) + { + requestAnimationFrame(loop); + } + else + { + gameState.frame = thisFrame; + if (!gameState.playing) + { renderer.titleScreen(); } + else + { + if (!gameState.over){ + if (gameState.paused) { renderer.bgPaused()} + else{ + renderer.bg()}; + if (!gameState.paused) + { + catbug.move(); + managePickups(); + } + renderer.drawCatbug(); + renderer.drawPickups(); + renderer.drawHP(); + if (gameState.paused) + { + renderer.drawPaused(); + } + renderer.drawScore(); + checkHP();} + else + { + scoreSummary(); + } + } + requestAnimationFrame(loop); + } +} + +document.addEventListener("keydown", pressHandler, false); +document.addEventListener("keyup", releaseHandler, false); + +function pressHandler(e) +{ + switch (e.key) + { + case "Right": + case "ArrowRight": + controller.right = true; + if (!gameState.paused) + { + catbug.vX = 6; + } + break; + case "Left": + case "ArrowLeft": + controller.left = true; + if (!gameState.paused) + { + catbug.vX = -6; + } + break; + case "Up": + case "ArrowUp": + controller.up = true; + if (!gameState.paused) + { + catbug.vY = -6; + } + break; + case "Down": + case "ArrowDown": + controller.down = true; + if (!gameState.paused) + { + catbug.vY = 6; + } + break; + case "q": + controller.q = true; + gameState.playing = false; + gameState.paused = false; + gameState.over = false; + break; + case " ": + case "Spacebar": + controller.space = true; + if (gameState.over) + { + gameState.over = false; + gameState.playing = false; + controller.space = false; + } + else + { + if (gameState.playing) + { + if (!gameState.paused) + { + gameState.paused = true; + } + else{ gameState.paused = false; } + } + } + break; + default: + console.log("That key doesn't do anything!"); + } +} + +function releaseHandler(e) +{ + switch (e.key) + { + case "Right": + case "ArrowRight": + controller.right = false; + if (catbug.vX > 0) + { + catbug.vX = 0; + } + break; + case "Left": + case "ArrowLeft": + controller.left = false; + if (catbug.vX < 0) + { + catbug.vX = 0; + } + break; + case "Up": + case "ArrowUp": + controller.up = false; + if (catbug.vY < 0) + { + catbug.vY = 0; + } + break; + case "Down": + case "ArrowDown": + controller.down = false; + if (catbug.vY > 0) + { + catbug.vY = 0; + } + break; + case "q": + controller.q = false; + break; + case " ": + case "Spacebar": + controller.space = false; + break; + default: + break; + } +}
A LICENSE

@@ -0,0 +1,30 @@

+Copyright (c) 2019, Derek Stevens +nilix@nilfm.cc + +Catbug & The Bravest Warriors (c) Pendelton Ward/Cartoon Hangover +This is fan-made free software, not endorsed or otherwise sanctioned by the +above copyright holder(s) regarding the intellectual property therein. + +"kongtext" font (c) codeman38 (zone38.net) + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must include source code and reproduce the + above copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +
A Pickup.js

@@ -0,0 +1,99 @@

+window.stuff = [{},{},{},{},{},{},{},{},{},{}] + +function Pickup() +{ + var pType = Math.floor(Math.random()*10); + this.y = -16 + Math.floor(Math.random()*180); + this.x = 320 + Math.floor(Math.random()*120); + this.vX = -4 - Math.floor(Math.random()*catbug.maxHP); + this.vY = 0; + + switch (pType) + { + case 0: + case 1: + case 2: + case 3: + case 4: + this.sprite = renderer.gfx.peas; + this.points = 1; + break; + case 5: + case 6: + case 7: + this.sprite = renderer.gfx.pbs + this.points = 2; + break; + case 8: + case 9: + this.sprite = renderer.gfx.taco; + this.points = 3; + break; + } +} + +function managePickups() +{ + var genSpeed = 0; + var i; + if (gameState.points > 10) + { + genSpeed = Math.floor(Math.random()*gameState.points); + } + else genSpeed = Math.floor(Math.random()*10); + for (i = 0; i < 10; i++) + { + if (!isEmptyObject(stuff[i])) + { + movePickup(stuff[i]); + if (stuff[i].x < -16) + { + stuff[i] = {}; + catbug.HP--; + } + else + { + var box = {}; + box.x = stuff[i].x; + box.y = stuff[i].y; + box.w = 36; + box.h = 36; + if (catbug.isInRect(box)) + { + getPickup(stuff[i]); + stuff[i] = {}; + } + } + } + else + { + if (( gameState.points >= 40 && genSpeed*genSpeed >= gameState.points*4) || + ( gameState.points <= 40 && genSpeed*genSpeed >= gameState.threshold*2)) + { + var now = new Date(); + if (now - gameState.ticker >= 1000) + { + stuff[i] = new Pickup(); + gameState.ticker = now; + } + } + } + } +} + +function movePickup(self) +{ + self.x += self.vX; +} + +function getPickup(self) +{ + gameState.points += self.points; + if (gameState.points >= gameState.threshold) + { + gameState.threshold *= 2; + catbug.maxHP++; + catbug.HP = catbug.maxHP; + } + +}
A Touch.js

@@ -0,0 +1,225 @@

+function doNothing(e) +{ + e.preventDefault(); +} + +function pressUp(e) +{ + if (!gameState.paused) + { + catbug.vY = -6; + } + doNothing(e); +} + +function releaseUp(e) +{ + if (catbug.vY < 0) + { + catbug.vY = 0; + } +} + +function pressDown(e) +{ + if (!gameState.paused) + { + catbug.vY = 6; + } + doNothing(e); +} + +function releaseDown(e) +{ + if (catbug.vY > 0) + { + catbug.vY = 0; + } +} + +function pressLeft(e) +{ + if (!gameState.paused) + { + catbug.vX = -6; + } + doNothing(e); +} + +function releaseLeft(e) +{ + if (catbug.vX < 0) + { + catbug.vX = 0; + } +} + +function pressRight(e) +{ + if (!gameState.paused) + { + catbug.vX = 6; + } + doNothing(e); +} + +function releaseRight(e) +{ + if (catbug.vX > 0) + { + catbug.vX = 0; + } +} + +function pressUpLeft(e) +{ + pressUp(e); + pressLeft(e); +} + +function releaseUpLeft(e) +{ + releaseUp(e); + releaseLeft(e); +} + +function pressUpRight(e) +{ + pressUp(e); + pressRight(e); +} + +function releaseUpRight(e) +{ + releaseUp(e); + releaseRight(e); +} + +function pressDownLeft(e) +{ + pressDown(e); + pressLeft(e); +} + +function releaseDownLeft(e) +{ + releaseDown(e); + releaseLeft(e); +} + +function pressDownRight(e) +{ + pressDown(e); + pressRight(e); +} + +function releaseDownRight(e) +{ + releaseDown(e); + releaseRight(e); +} + +function pressQ(e) +{ + controller.q = true; + gameState.playing = false; + gameState.paused = false; + gameState.over = false; +} + +function releaseQ(e) +{ + controller.q = false; +} + +function pressPause(e) +{ + controller.space = true; + if (gameState.over) + { + gameState.over = false; + gameState.playing = false; + controller.space = false; + } + else + { + if (gameState.playing) + { + if (!gameState.paused) + { + gameState.paused = true; + } + else{ gameState.paused = false; } + } + } + +} + +function releasePause(e) +{ + controller.space = false; +} + +function prepareTouchInput() +{ + +document.getElementById("dpad").style.visibility = "visible"; +document.getElementById("spaceButton").style.visibility = "visible"; +document.getElementById("qButton").style.visibility = "visible"; +window.dPad = {}; + +dPad.n = document.getElementById("upButton"); +dPad.s = document.getElementById("downButton"); +dPad.w = document.getElementById("leftButton"); +dPad.e = document.getElementById("rightButton"); +dPad.nw = document.getElementById("nwButton"); +dPad.ne = document.getElementById("neButton"); +dPad.sw = document.getElementById("swButton"); +dPad.se = document.getElementById("seButton"); +dPad.n.addEventListener("touchstart",pressUp); +dPad.n.addEventListener("touchend", releaseUp); +dPad.n.addEventListener("touchcancel", releaseUp); + +dPad.s.addEventListener("touchstart", pressDown); +dPad.s.addEventListener("touchend", releaseDown); +dPad.s.addEventListener("touchcancel", releaseDown); +dPad.e.addEventListener("touchstart", pressRight); +dPad.e.addEventListener("touchend", releaseRight); +dPad.e.addEventListener("touchcancel", releaseRight); +dPad.w.addEventListener("touchstart", pressLeft); +dPad.w.addEventListener("touchend", releaseLeft); +dPad.w.addEventListener("touchcancel", releaseLeft); +dPad.nw.addEventListener("touchstart", pressUpLeft); +dPad.nw.addEventListener("touchend", releaseUpLeft); +dPad.nw.addEventListener("touchcancel", releaseUpLeft); +dPad.ne.addEventListener("touchstart", pressUpRight); +dPad.ne.addEventListener("touchend", releaseUpRight); +dPad.ne.addEventListener("touchcancel", releaseUpRight); +dPad.sw.addEventListener("touchstart", pressDownLeft); +dPad.sw.addEventListener("touchend", releaseDownLeft); +dPad.sw.addEventListener("touchcancel", releaseDownLeft); +dPad.se.addEventListener("touchstart", pressDownRight); +dPad.se.addEventListener("touchend", releaseDownRight); +dPad.se.addEventListener("touchcancel", releaseDownRight); + +window.pauseButton = document.getElementById("spaceButton"); +pauseButton.addEventListener("touchstart", pressPause); +pauseButton.addEventListener("touchend", releasePause); +pauseButton.addEventListener("touchcancel", releasePause); + +window.qButton = document.getElementById("qButton"); +qButton.addEventListener("touchstart", pressQ); +qButton.addEventListener("touchend", releaseQ); +qButton.addEventListener("touchcancel", releaseQ); + +dPad.n.addEventListener("touchmove", doNothing); +dPad.s.addEventListener("touchmove", doNothing); +dPad.e.addEventListener("touchmove", doNothing); +dPad.w.addEventListener("touchmove", doNothing); +dPad.nw.addEventListener("touchmove", doNothing); +dPad.ne.addEventListener("touchmove", doNothing); +dPad.sw.addEventListener("touchmove", doNothing); +dPad.se.addEventListener("touchmove", doNothing); +pauseButton.addEventListener("touchmove", doNothing); +qButton.addEventListener("touchmove", doNothing); +}
A index.html

@@ -0,0 +1,212 @@

+<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <meta name="HandheldFriendly" content="True" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + + <title>Catbug: in the see-through zone!</title> + <style> + *{padding: 0; margin: 0;} + @font-face + { + font-family: kong; + src: url("assets/kong.ttf") format('truetype'); + font-weight: normal; + font-style: normal; + } + #loadFont + { + font-family: 'kong'; + visibility: auto; + height:0px; + } + #dpad + { + position:fixed; + bottom: 10px; + left: 10px; + background: rgba(0,0,0,0); + z-index:2; + width:200px; + height:200px; + visibility:hidden; + } + #upButton + { + position:absolute; + top: 0px; + left: 33%; + width:33%; + height:33%; + background: rgba(0,0,0,0.4); + border-width: 1px; + border-style: solid; + border-color: #444444; + } + #downButton + { + position:absolute; + bottom:0px; + left:33%; + width:33%; + height:33%; + background: rgba(0,0,0,0.4); + border-width: 1px; + border-style: solid; + border-color: #444444; + } + #leftButton + { + position:absolute; + top: 33%; + left: 0px; + width:33%; + height:33%; + background: rgba(0,0,0,0.4); + border-width: 1px; + border-style: solid; + border-color: #444444; + } + #rightButton + { + position:absolute; + top: 33%; + right: 0px; + width:33%; + height:33%; + background: rgba(0,0,0,0.4); + border-width: 1px; + border-style: solid; + border-color: #444444; + } + #nwButton + { + position:absolute; + top: 0px; + left:0px; + width:33%; + height:33%; + background: rgba(0,0,0,0.4); + border-width: 1px; + border-style: solid; + border-color: #444444; + } + #neButton + { + position:absolute; + top: 0px; + right: 0px; + width:33%; + height:33%; + background: rgba(0,0,0,0.4); + border-width: 1px; + border-style: solid; + border-color: #444444; + } + #swButton + { + position:absolute; + bottom: 0px; + left: 0px; + width:33%; + height:33%; + background: rgba(0,0,0,0.4); + border-width: 1px; + border-style: solid; + border-color: #444444; + } + #seButton + { + position:absolute; + bottom: 0px; + right: 0px; + width:33%; + height:33%; + background: rgba(0,0,0,0.4); + border-width: 1px; + border-style: solid; + border-color: #444444; + } + #spaceButton + { + position: fixed; + bottom: 10px; + right: 10px; + background:rgba(0,0,0,0.4); + width: 50px; + height: 50px; + border-color: #444444; + border-width: 1px; + border-style: solid; + visibility:hidden; + } + #qButton + { + position: fixed; + top: 10px; + right: 10px; + background:rgba(0,0,0,0.4); + width: 50px; + height: 50px; + border-color: #444444; + border-width: 1px; + border-style: solid; + visibility:hidden; + } + canvas + { + font-family: 'kong'; + background: #444444; + display: block; + margin: 0 auto; + width: 100%; + height: auto; + max-width: 640px; + -ms-interpolation-mode: nearest-neighbor; + image-rendering: -webkit-optimize-contrast; + image-rendering: -moz-crisp-edges; + image-rendering: -o-cristp-edges; + image-rendering: pixelated; + } + </style> + <script type="text/javascript" src="Catbug.js"></script> + <script type="text/javascript" src="Engine.js"></script> + <script type="text/javascript" src="Pickup.js"></script> + <script type="text/javascript" src="Touch.js"></script> + <script type="text/javascript"> + function main() + { + var gCanvas = document.getElementById("screen"); + window.screen = gCanvas.getContext("2d"); + renderer.loadgfx(); + if (navigator.userAgent.match(/Android|webOS|iPhone|iPad|iPod|BlackBerry|BB|PlayBook|IEMobile|Windows Phone|Kindle|Silk|Opera Mini|Mobile/i)) + { + + prepareTouchInput(); + } + + loop(); + } + </script> + + </head> + + <body onload="main();"> + <div id="loadFont">loading font...</div> + <div id="dpad"> + <div id="upButton"></div> + <div id="downButton"></div> + <div id="rightButton"></div> + <div id="leftButton"></div> + <div id="nwButton"></div> + <div id="neButton"></div> + <div id="swButton"></div> + <div id="seButton"></div> + </div> + <div id="qButton"></div> + <div id="spaceButton"></div> + <canvas id="screen" width="320" height="180"></canvas> + + </body> +</html>