Fireworks

Fireworks stress tests the MC Engine by throwing 1,000’s of Sprite objects about on the screen.

LEFT CLICK TO FIRE.

Code

// MaxCanvas Demo - Fireworks
// by Max Bryans (March 2019)
// 
//
// Required to drag MC.js into Intellisense
// thanks to https://visualstudiomagazine.com/blogs/tool-tracker/2018/07/use-javascript-code.aspx (Dec 2018)
/// <reference path="MC.js" />

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 (in this case 900 x 700)
    MC.init(c);
    MC.game.setBounds(0, 1, 0, 1);
    
    
    // CHOICE 2: Full Window Option (Canvas will be resized to fit window)
    //MC.init(c,"fullWindow");

///////////////////////////
// END OF Initialisation //
///////////////////////////
//-----------------------------------------------------------------------------
//////////////////////////
// VARIABLE DECLARATION //
//////////////////////////

    // This simulation uses gravity
    MC.game.gravity.set(0,15);
    
    // Container for Game Objects
    var FW = {
        spd: 50,
        Particles: new MC.SpriteBin(),
        Trails: new MC.SpriteBin(),
        // 2 x ConBoxes as text holders.  1 for Reporting Sprite Count and fps, 2nd for Instructions
        Report: new MC.ConBox(),
        Instructions: new MC.ConBox()
    };
    // Configure Text boxes
    FW.Report.pos.set(FW.Report.width/2,MC.canvas.bottom - FW.Report.height/2);
    FW.Instructions.pos.set(MC.canvas.right - FW.Report.width/2,MC.canvas.bottom - FW.Report.height/2);
    FW.Instructions.text = "Click for Fireworks !!";
    
    // Player Input Event Handler ... Left Mouse click = make a firework
    MC.mouse.onClickL = function () { MakeBurst(); }

    // Control Function, sets parameters for a series of "Sparks" which make up an explosion
    function MakeBurst() {
        var angle = 0;
        var InitialVel = new MC.Point(FW.spd * MC.maths.randBetween(0.9,1.1),0);
        var pos = MC.mouse.pos.clone();
        var color = MC.utils.randomColor();
        var rocketVel = new MC.Point(0,-FW.spd * 1.2);        
        rocketVel.rotate(MC.maths.randBetween(-30,30));  // a small random direction to add a bit of variety
        while (angle < 360) {
            // Set off a series of Sparks, with velocities radiating around (and away from) the burst centre
            MakeSpark(pos,InitialVel.clone().rotate(angle).add(rocketVel), color);
            angle += MC.maths.randBetween(5,25);        // between 15-72 Sparks per Burst
        };
    }
    
    // Sprite Factory
    function MakeSpark(position, velocity, color) {
        // Make standard Sprite object
        var s = new MC.Sprite({
            type: "circ",
            size1: 3,
            pos: position.clone(),
            vel: velocity.clone(),
            wrapAround: false,
            fillColor: color.clone(),
            edgeColor: null,
            gravity: true,
            killAge: 4
        });
        
        // Customise behaviour via the Sprite.dev object
        s.dev.oldPos = s.pos.clone();
        s.dev.update = function () {
            // Only need to add a "Trail" if the distance from the last one is > 1 pixel
            if (MC.maths.rangeSqr(s.pos,s.dev.oldPos) > 1) {
                MakeTrail(s.pos,s.fillColor);
                s.dev.oldPos = s.pos.clone();
            }
            s.vel.times(0.995);     // retard velocity slightly each frame to emulate drag
        };
        
        // Store in SpriteBin
        FW.Particles.push(s);       // Spritebin will handle all hereafter
    }
    
    // Sprite Factory.  Trails do not move, but shrink over a 1 second lifespan, creating the illusion of movement
    function MakeTrail(position, color) {
        var t = new MC.Sprite({
            type: "circ",
            size1: 3,
            pos: new MC.Point(position),
            vel: new MC.Point(0,0),
            wrapAround: false,
            fillColor: color,
            edgeColor: null,
            gravity: false
        });
        
        t.dev.TrailTimer = 1;
        t.dev.update = function () {
            t.dev.TrailTimer -= MC.game.deltaTime;
            if (t.dev.TrailTimer < 0) {
                    t.kill();
                }
            else {
                    t.scale = t.dev.TrailTimer;
                }
            };
        FW.Trails.push(t);
    }

//////////////////////////
// END OF declarations  //
//////////////////////////
//-----------------------------------------------------------------------------
//////////////////////////
//      GAME LOOP       //
//////////////////////////


    // first call, request subsequently made within itself
    gameLoop();
    
    function gameLoop() {
        requestAnimationFrame(gameLoop);
        // Essential to update MC.game to ensure MC.game.deltaTime is available
        MC.game.update();
        update();
        render();
    }
    
    function update() {
        FW.Particles.update();
        FW.Trails.update();
        // Populate Report Text
        FW.Report.text = (~~((FW.Particles.bin.length + FW.Trails.bin.length)/100)*100) + "+ Sprites @ " + (~~(1/MC.game.deltaTime/10)*10) + " fps";
    }
    
    function render() {
        MC.draw.clearInBounds("Black");
        FW.Trails.render();
        FW.Particles.render();
        FW.Report.render();
        FW.Instructions.render();
    }
    
}); // EoF