2010-11-22

Project "HoT MetaL JaZz" - Part 4

In previous editions of this series, we went over some of the basic functions of the HTML5 Canvas element, including plotting paths, drawing images, and manipulating drawing parameters. Now, we will go over a very simple game framework written in JavaScript that uses HTML5 Canvas elements to display visual output.

The framework I created demonstrates a lot of tricks, as well as basic functions common in games. Features include:

  • Frame-based sprite animation
  • Sprite scaling, rotation, and transparency
  • Keyboard input management
  • Ability to scale to browser size while maintaining original aspect ratio

Furthermore, I've added functions such as a "wait until loaded" feature that ensures the game doesn't start until all related assets have been fully loaded. It currently has no progress indicator (just a static "Loading..." message), so you may want to add one of those if you decide to use any of the framework for your own games.

Now, let's go over some of the highlights of the framework for HoT MetaL JaZz, beginning with the front-end: "hotmetal.html".

hotmetal.html

When hotmetal.html loads, it runs the script function "startGame()", which is embedded right into the HTML page. Within the "startGame()" function, we create a new "GameCore" object, the constructor for which takes several parameters. Let's go over those quickly:

The first argument is the primary canvas - what the player will see. The next two arguments specify a maximum width and height for the primary canvas - this is mainly to ensure that the canvas doesn't get so large that performance begins to degrade too far. The next argument assigns the backbuffer canvas. The next two arguments are the width and height of the backbuffer - this value does not change; instead, this is the "natural" size of the backbuffer. The final argument is the desired frames-per-second timing at which to run the game.

Also, the body element of the page is given a function to run when the browser is resize: "game.resize()". We will go over this function as we go through the "gamecore.js" file.

gamecore.js

The constructor for the GameCore object is fairly straightforward - we assign values, and run a couple of functions.

We begin by setting some variables to keep track of our canvases and their sizes. Then we call "this.resize()" to size the canvas for a "best fit" within the browser's viewing area. We also assign a GameInputManager to the GameCore. As for the logic of GameCore, we use a demonstration logic script called "HotMetalLogic" - this contains all the code specific to how the actual game functions.

After this is done, we call "this.init()". Here, we set up the contexts for our canvases and save them. We clear both contexts, and draw a simple loading screen. At the end of the method, we call "this.loadData()".

The GameCore.loadData() method simply offloads the data-loading tasks to the logic part of the game. It will then run "this.assertReady()", which will cause the game to wait until the logic is fully loaded and ready.

The GameCore.assertReady() function will call the logic script's "images.loadImages()" function. The logic.images object is an instance of GameImageManager, and its "loadImages()" function will (re)assign the URL to each image loaded by the logic script. The function sets the GameImageManager.ready property to "true" if all images have finished loading. GameCore.assertReady will start running the logic (this.logic.run()) if the GameImageManager.ready property is set to true; otherwise, it will wait 1000 milliseconds (1 second) and run the check again. This will continue until the GameImageManager lets us know it is ready.

GameCore.resize() is an interesting function. It will find the "best fit" for the game's primary canvas within the browser window, all while maintaining the original aspect ratio of the canvas. First, we get the width-wise aspect ratio, and the "reverse", height-wise aspect ratio. We query the browser's "inner" window width and height, and then run a few checks to make sure the resize fits properly based on the aspect ratios. When all is said and done, you can resize your browser any way you want and have the primary canvas fit inside - with no portion of the primary canvas stretching past the window boundaries.

gameinputmanager.js

The GameInputManager object is quite simple - it sets up some functions for the document to run whenever a key is pressed or released. It also will release all input whenever the document loses keyboard focus via the document.onblur event. To determine whether a particular key is pressed, just check the GameInputManager.keyControls[keyCode] variable - it will be "true" if the key with the specified keyCode is pressed; otherwise, it will return "false" or "undefined".

gameimagemanager.js

This file contains two object constructors. The first is GameImageManager; the second is TaggedImage.

GameImageManager sets up an array of TaggedImage objects, which stores each image inserted into the manager along with a string ID for each. To begin adding images to the GameImageManager, use the function GameImageManager.addImage(filename, tag). The filename is the URL of the image to load; tag is a string identifier used to store and access the image. For example, you can use the code:

myImageManager.addImage("images/my-image.png", "sprite");

The file will be associated with the string "sprite". Thus, to retrieve the image from the GameImageManager instance, you would use:

var img = myImageManager.getImage("sprite");

Finally, the "loadImages()" function will check the "complete" status of each image loaded into the GameImageManager. If it finds that all images have a "complete" status, it will set the GameImageManager's "ready" flag to true.

gamesprite.js

This file contains three object constructors: GameSprite; GameSpriteProperty; and GameSpriteAnimation.

