udacity3-1.png

Put the complete transcripts (taken from video subtitles) and screenshots here!
For original Notes, Mindmaps and Sketches please use the Notes page

Unit 0 - Optional HTML/Javascript crash course

Contents

Optional JavaScript crash-course

Introduction

Now, in this unit, we're going to be going over the basics of JavaScript, and how to interact with a modern browser. Things like manipulating the DOM, accessing JSON and making an XMLHttp request.

JavaScript Syntax

First thing we start off with, is we create a variable here called Render Engine Class. And you define a variable by starting with the key word var, and then the variable name. And then you say equals, whatever you would like it to be equal to. In this case we use an opening brace, to indicate that we'd like this to be a javascript object. This is the object literal notation, for defining an object in Javascript. From there we can declare Methods and member variables of that object by specifying the name of the member, a colon and then the value that its going to take. In this case we'd clear the method at the name draw String, we use a colon and then we say that this is going to be a function, we use the keyword function to define this and then we give a list of all the parameter. And then open the function with an opening brace. Similarly to how you would in many other languages. Now, again, we create a variable, name it, and then assign to it, the value of gRenderEngine.context. Now this is going to take the JavaScript object, gRenderEngine, and the dot notation is going to tell us that we want the context member. Of this object and we do something similar down here where we, grab the font member of the ctx object and assign it this felly here. Now you notice that we're using additional strain. Addition is over loaded in javascript to also designate strength encathnation Again similar to a lot of other languages you might have seen. Next we have an example of an if statement. If statements work essentially the same way that they would in any other language where you have the keyword if, then in parenthesis you specify the predicate that's going to return a bouillon value, either true or false and then the body of the if statement that executes If that evaluates to true. Now, note that the statement here doesn't have braces around it designating what executes if the if statement evaluates to true. This is similar to some other languages where, if there's a single line that you need to evaluate, you don't require those braces. We use this here there. Just for readability purposes. So you might see this in the code. Now for larger f blocks, you can delineate them with a opening brace, and then once they are done, way down here, you close them with another brace. From there we have a number more examples of declaring and setting variables. And then we come to a fore loop. Fore loops are structured essentially the way they are n c. The way we have An initializer value, then a predicate to evaluate for when the for loop should end. And then an expression to execute at the end of each for loop. And similarly, to you if blocks, the block of code that you'd like to execute each time the for loop runs, is delineated by braces. Although again, if it's a single line, you don't require those braces to be there. There. Now here we see words N where N is in square braces. And, again, N is the variable that our for-loop is looping over. This is an example of accessing a list, where N is the Nth element in that list. Now, like most other languages, this is zero indexed as well. So keep that in mind.

JSON

Now one important detail we'll need to understand is how to get our image data that our artists create into our game. For example, the image file name. Also the width and height of the image, whether it's been rotated at all and various other pieces of information we might need to know. Now to do this, we're going to use JSON, which is a very lightweight data interchange format that's based on JavaScript object syntax. Now, since it's based on JavaScript, it turns out that it's really easy to use with JavaScript. Much easier than, say XML. Now, here's an example of a JavaScript object, right here. Now, this is in JSON. Technically. It is the JavaScript object representation of JSON that has been passed in. The difference between a JavaScript object and JSON is that JSON is simply a string that we can then parse to create a java script object. And that's what we're going to do.

Inspecting JSON

Now, you see we have a parseJSON function that takes a string of JSON in, and then we're going to parse it using the JSON.parse method that is included in and accessible in just about any modern browser. JSON.parse takes this string. Weapon.JSON and will return a JavaScript object representing the JSON that it's parsed. Now, once we've done that what I want you to do is grab a specific piece of data, from that JSON. Let's say you want to grab data from the JavaScript object returned from JSON.parse and let's say you put that JavaScript object into the variable parsed. Now in order to grab the chaingun sub object then you'd simply say parsed frames chaingun.png like so and then put it in the variable chaingun lets say. Now lets take a look up above at this structure, what that would do it'd walk down on first on frames and then in the chaingun and what would be stored in the chaingun variable. Would then be this entire structure, so everything inside of chaingun.png. Now what I would like you to do, is grab the x data field inside spriteSourceSize of chaingun < u >impact.png.< /u > And go ahead and print that out to the console. chaingun < u >impact.png isn't up in< /u > our example JSON, but the JSON that we'll be running through this function does have it.

