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)...

Thursday, October 31, 2013

Adding Keyboard Input (part 2)

Now that we have included the new keyboard input helper file, how do we access it?  Well, Jerome Etienne designed his 'threex.keyboardstate.js' to work a lot like Three.js does, so we should feel comfortable using it. 

Open up our 'tempGame.js' file and add the following line near the top:    
var keyboard = new THREEx.KeyboardState();
That looks familiar doesn't it?  There IS a small difference however in this variable declaration.  Notice the small letter 'x' placed right after the word 'THREE' .  This tells us that the variable 'keyboard' will be using the new THREEx library rather than the older THREE library that we have had since the beginning of our project.  Other than that, it's identical in syntax, so we should feel right at home.

The 'keyboard' object now holds all the properties and functionality designed by Jerome to aid us in scanning the player's keyboard for keypresses in real time.  And since we want our game to quickly scan the keyboard on every animation frame (as fast as it can), we will put the actual scanning code inside the animation function.  Here's how the new keyboard scanning code lines look:
if( keyboard.pressed("D") ){
 cube.position.x = cube.position.x + 60 * frameTime;
}
Let's take a look at each line above.  Remember our 'if( )' statement a while back.  This 'if' statement tests for whether something is true or not.  And what we want to test for is if the user is pressing the 'D' key on their keyboard.  Jerome has designed a function for the keyboard object called '.pressed(" ")' .  Inside the double quotes we place the key on the keyboard that we want to test for.  In this case it's the 'D' key, so we type 'keyboard.pressed("D")' .  This will return 'true' if the 'D' key is pressed, and 'false' if it is not pressed.

Also recall that if the test comes back as 'true', then the 'if' statement will execute whatever is inside its curly braces { }.  In this case I have put a line of code to move our green cube's position to the right (positive x).  I chose '60' because it is medium-fast and looked good for this demo (feel free to experiment with this number).  Notice I also put ' * frameTime; ' at the end.  Get used to doing this for every line of code that deals with motion or animation.  Using 'frameTime' like this on all the game's moving objects will make sure that it looks the same on everyone's computer, no matter what the specs are for their device.

All that's left to do now is add the tests for the other 3 keys : the W, A, and S keys.  Here's how the whole thing looks now:
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;
}
The above statements test for the 4 keys that most games use as input: the WASD keys.  Looking at each case above, the 'D' key will move the cube right (positive x), the 'A' key will move the cube left (negative x), the 'W' key will move the cube up (positive y) and the 'S' key will move the cube down (negative y).  

What's really cool is that Jerome's keyboard helper also can handle multiple keypresses.  So, if you hold down 'D' and 'A' at the same time, the +60 and -60 will cancel each other out, and you will see the cube stop.  If you hold the 'D' and the 'W' key 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!  Thinking in a clockwise manner, they are: up, upper-right, right, lower-right, down, lower-left, left, and upper-left.  

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 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;

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 );
   
}


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 our new help text at the top of the webpage that reads: "Press W, A, S, and D keys to move the cube".  And when you press them - the cube moves!  We now have a responsive, interactive graphics demo.  It is not a game yet, but we are getting there.

I should note that this demo only works on desktops and laptops that have a physical keyboard attached.  For smart phones and tablets, we will have to figure out how to handle touch input on those devices.  We want ALL users to be able to play our games right?  Luckily, there are some libraries and helper routines that we can quickly add to make the demos/games work on mobile devices as well.  We will take a look at those in the near future. 

It's pretty awesome though that we only added about 10 lines of code and now we have user input, making our demo truly interactive.  That is the magic of JavaScript and helper libraries!

In the next posts we will add debug/info text to the top left-hand corner of our webpage that will tell us the number values of any game object that we want to inspect more closely.  This is really helpful when monitoring game performance, game variables that are quickly changing, and also when something isn't quite working how we want - it can help us correct any problems that come up. 

See you soon!

Wednesday, October 30, 2013

Adding Keyboard Input (part 1)

At this point we have the makings of a fun graphics demo, but it is not responsive yet for the player.  Other than re-sizing the browser window, the user has no way of interacting with our program.  It's time to remedy that!  What we will do in this post is add a simple keyboard input file to our project.  When we've done that, our demo (future game) will respond to keyboard key-presses in real time.   

