Migrating from v2000 to v3000

  • obj._id is renamed to obj.id
const obj = add([pos(300, 200), sprite("bean"), area()]);

// before
console.log(obj._id);

// v3000
console.log(obj.id);
  • origin() is renamed to anchor()
// before
add([sprite("bean"), origin("center")]);

// v3000
add([sprite("bean"), anchor("center")]);
  • obj.onHover() in area() comp is renamed to obj.onHoverUpdate(), obj.onHover() now only runs once when obj is hovered
const obj = add([pos(300, 200), sprite("bean"), area()]);

// before
obj.onHover(
    () => {
        console.log("this will print every frame when obj is hovered");
    },
    () => {
        console.log("this will print every frame when obj is not hovered");
    },
);

// v3000
obj.onHover(() => {
    console.log("this will run once when obj is hovered");
});

obj.onHoverUpdate(() => {
    console.log("this will run every frame when obj is hovered");
});

obj.onHoverEnd(() => {
    console.log("this will run once when obj stopped being hovered");
});
  • obj.pushOut() is renamed to obj.resolveCollision()
const player = add([sprite("bean"), pos(300, 200), area()]);

// before
player.pushOut(rock);

// v3000
player.resolveCollision(rocker);
  • solid() comp becomes an option in body({ isStatic: true })
// before
add([sprite("bean"), area(), body(), solid()]);

// v3000
add([sprite("bean"), area(), body({ isStatic: true })]);
  • gravity now needs to be manually enabled, gravity() is renamed to setGravity() and getGravity()
// before, gravity will be enabled by body() component
add([pos(100, 100), sprite("bean"), area(), body()]);

// v3000, use gravity() to manually enable gravity
setGravity(1600);

add([pos(100, 100), sprite("bean"), area(), body()]);
  • body.weight is renamed to body.gravityScale
// before
add([body({ weight: 2 })]);

// before
add([body({ gravityScale: 2 })]);
  • body.doubleJump() is removed in favor of new doubleJump() component
const obj = add([pos(100, 100), sprite("bean"), area(), body()]);

obj.doubleJump();

// after
const obj = add([pos(100, 100), sprite("bean"), area(), body(), doubleJump()]);

obj.doubleJump();
  • body.onFall() is renamed to body.onFallOff(), body.onFall() now runs when body is in the air and starts to fall
gravity(1600);

const obj = add([pos(100, 100), sprite("bean"), area(), body()]);

// before
obj.onFall(() => {
    console.log("this will print when object falls off a platform");
});

// v3000
obj.onFallOff(() => {
    console.log("this will print when object falls off a platform");
});

obj.onFall(() => {
    console.log("this will print when object is in the air and starts falling");
});
  • removed outview() in favor of offscreen(), which is less accurate but much faster
// before
add([sprite("flower"), outview({ hide: true })]);

// v3000
add([
    sprite("flower"),
    // will hide itself when its position is 64 pixels offscreen
    offscreen({ hide: true, distance: 64 }),
]);
  • removed cleanup() in favor of offscreen({ destroy: true })
// before
add([pos(player.pos), sprite("bullet"), cleanup()]);

// v3000
add([pos(player.pos), sprite("bullet"), offscreen({ destroy: true })]);
  • sprite.flipX and sprite.flipY becomes properties instead of functions
const bean = add([sprite("bean")]);

// before
bean.flipX(true);

// v3000
bean.flipX = true;
  • sprite.onAnimStart() and sprite.onAnimEnd() now triggers on any animation
const bean = add([sprite("bean")]);

// before
bean.onAnimStart("walk", () => {
    // do something
});

// before
bean.onAnimStart((anim) => {
    if (anim === "walk") {
        // do something
    }
});
  • obj.scale now is always a Vec2
scale(2); // scale is vec2(2, 2)
obj.scale; // vec2(2, 2)
  • loadFont() now only loads .ttf, .otf, .woff etc fonts that browser support, use loadBitmapFont() to load bitmap fonts
// before
loadFont("unscii", "/examples/fonts/unscii_8x8.png", 8, 8);

// v3000
loadBitmapFont("unscii", "/examples/fonts/unscii_8x8.png", 8, 8);
loadFont("apl386", "/examples/fonts/apl386.ttf");
  • removed builtin fonts apl386, apl386o, sink and sinko, using browser built-in monospace font as default font now
// v3000, manually load these fonts if you need them
loadFont("apl386", "/examples/fonts/apl386.ttf");
loadBitmapFont("sink", "/examples/fonts/sink_6x8.png");

// use outline option for "apl386o"
loadFont("apl386", "/examples/fonts/apl386.ttf", {
    outline: 3,
});
  • changed vertex format from vec3 to vec2 (only applied in shaders)
// before
loadShader(
    "test",
    null,
    `
vec4 frag(vec3 pos, vec2 uv, vec4 color, sampler2D tex) {
	return def_frag();
}
`,
);

// v3000
loadShader(
    "test",
    null,
    `
vec4 frag(vec2 pos, vec2 uv, vec4 color, sampler2D tex) {
	return def_frag();
}
`,
);
  • anchor (previously origin) no longer controls text alignment (only controls the anchor of the whole text area), use text({ align: "left" }) option for text alignment
// before
add([pos(center()), origin("center"), text("oh hi")]);

// v3000
add([pos(center()), anchor("center"), text("oh hi", { align: "center" })]);
  • changed text styling syntax to bbcode
const textOpts = {
    styles: {
        green: {
            color: rgb(128, 128, 255),
        },
        wavy: (idx, ch) => ({
            color: hsl2rgb((time() * 0.2 + idx * 0.1) % 1, 0.7, 0.8),
            pos: vec2(0, wave(-4, 4, time() * 6 + idx * 0.5)),
        }),
    },
};

// before
add([text("[oh hi].green here's some [styled].wavy text", textOpts)]);

// v3000
add([
    text("[green]oh hi[/green] here's some [wavy]styled[/wavy] text", textOpts),
]);
  • changed all event handlers to return an EventController object, instead of a function to cancel
// before
const cancel = onUpdate(() => {
    /* ... */
});
cancel();

// v3000
const ev = onUpdate(() => {
    /* ... */
});
ev.paused = true;
ev.cancel();
  • changed the interface for addLevel()
// before
addLevel(["@  ^ $$", "======="], {
    width: 32,
    height: 32,
    "=": () => [sprite("grass"), area(), body({ isStatic: true })],
    $: () => [sprite("coin"), area(), "coin"],
    any: (symbol) => {
        if (symbol === "@") {
            return [
                /* ... */
            ];
        }
    },
});

// v3000
addLevel(["@  ^ $$", "======="], {
    tileWidth: 32,
    tileHeight: 32,
    tiles: {
        "=": () => [sprite("grass"), area(), body({ isStatic: true })],
        $: () => [sprite("coin"), area(), "coin"],
    },
    wildcardTile: (symbol) => {
        if (symbol === "@") {
            return [
                /* ... */
            ];
        }
    },
});
kaplay logo

Making Your First Game

Expanding KAPLAY