Inspecting JSON- Quiz

// This is an example of how the JSON would be structured.
// Note that chaingun_impact.png is not here.
//
// Note that this is an actual Javascript object, whereas
// JSON is a string that represents that object.

JSONExample = {
    "frames": {
        "chaingun.png": {
            "frame": {
                "x": 1766,
                "y": 202,
                "w": 42,
                "h": 34
            },
            "rotated": false,
            "trimmed": true,
            "spriteSourceSize": {
                "x": 38,
                "y": 32,
                "w": 42,
                "h": 34
            },
            "sourceSize": {
                "w": 128,
                "h": 128
            }
        },
        "chaingun_impact.png": {
            "frame": {
                "x":1162,
                "y":322,
                "w":38,
                "h":34},
            "rotated": false,
            "trimmed": true,
            "spriteSourceSize": {
                "x":110,
                "y":111,
                "w":38,
                "h":34},
            "sourceSize": {
                "w":256,
                "h":256}
        },
        "chaingun_impact_0000.png": {
            "frame": {
                "x": 494,
                "y": 260,
                "w": 22,
                "h": 22
            },
            "rotated": false,
            "trimmed": true,
            "spriteSourceSize": {
                "x": 113,
                "y": 108,
                "w": 22,
                "h": 22
            },
            "sourceSize": {
                "w": 256,
                "h": 256
            }
        }
    }
};

// The above is an example of how the JSON would be structured.
// Note that chaingun_impact.png is not here, we'll call your
// parseJSON function with the full JSON input.
//
// Note also that the above is an actual Javascript object, whereas
// JSON is a string that represents that object.
parseJSON = function (weaponJSON) {
    // First, use the JSON.parse function to
    // parse the passed in weaponJSON.
    //
    // Next, grab the 'x' data field within
    // 'spriteSourceSize' of 'chaingun_impact.png'
    //
    // After that, print this value to the console
    // and also return it.
    // YOUR CODE HERE

};

Inspecting JSON - Quiz Answer

Alright. So, this actually isn't too much code. So first, what we do is run weaponJSON through JSON.parse and then store the return JavaScript object into parsedJSON. From there, we print out to the console parsedJSON frames chaingun < u >impact.png spriteSourceSize finally, the x data field. < u / >

XMLHttpRequests

Alright. So now we know how to parseJSON data. How do we actually retrieve that from our server? This is where XMLHttPRequest come in. An XMLHttpRequest allows our Javascript code running in a browser to fire a request off to our server to a specific URL. We can also specify a function call once the server responds to our request. Now, there are a few steps to this. First, we need to create a new XMLHttpRequest object, and we do this using the new keyword, followed by XMLHttpRequest with the parentheses. Now, note that case is sensitive here. Next, we need to call the XMLHttpRequest open method. First, we need to specify the HTTP method to use. In our cases, we're pretty much always going to want to use GET. Second, we specify the URL to call out to. For example, if we were requesting a JSON file, we would specify the file name that we were interested in. Finally, we specify a Boolean value that is set to true if we want the call to be asynchronous. We pretty much always want the call to be asynchronous. Next, we specify the onload parameter, which is a function that we define that gets called once the server responds to our request. Finally, we need to call the xhr.send method. This will actually kick-off the request. Note that we need to do all of these things first, before we can call send. Otherwise, the request might hit the server and respond before we can specify our onload function. And if that happens, we won't actually know what to do with the response yet.

1360511542.070225866.xmlhttprequest.png

Making a Request

