TSA Game JavaScript Reference
Working with Things
All objects in the game world are Things, including characters, enemies, special effects, and user-interface elements like menus and buttons. You create things by using the command
new Thing();
Which is an expression that returns a thing. This thing can be assigned to a variable, added to a list, etc.
Creating and Deleting Things
How to create a Thing:
var bob = new Thing();
Every time you see “new Thing()” a new Thing is being created and added to the world. You can use Things in place of any other variable, such as lists.
Any time you wish for a Thing to disappear from the environment, do so like this
bob.isdeleted = true;
All properties of a Thing can be changed by using
thingname.property = value;
where thingname is any variable, array member, parameter, etc., that you have created that refers to a Thing. You can also invent properties to add to Things such as
player.health = 100;
The game engine has no idea what to do with invented properties. You will have to write code that does something meaningful with them (such as ending the game when the player’s health drops to or below zero.)
The game engine does pay attention to many built-in properties that determine how the Thing behaves.
Graphic Image Properties
bob.imagename = 'manblue'; bob.isflipped = true; // bob now faces left
Note that several physical properties will affect the image, such as width, height and angle. The shape property does not affect the image; only the physical behavior (so you can assign a square image to a round Thing and a round image to a square Thing if you really want to. Sliding barrels and rolling crates look a little weird, though.)
Graphic Polygon Properties
bob.strokecolor = 'blue'; bob.fillcolor = 'yellow';
If you use colors instead of images, it will use the exact shape specified by the physical definition.
The colors use standard HTML notation, so it is possible to use any of the standard CSS colors including specific RGB values.
Graphic Text Properties
You can use text instead of shapes or images. If you do, it will use the strokecolor and fillcolor to draw the text, as described in the polygon section. It will also modify the shape to be a rectangle that fits the text.
Note that the height of the text is always the Thing’s height property. If the Thing is 1 meter tall, it will use 1-meter tall letters. You may not specify point size or pixels because those are meaningless in the game world which measures everything in meters.
bob.height = 0.2; bob.font = 'Arial bold'; bob.strokecolor = 'black'; bob.fillcolor = 'red'; bob.imagetext = 'Ammo: ' + bob.bulletcount;
No matter how you make a Thing visible, you can use the following:
bob.iscentered = false;
Normally, Things are centered on the x/y coordinate. If you wish for the x/y coordinate to refer to the top-left corner, set this property to false.
bob.isoverlay = true;
Normally, Things move around with the view, which can be larger than the screen. If you wish to keep the Thing moving relative to the screen only, set this to true. This is useful for indicators and other user-interface elements.
Physical Position Properties
bob.x = 4.5; bob.y = 3.2; bob.z = 50; bob.angle = 180; //degrees, where 0 points to the right
All coordinates are in meters, except the z-coordinate, which is just a visual helper to determine which objects overlap which other objects. All objects default to a z-coordinate of 100. Set lower to put them in the background, higher to put them in the foreground.
Physical Shape Properties
bob.width = 1.5; bob.height = 1.0; bob.shape = BOX; mike.shape = CIRCLE; car.shape = [[-0.4, -0.47], [0, -0.5], [0.5, 0], [0.5, 0.5], [-0.5, 0.5]];
All measurements are in meters. The width and height can be different (except for CIRCLE shapes.) Note that some images, such as the rocket and car images, are designed to be taller or wider than a square.
Custom shapes can be created by specifying a series of points that draw a polygon, as above, such as this shape that fits the “carsideblue” image. Note that there are both negative and positive values being used because the center of the Thing should always be the origin (0,0). Also note that concave shapes are not permitted by the physics engine (they can be done only be welding together a number of convex shapes.)
Physical Movement Properties
bob.xvel = 2; bob.yvel = -10; bob.avel = 720; bob.bounce = 0.9; bob.friction = 0.1; bob.density = 0.2;
Bob is going to jump up (negative y direction) and to the right (positive x) and spin counter-clockwise with an angular velocity of 720 degrees per second. When he hits something, he’s going to bounce off it (with 0.9 of the velocity he hit it) and when he finally lands, he’s going to slide for a long time (friction 0.0 slides forever, 1.0 sticks like glue.) He is also quite light and will get knocked around by other Things (with a density of only 0.2 kg per square meter.)
You can also use several convenience functions to set these values according to automatic calculations:
bob.pointat(planet.x, planet.y); bob.velocityat(waypoint.x, waypoint.y, 5, 10); rocket.setvelocity(45, 5, 10); wheel.spin(-720);
Bob’s angle will be set to point directly at the planet Thing’s coordinates. Bob will move toward the waypoint at 5 meters per second, accelerating at 10 meters per second per second (acceleration is optional if you wish to use a fixed speed.)
The rocket Thing will move at a 45-degree angle from horizontal at 5 meters per second, also with an optional acceleration.
The wheel Thing will gradually spin faster and faster, accelerating clockwise at 720 degrees per second per second (positive for counter-clockwise.)
Physical Movement Limiters
bob.slowing = 0.5; bob.spinslowing = 180;
Bob will gradually slow down at 0.5 meters per second per second, and his spinning will gradually slow down by 180 degrees per second per second, all of which will make it feel as though he’s moving through syrup. Use these mostly for top-down simulations (car racing, sports, RTS, etc.) so that Things don’t fly around forever. Space games shouldn’t have it, but they usually do because real frictionless space physics is hard for players.
platform.isfixed = true;
Set this to true for any objects like platforms, ramps, terrain or user-interface elements which must never move.
bob.isturnable = false;
Running and walking characters look really silly if they fall on their face jumping down from a platform. Take a cue from Mario and set any Thing that must always remain at a fixed angle so that it is not turnable.
bullet.isfast = true;
Only use this if your projectiles or other fast-moving objects are tunneling right through solid Things when you don’t want them to. Setting this to true tells the physics engine to check every point along the path every frame.
Physical Collision Management
bob.type = 5;
All Things collide or pass through other Things depending on their type, which is a whole number (defaulting to zero.)
bob.collidetypes = [0, 2, 5];
You supply a list of types that the Thing is allowed to collide with. It will pass through all other types.
particle.type = GHOSTTYPE;
Things with a GHOSTTYPE (a value of 16 or greater) will never collide with anything.
To react to collisions, use thingname.collidefunc function described in the event handling section.
Event Handling
All Things can be assigned a function for when they are pressed or released by the player, when they collide with something, or every frame if you need to do some special drawing effect (do not perform physics operations in a frame function.) The press/drag/release functions can have optional x,y coordinates after the Thing parameter. The complete list of available event handler properties are:
pressfunc, dragfunc, releasefunc, framefunc, collidefunc
The collidefunc has not only the Thing being checked, but the Thing that collided with it, and it will be called both when the Thing starts colliding with it, and when the Thing moves away and is no longer touching it, which is why there is an “istouching” parameter. Note that an elastic “bounce” will cause this function to be called twice in succession, once for colliding and once for moving away.
function pressitem(item) { item.fillcolor = 'yellow'; bob.yvel = -10; } function releaseitem(item) { item.fillcolor = 'black'; } jumpbutton.pressfunc = pressitem; jumpbutton.releasefunc = releaseitem; function drawforcefield(thing) { if (thing.isforcefieldon) drawcircle(thing.x, thing.y, thing.width, 'red'); } function collideplayer(player, otherthing, istouching) { if (otherthing.type == FLOOR) player.isstanding = istouching; else if ((otherthing.type == BULLET) && istouching) { player.health -= otherthing.damage; otherthing.isdeleted = true; } } bob.framefunc = drawforcefield; bob.collidefunc = collideplayer; joe.collidefunc = collideplayer; crosshairs.dragfunc = aimweapon;
Note that the same function can be used for different Things if they are using the same behavior, as with bob and joe above, who are things that stand on the floor and can be hit and damaged by bullets.
In order to tell a thing to do something after a certain amount of time elapses, or to keep doing something over and over on a particular schedule, use the dolater function:
function deletething(thing) { thing.isdeleted = true; } function dropbomb(enemy) { var bomb = new Thing(); bomb.imagename = 'fire'; bomb.x = enemy.x; bomb.y = enemy.y; bomb.dolater(deletething, 2.0); enemy.dolater(dropbomb, 1.5); } badguy.dolater(dropbomb, 5.0);
In this example, the badguy Thing will wait five seconds, then drop a bomb. It will then keep dropping another bomb every one and a half seconds. The bombs will delete themselves after 2 seconds.
Keyboard Handling
There are two functions for interacting with the computer keyboard. Be aware that if you use these functions, your game can no longer be played on a mobile device as a touch-screen game.
ispressing(KEYCODE)
isholding(KEYCODE)
Available key codes are: LEFT, UP, RIGHT, DOWN, SPACE, KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J, KEY_K, KEY_L, KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z, KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, and CLICK (the left mouse button, an alternative to the event handler functions.)
At any point in the program, you can check to see if a key is being held down, or if the key was pressed since you last checked. Use ispressing for any situation where pressing a key once should make something happen, such as a “throw” button. Use isholding in any situation where something should keep happening as long as you keep the key held down, such as a “run” function.
function throw() { rock = new Thing(); rock.x = player.x; rock.y = player.y } function run() { player.xvel = 1; } function everyframe() { if (ispressing(KEY_R)) throw(); if (isholding(KEY_W)) run(); } game.framefunc = everyframe
Special Location Functions
if (bob.distanceto(joe.x, joe.y) < 3.0) bob.explode();
The distanceto function measures from a Thing to another point. If bob is less than 3 meters away from joe, he will explode.
if (bob.contains(waypoint.x, waypoint.y)) bob.getnextwaypoint();
The contains function can tell if a point is inside the Thing’s shape, which is especially helpful for Things that intentionally don’t collide.
if (!particle.isonscreen()) particle.isdeleted = true;
The isonscreen function is false if the object is off the screen.
if (bob.ontile() == 't') bob.teleport();
The tile function can tell what environment tile is under the Thing.
Attaching Things Together
metalbox.weldto(otherbox, 1, 0.5); volleyball.springto(pole, 0.8, 0, -2.0); backwheel.axleto(car, -0.5, 0.5); frontwheel.axleto(car, 0.5, 0.5);
In this example, a metal box has been welded to another box so that they move as one object.
A volleyball has been attached to the top of a pole to make a tetherball with a very springy connection like a bungee cord.
Two spinning wheels have been attached to a car at the bottom left and right.
All three attachment functions, weldto, springto and axleto, can also accept two more parameters representing where on the attaching Thing the attachment is connected. For example, if you wanted the wheels to be wobbly and off-center from the axle, you could add two more parameters such as 0.02, 0.01 to offset it two centimeters to the right and one centimeter down. Also, the axleto function can accept two more parameters for the minimum and maximum angle.
catapultarm.axleto(catapultbody, 1.0, 0, 1.5, 0.2, -45, 10);
A catapult arm has been attached to a catapult body. The far right side (1.5) and partially lower part (0.2) of the arm has been attached to the right side (1.0) of the body. It can rotate up to 45 degrees clockwise and 10 degrees counter-clockwise.
Working with the World
There are a number of functions and properties that are not specific to individual Things but affect the entire game world. Note that the environment tiles have some simplified properties of Things for convenience.
Global Effects
setbackground('landscape'); setgravity(0, 9.8);
Some background images imply a top-down view, such as the pavement or grass images. Others imply a side view, such as sky and landscape. Gravity is appropriate for a side view game, and 9.8 meters per second per second is appropriate Earth gravity.
game.clear();
Make sure you clear the entire game when doing things like switching levels or going to new environments. This removes all Things and clears the map of environment tiles. It leaves the background and view sizing options.
Environment Tiles
To save time creating levels, instead of making huge numbers of Things, you can specify environment tiles in a square grid. Each tile can be represented by a single letter. First, define what the environment tiles are like. By default, they use a BOX shape and have a small amount of bounce and friction. You can optionally change any of those properties. Tiles can have an imagename, shape, type, bounce and friction.
tiletype('x').imagename = 'metal'; tiletype('x').type = WALLS; // user-defined type tiletype('i').imagename = 'ice'; tiletype('i').friction = 0.06; tiletype('d').imagename = 'wood'; tiletype('d').type = GHOSTTYPE; tiletype('b').imagename = 'planetred'; tiletype('b').shape = CIRCLE; tiletype('b').bounce = 2.0;
In this map, the ‘x’ character will represent metal blocks. ‘i’ will be ice blocks with low friction. ‘d’ will be wooden doors that the player can walk through. ‘b’ will be extremely elastic round bumpers like in a pinball machine.
You may also want to specify that some tile letters are places where you are going to create a Thing yourself, but rather than manually place them, you’re going to let the map generator tell you where to put them. In order to make that possible, define a map builder function which takes a tile letter and a column and row as parameters
function mapbuilder(tile, column, row) { if (tile == 'p') makeplayer(column, row); if (tile == 'e') makeenemy(column, row); }
Once you have the tiles created and the map builder function, create your map by making an array of strings. Note that you can make as many different maps as you want and you can change them whenever you like.
var testmap = [ 'x b b x b x', 'x x x', 'x p d x', 'xxxxxxxiiix' ];
Then call the function to set up the board according to the map. All tiles are assumed to be one meter square unless you specify otherwise
board.tilesize = 1.0; // redundant setboard(mapname, mapbuilder);
Scrolling and Scaling the View
The screen size will always expand to fit your browser window, but how much of the world that you can see in the view will always be scaled to fit your specifications. For example, if you tell it that the view is 20 meters tall, then you will always be able to see 20 meters worth of the world in the view no matter what your screen size or window size.
view.height = 20;
You can optionally change the view.width, as well. If you wish to create a vertical portrait-style view, change the view.aspect to a different proportion (it represents width / height and defaults to a golden ratio landscape format.) If you change one property of the view, the others change to fit.
The world itself is infinite in size. Things can be placed anywhere. The tiled environment can be any size (watch out for performance issues, though.) In order to scroll the view, imagine that the view was a rectangle that was being moved around the world.
view.x = 30; view.y = 15;
This would should you a view starting with the top-left corner of the screen showing you the world 30 meters to the right and 15 meters down from the top-left.
You can center the view on a particular Thing by using
view.x = thing.x - view.width / 2; view.y = thing.y - view.height / 2;
but with many games, it makes more sense to shift the view to make more playing area relevant in front of the player wherever they’re moving, so a more fluid, variable view movement system is recommended. (Note that you can do fun things with invisible objects connected to the main player with springs and slowing. Automatic physics makes everything easier.)
Sound and Music
Sound effects and music are handled slightly differently. A sound effect will play only once, but music will loop continuously until told to stop.
playsound('bell'); playmusic('rock'); stopmusic('rock');
Utilities (Randomization and Drawing)
Any time you need a random number, call one of the random number functions. You can call either one with one parameter to get a number from zero to the number specified, or with two parameters to get a random number in a range.
Use randint for getting random integers, which is useful when picking items from a list. Use random when setting random positions, velocities, angles, etc., because the physics engine does everything with decimal numbers and can handle any values.
var names = ['bob', 'sue', 'joe']; var whoever = names[randint(2)]; // will be 0, 1 or 2. player.avel = randint(360, 720);
Whoever will be a name from the names list with an index number between 0 and 2 (the indexes of the names list are 0, 1 and 2.)
The player will spin counter-clockwise at a rate somewhere between 360 and 720 degrees per second.
It is sometimes convenient to draw lines between Things or circles around them without having to create whole new Things.
drawline(player.x, player.y, waypoint.x, waypoint.y, 'green', 0.05); drawcircle(waypoint.x, waypoint.y, 0.7, 'blue', 0.05);
Global Event Handling
It is sometimes helpful to tell when the user has pressed, dragged or released somewhere other than on a Thing. It can also be helpful to do some extra drawing work every frame for the world in general. For that purpose, there is a “game” object with event handlers identical to the Thing event handlers pressfunc, dragfunc, releasefunc and framefunc, but with only the pointer x and y positions as parameters. This can be used to create gesture detection.
var startx, starty; function pressanywhere(x, y) { startx = x; starty = y; } function releaseanywhere(x, y) { if (x > startx + 1.0) console.log('swiped right at least one meter'); } game.pressfunc = pressanywhere; game.releasefunc = releaseanywhere;
There is also a generic “dolater” function that is not associated with any particular Thing
function timesup() { message.imagetext = "OUT OF TIME!"; dolater(returntomenu, 3.0); } dolater(timesup, 300); // five-minute timer