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