Alright, now let's try creating our own XML HGP request. What we'd like to do is request weapon dot JSON which is the previous JSON code that we parsed. Then, once the response kicks in, we'd like the onLoad function specified to parse it like we did before, If you're a little confused, more detailed instructions are provided in the comment

Making a Request- Quiz

parseJSON = function (weaponJSON) {
    parsedJSON = JSON.parse(weaponJSON);

    return parsedJSON['frames']['chaingun_impact.png']['spriteSourceSize']['x'];
};

// Create a new XMLHttpRequest object, then use
// its open method to to define the request that
// will be sent. The parameters to 'open' are:
//
// 1) The HTTP method to use, in our case we want
//    "GET".
// 2) The resource to request, in this case we're
//    interested in "/media/js/standalone/libs/gamedev_assets/weapon.json".
// 3) A boolean indicating whether or not we want
//    the request to be asynchronous or not. True
//    means we do want it to be asynchronous.
//
// After that, we want to define the onload method
// of the request to be our parsing function from
// before. We've included that code above for
// reference. A few things to keep in mind here:
//
// 1) This function can't take any parameters.
// 2) Instead of parsing 'weaponJSON', we'll need
//    to parse the 'responseText' member of the
//    request object.
// 3) You can access the request object inside
//    your 'onload' function by using the 'this'
//    keyword.
//
// Finally, we want to call the send method of the
// request object to actually send the request.
//
// YOUR CODE HERE
var weaponXHR = 

var setup = function() {
    // YOUR CODE HERE
    weaponXHR.send();
};

Making a Request- Quiz Answer

Alright, so not too much code. First we call a new XMLHttpRequest, then we specify the open method with the primaries GET, weapon.json, and true since we want the call to be asynchronous. And then we set the onload function to be our parsing code from before. And finally we kick of the request with xhr.sent. Now if we open this In chrome's developer tools, we can see that we actually do make this request for weapon dot JSON. Method get, and it's successful status, and it takes about thirteen milliseconds. And if we look at the console, we can see that we do actually print out the value of the X parameter that we were looking for before.

Loading Sound

Now, this is great for j sound but what if we want to grab something else, say, a sound file. To do that, we need to set the response type of the XMLHttp request object to arraybuffer and what that will do is specify that it is binary information rather than text that we can then decode as necessary. What we like you to do is create another XMLHttp request but this time we're going to request. Bg<u>menu.ogg, a sound file and set the response type to arraybuffer.</u> Now, we've already created the on load function for you to use. Don't worry if you don't understand any of this. We'll be going over this in more detail later in the sound unit. For right now, your job is just to fire off the XMLHttp request.

Loading Sound- Quiz

// Create a new XMLHttpRequest, that GETs the
// file '/media/js/standalone/libs/gamedev_assets/bg_menu.ogg'.
//
// To properly read this binary file, we'll need
// to specify the responseType of the request as
// an 'arraybuffer'.
//
// Doing this is necessary to work with any kind
// of binary data, like sound files, rather than
// text data.
//
// WARNING: If you don't specify a responseType
// of 'arraybuffer', your browser will try to
// interpret the sound file as text data. This
// could cause your browser to slow to a crawl or
// worse.
//
// Once you have done this, leave the request's
// onload to the below function. This will play
// the sound that you loaded.
//
// Don't worry if you don't understand what this
// code does, we'll be going over it later!
//
// YOUR CODE HERE
var soundRequest = 

var setup = function() {
    // YOUR CODE HERE

    soundRequest.onload = function () {

        try {
            var context = new webkitAudioContext();

            var mainNode = context.createGainNode(0);
            mainNode.connect(context.destination);

            var clip = context.createBufferSource();

            context.decodeAudioData(soundRequest.response, function (buffer) {
                clip.buffer = buffer;
                clip.gain.value = 1.0;
                clip.connect(mainNode);
                clip.loop = true;
                clip.noteOn(0);
            }, function (data) {});
        }
        catch(e) {
            console.warn('Web Audio API is not supported in this browser');
        }
    };

    soundRequest.send();
};

