Thursday, November 21, 2013

Starting Our First Game, SpacePong3D!

If you have been following along with this blog, you might be saying "These graphics demos and code examples are nice, but when are we going to start making a real 3D game?"  Well, I'm happy to tell you that we are finally ready to start our first game!  

Sorry if the game setup blog posts before this were kind of dull.  But this is a necessary chore for anyone wanting to create a computer game.  Here's the good news:  now that we have gotten the boring setup code out of the way, we can now focus on the fun part: designing and building a real 3D game!  And because we have some basic template code in place to handle different devices, different speeds of hardware,  and different methods of user input, we can relax and feel confident that whatever games we make will work and run smoothly on most devices in the world today.

So, what kind of game should we start with?  There's a saying I like that goes, "Learn to walk before you run."  This phrase applies to a lot of things in life, and I believe it applies to game programming as well.  We must start somewhere to get some experience with coding games.  And what better place to start, than with one of the first and most popular computer games of all time - Pong.  

This may date me, but my first video game experience as a kid was playing Pong on my new Atari 2600 home system.  It is a simple game with simple elements, but it is a classic that stands the test of time.  Countless clones and emulations have been made from its original concept.  Although we will not be the first, nor the last, we will make a 3D version of this classic with some extra tricks.  I chose Pong because it is an excellent teaching example.  The lessons we learn by building a simple 3D game like this will carry over to our more ambitious future game projects.

Since the name Pong is already taken, I thought maybe we could spice up our game's name and call it SpacePong3D.  As the name implies, it will be a Pong-style game set in outer space, with true 3D graphics!  We will take an old flat 2D game and make it pop out into the 3rd dimension.  Sound fun?  I hope so - let's get started!           

Before we start coding, let's familiarize ourselves with the original Pong game.  Here's a screenshot:



The gameplay is sort of like Table Tennis, as viewed from high above the table (but nowhere near as fast as real Table Tennis matches!) .   Actually the game environment is more like Air Hockey because Air Hockey is a flat 2D game where as Table Tennis is 3D (you can go up-and-down as well as side-to-side and forwards and backwards).  Anyway, in Pong, 2 Human players can play against each other, or you can have 1 Human player vs. a Computer-controlled AI opponent.  Each player controls a paddle which hits a small ball back and forth.  A player tries to score against their opponent by having their opponent miss the ball.  When the ball slips past a player's paddle, their opponent receives 1 point.  When a pre-determined score number is reached, for example 10 points,  the player who reaches that score first wins the game.

OK so now we now the basics of Pong.  How do we add something new to the game?  One way is to make the paddles and ball have depth as well.  The rectangles pictured above can be turned into elongated cubes and the ball could be turned into a sphere with depth.  However many clones like this have already been made.  Plus, those re-makes are not being played in true 3D (they have no up and down motion, only side to side).  The gameplay, the motion of the ball, is still confined to a flat table.

Let's go one step further!  Instead of confining the play to a flat 2D table, let's give the table depth as well, and make it a hollow cube!  Now the paddles and ball will be able to move around freely inside the 3D cube space.  And since we have the awesome Three.js library on our side, this 3D updating of the classic game will be within our reach!     

We have been using the same old file names for our demo projects up until now.  I think it's time to create some new files with new names for our game.  The good news is that we don't have to start from scratch.  We can just use our existing code inside example01.html and tempGame.js, and change them to meet our new needs.  Always keep these old files around - that's why I called it tempGame.js - it stands for 'template Game'.   We might update tempGame.js somewhere in the future if we want to add some more functionality to every game, but for now we are just going to copy, paste, and rename.

  Here is our old 'example01.html' file.  Copy and Paste the following code, but save as 'SpacePong3D.html' instead.  You can place it right beside the old example01.html file in the same folder

<!DOCTYPE html>
<html>
   <head>
      <title> Hello Three.js </title>
   </head>
   <body>
      <div id="help" style="position:fixed; left:40%; top:4%; color:grey;">
         Desktop: Press W A S D keys, or Click/Drag Mouse to move the cube
      </div>

      <div id="help2" style="position:fixed; left:40%; top:8%; color:grey;">
         Mobile: Hold finger down and slow Swipe to move the cube
      </div>

      <div id="debug1" style="position:fixed; left:5%; top:4%; color:grey;">
         Debug Info 
      </div>

      <div id="debug2" style="position:fixed; left:5%; top:8%; color:grey;">
         Debug Info 
      </div>

      <script src="js/three.min.js"></script>
      <script src="js/threex.keyboardstate.js"></script>
      <script src="js/virtualjoystick.js"></script> 
      <script src="js/tempGame.js"></script>     
   </body>
