Code viewer for World: Cloned Bouncy Balls

```
// Cloned by Ian Gilligan on 21 Jan 2019 from World "Bouncy Balls" by Starter user
// Please leave this clone trail here.

// ==== Starter World ===============================================================================================
// This code is only for use on the Ancient Brain site.
// This code may be freely copied and edited by anyone on the Ancient Brain site.
// This code may not be copied, re-published or used on any other website.
// To include a run of this code on another website, see the "Embed code" links provided on the Ancient Brain site.
// ==================================================================================================================

// Physijs based World
// Bouncy balls

// ===================================================================================================================
// === Start of tweaker's box ========================================================================================
// ===================================================================================================================

// The easiest things to modify are in this box.
// You should be able to change things in this box without being a JavaScript programmer.
// Go ahead and change some of these. What's the worst that could happen?

AB.screenshotStep  = 200;

// Take screenshot on this step. (All resources should have finished loading.) Default 50.

const BALLSIZE = 5 ;

const BALLPOS       = BALLSIZE * 2 ; 		// x,z start position of balls is random in this interval
const BALLHEIGHT    = BALLSIZE * 10 ; 		// y start position of balls

const HEAVYIMPACT   = 60 ;     				// heavy impacts have y velocity greater than this

const GROUNDSIZE    = BALLSIZE * 50;

const startRadius   = BALLSIZE * 30 ;
const maxRadius     = BALLSIZE * 250 ;

//	const TEXTURE_GROUND 	= '/uploads/starter/rocks.jpg' ;

// credit:
// http://soundbible.com/1343-Jump.html

const SKYCOLOR  = 0xffffcc;

// friction and restitution between 0 and 1:

const GROUND_FRICTION 		= 0.1;
const GROUND_RESTITUTION 	= 0.95;

const BALL_FRICTION 		= 0.1;
const BALL_RESTITUTION 		= 0.95;

// define gravity along x,y,z dimensions:
var gravity = new THREE.Vector3 ( 0, -50, 0 );

const BALL_MASS = 1;

function nextballin()
// Speed of ball creation
// Create next ball in some random (m to n) milliseconds time.
{
//	return ( AB.randomIntAtoB ( 2000, 4000 ) );
return ( AB.randomIntAtoB ( 20, 100 ) );
}

const FILE_ARRAY = [
];

ABWorld.drawCameraControls = false;

// ===================================================================================================================
// === End of tweaker's box ==========================================================================================
// ===================================================================================================================

// You will need to be some sort of JavaScript programmer to change things below the tweaker's box.

var splashClicked = false;

function World()
{

var textureArray = new Array ( FILE_ARRAY.length );
var ground_texture ;

var self = this;

function loadResources()		// asynchronous file loads - call initScene() when all finished
{

{
thetexture.minFilter  = THREE.LinearFilter;
ground_texture = thetexture;
if ( asynchFinished() ) initScene();
});

for ( var i = 0; i < FILE_ARRAY.length; i++ )
}

function startFileLoad ( n )				// asynchronous file load of texture n
{

{
thetexture.minFilter  = THREE.LinearFilter;
textureArray[n] = thetexture;
if ( asynchFinished() ) initScene();
});
}

function asynchFinished()		// all file loads returned
{
if ( ! ground_texture ) return false;

for ( var i = 0; i < FILE_ARRAY.length; i++ )
if ( ! textureArray[i] )
return false;

return true;
}

function initScene()			// all file loads have returned
{
// --- Light ------------------------------------------------------------------

var light = new THREE.DirectionalLight( 0xFFFFFF, 1.3 );

// close to origin, high up, works best for shadows
light.position.set ( BALLHEIGHT, (BALLHEIGHT * 2), 0 );
light.target.position.copy( ABWorld.scene.position );

// how far away to draw shadows:

// higher quality shadows at expense of computation time:

// --- Ground ------------------------------------------------------------------

var ground_material = Physijs.createMaterial(
// new THREE.MeshBasicMaterial( { color: 0xdddddd  } ),
new THREE.MeshLambertMaterial({  map: ground_texture }),
GROUND_FRICTION,
GROUND_RESTITUTION );

ground_material.map.wrapS = THREE.RepeatWrapping;
ground_material.map.wrapT = THREE.RepeatWrapping;
ground_material.map.repeat.set( 3, 3 );

// ground as plane allows bounces but seems to be infinite plane

var ground = new Physijs.PlaneMesh (
new THREE.PlaneGeometry ( GROUNDSIZE, GROUNDSIZE ),
ground_material );

ground.rotation.x = (Math.PI / 2) * 3;

/*
// ground as box is finite (beyond it, things fall into the void)
// but it does not seem to allow bounces

ground = new Physijs.BoxMesh (
new THREE.BoxGeometry ( GROUNDSIZE, 1, GROUNDSIZE ),
ground_material,
0 );
*/

// start infinite loop - but maybe neutralised until splashClicked is true

ABWorld.render();

createBall();

}

function createBall()
{

if ( resourcesLoaded && splashClicked )
{

// (subtle bug) apparently each ball needs its own material if it is to bounce separately

var ball_material =    Physijs.createMaterial (
new THREE.MeshLambertMaterial({ map:  AB.randomElementOfArray(textureArray)  }),
BALL_FRICTION,
BALL_RESTITUTION );

var ball_geometry = new THREE.SphereGeometry ( BALLSIZE, 20, 20 );

var ball = new Physijs.SphereMesh ( ball_geometry, ball_material, BALL_MASS );

ball.collisions = 0;

ball.position.set(  AB.randomIntAtoB(-BALLPOS,BALLPOS),		BALLHEIGHT,		AB.randomIntAtoB(-BALLPOS,BALLPOS)	);

ball.addEventListener( 'collision', function( other_object, relative_velocity, relative_rotation, contact_normal )
{
var mainImpact = relative_velocity.y;		// impact in direction of gravity
//	console.log ( Math.abs(mainImpact) );
if (  Math.abs(mainImpact) > HEAVYIMPACT )			// main impact, not lesser ones as it settles
{
soundCollision();
}
});

}

// --- call createBall again -----------------------------------------------------

setTimeout ( createBall, nextballin()  );		 		// set to create next ball in some random time
}

// --- public interface ----------------------------------------------------------------------

this.newRun = function()
{
ABWorld.init3d ( startRadius, maxRadius, SKYCOLOR  ); 			// sets up renderer, scene, camera

// scene is Physijs.Scene, not THREE.Scene
// can adjust scene - change gravity from default:

ABWorld.scene.setGravity ( gravity );

// calls initScene() when all done

ABWorld.lookat.copy ( ABWorld.scene.position );

ABWorld.scene.simulate();		// Physics simulate - runs on independent timer to AB nextStep

};

this.nextStep = function()		// not used
{
};

}

// --- audio --------------------------------------------------------------

// This is a more sophisticated solution to get audio autoplay on mobile/Chrome.
// Splash screen forces user interaction, which marks *multiple* audio global variables as "approved" for later play.
// Then when we need to play audio, find one of these global variables that is not already playing, and play it.

// global variable audio objects:

var theaudio1 = new Audio( SOUND_COLLISION );
var theaudio2 = new Audio( SOUND_COLLISION );
var theaudio3 = new Audio( SOUND_COLLISION );
var theaudio4 = new Audio( SOUND_COLLISION );
var theaudio5 = new Audio( SOUND_COLLISION );

function soundCollision()		// triggered by JS, not by user interaction
{

if ( ! AB.audioIsPlaying ( theaudio1 ) ) 	{ theaudio1.play(); return; }
if ( ! AB.audioIsPlaying ( theaudio2 ) ) 	{ theaudio2.play(); return; }
if ( ! AB.audioIsPlaying ( theaudio3 ) ) 	{ theaudio3.play(); return; }
if ( ! AB.audioIsPlaying ( theaudio4 ) ) 	{ theaudio4.play(); return; }
if ( ! AB.audioIsPlaying ( theaudio5 ) ) 	{ theaudio5.play(); return; }

// else try to make a new one, local variable, without user interaction
// but this will not play on some browsers:

var a = new Audio( SOUND_COLLISION );
a.play();
}

// --- Splash screen --------------------------------------------------------------

AB.newSplash ( "Demo of Physics API" );

// when user clicks/touches button on splash screen, audio starts and run starts:

\$("#splashbutton").click ( function ()
{
// mark multiple audio objects as good now for JS to call play() without further user interaction:
theaudio1.play();	theaudio1.pause();
theaudio2.play();	theaudio2.pause();
theaudio3.play();	theaudio3.pause();
theaudio4.play();	theaudio4.pause();
theaudio5.play();	theaudio5.pause();

AB.removeSplash();			// remove splash screen

splashClicked = true;

});

```
```
```