Reference
Here is the full reference docs for SALD. Still undergoing development.
Benchmark.js - Utility functions for benchmarking
Collide.js - Collision checking functions
GameObject.js - Basic game object with common properties and functions
Sound.js - Basic sound functionalities
Sprite.js - Spritesheets and easy frame selections and animations
Benchmark
The benchmark library can be used to calculate some data about the performance of a function.
Example
var benchmark = require('sald:benchmark');
function myReallySlowFunction() {
var i = 0;
for(var j = 0; j < 1000000000; j++) {
i++;
}
}
console.log(benchmark(myReallySlowFunction, {iterations: 1000, timeout: 10}));
Functions
benchmark(fn,opts)
benchmark will run fn a number of times specified by opts, either in iterations or elapsed time,
and return stats about the runtime performance of that fn.
Parameters
Param |
Type |
Decription |
fn |
function |
the function to benchmark |
opts |
object |
the options object which specifies iterations, timeout(ms), or both |
Returns
Return |
Type |
Description |
avg |
float |
average runtime for each call to fn (ms) |
min |
float |
minimum runtime for each call to fn (ms) |
total |
float |
total time it takes to run every iteration of fn (ms) |
iters |
int |
total number of times fn executed |
Collide
The collision library is used to test collisions between various shapes. The compiled collide.js can be viewed [here](../sald/collide.js)
The collide.js currently supports 6 collision detection functions as follows:
- Circle vs Circle
- Rectangle vs Rectangle
- Polygon vs Polygon {Convex}
- Ray vs Circle
- Ray vs Rectangle
- Ray vs Convex Polygon
Shape Definitions
Circle
A circle defined by the center and radius.
{
x: the x coordinate of the center of the circle
y: the y coordinate of the center of the circle
r: the radius of the circle
}
Rectangle
A rectangle defined by the top left and bottom right points.
{
min : {
x: the minimum x coordinate of the rectangle
y: the minimum y coordinate of the rectangle
},
max: {
x: the maximum x coordinate of the rectangle
y: the maximum y coordinate of the rectangle
}
}
Polygon
A polygon defined by the verticies.
[
{
x: the x coordinate of a vertex in the polygon
y: the y coordinate of a vertex in the polygon
},
...
]
Ray
Ray defined by the start and end points.
{
start: {
x: x coordinate of the start of the ray
y: y coordinate of the start of the ray
},
end: {
x: x coordinate of the end of the ray
y: y coordinate of the end of the ray
}
}
Functions
function rectangleRectangle(r1,r2)
Check to see if r1 and r2 overlap
Parameters
Param |
Type |
Decription |
r1 |
Rectangle |
First rectangle to check |
r2 |
Rectangle |
Second rectangle to check |
Returns
Return |
Type |
Description |
var |
bool |
true if r1 and r2 overlap, otherwise false |
function rayRectangle(r,b)
Check to see if r intersetcts b, and return where if it does
Parameters
Param |
Type |
Decription |
r |
Ray |
Ray to check for intersection |
b |
Rectangle |
Rectangle being intersected |
Returns
Return |
Type |
Description |
var |
object |
{t: t} if there is an intersection at (r.start + (r.end - r.start)*t), otherwise null |
function circleCircle(c1,c2)
Check to see if c1 and c2 overlap
Parameters
Param |
Type |
Decription |
c1 |
Circle |
First circle to check |
c2 |
Circle |
Second circle to check |
Returns
Return |
Type |
Description |
var |
bool |
true if c1 and c2 overlap, otherwise false |
function rayCircle(r,c)
Check to see if r intersetcts c, and return where if it does
Parameters
Param |
Type |
Decription |
r |
Ray |
Ray to check for intersection |
c |
Circle |
Circle to being intersected |
Returns
Return |
Type |
Description |
var |
object |
{t: t} if there is an intersection at (r.start + (r.end - r.start)*t), otherwise null |
function convexConvex(p1, p2)
Check to see if p1 and p2 (Convex Polygons) overlap
Parameters
Param |
Type |
Decription |
p1 |
Polygon |
First polygon to check |
p2 |
Polygon |
Second polygon to check |
Returns
Return |
Type |
Description |
var |
bool |
true if p1 and p2 overlap, otherwise false |
function rayConvex(r,p)
Check to see if r intersetcts p (Convex Polygon), and return where if it does
Parameters
Param |
Type |
Decription |
r |
Ray |
Ray to check for intersection |
p |
Polygon |
Polygon being intersected |
Returns
Return |
Type |
Description |
var |
object |
{t: t} if there is an intersection at (r.start + (r.end - r.start)*t), otherwise null |
Game Object
GameObjects can be used to define characters and objects in the game world which can collide. GameObject.js can be viewed [here](../sald/GameObject.js)
Usage
Require GameObject.js in your code
var GameObject = require('sald:GameObject.js');
Following which you, you can create new instances of GameObject.
Note, by default a game object has a rectangular collider from its top left corner (x, y) to the bottom right corner (x + width, y + height).
If you want to override this default behavior, pass 0 for width or 0 for height on initialization.*
Example
var x = 0;
var y = 0;
var width = 20;
var height = 60;
var anchor = { x : 0, y : 0 }; // this is the top left corner, which happens by default {x : 1, y : 1} is the bottom right corner
var player = new GameObject(x, y, width, height);
// is the same as
var player2 = new GameObject(x, y, width, height, { x : 0, y : 0 });
Custom Collision Shapes
If you want the collision shape to be different, you can change it. For example:
...
var player1 = new GameObject(x, y, width, height, anchor);
...
// A circle with radius 20, 120 pixels in on the x axis, 40 pixels in on the y axis from the anchor
// r : radius, x : x position of center, y : y position of center
var circle = {
r : 20,
x : 120,
y : 40,
};
// A circle half the width away from the anchor and half the height away from the anchor
var relativeCircle = {
r : 0.5, // r is relative to the GameObject's width
x : 0.5,
y : 0.5
}
// min : top left corner, max : bottom right corner
var rectangle = {
min : {
x : 20,
y : 20
},
max : {
x : 80,
y : 60
}
};
var relativeRectangle = {
min : {
x : -0.5,
y : -0.5
},
max : {
x : 0.5,
y : 0.5
}
}
// a list of {x, y} points listed in Counter Clockwise order which form a convex polygon.
var convexPolygon = [
{x : 20, y : 20},
{x : 10, y : 30},
{x : 20, y : 40},
{x : 30, y : 30}
];
player1.setCollisionRect(rectangle, false);
player2.setCollisionRect(relativeRectangle, true);
player3.setCollisionCircle(circle, false);
player4.setCollisionCircle(relativeCircle, true);
player5.setCollisionConvex(convexPolygon, false);
Specifications
GameObject.js supports collisions between Convex Polygons, Circles, and Rectangles. Any two of the listed shapes can collide with each other. (circle/circle, rectangle/circle, etc.)
if (player.isColliding(ball)){
// decide what happens when the objects are colliding
}
You can also retrieve the collision shape as follows:
var shape = player.collisionShape();
Shape will be null if there is no collider, otherwise it will be a json object containing a key that is either "rect", "circle", or "convex", and the value will be the collision object.
The returned object will not be relative to the objects width and height, but rather absolute, in order to compute collisions with the collision library should you want to add extra functionality (see ray collisions).
Mainloop
SALD provides a basic mainloop. To use, attach to a canvas with:
mainloop.start( ):
var mainloop = require('sald:mainloop.js');
//configure size and callbacks:
sald.size = {x:320, y:240, mode:"exact"};
sald.scene = myScene;
//call this once the page has loaded:
mainloop.start(document.getElementById("canvas"));
The mainloop.start()
method takes a second options argument, which currently only has one supported option -- gl
-- which creates a webgl context instead of a 2d context, and uses the supplied attributes:
mainloop.start(document.getElementById("canvas"), {gl:{}}); //webgl canvas, default attribs
mainloop.start(document.getElementById("canvas"), {gl:{antialias:false}}); //webgl canvas, 'antialias' attribs set false
A list of possible attributes are available at https://www.khronos.org/registry/webgl/specs/1.0/#5.2 .
The mainloop module uses the following properties in the `sald` namespace:
Canvas
sald.size configures the desired canvas size and resizing behavior:
If mode is not specified, or is "exact"` size is set exactly in pixels:
sald.size = {x:320, y:240};
sald.size = {x:320, y:240, mode:"exact"};
If mode
is multiple
, canvas size will be an integer multiple of the given size.
This is good for pixel-art games, or games that want an exact aspect ratio:
sald.size = {x:320, y:240, mode:"multiple"}; //pixel art game with 320x240 canvas
sald.size = {x:16, y:9, mode:"multiple"}; //game wants to be exactly 16x9 aspect
If mode
is ratio
, canvas size will be an rounded, non-integer multiple of the size.
This is good for games that just want an approximate aspect ratio:
sald.size = {x:1, y:1, mode:"ratio"}; //square canvas
sald.size = {x:16, y:9, mode:"ratio"}; //16x9-ish, might not be exact due to rounding
`sald.ctx` is the drawing context associated with the canvas. SALD adds some extra properties:
sald.ctx.width //current width
sald.ctx.height //current height
sald.ctx.factor //scaling factor relative to sald.size
One generally uses `ctx.factor` to set the `ctx`'s transform to allow working in "virtual pixels":
sald.size = {x:320, y:240, mode:"multiple"}; //canvas will be some multiple of 320x240
function draw() {
var ctx = sald.ctx;
//scale to account for pixel size:
ctx.setTransform(ctx.factor,0, 0,ctx.factor, 0,0);
//draw full-screen pink rectangle:
ctx.fillStyle = "#f0f"; //bright pink
ctx.fillRect(0, 0, 320, 240); //<-- working in pixel units
}
Input
`sald.keys` contains the names of every held-down key. (Key names are specified in mainloop.js)
function update(elapsed) {
var keys = sald.keys;
if (keys.LEFT) player.x -= 10.0 * elapsed;
if (keys.RIGHT) player.x += 10.0 * elapsed;
}
sald.mouse contains the position (in pixels from the upper-left corner of the canvas) and held-down buttons for the mouse. NOTE: `mouse === null` if no mouse events have happened yet.
function update(elapsed) {
var mouse = sald.mouse;
if (mouse && mouse.buttons.LEFT) {
console.log("Left button held down; currently at " + mouse.x + ", " + mouse.y + ".");
}
}
Callbacks
The `sald.scene` object holds callbacks used by the mainloop to define custom update, draw, and event handlers. You can package handlers together and assign to scene all at once, or assign each function individually.
The `sald.scene.update` function is called with the current elapsed time. You can use it to update game state.
The `sald.scene.draw` function is called once per frame. It should draw the current game state into `sald.ctx`.
Considering the Model-View-Controller paradigm, the update function should update the model, while the draw function should update the view.
function update(elapsed){
// elapsed is a float measure of how many seconds have passed since the last update
}
sald.scene.update = update;
function draw(){
// draw whatever should be on the screen, using sald.ctx
}
sald.scene.draw = draw;
The `sald.scene.key` function is called every time a key is pressed or released:
function key(keyName, isPressed){
if (keyName === "SPACE" && isPressed) {
console.log("You pressed space.");
}
}
window.sald.scene.key = key;
NOTE: On a Mac, `sald.keys.WINDOWS` is the "Command" key.
The `sald.scene.mouse` function is called whenever a mouse button is pressed or the mouse moves:
function mouse(position, buttonName, isPressed) {
if (buttonName === undefined) {
//just movement
} else {
if (buttonName === "MIDDLE") {
console.log("Middle button " + (isPressed ? "down" : "up") + ".");
}
}
}
sald.scene.mouse = mouse;
NOTE: when the mouse is moved, this function will be called with `buttonName` and `isPressed` undefined.
NOTE 2: `sald.takeRightClickInput` (default: true) can be set to false to allow right click events to open the context menu as expected.
The `sald.scene.wheel` function handles scroll wheel events:
function wheel(delta) {
}
sald.scene.wheel = wheel;
NOTE: if not defined, scroll events will scroll the page as usual.
Sound
SALD provides a sound support. To use first require sound.js in your code
var soundController = require('sald:sound.js');
Require individual sound files, then play
var explosionSound = require('./data/sounds/explosion.ogg');
explosionSound.play();
Currently, the sound module only supports .ogg files. Here is an online file converter:
http://audio.online-convert.com/convert-to-ogg
Sound Controller
Use sound controller to control the master volume.
soundController.setVolume(float from 0.0 to 1.0);
soundController.getVolume() // returns float from 0.0 to 1.0
soundController.toggleMute();
soundController.setIsMuted(boolean);
soundController.isMuted(); // returns boolean
CustomAudio Class
The CustomAudio class is used as a wrapper to the Audio html tag. It allows you to play multiple instances of a single sound at the same time.
var explosionSound = require('./data/sound/explosion.ogg');
// The default number of instances is 4
explosionSound.setNumInstances(number of instances);
explosionSound.getVolume(); // returns float between 0.0 and 1.0
explosionSound.setVolume(float between 0.0 and 1.0);
explosionSound.play();
explosionSound.setShouldLoop(boolean);
explosionSound.doesLoop(); // returns boolean
explosionSound.getNumberOfLiveInstances() // How many of this sound is playing right now
explosionSound.pause(); // pauses all instances of this sound, allowing you to resume at any point
explosionSound.resume(); // resumes paused audio
explosionSound.stop(); // Stops all instances of this sounds audio, with no ability to resume
Sprite
Allows the user to draw sprites from a spritesheet and define various animations to be drawn
Usage
Create a new sprite object in your game scene file like below
var sprite = require('sald:Sprite.js');
var heroImg = require('../img/spritesheet.png');
All the spritesheet objects to be animated have to be strictly defined as below:
var heroSprite = new sprite(heroImg, {
'walk' : {
x:0,y:0,
width:40,height:60,
size:4
},
'run' : {
x:0,y:60,
width:40,height:60,
size:4
}
})
The first parameter is the spritesheet image, heroImg in this case is the entire spritesheet image with several different animation frames within it. The second parameter has to be a list of animations by name along with x, y, width, height and size properties.
[x,y represent the indexes in pixel coordinates of start row and column of animation from the spritesheet image]
[width,height represent the width and height of each sprite in pixel size, you can check the same in paint for image width and height in pixels]
[size is the max number of frames till which that animation needs to be played from the spritesheet image]
A note about proper sprite splicing: The x and y properties above are what the engine will ultimately use to splice the individual sprites from the sprite sheet. x is the horizontal offset, while y is the vertical offset (with 0,0 representing the top-left corner of the image). There are two things to keep in mind when dealing with pixel coordinates. One - if you truly wish to have well defined and proportional sprites, it helps if the pixel width is divisible by the number of columns and the pixel height is divisible by the number of rows. Otherwise you risk them being clipped at weird positions. Two - If you wish to start cutting from the middle of a sprite sheet and not from the far left, you'll need to make sure that you're providing the top-left corner of the sprite you wish to start at (not the center of the sprite).
-> The draw function takes the following arguments:
* anim: animation name
* x: x position in world space
* y: y position in world space
* scalex: x scale of sprite
* scaley: y scale of sprite
* anchorx: x anchor value (0-1)
* anchory: y anchor value (0-1)
Call the draw function inside the update loop.
sprite.draw(anim, x, y, )
You do not need to give the frame number in draw function anymore because it is handled by a framework called Animators inside the Sprite object. When you initialize a Sprite object, each animation of sprite gets an animator object attached to it internally which handles the state of the animation. It also provides neat features like animation speed, looping functionality and stop feature.
After creating your sprite, you can call these functions to access animators and its functions:
sprite.animators[<animation-name>].loop(boolean);
- The boolean here can be true or false depending upon whether you want to loop the animation or not
sprite.animators[<animation-name>].speed(speed);
- The 'speed' has to be in frames/second.
- It is optional. Default is 60 frames/second.
sprite.animators[<animation-name>].stop();
- This function is used to stop the animation from running. And resets that animation's the state. (Can be changed, just a design decision)
Tilemap
Require Tilemap.js in your code
var Tilemap = require('sald:Tilemap.js');
Initilize Tilemap with correct parameters.
Tilemap(img, map, tilW, tilH, tilR, tilC, defaultTile)
img = tilemap image
map = 2d map of which location to draw which tile
tilW = pixel width of individual tile
tilH = pixel height of individual tile
tilR = Height of tilemap in terms of tiles
tilC = Width of tilemap in terms of tiles
defaultTile = Number of tile to be draw when outside of map
Tilemap.draw(camera) takes in a object that has properties x & y which defines the center of the map to draw from. Call it after updates to tilemap or camera for redraw.
camera = {x : 0, y : 0}
x = x index location in the 2d world map that was pased to Tilemap
y = y index location in the 2d world map that was pased to Tilemap