kaboom.global()
import all kaboom functions into global namespace
// 1) import everything to global
kaboom.global();
init();
// 2) keep them under kaboom namespace to prevent any collision
const k = kaboom;
k.init();
kaboom.js (beta) is a JavaScript library that helps you make games fast and fun!
also check out the kaboom environment on replit.com!
Usage
quick start
<script src="https://kaboomjs.com/lib/0.1.0/kaboom.js"></script>
<script type="module">
// make kaboom functions global
kaboom.global();
// init kaboom context
init();
// define a scene
scene("main", () => {
// add a text at position (100, 100)
add([
text("ohhimark", 32),
pos(100, 100),
]);
});
// start the game
start("main");
</script>
Import
All functions are under a global object 'kaboom', but you can also choose to import all functions into global namespace.
kaboom.global()
import all kaboom functions into global namespace
// 1) import everything to global
kaboom.global();
init();
// 2) keep them under kaboom namespace to prevent any collision
const k = kaboom;
k.init();
Lifecycle
Application Lifecycle Methods
init([conf])
initialize context
// quickly create a 640x480 canvas and get going
init();
// options
init({
width: 480, // width of canvas
height: 480, // height of canvas
canvas: document.getElementById("game"), // use custom canvas
scale: 2, // pixel size (for pixelated games you might want small canvas + scale)
clearColor: rgb(0, 0, 1), // background color (default black)
fullscreen: true, // if fullscreen
crisp: true, // if pixel crisp (for sharp pixelated games)
});
start(scene, [...args])
start the game loop with specified scene
scene("game", () => {/* .. */});
scene("menu", () => {/* .. */});
scene("lose", () => {/* .. */});
start("game");
Scene
Scenes are the different stages of a game, like different levels, menu screen, and start screen etc. Everything belongs to a scene.
scene(name)
describe a scene
scene("level1", () => {
// all objs are bound to a scene
add(/* ... */)
// all events are bound to a scene
keyPress(/* ... */)
});
scene("level2", () => {
add(/* ... */)
});
scene("gameover", () => {
add(/* ... */)
});
start("level1");
go(name, [...args])
switch to a scene
// go to "paused" scene when pressed "p"
scene("main", () => {
let score = 0;
keyPress("p", () => {
go("gameover", score);
})
});
scene("gameover", (score) => {
// display score passed by scene "main"
add([
text(score),
]);
});
layers(names, [default])
define the draw layers of the scene
// draw background on the bottom, ui on top, layer "obj" is default
layers([
"bg",
"obj",
"ui",
], "obj");
// this will be added to the "obj" layer since it's defined as default above
const player = add([
sprite("froggy"),
]);
// this will be added to the "ui" layer cuz it's specified by the layer() component
const score = add([
text("0"),
layer("ui"),
]);
gravity(value)
set the gravity value (defaults to 980)
// (pixel per sec.)
gravity(1600);
camPos(pos)
set the camera position
// camera position follow player
player.action(() => {
camPos(player.pos);
});
camScale(scale)
set the camera scale
if (win) {
camPos(player.pos);
// get a close up shot of the player
camScale(3);
}
camRot(angle)
set the camera angle
camRot(0.1);
camIgnore(layers)
make camera don't affect certain layers
// make camera not affect objects on layer "ui" and "bg"
camIgnore(["bg", "ui"]);
Asset Loading
Load assets into asset manager. These should be at application top.
loadSprite(name, src, [conf])
load a sprite
loadSprite("froggy", "froggy.png");
loadSprite("froggy", "https://replit.com/public/images/mark.png");
// slice a spritesheet and add anims manually
loadSprite("froggy", "froggy.png", {
sliceX: 4,
sliceY: 1,
anims: {
run: [0, 2],
jump: [3],
},
});
// load with aseprite sprite sheet
loadSprite("froggy", "froggy.png", {
aseSpriteSheet: "froggy.json", // use spritesheet exported from aseprite
});
loadSound(name, src, [conf])
load a sound
loadSound("shoot", "shoot.ogg");
loadFont(name, src, charWidth, charHeight, [chars])
load a font
// default character mappings: (ASCII 32 - 126)
// const ASCII_CHARS = " !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
// load a bitmap font called "04b03", with bitmap "04b03.png", each character on bitmap has a size of (6, 8), and contains default ASCII_CHARS
loadFont("04b03", "04b03.png", 6, 8);
// load a font with custom characters
loadFont("CP437", "CP437.png", 6, 8, "☺☻♥♦♣♠");
Objects
Game Object is the basic unit of Kaboom, each game object uses components to compose their data and behavior.
add(comps)
add a game object to scene
// a game object consists of a list of components
const player = add([
// a 'sprite' component gives it the render ability
sprite("froggy"),
// a 'pos' component gives it a position
pos(100, 100),
// a 'body' component makes it fall and gives it jump()
body(),
// raw strings are tags
"player",
"killable",
// custom fields are assigned directly to the returned obj ref
{
dir: vec2(-1, 0),
dead: false,
speed: 240,
},
]);
player.action(() => {
player.move(player.dir.scale(player.speed));
});
player.hidden = false; // if this obj renders
player.paused = true // if this obj updates
// runs every frame as long as player is not destroy() ed
player.action(() => {
player.move(100, 0);
});
// provided by 'sprite()'
player.play("jump"); // play a spritesheet animation
console.log(player.frame); // get current frame
// provided by 'pos()'
player.move(100, 20);
console.log(player.pos);
// provided by 'body()'
player.jump(320); // make player jump
destroy(obj)
remove a game object from scene
collides("bullet", "killable", (b, k) => {
// remove both the bullet and the thing bullet hit with tag "killable" from scene
destroy(b);
destroy(k);
score++;
});
obj.action(cb)
update the object, the callback is run every frame
player.action(() => {
player.move(SPEED, 0);
});
obj.use(comp)
add a component to a game object
// rarely needed since you usually specify all comps in the 'add()' step
obj.use(scale(2, 2));
obj.exists()
check if obj exists in scene
// sometimes you might keep a reference of an object that's already 'destroy()'ed, use exists() to check if they were
if (obj.exists()) {
child.pos = obj.pos.clone();
}
obj.is(tag)
if obj has certain tag(s)
if (obj.is("killable")) {
destroy(obj);
}
obj.on(event, cb)
listen to an event
// when obj is 'destroy()'ed
obj.on("destroy", () => {
add([
sprite("explosion"),
]);
});
// runs every frame when obj exists
obj.on("update", () => {
// ...
});
// custom event from comp 'body()'
obj.on("grounded", () => {
// ...
});
obj.trigger(event)
trigger an event (triggers 'on')
obj.on("grounded", () => {
obj.jump();
});
// mainly for custom components defining custom events
obj.trigger("grounded");
get(tag)
get a list of obj reference with a certain tag
const enemies = get("enemy");
every(tag, cb)
run a callback on every obj with a certain tag
// equivalent to destroyAll("enemy")
every("enemy", (obj) => {
destroy(obj);
});
destroyAll(tag)
destroy every obj with a certain tag
Components
Built-in components. Each component gives the game object certain data / behaviors.
pos(x, y)
object's position in the world
const obj = add([
pos(0, 50),
]);
// get the current position in vec2
console.log(obj.pos);
// move an object by a speed (dt will be multiplied)
obj.move(100, 100);
scale(x, y)
scale
const obj = add([
scale(2),
]);
// get the current scale in vec2
console.log(obj.scale);
rotate(angle)
scale
const obj = add([
rotate(2),
]);
obj.action(() => {
obj.angle += dt();
});
color(r, g, b, [a])
color
const obj = add([
sprite("froggy"),
// give it a blue tint
color(0, 0, 1),
]);
obj.color = rgb(1, 0, 0); // make it red instead
sprite(id, [conf])
draw sprite
// note: this automatically gives the obj an 'area()' component
const obj = add([
// sprite is loaded by loadSprite("froggy", src)
sprite("froggy"),
]);
const obj = add([
sprite("froggy", {
animSpeed: 0.3, // time per frame (defaults to 0.1)
frame: 2, // start frame (defaults to 0)
}),
]);
// get current frame
console.log(obj.frame);
// play animation
obj.play("jump");
// stop the anim
obj.stop();
obj.onAnimEnd("jump", () => {
obj.play("fall");
});
text(txt, size, [conf])
draw text
// note: this automatically gives the obj an 'area()' component
const obj = add([
// content, size
text("oh hi", 64),
]);
const obj = add([
text("oh hi", 64, {
width: 120, // wrap when exceeds this width (defaults to 0 no wrap)
font: "proggy", // font to use (defaults to "unscii")
}),
]);
// update the content
obj.text = "oh hi mark";
rect(w, h)
draw rectangle
// note: this automatically gives the obj an 'area()' component
const obj = add([
// width, height
rect(50, 75),
pos(25, 25),
color(0, 1, 1),
]);
// update size
obj.width = 75;
obj.height = 75;
area(p1, p2)
a rectangular area for collision checking
// 'area()' is given automatically by 'sprite()' and 'rect()', but you can override it
const obj = add([
sprite("froggy"),
// override to a smaller region
area(vec2(6), vec2(24)),
]);
// callback when collides with a certain tag
obj.collides("collectable", (c) => {
destroy(c);
score++;
});
// similar to collides(), but doesn't pass if 2 objects are just touching each other (checks for distance < 0 instead of distance <= 0)
obj.overlaps("collectable", (c) => {
destroy(c);
score++;
});
// checks if the obj is collided with another
if (obj.isCollided(obj2)) {
// ...
}
if (obj.isOverlapped(obj2)) {
// ...
}
// register an onClick callback
obj.clicks(() => {
// ...
});
// if the obj is clicked last frame
if (obj.isClicked()) {
// ...
}
// register an onHover callback
obj.hovers(() => {
// ...
});
// if the obj is currently hovered
if (obj.isHovered()) {
// ...
}
// check if a point is inside the obj area
obj.hasPt();
// resolve all collisions with objects with 'solid'
// for now this checks against all solid objs in the scene (this is costly now)
obj.resolve();
body([conf])
component for falling / jumping
const player = add([
pos(0, 0),
// now player will fall in this gravity world
body(),
]);
const player = add([
pos(0, 0),
body({
// force of .jump()
jumpForce: 640,
// maximum fall velocity
maxVel: 2400,
}),
]);
// body() gives obj jump() and grounded() methods
keyPress("up", () => {
if (player.grounded()) {
player.jump(JUMP_FORCE);
}
});
// and a "grounded" event
player.on("grounded", () => {
console.log("horray!");
});
solid()
mark the obj so other objects can't move past it if they have an area and resolve()
const obj = add([
sprite("wall"),
solid(),
]);
// need to call resolve() (provided by 'area') to make sure they cannot move past solid objs
player.action(() => {
player.resolve();
});
origin(orig)
the origin to draw the object (default center)
const obj = add([
sprite("froggy"),
// defaults to "topleft"
origin("topleft"),
// other options
origin("top"),
origin("topright"),
origin("left"),
origin("center"),
origin("right"),
origin("botleft"),
origin("bot"),
origin("botright"),
origin(vec2(0, 0.25)), // custom
]);
layer(name)
specify the layer to draw on
layers([
"bg",
"game",
"ui",
], "game");
add([
sprite("sky"),
layer("bg"),
]);
// we specified "game" to be default layer above, so a manual layer() comp is not needed
const player = add([
sprite("froggy"),
]);
const score = add([
text("0"),
layer("ui"),
]);
Events
kaboom uses tags to group objects and describe their behaviors, functions below all accepts the tag as first arguments, following a callback
action(tag, cb)
calls every frame for a certain tag
// every frame move objs with tag "bullet" up with speed of 100
action("bullet", (b) => {
b.move(vec2(0, 100));
});
action("flashy", (f) => {
f.color = rand(rgb(0, 0, 0), rgb(1, 1, 1));
});
collides(tag, cb)
calls when objects collides with others
collides("enemy", "bullet", (e, b) => {
destroy(b);
e.life--;
if (e.life <= 0) {
destroy(e);
}
});
overlaps(tag, cb)
calls when objects collides with others
// similar to collides(), but doesn't pass if 2 objects are just touching each other (checks for distance < 0 instead of distance <= 0)
overlaps("enemy", "bullet", (e, b) => {
destroy(b);
e.life--;
if (e.life <= 0) {
destroy(e);
}
});
on(event, tag, cb)
add lifecycle events to a tag group
// called when objs with tag "enemy" is added to scene
on("add", "enemy", (e) => {
console.log("run!!");
});
// per frame (action() is actually an alias to this)
on("update", "bullet", (b) => {
b.move(100, 0);
});
// per frame but drawing phase if you want custom drawing
on("draw", "bullet", (e) => {
drawSprite(...);
});
// when objs gets destroy() ed
on("destroy", "bullet", (e) => {
play("explosion");
});
Input
input events
keyDown(key, cb)
runs every frame when specified key is being pressed
keyPress(key, cb)
runs once when specified key is just pressed
keyRelease(key, cb)
runs once when specified key is just released
mouseDown(cb)
runs every frame when left mouse is being pressed
mouseClick(cb)
runs once when left mouse is just clicked
mouseRelease(cb)
runs once when left mouse is just released
Query
information about current window and input states
width()
canvas width
height()
canvas height
time()
current game time
dt()
delta time since last frame
mousePos()
current mouse position
Timer
timed events
wait(time, cb)
runs the callback after time seconds
wait(3, () => {
destroy(froggy);
});
// or
await wait(3);
destroy(froggy);
loop(time, cb)
runs the callback every time seconds
loop(0.5, () => {
console.log("just like setInterval");
});
Audio
yeah
play(id, [conf])
plays a sound
on("destroy", "enemy", (e) => {
play("explode", {
volume: 2.0,
speed: 0.8,
detune: 1200,
});
});
const music = play("mysong");
music.pause();
music.resume();
music.stop();
volume(volume)
set the master volume
Math
math types & utils
vec2(x, y)
creates a vector 2
vec2() // => { x: 0, y: 0 }
vec2(1) // => { x: 1, y: 1 }
vec2(10, 5) // => { x: 10, y: 5 }
const p = vec2(5, 10);
p.x // 5
p.y // 10
p.clone(); // => vec2(5, 10)
p.add(vec2(10, 10)); // => vec2(15, 20)
p.sub(vec2(5, 5)); // => vec2(0, 5)
p.scale(2); // => vec2(10, 20)
p.dist(vec2(15, 10)); // => 10
p.len(); // => 11.58
p.unit(); // => vec2(0.43, 0.86)
p.dot(vec2(2, 1)); // => vec2(10, 10)
p.angle(); // => 1.1
rgba(r, g, b, a)
creates a color from red, green, blue and alpha values (note: values are 0 - 1 not 0 - 255)
const c = rgba(0, 0, 1, 1); // blue
p.r // 0
p.g // 0
p.b // 1
p.a // 1
c.clone(); // => rgba(0, 0, 1, 1)
rgb(r, g, b)
shorthand for rgba() with a = 1
rand(a, b)
generate random value
rand() // 0.0 - 1.0
rand(1, 4) // 1.0 - 4.0
rand(vec2(0), vec2(100)) // => vec2(29, 73)
rand(rgb(0, 0, 0.5), rgb(1, 1, 1)) // => rgba(0.3, 0.6, 0.9, 1)
randSeed(seed)
set seed for rand generator
randSeed(Date.now());
makeRng(seed)
create a seedable random number generator
const rng = makeRng(Date.now());
rng.gen(); // works the same as rand()
choose(arr)
get random element from array
chance(p)
rand(0, 1) <= p
lerp(a, b, t)
linear interpolation
map(a, b, x, y, t)
map number to another range
Draw
Raw immediate drawing functions (you prob won't need these)
render(cb)
use a generic draw loop for custom drawing
scene("draw", () => {
render(() => {
drawSprite(...);
drawRect(...);
drawLine(...);
});
});
drawSprite(name, [conf])
draw a sprite
drawSprite("car", {
pos: vec2(100),
scale: 3,
rot: time(),
frame: 0,
});
drawRect(pos, w, h, [conf])
draw a rectangle
drawRect(vec2(100), 20, 50);
drawLine(p1, p2, [conf])
draw a rectangle
drawLine(vec2(0), mousePos(), {
width: 2,
color: rgba(0, 0, 1, 1),
z: 0.5,
});
drawText(text, [conf])
draw a rectangle
drawText("hi", {
size: 64,
pos: mousePos(),
origin: "topleft",
});
Level
helpers on building tiled maps
addLevel(map, ref)
takes a level drawing and turn them into game objects according to the ref map
const characters = {
"a": {
sprite: "ch1",
msg: "ohhi how are you",
},
};
const map = addLevel([
" a ",
" ===",
" ? * ",
" ==== ^^ ",
"===================",
], {
width: 11,
height: 11,
pos: vec2(0, 0),
// every "=" on the map above will be turned to a game object with following comps
"=": [
sprite("ground"),
solid(),
"block",
],
"*": [
sprite("coin"),
solid(),
"block",
],
// use a callback for dynamic evauations per block
"?": () => {
return [
sprite("prize"),
color(0, 1, rand(0, 1)),
"block",
];
},
"^": [
sprite("spike"),
solid(),
"spike",
"block",
],
// any catches anything that's not defined by the mappings above, good for more dynamic stuff like this
any(ch) {
if (characters[ch]) {
return [
sprite(char.sprite),
solid(),
"character",
{
msg: characters[ch],
},
];
}
},
});
// query size
map.width();
map.height();
// get screen pos through map index
map.getPos(x, y);
// destroy all
map.destroy();
// there's no spatial hashing yet, if too many blocks causing lag, consider hard disabling collision resolution from blocks far away by turning off 'solid'
action("block", (b) => {
b.solid = player.pos.dist(b.pos) <= 20;
});
Debug
debug utilities
fps()
current frames per second
objCount()
current number of objects in scene
pause()
pause the game
unpause()
unpause the game
kaboom.debug()
debug flags
// scale the time
kaboom.debug.timeScale = 0.5;
// show the bounding box of objects with area()
kaboom.debug.showArea = true;
// hover to inspect objects (needs showArea checked)
kaboom.debug.hoverInfo = true;