Loading Sound- Quiz Answer

Just a few lines of code. Create a new XMLHttpRequest object like before. We call request.open again, pretty much the same except that we've changed weapon.jsan to bgmenu.ogg, and we set request.responseType to be arraybuffer. And if we fire this up in our browser, we see that we do make that request, and it starts playing our sound file.

Abstracting XMLHttpRequests

Alright. So, we've written this XMLHttp request code a few times now. Let's go ahead and extract to that. What I'd like you to do is fill out the function xhrGet that does, well, exactly what we've done the past couple of times with xhrs. Takes in the reqUri, like weapon.json, or bg<u>menu.ogg.</u> It takes in a callback function to set the on load function to and it takes in a type, which could be either nothing, if we're expecting text error or arraybuffer, if we are expecting arraybuffer data. Now, one quick note about the callbacks, we're going to create the callbacks such that we assume that it takes the request object as a parameter. So, for example, you can see our parseJSON function here takes the xhr, and then parses xhr.responseText. Now, we have both the parseJSON and playSound functions here, that if passed in as callbacks, will appropriately play sound or parseJSON as we've done before. Now, down at the bottom of this file, we make 2 calls to xhrGet with weapon.json and bg<u>menu.ogg.</u> Just for you to test against to make sure that your xhrGet function is behaving properly.

Abstracting XMLHttpRequests- Quiz

// Fill out the provided xhrGet function to abstract
// out the functionality of performing an XMLHttpRequest
// GET request.
//
// The provided parameters are the URI to make the request
// to, the callback to call at onload, and the responseType,
// if necessary. If we don't need a special responseType,
// assume that that parameter is null.
//
// Now, we're going to assume that the callback takes the
// request object as a parameter, instead of taking no
// parameters.
//
// We've provided you modified versions of the previous
// two callbacks below. At the bottom of the file, we call
// xhrGet with both callbacks to help you with testing your
// code.

function xhrGet(reqUri, callback, type) {
    // YOUR CODE HERE
}

parseJSON = function (xhr) {
    parsedJSON = JSON.parse(xhr.responseText);

    x = parsedJSON['frames']['chaingun_impact.png']['spriteSourceSize']['x'];
    console.log(x);
    return x;
};

playSound = function (xhr) {
    try {
        var context = new webkitAudioContext();

        var mainNode = context.createGainNode(0);
        mainNode.connect(context.destination);

        var clip = context.createBufferSource();

        context.decodeAudioData(xhr.response, function (buffer) {
            clip.buffer = buffer;
            clip.gain.value = 1.0;
            clip.connect(mainNode);
            clip.loop = true;
            clip.noteOn(0);
        }, function (data) {});
    }
    catch(e) {
        console.warn('Web Audio API is not supported in this browser');
    }
};

// Test code for you to run
var test = function() {
    xhrGet('/media/js/standalone/libs/gamedev_assets/weapon.json', parseJSON, null);
    xhrGet('/media/js/standalone/libs/gamedev_assets/bg_menu.ogg', playSound, 'arraybuffer');
};

//test();

Abstracting XMLHttpRequests- Quiz Answer

Alright, so the skeleton of this code is pretty much what you've already done before, but we're going to introduce a few other things here. First of all, we set this caller variable to be the caller of xhrGett. Now, this is a special thing in Javascript and what it does is, whoever ends up calling xhrGet, will be set as this xhrGet.caller parameter. And you can grab that as necessary. Next, we do the same thing that we've done this entire time, create a new XML http request and call the open method. Then we check the type. If it exists, then we set it, otherwise, we assume by default that it is just text, now the onload function is where things get interesting if we set a callback then we go ahead and try and call it otherwise we don't do anything and this try catch plug is something that some of you might be familiar with we try to do what is an this callback here and if it fails then we catch the error and throw an exception. Now this is basically just going to print out a whole bunch of information about the exception. And this is where caller comes in as well. You can see that we print out the exception. The response text and the caller just for debugging purposes. Finally, we call xhr.send, and that's it. And if we load this up in our browser, we can see we make both calls. The first one takes a lot less time than the second one. Since it's only JSON. We can see our output JSON in the console. [MUSIC] And our song starts.

