Files
2026-09-12 04:58:59 +03:00

136 lines
3.6 KiB
TypeScript

import { emptyProject, entity, activeScene, type Asset } from "./schema.ts";
import { sliceImage } from "./two-d.ts";
/** Small original pixel-art sheet for the runnable 2D example; no external assets. */
const sheet =
"iVBORw0KGgoAAAANSUhEUgAAAGAAAAAQCAYAAADpunr5AAAAw0lEQVR4nGNgGOGgeHPBf0rw0nQHijCGg74d7f+PDxPy0FDTPxoBwyQCLs0KJwmTFAGSaqZgTG4ADGb9MU6O/ynBoxEwyCJgWrQjXkxWBAzmIoRS/aMRMBoBmI733HgVKyYUCENRPywgS4I9UPBoBIxGwNAIQGpFwGgRNBoBhD0Awrg8P1T1D6oIAAF8jicUAENRP7EB7WBoBcf4IoDijpiqVdx/GMYnhg8gq6dELz3sH42AAbafWkUQ1QbjRiOAvhEAAD+44yBoQmIqAAAAAElFTkSuQmCC";
export function project2D(example = false) {
const p = emptyProject();
p.name = example ? "2D Platformer" : "2D Project";
const scene = activeScene(p);
scene.name = "2D Scene";
scene.mode = "2d";
p.settings.background = "#233546";
p.settings.physics2d = { gravity: [0, -9.81] };
if (!example) return p;
const a: Asset = {
id: "sprites_2d",
kind: "image",
name: "platformer.png",
uri: "data:image/png;base64," + sheet,
image: {
width: 96,
height: 16,
pixelsPerUnit: 16,
filter: "nearest",
frames: sliceImage(96, 16, 16, 16),
},
};
p.assets = [a];
const sprite = (frame: number, size = [1, 1]) => ({
assetId: a.id,
frame,
size,
});
const player = entity(
"Player",
{
sprite: sprite(0, [1, 1.5]),
spriteAnimator: {
autoplay: "idle",
autoStates: true,
clips: [
{ name: "idle", frames: [0], fps: 4, loop: true },
{ name: "run", frames: [1, 2], fps: 10, loop: true },
{ name: "jump", frames: [3], fps: 1, loop: true },
],
},
collider2d: { shape: "capsule", radius: 0.3, height: 1.4 },
rigidbody2d: { type: "kinematic", lockRotation: true },
character2d: {
mode: "platformer",
controls: true,
speed: 5,
jumpSpeed: 9,
gravity: 20,
autostep: 0.15,
},
},
[3, 2, 0],
"player_2d",
);
const ground = entity(
"Tilemap",
{
tilemap: {
assetId: a.id,
width: 26,
height: 10,
tileSize: [1, 1],
cells: [
...Array.from({ length: 26 }, (_, x) => ({ x, y: 0, frame: 4 })),
...Array.from({ length: 4 }, (_, x) => ({
x: x + 7,
y: 2,
frame: 4,
})),
...Array.from({ length: 4 }, (_, x) => ({
x: x + 18,
y: 3,
frame: 4,
})),
],
collisions: true,
layer: -1,
},
},
[0, -1, 0],
"tilemap_2d",
);
const platform = entity(
"One-way platform",
{
sprite: { ...sprite(4, [3, 0.3]), color: "#90bdc8" },
collider2d: { shape: "box", size: [3, 0.3], oneWay: true },
rigidbody2d: { type: "fixed" },
},
[14, 3, 0],
"platform_2d",
);
const crate = entity(
"Dynamic crate",
{
sprite: sprite(5),
collider2d: { shape: "box", size: [1, 1] },
rigidbody2d: { type: "dynamic", mass: 1, lockRotation: false },
},
[5, 4, 0],
"crate_2d",
);
const camera = entity(
"Camera 2D",
{
camera: {
mode: "2d",
orthoWidth: 22,
targetId: player.id,
offset: [5, 3, 0],
bounds: [11, 4, 15, 4],
},
},
[11, 4, 20],
"camera_2d",
);
const background = entity(
"3D object in the same scene",
{
mesh: { type: "box", size: [2, 2, 2] },
material: { color: "#45647c", roughness: 1 },
},
[20, 5, -4],
"background_3d",
);
background.transform.rotation = [0.3, 0.4, 0.2];
scene.entities = [ground, player, platform, crate, camera, background];
return p;
}