Friday, November 15, 2013

Adding Mobile Touch and Mouse Input (part 2)

We have successfully included the new Mobile Touch/Mouse input helper file.  But how do we access it?  Well, Jerome Etienne, creator of this helper file, has designed his library to be intuitive and easy to use.  As we have done many times before, we must first declare a variable that will hold the 'virtualjoystick' object and all its properties and functionality.

Open up our 'tempGame.js' file and add the following line near the top:    
var joystick = new VirtualJoystick({
                      mouseSupport: true
                   });
There is some difference in the look of the above code compared to how we declared a 'keyboard' variable a couple of posts back.  Going line by line, we use the 'var' keyword followed by a name for our game controller, in this case 'joystick'.  Then comes the '= new' which fills 'joystick' with whatever comes next.  And what comes next is a call to 'VirtualJoystick({ });'  Notice this function has curly braces { } inside its parentheses.  These curly braces let us know that there will be 'key: value' pairs inside.  The purpose of these 'key: value' pairs is to further customize this joystick object to our liking.  Jerome has offered a number of parameters that can be placed here, but the one we want for this project right now is called 'mouseSupport'.  'mouseSupport' can be set to either true or false.  We DO want to be able to read the mouse, if the user has one, so we put 'mouseSupport: true' inside the curly braces. 

This style of object creation is kind of like what we saw with 'var material = new THREE.MeshLambertMaterial({ color: 'rgb(0,255,0)' }); '  - except that this was all written on one line.  We could have done this with VirtualJoystick as well, but I like the 'mouseSupport: true' part being on its own line.  It makes this function and its parameters a little more readable.

Moving on, the 'joystick' object we just created now contains all the information and parameters that we wanted it to have.  We can access the data coming from the player's touch/or mouse input by calling functions on 'joystick'.  

Here's how we determine if the user is moving the game controller joystick to the right:
if( joystick.right() ){
     cube.position.x = cube.position.x + 60 * frameTime;    
}
Let's take a look at each line above.  Remember the 'if' statement tests for whether something is true or not.  And what we want to test for is if the user is moving their game controller joystick in a certain direction.  Jerome has designed a function for the joystick object called '.right( )' .  This simple function tests whether the user is moving the virtual joystick to the right or not.  It returns 'true' if yes, and 'false' if no.

Also recall that if this test comes back as 'true', then the code inside the 'if' statement's curly braces { } gets executed.  So if indeed the user is moving the joystick to the right, we move the green cube's x position to the right by adding positively to it.  Similarly, we can test for 'joystick.left( )', joystick.up( )', and 'joystick.down( )', and then perform the appropriate actions on the cube for each case.   

Here's how that looks:
if( joystick.right() ){
     cube.position.x = cube.position.x + 60 * frameTime;    
}
if( joystick.left() ){
     cube.position.x = cube.position.x - 60 * frameTime;     
}
if( joystick.up() ){
     cube.position.y = cube.position.y + 60 * frameTime;       
}
if( joystick.down() ){
     cube.position.y = cube.position.y - 60 * frameTime;
}
What's really cool is that Jerome's 'joystick' helper also can handle diagonal movements.    If you move the joystick right AND up at the same time, the cube will combine movement right (+60) and up (+60) at the same time, which is diagonally upper-right motion.  We now have 8 possible movement directions, similar to the old 1980's game controller sticks and gamepads!   

All we have to do now is add all the above lines back into our tempGame.js file.  Copy and Paste the following code and save it as 'tempGame.js', overwriting the old one:

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);
var clock = new THREE.Clock();

var keyboard = new THREEx.KeyboardState();
var joystick = new VirtualJoystick({
                      mouseSupport: true
                   });

var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
window.addEventListener('resize', onWindowResize, false); 

var cubeGeometry = new THREE.CubeGeometry(20,20,20);
var cubeMaterial = new THREE.MeshLambertMaterial({ color: 'rgb(0,255,0)' });
var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
scene.add(cube);

var sphereGeometry = new THREE.SphereGeometry(5);
var sphereMaterial = new THREE.MeshBasicMaterial({ color: 'rgb(255,255,0)' });
var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
scene.add(sphere);

var light = new THREE.PointLight( 'rgb(255,255,255)', 1, 0 );
scene.add(light);

var sunRiseFlag = true;
var sunHeight = 0;
var frameTime = 0;

camera.position.y = 40;
camera.position.z = 160;

cube.rotation.x = 0.4;
cube.rotation.y = 0.6;

var debugText1 = document.getElementById("debug1");
var debugText2 = document.getElementById("debug2");

animate();

function animate(){
   
   requestAnimationFrame(animate);

   frameTime = clock.getDelta();

   if(sunRiseFlag == true){
      sunHeight = sunHeight + 60 * frameTime;
   }

   if(sunRiseFlag == false){
      sunHeight = sunHeight - 60 * frameTime;
   }

   if(sunHeight > 150){
      sunHeight = 150;
      sunRiseFlag = false;
   }

   if(sunHeight < 0){
      sunHeight = 0;
      sunRiseFlag = true;
   }

   light.position.set(50,sunHeight,50);
   sphere.position = light.position;

   
   if( keyboard.pressed("D") ){
 cube.position.x = cube.position.x + 60 * frameTime;
   }
   if( keyboard.pressed("A") ){
 cube.position.x = cube.position.x - 60 * frameTime;
   }
   if( keyboard.pressed("W") ){
 cube.position.y = cube.position.y + 60 * frameTime;
   }
   if( keyboard.pressed("S") ){
 cube.position.y = cube.position.y - 60 * frameTime;
   }

   if( joystick.right() ){
 cube.position.x = cube.position.x + 60 * frameTime;    
   }
   if( joystick.left() ){
 cube.position.x = cube.position.x - 60 * frameTime;     
   }
   if( joystick.up() ){
 cube.position.y = cube.position.y + 60 * frameTime;       
   }
   if( joystick.down() ){
 cube.position.y = cube.position.y - 60 * frameTime;
   }

   renderer.render( scene, camera );
   
   debugText1.innerHTML = "Cube position X: " + cube.position.x.toFixed(1);
   debugText2.innerHTML = "Cube position Y: " + cube.position.y.toFixed(1);
   
}


function onWindowResize(){
   camera.aspect = window.innerWidth / window.innerHeight;
   camera.updateProjectionMatrix();
   renderer.setSize( window.innerWidth, window.innerHeight );   
}

Go back and open the 'example01.html' file with your browser.  If you saved everything correctly, you should be able to use your mobile device (smartphone or tablet), or your mouse (if you are at your desktop/laptop).  When you touch and hold (or hold down the mouse button) you will see our new virtual joystick appear exactly where you touched or clicked!  Then, while holding down your finger (or mouse button) try moving around.  On a mobile touch device, this would be like a slow 'swipe' motion.  On the mouse, its known as a click and 'drag' motion.  

The green cube now magically responds to your touch / mouse input and moves where you want it to.  If you lift off the screen (or let go of the mouse button), the joystick base and stick circles will disappear.  But just touch or click again and they will turn back on, and instantly be ready to control your game's characters!

Thanks to Jerome Etienne's awesome helper libraries, we now have complete support for all the users out there that might be playing our games on a desktop, laptop, tablet, or even smartphone.  

Guess what?  We are now ready to make 3D games like I promised!  We have completed most of the template or set-up code so that now we can focus on creating an actual playable game.  There are some minor house-keeping techniques that we must learn, but I will fill in the remaining pieces along the way as we are making our first game.  

Hang in there - it's about to get real!