GameSprite.update(elapsedTime) will begin its task by checking whether the sprite is animated - if so, it calls the update(elapsedTime) function of the animation sequence. It continues by ensuring the sprite speed hasn't exceeded the designated maximum, and then updating the sprite's position by factoring its velocity in.

GameSprite.draw(context) takes the backbuffer context as its sole argument. The function sets the appropriate globalAlpha, translation, rotation, and scale of the context to match those of the sprite, and draws the sprite to the context. If the sprite is animated, it will draw the current frame; otherwise, it will draw the image parameter used in the GameSprite's constructor.

GameSprite.setProperty(name, value) and GameSprite.getProperty(name) allow the sprite to have an arbitrary set of properties. For example, you can add a "Hit Points" property to the sprite, without having to write a new sprite class. The GameSpriteProperty class simply holds a name for the property and its value.

The GameSpriteAnimation(image, width, height, delay) constructor takes an image, divides it into "cells" of the given width and height, and delays frame transitions by "delay" milliseconds. The update function simply checks whether it needs to transition to the next frame, by comparing the current frame time with the delay of the transition. If it's time to transition, the frame index is incremented. If the frame index has gone past the last frame index, the animation "loops" back to the first frame.

hotmetallogic.js

The HotMetalLogic class is the "glue" that ties all the previous classes together. It uses a GameImageManager to load and assign images, it interacts with sprites using the GameInputManager (use the arrow keys to move around), and handles the AI for different sprites.

When playing the game, you need to avoid the fireballs shooting out of the flares on the four corners of the play area. As the flares shoot out fireballs, they gradually weaken until they disappear. When all flares and fireballs have been extinguished, the game ends. You lose points each time a fireball hits you.

Of course, this is just a modest demo of what is possible. There are better ways to set up a framework for a game - this is just to get your creative juices flowing.

Some suggestions for improvements:

Make the GameCore capable of holding an array of logic scripts, and switch between them. For example, run a logic script for a main menu, and another logic script for the game in action.

Sort sprite drawing order by Y: The farther from the bottom of the canvas a sprite is, the sooner it should be drawn, so that sprites appear behind or in front of their appropriate peers.

How did they do it? - Al Bhed

I am introducing a new series I call “How did they do that?” Basically what is going to happen here is I am going to tackle some...odd, interesting or complicating aspects from different games. I will explain what it is and I will use examples on where it is used and attempt to mimic it.

Note: This will be based on mechanics, for example, stats, formulas, stuff like that. So don’t expect something outrageous such as importing models or special effects.

Note: A code sample is attached at the bottom of the article. I wouldn’t want people to use all their brain power trying to understand my theory and translating it to code when there is a downloadable available. I will try to put all the stuff in classes. That way, you don’t have to know the code or even know how it works. You just have to know how to use it. However, reading it will be an interesting learning experience.

The first article will be on Al Bhed.

Al What?
Al Bhed, it is a fictional language used in Final Fantasy X (FFX) and FFX-2. What happens is there is another language, obviously which you do not understand. The minigame is that you have the opportunity to find these “letters” throughout the game. The more you find, the more you understand. If you want to know more about this, go here. Each letter is represented by another character.

Simple concept right?

Stuff to note:
This is important if you want to make a new language. The words have to be pronounceable. For example, if you say “Hello” in English it turns out as “Rammu” in Al Bhed. It is pronounceable. There is a simple reason for that. You will notice that the vowels, and Y, is mixed with each other. This ensures that all words that are pronounceable in English is pronounceable in Al Bhed.

So How does it work?
Obviously I can’t tell you how Square Enix did it simply because I do not know. I do however have a method that you can use.

#1. Create an array with space for 26 characters. It will represent the letters a to z. It should be a Boolean so that you can simply check true or false.

Why?
When we come to a later part of the code, you will have to check if the character has received the letter. Otherwise the code will blindly convert to Al Bhed without caring if you know the letter or not.

#2. Make a function to modify the above array to a true/false.

Why?
Same as above, if you do not ever check that they indeed do have the letter, the code will convert without taking the collected letters into consideration.

#3. Make a function to convert each individual letter from English to Al Bhed.

Why?
Obviously to translate the letters.

Why only one way?
You only need it one way. It only has to be translated to one language. If you want to do it both ways by all means. It just makes more sense for the programmer to type in the message in English and have it converted to Al Bhed.

Why each individual letter? Why not just use a replace function?
The problem when converting all the letters using a replace function is that you have less control over it. That’s not the big problem. Consider the string below.

Hello, I am converting text.

Suppose you convert this to Al Bhed using a replace function. You start by converting all the a’s:
Hello, I ym converting text.

Everything is still fine. Now you get to the e’s:
Hallo I ym convarting taxt.

