Saturday, January 7, 2012

KAPOW! Tile Creation Part: Three

Now, we want to use the "cell" array to place the tiles on the page.

We need to create a function called "placeTiles"...

function placeTiles(){}

This function will go through the array "cell" and check the value of "bgImage".  If "bgImage" == undefined, we will do nothing, if it is defined, we'll place a tile and use the properties of the cell to determine the style of the div element we create.

Create a <div> with JavaScript
To create an element, we use "document.createElement()" like so:
var newDiv = document.createElement('div');
  This will store a <div> as the variable "newDiv".  Now with our new div element stored as this variable, we can change the attributes of it and the style of it pretty easily.

However, for this example we only want to create a div element if the bgImage property is set.  This means we need to cycle through the items in cell and check the property bgImage.
for(idNum in cell){}
This FOR LOOP will cycle through all the cells and return idNum.  idNum is the index of each item and do the code in {}'s for each item.

Currently, this FOR LOOP does nothing... let's change that.
for(idNum in cell){
     if(cell[idNum].bgImage != undefined){}
}
Now, we're checking the bgImage property of cell[idNum] for each idNum that is returned by the FOR LOOP.
!= is the same as saying NOT EQUAL
If this if statement returns true, it will execute whatever code in the {} following it.


What do we do if the bgImage is defined?
This is when we want to create our <div>.  And set a variable for the "id" of the div that is more easily read.
for(idNum in cell){
     if(cell[idNum].bgImage != undefined){
     var newDiv = document.createElement('div');
     var dId = idNum;
  }
}
setAttribute()
We'll use "setAttribute()" to change the attributes we'd normally set in html like this
<div id="someID" class="myDivClass">
This becomes:
newDiv.setAttribute('id',dId);
newDiv.setAttribute('class','gamePiece');
We can use this for any of the attributes we want to change that would normally be written into the HTML to modify the object.

Notice that 'id' and dId are seperated by a comma and not an =.  This is because setAttribute() is a function and 'id' is the attribute we want to pass to it as an argument of the function.  And dId is another argument the function uses to set the value of 'id'.  Make sense?

newDiv.style
"style" is a property of newDiv.  This property is an associative array of properties that can be set and are interpreted as CSS3.
newDiv.style.backgroundImage = "url('images/" + cell[idNum].bgImage + ".png')";
Here, we're setting the backgroundImage property of style to "url('images/...png')";

If we were going to write the style in CSS3, it would be written as follows:
background:"url('../images/purpleTile.png')";
But remember, the url is being set relative to the document and not the CSS3 document linked in the HTML file.  SO we don't need the "../".  Also, the image is going to change based on the property in bgImage, so we need to concatenate the string with that property.  To do this, we use the + symbol.

When you use + against two strings like "dog" and "cat" it will make the string "dogcat" so you need to add spaces explicitly. "dog" + " " + "cat" will return "dog cat".  if you want to add a string to a variable you do "dog" + variable + "cat".  If the value of variable is hamburger, you will get "doghamburgercat".  So you have to add the spaces if you want them like in the previous example.  In this example, we're adding the bgImage property, so that's why we're using:
"url('images/" + cell[idNum].bgImage + ".png')"
If the value of bgImage is purpleTile it will return:
"url('images/purpleTile.png')"
Try it yourself in the console.  Set some variables to strings, then add them together like you would numbers.

function placeTiles(){}
function placeTiles(size){
    var gF = document.getElementById('gameFrame');

    for(idNum in cell){
        if(cell[idNum].bgImage != undefined){
        var newDiv = document.createElement('div');

        var dId = idNum;

        console.log(dId + ": current Div ID");
        newDiv.setAttribute('class', "gamePiece");
        newDiv.setAttribute('id', dId);

        newDiv.style.margin = "0px";
        newDiv.style.backgroundImage = "url('images/" + cell[idNum].bgImage + ".png')";
        newDiv.style.backgroundPosition = "0px, 0px";
        newDiv.style.backgroundSize = "100% 100%";
        newDiv.style.position = "absolute";
        newDiv.style.width = size + "px";
        newDiv.style.height = size + "px";
        newDiv.style.top = (cell[idNum].row * size) + 21 + "px";
        newDiv.style.left = (cell[idNum].column * size) + 21 + "px";
        newDiv.style.zIndex = 100;

        gF.appendChild(newDiv);       
        }
    }
}
Setting CSS3 Properties through JavaScript
Notice style.width and style.height for a second.
   newDiv.style.width = size + "px";
   newDiv.style.height = size + "px";
