// Cloned by lillisl2 on 28 Jan 2019 from World "Cloned Bouncy Balls" by Ian Gilligan
// Please leave this clone trail here.
// Cloned by Ian Gilligan on 28 Jan 2019 from World "Bouncy Balls" by Starter user
// Please leave this clone trail here.
// ==== Starter World ===============================================================================================
// (c) Ancient Brain Ltd. All rights reserved.
// 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 * 1 ; // y start position of balls
const HEAVYIMPACT = 1000 ; // heavy impacts have y velocity greater than this
const GROUNDSIZE = 150;
const startRadius = BALLSIZE * 30 ;
const maxRadius = BALLSIZE * 250 ;
const TEXTURE_GROUND = '/uploads/gilligi2/8451323128.png' ;
// const TEXTURE_GROUND = '/uploads/starter/latin.jpg' ;
const TEXTURE_WALL = "/uploads/lillisl2/8451232143.png";
const SOUND_COLLISION = '/uploads/starter/bounce.mp3' ;
// credit:
// http://soundbible.com/1343-Jump.html
const SKYCOLOR = 0xffffff;
// friction and restitution between 0 and 1:
const GROUND_FRICTION = 0.4;
const GROUND_RESTITUTION = 0.95;
const WALL_FRICTION = 0.6;
const WALL_RESTITUTION = 0.3;
const BALL_FRICTION = 0.4;
const BALL_RESTITUTION = 0.5;
// define gravity along x,y,z dimensions:
var gravity = new THREE.Vector3 ( 0, -50, 0 );
const BALL_MASS = 6;
function nextballin()
// Speed of ball creation
// Create next ball in some random (m to n) milliseconds time.
{
return ( AB.randomIntAtoB ( 2000, 4000 ) );
}
const FILE_ARRAY = [
"/uploads/gilligi2/8451323595.png",
"/uploads/gilligi2/8451323612.png"
];
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 resourcesLoaded = false;
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
{
var loader = new THREE.TextureLoader();
loader.load ( TEXTURE_GROUND, function ( thetexture ) // asynchronous file load
{
thetexture.minFilter = THREE.LinearFilter;
ground_texture = thetexture;
if ( asynchFinished() ) initScene();
});
for ( var i = 0; i < FILE_ARRAY.length; i++ )
startFileLoad ( i ); // start n more asynchronous file loads
var loader2 = new THREE.TextureLoader();
loader2.load ( TEXTURE_WALL, function ( thetexture ) // asynchronous file load
{
thetexture.minFilter = THREE.LinearFilter;
wall_texture = thetexture;
if ( asynchFinished() ) initScene();
});
for ( var j = 0; j < FILE_ARRAY.length; j++ )
startFileLoad ( j );
}
function startFileLoad ( n ) // asynchronous file load of texture n
{
var loader = new THREE.TextureLoader();
loader.load ( FILE_ARRAY[n], function ( thetexture )
{
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 );
light.castShadow = true;
// how far away to draw shadows:
light.shadow.camera.left = -GROUNDSIZE;
light.shadow.camera.right = GROUNDSIZE;
light.shadow.camera.bottom = -GROUNDSIZE;
light.shadow.camera.top = GROUNDSIZE;
// higher quality shadows at expense of computation time:
light.shadow.mapSize.width = 2048;
light.shadow.mapSize.height = 2048;
light.shadow.bias = -0.0001;
ABWorld.scene.add( light );
// --- Ground ------------------------------------------------------------------
var ground_material = Physijs.createMaterial(
// new THREE.MeshBasicMaterial( { color: 0xdddddd } ),
new THREE.MeshStandardMaterial({ map: ground_texture }),
GROUND_FRICTION,
GROUND_RESTITUTION );
ground_material.map.wrapS = THREE.RepeatWrapping;
ground_material.map.wrapT = THREE.RepeatWrapping;
ground_material.map.repeat.set( 50, 50 );
// ground as plane allows bounces but seems to be finite box
var ground = new Physijs.BoxMesh (
new THREE.BoxGeometry ( GROUNDSIZE, 1, GROUNDSIZE*2 ),
ground_material,0 );
//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 );
*/
ground.receiveShadow = true;
ABWorld.scene.add( ground );
var wall_material = Physijs.createMaterial(
// new THREE.MeshBasicMaterial( { color: 0xdddddd } ),
new THREE.MeshStandardMaterial({ map: wall_texture }),
WALL_FRICTION,
WALL_RESTITUTION );
wall_material.map.wrapS = THREE.RepeatWrapping;
wall_material.map.wrapT = THREE.RepeatWrapping;
wall_material.map.repeat.set( 50, 50 );
var wall = new Physijs.BoxMesh (
new THREE.BoxGeometry(6, 25, GROUNDSIZE*2),
wall_material,0 );
wall.position.set((GROUNDSIZE / 2),0,0);
wall.receiveShadow = true;
ABWorld.scene.add( wall );
wall.addEventListener('collision', function(object) {
console.log("Object " + this.id + " collided with " + object.id);
});
var wall2 = new Physijs.BoxMesh (
new THREE.BoxGeometry(GROUNDSIZE, 25, 6),
wall_material,0 );
wall2.position.set(0,0,(GROUNDSIZE));
wall2.receiveShadow = true;
ABWorld.scene.add( wall2 );
wall2.addEventListener('collision', function(object) {
console.log("Object " + this.id + " collided with " + object.id);
});
var wall3 = new Physijs.BoxMesh (
new THREE.BoxGeometry(6, 25, GROUNDSIZE*2),
wall_material,0 );
wall3.position.set(-(GROUNDSIZE / 2),0,0);
wall3.receiveShadow = true;
ABWorld.scene.add( wall3 );
wall3.addEventListener('collision', function(object) {
console.log("Object " + this.id + " collided with " + object.id);
});
var wall4 = new Physijs.BoxMesh (
new THREE.BoxGeometry(GROUNDSIZE, 25, 6),
wall_material,0 );
wall4.position.set(0,0,-(GROUNDSIZE));
wall4.receiveShadow = true;
ABWorld.scene.add( wall4 );
wall4.addEventListener('collision', function(object) {
console.log("Object " + this.id + " collided with " + object.id);
});
// start infinite loop - but maybe neutralised until splashClicked is true
ABWorld.render();
console.log ( "Resources loaded." );
resourcesLoaded = true;
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, 50, 50 );
var ball = new Physijs.SphereMesh ( ball_geometry, ball_material, BALL_MASS );
ball.collisions = 0;
ball.castShadow = true;
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();
}
});
ABWorld.scene.add( ball );
}
// --- call createBall again -----------------------------------------------------
setTimeout ( createBall, nextballin() ); // set to create next ball in some random time
}
// --- keyboard and touch handling functions: ----------------------------------------
var OURKEYS = [49,50];
function ourKeys ( event ) { return ( OURKEYS.includes ( event.keyCode ) ); }
function keyHandler ( event )
{
if ( ! ABRun.runReady ) return true; // not ready yet
// if not handling this key, send it to default:
if ( ! ourKeys ( event ) ) return true;
// else handle it and prevent default:
if ( event.keyCode == 49 ) createBall();
if ( event.keyCode == 50 ) createBall();
event.stopPropagation(); event.preventDefault(); return false;
}
// --- public interface ----------------------------------------------------------------------
this.newRun = function()
{
ABWorld.init3d ( startRadius, maxRadius, SKYCOLOR ); // sets up renderer, scene, camera
// can adjust renderer:
ABWorld.renderer.shadowMap.enabled = true;
// scene is Physijs.Scene, not THREE.Scene
// can adjust scene - change gravity from default:
ABWorld.scene.setGravity ( gravity );
loadResources(); // asynchronous file loads
// calls initScene() when all done
ABWorld.lookat.copy ( ABWorld.scene.position );
ABWorld.scene.simulate(); // Physics simulate - runs on independent timer to AB nextStep
document.onkeydown = keyHandler;
};
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;
});