Level-Up Coding

Level 5: Objects and Functions can organize your programs

You have seen objects and functions before. In a previous level we put all of the properties of the game in an object called gameState, and we have been using functions all along. In this lesson, you will learn more about how to use objects and functions to organize your program better.


Objects are like bins or boxes

Remember that an object variable in Javascript is like a bin or box that can hold variables with names. The variables inside an object are called its properties. Some of the properties can themselves be objects. This should not be a big mystery. It’s like having a small bin with a label on it. You can put a few things in the small bin, and then put the small bin into a bigger bin.


Accessing object properties

In Javascript, you can access object properties in two ways. The first way is called dot notation, and you have seen it before. To use this notation, you type (1) the name of the object, (2) a dot, and (3) the name of the property inside the object. For example ship.x accesses the x property inside the ship object.

A second way to access object properties is to use square bracket notation. To do this, you type (1) the name of the object, (2) square brackets [], and (3) inside the square brackets type the property in quotes. For example ship["x"] does the same thing as ship.x.

One difference between the two access methods is that square brackets allow you to use spaces and symbols in the property name, but dot notation does not. So, for example, “My Property!” can be used with square bracket notation, but not dot notation. Usually, we like to use dot notation if we can because it is simpler.


“Sprite” objects

Many 2D games work by moving little images around the screen. These images are often called sprites. To organize the code for a game like this, it is a good idea to put everything about a sprite in an object variable. Sprites may include a large number of properties, such as a reference to one or more images, a position (x,y), the width and height (w,h), and the velocity (vx,vy), which is the speed that the sprite moves in both the x and y directions. Already, that’s seven properties!


Making sprites with functions

Suppose that you want to have two sprites in your game, a ship, and a robot. To make the ship sprite and put it into the gameState you could write something like this:

    var ship = {};
    ship.image = document.getElementById("shipImage");
    ship.x = 10;
    ship.y = 10;
    ship.w = ship.image.width;
    ship.h = ship.image.height;
    ship.vx = 0;
    ship.vy = 0;
    gameState.ship = ship;

That’s nine lines of code. To make the robot, you would have to add nine more lines of code that look almost the same! To get rid of the repeated code you can write a function that will make a sprite object and return it:

    var makeSprite = function(imageName, x, y, vx, vy)
    {
        var sprite = {};
        sprite.image = document.getElementById(imageName);
        sprite.x = x;
        sprite.y = y;
        sprite.w = sprite.image.width;
        sprite.h = sprite.image.height;
        sprite.vx = vx;
        sprite.vy = vy;
        return sprite;
    }

This is slightly longer than before, but it can be used any time you want to make a sprite. In fact, once the makeSprite function has been set up, you can use just two lines make the ship and robot add them to the gameState:

    gameState.ship  = makeSprite("shipImage",  10,  10,  0, 0);
    gameState.robot = makeSprite("robotImage", 250, 250, 0, 0);

With this change the code is not only shorter, it is easier to understand. This is a good example of the principle of code reuse, writing your code so that parts can be used multiple times to to make it shorter and easier to understand. If you continue in programming, you will learn how to initialize object variables in another way using constructor methods.


Drawing sprites with a function

Now that all of the information about the sprites is stored in an object, you can put the code to draw a them in a function. Remember that the code for the previous level drew a sprite using two lines of code, similar this:

    var s = gameState.ship;    
    drawing.drawImage(s.image, s.x, s.y, s.w, s.h);

This code is fine, but to use it a programmer has to write two lines and remember that the drawImage function has 5 parameters. The order of the parameters also has to be right. To make things easier, you can write a function to draw a sprite, and you only have to remember the details of drawImage once. The drawSprite function looks like this.

    var drawSprite = function(s)
    {
        drawing.drawImage(s.image, s.x, s.y, s.w, s.h);
    }

What this function does is to draw the sprite object that is passed in to the function as a parameter. Parameter passing is powerful because it allows the function to draw any sprite. To draw the ship and the robot with the drawSprite function, you simply write

    drawSprite(gameState.ship);
    drawSprite(gameState.robot);

That’s a lot shorter! We had to do some work at the beginning to set things up, but when we added another sprite, it was easy to draw. One thing to notice about the code above is that when it calls drawSprite it does not use the name s. Instead, it uses gameState.ship and gameState.robot. This is an example of parameter renaming, which just means that the object that you pass in to a function does not have to have the same name as the parameter. This is very useful if you want to run the function for different objects.

The drawSprite function also demonstrates the concept of encapsulation that was talked about in an earlier lesson. That is, whenever possible, you should hide the messy details of the code so you don’t have to remember them.


Indentation and camelCase

You may have noticed that in the makeSprite and drawSprite function, we indented the code inside the curly braces. As we talked about earlier, this helps the programmer to understand the code. Usually, when we indent it means that something is logically inside something else. Besides functions, some other things normally have indentation, including if statements and loops.

Another thing we have done in the code examples is to use camelCase for variable and function names. Remember that in camelCase you capitalize the first letter in each word, starting with lower case for the first one. For example: amazeYourFriendsWithCamelCase.


Moving sprites with a function

Moving sprites is a another good place to use a function. The function below moves a sprite, and also causes to wrap around horizontally on both the left and the right.

    var moveSprite = function(s)
    {
        s.x += s.vx;
        s.y += s.vy;

        if (s.x > canvas.width) 
        {
            s.x = -s.w;
        }
        if (s.x < -s.w)
        {
            s.x = canvas.width;
        }
    }

Now, we can replace the code that moves the ship in animateGame with the function call:

    moveSprite(gameState.ship);

This is much shorter than before because all of the complicated code to wrap around is packaged up, or encapsulated in the moveSprite function.


Code refactoring

Often when you write code the first time it is inefficient and bulky. Rewriting code to be simpler without changing what it does is called refactoring. Refactoring can be very useful because it helps keep your code readable and clean.


Terms


Exercise: Code refactoring

Refactoring your code can make it a lot easier to work with. For this exercise, you will refactor the code from the previous level (level4example.html) to make it use objects and functions to manage sprites:

  1. Copy the example code from the previous level and save it as an HTML page in its own folder. (Or, start with the final HTML page that you made for the previous level.)

  2. Write or copy the makeSprite function onto the page. Put it inside the <script> element, but not inside any other function.

  3. Write or copy the drawSprite and moveSprite functions onto the page.

  4. Change the code that makes the ship sprite (in window.onload) to use the makeSprite function.

  5. Change the code that draws the ship sprite (in drawGame) to use the drawSprite function.

  6. Change the code that moves the ship sprite (in animateGame) to use the moveSprite function.