We're taking the argument size that is passed when the placeTile() function is called, and adding the string "px" to it.  But why?

This is because CSS3 doesn't know what numbers mean without context.  So you have to tell it how to interpret those units.  All the values that are passed to the style of an object MUST be strings, otherwise they won't do anything.  Passing 12 to your CSS3 will result in nothing happening.  Passing "12px" or "12%" for instance is something the CSS3 can interpret.

Setting the .top and .left properties
When we first set the properties of our tile in the "cell" array, we gave it information about the x and y position.  A number between 0 and rows = y and a number between 0 and columns = x.  These values are used to get the pixel position of our tile.

We do this by multiplying the row and column by the size of the tile.
   newDiv.style.top = (cell[idNum].row * size) +"px";
   newDiv.style.left = (cell[idNum].column * size)+"px";
But if we only do this, we'll be off because we have to account for our padding.  This can be kinda tricky, but for now I simply added 21 to the top and left value because i know the padding value of the gameFrame div is 20.  This could be acquired by getting that element, and it's style data and grabbing the .padding value of that, but It's a constant value, and it's a known value, so i didn't bother.  If your padding value is 0, don't worry about this part... or if your value of padding is different you may want to get this number dynamically, or grab it from your own .css file.
newDiv.style.top = (cell[idNum].row * size) + 21 + "px";
newDiv.style.left = (cell[idNum].column * size) + 21 + "px";

var gF = document.getElementById('gameFrame');
 This variable is set to define the parent of our new div.  'gameFrame' should be a <div> in your HTML file.  This div will contain your game objects and canvas.
     <div class="container" id="master">
         <div class="content" id="gameFrame">
             <canvas class="gameScreen" id="background" width="1024" height="768">
             </canvas>
         </div>
     </div>
Now that we have our variable defined for the parent element, we use appendChild() to add the new <div> to the page.

gf.appendChild(newDiv);

after you've set all the style rules and attributes of your new div element, all that's left to do in this function is to call "appendChild()" and pass the argument of your "newDiv" variable.  This will add the <div> element you've created and stored to the actual page, and make it visible to the user.

Calling placeTiles()
All that's left to do for this part is to call, "placeTiles()" in your init() function, just after "createTiles()".  You also need to pass the variable tileSize, which will be stored in the size argument of placeTiles();
function init(){
    tilesRange = 10;
    tileSize = 64;

    window.canvas = document.getElementById('background');
    window.ctx = canvas.getContext("2d");

    window.screenWidth = canvas.width;
    window.screenHeight = canvas.height;

    console.log("Setting screen width to:" + " " + screenWidth);
    console.log("Setting screen height to:" + " " + screenHeight);

    tilesWidth = screenWidth/tileSize;
    tilesHeight = screenHeight/tileSize;

    createTiles(tilesWidth, tilesHeight, tilesRange, tileSize);

    placeTiles(tileSize);
}
That's it for now.  Next we'll talk about transitions!

At this point you should be able to see the tiles, they don't do anything, but that's ok.  We'll do interaction in the next post.

KAPOW! Tile Creation Part: Two


Now, we want to actually store the array of data to be used to create the visible tiles.

In our nested for loops we'll be replacing "//code to create tiles" with a block of code that will fill each item in the array with the following information:
The cell's ID
The background Image associated with it
The Row number
The Column number
So let's get started...

    for (y=0;y<tH;y++)
    {
        console.log("Row :" + y);
        for (x=0;x<tW;x++)
        {
            //console.log("Column :" + x);
            if (x > 0 && x < (tW - 1) && y > 0 && y < (tH - 1))
            {
               
                var randomnumber=Math.floor(Math.random()*tR);
                if(randomnumber > 2 )
                {
                    cell[i] = {'id':i , 'bgImage':undefined, 'row':y, 'column':x};
                }
                if(randomnumber == 0)
                {
                    cell[i] = {'id':i , 'bgImage':'orangeTile', 'row':y, 'column':x};
                }
                if(randomnumber == 1)
                {
                    cell[i] = {'id':i , 'bgImage':'blueTile', 'row':y, 'column':x};
                }
                if(randomnumber==2)
                {
                    cell[i] = {'id':i , 'bgImage':'greenTile', 'row':y, 'column':x};
                }               
            }
            else
            {                   
                    cell[i] = {'id':i , 'bgImage': undefined, 'row':y, 'column':x};
            }
            i++;
        }
    }