Everything is still fine. Now you continue this until you get to the I’s:
Hallo e ym convarteng taxt.

Perfect. Now you get to the O’s:
Hallo e ym cunvarteng taxt.

Still great. This is where the problem comes in. Now you go down the alphabet converting everything, now you are with the U’s.
Hallo e ym cinvarteng taxt.

Something isn’t right here. The original text does not have U’s, but because the converted text, from O’s, created a U, the text converted the already converted U. So in the end, you have an inaccurate translation. This is even worse with the consonants. That is why you convert the word one letter at a time, from left to right.

#4. Go through the string, checking letters one by one, converting them provided the array in #1 representing the current letter is set to true.

Why?
The idea is that the words should not make sense. Only “obtained” letters should be translated. That way, the more letters they get the more they understand. If the array value returns false, make the PC show Al Bhed, if it return true, make it show the English translation. So if only certain letters are found, only certain pieces of a word would be in English.

Eg. When translating “Hello”, but you only have the “H” and the “L” letters, the word should be displayed like so:
Hallu
Usually the letters you have are displayed in a different color.
So, if the user has the letter, simply don’t convert it.

#5. Return the converted string.

Why?
How else are the people suppose to read the converted string?

Problems you will face
If you just blindly convert everything you will run into problems.
If I want to convert this:

Hey! Zappy77! Watch out for Pinky! My pet mouse!

Problem #1: You will not display the punctuation, so have to make the computer check for it.

Solution:
My method is simple, if the letter is not in the list, simply write the input letter. That way you don’t have to check for a “!”, a “,”, etc.

Problem #2: If you are like me, you probably would code the engine to search for letters from A – Z, all in capital letters. You will not specify the lower case letters. If that is the case, the lower case letters will not be converted.

Solution:
Convert the inputted letter to uppercase before processing.

Problem #3:
Assuming you had Problem #2, the solution will create another problem. When the string is returned, the whole string will be in uppercase, which put a little more strain on the readers eyes. This is not good.

Solution:
Simple really, before you add it to the main string, check from the buffer if it was originally upper or lower case. If it was lower case, simply convert to lower case. If it was upper case, simply add it to the string.

Problem #4:
If you convert the above string, the output would be:
Rao! Wybbo77! Fydlr uid vun Behgo! So bad suica!
That’s bad! Why? Zappy77 and Pinky are proper nouns. Meaning they are names. You cannot convert names, that would be stupid. Nobody will know who they are referring to.

If there is a place called “Death Cage”, would you want the player to know “Death Cage” (Which is a name) or should they read: “Taydr Lyka” and not know what the places names are?

Solution:

I wrote a simple text parser. What I do is if I don’t want a part converted, I simply put [square brackets] around it. The script will then ignore the conversion words between square brackets and simply remove the square brackets.

If you cannot write a function like this yourself, simply refer to my included source code, or study this sample extracted from my source:

