2014 - 5stars - HTML5 Game Engines App Development and Distribution
2014 - 5stars - HTML5 Game Engines App Development and Distribution
This book contains information obtained from authentic and highly regarded sources. Reasonable
efforts have been made to publish reliable data and information, but the author and publisher cannot
assume responsibility for the validity of all materials or the consequences of their use. The authors and
publishers have attempted to trace the copyright holders of all material reproduced in this publication
and apologize to copyright holders if permission to publish in this form has not been obtained. If any
copyright material has not been acknowledged please write and let us know so we may rectify in any
future reprint.
Except as permitted under U.S. Copyright Law, no part of this book may be reprinted, reproduced,
transmitted, or utilized in any form by any electronic, mechanical, or other means, now known or
hereafter invented, including photocopying, microfilming, and recording, or in any information stor-
age or retrieval system, without written permission from the publishers.
For permission to photocopy or use material electronically from this work, please access www.copy-
right.com (http://www.copyright.com/) or contact the Copyright Clearance Center, Inc. (CCC), 222
Rosewood Drive, Danvers, MA 01923, 978-750-8400. CCC is a not-for-profit organization that pro-
vides licenses and registration for a variety of users. For organizations that have been granted a pho-
tocopy license by the CCC, a separate system of payment has been arranged.
Trademark Notice: Product or corporate names may be trademarks or registered trademarks, and are
used only for identification and explanation without intent to infringe.
Visit the Taylor & Francis Web site at
http://www.taylorandfrancis.com
and the CRC Press Web site at
http://www.crcpress.com
To my parents,
Randall and Karen Nagle.
Contents
Preface xi
Acknowledgments xvii
1 Introduction to HTML5 3
1.1 The HTML5 Standard . . . . . . . . . . . . . . . . . . . . . 3
1.2 HTML5, the New Living Standard . . . . . . . . . . . . . . 4
1.3 HTML5 Stands Alone . . . . . . . . . . . . . . . . . . . . . 5
1.4 Source Code . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.5 A Touch of Game Design . . . . . . . . . . . . . . . . . . . 6
1.6 HTML5 Hello World Canvas . . . . . . . . . . . . . . . . . 7
1.7 Pong Game Board . . . . . . . . . . . . . . . . . . . . . . . . 11
1.8 Pong Game Loop . . . . . . . . . . . . . . . . . . . . . . . . 12
1.9 Pong Game Refactoring . . . . . . . . . . . . . . . . . . . . 13
1.10 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
vii
viii CONTENTS
3 Crafty 43
3.1 Source Code . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
3.2 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
3.3 Crafty Pong . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
3.4 HTML5 Game Graphics . . . . . . . . . . . . . . . . . . . . 50
3.5 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
4 EaselJS 57
4.1 Source Code . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
4.2 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
4.3 Tic-Tac-Toe . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
4.4 Setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
4.5 Hello EaselJS . . . . . . . . . . . . . . . . . . . . . . . . . . 58
4.6 PreloadJS for EaselJS . . . . . . . . . . . . . . . . . . . . . 59
4.7 EaselJS Sprites . . . . . . . . . . . . . . . . . . . . . . . . . 60
4.8 Tic-Tac-Toe Implementation . . . . . . . . . . . . . . . . . . 62
4.9 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
5 Impact 75
5.1 Source Code . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
5.2 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
5.3 Setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
5.4 Hello Impact . . . . . . . . . . . . . . . . . . . . . . . . . . 78
5.5 MechaJet Implementation . . . . . . . . . . . . . . . . . . . 79
5.6 Weltmeister . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
5.7 main.js . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
5.8 Player Entity . . . . . . . . . . . . . . . . . . . . . . . . . . 93
5.9 Level 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
5.10 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
6 Turbulenz 101
6.1 Source Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
6.2 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
6.3 WebGL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
6.4 Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
6.5 From Source . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
6.6 Hello Turbulenz App . . . . . . . . . . . . . . . . . . . . . . 107
6.7 Turbulenz Game Structure . . . . . . . . . . . . . . . . . . 109
6.8 Sky Marble Implementation . . . . . . . . . . . . . . . . . . 110
6.9 Other Turbulenz Tips . . . . . . . . . . . . . . . . . . . . . 118
6.10 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
CONTENTS ix
Bibliography 185
Index 193
Preface
xi
How to Use This Book
This book is divided into three main parts. Each part has a specific goal.
xiii
xiv How to Use This Book
The book can be read from start to finish with each chapter building on
top of the other. However, the book was also designed so that, if you have
specific goals in mind, you can jump to that particular chapter to read about
the presented example. Though the book may reference techniques from earlier
chapters, all the examples were intentionally built independently to support
this strategy.
Once your game with your choice of engine is finished, you can work
through Part III on techniques to get your game into all the different app
markets you wish to target. For example, if Impact is your engine of choice,
consider this reading strategy:
Part I (if you want a refresher) → Part II (just Impact section) → Part III
(the different markets you wish to cover).
Not all the examples are placed in all the app markets, but the techniques
among the different engines are very similar. Finally, there is a section at the
end of the book called Tools Appendix on page 175. HTML5 game development
can require a very wide variety of tools. Rather than give each one a formal
introduction, the minor tools were placed in this appendix.
All the source code and examples are available at the website http://
HTML5GameEnginesBook.com/. All the code, graphics, and sound are li-
censed free for personal and commercial use (MIT and CC-BY-3.0). The game
engines and other tools are distributed with respect to their licenses.
About the Author
xv
Acknowledgments
I would like to thank everybody at CRC Press for their help. I would particu-
larly like to thank my editor, Rick Adams, for being very flexible during this
project as it is chasing a very fast-moving industry. I would like to thank my
wife, Karen, for all her support with my various ventures. Finally, I would like
to thank Philip Rideout, Jerry McMahan, and Michael Rutledge for their help
with proofreading.
xvii
Part I
HTML5 Game
Development
Chapter 1
Introduction to HTML5
3
4 1. Introduction to HTML5
• Canvas
• CSS3
• Device API
• File API
• Geolocation
• Microdata
• SVG
• Web Sockets
1.3. HTML5 Stands Alone 5
• Web Storage
• Web Workers
• WebGL
Not all sections are technically part of HTML5 (such as CSS3, and to
some extent, WebGL). However, many parts that make HTML5 attractive
to developers rely on them, so they are included for convenience. Web SQL
Database was on the road map, but has been abandoned [15]. It is listed above
because some books mention it, and since HTML5 is now a living standard, it
could be put back.
The feature that you should become most familiar with as an HTML5
game developer is Canvas. No longer are you constrained to performing
DOM(document object model) tricks on the browser page to make a game.
With Canvas, you draw animated graphics directly. The other major addition
that makes games for HTML5 possible is audio. Audio is now a first class
citizen (no “embed” tag needed). Finally, the last addition covered in this
book is WebGL. WebGL acts as a gateway to OpenGL to create some truly
impressive games.
Shortly after Adobe dropped support for Flash in Android, Apple declared
Java a deprecated technology and no longer ships it with Mac OS X [3]. With
Java deprecated from Apple products and Flash support waning for mobile,
HTML5 now stands alone as the one high-level platform that can target the
top computing platforms.
This “Hello World!” does not really showcase HTML5. The same could be
done using normal DIV elements from HTML 4. Let’s make it more HTML5-
like. Look at the code in Listing 1.2 and the result in Figure 1.2. This result
is much more interesting.
8 1. Introduction to HTML5
<!doctype html>
This is the HTML5 document type declaration (DTD). It is required
for all HTML5-based web pages. There is no versioning as HTML5 is a
living standard.
Do not leave off your document type declarations. Though Firefox and
Chrome are very tolerant, you could get strange behaviors from Internet
Explorer. IE has many different rendering modes (strict, loose, IE8-
compatible, etc.), and without a DTD, the one it chooses is difficult to
predict.
<html lang="en">
This sets the language to English. There is no language default in
HTML5, so you should specify one.
<head>
Start of header. This section contains special metadata about the
document (such as the title).
<meta charset="UTF-8">
Even if you are using no special characters, you should declare that you
are using UTF-8 as your character encoding. Some versions of IE have
known security vulnerabilities if UTF-8 is left off. Without it, IE or
other browsers may treat your encoding as UTF-7 (an extended version
of plain ASCII).
<title >Hello World </title >
Title of the page. The WHATWG standard says this is optional, but the
W3C standard says this is required for all HTML documents. For your
purposes, you should require it because various features of web browsers
rely on it (such as bookmarks). Also, a good title is extremely important
for Search Engine Optimization (SEO).
<script>
The phrase type=’text/javascript’ is no longer required in HTML5. All
browsers will assume JavaScript. These type attributes are a holdover
from XHTML, which required them. Actually, you would do best to
avoid it completely because a typo in the attribute could cause the
browser to not read it correctly since specifying type overrides its default
assumptions. Note that using just <style> is acceptable for CSS as well.
var canvas=document.getElementById("canvas");
Fetch the element with id="canvas". Store it in a variable.
var context=canvas.getContext("2d");
I am using a 2D canvas. HTML5 supports a 3D canvas, but support is
still very limited. 3D will be covered in Chapter 6.
context.font = ’bold 30px Times’;
Set the font designation.
10 1. Introduction to HTML5
context.rotate(Math.PI/4);
I am now rotating the canvas 45 degrees; rotate() uses radians to measure
angles. Math.PI is the JavaScript constant for PI.
context.fillText("Hello World!", canvas.width/4, 0);
I am now writing the text to x and y coordinates. I use width of the
canvas divided by 4 to make it more in the middle of the canvas block.
context.restore();
I am now restoring the canvas from its rotation. This leaves the “Hello
World!” at an angle. Note that to the viewer, it appears I operated solely
on the text. What I actually did was rotate the entire canvas, write the
text normally, and then restore the orientation of the canvas. Rotate-
write-restore is a very common technique in graphics programming.
<body onload="onload()">
This tells the browser to execute the custom onload function after the
page has finished loading. This is important because I want the canvas
available before I begin using it.
<canvas id="canvas" width="320" height="480">Sorry!</canvas>
This sets up my HTML5 canvas block. A browser that didn’t support
canvas would display the inner text or possibly leave it blank.
Like many examples of “Hello World!”, we could take a different path. If all
we wanted was rotated text, we could turn the canvas into
<div id="mytext">Hello World!</div> and just use the CSS3 transform property.
No JavaScript needed. See Listing 1.3 for the example code and Figure 1.3 for
the output.
<! doctype html >
< html lang = " en " >
< head >
< meta charset = " UTF -8 " >
< title > HTML5 Hello World CSS Angled </ title >
</ head >
< style >
# mytext
{
font - weight : bold ;
font - size :30 px ;
font - family : " Times " , Georgia , Serif ;
transform : rotate (45 deg ); /* Non - prefixed future versions */
-ms - transform : rotate (45 deg ); /* IE */
-moz - transform : rotate (45 deg ); /* Firefox */
- webkit - transform : rotate (45 deg ); /* Safari and Chrome */
-o - transform : rotate (45 deg ); /* Opera */
position : absolute ;
top : 100 px ;
1.7. Pong Game Board 11
left : 50 px ;
}
</ style >
< body >
< div id = " mytext " width = " 320 "
height = " 480 " > Hello World ! </ canvas >
</ body >
</ html >
There are lots of new features with CSS3, enough to make a game in its
own right. CSS3 transitions and translate are really nice, and using these
built-in effects usually means hardware-level optimization by the browser
manufacturers and less work for you. The old way of performing effects
required manually moving elements by iterating over their x/y or other style
values over time. This can be inefficient and sometimes look jagged. If you
can find a CSS3 way of doing things, you should favor it.
function onload () {
var canvas = document . getE lementBy Id ( " canvas " );
var context = canvas . getContext ( " 2 d " );
// draw rectangle
context . fillRect (0 , 0 , canvas . width , canvas . height );
// draw ball
context . arc ( canvas . width / 2 , canvas . height / 2 ,10 ,0 ,
Math . PI *2 , true );
“FPS” is “frames per second” and is also called “frame rate.” The final
rendering of each redraw of the game is called a frame. Each time the game
begins traversal through the game logic, this is called a “frame tick.” For a
good idea on what is a good frame rate, consider that up until The Hobbit:
An Unexpected Journey (2012) was released, nearly all movies were filmed at
24 FPS. The Hobbit was filmed at 48 FPS [33]. Most monitors can handle up
to 60 FPS. With these stats at hand, a good benchmark for game developers
should be a minimum of 30 FPS.
The HTML5 specification states a minimum setInterval() of 4 ms [8]. That
is 250 FPS. Note that a hard-coded frame rate is not good game design. You
would rather the game gets drawn as fast as the hardware can handle and
adjust your game logic accordingly. If the game can run at 60 FPS, why stop
it? Also, there is no guarantee that the device that plays your game can handle
30 FPS, which means pongGame() could get called before the previous frame is
finished (though unlikely because JavaScript is single-threaded, but that is not
guaranteed either).
Some of the problems with setInterval() are solved with the more modern
requestAnimationFrame() function. Instead of a hard-coded target frame rate,
requestAnimationFrame() tells the browser to update the screen at the next
convenient opportunity. The browser recognizes this function as animation
and optimizes accordingly (such as waiting until the entire frame is finished
and updating the final image at once versus individual elements).
Fortunately, the game engines we are going to look at in Part II man-
age requestAnimationFrame(), setInterval(), and other game loop decisions for
you. For our learning exercise for this simple game, a hard-coded rate with
setInterval() is fine.
pong game so it redraws the entire scene every frame. See Listing 1.5 for the
initial round of refactoring. All the major components and logic have been
reorganized into functions.
var gameFPS = 30; // Our game will run at 30 frames per second
var canvas = document . getE lementBy Id ( " canvas " );
var context = canvas . getContext ( " 2 d " );
// draw rectangle
context . fillRect (0 , 0 , canvas . width , canvas . height );
}
function drawBall () {
context . strokeStyle = ’#000000 ’; // color for ball
context . beginPath (); // start a draw path
context . arc (160 ,240 ,10 ,0 , Math . PI *2 , true ); // draw ball
context . fill (); // close path and fill in the shape
}
function drawTopPaddle () {
context . fillStyle = ’#000000 ’; // color for inside shapes
context . fillRect (100 ,10 ,100 ,10); // draw top paddle
}
function d r a w B o t t om P a d d l e () {
context . fillStyle = ’#000000 ’; // color for inside shapes
context . fillRect (100 ,460 ,100 ,10); // draw top paddle
}
function pongGame () {
draw Backgrou nd ();
drawTopPaddle ();
d r a w B o t t o m P a d dl e ();
drawBall ();
}
// start game loop
window . setInterval ( pongGame , 1000 / gameFPS );
This will render the same screen as before in Figure 1.4. The difference is
our code is better organized so we can manage changes to the background, ball,
and paddles. Notice the ordering. Since the end result is a single graphical
image, whatever gets drawn last is layered at the top. That means our ball
will get drawn on top of the paddles if we are not careful.
Now, let us make the game do something. We will start with the ball
bouncing around. Every frame, the ball should move, and it should bounce off
of walls. See Listing 1.6 for this update to the code.
1.9. Pong Game Refactoring 15
function drawBall ()
{
context . strokeStyle = ’#000000 ’; // color for ball
context . beginPath (); // start a draw path
ball . x += ball . xspeed ;
ball . y += ball . yspeed ;
context . arc ( ball .x , ball .y ,10 ,0 , Math . PI *2 , true ); // draw ball
context . fill (); // close path and fill in the shape
The ball was pulled out of the update function and given variables to track
location and speed. Now the ball will bounce around the screen forever moving
at one pixel every frame. The variable yspeed was set to 3 to give the ball a
bit more downward motion since the game board is tall. Depending on which
edge, it will reverse the direction of one of its speed variables.
However, we only want the ball to reverse course if it hits the side walls or
a paddle. It is not enough to just look at the canvas coordinates. To achieve
this, we need to add hit detection. Some goals we need to solve with the hit
detection include:
Let us add a function called hitDetect() that performs the various hit
detections for our ball. Like all functions that need repeated calls, it will go in
pongGame(). See Listing 1.7. This is a meaty example. Don’t get too concerned
about the numerous additions and subtractions.
16 1. Introduction to HTML5
function hitDetect ()
{
if (( ball . y + BALL_RADIUS ) >= ( bottomPaddle . y ))
{
if ( bottomPaddle . x <= ball . x && ball . x <=
( bottomPaddle . x + PADDLE_WIDTH ))
{
console . log ( " bottomPaddle hit " , ball .x , ball .y ,
bottomPaddle .x , bottomPaddle . y );
ball . yspeed = ball . yspeed * -1;
ball . y = bottomPaddle . y - BALL_RADIUS ;
return ;
}
}
if (( ball . y - BALL_RADIUS ) <= ( topPaddle . y + PADDLE_HEIGHT ))
{
if ( topPaddle . x <= ball . x && ball . x <=
( topPaddle . x + PADDLE_WIDTH ))
{
console . log ( " topPaddle hit " , ball .x , ball .y ,
topPaddle .x , topPaddle . y );
ball . yspeed = ball . yspeed * -1;
ball . y = topPaddle . y + BALL_RADIUS + PADDLE_HEIGHT ;
return ;
}
}
if (( ball . x + BALL_RADIUS ) >= canvas . width
|| ( ball . x - BALL_RADIUS ) <= 0 )
{
ball . xspeed = ball . xspeed * -1;
}
if ( ball . y > ( canvas . height + BALL_RADIUS )
|| ball . y < (0 - BALL_RADIUS ) )
{
i ni tG am e Ob je ct s ();
}
}
• Paddles are now objects with location variables for easy tracking.
The logic becomes more complex depending on if the ball is at the top,
left, bottom, or right portions of the screen. PADDLE_WIDTH, PADDLE_HEIGHT, and
BALL_RADIUS are added and subtracted as needed so the collision occurs on the
surface of the ball.
1.9. Pong Game Refactoring 17
Also, notice after the bounce the ball.y value is forced outside the paddle.
This covers the case of the user sliding the paddle into the side of the ball.
Without this, the ball could get “stuck” inside the paddle as it toggles direction
rapidly.
Finally, you can see console.log() statements. These values are outputted
to the chrome debugger (or the Firefox debugger). The console.log() function
is an invaluable tool for run-time troubleshooting.
Next, we need to let the user control the paddle. The player will control
the bottom paddle using the keyboard. Left arrow will move the paddle left.
Right arrow will move the paddle right. To do this, we bind the keyboard
event and track which key was hit. Then the input will be applied during the
next frame tick as appropriate.
See below for the keyboard binding. A very easy way to find correct key
codes is to simply drop the event into console.log():
document . onkeydown = function ( event ){
console . log ( event . keyCode ); // what key did I press ?
if ( event . keyCode == 39) // right arrow
{
rightArrowHit = true ;
}
if ( event . keyCode == 37) // left arrow
{
leftArrowHit = true ;
}
}
Now, the player can move the paddle around and bounce the ball. The next
step is having the computer control the top paddle. All artificial intelligence
can go in a function called computerAI() that gets called with our other functions
inside pongGame(). See below for the computer AI.
function computerAI ()
{
if ( ball . yspeed < 0)
{
if ( ball . x < ( topPaddle . x + PADDLE_WIDTH / 2))
{
topPaddle . x -- ;
} else {
t o p P a d d l e . x ++;
}
}
if ( t o p P a d d l e . x <= 0)
{
topPaddle . x = 0;
}
if ( topPaddle . x >= ( canvas . width - P A D D L E _ W I D T H ))
{
t o p P a d d l e . x = canvas . width - P A D D L E _ W I D T H ;
}
}
Our AI is very simple. It tries to center its paddle on the ball, and it only
moves when the ball is traveling toward it. It also moves slower than the player
(just one pixel per frame). This will be a very easy game, but it works well
enough for our demo.
Now, our game just needs a score. We can write the text in the top left
corner. It will get updated in our pongGame() loop. See below for the score
function:
var pointsPlayer = 0;
var pointsC omputer = 0;
function drawScore ()
{
if ( pointsPlayer > 0 || p ointsCo mputer > 0)
{
context . font = ’ bold 15 px Times ’;
context . fillText ( " You : " + pointsPlayer
+ " CPU : " + pointsComputer , 5 , 12);
}
}
Not shown is the updated hitDetect() function that increments the player
or computer score when the ball goes off-screen. All of the added functions get
executed during each frame tick. Below is the final list of functions executed
by pongGame().
1.10. Summary 19
function pongGame () {
keybardEvents ();
computerAI ();
drawB ackgrou nd ();
drawTopPaddle ();
d r a w B o t t o m P a d d l e ();
drawBall ();
hitDetect ();
drawScore ();
To clarify, every 1/30 of a second (30 FPS), the JavaScript engine executes
pongGame() via setInterval(). Inside pongGame(), keyboard events were captured
between frames. We then process the computer AI. With the internal stats
updated, we then draw the background, top paddle, bottom paddle, and
ball. We then perform hit detection, and finally we draw the score on top of
everything else. You are invited to rearrange the order or comment out these
functions to see what happens.
1.10 Summary
We have a “complete” game written purely in HTML5. We have player control,
AI control, and a score. The game is a bit dull, though. Sprinkling some
random values in various places (like the start location of the ball) could help.
Also, mobile users are completely left out. They cannot control the game
because mobile devices do not have left/right arrows. As soon as they load
the page, the ball will immediately go off the board. Finally, what is a game
without some graphics and sound? Once you start adding these requirements,
you will need to learn some more complex techniques, which brings us to the
next chapter.
Chapter 2
In the previous chapter, we created a very basic HTML5 Pong game. The
entire game was around 200 lines. We now want to make the game more
interesting by adding sound and graphics. We also want to support mobile
users. Because our time is precious to us, we don’t want to reinvent the wheel.
We will use a game engine to do the low-level heavy lifting for us, but before
doing that, we need to step back and look at a more formal approach to our
development. This chapter presents some development and design theory. Our
Pong game will receive its game engine in the next chapter.
The core goal is to build your site using nothing but well-established
standards and HTML tags focused on the meaning of the content rather
than presentation (such as using <cite> to cite a reference versus <i> to
just italicize it).
21
22 2. HTML5 Development Strategies
After your site is built this way, if a newer more capable browser comes
along, you can add hooks to your solid foundation of code to give the
user a better experience.
Graceful Degradation takes the opposite approach. It is the idea that you
want to give your users the best experience possible. If they have a
highly capable device, you want to give them the means to use it.
There are pros and cons to each strategy, and the proper approach could
be the topic of a nice debate. The best strategy is dependent on the task. For
example, a very content-oriented website (such as Wikipedia) benefits greatly
with a progressive enhancement approach.
Video game development benefits from a graceful degradation approach.
Audio is a big problem with HTML5 and mobile devices. The standard
approach is to just turn sound off for mobile devices accessing via their internal
browser. That doesn’t mean our app should be built without sound and then
bolted on later. Eventually, mobile HTML5 audio will improve, the users will
upgrade their phones, and our game will be ready for them.
There is a third, hybrid strategy to consider when developing a game or any
app. It is called “mobile-focused development.” This strategy is also known
as “Mobile First” and is often discussed when discussing the term responsive
design. The idea is that mobile devices present a very unique challenge, and
to give them a good experience, their needs must be addressed from Day 1.
This strategy warrants an entire section. We intend to support mobile devices
in this book.
will try to access it. If your app won’t run on their device, they will leave and
possibly not come back. Depending on the stat agency and how the metric is
calculated, mobile web access via tablets or phones now accounts for anywhere
from 13% [36] to 27% [37] of all internet usage, and that number will continue
to rise quickly as smartphones for the first time outsold “dumbphones” in
2013 [38].
If you don’t focus on mobile web users, your audience will shrink. Even
users that are usually on a desktop browser very likely have a mobile internet
device as their secondary. Naturally, they would prefer that your app worked
on both.
There is actually a side benefit to such focus on mobile users: Their devices
are weak. The current generation (mid-2013) high-end cell phones and tablets
boast a power-conserving 1.4 GHz dual-core processor and usually 1 GB of
RAM. The graphics processing unit is often embedded in the CPU. To find
a current desktop with specifications that weak, you would have to reach for
the low end of the all-in-one Black Friday deals. We as developers have been
spoiled with multicore processors, lots of RAM, and good graphics processing
being standard issue since 2008. Targeting mobile devices forces us to keep our
apps fast and efficient. If your app can run tolerably on weak mobile devices
at varying resolutions, it will probably run smoothly on a desktop.
Notice I didn’t mention our “big monitors” or “nice display resolution” in
that paragraph about developers being spoiled by their workstations. This
is because the monitor market has seen little innovation since 2006. This is
because TV manufacturers tend to focus on just one market: HDTVs. That
means computer monitor resolutions have been frozen at 1080p (1920 × 1080
pixels) for several years. Current tablets already exceed this resolution (iPad
has 2048 × 1536) [32] while phones are just now meeting it (Samsung Galaxy
S4 is 1920 × 1080 [39]).
An interesting situation has occurred with the fast improvement of mobile
devices and stagnation of computer monitors: By the time you read this book,
it is quite possible your 5-inch cell phone will be able to play your game at a
higher resolution than your desktop. This is already true for many tablets.
2.2.1 WebKit/Blink
On the desktop, there are several browsers with various shares of the market.
They range from 10% to around 30% with Chrome as the market leader. There
is no real market domination on the desktop, but it is possible Chrome may
break away further and achieve a significant lead by the time you read this
book. Chrome had a strong upswing in 2012–2013 gaining 8% [40]. See the
chart in Figure 2.1. Note that Opera was left out since it only has 1% on the
desktop.
45
40
35
30
25 Chrome
20 IE
15 Firefox
10 Safari
5
0
12 4
1 5
1 6
12 7
1 8
1 9
12 0
1 1
13 2
1 1
13 2
13 3
4
20 2–0
20 –0
20 2–0
20 2–0
20 – 0
20 2–0
20 2–1
20 –1
20 2–1
20 –0
20 3–0
20 –0
–0
1
20
However, as web developers, the name on the browser does not matter
so much. It is the actual underlying engine, how the browser processes our
HTML5, that we are concerned about. See the next chart in Figure 2.2 of
market share with the common engines combined.
50
45
40
35
30
25 Webkit/Blink
20 Trident
15 Gecko
10
5
0
12 4
12 5
12 6
12 7
12 8
12 9
12 0
12 1
13 2
13 1
13 2
13 3
4
20 –0
20 –0
20 –0
20 –0
20 –0
20 –0
20 –1
20 –1
20 –1
20 –0
20 –0
20 –0
–0
12
20
35
30
25
20 Android
15 iPhone
Opera
10
Nokia
5
0
12 4
1 5
1 6
12 7
1 8
12 9
1 0
1 1
13 2
1 1
1 2
13 3
4
20 2–0
20 –0
20 2–0
20 2–0
20 – 0
20 2–0
20 –1
20 2–1
20 2–1
20 –0
20 3–0
20 3–0
–0
1
20
Now, the same as before, the combined chart showing only the engines
powering the browser. See Figure 2.4.
120
100
80
60
Webkit/Blink
40 Other
20
0
12 4
1 5
1 6
12 7
1 8
12 9
1 0
12 1
1 2
13 1
1 2
13 3
4
20 2–0
20 –0
20 2–0
20 2–0
20 –0
20 2–0
20 –1
20 2–1
20 –1
20 3–0
20 –0
20 3–0
–0
1
20
their base rendering engine, and the next closet leader, Opera, has announced
a migration to Blink [41] (as it follows the Chromium project with Google).
This means the odd discrepancy when testing against multiple browsers
may happen less often. However, iOS and Android web page behavior are
not completely identical. Apple’s Safari uses Webkit’s built-in JavaScriptCore
for their JavaScript engine, while Google’s Chromium project uses the V8
JavaScript engine for theirs [43]. Still, unlike desktop development, if you get
it working on one, you are very close to getting it to work on the other.
Since Chrome is available on both Mac and Windows and uses the We-
bkit/Blink engine, and since it is also has best-in-class web development tools
built in, it should be your browser of choice for mobile-focused development.
If you are a fan of Firefox, it too has really good developer tools (launch using
Ctrl+i, same as Chrome). Still, keep Chrome around because you need a
WebKit-derived browser to support your testing. If you are a Windows user,
you have little choice because Apple abandoned Safari for Windows as of Safari
6 [31]. Opera’s Blink-based browser was still in beta as of this writing.
The old way of handling different browsers is detecting which browser is being
used. Depending on how the browser identifies itself (by looking at the User–
Agent string), you would serve up a custom-made page. This worked fine in
the days of Firefox versus Internet Explorer. However, there are now far more
browsers on far more devices and these browsers are updated very rapidly. In
2010, Google adopted an aggressive six-week release cycle for Chrome [49]. In
2011, Mozilla adopted a similar six-week major release cycle for Firefox [27].
Once you factor in all the mobile devices and the fairly large fragmentation of
Android, you can see that sending up a custom sheet for each combination of
OS + Browser is unmaintainable.
See Listings 2.1 and 2.2 for various methods of browser detection. One uses
conditional comments inside HTML. Conditional comments are only supported
in IE. Therefore, you may see the jQuery version of detecting the browser
agent. You should not do this. It is presented below so you can recognize
it because the technique has been very prevalent. Note that even jQuery
does not recommend using it and has removed this feature as of version 1.9.
Browser detection should only be a technique of last resort. IE10 does not
support conditional comments [53].
<! -- Do not do this ! -- >
[ if lt IE 7] > < link href = " ie_ 6_and_be low . css "
rel = " stylesheet " type = " text / css " >
<! -- [ if IE 7] >
< link rel = ’ stylesheet ’ href = ’ ie7 . css ’ type = ’ text / css ’/ >
< ![ endif ] -- >
<! -- [ if IE 8] >
< link rel = ’ stylesheet ’ href = ’ ie8 . css ’ type = ’ text / css ’/ >
< ![ endif ] -- >
// Do not do this !
if ( $ . browser . webkit ) {
alert ( " This is Chrome or Safari ! " );
// Do something just for WebKit
}
if ( $ . browser . mozilla ) {
alert ( " this is Firefox ! " );
// Do something just for Firefox
}
if ( $ . browser . msie ) {
alert ( " this is IE ! " );
// Do something just for IE
}
If you only care about a handful of these checks, the Modernizr website has
a utility that can build a special version with just the checks you need. There
is no configuration for Modernizr. Just include the JavaScript library, and it
will generate a global “Modernizr” object that holds the results. Using feature
detection with Modernizr allows anybody to use any browser they wish. If it
happens to support Canvas, you can send them Canvas. If it doesn’t, you can
try to degrade gracefully.
The images are showing four browsers with three common formats, and
results vary by platform and browser. IE only plays MP3. Chrome plays them
all. Firefox 21 will only play MP3 for Windows. Safari does not play Wav.
See Table 2.1 for a summary. Note that this chart may change by the time
you read this book. While writing this book, Firefox 21 received MP3 support
on Windows, and then Firefox 26 received it for the Mac.
32 2. HTML5 Development Strategies
The only way to support all major browsers is to encode your files to MP3
plus Wav or Ogg. Since Ogg is significantly smaller than a standard Wav, that
should be the preferred secondary format. Unlike MP3, Ogg is completely
royalty free [50] [51], so it may eventually catch on to be a universal format
for compressed audio.
Despite the annoying audio format war, previous problems of HTML5 are
waning. With iOS 5 and below, you only had one audio channel to work with.
Having one audio channel meant only one sound could play at a time. Audio
hasn’t been this crippled since the 1980s. Though the quality of the sound was
very limited, the original 8-bit Nintendo supported five audio channels [52].
Though the problem is now solved in hardware by adding more channels,
the original software workaround was to have “sound sprites” similar to a
normal sprite sheet (discussed next chapter). You would have one big file with
all your sounds. When an event occurred, you’d jump the audio player to
the correct sound location, play the sound, and then jump the audio player
back. It was crude, but it worked. Fortunately, iOS6 and recent Android has
better multiple audio channel support. By the time you read this book, you
won’t need to worry about this, but keep that technique in mind if you want
to support older browsers or old hardware (or simply enjoy game dev trivia).
To stay up to date on mobile and desktop browsers and what features of
HTML5 are supported, you can visit http://html5test.com/. http://mobilehtml5.
org is also a good resource for details specific to mobile.
1. Install it in a directory your user account has write access to, such as:
c:\Users\YourAccountName\wamp
In the past, the normal procedure was to install the server in a folder off
the root directory. This cannot be done as easily since Vista/Windows
7/Windows 8 has stricter permissions for apps. There will be file access
issues. Also, some versions of these Windows-based servers have problems
with spaces in directory names.
2. If you have VMware installed (to test different operating systems), you
may need to reconfigure SSL to run on a different port. One of VMware’s
services likes to use 443, the default SSL port. For XAMPP, edit:
xampp\apache\conf\extra\httpd-ssl.conf
Search, find, and then change 443 to something else, like 4430. Figure
2.10 shows the SSL port being used by VMware and then starting on a
different port.
WAMP does not enable SSL by default, so port 443 will not be opened.
If you are using SSL on WAMP, the location and file is very similar.
Look for it at:
wamp\bin\apache\apache2.2.22\conf\original\extra\httpd-ssl.conf
34 2. HTML5 Development Strategies
Start WAMP/XAMPP and then start Apache web server. For this book,
there is no need for MySQL or any other standard service beyond Apache
provided by the web server. Go to http://localhost/ to verify that your web
server is running.
If you are looking for a personal recommendation on which development
server to use, I have used XAMPP for eight years. However, the project has
been lacking since user account controls (UACs) became normal in Windows
Vista, 7, and 8. As of this writing, the official instructions describe how to
turn off UAC [89]. This is undesirable for a professional environment. For new
development environments, I recommend installing WAMP. For Mac, I use
MAMP. Apple’s OS X Server’s web server works fine too. MAMP is simply
easier to configure and remap the web root.
With the web server configured, you now need to know your IP address so
you can view your web page on your mobile device (on the local desktop, you
can just use “localhost” or “127.0.0.1” as the web address). Follow these steps:
4. Look for your IPv4 address. You may have more than one. Your web
server will try to bind to all addresses.
See Figure 2.11 for an example IP address output. You can see my IP
address is 192.168.1.23. If I had another adapter connected (such as Ethernet),
it would show as connected too, and I would have another IP address. You
may want to give yourself a static IP address because you will be frequently
visiting this address on your mobile devices. Note that you can give a single
network adapter multiple IP addresses. This can be useful for testing separate
networks with a single adapter (often called a “network bridge”). Also, if
you have VMware installed, you may have additional virtual adapters with
additional IP addresses being shown as connected. Find the IP addresses of
the adapter that is connected to your local network.
2.4. Testing on Mobile Devices 35
Direct your mobile device’s browser to each entry in your list of IPV4
addresses. One of them should show the default welcome page. If none work,
check that your mobile device is connected to Wi-Fi and not 3G/4G. If Wi-Fi
is connected, try turning off Windows firewall (but don’t leave it off; set up a
rule instead). Also, WAMP has two modes. Try clicking your WAMP task bar
icon and select “Put Online.” One of WAMP’s modes is to just bind locally.
You will then have a very simple HTTP server running for that location
on port 8000. Go to http://127.0.0.1:8000 to view your pages. The Python
server will work for the majority of the examples. The main exception is the
Impact game engine. Impact requires PHP. Eventually, you will need a web
server with PHP support.
2.4. Testing on Mobile Devices 37
Now, on to MAMP. MAMP is the easiest full development server for Mac
users. To install MAMP, download it (http://www.mamp.info/), unzip it, and
then right click to open it. Note that you have to use right click because in
OS X Mountain Lion and above, the GateKeeper feature prevents unsigned
installers from running normally. Install with default options. Navigate to
your Applications folder, and then run MAMP. If it all goes well, you will see
Figure 2.12 after starting the app.
We will not need MAMP Pro as PHP and Apache are sufficient for our
purposes. Note: To ease development, you may want to set MAMP to use
default HTTP port 80. Test the server by opening the start page. Now, you
need to find the IP address of your Mac so you can go to it on you mobile
device. To do this:
1. Start Terminal (Go to Spotlight and search for “Terminal.” It also can
be found in Applications/Utilities).
3. Look for your “inet” device. You may have more than one. As before,
my IP address is 192.168.1.23. I use the same IP address whether in
Mac or Windows to make my development easier (Figure 2.13).
38 2. HTML5 Development Strategies
Navigate to that IP address on your mobile device. You should reach your
Mac web server. With your IP addresses in hand, try each one on your mobile
device to access the page. Make sure you are connected to Wi-Fi and not
3G/4G.
If you have access to a website with a web host, you also can upload and
test your game in a private section of it. It’s a bit slower than using a local
server, but it is a lot easier to set up and share. Take a look at FileZilla in
the Tools Appendix. Some programming editors, such as Komodo Edit, are
capable of directing remote files through FTP and SFTP. That can save you
the upload step.
These meta tags are telling the mobile device that the page has been
optimized for mobile devices, and that it should set the viewport to the width
of the device. There are many useful meta tags, such as setting the home
screen button if the link is saved on an iPhone, but this is enough to get
started. If you would like a shortcut to the proper ways of serving mobile
and desktop users (and I am a big fan of shortcuts), HTML5 Boilerplate is an
all-in-one starting template to build a modern HTML5-based website. It is
discussed in the Tools Appendix.
2.7 Summary
This concludes Part I of the book. In the previous chapter, we built a proof-
of-concept HTML5 game that can run on all modern (desktop) web browsers.
40 2. HTML5 Development Strategies
HTML5 Game
Engines
Chapter 3
Crafty
3.2 Introduction
Crafty is an open-source JavaScript game library developed by Louis Stowasser
[54]. It renders to either Canvas or DOM. Its emphasis is on being lightweight
and easy to use. For that reason, it is a good candidate to rewrite the Pong
game from the previous chapters.
In this chapter, not only do we rewrite Pong, we will introduce a few more
HTML5 considerations and best practices that are more fitting to be discussed
here than in Part I.
43
44 3. Crafty
One problem with game engines is that they have their own way of doing things,
which means very little code gets reused. Crafty is no exception. Therefore,
it is important to find an engine that will do what you need. Otherwise, you
may be abandoning a lot of code at failed attempts.
From Part I, our header needs to be modified to add the Crafty and
Modernizr libraries. Modernizr provides HTML5 feature detection for us so
our code works better across multiple browsers. Since we are moving our app
to an external file, we also are going to make tweaks to the meta tags to tell
our browser not to cache the files. See Listing 3.1 for these changes.
< head >
< meta charset = " UTF -8 " >
< meta http - equiv = " cache - control " content = " max - age =0 " / >
< meta http - equiv = " cache - control " content = " no - cache " / >
< meta http - equiv = " expires " content = " 0 " / >
< meta http - equiv = " expires "
content = " Tue , 01 Jan 1980 1:00:00 GMT " / >
< meta http - equiv = " pragma " content = " no - cache " / >
Note that Crafty hosts minified versions of its library on its website that
we could link directly. Also, note again, the absence of type=’text/javascript’.
This is no longer necessary with HTML5. Also, we are now adding no-cache
clauses in our meta tags. This is very useful when working with graphics
and external JavaScript files. On production servers, you would allow the
browser to cache and instead use versioning. See below for example versioning
of external JavaScript files.
< script src = " crafty . js ? v =0.5.3 " > </ script >
< script src = " modernizr . js ? v =2.6.2 " > </ script >
< script src = " pong_crafty . js ? v =1.0 " > </ script >
When you release a new version, change the v= to the new version descrip-
tion, and the next time your user visits, the browser will fetch and cache the
new file. You should use this technique for all external assets (.js, .css, images,
etc).
If you are using a scripting engine, such as PHP, you can have the server
do the versioning for you. Below is an example of how to do this:
3.3. Crafty Pong 45
Now, PHP will append the file’s modified time to the end of the v= parameter.
A change in file modification will change the value for v. This will automatically
force browsers to fetch new versions. This is very useful since browsers tend
to cache aggressively.
If you are using a web server, such as XAMPP, WAMP, or MAMP men-
tioned in Part I, your server supports PHP. I recommend switching to the
automatic versioning method now. Browsers will sometimes cache even if you
try to tell them not to with no-cache headers. However, they always fetch
when given new version numbers. A stale cache can cause lots of lost time
during development.
Entity
A crafty entity is an object that can be placed onto the screen and react
to events. For those familiar with object-oriented programming, this can
be thought of as a “class.” Entities are declared using Crafty.e. In our
pong game, the paddles, ball, and score are entities.
Components
Entities are comprised of components. This can be thought of as methods
and properties in object-oriented programming. In our pong game, our
sprites and audio declarations are components that get attached to our
3.3. Crafty Pong 47
Events
Components react to events. Some events are specially defined, such as
the Collision component, which will make available an event called onHit.
Some are built in to the entity system, such as the EnterFrame event when
the game loop restarts.
The entity declaration when finished with the game ball will look like this:
Crafty . e ( " gameBall ,2 D , Canvas , Collision , SpriteAnimation , ball0 " )
You will notice that the game ball is now a square. This is because Crafty
does not support circles for entities. This is one of the downsides of game
libraries; we can only do what the engine supports. That is, unless we wish to
write extra code to extend the engine. We could do this since Crafty is open
source [55]. What we will eventually do instead is use the Crafty sprite engine
to load a round graphical ball. For now, a square is sufficient. Next, we need
to add movement to the ball. This can be done by binding to the EnterFrame
event. While we are at it, we will add hit detection to the ball. This is a lot
easier with Crafty than it was with our old method. See below for the code:
Crafty . e ( " gameBall , 2D , Canvas , Color , Collision " )
. attr ({ x : 240 , y : 240 , w : BALL_RADIUS , h : BALL_RADIUS ,
xspeed : 2 , yspeed : 3
})
. color ( BALL_COLOR )
. bind ( ’ EnterFrame ’ , function () {
})
. onHit ( ’ bottomPaddle ’ , function () {
this . yspeed *= -1;
this . y = 460 - BALL_RADIUS ;
3.3. Crafty Pong 49
})
. onHit ( ’ topPaddle ’ , function () {
this . yspeed *= -1;
this . y = 10+ BALL_RADIUS ;
});
Crafty lets us name entities, and then it uses its collision engine to see
if the entities collide. This makes our job a lot easier. Notice we are using
the same trick as before. We force the “y” value of the ball to something
meaningful so it doesnt get “stuck” inside the paddle if a user slides into it.
We almost have a working pong game. We now need to add a score. This
requires a new entity with score variables. See below:
Crafty . e ( " scoreValue , 2D , Canvas , Text " )
. attr ({ x : 5 , y : 12 , w : PADDLE_WIDTH , h : PADDLE_HEIGHT ,
pointsPlayer :0 , points Compute r :0
})
. bind ( ’ EnterFrame ’ , function () {
this . text ( " You : " + this . pointsPlayer +
" CPU : " + this . pointsC omputer );
});
The scores need to be updated. The code for this is not in a figure as it is
simply incrementing the points variable when the ball leaves the stage. Next
is the computer AI. It will be the same as before. The difference is that the
AI is now inside the EnterFrame event of the top paddle. See below:
Crafty . e ( " topPaddle , 2D , Canvas , Color " )
. attr ({ x : 100 , y : 10 , w : PADDLE_WIDTH , h : PADDLE_HEIGHT })
. color ( PADDLE_COLOR )
. bind ( ’ EnterFrame ’ , function () {
var gameBall = Crafty ( " gameBall " ); // get gameBall
if ( gameBall . yspeed < 0)
{
if ( gameBall . x < ( this . x + PADDLE_WIDTH / 2))
{
this . x -- ;
} else {
this . x ++;
}
}
if ( this . x <= 0)
{
this . x = 0;
}
if ( this . x >= (320 - P A D D L E _ W I D T H ))
{
this . x = 320 - P A D D L E _ W I D T H ;
}
});
50 3. Crafty
Our last step is to prevent the user from scrolling the paddle off the screen.
We need to modify the EnterFrame event of the bottom paddle. A pattern
should be emerging. All the entities have an EnterFrame available to them that
is convenient for adding logic. See below for the bottom paddle:
Crafty . e ( " bottomPaddle , 2D , Canvas , Color , Multiway " )
. attr ({ x : 100 , y : 460 , w : PADDLE_WIDTH , h : PADDLE_HEIGHT })
. color ( PADDLE_COLOR )
. multiway (4 , { LEFT_ARROW : 180 , RIGHT_ARROW : 0 })
. bind ( ’ EnterFrame ’ , function () {
if ( this . x <= 0)
{
this . x = 0;
}
if ( this . x >= (320 - PADDLE_WIDTH ))
{
this . x = 320 - PADDLE_WIDTH ;
}
});
We have now completely replicated the original HTML5 Pong game in Crafty
(with the small exception of a square ball). We went from 230 lines to 120
lines. The use of the game engine also makes the code much easier to update,
which we will demonstrate by adding graphics and sound.
Writing a sprite sheet system for a game is difficult. You need to handle
loading, unloading, and a coordinate system to slice up the image. Then you
need to handle timed repetition if you want animation. Fortunately, we don’t
have to worry about that because all the game engines in this book have a
sprite sheet engine. We just need to learn each system’s way of managing it.
The load step tells the browser to fetch the sprite image. After it has
fetched, it goes to the next scene, which is main. This strategy of having a load
step before launching the game is used in every engine in this book (whether
directly or indirectly via the way the engine is designed).
While assets are being fetched, Crafty reads the sprite declarations. The
sprites ask for pixel size of each block, which is 16 pixels by 16 pixels for our
purposes. Declaring them requires block coordinates, length, and height. If
length or height is not specified, Crafty will set each value to 1.
We will now tile our background. Normally, if you have a complex map,
you would have a tile map that goes along with your sprite map. You would
parse the map and drop the tiles. Tile maps will be introduced with Impact
(Chapter 5). For now, since this is just a one-level stage, we will hard-code
the background tiles. See Listing 3.5 for code to draw the background.
52 3. Crafty
} else {
Crafty . e ( " 2D , Canvas , floor " + usefloor )
. attr ({ x : xtile * 16 , y : ytile * 16 , z : -2});
}
}
}
We are looping through and creating sprite entities throughout the back-
ground. The floor+usefloor portion decides which sprite to use. Just to make
the background a little more interesting, a random sprite is thrown in. Also,
when we are at the edge of the stage, we use a wall sprite. To make our paddles
and ball use the new sprites, we need to modify their declarations. See below
for their new entity declarations:
Crafty . e ( " topPaddle , 2D , Canvas , toppaddle " )
. attr ({ x : 100 , y : 10 , w : PADDLE_WIDTH , h : PADDLE_HEIGHT })
// . color ( PADDLE_COLOR )
The problem is that this event will only be active if the user clicks/taps
on the actual paddle. We want the paddle to follow the user’s finger even
3.4. HTML5 Game Graphics 55
if it is not quite on the paddle. Crafty does not have a convenient global
mouse binding mechanism. It only binds to entities. Therefore, to react to all
mouse/touch events, we need to create a full Canvas-sized entity and bind the
mouse to it. See below:
Crafty . e ( " mouseTracking , 2D , Mouse , Touch , Canvas " )
. attr ({ w :320 , h :480 , x :0 , y :0 })
. bind ( " MouseMove " , function ( e )
{
// get bottomPaddle
var bottomPaddle = Crafty ( " bottomPaddle " );
Our invisible mouseTracking entity takes up the entire screen and consumes
the mouse and touch movements. It then fetches the bottom paddle entity and
sets the X coordinate of the middle of the paddle to wherever the mouse/touch
is.
This will react the same to both touch start and touch move since Crafty
routes those events through the same code as its mouse events. This works
well for our purposes, but keep in mind, touch is not the same as clicking.
Mobile Safari has guidelines and rules regarding when it will send an actual
“click.” It involves the speed of the finger, the type of object being touched,
the potential result, etc. [56]. In some cases, we will need to bind to touchstart
and mousedown separately.
The first animate() command sets up the animation. It states where and
the range of sprites to use. The next animate() starts the animation and says
to loop it forever. What was once a black square at the start of the chapter is
now an animated fireball.
56 3. Crafty
3.5 Summary
We built a fully functional HTML5 game that can run on all modern web
browsers and mobile devices, thus capable of reaching a very large number
of users. Our game has sound, graphics, scoring, AI, keyboard controls, and
touch controls. The game was built entirely on standards-based technologies,
so it will continue to be playable, and maintainable, for years to come.
In Part II, we started off by building it from scratch, and then, in the first
chapter of Part II we migrated to a game engine when we were ready for more
heavy lifting. Crafty was chosen because it was lightweight and flexible, which
is what we need for a quick game of Pong. There are many HTML5 game
engines of varying strengths and weaknesses. Next, we will build a Tic-Tac-Toe
game with the heavier EaselJS engine.
Chapter 4
EaselJS
4.2 Introduction
EaselJS is part of the broader CreateJS suite sponsored mostly by Adobe [58]
and led by Grant Skinner [57]. Its goal is a suite of useful JavaScript libraries
to build rich HTML5 applications. Much of its structure is Flash inspired.
The CreateJS suite includes EaselJS, TweenJS, SoundJS, PreloadJS, and Zoe.
We are mostly interested in EaselJS., which is the portion that focuses
on the Canvas element of HTML5. However, we will be using PreloadJS
and SoundJS as well. Though video games may not be the original goal of
EaselJS, in this chapter we will see it has all the necessary elements to build a
respectable game.
4.3 Tic-Tac-Toe
Here is the problem statement for this chapter:
“We want a 1-player Tic-Tac-Toe game written in HTML5 that
demonstrates some of the abilities of EaselJS.”
57
58 4. EaselJS
Despite being a very basic game, there is plenty of material to make a wor-
thy tutorial with Tic-Tac-Toe. In this chapter, we will learn how to use
EaselJS/CreateJS to preload assets, set up a sprite sheet, interact with mouse
movements, draw shapes (the “O”), have collision detection, and play sounds.
By the time we are done, we will have a good foundation to build more complex
games with EaselJS.
4.4 Setup
CreateJS hosts minified versions of all its JavaScript libraries on their Content
Delivery Network at http://code.createjs.com/. CreateJS allows direct linking
of these minified libraries. For convenience, the example game in this book uses
the all-in-one version of the CreateJS suite. See Listing 4.1 for the complete
EaselJS header. Once again, your version should use versioning, preferably
dynamic versioning.
<! DOCTYPE html >
< html lang = " en " >
< head >
< meta charset = " UTF -8 " >
< title > Tic Tac Toe with EaselJS </ title >
< script src = " createjs -2013.05.14. min . js " > </ script >
<! --
We could instead link d i r e c t l y to
http :// code . c r e a t e j s . com / createjs - 2 0 1 3 . 0 5 . 1 4 . min . js
-- >
< script src = " t i c t a c t o e _ e a s e l j s . js " > </ script >
</ head >
< body onload = " onload () " >
< canvas id = " canvas " width = " 600 " height = " 600 " > </ canvas >
</ body >
</ html >
helloworld . x = 50;
helloworld . y = 10;
The output is in Figure 4.1. EaselJS uses the concept of a stage with
objects that are then put on the stage. After everything is set up the way you
want, call update() to show the result. Flash developers will recognize some of
the concepts of EaselJS because much of it was Flash-inspired.
var manifest = [
{ src : " ti ctactoeb oard . png " , id : " tict actoeboa rd " } ,
{ id : " win " , src : " win . mp3 | win . ogg " } ,
{ id : " lose " , src : " lose . mp3 | lose . ogg }
];
The EaselJS sprite sheet system separates the images into frames based on
the “frames” declaration. It starts at the top left, counting at zero, and then
it reads left to right assigning each a number. The EaselJS documentation
has a lot of detail about this. Sprite sheets also happen to be the easiest way
to display a static image. Our Tic-Tac-Toe board is a one frame sprite sheet.
See Listing 4.4 for the declaration and how to display the image. Note that
our “frame” is the size of the entire image.
See Figure 4.2 for our Tic-Tac-Toe game board being displayed.
62 4. EaselJS
Audio in EaselJS is just as easy to use as it was for Crafty. The sound
needs to be registered, and then it is easy to play. See below:
// only need to register once .
createjs . Sound . registerSound ( " win . mp3 | win . ogg " , " win " );
Like Crafty, EaselJS detects and plays the format compatible with the
user’s browser.
, mous eMoveEve nt );
}
We can now start adding logic. We see the mouse/touch event occur, but
we don’t know which area of the game board the user wants the “X”. To do
this, we slice up our game board into the game areas for hit detection. See
below:
64 4. EaselJS
We are slicing up our game board like an EaselJS spite sheet. These x
and y values were determined through a little math plus experimentation. If
there was a large number of squares in the game, then math would be used
exclusively. However, with just nine squares, hard-coding values is sufficient.
Most of this game will be hard-coded for simplicity.
Next, we need to add collision detection to the mouse move event. See
Listing 4.6 for adding it to mouse move and Listing 4.7 for the actual collision
detection function. This is a very important piece. It is the first time we
needed to detect collision on all four sides.
function mouse MoveEve nt ( event )
{
var newPoint = new Object ();
newPoint . x = stage . mouseX ;
newPoint . y = stage . mouseY ;
newPoint . diameter = 15;
return false ;
}
What we are doing is looping through all the Tic-Tac-Toe areas and
detecting if the mouse collided with that area. The collision detection function
used in Listing 4.7 is the standard “bounding box” collision detection. All the
pathways in the if statement must be true for one rectangle to have collided
with another rectangle. To have adjacent items also be considered collided,
modify the if statement by removing the =.
If the area has collided with the mouse, we save the object’s value to “x” so
it can be looked at later to determine computer movement and win conditions.
The game now knows the state of the game board, but the player doesn’t. We
need to draw the “X”. Note we are adding lots of unneeded complexity with
our mouse tracking. This game would be far, far easier to implement if the
user just clicked/touched the area for the desired placement of their “X” and
then just have the “X” appear. There would be no mouse movement tracking
required, but that wouldn’t make a very interesting tutorial. The reason we
want mouse/touch movement is because we are instead going to follow the
mouse and actually paint the “X” as the player is drawing it. That’s more
interesting, and EaselJS has nice drawing libraries to help us.
To get started with drawing, we need a drawing canvas. We also need a
player color and computer color. We need to prevent the player from drawing
in more than one area. We need to update the stage. There is a lot more code
to add. See Listing 4.8 for the changes. This completes the setup logic. The
rest of the game will happen in mouse events.
66 4. EaselJS
}
function tick () {
stage . update ();
}
Notice the addition of the tick() function. As promised, this is the EaselJS
equivalent to Crafty’s EnterFrame event. If some background logic is needed,
it can go here. As all our events are mouse/touch driven, the only thing we
are interested in is updating the stage. Now that you have seen how to do
it, this tick event will be removed and the stage will be manually updated as
needed. Also, notice xoCanvas has been added to the stage. This is being used
as a container to hold all the X’s and O’s being drawn. When we draw, we
will draw to this canvas. Adding this canvas to the stage draws all the X’s
and O’s.
To mimic the user drawing, we will remember the previous point and then
draw a line to the next point. It will happen as quickly as the mousemove /
touchmove events fire. Although the underlying engine is drawing lines, to the
user, it will look curved and follow their pointer. See Listing 4.9 for the code.
4.8. Tic-Tac-Toe Implementation 67
if ( previousPoint . x > 0)
{
xoCanvas . graphics . clear (). se tStrokeS tyle ( newPoint . diameter ,
’ round ’)
. beginStroke ( colorPlayer )
. moveTo ( newPoint .x , newPoint . y )
. lineTo ( previousPoint .x , previousPoint . y );
}
previousPoint . x = newPoint . x ;
previousPoint . y = newPoint . y ;
Listing 4.9. Mouse draw events.
What is happening is as soon as touch/mousedown occurs, we start listening
for movement. When the move event fires, we can draw a line from the previous
point to the new point. setStrokeStyle() says to use a round pattern with
a radius of 15. moveTo() moves the paintbrush to the mouse’s X,Y. lineTo()
draws the line to the previous point. When the mouseup/touchend event occurs,
we stop looking for movement. We also set the previous point to 0 so we know
not to look at it when we start again. This code is sufficient to draw an X.
See Figure 4.4.
The game also is tracking that the square has been hit. Unfortunately, the
game does not restrict just 1 square per turn. We need to add the logic from
the variables added back on in Listing 4.8. We need to restrict to just one
square and two strokes and one turn before the computer can take over and
set the variable flags back. See Listing 4.10 for the completed mousemove event
handler.
function mouse MoveEve nt ( event ) {
if ( playerTurn ) {
var newPoint = new Object ();
newPoint . x = stage . mouseX ; newPoint . y = stage . mouseY ;
newPoint . diameter = 15;
previousPoint . x = newPoint . x ;
previousPoint . y = newPoint . y ;
Several pieces of logic have been added. Let’s go through them. We are
only drawing during the player’s turn and a valid collision was detected. What
makes a valid collision? A valid collision is when the player hits a square that
has not yet been assigned. Also, the player can only have one valid collision
per turn. The collision is detected on the first stroke. The player gets two
strokes so they can complete their X.
At the end of mouseup/touchend, a check is made to see if the player has won.
If not, it is the computer’s turn. See Listing 4.11.
4.8. Tic-Tac-Toe Implementation 69
} else {
// did not win . Computer turn
computerMove ();
if ( checkWin ( " o " ))
{
console . log ( " computer win " );
gameOver = true ;
}
}
}
}
}
Indeed, our computer player is very dumb. It just scans through and finds
the first available board position and takes it. This needs to be improved.
Tic-Tac-Toe is a “solved” game. If both players are playing optimally, the
game always ends in a tie. The perfect strategy will never lose. There are
thousands of possible board movements, but a winning AI strategy needs only
be concerned with about a handful of them. If you wanted a perfect player,
70 4. EaselJS
consider this common strategy (note that while opening with the corner is
better strategically, it is not necessary):
• Always take the win.
• Always take the block.
• Always take the middle.
• Note that the board has symmetry.
• Hard code the rest of the scenarios.
This will reduce the number of game layouts from a few thousand to a
couple hundred (rotate the board to find a matching layout). How to program
a perfect tic-tac-toe player is not covered in this book, but it is a useful exercise.
The first three strategies are easy and will be implemented. See below for
scanning for the win condition:
// check for a win
for ( var i =0; i < areaArray . length ; i ++)
{
if ( areaArray [ i ]. value == " " )
{
areaArray [ i ]. value = " o " ;
if ( checkWin ( " o " , false ))
{
areaArray [ i ]. value = " o " ;
console . log ( " Found a win at " , i );
addOh ( areaArray [ i ]. x , areaArray [ i ]. y );
return ;
} else {
areaArray [ i ]. value = " " ;
}
}
}
With a touch of AI, our computer player is now a lot smarter. It is not
perfect, but at least the player will need to try to fool it. We now need to
draw the O for the computer. See below:
function addOh (x , y )
{
x += 50; y += 50; // move to center
var circle = new createjs . Shape ();
circle . graphics . beginFill ( colorComputer ). drawCircle (x ,y ,50)
. beginFill ( " #000000 " ). drawCircle (x ,y ,50 -15);
stage . addChild ( circle );
}
// cols
if ( check3 (0 ,3 ,6 , winChar , drawit )){ return true ; }
if ( check3 (1 ,4 ,7 , winChar , drawit )){ return true ; }
if ( check3 (2 ,5 ,8 , winChar , drawit )){ return true ; }
// diagonals
if ( check3 (0 ,4 ,8 , winChar , drawit )){ return true ; }
if ( check3 (2 ,4 ,6 , winChar , drawit )){ return true ; }
return false ;
}
A more elegant solution would be to apply some math, but this is not
necessary for Tic-Tac-Toe. There are only eight possible win conditions. It
is easier to just hard-code. The check3() function takes the three areas, looks
to see if they match the winChar, and returns true if it does. The drawit gets
passed to state whether to draw the winning line.
Finally, we need to draw the final slash to represent the win. See below:
function drawWin (a , b )
{
var useColor = colorPlayer ;
if ( a . value == " o " )
{
useColor = colorComputer ;
createjs . Sound . play ( " lose " );
} else {
createjs . Sound . play ( " win " );
}
var s = new createjs . Shape ();
s . graphics . s etStroke Style (16 , " round " , " round " )
. beginStroke ( useColor )
. moveTo ( a .x , a . y ). lineTo ( b . x + b . diameter , b . y + b . diameter );
stage . addChild ( s );
}
drawWin() accepts two areaArray objects. It looks at one to see the winning
value so it knows which color and sound to use. It starts at the top left of a
and draws to the bottom right of b. This is an easy method to cover the win
zones, but it gives the line a slant, but overall, it looks well enough for our
purposes. See Figure 4.5 for our final Tic-Tac-Toe game.
4.9 Summary
Though game development is not really its original focus, EaselJS, along with
the broader CreateJS suite, has all the tools needed to create a respectable
HTML5-based 2D game. Our entire game is only 400 lines, and we were able
to support both mobile and mouse, sound, preload images, add our own hit
detection, add computer logic, and ultimately win (or lose) the game. This
base could easily be expanded for a bigger game.
In the next chapter, we will build a new game with another engine. Impact
will be used to create MechaJet, a side-scrolling action, space-style, shooter
game that will introduce physics, tiles, and other concepts. Impact is the last
2D engine before diving into the very complex Turbulenz 3D game engine.
Chapter 5
Impact
5.2 Introduction
Impact is an HTML5 JavaScript game engine developed by Dominic Szablewski
[63]. It is the only proprietary game engine reviewed in this book. Despite its
proprietary nature, the engine is delivered in source form so you can modify it.
Also, the barrier to entry is (as of this writing) a modest, no-royalty, one-time
$99 fee. You get a lot for $99:
Impact
The engine itself.
Weltmeister
A well-organized level editor with tight Impact integration.
Source code for Impact and Weltmeister
Though these tools are not open source, you can still get benefits of
low-level customization with access to the source code itself.
Demo games with source code
The examples demonstrate collisions, scrolling, and physics.
75
76 5. Impact
5.3 Setup
Setup is a bit different from the other game engines. Though Impact devel-
opment mostly works standalone, Weltmeister saving and loading requires
the use of a web server with PHP. Also, the files in your project have to be
structured a particular way. Once again, this is mostly to serve Weltmeister. If
you do not care to use Weltmeister, you have much more flexibility. However,
Weltmeister is a really nice tool, and adhering to its requirements is a small
price to pay.
Publishing with Impact requires access to command-line PHP. This process,
called baking, is a minify utility that combines all your files into a single .js file
for distribution. You must bake your project before publishing. Otherwise,
you may accidentally distribute the Impact source code.
Mac users with a web server probably have PHP in their system path. If you
do not, MAMP puts the php executable at:
/Applications/MAMP/bin/php/php5.3.6/bin/php
Note that your version of PHP may be slightly different. Replace “php5.3.6”
with the correct version. Take the full path you found and modify “bake.bat”
for Windows and “bake.sh” for Mac to use it.
this chapter, we are developing the game MechaJet. Therefore, I have a folder
called “mechajet impact” as a subdirectory of my web server root at “www”.
Inside the project root there are a handful of files and directories. These serve
a specific purpose.
lib
This contains the core logic for Impact, Weltmeister, and your game. All
your code will go into the “game” subdirectory inside this directory. All
projects have their own copy of Impact and Weltmeister.
media
Graphics, tile sheets, audio, and other media go into this directory. This
directory is by convention. The actual media files can be anywhere. You
point to them when declaring your entities.
tools
The tools required to minify (bake) your project for publishing are found
here.
dev.html
This is a file I created with the minimal required code to run and test
the unbaked game.
weltmeister.html
Run this in your browser to launch the Weltmeister level editor.
78 5. Impact
entities
The core logic for your game objects are here. You will spend most of
your time programming here.
levels
This holds your level files that are saved by Weltmeister.
main.js
The boilerplate start-up code that ultimately launches your first level
goes here. This also contains your code that doesn’t quite fit into a
normal entity.
If you wish to change the arrangement of your files and have them still
detectable by Weltmeister, there are some configuration options available
inside lib/Weltmeister/config.js
Copy and paste the directory. Rename it to your game’s name, then edit
it directly as your starting point. This is the recommended way to develop
with Impact.
Impact is compact and well organized and, like before, little from the
previous chapters will be reused in order to follow the Impact way of doing
things.
To support both keyboard and touch controls in the sidescroller, “touch
arrows” are being added. The best way to explain is to show the final screenshot.
See Figure 5.3.
80 5. Impact
5.5.2 Entities
With Impact, every sprite that the player interacts with is an entity. In our
game, we have four main entities with several subentities. The main entities
are:
Player
The human-controlled player. The player can fly, run, get hurt, shoot,
die, and respawn. Whenever the player shoots, it spawns another entity
(PlayerBullet). For convenience, the bullet entity can be defined in the
same file as player. However, only the root entity with the file named
after it will appear in Weltmeister. See the sprite sheet in Figure 5.5.
The sprites are tiny because Impact will be double-sizing everything.
This helps performance. There is animation for rolling, flying, falling,
and a death sequence.
Buzzard
The buzzard flies back and forth, applies damage to the player, and dies
in one hit. It also has a death sequence. See the sprite sheet in Figure
5.6. There is animation to fly and explode.
Corridor
The corridor to the next level simply has a glow effect. Its sprite sheet
is in Figure 5.7.
Bomb
Our bomb explodes when the player touches or shoots it. Instead of a
sprite sheet, it is just a static image. It will be replaced with another
static image when it explodes. These images are shown in Figure 5.8,
and 5.9.
What you don’t see being defined above are the entities for our HUD (our
“Heads-Up-Display,” the health bar and arrow controls). Unfortunately, there
is no convenient spot within Impact to put a HUD.
We could declare our health and arrows to be immortal entities with
no gravity and hit detection. That is what we did with Crafty Pong, and
that worked fine. Reusing that strategy might work here, but my experience
has always found this path difficult. Impact and Weltmeister makes lots of
assumptions for you, and if you aren’t careful, your health bar may scroll off
the screen along with your other entities. Instead, I have found the best way
to do a HUD with Impact is to pull that logic out of the entities and directly
apply the updates manually within the main game loop (defined in main.js).
That way, we regain the control we need, and there is no chance Impact may
misinterpret our intent with it. This technique will be shown later.
ig . module (
’ game . entities . buzzard ’
)
. requires (
’ impact . entity ’
)
. defines ( function () {
EntityBuzzard = ig . Entity . extend ({
size : { x :16 , y :16} ,
gravityFactor : 0 ,
collides : ig . Entity . COLLIDES . PASSIVE ,
type : ig . Entity . TYPE .B ,
checkAgainst : ig . Entity . TYPE .A ,
Impact uses a file include structure that may have been inspired by Google
Closure (for those who may have seen it). When you create an entity, you
are actually creating a module that gets loaded into the Impact engine and
Weltmeister. Wherever this entity is used, you must include the module in its
ig.module list.
The module declaration is the file path minus the .js. The entity name is
called Entity<Name Of Entity>. Both parts are capitalized. You can have other
entities in the file, but the name of the core entity is the same as the file name.
That means our “buzzard” entity is declared as EntityBuzzard in a file
located at game/entities/buzzard.js. This is the structure required to work
with Weltmeister.
Inside the file, we have already declared some settings for our entity. Impact
has lots of built-in parameters. Only a handful are actually required as it will
make assumptions for whatever is missing. In our buzzard entity, we declared
the size to 16 pixels by 16 pixels. The gravityFactor is 0 meaning Impact’s
gravity simulation has no effect on the buzzard.
Impact’s built-in collision system is far smarter and more fluid than the
crude method we used for EaselJS. It is actually a two-step system where it
collides with the world and moving objects. Whereas our own collision system
checked after we collided and then moved the object, Impact checks before
it moves and has separate reactions depending on the collision settings. The
different reactions are:
84 5. Impact
ACTIVE
if ( this . vel . x == 0)
{
if ( this . flip )
{
this . flip = false ;
this . vel . x = -10;
} else {
this . flip = true ;
this . vel . x = 10;
}
}
measure seconds of game time (not real time). Using the Impact Timer, the
BuzzardBoom entity calls its own kill() function after appearing on the screen
for 1 second. Note that because Impact adds default gravity, BuzzardBoom will
“fall” out of the sky.
kill : function ( ) {
ig . game . spawnEntity ( EntityBombBoom , this . pos .x -16 ,
this . pos . y - 16);
this . boomSound . play ();
this . parent ();
},
First, the collision type is PASSIVE so it doesn’t slow down the player.
Second, the check() function gives the player 50 damage and then calls kill()
on itself. Like the buzzard, the kill function is overloaded. It spawns a no-
collision “boom” entity called EntityBombBoom. Some offsets are applied because
the bomb’s boom is significantly larger than the bomb item. This puts the
core of the explosion in the center.
88 5. Impact
Like all the previous engines, Impact will load the appropriate audio (.ogg
or .mp3) depending on the browser’s capabilities. This is performed with
declaring the audio bombboom.*. The correct file is played whenever play() is
called.
5.6 Weltmeister
Before starting on the player entity, now is an appropriate time to draw the
first level with Weltmeister. We have enough information to put together a
collision map/tile map. The level needs to come before the player because
5.6. Weltmeister 89
development of an entity as complex as our player entity will not go far without
putting it in a level.
If you unzipped and dropped in your Impact directory as-is on to your web
server, then your Weltmeister installation can be reached at:
http://localhost/your dev directory/weltmeister.html
Weltmeister will scan your game/lib/entities directory for *.js files. If you
followed the naming structure stressed throughout the chapter, it will find your
entities and make them available to you in the editor. See the screenshot in
Figure 5.10 for a level in progress. If you are having problems with Weltmeister,
make sure you have PHP working.
When first going to Weltmeister, you will be presented with a blank screen
with the always-available “entities” layer. The first thing you will want to
do is select this entities layer and then press the space bar to see if all the
entities you created in game/lib/entities are available. If they are, press space
to cancel and then use the plus symbol near the top right to create two more
layers and rename them. You will eventually have these three layers named
“entities,” “main,” and “collision.” As you create these layers, you are given
these options:
Name
The name of the layer. Note that the name “collision” is reserved for
the collision layer.
Tileset
Select the image that represents your tiles.
90 5. Impact
Tilesize
It must be square. The default is 8, meaning 8 × 8 pixels.
Dimensions
Decides the size of your layer. The size is measured by tile size. A 20 × 30
dimension with a tilesize of 8 will be a 160 × 240 layer.
Distance
Determines scroll speed. “1” will scroll the same speed as the entities.
“2” will scroll slower, and “3” will be even slower. This can be used to
give a parallax scrolling effect.
Pre-Render in Game
Will pre-render your map. This can improve performance. The trade-off
is that your tiles cannot be animated.
Repeat
Will repeat your tiles as it scrolls by. This can be used to create a
repeating background effect.
Is Collision Layer
States the layer is a collision layer and will start using collision tiles
allowing you to draw a collision map. Impact comes with a set of collision
tiles you can use with your main layer tile set.
Link with Collision
Will allow you to create tiles that automatically have a solid collision tile
behind it located on the collision layer. This can save you some mouse
clicks.
Once your layers are configured, save your level. The convention is to save
Impact levels into the directory lib/game/levels. Weltmeister will append “.js”
to the name.
You are now ready to draw a level. First, you will probably want to draw
collision tiles around the border to prevent the player from falling off the world.
When working with the level editor, space will display the entity/tile selection
box. Click to select. Then left click to paint. Holding right-click, drag the
view around on your screen. Use the mouse wheel to zoom.
To erase an entity, click the entity layer (if not already selected), and then
press the delete key. Erasing tiles is not so obvious. To erase a tile, click the
appropriate layer, then press space to display tile selection, and then look at
the top left corner of your tile selection box. There is a blank highlighted tile
standing alone. Click the blank highlighted tile. Now paint the blank tile over
the tiles you want to delete. This will delete them.
You now know the basics to build a level, but before we move on, here are
a few handy keyboard shortcuts.
5.7. main.js 91
• You can copy a tile by clicking it while holding down the shift key.
• You can copy multiple tiles by dragging while holding down the shift
key.
5.7 main.js
Like the name suggests, main.js is the entry point of the game. It contains
the logic necessary to initialize the canvas, key binding, load the first level,
and then start the game. Actually, right now, it won’t load the first level
because Weltmeister just created it, and main.js does not yet know it exists.
See Listing 5.7 for the minimum amount of code to launch your level with
keyboard and mouse binding.
ig . module (
’ game . main ’
)
. requires (
’ impact . game ’ ,
’ game . levels . mechajetlevel1 ’ ,
’ game . entities . player ’
)
. defines ( function (){
MyGame = ig . Game . extend ({
init : function () {
ig . input . bind ( ig . KEY . UP_ARROW , ’up ’ );
ig . input . bind ( ig . KEY . DOWN_ARROW , ’ down ’ );
ig . input . bind ( ig . KEY . LEFT_ARROW , ’ left ’ );
ig . input . bind ( ig . KEY . RIGHT_ARROW , ’ right ’ );
ig . input . bind ( ig . KEY . MOUSE1 , " CanvasTouch " );
ig . input . bind ( ig . KEY .X , " xkey " );
this . loadLevel ( LeveMylevel );
}
}
});
// 60 fps , 320 x240 , scaled by 2
ig . main ( ’# canvas ’ , MyGame , 60 ,480/2 ,320/2 , 2 );
});
Though these events do not do anything yet, we also binded the mouse,
keyboard, and touch to our game. The reaction to these events will be
processed in the player entity. For now, we need to add our arrow buttons so
non keyboard users have control. We will be using the technique discussed
earlier. These buttons are managed outside Impact entities. See Listing 5.8.
First, you will create the buttons as objects. This makes them easier
to track and pass in to our collisionDetect() function. The collisionDetect()
function is our standard bounding box check identical to what was used in Tic-
Tac-Toe. We have to use our own collision detect because the arrow buttons
are not entities. buttonCheck() simply checks to see that the mouse/touch is
actually occurring before calling collisionDetect. Finally, the draw function
was overloaded to draw our arrow keys after it finishes drawing the other
sprites.
5.8. Player Entity 93
Our player entity wraps up all the techniques we used for the Bomb (a death
sequence) and Buzzard (movement) as well as adding more (projectiles, control,
and respawn). First, we will add just enough code so the player can be placed
in the game by Weltmeister. See Listing 5.9.
ig . module (
’ game . entities . player ’
)
. requires (
’ impact . entity ’
)
. defines ( function () {
EntityPlayer = ig . Entity . extend ({
size : { x :16 , y :16} ,
gravityFactor : 7 ,
type : ig . Entity . TYPE .A ,
checkAgainst : ig . Entity . TYPE .B ,
flip : false ,
collides : ig . Entity . COLLIDES . PASSIVE ,
animSheet : new ig . Anim ationShe et ( ’ robot . png ’ , 16 , 16 ) ,
init : function ( x , y , settings ) {
this . parent ( x , y , settings );
this . addAnim ( ’ idle ’ , 0.1 , [0 ,0] );
this . addAnim ( ’ roll ’ , 0.1 , [0 ,1] );
this . addAnim ( ’fly ’ , 0.1 , [12 ,13] );
this . addAnim ( ’ death ’ , 0.7 , [24 , 25 ,26] , true );
}
});
});
Our entity doesn’t do anything, but at least Weltmeister can see it, and
we can put it in our level. With our sprite available, we will be developing
main.js and player.js to make all the controls available. Because we’ve already
binded the keyboard and tracked buttons in main.js, we add the control logic
to the player.js. There is a lot of sprite manipulation happening with the
movement. Our player can roll, fly, or fall. This can happen in either direction.
Therefore, we need to keep track of the flip state and if the player is on the
ground and swap out animation accordingly. For clarity, only the X button
and left buttons are shown in Listing 5.10.
94 5. Impact
update : function () {
// ig . log ( this . friction .x , this . vel . x )
if ( ig . input . state ( ’ left ’) || this . leftBu ttonDown ) {
this . accel . x = -100;
if (! this . vel . y )
{
this . friction . x = 35;
this . currentAnim = this . anims . roll ;
} else {
this . friction . x = 0;
this . currentAnim = this . anims . idle ;
}
this . currentAnim . flip . x = true ;
flip = true ;
}
if (! this . vel . y && ! this . vel . x )
{
this . currentAnim = this . anims . idle ;
this . currentAnim . flip . x = flip ;
}
if ( ig . input . state ( ’ xkey ’) || this . xButtonDown ) {
var bullets ettings = { flip : this . currentAnim . flip . x };
// this forces 1 bullet at a time
var alreadythere =
ig . game . g e t E n t i t i e s B y T y p e ( E n t i t y P l a y e r B u l l e t )[0];
if ( ! alreadythere ) {
this . shootSound . play ();
ig . game . spawnEntity ( EntityPlayerBullet ,
this . pos .x , this . pos .y , bulle tsetting s );
}
}
this . parent ();
},
. requires (
’ impact . game ’ ,
’ game . levels . mechajetlevel1 ’ ,
’ game . levels . mechajetlevel2 ’ ,
’ game . entities . player ’
)
/* REMOVE FOR CLARITY */
update : function () {
this . parent ();
// screen follows the player
var player = this . g e t E n t i t i e s B y T y p e ( EntityPlayer )[0];
if ( player ) {
this . screen . x = player . pos . x - ig . system . width /2;
this . screen . y = player . pos . y - ig . system . height /2;
There are a few things happening in the new main.js update function.
First, we added the player entity to our requirements list. Next, in the update
function, we fetch the player entity. Impact fetch returns an array. We only
need the first. We check to see if the player exists. If it does, we center the
screen on the player. Because we are doing this constantly with every update,
this will have a smooth-scrolling effect. Impact is well optimized to handle
smooth scrolling for this simple game, but note that a more efficient strategy
would be to scroll only when the player is near the edge of the screen.
Finally, we check the state of our buttons with the collision checks we
added earlier. The player looked at these button states in its update function
back in Listing 5.10. The bullet-spawning section we added will need to be
commented out, but other than that, our player should now be able to move
around the screen by keyboard, mouse, or touch. The gravityFactor will cause
the player to fall, and the collision tiles will cause the player to stop when it
lands on a platform.
We are now ready to add the player’s bullets. See Listing 5.12 for the
bullet entity.
96 5. Impact
E n t i t y P l a y e r B u l l e t = ig . Entity . extend ({
size : { x :16 , y :16} ,
collides : ig . Entity . COLLIDES . PASSIVE ,
type : ig . Entity . TYPE .A ,
gravityFactor : 0 ,
checkAgainst : ig . Entity . TYPE .B ,
update : function () {
this . parent ();
if ( this . vel . x == 0) {
this . kill ();
}
},
} else {
this . vel . x = 100;
}
this . addAnim ( ’ idle ’ , 0.1 , [0 ,1] );
}
});
During the update function, all the normal player interactions have been
bypassed so the only thing that happens is the death animation gets played.
After 2 seconds, this.killcallback() gets called to complete Impact’s kill()
process. The restart sound gets played and another player entity is immediately
spawned to the original starting location.
5.9 Level 2
The first level of MechaJet is now playable. However, when the Corridor is
reached, there is no level 2. The game will crash. We need to go back to
Weltmeister and create a 2nd level. See the screenshot in Figure 5.11.
Level 2 is built the same way as level 1. The actual level is not impor-
tant. Look in the bottom far right. Corridor is selected. Inside the corridor
parameters there are “Key:” and “Value:” fields. These let you overload
default parameters declared in the entity. Remember the tolevel property
declared when first programming the Corridor entity (Listing 5.6)? We are now
overloading the default value from within Weltmeister. This will cause this
corridor to go back to level 1 when it is reached. Any field can be overridden
this way. This feature allows a great level of flexibility within Weltmeister.
98 5. Impact
This completes the game. The player will go to the 2nd level when reaching
the 1st corridor, and the player will be sent back to the 1st level when reaching
the 2nd corridor. However, from a game design standpoint, the game is
arguably not fully complete. There is no way to “win” the game. It is an
endless loop from level 1 and level 2. However, it is complete enough for our
tech demo.
5.10 Summary
In this section, we built MechaJet for Impact. We looked at the Impact folder
structure and Impact’s general way of doing things, particularly with getting
our Entities to work with Weltmeister, the Impact level editor. From there,
one-by-one, we created each entity and put them in our level. We then created
5.10. Summary 99
Turbulenz
6.2 Introduction
Turbulenz is an MIT-licensed open-source (as of version 0.26+) HTML5
JavaScript/TypeScript game engine developed by Turbulenz Limited [ 91]. It
is the only game engine featured that is powered by WebGL. Though some
engines may allow WebGL to be used as an alternative choice for rendering,
Turbulenz is WebGL at its core. This aspect has pros and cons which will
be addressed in this chapter. Turbulenz is large, complex, and very powerful.
The SDK (software development kit) encompasses many aspects of game
development. Beyond the normal libraries developers would expect from a
large HTML5 game engine, such as image, sound, animation, and physics, it
also contains API to save games, generate stats, and even a payment system
that connects to the Turbulenz services [94]. These extra services will not be
covered.
101
102 6. Turbulenz
6.3 WebGL
All our HTML5 game demos thus far were 2D and powered by HTML5
Canvas. However, not technically part of HTML5, but often included when
discussing, many browsers offer 3D support in the form WebGL. WebGL is
based on OpenGL ES 2.0 and is maintained by the Khronos Group, the same
maintainers of OpenGL [72]. OpenGL ES is a mobile-optimized subset of
the normal OpenGL standard that has been available on desktop computers
for decades [73]. OpenGL ES 2.0 is the most popular revision because it is
available on Android and iOS devices since 2009. However, up until recently,
accessing the OpenGL features did not occur in the browser. The function
calls were only available in that platform’s native development environment
(such Java, Obj-C, C++).
With WebGL, we can now have a hardware-accelerated 3D API available
directly in the browser. As of this writing, WebGL availability is sparse and
only within 2012+ versions of the Firefox and Chrome (not IE 10) desktop
web browsers and limited support in Chrome/Firefox for Android. Still, 3D
hardware acceleration is a welcome addition, and support should only improve.
For now, if you as a developer wish to use WebGL, then you must accept that
your audience will be limited to mostly non-IE desktop users. That is still a
massive audience for your game, and mobile (and IE) will eventually catch up.
Microsoft has already announced IE11 will support WebGL [95], so IE may be
included in your test cases by the time you read this book.
This chapter bypasses direct WebGL to let Turbulenz handle the heavy
lifting. If you want to dive deeper with a lower-level API, take a look at the
popular “three.js” library located at http://threejs.org/.
6.4 Installation
Because this project is developing very quickly, be sure to look at http://docs.
turbulenz.com/installing.html for the latest instructions. As of this writing,
the best way to get Turbulenz is to download and install their SDK installer
directly from the source located at GitHub (https://github.com/turbulenz/
turbulenz engine). They perform regular releases of the SDK, but personal
development has shown that it leaves out a couple useful features, such as the
python scripts found in “manage.py”. Installation from source is long and
tedious. Both methods will be detailed here. Try the official installer first.
Note that if you are only looking for game engine libraries (Protolib), the
official SDK releases are more than sufficient.
6.4. Installation 103
1. It will install 32-bit Python 2.7 (if on Windows). Note that this
is not the latest version of Python. Python 2.x is a legacy support
branch and is the version bundled with Mac (particularly version 2.6 for
Mountain Lion, though 2.7 is made available [96]). The reason for 2.x
is because Python 3.x breaks some backwards compatibility for various
structural enhancements [92].
3. For Mac, Xcode is required. You do not need an iOS or Mac dev
license. Turbulenz needs the command line tools from it. This can be
found by going to Xcode → Downloads → components and then install
“Command Line Tools”. Xcode can be downloaded from the Mac App
Store.
4. Turbulenz installs a web server for you (called the “Local Devel-
opment Server”). It is running on port 8070. You can use XAMPP/-
MAMP/WAMP instead, but the official development server is highly
desirable to be able to test against Turbulenz back-end services. You do
not need to move your www directory. The local development server is
capable of serving any Turbulenz project local to your machine. The local
development server is also preconfigured with lots of helpful samples.
After installing:
• For Windows, find “Run Local Server” in your Start menu. It should
launch a console web server. See Figure 6.1.
There are lots of samples running on the local server. Flip through and
take a look at them. Note that some of the engine was written in TypeScript.
TypeScript is an open-source superset of JavaScript developed by Microsoft [93].
One of the pitfalls of flexible interpreted languages, such as JavaScript, is that
optimization is tedious. TypeScript introduces classes, interfaces, modules,
and optional static typing. This allows easier development for large-scale
projects as well as the ability to detect problems at compile time (thus, saving
tedious hand-testing development time). Because TypeScript compiles to
JavaScript, the language choice is seamless to the end user. TypeScript has
often been compared to Dart and CoffeeScript because they also compile to
normal JavaScript [97, 98].
This book does not dive into TypeScript. However, it is a nice language,
and you may find yourself needing to look at it or wanting to use it for
more serious Turbulenz (or other large web app) development. Regardless, at
minimum, you will probably want syntax highlighting. Microsoft offers official
TypeScript support for Visual Studio (Windows only) or Sublime Text 2 (for
Windows and Mac).
6.4.2 Protolib
Protolib is the JavaScript object that encompasses a large portion of the
Turbulenz SDK. You will be using protolib (and jslib) a lot to develop your
6.5. From Source 105
game. If you do not wish to connect to Turbulenz’s backend services and want
only a nice game library, then these libraries are what you need. You can
copy them off the sample apps and build a respectable game with just them
(like the rest of Turbulenz, they are MIT licensed). However, we are going to
continue on and use standard Turbulenz build procedures.
This clones the Git repo, navigates to the directory, and initializes the
submodules. This will take a few minutes. Once it completes, close your
Git shell if you are using Git Bash. It is no longer needed. The following
commands will be with a normal command prompt. If you do not have the
Visual Studio compiler, you should get it now. Also, the next steps require
virtualenv. It is possible that you already have virtualenv because it is common
to bundle it along with Python. However, it is not bundled with the official
Python installer from python.org. First check to see that you have virtualenv
installed. To do this, run
C :\ Python27 \ python . exe -m virtualenv -- version
3. Download https://raw.github.com/pypa/pip/master/contrib/get-pip.py
4. C:\Python27\python.exe your\download\path\to\get-pip.py
This will install pip, a handy python installer tool. Then, it uses pip to
install virtualenv. Test your virtualenv again with:
C :\ Python27 \ python . exe -m virtualenv -- version
106 6. Turbulenz
This will fetch and set up and activate your local environment. After
you are finished, you should see “env” next to your command prompt. See
Figure 6.2.
Setup has been completed. Launch the local development server with:
local_server -- launch
You may want to use a second window to leave the local development
server running. Note that whenever you launch a command prompt, you need
to navigate to the Turbulenz source directory and run “env\Scripts\activate”
to activate the development environment. This includes launching the web
server.
Launch your web browser and go to http://127.0.0.1:8070/. You should
see the Turbulenz dashboard and sample apps and games. You have now
6.6. Hello Turbulenz App 107
replicated the setup from the installer in the previous section. The rest of this
chapter assumes you installed from source.
Turbulenz will invoke the build script. If you wish to add your app to
the standard build list used during installation, open “manage.py”, jump to
around line 250, and add your game to this python list:
app_dirs = [ ’ samples ’ ,
’ apps / inputapp ’ ,
’ apps / multiworm ’ ,
’ apps / sampleapp ’ ,
’ apps / templateapp ’ ,
’ apps / viewer ’ ,
’ apps / helloworld ’ ,
’ apps / protolibsampleapp ’]
Now, the “build all” command, “python manage.py apps” will build yours
along with all the others. Next, we need to add this game to the local
Turbulenz server. Assuming you left the local server running, navigate to
http://127.0.0.1:8070/.
3. Change the title and the slug to the name of your game, such as “Hello
World”.
The slug will auto-update. The slug is the unique web path, such as
“localhost/helloworld”.
Congrats! You have Turbulenz running a game. The game does nothing,
so let’s fix that. Open scripts/app.js and modify “initFn” and “updateFn”
with these lines in Listing 6.1:
init : function initFn ()
{
var protolib = this . protolib ;
var mathDevice = protolib . getMathDevice ();
protolib . setClearColor ( mathDevice . v3Build (0 , 0 , 0));
// Intialization code goes here
whiteV3 = mathDevice . v3Build (1 , 1 , 1);
},
update : function updateFn ()
{
var protolib = this . protolib ;
// Update code goes here
if ( protolib . beginFrame ())
{
// Render code goes here
protolib . drawText ({
text : " Hello World ! " ,
position : [ protolib . width / 2 ,
protolib . height / 2] ,
scale : 10 ,
alignment : protolib . textAlignment . CENTER ,
v3Color : whiteV3
});
protolib . endFrame ();
}
},
deps.yaml
This contains the listing of assets used by your game. If you want to
include any graphics, audio, etc, reference it in this file. The build script
will give it a hash in mapping table.json and copy it to the static-
max/directory. The “staticmax” directory contains files told by the
browser to very aggressively cache, which is why the file names are hashed.
The deps.yaml file also is used by the build script when running “python
manage.py apps apps\mygame”. If the build sees a missing file, it will
look in your turbulenz engine\assets for a copy to bring forward.
manifest.yaml
This contains all the game’s meta information used by the Turbulenz
server. Most of the time, you will be configuring this file via the Turbulenz
web interface. This is where the changes are saved. It contains path,
slug, cover art file, deployment information, etc.
templates
This directory contains the HTML and script logic to build the header
and footer, and says which JavaScript to include for your game. Once
it is configured, it should not need to be touched except when another
JavaScript library needs to be included or surrounding HTML structure
is changed.
scripts/app.js
This file contains your game logic. You will spend most of your time
here.
That is the bird’s-eye view of the files most edited when working with
Turbulenz. For more information, the docs are an invaluable resource. They
are located at http://docs.turbulenz.com/.
110 6. Turbulenz
protolib . setPreDraw (
function ()
{
floor . render ( graphicsDevice , protolib . globals . camera );
}
);
Physics2DDevice This creates our “World”, the root object that holds all
our other objects. This is identical to the notion of a “stage” used in
EaselJS.
For Turbulenz, instead of adding objects to our stage, we are now adding
them to our 2D “World”. We also pass a 2D gravity parameter that
states a vector of X = 0 and Y = 2. Our objects will fall downward. The
gravity vector could point anywhere to make some interesting games.
Turbulenz is the only featured game engine in this book that can handle 3D
objects exported with 3D graphics animation software, such as Autodesk Maya
or Blender. It accepts a translated COLLADA formatted scene/object, usually
represented as a “.dae” file. COLLADA is an open 3D format maintained by
the Khronos Group, the same maintainers as OpenGL and WebGL [99]. It was
designed to be a transition format between other 3D systems. Turbulenz does
not load COLLADA files directly. It converts it to a JSON (JavaScript Object
Notation) format as part of the build process and then puts it in “staticmax”.
Loading and processing 3D objects is a fairly expensive browser operation, so
keep that in mind when using 3D objects.
The above code simply sets up a 3D Torus, a primitive ring shape in 3D
designs. It then sets up a rotation matrix around the X axis. This will cause
the mesh (a collection of vertices and edges that represents a 3D object) to
rotate as time passes creating a nice effect. Try changing “v3BuildXAxis” to
“v3BuildYAxis” to see a different rotation. The meshPositionDirection variable
tracks the movement direction inside the game loop.
Next, the game floor is set up as 0.1 tall (just enough to add a little texture)
and 2 * viewport-wide box at the bottom of the screen:
var solidFloor = {};
solidFloor . width = viewPort . width * 2;
solidFloor . height = 0.1;
solidFloor . position = [0 , viewPort . height ];
solidFloor . phys2DPolygon = phys2D . c r e a t e P o l y g o n S h a p e ({
vertices : phys2D . c r e a t e B o x V e r t i c e s ( solidFloor . width ,
solidFloor . height )
});
solidFloor . phys2DBody = phys2D . c re a te Ri gi d Bo dy ({
type : ’ static ’ ,
shapes : [ solidFloor . phys2DPolygon ] ,
position : solidFloor . position
});
world . addRigidBody ( solidFloor . phys2DBody );
114 6. Turbulenz
Turbulenz supports three rigid bodies, and they are each featured in the
game. They are “static”, “kinematic”, and “dynamic” [100]. Static bodies
cannot move, which is why it was chosen for our floor. Kinematic bodies are
like static, except they can be moved around the screen. They are appropriate
for player-controlled entities. Dynamic bodies react to the world’s physics.
They are used for the marbles and bricks.
Developers familiar with the Box2D physics engine should recognize the
terms “static”,“kinematic”, and “dynamic”. Box2D uses the exact same terms
for its bodies [101] and they have the same meaning. Turbulenz’s 2D physics
engine is not Box2D, but it follows many Box2D patterns.
Next, to set up the 3D camera:
protolib . s e t N e a r F a r P l a n e s (0.1 , 100);
protolib . s e t C a m e r a P o s i t i o n ( mathDevice . v3Build (0 , 2 , -4));
protolib . s e t C a m e r a D i r e c t i o n ( mathDevice . v3Build (0 , 0 , 1));
protolib . s e t A m b i e n t L i g h t C o l o r ( mathDevice . v3Build (1 , 1 , 1));
This sets up the player’s view of the 3D scene. The camera is offset a little
bit and looking directly in the center at our torus. This allows the floor to
show. Play with these variables to see how very useful the floor can be in
development. The ambient color is white. Changing this value will affect the
glow color of the torus.
The last initialization step is our event listener. We are only using desktop
mouse clicks. Turbulenz supports a “touchstart” event, and it is set up in a
similar fashion, but as of this writing, mobile support is extremely limited.
Only the desktop mouse version is presented below. By the time you get this
book, you may be ready to investigate mobile support.
inputDevice . a d d E v e n t L i s t e n e r ( ’ mousedown ’ ,
function ( mouseCode , x , y )
{
protolib . setGameVar ( " eve ntPositi onX " , x );
protolib . setGameVar ( " eve ntPositi onY " , y );
if ( mouseCode === mouseCodes . BUTTON_0 )
{
protolib . setGameVar ( " mouseClicked " , true );
}
if ( mouseCode === mouseCodes . BUTTON_1 )
{
// right mouse for testing
}
}
);
Like in our other games, the mouse location is saved and put in a variable
so it can be used later during the game loop. With this, the game has been
initialized. Other than spinning and moving the torus around, the rest of the
game is 2D physics with marbles and bricks. These functions are presented in
the next sections.
6.8. Sky Marble Implementation 115
This is initialized similar to the floor, except an array is used because the
game is tracking multiple marbles. Also, we are creating rigid kinemetic circles
(createCircleShape instead of createPolygonShape) to add to our world. Width,
height, and position are self-explanatory. The velocity alternates left and right.
The marble comes out spinning with a strong random angular velocity and
also alternates direction. We also save the red marble texture (used later).
Like most 3D engines, Turbulenz is particular about texture dimensions.
The image has to be square and a power of two (32 × 32, 64 × 64, 128 × 128,
etc) [102].
dropBrick(x,y) is nearly identical. brickArray holds all the bricks. The
relevant part is presented below:
brickArray [ i ]. phys2DPolygon = phys2D . c r e a t e P o l y g o n S h a p e ({
vertices : phys2D . c r e a t e B o x V e r t i c e s (1 , 1)
});
brickArray [ i ]. phys2DBody = phys2D . cr e at eR i gi dB od y ({
type : ’ dynamic ’ ,
shapes : [ brickArray [ i ]. phys2DPolygon ] ,
mass : 50 ,
position : brickArray [ i ]. position ,
a ng ul ar V el oc it y : Math . floor ( Math . random ()*11)
* (( brickArray . length % 2) ? -1 : 1)
});
world . addRigidBody ( brickArray [ i ]. phys2DBody );
A 1 × 1 kinemetic box is being created in the same way as the floor. It has
the same spin as the marble. The biggest difference is the mass is huge at 50.
This makes the drop effects very splashy.
116 6. Turbulenz
For the very brief moment as the torus flies over, if there is a red marble
in the center of the screen, it will turn the texture blue and add a point to
the score. It then plays a sound and drops another marble. This is the core
piece of game logic. From trial and error, these hard-coded values made for
the best user experience. Hard numbers are sufficient for a tech demo, but
further abstraction would use percentages based on screen width, height, and
marble size.
This method of moving the torus by directly manipulating position values
is essentially teleporting it around and is not recommended. For a smoother
animation, a 3D physics simulator could be used and the torus could be given
a Z velocity. This also would allow the engine to handle collision detection for
us if needed. However, our physics is 2D, and the torus is not colliding with
6.8. Sky Marble Implementation 117
anything anyway. Thus, we move it manually because the code is easier (for
us and the browser).
That concludes the helper functions. Next is the actual game loop.
protolib . playSound ({
sound : protolib . getGameVar ( " hit1 " )});
protolib . setGameVar ( " mouseClicked " , false );
this . dropBrick (x , y );
}
The first section looks to see if a mouse button is pressed (the mouseClicked
flag was set during the actual event). The math is scaling the mouse click
location to the in-game coordinates. The viewport width is 20 while the mouse
click can range from 0 to whatever the browser size may be. Thus, it needs to
be scaled.
The next bit of code is tricky. After adding all the objects to the physics
world, we need to tell the simulator to perform its calculations. This is done
with the step() function. The function is locked in a while loop with the total
simulation time compared to the total real time (by accumulating time deltas).
If the physics is behind, the while loop will cause it to catch up with the
current time (at a simulated rate of 60 FPS). Looping to catch up is a very
common task with physics simulations.
This technique allows the game flexibility to run at 60 FPS on strong
hardware and smoothly scaled down to a slower speed (such as 30 FPS) if that
is all that can be handled.
118 6. Turbulenz
Next, we need to draw the bricks, marbles, and floor. The bricks and
marbles code look nearly identical, so only the marble is presented because it
has a twist.
for ( var i =0; i < marbleArray . length && ! gameover ; i ++)
{
marbleArray [ i ]. phys2DBody . getPosition ( marbleArray [ i ]. position );
protolib . draw2DSprite ({
texture : marbleArray [ i ]. marblecolor ,
position : [ marbleArray [ i ]. position [0]
- marbleArray [ i ]. height / 2 -0.1 ,
marbleArray [ i ]. position [1]
- marbleArray [ i ]. height / 2 -0.1] ,
width : marbleArray [ i ]. height +0.2 ,
height : marbleArray [ i ]. height +0.2 ,
rotation : marbleArray [ i ]. phys2DBody . getRotation ()
});
Turbulenz has its own method of debugging. Take a look at this code:
protolib . a d d W a t c h V a r i a bl e ({
title : " Score " ,
object : protolib . globals . gamevars ,
property : " score " ,
options : {
min : 0 , max : 100 , step : 1
}
});
Assuming you are using the default template, this will put a slider in the
debug area to let you dynamically modify the score variable of the object
protolib.globals.gamevars. Slide the bar left and right to see the score change.
Any variable can be exposed to a slider like this. This is a very useful debugging
tool.
6.9.2 Templates
To customize the surrounding HTML, the layout is located in “templates”.
The app is currently loading the default built-in template. To extract this
default template and make it your own, run this command (from the virtual
environment):
( env ) C :\ Users \ dev \ turbulenz_engine > makehtml -D \
-o d e f a u l t _ m o d i f i ed . html
Put that file in the same directory, and tell templates\app.js to import
your own template:
/*{% extends " d e f a ul t _ m o d i f i e d . html " %}*/
The app should load identical as before, but you can now fully customize
the surrounding HTML by modifying default modified.html.
6.10 Summary
In this section, we built a 2D game using a 3D engine and 2D physics. Though
our game was structurally 2D, we leveraged the 3D engine to sprinkle some
visual enhancements that would not have been possible with the other engines.
This allowed us to touch a bit on 3D game development topics, such as textures,
meshes, and matrix transformations.
We learned about WebGL and its pros (impressive graphics) and cons
(very limited support). Fortunately, the main detriment, limited support, will
eventually go away.
120 6. Turbulenz
Turbulenz has a very high learning curve, and it will probably take until
late 2014 before a large mobile audience has the hardware available for it.
However, the performance of Turbulenz is top notch, and the price (free and
open source) can’t be beat. Eventually, all the browsers will catch up with it,
and your games can be ready for them to use it.
This concludes the tour through the different game engines. In Part III,
you will learn how to package an HTML5 game for distribution. The promise
of HTML5 as an app can be written once and run anywhere. Right now, that
is true if they are online and using a web browser. What if the player wants
to play offline? What if we want the player to find our app in Google Play or
iTunes? How about allowing it to be played outside of a browser?
These distribution scenarios will be covered in Part III.
Part III
HTML5 App
Distribution
Chapter 7
7.2 Introduction
If you have followed the guides in any of the previous chapters, then you
have successfully written a game that can run on nearly all computers and
mid-to-high-range mobile devices being manufactured in 2012 (except, perhaps,
Turbulenz).
But there is slight caveat to that statement: The player has to be online
and direct their browser to the site serving the game libraries every time they
want play. This inconvenience is the main motivation for an entire third part
to this book. After all, the game is just JavaScript and media files. Once the
web server hands over the files, its job is done. The player could disconnect
immediately.
The purpose of Part III is to look at various ways to get the game into the
hands of players. We want the player to be able to find and install the game
without having to visit our obscure website every time they want to play it.
We want the player to be able to find our game through all the major app
centers they already know and trust. We want the player to be able to play
the game even if they aren’t online.
123
124 7. Chrome Web Store
See the Tools Appendix on page 175 for image software that may help you
in creating these icons. These images all have associated guidelines beyond just
pixels. Also, the app submission page requires text and other requirements.
Expect to spend a day creating images and gathering information to submit
your app. For now, we are only concerned with bundling up our app into a
developer package. For that, we only need the app icon.
Next step is:
• Create a Manifest.
Those who have worked with Android development will be familiar with
the term “manifest”. It is a file that makes declarations about the overall app
package. Google Play requires it for Android apps, and Chrome Web Store
requires it for Chrome apps. The Chrome Store version is much easier. It is
simply a JSON file. There are several items that are required.
1. Name of app
2. Description of app
3. Version of app
{
" name " : " HTML5 Game Engines Book Demo " ,
" description " : " MechaJet demo game " ,
" version " : " 0.0.0.1 " ,
" manifest_version ": 2,
" icons " : {
" 128 " : " icon_128 . png "
},
" app " : {
" launch " : {
" local_path " : " index . html "
}
}
}
7.5 Summary
With just 14 lines of additional code, a few extra images, and the help of
Chrome, we turned our HTML5 game into a desktop app that can be played
offline. Submitting it to the Chrome Web Store will give us access to millions
of Chrome users. As of May 2013, there are 750 million desktop and mobile
Chrome users [82].
In the next chapter we will look at the more involved task of using Ejecta
to package MechaJet into an app that can go into the Apple App Store for
distribution to iOS devices.
Chapter 8
8.2 Introduction
If you wish to have your app run standalone on an iPhone or iPad, there is
simply no other choice than to put it in the Apple’s iOS App Store. Whereas
Android, Windows (non-Windows 8 UI style [77]), and Mac allow multiple
distribution channels, Apple only allows its store for iOS devices. Fortunately,
packaging an HTML5-based app for the Apple App Store is a mostly solved
problem. Every game engine goes out of its way to make sure it is well
supported, sometimes even improving over normal browser performance during
the packaging process. This chapter investigates using the Ejecta framework
to improve performance. Note that Google Play Store distribution, discussed
in the next chapter (page 143) features CocoonJS, which can target iOS as
well. If you are targeting both platforms and wish to have a one-size-fits-all
approach, you may wish to skip to the next chapter.
Before diving into complex process of device provisioning and setting up
Ejecta, note that Apple provides a shortcut method to hosted web apps wanting
to provide a more native experience. See the HTML meta tags below:
129
130 8. Apple iOS App Store
These meta tags were introduced back in section 2.5.1 on page 38. These
tags allow the user to create a shortcut that will launch a full-screen Mobile
Safari for a more native-like experience. The upside is that this takes just
two minutes to implement. The downside is losing the potential performance
improvements provided by Ejecta, and the app cannot be run offline. A complex
HTML5 game will need the acceleration provided by Ejecta (discussed later),
but this trick could be used for less intense web apps or as a baby step until
the full app is available.
3. Select your device in the left sidebar. If you don’t see a sidebar, you can
enable it in the “View” menu.
4. Find your serial number in the main window. See Figure 8.1. Click
your serial number. Your UDID will be revealed. See Figure 8.2.
Armed with your UDID, you are now ready to provision the device for
development. You are allowed to provision up to 100 devices with one developer
account. Launch a browser and navigate to https://developer.apple.com/ and
go to iOS Dev Center. Near the top right, click the link to go to “Certificates,
Identifiers & Profiles”. From there click “Provisioning Profiles”. If you have
not registered and purchased a $99 per year iOS Developer account, you will
be required to do so during the course of navigating. Once in the portal, you
are now ready to begin the fairly complex task of setting up an iOS device
and app for testing and distribution.
iOS provisioning comes in two profiles: Development and Distribution.
Distribution is covered later. For now, you want to create a “development”
profile. Follow Apple’s walk-through as your certificates and profiles get
generated, saved, uploaded, downloaded, and attached to your Mac’s Keychain
along the way. Apple and your Mac will jointly generate the various files you
need. Here is what will happen. Naturally, if the process has changed, follow
Apple’s steps over this one:
132 8. Apple iOS App Store
You are now set up with a paid iOS development profile and certificate.
You now need to create an App ID and add your iPhone or iPad to your
development profile. Go to App IDs, click the big plus sign, and fill in the
information. The only recommendations are that using a wildcard makes
development easier. However, wildcard bundle IDs are not supported with in-
app purchasing. Also, by convention, bundle IDs use reverse-domain notation
(com.example.myapp.ios). This bundle ID will be used in your app within
Xcode. Now, go to “Devices” and add your iOS device by submitting a common
name and the UDID that you copied earlier. Click Save.
Now, on to the final step: the actual development profile. Here are the
steps:
That was quite complicated, but all the pieces are now in place. Download
Xcode from the Mac App Store if you do not already have it. Connect your
iOS device if it is not already connected. Launch Xcode and then navigate to
Window → Organizer. The Organizer utility will launch. Click “Devices” at
8.4. Ejecta 133
the top and then “Provisioning Profiles” on the left. Click “Refresh” on the
bottom right, and then Xcode will fetch your information from Apple. See the
screenshot in Figure 8.3. In the screenshot, there is also a distribution profile
which you may not yet have. That happens when you are ready to publish,
and the steps are exactly the same as development. The only difference is the
certificate is submitted to the distribution section in the portal.
Now, enable development on your iOS device and attach your profile to it.
See the screen shot in Figure 8.4.
Finally finished. The iOS device should be recognized by Xcode and allowed
to run the app within the device instead of a simulator. If it is not listed,
try exiting out and then relaunching. Xcode does a fetch from Apple when
it starts. If that doesn’t work, check “Build Settings” under “Code Signing”.
We are now ready to build an HTML5-based game that can run native on iOS.
To help us, we will be using the Ejecta framework.
8.4 Ejecta
Ejecta is an open source project by the same developer who maintains Impact
[67]. Dominic describes it as: “A web browser that is Canvas-only.” As
expected, Impact is very well supported, but it also can be used for other
engines with some caveats that won’t be covered here (hint: avoid DOM. Use
134 8. Apple iOS App Store
8.4.1 Setup
Ejecta can be found at impactjs.com/ejecta. As of this writing, the official
stable branch is at version 1.2, but as with many open source projects, the
adventurous may download and work with the latest development branch.
Currently, I have found that the latest master branch from GitHub seems to
work best, so that is what will be described here despite the structure being
slightly different.
First, we will set up the “Hello World” example that comes bundled with
Ejecta. There are a lot of files and libraries associated with Ejecta. It is
essentially a low-level optimized version of the JavaScriptCore engine used
by Safari being bundled with your app. The root directory of a fresh Ejecta
project looks like this.
App
The directory that has your Canvas-based JavaScript app. You may
need to create this directory. Inside this directory should be an “index.js”
file that is the main entry point of your application. One was probably
given when downloaded.
ejecta.xcodeproj
The XCode project file.
Resources
App icons and other settings not directly related to rendering the game.
Source
Implementation of the Ejecta framework and lots of supporting libraries.
No Orientation Changes
No DOM
Ejecta’s support for DOM specific calls, like document.getElementById, is
extremely limited. Heavy DOM-oriented libraries, such as jQuery, cannot
be used.
console.log
Your nice Chrome and Impact debugger tools are no longer available,
but you still have trusty console.log(). It shows up in your normal Xcode
output window.
1. 30 seconds or less
2. In linear PCM or IMA4 (IMA/ADPCM) format
3. Packaged in a .caf, .aif, or .wav file
While Apple supports wav, we are restricted by Ejecta to the .caf format.
For longer and looping sounds (e.g., background music), Apple recommends
instantiating the AVAudioPlayer class. Ejecta handles this for us and will pick
the appropriate method. MP3s are fine for background music, but this means
we still need to solve our short audio problem. Apple has a command-line tool
called “afconvert” that runs in Terminal that will get our short audio files into
the format we need.
Here is an example conversion for our sound files:
afconvert -f caff -d LEI16@44100 -c 1 fireburn . mp3
afconvert -f caff -d LEI16@44100 -c 1 bombboom . mp3
afconvert -f caff -d LEI16@44100 -c 1 restart . wav
afconvert accepts a variety of formats. The above will generate the files
“fireburn.caf”, “bombboom.caf”, and “restart.caf”. Convert your sound effect
audio files to CAF, and then modify the lines that launch your game to look
as follows:
if ( ig . ua . mobile && ! window . ejecta ) {
// Disable sound for all mobile devices except Ejecta
ig . Sound . enabled = false ;
}
var width = 1024/2;
var height = 768/2;
var ctx = canvas . getContext ( ’2d ’);
8.6. Preparing for the App Store 137
ctx . i m a g e S m o o t h i n g E n a b l e d = false ;
canvas . style . width = window . innerWidth ;
canvas . style . height = window . innerHeight ;
ig . Sound . channels = 2;
ig . Sound . use = [ ig . Sound . FORMAT . CAF , ig . Sound . FORMAT . OGG ,
ig . Sound . FORMAT . MP3 ];
ig . main ( ’# canvas ’ , MyGame , 60 , width , height , 1);
});
With that, our app is finished and can run natively on iPads.
8.6.1 Distribution
Navigate back to the Apple Provisioning Portal and follow the previous steps
to set up a distribution profile. The steps are exactly the same as when you
set up your development profile. State that the certificate is for distribution,
not development. When finished, download and install the generated cer file.
Next, follow these steps. They should look very familiar:
2. Select the “Build Settings” tab, and change the Code Signing for “Dis-
tribution” to use the distribution profile. See Figure 8.7.
3. Click the name of the project in the top left. This is the build scheme
section. Create a new scheme called “Distribution”. See Figure 8.8.
4. Xcode will switch to the Distribution scheme. Click the scheme, and
then click “Edit Scheme...”.
5. Select “Run Ejecta.app” (or whatever your app name may be) and choose
the Distribution build configuration. See Figure 8.9.
8.6. Preparing for the App Store 139
Xcode is now properly configured with two schemes: One for development,
another for distribution. When built, Xcode will generate a “.app” file for iOS.
Its icon will look like Figure 8.10. This app cannot be run by Mac OS X. It is
built for iOS.
When your final app is ready to be uploaded to iTunes, your next stop is over
at iTunes Connect, located at http://itunesconnect.apple.com/. Login using
your Apple credentials and navigate to “Manage Your Apps”. Click “Add
New App” and follow the steps. The only thing remarkable about this process
is you may have another round of graphics to generate. When finished, your
app will say “Waiting for Upload”.
Back to Xcode. Select the distribution scheme. Select iOS device as the
target. Then select Product → Archive. Xcode will build your app and
Organizer will appear with your app icon. Next, click “Validate”. Xcode
will now validate and sign your app with the credentials stored on your Mac
and created within iTunes Connect. If all goes well, you will get a success
notification that looks like Figure 8.11.
8.7. Summary 141
Next, click the “Distribute” button. This will invoke more code signing
and eventually upload and place the app into iTunes for review. The status
will eventually transition to “Waiting for Review”. If your app is waiting for
review and you change your mind, you can reject the binary. The navigation is
iTunes Connect → “Manage Your Applications” → “View details” → “Binary
Details”. The “Reject this binary” button is in the upper right.
Apple does not specify a review schedule. Personal experience shows about
two weeks before your app gets reviewed and published, and getting a rejection
on the first attempt is pretty normal. Provide more information for the
reviewer and fix the problems that were raised. The next attempt usually gets
reviewed a lot quicker. Two days is normal. Updates to previously reviewed
apps are also quicker, usually within five days.
8.7 Summary
Despite Android having the marketshare, iTunes has the mindshare. Often
the question, “Is there an app?” is actually asking: “Is there an iPhone/iPad
app?” Sometimes, a business has not really arrived until it has a published
iOS app. Yes, it can be difficult to get into the Apple App Store, but working
through the process is necessary to get your game in front of the very large
and potentially lucrative iOS app market. Apple users are big fans of apps,
and they are willing to spend money on apps that are quality. The great thing
about HTML5 is that this crowd does not have to go unserved. There were
very few technical aspects in this chapter. It was almost completely about the
iTunes publishing process. Hopefully, after reading this chapter, it will feel a
little less draconian and more like just a necessary evil.
The next chapter teaches how to package and submit an HTML5-based
app to the Google Play Store for Android users.
Chapter 9
9.2 Introduction
Despite accounting for roughly 70% of all smartphones worldwide [68], Android
distribution always seems to be a second class citizen when it comes to game
engine support. Game engine maintainers go far and out of their way to make
sure iOS is well supported and Android becomes an afterthought. However,
the game engine maintainers cannot be faulted entirely. Android development
is very hard in that the number devices running on it are extremely varied,
and each could be running a different version of the Android operating system,
a problem called “fragmentation.”
When an iOS developer finishes a game for the iPhone and iPad running
the latest version of iOS plus one version back (a good development strategy),
the developer can be assured that a very large majority of all iPads and
iPhones can install and run their app. Android does not have that luxury.
The only sure way to test is to build up a collection of tablets and phones
with different Android versions and run the app. As HTML5 web developers,
we are familiar with this test strategy by juggling browsers and operating
systems inside virtual machines. Unfortunately, with Android, we are now
143
144 9. Google Play Store
9.3 CocoonJS
Last chapter, we used Ejecta for our Impact game, but Ejecta is not supported
for Android. CocoonJS is a commercial HTML5 wrapper for both Android and
iOS maintained by Ludei [69]. The setup is the same for each platform, so we
could have just used CocoonJS for both Android and iOS, but that wouldn’t
have been as fun, would it? Also, CocoonJS uses its own “cloud” compiler.
That means you upload your code to Ludei, and their server compiles your
code. It then lets you download your final package. The pros and cons of
being dependent on “the cloud” could fill an entire book. Here is a summary:
Cost
CocoonJS is currently free, but it may not always be. CocoonJS also
requires their logo shown during startup.
Internet
CocoonJS requires an Internet connection to use. You may not always
have stable fast connection during development.
Control
CocoonJS uses its own compiler with its own capabilities. You may wish
to have more, but you can only have what they give you.
make it worth it for the iOS version. However, there is the cost of having 2
build structures to maintain if you wish to support both Android and iOS. If
you are willing to do the cost/benefit trade and be CocoonJS-only, you can
make that decision.
9.3.1 Setup
As mentioned before, CocoonJS uses the cloud to compile code. Eclipse and
the Android developer tools do not even need to be installed (though the
Java Development Kit will be needed eventually to sign your apk (Android
packaged kit) to upload to Google Play). However, you will want to keep
MAMP, XAMPP, or WAMP to easily fetch the zip package and install it on
your Android device. To get started, download and install the free CocoonJS
launcher from Google Play. This is what you will use to locally develop and
test your app. Once installed, you will need to register an account with Ludei.
From there, Ludei will email you a registration code to enable the CocoonJS
launcher.
After everything is configured and registered, your launch screen will look
like Figure 9.1 (Nexus 7 being shown).
CocoonJS comes with demo apps to install and test its system. These demo
apps are also available from its website. They are invaluable in determining
the CocoonJS methods and API for developing HTML5 games. Our game will
need to be modified to work with their tools, which we will get to shortly. For
now, download one of its sample games, put it on your local web server (MAMP,
WAMP, etc.), and then download it using their launcher. See Figure 9.2.
146 9. Google Play Store
If everything is set up correctly, its demo app will download and launch.
We can now begin the process of replacing its app with ours.
Now the game loads and runs. However, touch controls do not work. As
mentioned before, CocoonJS has trouble accepting our touch controls. We
need to add our own touch listeners. See the additions in Listings 9.1.
ig . touchLocation = Object ();
ig . touchactive = false ;
c. addEventListener (
" touchstart " ,
function ( touchEvent ) {
var e = touchEvent . targetTouches [0];
var touch = Object ();
touch [ ’x ’] = e . pageX /2; touch [ ’y ’] = e . pageY /2;
ig . touchLocation = touch ;
ig . touchactive = true ;
});
c. addEventListener (
" touchend " ,
function ( touchEvent ) {
ig . touchactive = false ;
});
c. addEventListener (
" touchmove " ,
function ( touchEvent ) {
var e = touchEvent . targetTouches [0];
var touch = Object ();
touch [ ’x ’] = e . pageX /2; touch [ ’y ’] = e . pageY /2;
ig . touchLocation = touch ;
ig . touchactive = true ;
});
9.3.4 Distribution
Android apps are distributed as “apk” files. The apk file is simply a col-
lection of files zipped. If you want to look inside it, you can easily append
“.zip” and then open it with your file manager. Inside, you will find several
Android-related directories and files, such as “assets”, “META-INF”, “res”,
“AndroidManifest.xml”. Desktop Java developers may recognize this format
since Java distributables, “.jar” files, are also zip files set up in a similar way.
Take a look inside “assets”, and you will find the JavaScript files and other
assets that contain your game. This brings us to the point of the exercise of
opening up the Android package. There is little worry about extracting your
game from an iOS device. Apple has gone to great lengths to remove a user’s
ability to do that. However, with Android, extracting an apk and unzipping it
to get access to the JavaScript for your game is not a difficult task. Consider
running your files through obfuscation or minify before final deployment (such
9.3. CocoonJS 149
1. Install the Java Development Kit (JDK) if you do not have it. This will
install Keytool and Jarsigner tools for you. If you have gone through the
process of installing the Android SDK, then you already have the JDK.
3. Sign your apk using jarsigner. The utility is located in the same place
you found keytool. Here is the command:
jarsigner - verbose - keystore mykey . keystore \
- storepass K E Y S T O R E P A SS W O R D - keypass KEYPASSWORD \
unsigned_app . apk mykeyalias
150 9. Google Play Store
Your app is now signed and ready for Google Play. Next:
1. Go to https://play.google.com/apps/publish/.
2. Fill out the forms and upload the required graphics.
3. Upload the apk binary.
If you have not done so, you will be prompted to pay the one-time $25
Google Play developer fee to list your app. Unlike Apple, there is no review
process once your app is submitted. There is also no annual renewal fee. The
pros and cons of a cheap no-review style app market versus Apple’s process-
heavy with higher fee-style app market is a very fruitful debate that will not
be discussed here.
9.4 Summary
The Android market is massive. At Google’s annual developer conference
in March 2013, they announced 900 million Android activations to date and
48 billion app installs from Google Play [81]. As mentioned before, despite
the huge install base and large app market, the mindshare and game engine
support is still with Apple iOS. However, these numbers cannot be denied.
Android is taking over the mobile world. Ignore the naysayers. Pay the
extremely small $25 fee and make the effort to list your game in the store.
Google has made the barrier to entry too small to dismiss. The problem of
finding HTML5 compilers with good game performance will be solved. We
have a good solution with CocoonJS now and more will come along.
The next chapter takes a break from devices and goes back to online
distribution through Facebook.
Chapter 10
Facebook App
10.2 Introduction
The focus of Part III of this book is the various ways to run HTML5 apps offline
and purchase them through app stores. Therefore, it seems odd to include
a chapter about Facebook. Our games will not run offline with Facebook.
However, the extremely large audience on Facebook (arguably the world’s
biggest web site [70]) trumps the offline requirement to be included in Part III.
We want our game in front of as many people as reasonably possible.
Facebook attracts lots of gamers, and it would behoove us to have our game
available to users within their preferred medium of entertainment, particularly
if there is a strong social aspect to the game. Facebook is not difficult to
support. Like the Chrome Web Store chapter, this chapter will be very
process-driven.
151
152 10. Facebook App
10.3 Setup
Naturally, the first step is to get a Facebook account. Chances are high
that you already have one (BTW, this book’s Facebook page is located at
http://facebook.com/HTML5GameEngines). The next step is to go through
the developer registration process. Follow these steps:
Once you work through the registration process, next time you visit the
developer portal, your “Register Now” button will become a “Create an App”
button. The welcome screen is now a dashboard. See Figure 10.3.
Facebook has essentially developed its own language for developing web
apps on its platform. They have a very rich and diverse API that can fill
many books (and it does). One word used a lot is “Canvas.” That is their
term for content presented to users when they visit a page. We will be using a
Facebook canvas to develop our Facebook game, and the Facebook canvas has
little to do with our HTML5 canvas. Fortunately, we don’t necessarily have to
be too concerned with the bulk of the Facebook language (Graph API, Social
Channels, Facebook Credits, etc.) to get our game loaded and running.
Once you save and submit, your app will be created instantly, and you will
be taken to its hosting page. It will look something like Figure 10.6, and your
Facebook app setup page will look like Figure 10.7. Your Heroku page comes
prepopulated with lots of useful Facebook features, such as logging in, seeing
a list of users, and more. For our purposes, we are only keeping app login.
Heroku uses a toolkit (which they call “toolbelt”) that includes SSH, Git
(msysgit for Windows), and other utilities. They install into your system
PATH and are used for developing and deploying your Facebook application.
After installing their toolbelt, launch a command prompt and login. See Figure
10.8.
Then immediately follow the login with the Git clone command sent to
you in your registration email. Your Heroku system is ready for development.
You can edit the sample source code and push it back to Heroku when finished
(“git push heroku”).
156 10. Facebook App
Next, clear out the unneeded tags, the body “onload” event, and the login
link. See Listings 10.1. After you make the changes, run these commands to
push your changes to the server:
git commit -a -m ’ your comment ’
git push heroku
<? php
} else {
? > </ h1 >
< div class = " fb - login - button "
data - scope = " user_likes , user_photos " > </ div >
<? php
}
10.6 Summary
This short chapter did not touch any social aspects, which is one of the pillars
of Facebook. Heroku includes the entire Facebook PHP SDK, JavaScript SDK,
jQuery, and sample code widgets to get you going. Now that you know how to
get a basic app running on Facebook, experiment with the API to add social
aspects. Facebook puts your app in sandbox mode to help you develop so you
can release when ready. Also, within the code, Heroku shows how you can run
your app on localhost so you don’t have to constantly push to their server.
Experiment. Have fun with it.
158 10. Facebook App
The sample Tic-Tac-Toe game is included on the book’s website, but you
are encouraged to use Heroku’s starting base instead. The next chapter is back
to offline distribution through native desktop installers. It will cover both Mac
and Windows.
Chapter 11
11.2 Introduction
The Chrome Web Store was a convenient way to quickly get our game into the
hands of the millions of desktop Chrome users. However, despite the millions
of users, there are still millions of desktop users not using Chrome and who
are being left out. Also, even among the Chrome users, not everybody has
a Google account and regularly visits the Chrome Web Store to be able to
install our game.
Regardless of the reasons, it is possible to distribute an HTML5 app directly
to the end user and bypass all the third parties. It takes a bit of extra effort,
but the benefit will be ultimate end-to-end control of the user experience. The
only requirement to play the game will be proper hardware with a supported
operating system. This chapter will describe how to do this for both Windows
and Mac using node-webkit, which does much of the heavy lifting. The bulk
of this chapter will be building installers.
159
160 11. Windows and Mac Native
11.3 node-webkit
node-webkit is an MIT-licensed (which is free to distribute commercially)
project on GitHub that is managed by Roger Wang and sponsored by the
Intel Open Source Technology Center. It is based on a combination of Node.js
and Chromium [83]. Having Node.js functionality in apps is interesting in its
own right, but for our purposes, this project allows us a very convenient way
to distribute our HTML5 web-based apps as normal desktop apps. Note that
node-webkit predates the announcement where Chrome would fork Webkit to
use the Blink rendering engine. Because node-webkit is based on Chromium,
which technically does not use Webkit, the name of the project could possibly
change.
To get started, download the Windows and Mac precompiled binaries listed
on the main page under “Downloads”. Both are necessary to target both
platforms. Unzip and then launch the application. You should see something
like Figure 11.1. If you do, you are ready to customize it for your app. First,
we will customize for Windows, and then we will customize for Mac. If you
wish to start with Mac (or simply not use Windows), just read the Mac section
first and come back and adapt with the Windows steps. The customizations
are identical except for location.
{
" main " : " index . html " ,
" name " : " com . example . craftypong ,
" description " : " Crafty Pong " ,
" window " : {
" toolbar " : false ,
" width " : 500 ,
" height " : 500 ,
" min_width " : 500 ,
" min_height " : 500 ,
" max_width " : 500 ,
" max_height " : 500
}
}
Right now, you could zip up the entire directory and distribute it (after
code obfuscation/minify techniques if desired, such as with UglifyJS). Every
user with enough tech savviness to unzip files into their own directory can
launch and play your game. That is good for development and testing, but
that is usually not acceptable for a professional game. The next few sections
show how to create installers for each platform and to tweak node-webkit to
make it your own.
162 11. Windows and Mac Native
Note that from the web page for Resource Hacker, the author states the
project has been discontinued and, as of this writing, the latest version is 16
September 2011 [84]. My personal use over the years has shown that this
utility is very stable. However, as it ages, and if there are no more updates,
you may have to look for alternatives or not use precompiled node-webkit
binaries and compile nw.exe from the source with the icon of your choosing.
The easiest and safest alternative would be to sidestep the issue altogether
and build a “launcher” with the correct icon that simply launches nw.exe. The
easiest utility to do this is probably AutoIt, located at http://www.autoitscript.
com. It comes with an all-in-one installer that compiles to an exe with an icon
of your choice and no dependencies. The compiled AutoIt script can be done
in one line of code. However, if you wish to go this route, you may want to
add a check to see that nw.exe actually exists before trying to launch it. See
below for AutoIt code:
11.4. Windows Distribution 163
; AutoIt Script
If FileExists ( " nw . exe " ) Then
Run ( " nw . exe " )
Else
MsgBox (4096 , " Error " , " Could not find nw . exe ! " )
EndIf
Set up the AutoIt compiler to use the icon of your choice and then compile.
Include your launcher app with the main app and point all related shortcuts
to the launcher app so Windows uses the correct icon to refer to your app.
The big downside to this technique is the app will still use the wrong icon
when it is actually running, but that is the recommended stop-gap if you can
no longer directly “hack” the resources inside nw.exe.
[ Setup ]
; NOTE : The value of AppId uniquely identifies this application .
AppId ={{ E7A1A2A9 - B9D8 -4 AC4 -9 E57 -5 F81F478D1D5 }}
AppName ={# MyAppName }
164 11. Windows and Mac Native
[ Languages ]
Name : English ; MessagesFile : compiler : Default . isl
[ Tasks ]
Name : desktopicon ; Description : { cm : C r e a t e D e s k t o p I c o n }; \
G r o u p D e s c r i p t i o n : { cm : Ad di t io na lI c on s };
[ Files ]
Source : resources \*; DestDir : { app }; Flags : ignoreversion
[ Icons ]
Name : { group }\{# MyAppName }; Filename : { app }\{# MyAppExeName }
Name : { commondesktop }\{# MyAppName }; Filename : \
{ app }\{# MyAppExeName }; Tasks : desktopicon
[ Icons ]
Name : { group }\{# MyAppName }; Filename : { app }\{# MyAppExeName };
Name : { commondesktop }\{# MyAppName }; \
Filename : { app }\{# MyAppExeName }; IconFilename : \
{ app }\ html5logo . ico ; Tasks : desktopicon
[ Run ]
Filename : { app }\{# MyAppExeName }; Description : \
{ cm : LaunchProgram ,{# MyAppName }}; Flags : \
nowait postinstall skipifsilent
3. Next, it grabs the entire “resources” directory. This is what gets installed.
4. Under “Tasks”, it asks the user if they want a desktop icon for the app.
Many users get annoyed when icons are placed without asking. Placing
“Quicklaunch” icons is a similar process.
5. Finally, after installing, it asks the user if they wish to run the app.
Build the install package, and you are finished. You now have a desktop
HTML5 game that installs and runs like any normal offline game.
11.5.1 node-webkit.app
A Mac “.app” is actually a normal directory with a special structure. You
can right-click an “.app” and click “Show Package Contents” to look inside.
Do this now with the baseline node-webkit.app. You will see a “Contents”
directory. Inside that directory, you will find:
Frameworks
This holds the core frameworks that are used by node-webkit. It should
be left alone.
info.plist
This follows Apple guidelines to customize your app and make it your
own. This file is in XML and should be edited to fit your needs.
At minimum, “CFBundleIdentifier” should be changed to your own
“com.example.myapp”. CFBundleInfoDictionaryVersion should be your
version. CFBundleName should be your app’s name.
MacOS
This contains the node-webkit executable that gets launched.
166 11. Windows and Mac Native
PkgInfo
The contents of this file simply states it is an Apple package.
Resources
Inside Resources, you have a directory called “app.nw”. That is where
your actual game resides. It is just like the zipped app.nw created
for Windows except this is a normal uncompressed directory. Also, inside
Resources is an icon set called “nw.icns” described next.
“nw.icns” is a collection of PNG images combined into a special icon format
designed by Apple. It is similar to the classic Windows “.ico” format. Apple
ships a utility called “iconutil” that can generate icns files. First, you need to
create the icons in the required formats. These include:
Filename Dimensions
icon 16.png 16 × 16
icon [email protected] 32 × 32
icon 32.png 32 × 32
icon [email protected] 64 × 64
icon 128.png 128 × 128
icon [email protected] 256 × 256
icon 256.png 256 × 256
icon [email protected] 512 × 512
icon 512.png 512 × 512
icon [email protected] 1024 × 1024
The file generated will be “nw.icns”. That file should be placed in game.app
→ Contents → Resources. After copying it over, the icon of the app should
change.
The app is finished. You could zip this up right now and distribute. The
next section describes how to put together an installer. Most Mac apps use
DMG files.
internally they can use the HFS+ file system, a format developed by Apple for
Mac OS X [86]. Creating a disk image to hold these contents before compressing
makes them ideal to send over the internet to places that may not understand
how to read the HFS+ system. While standard compression utilities, such as
zip, may not properly capture all the miscellaneous permissions and other file
aspects unique to HFS+, DMG does not have these limitations. The DMG
archive itself is what gets compressed.
There are two ways to create a DMG to distribute your app (or perhaps a
third way: purchasing one of the many DMG creation utilities). The first way
is really easy, but not as professional. However, it may be sufficient for your
target audience.
After your app is ready, run this command:
hdiutil create - format UDBZ - quiet \
- srcfolder mygame . app mygame . dmg
This will create a writable DMG with the contents of the directory inside
it.
Test out installer final.dmg to make sure it behaves as you expect. This
is the final installer that you can use to distribute your app on the internet.
The intermediate file, installer.dmg, can be renamed to installertemplate.dmg
and checked into your version control (such as Git) since it is now part of
your build process. Swap files in and out of this DMG whenever there is a
new release. If this DMG is a bit big for your liking, you can compress and
uncompress it. Note that Git already uses compression.
11.6 Summary
In this chapter, we built native desktop distributions of our game. node-webkit
did the heavy lifting of creating a manageable version of Chromium that can
be used to help distribute our game. With this native distribution, we can
feel comfortable that any end-user on a wide variety of machines can play
our game. We also were able to lighten the game a bit (removing Modernizr
and MP3s) because we know exactly what “browser” is playing our game. Of
course, lighten may not be a good word. The trade-off was bundling an entire
browser engine for essentially a couple megabytes of JavaScript and media
files. Regardless, we now have another avenue of distribution to an extremely
large medium (Macs and Windows desktops) that allows offline playing of our
game and is not reliant on any app stores.
Node.js is a good development platform in its own right. If you would like
to explore beyond JavaScript (having the game talk back to your “server”),
research how having Node.js available in node-webkit can help you. Also, keep
in mind that since node-webkit is based on Chromium, and Chromium has
migrated from Webkit to Blink, the name of the project may change.
This concludes the distribution techniques. The next chapter is the final
notes of what has been covered.
Chapter 12
Final Notes
This book covered a large slice of HTML5 game development. Starting in Part
I, we learned high level concepts, tools for development, and how to build
a small pong game. Then in Part II, we stopped reinventing the wheel by
introducing game engines to take our examples further. We used four top
game engines to build 4 different demos that could be played on all the major
desktop and mobile platforms and learned a lot in the process. Then in Part
III, we learned how to package our games to be placed in the top app markets
and natively on the desktop so our users can find and play them however they
please.
In summary, we learned development and distribution of HTML5 games.
So, what could be missing? That is what this chapter covers.
12.1 Plugins
Whether officially or indirectly, many of the tools we used have a plugin system
to extend functionality beyond the normal use. This section covers a few of
them.
171
172 12. Final Notes
to HTML5. You will need to work through the style and approval process.
The end result will be a lightweight app that internally calls on Windows 8’s
own IE rendering engine to power your app.
For the Mac App Store, node-webkit will not pass their guidelines either.
The strategy here is the same as Windows Store: Use the desktop’s native
rendering engine. Take a look at https://github.com/maccman/macgap for
a method to build your app to run natively as a WebView calling the Mac’s
rendering engine.
Note that the capability of your game is dependent on the browser the end
user has installed, which usually means targeting the default OS version. Most
of the engines should work fine with this strategy except perhaps Turbulenz
because WebGL support is very weak in default Windows 8 IE.
12.3 Monetization
Throughout the entirety of this book, there has been little or no coverage on
app monetization. The best way to earn money from your game is a complex
decision that should be made very early. Therefore, it usually falls into the
realm of “game design” while this book tries to focus on “game development.”
However, it is worth at least a mention.
There are many ways to monetize an app, but the most common methods
include:
Important tools that aren’t necessarily the focus of the book have been placed
here. The appendix is intended to be read like another chapter, but with the
liberty to simply skip tools you have already seen.
Android Studio
Android Studio was announced in early 2013 as the next generation of
development tools for Android development. As of this writing, it is
currently in early access preview and has a few bugs, but it is still far
enough along that it can be used. By the time you receive this book, it
will probably be a lot more stable and should be your first download
for native development. Unlike the old Android SDK that uses Eclipse,
Android Studio is based on IntelliJ IDEA [105]. The feature list is very
promising.
Audacity
Audacity is a free audio editing tool available on all major platforms. It
has numerous features, including recording live audio, mixing, splicing,
and audio format conversion (Wav, MP3, OGG, . . . ).
Autodesk Maya
Maya is a commercial 3D computer graphics software used for animation
and modeling. It is very feature rich and has been used for many high-
budget films and computer games. For our purposes, it can be used to
create 3D models that can be imported into the Turbulenz game engine.
AutoIt
AutoIt is a freeware scripting, automation, and GUI builder utility. It is
notable in this book for its simple Basic-like syntax and compiling to a
single executable with no dependencies.
Blender
Blender is free, open-source-3D modeling and texturing software. It has
feature parity with some of the proprietary software packages. If you
have looked at it in the past and got annoyed with the interface, take
175
176 Tools Appendix
a look again. It has improved immensely in the past five years. The
Turbulenz HTML5 engine can translate the .dae export format from
Blender for use in 3D game development.
Bfxr
Bfxr is a handy utility for generating quick sound effects (such as jump,
hit, open, etc.). It can be run online or as a standalone app. All sounds
generated are yours to keep [23]. For those wanting to try to create a
complete musical score, there is MuseScore available for all platforms.
Box2D
Box2D is a widely used, open source physics engine. If a game engine does
not implement its own physics engine (such as Impact and Turbulenz),
there is a good chance that Box2D will be available as a plugin or module.
CocoonJS
CocoonJS is a compiler and launcher developed by Ludei that allows
packaging an HTML5-based app to be played and distributed on iOS
and Android [69].
Cygwin
Cygwin is the first choice for Linux admins and enthusiasts for getting
an encapsulated command prompt with access to their familiar Linux
commands (grep, ls, tar, cat, and others) on a Windows-based machine.
The installer has many packages available. Cygwin is free and maintained
by Red Hat, Inc. [61].
Eclipse
Eclipse is the current IDE for writing native Android apps. The Android
Development Toolkit (ADT) now comes with Eclipse bundled with it [29].
Some developers prefer Netbeans for Java development, but the official
and most supported tools use Eclipse. Eclipse runs on both Windows
and Mac.
Note that while ADT may be the tool of choice, the next generation
of Android development will be Android Studio, which is currently in
limited release. By the time you receive this book, Android Studio may
have already superseded the Eclipse version.
Enigma Virtual Box
Enigma Virtual Box is freeware utility that can be used to consolidate
an app with dependencies into a single-file portable executable app [85].
FileZilla
FileZilla is a widely used and frequently updated file transfer client. It
is available on all platforms. It is most commonly used for FTP, but a
Tools Appendix 177
less-often and more useful feature is SFTP, which is a secure file transfer
protocol provided by OpenSSH. If you have Secure Shell (SSH) access
to your server (fairly common), you can probably use SFTP to access
your files and not need to bother with FTP accounts.
Git
Git is a free and open source software version control system developed
by Linus Torvalds (creator of Linux) [24]. There are dozens of version
control systems out there. All are good. Git is simply the most prevalent
as it solves many of the problems with previous version control tools,
and it is free and runs well on all platforms. Many IDEs have git support
built in. Even if they don’t, the Git Bash shell for Windows is excellent.
HTML5 Boilerplate
HTML5 Boilerplate is an all-in-one starting template to build a modern
HTML5-based website. It includes many of the common libraries (jQuery,
Modernizr, Normalize, etc.) and configurations (.htaccess) to build a site
that leverages known best practices to support a wide range of browsers.
It is intended to be downloaded and then adapted to suit your needs.
Inno Setup Studio
Inno Setup is my recommended tool for building Windows installers.
It is very powerful (on parity with paid solutions), easy to use, and
free for commercial use. You will want the “Studio” version when you
download [26].
Instant Eyedropper/DigitalColor Meter.app
Instant Eyedropper for Windows will look at any pixel on your screen
and copy the HEX, RGB, or HTML color code to your clip board. It
sits in your system tray. Click and drag the magnifier to the point of
interest. It is very handy. Mac comes with an equivalent utility called
DigitalColor Meter. It is located in:
/Applications/Utilities/.
jQuery, jQuery UI, jQuery Mobile
jQuery is an excellent free JavaScript library. It abstracts all the odd
idiosyncrasies of IE, Firefox, Chrome, etc. to a standard function. By
favoring jQuerys functions over the normal standard JavaScript, your
site will behave the same cross-platform as jQuery performs the cross-
platform translations for you. As a bonus, you get a very nice selector
engine and other very useful library functions. If this was a general-
purpose web development book, then there would be anywhere from a
dozen pages to an entire chapter dedicated just to jQuery. Over half of
all websites use it [20]. This book is only really interested in HTML5
Canvas, which does not use DOM. However, you will probably want to
178 Tools Appendix
Legacy Browsers
Legacy Browsers are a must for testing. Portable Apps
(http://PortableApps.com/) has old versions of Chrome (few) and Firefox
(many) available as portable downloads and can run standalone in its
own separate directory. This is very useful for testing previous versions
as well as versions with no add-ons or some add-ons enabled. Opera
USB (http://opera-usb.com) tracks portable versions of Opera.
For Internet Explorer, one tool is IETester (http://www.my-debugbar.
com/wiki/IETester). It is an all-in-one tester for IE 6 through 9 (plus
10 if you are running Windows 8) though it has mediocre performance.
These Legacy Browser tools are Windows-only. Mac users can install
these tools on their Windows Virtual Machine that they use for IE
testing. Unfortunately, OS X is not allowed to be virtualized (outside
of some special cases for OS X Server) [30]. Windows users will simply
need to find a Mac to test Safari because as of version 6, Safari is no
longer available on Windows [31].
MediaWiki
The software that powers Wikipedia is called MediaWiki [16]. It is free
to download and install. Often, hosting providers have an auto-installer
that can install it for you (though making it private is up to you). If
you don’t have a web server and don’t wish to pay for one, there are
many free private wiki sites. A wiki allows other designers to edit,
upload files and graphics, roll back changes, be alerted on changes, link
internally/externally to other pages, provide comments, and dozens of
other very useful features. It is my recommend format for game design
rather than just a static document.
Tools Appendix 179
app requires game-engine level performance. If your app just uses DOM
and is light on visuals, PhoneGap can get you pretty far.
To get your app running with PhoneGap, you need to download and
install the Android SDK or use the recently released cloud-compiler.
Apache Cordova is a library package that gets compiled with the native
developer kit, which for Android is Eclipse bundled in with the Android
SDK. Once Eclipse is running, import the PhoneGap sample Android
project:
Putty/SSH
Putty is a free SSH client for Windows. Mac users have SSH built in
with Terminal. If you are developing a website, SSH is very useful for
the tasks that are inefficient with web-based managers.
Python
Python is free, widely used, general purpose programming language.
A version of it is included by default on Mac OS X and most Linux
distributions. It is a very nice programming language with numerous
uses, but for our purposes here, it is often used as part of a command-
line build system, particularly Turbulenz and Heroku (for Facebook).
Windows does not include Python by default, so it will need to be
installed. Python is also available through Cygwin.
Resource Hacker
Resource Hacker is a freeware Windows utility for directly editing DLLs
and executables. It supports functionality such as changing icons and
sounds. This utility has been discontinued, but is still widely used [84].
Three.js
Three.js is an open source (MIT licensed) JavaScript library used to
182 Tools Appendix
UglifyJS
UglifyJS is a JavaScript processor utility that can minify, obfuscate, or
beautify JavaScript files. Turublenz uses it for compaction. It is often
included through node.js packages.
Web Servers
WAMP is an easy way to get a development web server with Apache,
MySQL, and PHP on Windows. MAMP is an equivalent package for
Mac. XAMPP is available for both Windows and Mac with a lot more
features than just Apache+MySQL+PHP, but it isn’t as easy to use.
This book recommends WAMP for Windows and MAMP for Mac.
There is a $20 Apple-supported Mac App Store app for Mountain Lion
and later, called “OS X Server,” that will get a web server running. It is
not really suitable for quick development as it is oriented more toward a
Tools Appendix 183
You will then have a very simple HTTP server running for that location
on port 8000. Go to http://127.0.0.1:8000 to view it. All Macs and most
Linux distributions ship with a version of Python.
Xcode
Xcode is provided by Apple for writing iOS or Mac apps. There are
lots of third-party tools that often can bypass Xcode for Mac desktop
development, but it will be needed when you are ready to distribute iOS
apps.
Bibliography
185
186 Bibliography
[12] HTML Living Standard. WHATWG. 14 August 2013. XHTML 2.0. W3C
Working Draft http://www.whatwg.org/specs/web-apps/current-work/
multipage/introduction.html#is-this-html5 (accessed August 15, 2013).
[13] WHATWG. 23 February 2013. FAQ - WHATWG Wiki http://wiki.whatwg.
org/wiki/FAQ (accessed April 29, 2013).
[14] WHATWG. 19 January 2011. HTML is the new HTML5 http://blog.
whatwg.org/html-is-the-new-html5 (accessed April 29, 2013).
[15] W3C. 18 November 2010. Web SQL Database http://www.w3.org/TR/
webdatabase/ (accessed April 29, 2013).
[16] MediaWiki. 20 March 2013. Project:About. http://www.mediawiki.org/
wiki/Project:About (accessed April 29, 2013).
[17] Microsoft. 2013. Internet Explorer 9 system requirements.
http://windows.microsoft.com/en-us/internet-explorer/products/ie-9/
system-requirements (accessed April 29, 2013).
[18] Microsoft. 2013. Windows XP SP3 and Office 2003 Support Ends April
8, 2014. http://www.microsoft.com/en-us/windows/endofsupport.aspx (ac-
cessed April 29, 2013).
[19] Tim Berners-Lee. http://www.w3.org/People/Berners-Lee/ (accessed
April 29, 2013).
[20] Matthias Gelbmann. 13 August 2012. jQuery now runs on every second
website http://w3techs.com/blog/entry/jquery now runs on every second
website (accessed April 29, 2013).
[21] jQuery UI. http://jqueryui.com/ (accessed April 29, 2013).
[22] jQuery Mobile. http://jquerymobile.com/ (accessed April 29, 2013).
[23] Bfxr. http://www.bfxr.net/ (accessed April 29, 2013).
[24] Git. Getting Started - A Short History of Git. http://git-scm.com/book/
en/Getting-Started-A-Short-History-of-Git (accessed April 29, 2013).
[25] VMware. VMware Player. http://www.vmware.com/products/player/ (ac-
cessed April 29, 2013).
[26] Inno Setup. Jordan Russell. http://www.jrsoftware.org/isinfo.php (ac-
cessed July 7, 2013).
[27] New Channels for Firefox Rapid Releases. The Mozilla
Blog. 13 April 2011. http://blog.mozilla.org/blog/2011/04/13/
new-channels-for-firefox-rapid-releases/ (accessed May 4, 2013).
Bibliography 187
[41] 300 million users and move to WebKit. 12 February 2013. Opera. http://my.
opera.com/ODIN/blog/300-million-users-and-move-to-webkit (accessed
May 4, 2013).
[45] Blink: A rendering engine for the Chromium project. The Chromium Blog.
http://blog.chromium.org/2013/04/blink-rendering-engine-for-chromium.
html (accessed May 4, 2013).
[49] Release Early, Release Often. The Chromium Blog. 22 July 2010. http://
blog.chromium.org/2010/07/release-early-release-often.html (accessed May
4, 2013).
[70] Alexa Top 500 Global Sites. Alexa Internet, Inc. 7 May 2013. http://www.
alexa.com/topsites (accessed May 7, 2013).
[71] Facebook and Heroku: an even easier way to get started. Facebook
Developer Blog. 15 September 2011. https://developers.facebook.com/blog/
post/558/ (accessed May 7, 2013).
[72] WebGL - OpenGL ES 2.0 for the Web. Khronos Group. 2013. http://www.
khronos.org/webgl (accessed May 7, 2013).
[76] Apple Core Audio Format Specification 1.0. Mac Developer Library. 12
October 2011. https://developer.apple.com/library/mac/documentation/
MusicAudio/Reference/CAFSpec/CAF spec/CAF spec.html (accessed
May 8, 2013).
[77] Windows 8 Product Guide for Developers. Dev Center - Windows Store
apps. Microsoft. 2013. http://msdn.microsoft.com/en-us/windows/apps/
hh852650 (accessed May 8, 2013).
[78] Start Developing iOS Apps Today: Set Up. iOS Developer Library.
Apple. 2013. https://developer.apple.com/library/ios/referencelibrary/
GettingStarted/RoadMapiOS/chapters/GetToolsandInstall.html (accessed
May 11, 2013).
[80] Prepare for App Store Submission. iOS Developer Library. Apple. 23
March 2013. https://developer.apple.com/library/ios/referencelibrary/
GettingStarted/RoadMapiOS/chapters/RM DevelopingForAppStore/
DevelopingForAppStore/DevelopingForAppStore.html (accessed May 11,
2013).
[81] Google: 900 Million Android Activations To Date. IGN.
15 May 2013. http://www.ign.com/articles/2013/05/15/
google-900-million-android-activations-to-date (accessed May 16,
2013).
[82] Live from Google I/O: Mo screens, mo goodness. Google Of-
ficial Blog. 15 May 2013. http://googleblog.blogspot.com/2013/05/
live-from-google-io-mo-screens-mo.html (accessed May 18, 2013).
[83] rogerwang / node-webkit. GitHub. 27 April 2013. https://github.com/
rogerwang/node-webkit (accessed May 18, 2013).
[84] Resource Hacker Home Page. Angus Johnson. 16 September 2011. http://
www.angusj.com/resourcehacker/ (accessed July 19, 2013).
[85] Enigma Virtual Box. The Enigma Protector. 2013. http://enigmaprotector.
com/en/aboutvb.html (accessed July 19, 2013).
[86] Archived - Mac OS: About Mac OS Extended Volume Hard Drive For-
mat or HFS+. Apple. 10 May 2012. http://support.apple.com/kb/ht1604
(accessed May 21, 2013).
[87] Signing Your Applications. Android Developers. http://developer.android.
com/tools/publishing/app-signing.html (accessed May 23, 2013).
[88] Creating an installable Android .apk. Ludei. http://wiki.ludei.com/
cocoonjs:androidapk (accessed May 23, 2013).
[89] XAMPP for Windows FAQ. Apache Friends. 17 January 2010. http://
www.apachefriends.org/en/faq-xampp-windows.html#vista (accessed May
26, 2013).
[90] Joseph Juran, 103, Pioneer in Quality Control, Dies. New York Times. 3
March 2008. http://www.nytimes.com/2008/03/03/business/03juran.html?
r=0 (accessed May 26, 2013).
[91] Turbulenz Engine Goes Open Source. James Austin. 2
May 2013. http://news.turbulenz.com/post/49430669886/
turbulenz-engine-goes-open-source (accessed May 31, 2013).
[92] Python2orPython3. Python Wiki. 9 March 2013. http://wiki.python.org/
moin/Python2orPython3 (accessed May 31, 2013).
192 Bibliography