This is code we'll put in the second for loop.  Let's break this down.
if (x > 0 && x < (tW - 1) && y > 0 && y < (tH - 1)){}
This is our if statement that checks which row and column we're in.  It also makes sure we haven't reached the end of the game screen.  This IF statement will basically create a border of tiles around the "game space"... these cells can be used for UI elements or whatever, but for this example it just creates space between the outer border of the screen and the tiles, but in a way that allows us to change the attributes later.
x>0 && x<(tW -1)
These comparative statements check that X is greater than Zero, &&(and), X is less than the number of tiles accross (minus one).  The reason we use tW - 1 and >0 (instead of greater than equal to ) is because we don't want to add cell data for the outer rows/columns... this will create a border effect.  If we wanted to fill the whole game screen with tiles, we would simply drop the -1 and check if x is greater than equal to zero.
  y > 0 && y < (tH - 1)
This is the exact same thing, except done for the Y axis.  And basically says if you're not at the end of the page (minus the border), keep going... if you've reached the end of the game screen, stop.
&& means AND
then everything between {} is what will be executed if all conditions are true.  If we replaced && with || we would be doing OR...
|| means OR
Which would return true if ANY of the conditions are true... very important later.

the ELSE to this IF catches all the cells on the border.
    for (y=0;y<tH;y++)
    {
        console.log("Row :" + y);
        for (x=0;x<tW;x++)
        {
            //console.log("Column :" + x);
            if (x > 0 && x < (tW - 1) && y > 0 && y < (tH - 1))
            {
                     //animated tile spaces
            }
            else
            {                   
                    //border tile spaces
            }
        }
    }
