diff --git a/asdf/Container.js b/asdf/Container.js index 083fdee..a6e6af5 100644 --- a/asdf/Container.js +++ b/asdf/Container.js @@ -1,31 +1,54 @@ +/** + * Container class + */ class Container { constructor() { - this.pos = { x: 0, y: 0}; + this.pos = { x: 0, y: 0 }; this.children = []; } - // Contrainer methods - add (child) { - this.children.push(child); - return child; - } + /** + * Adds child to container + * @param {*} child Child to add + * @returns {any} Added child + */ + add(child) { + this.children.push(child); + return child; + } - remove (child) { - this.children = this.children.filter(c => c !== child); - return child; - } + /** + * Removes child from container + * @param {*} child Child to remove + * @returns {any} Removed child + */ + remove(child) { + this.children = this.children.filter(c => c !== child); + return child; + } - map (f) { - return this.children.map(f); - } + /** + * Preforms a function on all children + * @param {function} f Function to preform on children + * @returns {any} Function altered array + */ + map(f) { + return this.children.map(f); + } - update(dt, t) { - this.children = this.children.filter(child => { - if (child.update) { - child.update(dt, t, this); - } - return child.dead ? false : true; - }); - } + /** + * Updates all children when called + * @param {number} dt Delta time + * @param {number} t Total time + * @returns {boolean} Returns if the child is dead or not + */ + update(dt, t) { + this.children = this.children.filter(child => { + if (child.update) { + child.update(dt, t, this); + } + return child.dead ? false : true; + }); + } } export default Container; diff --git a/asdf/Game.js b/asdf/Game.js index 85272d3..d2b6376 100644 --- a/asdf/Game.js +++ b/asdf/Game.js @@ -4,8 +4,18 @@ import CanvasRenderer from "./renderer/CanvasRenderer.js"; const STEP = 1 / 60; const FRAME_MAX = 5 * STEP; +/** + * Game class + */ class Game { - constructor (w, h, pixelated, parent = "#board") { + /** + * Set the games parameters + * @param {number} w Width of canvas + * @param {number} h Height of canvas + * @param {boolean} pixelated Turns canvas smoothening on or off + * @param {String} [parent="#board"] HTML id of element to push the canvas element too + */ + constructor(w, h, pixelated, parent = "#board") { this.w = w; this.h = h; this.renderer = new CanvasRenderer(w, h); @@ -18,7 +28,11 @@ class Game { this.scene = new Container(); } - run(gameUpdate = () => {}) { + /** + * Start game loop + * @param {Function} gameUpdate Function to run next to scene updates such as debug logging, etc. + */ + run(gameUpdate = () => { }) { let dt = 0; let last = 0; const loop = ms => { diff --git a/asdf/Sprite.js b/asdf/Sprite.js index 1a0b06b..9926838 100644 --- a/asdf/Sprite.js +++ b/asdf/Sprite.js @@ -1,4 +1,11 @@ +/** + * Sprite class + */ class Sprite { + /** + * Draw sprite on canvas + * @param {*} texture Sprite image + */ constructor(texture) { this.texture = texture; this.pos = { x: 0, y: 0 }; diff --git a/asdf/SpriteSheetXML.js b/asdf/SpriteSheetXML.js index 76674d3..922f9e2 100644 --- a/asdf/SpriteSheetXML.js +++ b/asdf/SpriteSheetXML.js @@ -1,15 +1,29 @@ -// XML format must be: -// -// -// ... -// +/** + * SpriteSheetXML - Reads XML files to get texture data + * + * **XML format must be:** + * + * + * + * + * ... + * + */ class SpriteSheetXML { + /** + * Set url of XML file + * @param {String} url Url to XML file + */ constructor(url) { this.array = []; this.fetchXMLtoArray(url); } + /** + * Fetch XML file and put contents in a JS array + * @param {String} url Url to XML file + */ fetchXMLtoArray(url) { var xhr = new XMLHttpRequest(); xhr.open('GET', url, false); @@ -32,6 +46,12 @@ class SpriteSheetXML { } } + /** + * Find index of XML element with attribute == value + * @param {String} attribute XML element attribute + * @param {String} value Value of XML element attribute + * @returns {number} Index of XML element + */ findIndex(attribute, value) { for (let index = 0; index < this.array.length; index++) { const element = this.array[index]; diff --git a/asdf/Text.js b/asdf/Text.js index 27febea..18d96c2 100644 --- a/asdf/Text.js +++ b/asdf/Text.js @@ -1,6 +1,14 @@ +/** + * Text class + */ class Text { + /** + * Prints styled text on canvas + * @param {String} text Text to print + * @param {String} style Styles to apply to text + */ constructor(text = "", style = {}) { - this.pos = { x: 0, y: 0}; + this.pos = { x: 0, y: 0 }; this.text = text; this.style = style; } diff --git a/asdf/Texture.js b/asdf/Texture.js index eb70fbe..eef2e41 100644 --- a/asdf/Texture.js +++ b/asdf/Texture.js @@ -1,4 +1,11 @@ +/** + * Texture class + */ class Texture { + /** + * Sets url of source image and creates an instance of Image() + * @param {*} url + */ constructor(url) { this.img = new Image(); this.img.src = url; diff --git a/asdf/TileMap.js b/asdf/TileMap.js index b6d77fe..bb6b86e 100644 --- a/asdf/TileMap.js +++ b/asdf/TileMap.js @@ -1,7 +1,19 @@ import Container from "./Container.js"; import TileSprite from "./TileSprite.js"; +/** + * Tilemap class + */ class TileMap extends Container { + /** + * Draws array of tiles from unindexed spritesheet + * @param {[ { x: number, y: number} ]} tiles Array of x and y values of the source tile on an unindexed Spritesheet + * @param {number} mapW Amount of tiles over the width of the map + * @param {number} mapH Amount of tiles over the height of the map + * @param {number} tileW Width of source tile(s) in pixels + * @param {number} tileH Height of source tile(s) in pixels + * @param {*} texture Texture instance of source image file + */ constructor(tiles, mapW, mapH, tileW, tileH, texture) { super(); this.mapW = mapW; @@ -11,7 +23,6 @@ class TileMap extends Container { this.w = mapW * tileW; this.h = mapH * tileH; - // Add all tile sprites this.children = tiles.map((frame, i) => { const s = new TileSprite(texture, tileW, tileH); s.frame = frame; diff --git a/asdf/TileMapXML.js b/asdf/TileMapXML.js index e34547d..8c19afb 100644 --- a/asdf/TileMapXML.js +++ b/asdf/TileMapXML.js @@ -1,8 +1,19 @@ import Container from "./Container.js"; import TileSpriteXML from "./TileSpriteXML.js"; +/** + * TileMapXML class + */ class TileMapXML extends Container { - constructor (tiles, mapW, mapH, texture, xml) { + /** + * Draws array of tiles from XML indexed spritesheet + * @param {number[]} tiles Array of XML indexes + * @param {*} mapW Amount of tiles over the width of the map + * @param {*} mapH Amount of tiles over the height of the map + * @param {*} texture Texture instance of source image file + * @param {*} xml SpriteSheetXML instance of source xml file + */ + constructor(tiles, mapW, mapH, texture, xml) { super(texture); this.mapW = mapW; this.mapH = mapH; @@ -10,8 +21,7 @@ class TileMapXML extends Container { this.tileH = xml.array[tiles[0]].height; this.w = mapW * this.tileW; this.h = mapH * this.tileH; - - // Add all tile sprites + this.children = tiles.map((frame, i) => { const s = new TileSpriteXML(texture, xml, frame); s.frame = frame; diff --git a/asdf/TileSprite.js b/asdf/TileSprite.js index 2fe5c8c..65a69d7 100644 --- a/asdf/TileSprite.js +++ b/asdf/TileSprite.js @@ -1,7 +1,15 @@ import Sprite from "./Sprite.js"; - +/** + * TileSprite class + */ class TileSprite extends Sprite { - constructor (texture, w, h) { + /** + * Creates sprite instance from unindexed spritesheet + * @param {*} texture Instance of Texture with source image + * @param {number} w Width of sprite on source image + * @param {number} h Height of spirte on source image + */ + constructor(texture, w, h) { super(texture); this.tileW = w; this.tileH = h; diff --git a/asdf/TileSpriteXML.js b/asdf/TileSpriteXML.js index 70d0c63..5ceb12f 100644 --- a/asdf/TileSpriteXML.js +++ b/asdf/TileSpriteXML.js @@ -1,7 +1,16 @@ import Sprite from "./Sprite.js"; +/** + * TileSpriteXML class + */ class TileSpriteXML extends Sprite { - constructor (texture, xml, index) { + /** + * Creates sprite instance from XML indexed spritesheet + * @param {*} texture Instance of Texture with source image + * @param {*} xml Instance of SpriteSheetXML with xml index + * @param {number} index Index of XML element + */ + constructor(texture, xml, index) { super(texture); var src = xml.array[index]; this.imgPos = { x: src['x'], y: src['y'] }; diff --git a/asdf/controls/KeyControls.js b/asdf/controls/KeyControls.js index 3af4798..8a4f3d6 100644 --- a/asdf/controls/KeyControls.js +++ b/asdf/controls/KeyControls.js @@ -1,9 +1,15 @@ +/** + * KeyControls class + */ class KeyControls { + /** + * Listens for keypresses and prevents default actions + */ constructor() { this.keys = {}; // Bind event handlers document.addEventListener("keydown", e => { - if ([37,38,39,40].indexOf(e.which) >= 0) { + if ([37, 38, 39, 40].indexOf(e.which) >= 0) { e.preventDefault(); } this.keys[e.which] = true; @@ -12,13 +18,23 @@ class KeyControls { this.keys[e.which] = false; }, false); } - // Handle key actions + + /** + * Returns value of action key (spacebar) + * @returns {boolean} Key value + */ get action() { // Spacebar return this.keys[32]; } - get x () { + /** + * Returns -1 on Arrow Left or A + * + * Returns 1 on Arrow Right or D + * @returns {number} Key Value + */ + get x() { // Arrow Left or A (WASD) if (this.keys[37] || this.keys[65]) { return -1; @@ -30,7 +46,13 @@ class KeyControls { return 0; } - get y () { + /** + * Returns -1 on Arrow Up or W + * + * Returns 1 on Arrow Down or S + * @returns {number} Key value + */ + get y() { // Arrow Up or W (WASD) if (this.keys[38] || this.keys[87]) { return -1; @@ -42,6 +64,12 @@ class KeyControls { return 0; } + /** + * Read or write value of any key + * @param {number} key Keycode for targetted key + * @param {*} [value] Value to set to key + * @return {*} Value of key + */ key(key, value) { if (value !== undefined) { this.keys[key] = value; @@ -49,6 +77,9 @@ class KeyControls { return this.keys[key]; } + /** + * Resets default value to all keys + */ reset() { for (let key in this.keys) { this.keys[key] = false; diff --git a/asdf/controls/MouseControls.js b/asdf/controls/MouseControls.js index 1ae81f7..52e2f75 100644 --- a/asdf/controls/MouseControls.js +++ b/asdf/controls/MouseControls.js @@ -1,17 +1,28 @@ +/** + * MouseControls class + */ class MouseControls { + /** + * Sets container element where handlers will listen + * @param {*} [container] Container element, defaults to document.body + */ constructor(container) { this.el = container || document.body; // State - this.pos = {x: 0, y: 0}; - this.isDown = false; - this.pressed = false; - this.released = false; + this.pos = { x: 0, y: 0 }; + this.isDown = false; + this.pressed = false; + this.released = false; // Handlers document.addEventListener('mousemove', this.move.bind(this), false); document.addEventListener('mousedown', this.down.bind(this), false); document.addEventListener('mouseup', this.up.bind(this), false); } + /** + * Recalculates mouse position based on the position of the container + * @param {{ clientX: number, clientY: number}} param0 Native mouse event x and y values + */ mousePosFromEvent({ clientX, clientY }) { const { el, pos } = this; const rect = el.getBoundingClientRect(); @@ -21,26 +32,40 @@ class MouseControls { pos.y = (clientY - rect.top) * yr; } + /** + * Calls mousePosFromEvent() on mouse move + * @param {*} e Event + */ move(e) { this.mousePosFromEvent(e); } + /** + * Handles mouseDown event and calls mousePosFromEvent() to determine the exact pixel + * @param {*} e Event + */ down(e) { this.isDown = true; this.pressed = true; this.mousePosFromEvent(e); } + /** + * Handles mouseUp event and calls mousePosFromEvent() to determine the exact pixel + * @param {*} e Event + */ up() { this.isDown = false; this.released = true; } + /** + * Resets pressed and released values to make sure they are only true on a press or release + */ update() { this.released = false; this.pressed = false; } - - } + export default MouseControls; diff --git a/asdf/index.html b/asdf/index.html index 5488a39..ade551b 100644 --- a/asdf/index.html +++ b/asdf/index.html @@ -1,13 +1,16 @@ + ASDF Framework +

ASDF JS Framework

Nothing to browse here, just some shared files to make these games work.

+ \ No newline at end of file diff --git a/asdf/renderer/CanvasRenderer.js b/asdf/renderer/CanvasRenderer.js index 1e81cb1..ed64bb5 100644 --- a/asdf/renderer/CanvasRenderer.js +++ b/asdf/renderer/CanvasRenderer.js @@ -1,4 +1,12 @@ +/** + * CanvasRenderer class + */ class CanvasRenderer { + /** + * Renderer for CanvasJS, defines width and height for the canvas element + * @param {*} w Width for canvas element + * @param {*} h Height for canvas element + */ constructor(w, h) { const canvas = document.createElement("canvas"); this.w = canvas.width = w; @@ -8,7 +16,10 @@ class CanvasRenderer { this.ctx.textBaseLine = "top"; } - setPixelated(){ + /** + * Turns off image smoothening on the canvas element + */ + setPixelated() { this.ctx['imageSmoothingEnabled'] = false; /* standard */ this.ctx['mozImageSmoothingEnabled'] = false; /* Firefox */ this.ctx['oImageSmoothingEnabled'] = false; /* Opera */ @@ -16,6 +27,12 @@ class CanvasRenderer { this.ctx['msImageSmoothingEnabled'] = false; /* IE */ } + + /** + * Render all children on the canvas element + * @param {*} container Containing element of the canvas element + * @param {boolean} [clear=true] Defines if the canvas element needs to be cleared for the next render + */ render(container, clear = true) { const { ctx } = this; function renderRec(container) { @@ -27,7 +44,6 @@ class CanvasRenderer { ctx.save(); - // Draw the leaf node if (child.pos) { ctx.translate(Math.round(child.pos.x), Math.round(child.pos.y)); } @@ -53,7 +69,7 @@ class CanvasRenderer { if (font) ctx.font = font; if (fill) ctx.fillStyle = fill; if (align) ctx.textAlign = align; - ctx.fillText(child.text, 0,0); + ctx.fillText(child.text, 0, 0); } else if (child.texture) { @@ -64,7 +80,7 @@ class CanvasRenderer { child.frame.x * child.tileW, child.frame.y * child.tileH, child.tileW, child.tileH, - 0,0, + 0, 0, child.tileW, child.tileH ); } else if (child.imgPos && child.width && child.height) { @@ -73,7 +89,7 @@ class CanvasRenderer { child.imgPos.x, child.imgPos.y, child.width, child.height, - 0,0, + 0, 0, child.width, child.height ); } else { @@ -81,7 +97,7 @@ class CanvasRenderer { } } - // Handle the child types + // Handle children with children if (child.children) { renderRec(child); } @@ -89,7 +105,7 @@ class CanvasRenderer { }) } if (clear) { - ctx.clearRect(0,0,this.w,this.h); + ctx.clearRect(0, 0, this.w, this.h); } renderRec(container); } diff --git a/asdf/utilities/math.js b/asdf/utilities/math.js index a834db6..199fa70 100644 --- a/asdf/utilities/math.js +++ b/asdf/utilities/math.js @@ -1,26 +1,48 @@ +/** + * Returns random integer between min and max + * @param {number} min Minimum value + * @param {number} max Maximum value + * @returns {number} Random integer + */ function rand(min, max) { - return Math.floor(randf(min, max)); + return Math.floor(randf(min, max)); +} + +/** + * Returns random float between min and max + * @param {number} min Minimum value + * @param {number} max Maximum value + * @returns {number} Random value + */ +function randf(min, max) { + if (max == null) { + max = min || 1; + min = 0; } - - function randf(min, max) { - if (max == null) { - max = min || 1; - min = 0; - } - return Math.random() * (max - min) + min; - } - - function randOneFrom(items) { - return items[rand(items.length)]; - } - - function randOneIn(max = 2) { - return rand(0, max) === 0; - } - - export default { - rand, - randf, - randOneFrom, - randOneIn - }; \ No newline at end of file + return Math.random() * (max - min) + min; +} + +/** + * Returns random item from items array + * @param {*[]} items Array of anything + * @returns {any} Item from items array + */ +function randOneFrom(items) { + return items[rand(items.length)]; +} + +/** + * Returns true one out of max times + * @param {number} [max=2] Maximum value + * @returns {boolean} Outcome + */ +function randOneIn(max = 2) { + return rand(0, max) === 0; +} + +export default { + rand, + randf, + randOneFrom, + randOneIn +}; \ No newline at end of file diff --git a/examples/shooter/src/main.js b/examples/shooter/src/main.js index f4515bc..1f75955 100644 --- a/examples/shooter/src/main.js +++ b/examples/shooter/src/main.js @@ -2,115 +2,115 @@ import asdf from "../../../asdf/index.js"; const { Container, CanvasRenderer, KeyControls, MouseControls, Text, Texture, Sprite } = asdf; // Board Setup - const w = 640; - const h = 300; - const renderer = new CanvasRenderer(w, h); - document.querySelector("#board").appendChild(renderer.view); +const w = 640; +const h = 300; +const renderer = new CanvasRenderer(w, h); +document.querySelector("#board").appendChild(renderer.view); // Setup game variables - let dt = 0; - let last = 0; - let lastShot = 0; - let lastSpawn = 0; - let spawnSpeed = 1.0; - let scoreAmount = 0; - let gameOver = false; +let dt = 0; +let last = 0; +let lastShot = 0; +let lastSpawn = 0; +let spawnSpeed = 1.0; +let scoreAmount = 0; +let gameOver = false; // Setup game objects - const scene = new Container(); +const scene = new Container(); // Load game textures - const textures = { - background: new Texture("./res/images/bg.png"), - spaceship: new Texture("./res/images/spaceship.png"), - bullet: new Texture("./res/images/bullet.png"), - baddie: new Texture("./res/images/baddie.png") - } +const textures = { + background: new Texture("./res/images/bg.png"), + spaceship: new Texture("./res/images/spaceship.png"), + bullet: new Texture("./res/images/bullet.png"), + baddie: new Texture("./res/images/baddie.png") +} // Spaceship - const controls = new KeyControls(); - const ship = new Sprite(textures.spaceship); - ship.pos.x = 120; - ship.pos.y = h / 2 - 16; - ship.update = function(dt, t) { - const { pos } = this; - pos.x += controls.x * dt * 300; - pos.y += controls.y * dt * 300; +const controls = new KeyControls(); +const ship = new Sprite(textures.spaceship); +ship.pos.x = 120; +ship.pos.y = h / 2 - 16; +ship.update = function (dt, t) { + const { pos } = this; + pos.x += controls.x * dt * 300; + pos.y += controls.y * dt * 300; - if (pos.x < 0) pos.x = 0; - if (pos.x > w - 32) pos.x = w - 32; - if (pos.y < 0) pos.y = 0; - if (pos.y > h - 32) pos.y = h - 32; - } + if (pos.x < 0) pos.x = 0; + if (pos.x > w - 32) pos.x = w - 32; + if (pos.y < 0) pos.y = 0; + if (pos.y > h - 32) pos.y = h - 32; +} // Bullets - const bullets = new Container(); - function fireBullet(x, y) { - const bullet = new Sprite(textures.bullet); - bullet.pos.x = x; - bullet.pos.y = y; - bullet.update = function(dt, t) { - bullet.pos.x += 400 * dt; - } - bullets.add(bullet); - } +const bullets = new Container(); +function fireBullet(x, y) { + const bullet = new Sprite(textures.bullet); + bullet.pos.x = x; + bullet.pos.y = y; + bullet.update = function (dt, t) { + bullet.pos.x += 400 * dt; + } + bullets.add(bullet); +} // Bad guys - const baddies = new Container(); - function spawnBaddie(x, y, speed) { - const baddie = new Sprite(textures.baddie); - baddie.pos.x = x; - baddie.pos.y = y; - baddie.update = function(dt) { - this.pos.x += speed * dt; - this.pos.y += Math.sin(this.pos.x / 15) * 1; - }; - baddies.add(baddie); - } +const baddies = new Container(); +function spawnBaddie(x, y, speed) { + const baddie = new Sprite(textures.baddie); + baddie.pos.x = x; + baddie.pos.y = y; + baddie.update = function (dt) { + this.pos.x += speed * dt; + this.pos.y += Math.sin(this.pos.x / 15) * 1; + }; + baddies.add(baddie); +} // Show score - const score = new Text(`${scoreAmount}`, { - font: "15pt Visitor", - fill: "#000000", - align: "left" - }); - score.pos.x = 50; - score.pos.y = 15; - score.update = function() { - if (gameOver) { - score.pos.x = w / 2; - score.pos.y = (h / 3) * 2; - score.text = `Score: ` + `${scoreAmount}`; - score.style.align = "center"; - score.style.font = "24pt Visitor" - } else { - score.text = `${scoreAmount}`; - } +const score = new Text(`${scoreAmount}`, { + font: "15pt Visitor", + fill: "#000000", + align: "left" +}); +score.pos.x = 50; +score.pos.y = 15; +score.update = function () { + if (gameOver) { + score.pos.x = w / 2; + score.pos.y = (h / 3) * 2; + score.text = `Score: ` + `${scoreAmount}`; + score.style.align = "center"; + score.style.font = "24pt Visitor" + } else { + score.text = `${scoreAmount}`; } +} // Gameover - function doGameOver() { - const gameOverMessage = new Text(`Game Over`, { - font: "45pt Visitor", - fill: "#000000", - align: "center" - }); - gameOverMessage.pos.x = w / 2; - gameOverMessage.pos.y = h / 3; +function doGameOver() { + const gameOverMessage = new Text(`Game Over`, { + font: "45pt Visitor", + fill: "#000000", + align: "center" + }); + gameOverMessage.pos.x = w / 2; + gameOverMessage.pos.y = h / 3; - scene.add(gameOverMessage); - scene.remove(ship); - scene.remove(baddies); - scene.remove(bullets); - gameOver = true; - } + scene.add(gameOverMessage); + scene.remove(ship); + scene.remove(baddies); + scene.remove(bullets); + gameOver = true; +} // Add game objects - scene.add(new Sprite(textures.background)); - scene.add(ship); - scene.add(bullets); - scene.add(baddies); - scene.add(score); +scene.add(new Sprite(textures.background)); +scene.add(ship); +scene.add(bullets); +scene.add(baddies); +scene.add(score); // Looping Code