Machine Gun Breaker (MGB) is all about firing a stream of Sprites quickly.
Code
// MC Library // Machine Gun Breaker // Jan 2019 // Purely an Alpha window.onload = (function() { // AQUIRE HTML DOM CANVAS REFERENCE // HTML has to have a HTML5 canvas element tagged as "canvas" var c=document.getElementById("canvas"); ////////////////////////// // INITIALISE MC Engine // ////////////////////////// // CHOOSE 1 // // CHOICE 1: Canvas Size fixed by HTML MC.init(c); // CHOICE 2: Full Window Option (Canvas will be resized to fit window) //MC.init(c,"fullWindow"); /////////////////////////// // END OF Initialisation // /////////////////////////// //----------------------------------------------------------------------------- ////////////////////////// // VARIABLE DECLARATION // ////////////////////////// // screenset up .. 900x700 canvas MC.canvas.setSize(900,700); MC.game.setBounds(0,1,1/9,8/9); // 100px side borders MC.game.physicsUpdates = 10; // container for game variables var MGB = { // N.B. placeholder values, overwritten by reset() method (below) state: 0, // 0 = aiming, 1=firing, 2 = resolving, 3 = gameover (success) awaiting reset, 4 = gameover (failure) awaiting reset volleys: 5, o: new MC.Point(450,700), // Origin .. aka middle bottom of screen fp: new MC.Point(0,0), // Firing Point fv: new MC.Point(), // Firing Velocity firing: false, // are we firing? cooldown: 0.05, timer: 0, maxShots: 50, currShots: 0, typeFTitle: new MC.Typeface("Impact",21,"Black"), typeF: new MC.Typeface("Impact",21,"White"), typeF2: new MC.Typeface("Impact",40,"White"), typeF3: new MC.Typeface("Impact",40,"Red") }; // Sprite Bins var shots = new MC.SpriteBin(); var targets = new MC.SpriteBin(); var GUI = new MC.GUI(); var walls = new MC.SpriteBin(); var player = new MC.SpriteBin(); // set up a boundary of top, left and right hand walls. Invisible sprites, should not even have to update or render them. // But they will be there for "bounce" purposes var w1 = new MC.Sprite({type: "rect", size1: 100, size2: 700, pos: new MC.Point(50,350), fillColor: "Red", edgeColor: null, mobile: false}); var w2 = w1.clone(); w2.moveTo(new MC.Point(850,350)); var w3 = w1.clone(); w3.setValues({size1: 800, size2: 100, pos: new MC.Point(450,-50)}); walls.push(w1,w2,w3); // Player "Cannon" ... composite of a barrel and a few circles .. held and rendered in player SpriteBin var barrel = new MC.Sprite({type: "rect", size1: 100, size2: 10, pos: MGB.o.clone(), fillColor: "Red", edgeColor: null }); barrel.dev.pointer = new MC.Point(55,0); barrel.dev.update = function () { if (MC.game.mouse.y < barrel.pos.y && MGB.state == 0) { var p = new MC.Point(1,0); var t = MC.game.mouse.clone(); t.minus(barrel.pos); barrel.angle = p.angleTo(t); barrel.angle = MC.maths.clamp(barrel.angle,200,340); } }; var back = new MC.Sprite({ type: "circ", size1: 35, pos: barrel.pos.clone(), fillColor: "Ivory", edgeColor: null }); var middle = back.clone(); middle.scale = 0.85; var front = back.clone(); front.setValues({scale: 0.8, fillColor: "SlateGray"}); // Firing Point var fp = new MC.Sprite({type: "circ", size1: 2, fillColor: "White", edgeColor: null, pos: MGB.o.clone() }); fp.dev.update = function () { var off = new MC.Point(55,0); off.rotate(barrel.angle); fp.pos = MGB.o.clone().add(off); }; player.push(back,barrel,middle,front,fp); /////////// /// INITIAL RESET to start if all off ////////// reset(); function getTarget(value) { var coord = new MC.Point(MC.maths.randBetween(150,750),MC.maths.randBetween(50,550)); var t = new MC.Sprite({ type: "circ", size1: 30, fillColor: null, edgeWidth: 1, edgeColor: null, pos: coord.clone() }); t.dev.lives = 30; t.dev.onHit = function () { t.dev.lives--; setGUI(); if (t.dev.lives <= 0) t.kill(); }; var c = new MC.ConBox({type: "c", width: 60, text: "TBA",pos: coord.clone(), edgeWidth: 0, colorBody: "Red" }); switch (value) { case 1: // default break; case 2: t.setValues({size1: 20}); c. width = 40; break; case 3: t.setValues({type: "rect", size1: 60, size2: 60}); c.setValues({type: "r", width: 60, height: 60}); break; case 4: t.setValues({type: "rect", size1: 80, size2: 40}); c.setValues({type: "r", width: 80, height: 40}); break; default: break; } targets.push(t); GUI.push(c); } function reset() { MGB.state = 0; MGB.volleys = 4; MGB.currShots = 0; MGB.firing = false; MGB.maxShots = 50; shots.empty(); targets.empty(); GUI.empty(); for (var i = 0; i < 10; i++) getTarget(MC.maths.randBetween(1,4)); setGUI(); } function shot() { var s = new MC.Sprite({ type: "circ", size1: 5, fillColor: "White", edgeColor: null, edgeBounce: false, pos: fp.pos.clone(), vel: fp.pos.clone().minus(MGB.o).scale(13), }); s.dev.update = function() { s.bounce(walls); if (s.pos.y > MGB.o.y - 10) { s.kill(); }; }; s.kill(10); return s; } MC.keys.p.onUp = function () { if (MC.game.paused) MC.game.paused = false; else { MC.game.paused = true; console.log(shots); console.log(targets); console.log(GUI); } }; MC.mouse.onClickL = function () { if (MGB.state == 0 || MGB.state == 3 || MGB.state == 4) { changeState(); } } function setGUI() { var t = targets.bin.length; var g = GUI.bin.length; for (var i = 0; i < t; i++) { for (var j = 0; j < g; j++) { var T = targets.bin[i]; var G = GUI.bin[j]; if (T.pos.equals(G.pos)) { G.text = T.dev.lives; if (T.dev.lives <= 0) { G.visible = false; } G.colorBody = MC.utils.mix(new MC.Color(0,255,0,1), new MC.Color(255,0,0,1),T.dev.lives /30); } } } } function handleShots() { var s = shots.bin.length; var t = targets.bin.length; for (var i = 0; i < s; i++) { for (var j = 0; j < t; j++) { var S = shots.bin[i]; var T = targets.bin[j]; if (S instanceof MC.Sprite && T instanceof MC.Sprite && S.hit(T)) { S.bounce(T); T.dev.onHit(); } } } } function changeState(result) { switch (MGB.state) { // 0 = aiming, 1=firing, 2 = resolving, 3 = gameover (success) awaiting reset, 4 = gameover (failure) awaiting reset case 0: // was aiming ... move to firing MGB.firing = true; MGB.state = 1; break; case 1: // am firing move to resolving MGB.firing = false; MGB.volleys --; MGB.state = 2; break; case 2: if (result) { // have won MGB.state = 3; } else { if (MGB.volleys > 0) { MGB.state = 0; MGB.currShots = 0; } else { MGB.state = 4; } } break; case 3: reset(); break; case 4: reset(); break; default: break; } } function checkState() { if (MGB.state == 2 && shots.bin.length == 0) {// move to scoring if (targets.bin.length == 0) { // have Won changeState(true); } else { changeState(false); } } } ////////////////////////// // END OF declarations // ////////////////////////// //----------------------------------------------------------------------------- ////////////////////////// // GAME LOOP // ////////////////////////// // first call, request subsequently made within gameLoop(); function gameLoop() { requestAnimationFrame(gameLoop); // Essential to update MC.game to ensure MC.game.deltaTime is available MC.game.update(); // physicsUpdate Loop for (var z = 0; z < MC.game.physicsUpdates; z++) { if (MGB.firing && MGB.currShots != MGB.maxShots) { MGB.timer = MC.maths.maxOf(0, MGB.timer - (MC.game.deltaTime / MC.game.physicsUpdates)); if (MGB.timer == 0) { shots.push(shot()); MGB.timer = MGB.cooldown; MGB.currShots++; if (MGB.currShots >= MGB.maxShots) { changeState(); } } } player.update(); shots.update(); targets.update(); handleShots(); checkState(); } walls.update(); // Add Render / Draw Code here MC.draw.clearInBounds("Black"); player.render(); if (MGB.state == 0) { var end = new MC.Point(900,0); end.rotate(barrel.angle).add(MGB.o); MC.draw.line(fp.pos,end,new MC.Color(200,200,200,0.5),4); } shots.render(); targets.render(); GUI.render(); MC.draw.clearOutBounds("Olive"); // Perimeter to "Olive" drawText(); //MC.game.fpsLog(); //MC.game.mouseLog(); } function drawText() { MC.draw.text("Machine", new MC.Point(12, 35),MGB.typeFTitle); MC.draw.text("Gun", new MC.Point(28, 60),MGB.typeFTitle); MC.draw.text("Breaker", new MC.Point(13, 85),MGB.typeFTitle); MC.draw.text("Volleys", new MC.Point(14, 150), MGB.typeF); MC.draw.text("Remaining", new MC.Point(3, 180), MGB.typeF); MC.draw.text(MGB.volleys, new MC.Point(36, 240), MGB.typeF2); var text1 = ""; var text2 = ""; switch (MGB.state) { case 0: // aiming text1 = "AIMING"; text2 = "Point & Click"; break; case 1: text1 = "FIRING"; break; case 2: text1 = "RESOLVING"; break; case 3: text1 = "SUCCESS!"; text2 = "Click to Reset"; break; case 4: text1 = "GAME OVER"; text2 = "Click to Reset"; break; default: break; } MC.draw.text(text1, new MC.Point(110, 690), MGB.typeF3); MC.draw.text(text2, new MC.Point(550, 690), MGB.typeF3); }; }); // EoF