Now that we're correctly cycling through our cells, we need to add items to our array...  But what is our array?
function createTiles(tW, tH, tR, size) {   
    window.cell = new Array();
...
 The first thing our "createTiles" function needs to do (apparently), is create the array.  Otherwise, where would we store this data we're creating?
window.cell = newArray();
This creates our empty array.

Now that we have our array... we can go back to our IF statement and fill this array with data.
cell[i] = {'id':i , 'bgImage':undefined, 'row':y, 'column':x};
Remember our associative array example?  if not:


Understanding how associative arrays work,  we can see that cell[i] is given the following properties and values:
id = i (our current cell increment)
bgImage = undefined (we don't have an image yet)
row = y (our current row)
column = x (our current column) 
 But if you look closely you'll see there's this variable "i" that isn't defined.

What is I?
"i" is basically a one off variable used to increment something... this could be any name, but is most commonly "i".  If you're looking at someone's code and there's "i" being used, they're most definitely incrementing something somewhere... usually within the function or object and "i" is rarely a global variable... because it's not descriptive at all.

Now, go back to the beginning of your function (just outside of the for loops) and create the variable "i".
var i = 0;
This will set "i" to zero every time the function is called.


Incrementing
incrementing a number is easy.
i++;
What's not so straight forward, is when to increment.

I usually always increment AFTER I've used the number.

So in this case, it makes sense to create the cell with i being 0 and then increment it after the array is populated.

cell[i] = {'id':i , 'bgImage':undefined, 'row':y, 'column':x};
i++;
Now, every time the if statement is true, it will create the cell with the current value of "i", and then increment "i" so long as we are still in the "FOR LOOP".

Now, all of our tiles are being created with the background image 'undefined'.  Let's change that.  Here's the tiles:






Save the images, or create your own, it doesn't matter.
I saved these in my "images" folder of this project.

Let's change "undefined" to "purpleTile";
cell[i] = {'id':i , 'bgImage':'purpleTile', 'row':y, 'column':x};
 Now, every cell is being created with 'purpleTile' set to the bgImage property.

But we have 4 different tiles, how do we randomly place those in the array?



Randomization
Math.random()
This will return a random number that looks like this
 0.9849653994339609
Notice that it's less than 1.  This is because a random number generated with a value greater than one wouldn't be very useful, because you would then have no real control over it.

That's why we do:
Math.random() * range
if range = 10, then the number returned would be something like:
9.849653994339609
This isn't very useful for us either, because of all the decimal points... we don't need all that noise, so we want to drop everything after the decimal by doing this:
Math.floor(Math.random() * range)
This will return 9 if range is 10.
But this is only useful if we assign it to avariable
var randomnumber = Math.floor(Math.random() * range);
Now that we have our random number we can use it to change the properties we put into cell with an IF statement.
if(randomnumber > 2 )
{
     cell[i] = {'id':i , 'bgImage':'purpleTile', 'row':y, 'column':x};
}
This will make every instance where randomnumber is > 2 set to the defined properties.

What if it's not greater than 2?

if(randomnumber > 2 )
{
     cell[i] = {'id':i , 'bgImage':'purpleTile', 'row':y, 'column':x};
}
else
{
     cell[i] = {'id':i , 'bgImage':undefined, 'row':y, 'column':x};
}
So now, if the randomnumber variable is < 2 we'll fill it with a cell who's background image is undefined.

If the random number is equal to a specific value, we can set it to a specific style.  For instance, if randomnumber = 0 and we want all instances of 0 to be the orange tile.
if(randomnumber > 2 )
{
      cell[i] = {'id':i , 'bgImage':'purpleTile', 'row':y, 'column':x};
}
if(randomnumber == 0)
{
     cell[i] = {'id':i , 'bgImage':'orangeTile', 'row':y, 'column':x};
}
else
{
      cell[i] = {'id':i , 'bgImage':undefined, 'row':y, 'column':x};
}
The ELSE case in this example is not going to be used in the final function, as we have a tile for all the cases we will be checking for in the final version, but the example is still valid for understanding how this IF statement works.  If you wanted to keep it in, you could, but it would never be fired in the final example code.

Notice that instead of saying "if(randomnumber = 0)"... where the = is, we instead use ==... this is because == says, "is equal to" and = says, "is" which are two completely different things...

randomnumber = 0 will do two things... First it will set the variable randomnumber to 0, and then will return true.

So, not only are you getting a false positive, but you're resetting "randomnumber" to be 0.


Never forget 
= is
== is equal to
Next, we'll go over how to turn this into actual Div elements in your HTML page.


function createTiles(){}

Here's our function for createTiles()
function createTiles(tW, tH, tR, size) {   
    window.cell = new Array();
    window.visableCells = 0;
       
    var i = 0;

    for (y=0;y<tH;y++)
    {
        console.log("Row :" + y);
        for (x=0;x<tW;x++)
        {
            //console.log("Column :" + x);
            if (x > 0 && x < (tW - 1) && y > 0 && y < (tH - 1))
            {               
                var randomnumber=Math.floor(Math.random()*tR);
                if(randomnumber > 2 )
                {
                    cell[i] = {'id':i , 'bgImage':undefined, 'row':y, 'column':x};
                }
                if(randomnumber == 0)
                {
                    cell[i] = {'id':i , 'bgImage':'orangeTile', 'row':y, 'column':x};
                }
                if(randomnumber == 1)
                {
                    cell[i] = {'id':i , 'bgImage':'blueTile', 'row':y, 'column':x};
                }
                if(randomnumber==2)
                {
                    cell[i] = {'id':i , 'bgImage':'greenTile', 'row':y, 'column':x};
                }               
            }
            else
            {   
                     cell[i] = {'id':i , 'bgImage': undefined, 'row':y, 'column':x};
            }
            i++;
        }
    }
}

Now we can use the array "cell" to create our tiles!  Let's do that in Part Three!
Hope this was helpful!

Thursday, January 5, 2012

KAPOW! Tile Creation Part: One


So when making this animation demo, I knew I wanted to have a simple animation test with tiles sinking into the center of the screen when I clicked on them.
So, the first thing I had to ask myself is, "How do I create the tiles?"...
This was an easy question to answer by Googleling "Tile based game engine development" and reading up on how tile engines work. 
Which made me realize I needed to ask a bunch of questions.
  • How big are my tiles?
  • How do I make rows and columns in JavaScript?
  • How do I display the tiles?
  • How do I tell the tiles where to be displayed?
These are just some of the questions I had to answer...  So let's get to work.

How big are my tiles?

Well, in order to know this, I must know how big my display area is.
So, in this particular case, my "screen" is 1024 x 768 pixels.
I chose this because it's the resolution of the IPad, and seems to be a good display size, in addition to it being divisible by power of 2 numbers on both x and y.
768/64 = 12
1024/64 =  16
So, I know if my tile size is 64px wide and tall that I will have 12 rows. (horizontal bars) and 16 columns (vertical bars).
So, let's try a tile size of 64px 64px
Ok, so now my tile size needs to be determined by deciding how many different pieces I want to break the screen into.

How do I make rows and columns in JavaScript?

This too turned out to be the wrong question, because when trying to answer this question, I wasn't able to identify "Rows and Columns of... "

Rows and Columns of tiles?
But what's a tile?
    AH! That's the next question... "What is a tile?"

    What is a "tile"?

    As it turns out, there are two types of tile in the game, or rather two parts of what makes up a tile.

    The visible tile.
    The game data associated with the tile.
      Figuring out what the placeholder tiles would look like was easy.  It didn't matter at this point what they look like, we just wanted to place them on the screen.
      To do this, we needed to know where the tile was on the screen, and this requires the "game data".
      I refer to this as "game data" but that doesn't really mean anything, it's the list of attributes that we will use to position and interact with the object.  The "game data" is basically a set of rules and attributes that can be changed when the user provides some kind of stimulus to the visible element of the "tile".
      All of this information is stored in an array, which each contain their own associative array.


      An Array
      array[0] = "dog";
      array[1] = "cat";
      array[2] = "fish";
      So, in the above "favourite foods" array, we can see that the array's name is simply "array" and that there are 3 items in the array, and the index of the array starts at 0.  The first item (item 0) is set to the string value "dog", and so on.
      So, this is an example of a single dimensional array. 
      the variable "array" isn't very discriptive, so let's change that.
      pet[0] = "dog";
      pet[1] = "cat";
      pet[2] = "fish";
      Hrmm... still not very useful, why do we care about pets in this array?
      Let's give these pets an owner.  To do this, we want to make pet a property of a person.
      person[0] = {'pet':'dog');
      SO now we can say that person number 0 owns a pet and that pet is a dog.  (lot's of assumptions here).
      But who wants to be named person zero?
      person[0] = {'name':'Gilbert' , 'pet':'dog'};
      Ok, now person[0].name == "Gilbert" and his pet is a "dog".
      I think Gilbert likes cats too... so...
      person[0] = {'name':'Gilbert' , 'dog':true, 'cat':true, 'fish':false};
      But we don't need to do that... if fish is not a property of gilbert, we simply don't need to set that property, and instead can assume that if person[0] has a fish property, then they also have a fish.
      Probably best to set the property to undefined just to make your code neater.
      With that, we can instead, name our pets with the dog and cat property.
      person[0] = {'name':'Gilbert' , 'dog':'Fred', 'cat':'Garfield'};
      Maybe not the best example, but let's go with it.

      This is known as an associative array.  Which I plan to use a lot of. (I think)
      For more information on arrays in JavaScript please google : "JavaScript Arrays"



      Creating Our Tile Data 

      What do we know?
      -Our tile size - 64px 
      -Our screen width - 1024px
      -Our screen height - 768px
      -There are 16 columns on the screen
      -There are 12 rows on the screen
      Ok, so let's create tile data using this information.
      The columns tell us how many tiles are in a row.
      And for each row we need to add that many tiles until we have added all of our rows.
      We'll do this using nested for loops.
      for(y =0;y<12;y++)
      {
           for(x =0;x<16;x++)
           {
               //code to create tile data;
           }
        }
      This basically says, "for every row and every colum we want to eventually create a tile".
      This unfortunately assumes that the number of columns and the number of rows will not change.  This is a false assumption.  So how do we make this for loop scalable?

      By using variables!
      for(y =0;y<rows;y++)
      {
           for(x =0;x<columns;x++)
            {
                  //code to create tile data;
             }
      }
      These are not the variable names I actually use in the code.
      To get the value for rows and columns we need to simply do the math in JavaScript:
      var rows = screenHeight / tileHeight;
      var columns = screenWidth / tileWidth;
      Hrmm, what is screenHeight and screenWidth?  
      We need to get the width/height of an element on the page.
      To do this:
      var screenWidth = document.getElementById('background').width;
      var screenHeight = document.getElementById('background').height;
      Ok, that's good, but we can do this cleaner by assigning the object to a variable.
      var gameScreen = document.getElementById('background');
      var screenWidth = gameScreen.width;
      var screenHeight = gameScreen.height;
      Now that we have our screen width, the whole thing looks like this:
      var gameScreen = document.getElementById('background');
      var screenWidth = gameScreen.width;
      var screenHeight = gameScreen.height;

      var rows = screenHeight / tileHeight;
      var columns = screenWidth / tileWidth;
      But what is tileWidth/tileHeight?


      Well, for this example they represent the size, and we can assume that the two values will be the same.  So we don't need to have two variables.  Instead we can use one variable "tileSize":
      var tileSize = 64;
      var gameScreen = document.getElementById('background');
      var screenWidth = gameScreen.width;
      var screenHeight = gameScreen.height;
      var rows = screenHeight / tileSize;
      var columns = screenWidth / tileSize;
      And what's this 'background' we're getting?

      This is the 'background' element that should be placed in your html:
      <canvas class="gameScreen" id="background" width="1024" height="768"></canvas>
      So where are we? 
      • We have defined our tile size variable
      • We have identified the width and height of the screen by grabbing this from the 'background' canvas
      • We've determined how many cells are in a row
      • We have determined how many rows are on the screen.
      • We have created a set of nested for loops to cycle through the rows and columns to eventually create a tile for each one.  
      Now what? (Putting it all together)


      Probably should have done this first, but let's make functions out of these things.
      Usually best to put anything that can be considered it's own operation in a separate function and only call it when you need to actually do the task.
      We only need to get the dimensions of things (for instance) when the demo starts.

      So let's take all that jazz and put it in init();


      function init(){}
      function init(){
           tileSize = 64;

          window.canvas = document.getElementById('background');
          window.ctx = canvas.getContext("2d");

          window.screenWidth = canvas.width;
          window.screenHeight = canvas.height;
          console.log("Setting screen width to:" + " " + screenWidth);
          console.log("Setting screen height to:" + " " + screenHeight);
          tilesWidth = screenWidth/tileSize;
          tilesHeight = screenHeight/
      tileSize;
          createTiles(tilesWidth, tilesHeight, tileSize);

      }
      Wait a minute... there's a bunch of extra crap in there!
       That's because we're doing things a bit differently and it's nice to see it all together before jumping into it!
      The first thing you'll notice that's different is that the variable names are different.
      This is because if we're going to grab a constant element like 'background' we can assign it to a variable that we can reference later, and often!
      canvas is going to be a very important variable later on... and that's what the 'background' is anyway, so why not just assign the background canvas element to the 'canvas' variable?
      Notice the variable "canvas" is prefixed with "window.":
      window.canvas
      This is because we want to reference window from any function later on.  So we set the variable as a child of the "window".  This makes it a "global" variable. 
      Avoid using global variables where you can.  In this particular case it will make things easier later.
      The following line is added for intrigue and will be discussed later:  Feel free to google it!
      window.ctx = canvas.getContext("2d"); 
      any time you see console.log it's usually debug code.  Here I write to the console the variable screenWidth with a message so that I can know what it's being set to.
      After we've gotten all our variable set, we then execute the function "createTiles" passing the variables to the new function:
      createTiles(tilesWidth, tilesHeight, tilesRange, tileWidth);
      function createTiles(){}
      function createTiles(tW, tH, size) {     
          for (y=0;y<tH;y++)
          {
              console.log("Row :" + y);
              for (x=0;x<tW;x++)
              {
                  //console.log("Column :" + x);
                  //console.log("Row:" + y);
              }
          }
      }
       This function will take the variables that are passed in (changing their names to their shorthand tW, tH, and size... )
      The script is just writing to the console the x and y value currently.  But only if you uncomment it.
      JavaScript Comments
      //anything with the two /'s will be a comment as long as it
      //is in the same line...
       use comments to give yourself information in your code, or to disable a line of code without having to delete it.

       Initialization
      To kick this whole hot mess off, simply put init(); in your onload script.  For information on how to setup an onload script, please see:


      In part two, we will go over actually creating the cells. 
        

      KAPOW! Prototype

      Over the past weeks I've been come to understand:
      • How to structure my code
      • Some optimization
      • Creating HTML elements dynamically through JavaScript
      • Positioning these elements
      • Changing attributes and CSS3 of dynamically created elements
      • Storing game related data to objects on screen
      • Using transitions to animate objects (Firefox, Chrome, Safari)
      • Using stored game data to restore original state of objects
      The result of all this can be seen here:




      The next post will go over exactly how all of this was achieved.

      This is a lot of fun, thanks to everyone for your support and enthusiasm!  All ideas and comments are welcome!

      Tuesday, January 3, 2012

      A Better Way To OnLoad

      There's certain people out there that will say using "onload" in the body tag is weak.

      To them, I say, "you're probably right..."

      Q:  But why?

      A: Because by doing this, you are mixing your JavaScript in your HTML.  This makes it somewhat difficult to debug sometimes, and it's just not "neat".  Also there's instances where using In-line function calls can cause script errors (haven't personally encountered this, but if the internet says it's possible, it must be true.) These are the only reasons I can find that would make using the <body onload="function()">  method a terrible thing.  So, as an exercise let's do it a different way.

      onreadystatechange

      This is the event we will use to detect when the state of the "document" has changed.  We need to know when the "readyState" is "complete"...  this means the document is completely downloaded and ready to be used.

      For more information on this please visit:
      This Page

      Moving forward:

      Any time the state of the document is changed, it will fire this event.  So we need to therefor tell the script to do something when this event is changed to "complete".

      To query this event:
      this.document.onreadystatechange = function() {}
      In the above line we are telling onreadystatechange to execute a function when this.document triggers the event.

      "This" - JavaScript Keyword


      This can be pretty tricky, but basically refers to the "owner" of the element or function.  For more information on "this" keyword, visit:

      This Page

      The Function 

      The following function can be used to check the ready state of the current document.
      this.document.onreadystatechange = function() {
           if(document.readyState == "complete") {
           }
      What's happening here?  First, we assign the function to the onreadystatechange event.  So when the onreadystatechange event is fired, it will then check to see if the document's ready state is equal to "complete".   readyState of course being a property of document (and many other things as defined in the previously mentioned resource):
      This Page

      Now What?

      Now, simply remove onload="HelloWorld()" from the <body> tag, and instead put HelloWorld(); between the {}'s inside your if statement that checks for the readyState.
      this.document.onreadystatechange = function() {

           if(document.readyState == "complete") {
                 HelloWorld();

           }

      }
      Where HelloWorld(); is your function you want to call on load.  This is usually something like init();...

      Conclusion

      In conclusion, there are more than a million ways to skin a cat.  Do which one works best for you.  I prefer to check the ready state of the document.  It allows me to execute a range of functions and set some important variables for later use.  For instance, I use it to set the transform method and anything else that is constant... this way, when the transform method of the browsers become more standardized, I don't have to go through all my scripts and change the transform method, but instead can change the function that sets the transform method for every function I've written by changing how the variable is set when the document is ready.

      Have fun!

      Monday, January 2, 2012

      Changing CSS with JavaScript

      Okay, let's do this!

      Now that we know the anatomy of a function, and we know how to format our elements using CSS, let's try to change the CSS formatting of an element using a function!

      We want to change the image that's displayed in a div.  So naturally, the first thing we need is 2 images.

       The Images

      Here they are:

      Save these images to your project folder.  Or make your own, it doesn't matter.  Be sure to save them in the "images" folder of your "htmlfolder"... the "htmlfolder" of course being the location of your html files you're working on.

      The HTML

      Now that you have your images, you need to put one of them on your page.

      To do this, make a div and give it the class of "clickTest" and the id of "imageTestA".

      <div class="clickTest" id="imageTestA" ></div>
      The CSS

        Now with your div in place, add to your CSS a block for the style of this div.

      .clickTest{
            background-image:url('../images/ButtonTestA.png');
            background-repeat:no-repeat;
            width:200px;
            height:200px;
            z-index:30;
            top:200px;
            left:600px;
            position:absolute;
      }

      Now with your CSS in place, check to make sure the image is loading and visible on the page.

      If it is not, check to make sure you have the image in the correct folder which is "htmlfolder/images/"

      Where "htmlfolder" is the path to your html file.

      The Function

      Once you have the image showing up, you need to create a function to change it.

      function clickImg() {
            var div = document.getElementById('ImageTestA');
            div.style.background = "url('images/ButtonTestB.png') no-repeat";
      }

      WOAH! WAIT A MINUTE!

      "What's going on here?" you ask?  Ok, I'll tell you...

      First, we need to declare our function:

      function clickImg(){}

      But before we can change an element on the page, we need to tell the script what the element is!

      To do this, you need to use "getElementById()"  which is a function built into the "document".

      Use "document" to get information from the page at any time.  This will be something you do a lot.

      So, what we're doing in this script is acquiring the element and storing it in a variable which is "div"

      var div = document.getElementById('ImageTestA');

      So here, you see our variable is defined, we execute the function getElementById(), which is a function of document, and we pass it the argument of ImageTestA; which is the id we gave our div.

      if we wanted to change div to something else, we could do that by simply saying

      div = somevalue;

      But we won't do that... not now anyway.

      After we've acquired our element and stored it in the variable "div", we can then change attributes of it.

      We do this by adding:

      div.style

      Style is an associative array of the element that stores the CSS of the element.

      This means, that by adding .background to the end of div.style we could change the background attributes of Style of the div...  Make sense?

      div.style.background = "url('images/ButtonTestB.png') no-repeat";

      This is the same as putting in your stylesheet:

      .clickTest {
            background:url('../images/ButtonTestB.png');
      }

      But why did we have to put the ../ in our stylesheet, but not in our script?

      This is because the script is executed from the document.  And the path to the image from the document is "images/ButtonTestB.png", while the path from the examplePageA.css to the image is "../images/ButtonTestB.png"... this is because in order for the css file to see the image, the css file has to go back a folder, and then into the images folder.

      You put that all together, and you get:


      function clickImg() {
            var div = document.getElementById('ImageTestA');
            div.style.background = "url('images/ButtonTestB.png') no-repeat";
      }


      Hopefully, that wasn't too confusing.


      If you load it up, the page should function like this one does:


      CLICK HERE TO SEE THE DEMO


      I hope you enjoyed this as much as I did.  Good luck!  I'd like to see if anyone does anything cool with this too, so if anyone is reading this, feel free to shoot me some urls with your work!


      Cheers,
      Don

      Sunday, January 1, 2012

      JavaScript Functions

      All of this is cool and fun and stuff, but without some way to manipulate these things based off the user's input, it's all kinda worthless.

      This is why we use JavaScript.  JavaScript provides websites with the ability to execute functions when an event is triggered.

      For instance, if a user were to click on an image, we could execute a function that would change the image within that <img> element.

      So, let's do that. But first, let's learn about functions!

      Creating A Function

      In your HTML file in the <head> portion, add a <script> tag.

      <script>
      </script>
      This will be where we put the script for our function.

      For our first script, let's do the generic "hello world!" script.

      Next we want to define our function.  To do this, add the following lines between the <script> tags.

      <script>
      function HelloWorld(){}
      </script>

      We're telling the browser that HelloWorld is a function.  with no arguments "()" and providing the {} tells the script what code to execute.

      Now that we have declared our function, we can start writing the script.

      For this script we will simply "alert" the user "Hello World!" when the script is executed.  To do this:

      function HelloWorld(){
           alert("Hello World!");
      }

      Notice that "Hello World!" is in quotations, this is because it is a string.

      Any time you put data in quotations, it is interpreted as a string.

      Also notice the ( ) that the "Hello World!" is in.  This tells the built in "alert" function what to execute as the alert.

      And finally, notice the semi-colon ";" that is placed at the end of the line.  This tells the browser when the task has ended.  Every time you get to a semi-colon in the script, this is when that line will be executed.

      Executing The Function

      If you run the document now, nothing will happen.  This is because, at no point have we told the script to be executed.  Functions are only executed when the event that triggers them is detected by the browser.  For this case, we want to do this when the page is loaded.

      There are several ways to do this, and many different schools of thought.  We will be going over these methods later on in more depth.  For now, simply go to the body tag and add: onload="HelloWorld"

      <body onload="HelloWorld()">

      onload is an event of document, which is available to all documents loaded in the browser.

      This will tell the browser to execute the function "HelloWorld" once the document is loaded.

      Try it!



      That's pretty good, but also, pretty worthless...

      Now that we know how to create a function.  Let's make a script file and pull the scripting out of our HTML document and save it to a .js file.

      First open a new document and copy paste your function (WITHOUT THE <script> TAGS) into this new document and save it as "javascriptTest.js" or whatever you'd like.  I'm going to save it as "common.js" for my actual page, or something similar because this is where I'll store my functions common to the rest of my site.  Save this file to a javascript or "js" folder.  Just remember where you put it.

      Now, you want to edit your <script> tag to read:

      <script type="text/javascript" src="js/javascriptTest.js">

      Of course, where you saved your file and what you saved it as will affect the src= portion of this, but if you're following along exactly with this article, you can do exactly this.

      Save your documents, and reload your page.

      Pretty cool, huh?

      Ok, if that doesn't work, make sure your documents look like these:

      HTML DOCUMENT:

      <!DOCTYPE HTML>
      <html>
           <head>


               <title>
                  Title of my project: A learning experience.
               </title>
               <link rel="stylesheet" href="css/examplePageA.css">
               <script type="text/javascript" src="js/javascriptTest.js"></script>
           </head>
           <body onload="helloWorld()">

           <div class="container" id="master">
               <div class="menu" id="leftMenu">
               </div>
               <div class="content" id="article01">
               </div>
           </div>


           </body>


      </html>

      JAVASCRIPT DOCUMENT:

      function helloWorld() {
          alert("Hello World!");
      }
      If for some reason this business still doesn't work for you.  You can comment here, and I'll be happy to help.  But It should work.