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. 
        

      No comments:

      Post a Comment