a = modifier.find("[");
b = modifier.find("]");

    for (i=0;i <= len ;i++)
    {
        if (i >= a && i <= b)
        {
            if (buffer[i] != '[' && buffer[i] != ']')
            {
                albhed += buffer[i];
                continue;
            }
            else
            {   
                continue;
            }
        }

        if (i>a)
        {
            a = modifier.find("[",i-1);
        }

        if (i>b)
        {
            b = modifier.find("]",i+1);
        }
...

This obviously makes more sense in the code itself.
Anyway, you can find the source as well an example of using the code here.

So that’s it for the first article of the series of “How did they do it?”. If you find any bugs in the code, please notify me. This is a full blown Al Bhed engine, but might have 1 or 2 bugs. I wrote this code in about an hour so don’t expect it to be perfect.

2010-11-20

Starting out with 3D #5 (U - Z)

This is the final piece of  the 3D terminology. *Thank goodness*. If you followed all of these tutorials you should know the drill by now. If not, best read the other parts if you would like to know more!

The other parts can be found here:

Part 1: A - E
Part 2: F - L
Part 3: M - P
Part 4: Q - Z
UV Texture Co-ordinates
The co-ordinate system used for assigning textures. UV co-ordinates space is 2D, thus a projection method must be used to “unwrap” the UV’s from the model and then lay them on a flat plane. This plane can then be copied into a paint package to manipulate to finally add the model texture.

Viewport
The region of the 3D scene that is displayed to the artist. For example, from the top.

Volumetric
Volumetric lights are lights which can be view in the 3D space rather than on a flat surface. Just like that, volumetric textures are textures applied throughout a volume space rather than a surface.
 

Weighting
The process of determining which bone in a skeleton affects which part of the models surface. In a lot of cases, the influence is simply painted on the model.

Wireframe
A shading method in which lines are displayed to represent the models form.
 


Z-depth
The distance a point or surface lies in the scene. Z-depth is used to calculate where a light casts shadows and also which surfaces are actually visible.

If you know every single one of these terms, you are amazing! If you learnt new terms great! If you learnt so much and you are so pumped about these articles and you are going to tell all your friends to visit this website, best of all!

Let me just say, don’t feel like you should know all of this off by heart, as long as you have a basic idea of most of them you will do fine!

Thank you for reading, and as I promised, here is a downloadable PDF file of everything in the past 5 articles, for future references.
zSHARE
Note: Please excuse the messiness. The converter did a pretty bad job in converting the pages. At least the information is there right?

Starting out with 3D #4 (Q - T)

Picking up where we last stopped. Here are the words between Q and T.
The others can be found here:
Part 1: A - E
Part 2: F - L
Part 3: M - P

Quad View
A method of displaying 4 viewports for you to view your model. The standard viewports are usually top, front, side and perspective.

Raytracing
A technique for rendering scenes. Raytracing traces the path of every single light from its source until it leaves the scene or is to dim to be visible in the current image.

Reflection Map
An environment map used to simulate real world reflection effects. They render quicker than other methods such as raytracing.

Rendering
The process of converting the 3D data stored in a software package into the two-dimensional image “seen” by the camera within the scene. Rendering brings together the scene geometry, Z-depth, surface properties, lighting set-up and rendering method to create a finished frame.

Rigging
When preparing a 3D model for animation, you usually add in an underlying skeleton. This makes everything easier to animate. This skeleton is linked to the model. That process is known as rigging. When this process is completed, you refer to it as a rigged character.

Scene
A set of 3D objects. This includes the models themselves, lights, cameras etc.

Shading
The process of calculating how the model’s surface should react to light.

Skinning
When binding the surface of a model to the skeleton during character rigging.

Skeleton
A network of bones used to define and control the motion of a model during character animation. Moving a bone causes the mesh of the model to move and deform.

Snapping
The automatic alignment of one object to another or to a reference grid. This is used when extreme precision has to be taken into account.

Soft Body Dynamics
Simulates the behaviour of models that deforms when they collide with other objects. Such as a cloth on a table.

Secularity
A property which determines the way in how highlights appear on the specified surface.

Symmetry
A modeling option which duplicates the model across a specified axis. This is used a lot for organic modeling as only one half of the model has to be modeled.

Texture
An image that is applied to a 3D model to give it detail. They can be photographs or CGI. They can be applied to each of the material channels.

Trimming
The process by which NURBS surfaces are edited. This allows 3D artists to define areas that will be made invisible, and also not render them. Theoretically they still exist and you can also still edit them if you please. This is used to get rid of pieces in the model that will never be seen.

In the next part, I will finish up with the common 3D terms, I will also add a downloadable PDF document with all of these words if you wish to download them for future references.

Starting out with 3D #3 (M - P)

This is the follow up from #1, which can be found here, and #2, which can be found here
We are almost finished! Bare with me!


Material
Mathematical attributes that determines the ways in which the model will react upon light.

Mesh
The surface geometry of a 3D model.

Metalball modeling
A technique in which models are created using spheres that attract and cling to each other according to their proximity to one another and their field of influence. This technique is mostly used when creating organic models.

Model
As a verb it means to build a 3D object. As a noun it is referring to the end result of a 3D object.

Modifier
A modeling tool which deforms the structure of an entire object. Eg. Lathe

Multi-pass rendering
To render out the lighting or surface attributes of a scene as separate images, with the idea to put them together at a later stage. This technique can be used to speed up rendering or in order to develop the look of a scene by compositing the different passes together in various permutations.

Normal
An imaginary line drawn from the centre of a polygon at right angles to the surface.

Null
A point in a scene that does not render, but instead is used as a reference.

NURBS
Stands for Non-Uniform Rational B-Splines. NURBS curves are two-dimensional curves whose shape is determined by a series of control points between which they pass. When a series of such curves are joined together, they form a 3D NURBS surface.  NURBS are commonly used to model organic curved-surface objects.

Object
Anything that can be inserted and manipulated in a 3D scene. It can be lights, models, particles, cameras etc.

Patch
An area of a NURBS surface enclosed by a span square: the shape created by the intersection of four isoparms, two in the U direction, and two in the V direction.

Plane
A flat, 2D surface. This can be used for modeling or for references, depending on the final goal.

Point
A one-dimensional point in coordinate space. Points can be linked up to form polygons, used as control vertices for NURBS curves.

Polygon
Geometry formed by connecting 3 or more points. This is why in 2D you work with squares, but triangles in 3D.

Primitive
A simple 3D form often used as a basis when modeling something else. Examples include cubes, planes, spheres etc.

The next part goes from Q to T.