S.A.L.D.

Studio A Ludum Dare JS Game Engine


Home | Reference | Examples | About Us


Project maintained by sald-devs Hosted on GitHub Pages — Theme by mattgraham

Reference

Here is the full reference docs for SALD. Still undergoing development.


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

Return to top


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).

Return to top


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.

Return to top


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

Return to top


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)
        

Return to top


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

Return to top