You may recall back when we added the Three.js library file to our project, I said that it makes our 3D game programming lives much easier.  Three.js takes care of the not-so-fun raw GL graphics / math initialization for us, so that we can focus on the fun stuff.   However, Three.js is meant to only aid in 3D graphics rendering inside the browser.  It purposefully leaves out libraries of keyboard/mouse/touch input, AI, networking, game physics, audio, and other various components that we might need for our future games.  If it DID include all those, the Three.js library would grow into a huge, unmanageable file that would be slow to download over the internet for people wanting to try our games. Rather, Three.js focuses on just the rendering so that you, the programmer, can add in different components only as you need them.  This makes your game projects just big enough to work correctly, but not too big because of unused features and unnecessary components.  And everyone's component needs will vary, depending on their type of project, so this makes sense.

So now we have to add in our own keyboard input code.  We could try this from scratch, but why re-invent the wheel?  If someone has already coded it and packaged it up nicely for you, why not use it?   There are all sorts of additional helper files and functions floating around out there on the internet that are meant to be used in conjunction with Three.js.  These smaller, specialized files take care of various tasks that aren't inside the main Three.js library (like handling user keyboard input).  In fact, one such brave programmer has made it his mission to help everyone in their Three.js journey.  His name is Jerome Etienne from Paris, France.  He has been kind enough to create and share small helper files for Three.js and game programming in general.  And one of these files is devoted to Keyboard input, which we need.  

Click on the following link and after the new page loads, Right-Click on the webpage and Save As... 'threex.keyboardstate.js'  . Save this inside the 'js' folder where our 'tempGame.js' and 'three.min.js' are already located.  

threex.keyboardstate.js

Now you should have a total of 3 different .js (JavaScript) files in your 'js' folder : tempGame.js, three.min.js, and now threex.keyboardstate.js  .  This last file we just added to our project is kind of like a 'plugin' for game programming in the browser.  Thanks to Jerome's fine work, his helper file will allow us to query the keyboard for keypresses in real time.  We can then use this information to move the characters/objects in our game and make it truly interactive!

We haven't changed our old 'example01.html' file in a while; but we need to update it now because the browser has to be able to locate the new 'threex.keyboardstate.js' file that we just added to our project.   

Do you remember how to include a new JavaScript file in html?  We do it with the 'script' tag like this:      
<script src="js/threex.keyboardstate.js"></script> 
With the addition of the above line, we will have 3 different lines with the script tag, each loading in a different component (or dependency).  At this point, it's worth noting that the 3 script lines must be placed in the right order, otherwise we'll get errors in the browser console and our game won't work correctly.  Take a look below at our new example01.html file and how I ordered the 3 script tag lines: 

<!DOCTYPE html>
<html>
   <head>
      <title> Hello Three.js </title>
   </head>
   <body>
      <script src="js/three.min.js"></script>
      <script src="js/threex.keyboardstate.js"></script> 
      <script src="js/tempGame.js"></script>     
   </body>
</html>

If you follow each script line from top to bottom, this .html code first loads the most important library, 'three.min.js' .  Then it loads our new helper file, 'threex.keyboardstate.js'.  Lastly, it loads our 'tempGame.js' which holds the future game code that we are writing.  This file is dependent on the other two .js files to be already in place, so it must be loaded last.

While we have this .html file open, there's one more thing I want to add.  It will be a line of text that is placed at the top of our webpage that instructs the user what to do, and what keys to press.  It is like a mini help file and I really appreciate when others add it to their demos/games, so we will do it too.

Here's how to add a line of text using the html 'div' tag:
<div id="help" style="position:fixed; left:40%; top:4%; color:grey;"> 
         Press W, A, S, and D keys to move the cube 
</div>
This 'div' (short for 'division') element has an id named "help".  I chose that because it is descriptive of what this 'div' does.  Next we set the style by typing style = " " and inside the double quotes we put key:value pairs.  First comes the key/attribute followed by a colon ( : ) and then its value.  A semicolon ( ; ) follows each pair.  Then comes the next pair and so on.  The first key:value pair is 'position:fixed;' .  This sets the text to a fixed position on the webpage so that it is in relation to the browser window itself and not some other element on the page.  Next comes 'left:40%;' which pushes the left margin of the text over to 40% the width of the webpage (which is almost half way over).  Similarly, 'top:4%;' pushes the top margin down just a little, 4% down the webpage.  Finally, 'color:grey;' sets the text color to a stock grey.  
On the next line we type 'Press W, A....' which is the actual content of our text.  These instructions will make it more clear what to do for first-time users.  

Here is the updated example01.html file with our new 'div' text element and our new 'script' tag line which includes the keyboard helper library to our project: 

<!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>

      <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 actually use this new keyboard helper in our tempGame.js file.  We will then have a truly interactive demo!  See you in the next part!

(Continued in part 2)...