</html>


Now Copy and Paste the following old tempGame.js code, but instead save it as 'pongGame.js' , and place it inside your 'js' folder, right beside the old tempGame.js:
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 );   
}


Looking at the entire project, by now you should have a file called SpacePong3D.html and a 'js' folder that sits right beside it.  Inside this 'js' folder, you should have a file called pongGame.js, as well as ones that we have been using:  three.min.js , virtualjoystick.js , and threex.keyboardstate.js . 

If you have all these files and folders in the right place, we are now ready to start coding!  We will begin with the SpacePong3D.html file in the next post.

See you soon!

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!  

Thursday, November 14, 2013

Adding Mobile Touch and Mouse Input (part 1)

We want our future games to be playable on the widest variety of devices.  This includes support for keyboard (which we have already added a couple of posts ago), as well as Mouse input and mobile Touch input.  If our games respond to any and all types of player input, then our games will be more widely enjoyed and shared.    

So, it's time to add support for Mouse and mobile Touch input.  As we saw with keyboard input, we could try to develop a helper library from scratch that handles all the various types of events for both Mouse and Mobile devices.  But why re-invent the wheel?  If there is already an existing library in place that we could use, let's use it!

Once again we turn to Jerome Etienne from Paris, France.  Remember he's the one who developed the 'keyboardstate.js' helper that we used a couple of posts back.  Well, it just so happens that Jerome also has a nifty helper file for adding Touch input as well as Mouse input - it's called 'virtualjoystick.js'.  What Jerome was trying to accomplish with this helper is to make a tablet or smartphone behave like a joystick/gamepad.   And since tablets and smartphones are flat, with no analog sticks protruding from them, Jerome made a virtual representation of a joystick that is displayed on the flat screen - hence the name 'Virtual'Joystick. 

Let's take a moment and think about what a gamepad or joystick does.  When you pull the controller stick/pad to the left, you expect the game character to go left.  Same goes for right.  When you push the stick up, you expect the character to move upwards (or similar action like 'jump').  And when you pull the stick down, you expect the character to move down (or similar action like 'crouch').  

Jerome's virtualjoystick.js emulates this game-controller behavior by waiting for the user to touch the screen (or click if they only have a mouse).  Once the player touches(or clicks), a blue circle is drawn on the screen precisely at the point of touch contact (or click location).  This circle is called the 'base' and does not move.  Now, while the base is sitting still, a smaller circle is drawn inside of the base - this is called the 'stick' and this DOES move.  It can move completely outside the bigger circle if you want. Whenever the user swipes (or drags with the mouse button down) in a direction, the 'stick' will follow, just like a game controller stick.  

So, if the user swipes right, the 'stick' circle will follow the user's finger and the controller stick will go right also.  If the user swipes down, the stick moves down as well. Similarly, if the user only has a mouse, then when they move the mouse right, the 'stick' circle moves to the right also.  Even if the user goes crazy and moves in circles, the virtual stick will accurately follow! 

The only requirement for all this to work is that the user's finger must remain somewhere on the screen.  If the user lifts their finger (or mouse users let go of the mouse button), then the base and stick both disappear.  Not to worry though - nothing is lost.  The cool thing is that you can just touch (or click) anywhere on the screen again, and that new point becomes your new joystick 'base'!  It is now instantly ready to accept control movements from the player again.  

At some point we may want to make the base always 'on' and permanently displayed onscreen, no matter what the user does.  The solution will depend on the type of game we are making and the type of situation that we are dealing with.  

Enough talk - let's add this helper library to our project so we can use it!  Click on the following link:

virtualjoystick.js

Once you're there, Right-Click and Save as... 'virtualjoystick.js' .  Place it next to our other .js files inside your 'js' folder.  

We need to access this new file from our example01.html webpage.  By now, you should be an expert at adding libraries to html pages, right?  Just in case though, here's how to do it:         
<script src="js/virtualjoystick.js"></script> 
Remember order matters when you are adding these libraries (or dependencies) to your project.  Place the above line in between threex.keyboardstate.js and tempGame.js .  'virtualjoystick.js' will now be 3rd place in the list of 4 total .js files.   

Here is our updated 'example01.html' file with the new 'virtualjoystick.js' library added in correct order:

