tga, 01/15/2023
We're releasing Kaboom v3000 beta! This major version update includes a lot of new features and improvements, and also breaking changes (see migration guide below).
To try the beta version, install kaboom@next
$ npm install kaboom@next
<script src="https://unpkg.com/kaboom@next/dist/kaboom.js"></script>
or use this Replit template
Note that this is a beta release, there are bugs and possiblly breaking changes before official release. Please report bugs to github issues, and general questions / discussions to github discussions.
Objects can now have children with obj.add()
! Children will inherit the transform (position, scale and rotation) of the parent.
const bean = add([
sprite("bean"),
pos(160, 120),
])
const sword = bean.add([
sprite("sword"),
// transforms will be relative to parent bean object
pos(20, 20),
rotate(20),
])
const hat = bean.add([
sprite("hat"),
// transforms will be relative to parent bean object
pos(0, -10),
])
// children will be moved alongside the parent
bean.moveBy(100, 200)
// children will be destroyed alongside the parent
bean.destroy()
Check out the rotating pineapples in this live example (in case you didn't notice, it also demonstrates rotating areas!)
loadFont()
now loads .ttf
, .otf
, .woff
, .woff2
fonts (any font that browser font-face supports). The function to load bitmap font is renamed to loadBitmapFont()
.
// ComicSans all the way!
kaboom({
font: "ComicSans",
})
loadFont("ComicSans", "assets/fonts/ComicSans.ttf")
// Load a font with options
loadFont("apl386", "assets/fonts/apl386.ttf", { outline: 4, filter: "linear" })
// To load bitmap font is renamed to loadBitmapFont()
loadBitmapFont("4x4", "/examples/fonts/4x4.png", 4, 4)
const t = tween(
// start value
obj.pos,
// destination value
mousePos(),
// duration (in seconds)
0.5,
// how value should be updated
(p) => obj.pos = p,
// interpolation function (defaults to easings.linear)
easings.easeOutElastic,
)
// can cancel the tween any time
t.cancel()
t.onEnd(() => {
// register event that runs when tween finishes
})
// tween() returns a then-able, which can be used with await
await tween(...)
await wait(1)
await tween(...)
await tween(...)
await wait(1)
Go to tween example to play around with it! Press left / right arrow key to change the interpolation function, mouse click anywhere to set destination.
Previously Kaboom only supports unrotated AABB boxes for collision detection, now Kaboom supports collision detection and resolution between any arbitrary convex polygons (e.g. rotated rectangles).
// rotate() will also affect the area() collider box!
add([
sprite("bean"),
pos(200, 100),
rotate(30),
area(),
])
// use a custom polygon area shape (a triangle in this case)
add([
sprite("bean"),
pos(200, 100),
area({ shape: new Polygon([vec2(0), vec2(100), vec2(-100, 100)]) }),
])
Check out the new collision example. Press Q / E to rotate bean, and arrow keys to move around and see how bean now reacts with others!
Graphics performance improved ~3x - 50x, collision detection performance improved ~2x - 4x. Read more about this in this blog post.
Now you can draw your custom loading screen with onLoading()
(note that this function runs every frame, you can only use the drawXXX()
functions here)
// the callback is run every frame when assets are initially loading
onLoading(() => {
// Black background
drawRect({
width: width(),
height: height(),
color: rgb(0, 0, 0),
})
// A pie representing current load progress
drawCircle({
pos: center(),
radius: 32,
end: map(progress, 0, 1, 0, 360),
})
drawText({
text: "loading" + ".".repeat(wave(1, 4, time() * 12)),
font: "monospace",
size: 24,
anchor: "center",
pos: center().add(0, 70),
})
})
Another option is to turn off loading screen completely, default or custom:
kaboom({
// by doing this, unloaded assets will simply be not drawn or played until they're loaded
loadingScreen: false,
})
Try it out in the loader example! It also demonstrated some other loading techniques.
Kaboom now provides an easy way to add full screen visual effect with shader:
loadShader("pixelate", null, `
uniform float u_size;
uniform vec2 u_resolution;
vec4 frag(vec2 pos, vec2 uv, vec4 color, sampler2D tex) {
if (u_size <= 0.0) return def_frag();
vec2 nsize = vec2(u_size / u_resolution.x, u_size / u_resolution.y);
float x = floor(uv.x / nsize.x + 0.5);
float y = floor(uv.y / nsize.y + 0.5);
vec4 c = texture2D(tex, vec2(x, y) * nsize);
return c * color;
}
`)
// use the post effect "pixelate", and send the shader uniform
usePostEffect("pixelate", {
"u_resolution": vec2(width(), height()),
"u_size": 4,
})
Try it out in the postEffect example. Use UP/DOWN arrow to cycle through the example effects!
Added gamepad support.
onGamepadButtonPress("south", () => {
player.jump()
})
onGamepadStick("left", (v) => {
player.move(v.scale(SPEED))
})
If you gamepad doesn't work, add your gamepad mappings to Kaboom's gamepad mapping json file.
Connect a gamepad and go nuts with this classic minigame!
The level API has been rewored and added path finding support. Use the new tile()
component with option tile({ isObstacle: true })
to define obstacles in a level, and agent()
component to
const map = addLevel([
"##########",
"# #",
"# @ #",
"#..... #",
"# #",
"# ....#",
"# $ #",
"# #",
"##########",
], {
tileWidth: 64,
tileHeight: 64,
tiles: {
"$": () => [
sprite("coin"),
area(),
],
"#": () => [
sprite("wall"),
tile({ isObstacle: true }),
],
"@": () => [
sprite("player"),
tile(),
agent({ speed: 640, allowDiagonals: true }),
"player",
],
},
})
const player = get("player")[0]
onClick(() => {
bean.setTarget(mousePos())
})
Run this maze example to see for yourself. Click anywhere in the maze to travel to it!
Path finding is implemented by @mflerackers
See complete list of changes in CHANGELOG
You can check this doc