// Cloned by Martin Derwin on 30 Nov 2022 from World "User-controlled Model World" by Starter user// Please leave this clone trail here.// ==== Starter World =================================================================================================// This code is designed for use on the Ancient Brain site.// This code may be freely copied and edited by anyone on the Ancient Brain site.// To include a working run of this program on another site, see the "Embed code" links provided on Ancient Brain.// ====================================================================================================================// User controlled (not Mind controlled) 3D model World with various features:// - User controlled UP/LEFT/RIGHT arrows move model// - "First Person View" - Camera rotates with direction you are facing// Best effect is with "Move with" camera// - Can have one or multiple lions chasing you// Smooth movement// UP moves forward in whatever angle you are at// LEFT/RIGHT rotate by small angle// Uses x, y, z rather than grid of squares// Has collision detection for lion moves// ===================================================================================================================// === 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?/* PROJECT DESCRIPTION:
Authors: Patricija Shalkauskaite, Martin Derwin
How to play:
- You play as a mouse running from lions
- Move Player 1 with the arrow keys and Player 2 with WASD keys
- If a player is caught by a lion, they lose and the other player wins
*/
AB.clockTick =100;// Speed of run: Step every n milliseconds. Default 100.
AB.maxSteps =10000;// Length of run: Maximum length of run in steps. Default 1000.
AB.screenshotStep =100;// Take screenshot on this step. (All resources should have finished loading.) Default 50.
AB.drawRunControls =true;// Turn on the Run/Step/Pause controlsABWorld.drawCameraControls =false;// Scrap the Track/Move with/Normal camera controls//---- global constants: -------------------------------------------------------// number of lions, an array for each player:const NO_LIONS_1 =30;const NO_LIONS_2 =30;const OBJ_LIONS ='/uploads/martind/Lion-obj.OBJ';// credit: https://www.turbosquid.com/3d-models/3d-lioness-animation/1114520const OBJPATH ="/uploads/martind/";// path of OBJ and MTL (Peter Parker model)const OBJNAME ="rat.obj";// credit: https://www.turbosquid.com/3d-models/mouse-mammal-rodent-max/603885const MTLNAME ="player2texture.jpg"// rat object mtl file didn't work// multiply model sizes by some amount:const SCALE_RAT =30;// random size LION with each run:const SCALE_LIONS = AB.randomFloatAtoB (1,3);const TEXTURE_LION ='/uploads/martind/fur.jpg';const MAXPOS =3500;// length of one side of the arenaconst SKYCOLOR =0xffffcc;// a number, not a stringconst LIGHTCOLOR =0xffffff;const startRadiusConst = MAXPOS ;// distance from centre to start the camera atconst maxRadiusConst = MAXPOS *10;// maximum distance from camera we will render thingsconst AGENT_STEP =100;// how much agent moves each stepconst ENEMY_STEP =50;// how much enemy moves each step// this is as close as models come to other modelsconst CLOSE_LIMIT =100;//--- change ABWorld defaults: -------------------------------ABHandler.MAXCAMERAPOS = maxRadiusConst ;ABHandler.GROUNDZERO =true;// "ground" exists at altitude zero// --- Rotations -----------------------// rotate by some amount of radians from the normal position// default is 0 which has the model facing DOWN (towards increasing z value) as far as the initial camera seesconst ROTATE_AMOUNT =Math.PI /20;// rotate amount in radians (PI = 180 degrees)//--- skybox: -------------------------------// space skybox, credit:// http://en.spaceengine.org/forum/21-514-1const SKYBOX_ARRAY =[// skybox"/uploads/pat/cheese.png","/uploads/pat/cheese.png","/uploads/pat/cheese.png","/uploads/pat/cheese.png","/uploads/pat/cheese.png","/uploads/pat/cheese.png"];// ===================================================================================================================// === End of tweaker's box ==========================================================================================// ===================================================================================================================// You will need to be some sort of JavaScript programmer to change things below the tweaker's box.var THELIONS1 =newArray( NO_LIONS_1 );var THELIONS2 =newArray( NO_LIONS_2 );var theagent, theagent2, theenemy;// player1, player2, lionsvar p1lose =false;// when this is true, player 1 has lostvar p2lose =false;// when this is true, player 2 has lostvar agentRotation =0;var agentRotation2 =0;var enemyRotation =0;var enemyRotation2 =0;var lion_texture;function loadResources()// asynchronous file loads - call initScene() when all finished{// load lion - OBJ that we will paint with a texturevar loader =new THREE.OBJLoader(new THREE.LoadingManager());
loader.load ( OBJ_LIONS,function( object ){
theenemy = object;// canonical enemy object - may be copied multiple timesif( asynchFinished()) initScene();});// load RAT model - OBJ plus MTL (plus TGA files)
THREE.DefaultLoadingManager.addHandler (/\.tga$/i,new THREE.TGALoader());var m =new THREE.MTLLoader();
m.setResourcePath ( OBJPATH );
m.setPath ( OBJPATH );
m.load ( MTLNAME,function( materials ){
materials.preload();var o =new THREE.OBJLoader();
o.setMaterials ( materials );
o.setPath ( OBJPATH );
o.load ( OBJNAME,function( object ){
theagent = object;if( asynchFinished()) initScene();});});var n =new THREE.MTLLoader();
n.setResourcePath(OBJPATH);
n.setPath(OBJPATH);
n.load(MTLNAME,function(materials){
materials.preload();var p =new THREE.OBJLoader();
p.setMaterials (materials);
p.setPath(OBJPATH);
p.load(OBJNAME,function(object){
theagent2 = object;if(asynchFinished()) initScene();});});// load texturesvar loader2 =new THREE.TextureLoader();
loader2.load ( TEXTURE_LION,function( thetexture ){
thetexture.minFilter = THREE.LinearFilter;
lion_texture = thetexture;if( asynchFinished()) initScene();});}function asynchFinished()// all file loads returned{if( lion_texture && theenemy && theagent && theagent2)returntrue;elsereturnfalse;}function initScene()// all file loads have returned{// add agent model// start at center
theagent.position.y =0;
theagent.position.x =0;
theagent.position.z =0;
theagent2.position.y =0;
theagent2.position.x =300;
theagent2.position.z =0;
theagent.scale.multiplyScalar ( SCALE_RAT );// scale itABWorld.scene.add( theagent );
theagent2.scale.multiplyScalar ( SCALE_RAT);ABWorld.scene.add( theagent2 );// add enemies// first paint and size the canonical enemy object
theenemy.traverse (function( child ){if( child instanceof THREE.Mesh)
child.material.map = lion_texture ;});
theenemy.scale.multiplyScalar ( SCALE_LIONS );// scale it// add perhaps multiple enemy models, starting near outside of arena// LIONS for player 1for(var i =0; i < NO_LIONS_1 ; i++){var object = theenemy.clone();// copy the object multiple times
object.position.y =0;
object.position.x = AB.randomPick ( AB.randomIntAtoB (-MAXPOS/2,-MAXPOS/3), AB.randomIntAtoB ( MAXPOS/3, MAXPOS/2));
object.position.z = AB.randomPick ( AB.randomIntAtoB (-MAXPOS/2,-MAXPOS/3), AB.randomIntAtoB ( MAXPOS/3, MAXPOS/2));ABWorld.scene.add( object );// save in array for later
THELIONS1[i]= object;}// LIONS for player 2for(var i =0; i < NO_LIONS_2 ; i++){var object = theenemy.clone();// copy the object multiple times
object.position.y =0;
object.position.x = AB.randomPick ( AB.randomIntAtoB (-MAXPOS/2,-MAXPOS/3), AB.randomIntAtoB ( MAXPOS/3, MAXPOS/2));
object.position.z = AB.randomPick ( AB.randomIntAtoB (-MAXPOS/2,-MAXPOS/3), AB.randomIntAtoB ( MAXPOS/3, MAXPOS/2));ABWorld.scene.add( object );// save in array for later
THELIONS2[i]= object;}// finally skyboxABWorld.scene.background =new THREE.CubeTextureLoader().load ( SKYBOX_ARRAY,function(){ABWorld.render();
AB.removeLoading();
AB.runReady =true;});}// --- enemy move -----------------------------------// angle between two points (x1,y1) and (x2,y2)function angleTwoPoints ( x1, y1, x2, y2 ){returnMath.atan2 ( x2 - x1, y2 - y1 );}function dist ( a, b )// distance between them when a,b are Three vectors{return( a.distanceTo ( b ));}function collision ( proposedMove, k )// proposed move FOR lion k{if( dist ( proposedMove, theagent.position )< CLOSE_LIMIT ){
p1lose =true;// if a lion touches player 1, they lose
AB.abortRun =true;}for(var i=0; i < NO_LIONS_1 ; i++)if( i != k )if( dist ( proposedMove, THELIONS1[i].position )< CLOSE_LIMIT )returntrue;// elsereturnfalse;}function collision2 ( proposedMove, k )// proposed move FOR lion k{if( dist ( proposedMove, theagent2.position )< CLOSE_LIMIT ){
p2lose =true;// if a lion touches player 2, they lose
AB.abortRun =true;}for(var i=0; i < NO_LIONS_2 ; i++)if( i != k )if( dist ( proposedMove, THELIONS2[i].position )< CLOSE_LIMIT )returntrue;// elsereturnfalse;}function moveEnemy(){for(var i =0; i < NO_LIONS_1 ; i++){var e = THELIONS1[i];// rotate enemy to face agent
enemyRotation = angleTwoPoints ( e.position.x, e.position.z, theagent.position.x, theagent.position.z );
e.rotation.set(0, enemyRotation,0);// move along that line, if no collision with any other modelvar proposedMove =new THREE.Vector3(
e.position.x +( ENEMY_STEP *Math.sin(enemyRotation)),
e.position.y,
e.position.z +( ENEMY_STEP *Math.cos(enemyRotation)));if(! collision ( proposedMove, i ))
e.position.copy ( proposedMove );// else enemy i just misses a turn}}function moveEnemy2(){for(var i =0; i < NO_LIONS_2 ; i++){var e2 = THELIONS2[i];// rotate enemy to face agent
enemyRotation2 = angleTwoPoints ( e2.position.x, e2.position.z, theagent2.position.x, theagent2.position.z );
e2.rotation.set(0, enemyRotation2,0);// move along that line, if no collision with any other modelvar proposedMove =new THREE.Vector3(
e2.position.x +( ENEMY_STEP *Math.sin(enemyRotation2)),
e2.position.y,
e2.position.z +( ENEMY_STEP *Math.cos(enemyRotation2)));if(! collision2 ( proposedMove, i ))
e2.position.copy ( proposedMove );// else enemy i just misses a turn}}//--- key control of agent -------------------------------------------------------------const KEY_UP =38;const KEY_LEFT =37;const KEY_RIGHT =39;const KEY_A =65;const KEY_W =87;const KEY_D =68;var OURKEYS =[ KEY_UP, KEY_LEFT, KEY_RIGHT, KEY_A, KEY_W, KEY_D ];function ourKeys(event){// checks if a key pressed is either WASD or arrow keysreturn(OURKEYS.includes(event.keyCode));}// UP moves forward in whatever angle you are at// LEFT/RIGHT rotate by small anglefunction keyHandler ( event ){if(! AB.runReady )returntrue;// not ready yet// if not handling this key, send it to default:if(! ourKeys ( event ))returntrue;// else handle it and prevent default:// cases for each different key// sendData first sends to other worlds what to do, then does it locally using movePlayerif(event.keyCode == KEY_A) sendData(KEY_A,2);if(event.keyCode == KEY_W) sendData(KEY_W,2);if(event.keyCode == KEY_D) sendData(KEY_D,2);if(event.keyCode == KEY_LEFT){
console.log("Player 1 is moving left!!!");
sendData(KEY_LEFT,1);}if(event.keyCode == KEY_UP) sendData(KEY_UP,1);if(event.keyCode == KEY_RIGHT) sendData(KEY_RIGHT,1);
event.stopPropagation(); event.preventDefault();returnfalse;}
AB.socketStart();// start sockets
AB.socketIn =function(data)// incoming data on socket, i.e. key movements of other players{
action = data[0]
player = data[1]
movePlayer(action, player);};function sendData(action, player)// function to send out the move to do, then do the move locally{
data =[action, player];
AB.socketOut(data);
movePlayer(action, player);};function movePlayer(action, player){if(player ==1){if( action == KEY_UP )// move a bit along angle we are facing{
theagent.position.x = theagent.position.x +( AGENT_STEP *Math.sin(agentRotation));
theagent.position.z = theagent.position.z +( AGENT_STEP *Math.cos(agentRotation));}if( action == KEY_LEFT )// rotate in place{
agentRotation = agentRotation + ROTATE_AMOUNT;
theagent.rotation.set(0, agentRotation,0);}if( action == KEY_RIGHT ){
agentRotation = agentRotation - ROTATE_AMOUNT;
theagent.rotation.set(0, agentRotation,0);}}else// Player 2{if(action == KEY_W){
theagent2.position.x = theagent2.position.x +(AGENT_STEP *Math.sin(agentRotation2));
theagent2.position.z = theagent2.position.z +(AGENT_STEP *Math.cos(agentRotation2));}if(action == KEY_A){
agentRotation2 = agentRotation2 + ROTATE_AMOUNT;
theagent2.rotation.set(0, agentRotation2,0);}if(action == KEY_D){
agentRotation2 = agentRotation2 - ROTATE_AMOUNT;
theagent2.rotation.set(0, agentRotation2,0);}}}
AB.world.newRun =function(){
AB.loadingScreen();
AB.runReady =false;ABWorld.init3d ( startRadiusConst, maxRadiusConst, SKYCOLOR );
loadResources();// aynch file loads, returns the function call initScene()var ambient =new THREE.AmbientLight();// lightABWorld.scene.add( ambient );var thelight =new THREE.DirectionalLight( LIGHTCOLOR,-1);
thelight.position.set( startRadiusConst, startRadiusConst, startRadiusConst );ABWorld.scene.add(thelight);
document.onkeydown = keyHandler;};
AB.world.nextStep =function(){
moveEnemy();// enemies moves on their own clock
moveEnemy2();// enemies moves on their own clock};
AB.world.endRun =function(){if( AB.abortRun && p1lose) AB.msg (" <br> <font color=red> <B> Player 1 was caught! Player 2 wins. </B> </font> ",2);elseif( AB.abortRun && p2lose) AB.msg (" <br> <font color=red> <B> Player 2 was caught! Player 1 wins. </B> </font> ",2);};