<!DOCTYPE html>
<html>
   <head>
      <title> Hello Three.js </title>
   </head>
   <body>
      <div id="help" style="position:fixed; left:40%; top:4%; color:grey;">
         Desktop: Press W A S D keys, or Click/Drag Mouse to move the cube
      </div>

      <div id="help2" style="position:fixed; left:40%; top:8%; color:grey;">
         Mobile: Hold finger down and slow Swipe to move the cube
      </div>

      <div id="debug1" style="position:fixed; left:5%; top:4%; color:grey;">
         Debug Info 
      </div>

      <div id="debug2" style="position:fixed; left:5%; top:8%; color:grey;">
         Debug Info 
      </div>

      <script src="js/three.min.js"></script>
      <script src="js/threex.keyboardstate.js"></script>
      <script src="js/virtualjoystick.js"></script> 
      <script src="js/tempGame.js"></script>     
   </body>
</html>

Copy and paste the above code and save it as 'example01.html', writing over the old one.  Notice also that I added another div element with an id of "help2".  This is another line of help text placed directly below the older help text.  It tells the mobile (smartphone and tablet) users what to do in order to move the green cube on their devices.

In part 2 of this post, we will access this new helper file and learn how to apply it to our project.  Then our demo/future games will be playable on any device - laptop, desktop, tablet, and smartphone! 

(Continued in part 2)...

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!  

Tuesday, November 5, 2013

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

One very useful routine our future games can have is the ability to print real-time game data to the screen.  It often helps to see the raw numbers behind our game's objects to make sure that they are functioning properly.  

And, being human, we will make mistakes while coding, which leads to program errors, or 'bugs' as we programmers like to call them.  Therefore to get rid of all the bugs/mistakes, or 'debug', it helps to have game variables' numbers (position, speed, etc.) and flag variables' states (true, false, on, off, etc.) displayed quickly to our screen while the game is running so we can better spot the problem.  Chances are that if we see weird or unexpected raw data being output to the screen, then that is the source of the bug.  We can then quickly locate it inside the code and fix it.

In the last post we learned how to add simple instruction text to the top of the browser window to help first-time players know what to do.  Now we will add some 'debug' info text to the top left-hand corner of the browser window.  We will also update the text inside the animate( ) loop so that the numbers on-screen will quickly change as fast as the game updates.  This will indicate what's going on with all the game objects that we want to inspect more closely.        

Just for reference, here's how we added the help info text last time:      
<div id="help" style="position:fixed; left:40%; top:4%; color:grey;"> 
   Press W, A, S, and D keys to move the cube 
</div>

And here's our new code to add the debug text:
<div id="debug1" style="position:fixed; left:5%; top:4%; color:grey;"> 
   Debug Info 
</div>

Can you spot the small differences?  First, the 'id' is now named "debug1" instead of "help", which is representative of what this HTML code does.  The style properties are the same except for 'left: 5%;' , which places this text only 5% from the left edge of the browser window.  Finally, the 'Debug Info' text on the next line is what the user will actually see displayed in the browser window.  However, we will update this so quickly that you probably won't have time to see the words 'Debug Info' - instead it will be replaced by whatever numbers and data that we want to quickly print to the screen.  I just put the words 'Debug Info' in there for code readability.  
Let's add a second line of debug text.  This will be almost identical to the code above: 
<div id="debug2" style="position:fixed; left:5%; top:8%; color:grey;"> 
   Debug Info 
</div>
Notice that I changed the id name to "debug2" to help keep it separate from the "debug1" id.  Finally, in the style properties, all that is changed is the vertical positioning of the text by typing 'top:8%;'.  This text is now placed 8% from the top, which is a little farther down than 4%.  If we didn't change this, the "debug2" text would be displayed right on top of the "debug1" text, making it impossible to read.   

Here is the updated example01.html file with our new "debug1" and "debug2"  text 'div' elements:

<!DOCTYPE html>
<html>
   <head>
      <title> Hello Three.js </title>
   </head>
   <body>
      <div id="help" style="position:fixed; left:40%; top:4%; color:grey;">
         Press W, A, S, and D keys to move the cube 
      </div>

      <div id="debug1" style="position:fixed; left:5%; top:4%; color:grey;">
         Debug Info 
      </div>

      <div id="debug2" style="position:fixed; left:5%; top:8%; color:grey;">
         Debug Info 
      </div>

      <script src="js/three.min.js"></script>
      <script src="js/threex.keyboardstate.js"></script> 
      <script src="js/tempGame.js"></script>     
   </body>
</html>

Copy and paste the above code and save it as 'example01.html', writing over the old one.  In part 2 of this post, we will add a few lines of code inside our 'tempGame.js' file to quickly update the debug1 and debug2 texts in real-time.  Then we will see data quickly flowing to the screen!

(Continued in part 2)...