The DOM

So now we've gone through the basics of javascript syntax. Structuring and interacting with JSON data, and requesting data from a server. Now, we need to talk about how to interface to the browser. More specifically, we need to talk about the DOM, or document object model. The DOM is an interface to the structure of an HTML. document. For example, the head or body element. The DOM allows us to access and modify our hml document dynamically, using Javascript. Our game is primarily going to exist within a single element, the canvas, which we'll get into later. But what this means is that we won't have a lot of reason to heavily access the. The DOM. In fact we're only going to need a few things. So let's take a look at a quick example. Let's say you want to validate whether a user has entered a valid password into an input field. Here's how you would do that using the DOM API in Javascript. You can see the basic HTML here with the form. And the password input and the submit button, and we have a script down here where we start executing our javascript. First, we have this document.getElementById method. What this does is grab a dom element of the given id. In this case, it's exampleForm. Form, which is right here, so we would grab this entire form. When it's submitted, we run this function in here. Now, you don't really need to worry about the password rejects too much. We're interested in this part down here. Now if the password doesn't match, then we create a new tag element, assuming it doesn't already exist. We check if it exists by again using the document.getElementById method and checking for the ID notify. If it doesn't exist we use the document,createElement method specified at p tag. This parameter is just the name of a type of element. For example p or form or canvas. Now we set the text content. Of that p tag to this bit here. And we set the ID attribute to notify. That way, if we submit multiple times, we don't simply create a new p tag every single time. Finally, we grab a reference to the body tag. Since the body element has an ID of. Body we can do that, and we append, notify, to the body. Now if we look at this in our browser, we can see we have an input form with a submit button and you can see our basic HTML down here. However, if we submit an incorrect password, press submit, we can see that this pops up right here, which is the text that we set our tag to. You can also see that this tag was created down here. With an idea of notify and the correct text. You'll also see that if we keep pressing Submit again, more p tags don't get created, since we made that check.

<html>
    <head>
    </head>
    <body id="body">
        <form action="javascript:void(0);" id="exampleForm">
            <input type="password" id="examplePass" />
            <input type="submit" />
        </form>
    </body>
    <script>

document.getElementById("exampleForm").onsubmit = function() {
    // Regular expression matching strings between 6 and 8
    // characters long and consisting of uppercase characters,
    // lowercase characters, and digits.
    var passwordRegex = /^[A-Za-z/d]{6,8}$/; // any upper/lowercase characters and digits,
    // only create user notification if password doesn't match.
    if(!passwordRegex.test(document.getElementbyId9"examplePass").value)) {
        console.log("Regex didn't match");
        var notify = document.getElementById("notify");
        if (notify === null) {
            notify = document.createElement("p");
            notify.textContent = "Passwords need to be between 6 and 8 characters long and consist of uppercase characters, lowercase characters, and digits only."
            notify.id = "notify";

            var body = document.getElementById("body");
            body.appendChild(notify);
        }
    }
};

    </script>
</html>

Interfacing to the DOM

Alright, now that we've seen this in action, let's go ahead and use it ourselves. What I'd like you to do is create a new canvas DOM object inside a new div DOM object, that is then put inside the body element. To do this, we're going to have to grab the body element by its id of body. We're going to have to create a new element, using document.createElement, both for the div and for the canvas. Then, we'd like to set their ids. For the div, we'll set it to gameContent. And for the canvas, we'll set it to gameCanvas. Finally, we'll have to append both of those. The canvas to the div, and then the div to the body. You'll have to use document.getElementById, document.createElement, the appendChild method of DOM objects, and the id property to do all of this.

