Thursday, November 7, 2013

Printing Real-Time Game Data to the Screen (part 2)

Now we can access our new text 'div' elements through our JavaScript file, 'tempGame.js'.  First we must create a couple of variables that hold references to the new 'div' text elements back on our webpage.  This is how it's done: 
var debugText1 = document.getElementById("debug1");
Using the 'var' keyword, we create a variable called 'debugText1' that will hold the handle or reference to a text element.  But which webpage text element - there could be hundreds?  Well, remember back in part 1 of this post, inside our HTML file 'example01.html' we created a 'div' element and gave it an 'id' named "debug1".  In case you forgot, here is the line taken from 'example01.html' :
<div id="debug1" style="position:fixed; left:5%; top:4%; color:grey;"> ....
This is our target div that we want.  How do we access it from our 'tempGame.js' file when it's located on another file, example01.html?  Well, for just this purpose, all browsers come with some handy methods/functions that we can call from the 'document' object.  One of these is 'getElementById( )'.  Inside the parentheses goes the id name that you gave to the element, in this case "debug1".  Now this function will go and find the correct target element on our html webpage document and give us access to it so that we can change it, add to it, remove it, color it, anything we want.   Since we made the var 'debugText1' refer to the target element 'debug1', they are permanently linked. 

We also need to do this for the other div element which has the id of 'debug2'.  If you recall "debug2" sits right underneath "debug1" on our webpage.  Here's both lines of code for referencing "debug1" and "debug2" webpage elements:
var debugText1 = document.getElementById("debug1");
var debugText2 = document.getElementById("debug2");
All I did was change the '1's to '2's.  Now we have handles named 'debugText1' and 'debugText2' that give us access to their corresponding elements back on our webpage. We can now type for example 'debugText1.someFunction' where 'someFunction' is a method of our choice to change its target webpage element ("debug1").    

One of these functions/methods is '.innerHTML = ' and its purpose is to change the element's text content instantly.  After the equals sign ( = ) goes the new text that we want to be displayed on our webpage.  So if I type debugText1.innerHTML = "Hello World";  then our webpage will display the words "Hello World" right where that particular target 'div' ("debug1") element is located.  Whatever text that was stored in "debug1" will be erased and the words "Hello World" will now appear in their place. 

The cool thing about '.innerHTML = ' is that in addition to static text like "Hello World", we can also put dynamic variable text, like a game value that is constantly changing while our game runs.  For this demo I chose our green cube's X and Y position to be displayed as dynamic changing text output.  If you recall, any code that deals with quickly changing things MUST be placed inside our animate( ) function, so that it gets refreshed as fast as possible.

Here are the 2 lines of code dealing with the real-time text output.  These two lines will be placed inside our 'animate( )' function and will be executed as quickly as our program can run. The first line changes its target div element "debug1" (which is linked by reference now) and the second line changes its target div element "debug2":
debugText1.innerHTML = "Cube position X: " + cube.position.x.toFixed(1);
debugText2.innerHTML = "Cube position Y: " + cube.position.y.toFixed(1);
Don't be alarmed by the '.toFixed(1)' stuff - I will explain that in a moment.  For now,  let's review what is happening here:  'debugText1' refers to the "debug1" element on our webpage.  We use the '.innerHTML = ' method to change its text content.  In double quotes, we write any static text that we want.  So in this case we type "Cube position X: " .  This will now appear in the top left-hand corner of the webpage and it will not change or move at all - it is static.  However, the next part WILL change very quickly - many times a second.  First we use the plus sign ( + ) to add some more text that will appear immediately after the static words "Cube position X: ".  And this extra text will be in the form of our 'cube.position.x' variable, whatever that happens to be at the moment.  Finally I used the '.toFixed(1)' method to make this number have a fixed decimal place.  You see, JavaScript numbers have many precision decimal places after them (sometimes 0.000000000000000, or 15 decimal places!) .  We are not doing scientific measurements so we do not need to clutter up our webpage readouts with all these precision decimal places.  And just for this occasion, there is a handy function called 'toFixed( )' that shortens the number of precision decimal places to your liking.  Inside the parentheses goes the number of decimal places that you want.  So 'toFixed(1)' will give a fixed decimal place of 1 so that our text printout '0.000000000000000' will be shortened to '0.0' , which is much more readable and just fine for our game purposes. 

The same is done above for 'debugText2', which refers to the "debug2" element on the webpage.  Although its text reads "Cube position Y: " and then the 'cube.position.y' variable is used instead, with the 'toFixed( )' method tacked on the end as well so that it is also shortened nicely.

All we have to do now is add all the above lines 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 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;
   }

   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 see the "Cube position X and Cube position Y" and their respective values printed in the top left-hand corner of our webpage.  Go ahead and move the green cube around with the W, A, S, and D keys.  When you do this, notice that the position numbers refresh themselves in real-time, telling us their value!

This new debug text is for our programming eyes only.  We will remove it for the release of our future games so that end-users will not see the debug text.  But while we are creating the demos/games, this is a helpful feature to have.  Being able to watch certain game numbers behind the scenes has helped me a lot in my past game programming adventures.  Hopefully this small feature will help you too!  

Next time we will learn how to respond to mouse button and mouse movement events.  After that, we'll take a look at handling touch input from a smartphone or tablet, so that anyone, regardless of their device, can play our games!