Gravity

Gravity is a simple physics simulation. “Motes” of matter are spawned and given a random position, velocity & radius (analogous with mass). Each frame they all establish their acceleration due gravity, and off they go! An collisions see the larger mote absorb the mass (and momentum) of the smaller.

Code

// MC Library
// Gravity Demo (Feb 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
MC.init(c);
MC.game.setBounds(0, 1, 0.2, 1);


// CHOICE 2: Full Window Option (Canvas will be resized to fit window)
//MC.init(c,"fullWindow");

///////////////////////////
// END OF Initialisation //
///////////////////////////
//-----------------------------------------------------------------------------
//////////////////////////
// VARIABLE DECLARATION //
//////////////////////////
    var G = 2000;
    var mid = new MC.Point(MC.canvas.midX, MC.canvas.midY);
    var spd = 120;
    var reporting = false;
    var MoteBin = new MC.SpriteBin();
    var FlashBin = new MC.SpriteBin();
    var GUI = new MC.GUI();
    var Counter = new MC.ConBox({ // used as a text box ... not updated, no onClick() defined (or checked)
        type: "c", width: 80, pos: new MC.Point(80,400), text: "tba"
    });
    var Pause = new MC.ConBox({
        width: 150, height: 40, text: "Pause",
        onClick: function () { MC.game.paused = !MC.game.paused; }
    });
    var Report = new MC.ConBox({
        width: 150, height: 40, text: "Report",
        onClick: function () { reporting = !reporting; }
    });
    var ReStart = new MC.ConBox({
        width: 150, height: 40, text: "Re-Start",
        onClick: function () { Start(); }
    });
    GUI.push(Pause, Report, ReStart);
    MC.mouse.onClickL = function () { // configure MC.game so it checks the GUI upon Left click down 
        GUI.click();
    };

    // Sprite Factory to generate the "motes" of matter
    // as an approximation, mass is considered as Radius^2
function getMote() {
    var mote = new MC.Sprite({
        type: "circ",
        size1: MC.maths.randBetween(2, 5),
        pos: new MC.Point(MC.maths.randBetween(0, MC.canvas.right), MC.maths.randBetween(0, MC.canvas.bottom)),
        vel: new MC.Point(MC.maths.randBetween(-spd, spd), MC.maths.randBetween(-spd, spd)),
        wrapAround: true,
        fillColor: MC.utils.randomColor(),
        edgeColor: null
    });
    mote.dev.update = function () { // N.B. called automatically after sprite.update()
        mote.acc.set(0, 0);
        for (var i = 0; i < MoteBin.bin.length; i++) {
            var t = MoteBin.bin[i];
            if (t != null && !t.dead && t != mote) {
                var p1 = mote.pos.clone();
                var t1 = t.pos.clone();
                var rng = MC.maths.range(p1, t1);
                var myMass = mote.size1 * mote.size1;
                var TMass = t.size1 * t.size1;
                if (rng < t.size1 + mote.size1 && mote.size1 >= t.size1) { // have collision of bigger object, absord the smaller
                    // momentum calculation for new velocity
                    var m1 = mote.vel.clone().times(myMass);
                    var m2 = t.vel.clone().times(TMass);
                    mote.vel = m1.add(m2).div(myMass + TMass);
                    // Apply Mass change
                    mote.setValues({ size1: Math.sqrt(myMass + TMass) });
                    // flash
                    var flashPos = t1.clone().minus(p1).normalise().times(mote.size1).add(p1);
                    flash(flashPos, t.size1/2);
                    t.kill();
                }
                else {
                    // establish new acceleration
                    var mod = t1.clone().minus(p1);
                    mod.setLength((G * (myMass * TMass)) / (Math.pow(Math.max(rng, mote.size1), 2) * myMass));
                    mote.acc.add(mod);
                }
            }
        }
    }
    return mote;
}
// Sprite Factory, produces a "flash" and adds it to the FlashBin
function flash(position, size) {
    var boom = new MC.Sprite({ type: "circ", size1: size, fillColor: "White", edgeColor: null, edgeWidth: null, pos: position });
    boom.kill(0.05);
    FlashBin.push(boom);
}


function Start() {
    FlashBin.empty();
    MoteBin.empty();
    for (var i = 0; i < 150; i++) {
        MoteBin.push(getMote());
    }
}

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

Start();
// 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();    
    // Add game logic here
    update();
    render();
}

function update() {
    MoteBin.update();
    FlashBin.update();
    GUI.update();
    Counter.setValues({ text: "x" + MoteBin.bin.length });
}

function render() {
    MC.draw.clearInBounds("Black");
    MoteBin.render();
    FlashBin.render();

    if (reporting) {
        for (var i = 0; i < MoteBin.bin.length; i++) {
            var s0 = MoteBin.bin[i], p0 = MoteBin.bin[i].pos.clone(), e0 = p0.clone().add(MoteBin.bin[i].acc);
            MC.draw.line(p0, e0, "Red", 1);
        }
    }

    MC.draw.clearOutBounds("Olive");
    GUI.render(8, 75);
    Counter.render();
    MC.game.fpsLog();
}

}); // EoF