Interfacing to the DOM - Quiz

// 1) Grab the body DOM object and store it in
//    a variable for later use. Assume that the
//    body element has an id of 'body'.
//
// 2) Create a new div DOM object, and set its
//    id to "gameContent".
//
// 3) Create a new canvas DOM object and set its
//    id to "gameCanvas".
//
// 4) Attach the canvas DOM object to the div,
//    and the div DOM object to the body.
//
// You'll need to use the document.getElementById,
// document.createElement, as well as the
// <DOM Object>.appendChild methods to accomplish
// this. You'll also need to modify the id property
// of the DOM objects you create.
//
var manipulateDOM = function() {
    // YOUR CODE HERE

};

Interfacing to the DOM - Quiz Answer

So, the first thing we do, is grab the body dom object using document.getElementById with an id of body, and we create a div object, as well as a canvas object, and set their id's appropriately to div.id = "gameContent" and canvas.id = gameCanvas"" and finally, we append the div object to the body and the canvas object to the div. If we open this up into our browser, we can see that we have indeed created a div element and inside that is our new canvas element and both.

Javascript and Inheritance

Now, Javascript at first glance really doesn't resemble the same sort of object oriented programming languages that you see in C and C++ code. Now these are typically the languages that games are written in, NES, Super NES, XBox 360, PS3, all had some resemblance of this. Now Javascript provides a way that you can define a set of variables as well as functions encapsulated as an object called a prototype. But for game dev, we're really missing the inheritance factor, that is the ability to define that prototype and have another one inherent from it. Luckily, we can duct tape JavaScript just enough to make it look like we're getting classes and structure definitions. Now this type of setup is actually the backbone of how everything works inside of grids. So, before we move on, make sure that you're very, very familiar

Classes

All right. Now that Colt's explained why we want to use a more traditional object-oriented class base set up, let's go ahead and do that. Now, we'll be using a piece of code by John Resig that fakes class-based object oriented programming. Feel free to take a look at the code or the linked blog below to see how it works. Now, we're not terribly interested in the internals for our purposes. What we care about is how to use it for instantiating the proper class-based inheritance trees for our game objects. So, let's say we want to create the following inheritance tree. We want weapon and entity to both inherit from Class. And we want MachineGun and ChainGun to inherit from Weapon. And Teleporter and EnergyCanister to inherit from entity. Now we've started this off for you, but we're going to have you complete this yourself. First, we set weapon to be Class.extend. What this means is that weapon extends all of classes functionality, whatever that happens to be, and then adds it's own on top of it. Similarly, MachineGun is set to weapon.extend, which extends all of weapon's functionality, and then builds on top of that. Now, we've created these for you for a template. And, what I'd like you to do is fill in the rest of the inheritance tree we discussed above.

Classes - Quiz

// Create an inheritance tree using Class.extend() of the
// following form:
//
// 1) Weapon should extend Class.
//
// 2) MachineGun should extend Weapon.
//
// 3) ChainGun should extend Weapon.
//
// 4) Entity should extend Class.
//
// 5) Teleporter should extend Entity.
//
// 6) EnergyCanister should extend Entity.
//
// We've started things off for you by doing steps
// (1) and (2).

Weapon = Class.extend({
    init: function() {

    }
});

MachineGun = Weapon.extend({
    init: function() {

    }
});

// YOUR CODE HERE

Classes - Quiz Answer

Alright. So really, all you have to do is set ChainGun equal to Weapon.extend and in this case, we're not extending it with any further functionality. We'll be getting to that later. And similarly, we're defining Entity to extend Class, Teleporter to extend Entity, and EnergyCanister to extend Entity. This bit of code doesn't really do anything by itself but we'll be adding more functionality to these, as we go. And this way, we have a nice clean framework for doing that.

Outro

Great job. Now that we've got the fundamentals down, we're going to build on that in the later units to create fun interactive games.