Top-Down Game
Smooth 2D gameplay with dynamic maps, responsive movement, and fast rendering.
Active
index.js
import { Game } from "kernelplay-js";
import { Main } from "./scene/Main.js";
class MyGame extends Game {
init() {
this.sceneManager.addScene(new Main("Main"));
this.sceneManager.startScene("Main");
}
}
const game = new MyGame({
width: 800,
height: 600,
fps: 60 ,
// debugPhysics: true,
});
await game.audio.loadAll([
'./assets/jump.mp3',
'./assets/run.mp3',
]);
game.start();
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>KernelPlay Game</title>
</head>
<body>
<script type="importmap">
{
"imports": {
"kernelplay-js": "https://cdn.jsdelivr.net/npm/kernelplay-js@latest/dist/kernelplay.es.js"
}
}
</script>
<script type="module" src="./main.js"></script>
</body>
</html>
Main.js
import { Scene } from "kernelplay-js";
import { Camera } from "../prefabes/Camera.js";
import { Player } from "../prefabes/Player.js";
import { Object } from "../prefabes/Object.js";
export class Main extends Scene {
init() {
this.addEntity(new Camera(400, 300, this.game.config.width, this.game.config.height));
this.addEntity(new Player(400, 200));
this.spawn(Object, 100, 400, "tree");
this.spawn(Object, 400, 300, "rock");
this.spawn(Object, 700, 200, "tree");
this.spawn(Object, 100, 200, "rock");
this.spawn(Object, 700, 500, "tree");
}
}
Player.js
import { Entity, TransformComponent, CameraComponent, AudioListener } from "kernelplay-js";
export class Camera extends Entity {
constructor(x, y, width, height) {
super("Camera");
this.tag = "camera";
this.addComponent("transform", new TransformComponent({
position: { x: x, y: y, z: 0 }
}));
this.addComponent("audioListener", new AudioListener());
this.addComponent("camera", new CameraComponent({
width: width,
height: height,
isPrimary: true
}));
}
}
Player.js
import { Entity, TransformComponent, SpriteComponent, ColliderComponent, Rigidbody2DComponent, AnimatorComponent, AudioSource } from "kernelplay-js";
import { PlayerScript } from "../Script/PlayerScript.js";
import { PlayerAnimatorController } from "../animation/stats/PlayerAnimatorController.js";
export class Player extends Entity {
constructor(x, y) {
super("Player");
this.tag = "player";
this.addComponent("transform", new TransformComponent({
position: { x: x, y: y },
scale: { x: 1.4, y: 1.4 }
}));
this.addComponent("rigidbody2d", new Rigidbody2DComponent({
useGravity: false,
mass: 1,
drag: 0.02
}));
this.addComponent("collider", new ColliderComponent({ width: 20, height: 45 }));
this.addComponent("renderer", new SpriteComponent({
image: "./assets/player_sheet.png",
sourceWidth: 64,
sourceHeight: 64,
width: 50,
height: 50,
anchor: { x: 0.5, y: 0.5 },
zIndex: 10,
}));
this.addComponent("animator", new AnimatorComponent({ controller: PlayerAnimatorController() }));
this.addComponent("audio", new AudioSource({
clips: {
run: './assets/run.mp3',
jump: './assets/jump.mp3',
},
volume: 1.0,
}));
this.addComponent("script", new PlayerScript({
speed: 200,
force: 500,
}));
}
}
Object.js
import { TransformComponent, SpriteComponent, ColliderComponent } from "kernelplay-js";
export function Object(entity, x, y, skin = "tree") {
entity.name = "Platform";
entity.tag = "platform";
entity.addComponent("transform", new TransformComponent({
position: { x: x, y: y },
scale: { x: 2.3, y: 2.3 }
}));
entity.addComponent("collider", new ColliderComponent({
isTrigger: false, // true = trigger events only, no physics push
offset: { x: 0, y: 0 }
}));
entity.addComponent("renderer", new SpriteComponent({
image: skin == "tree" ? "./assets/tree.png" : "./assets/rock.png",
// sourceWidth: 1000,
// sourceHeight: 250,
width: 50,
height: 50,
anchor: { x: 0.5, y: 0.5 },
zIndex: 10,
}));
}
player_clips.js
import { AnimationClip } from "kernelplay-js";
export const idleClip = new AnimationClip({
name: "idle",
frames: [0, 2],
frameRate: 2,
loop: true,
gridWidth: 4,
frameWidth: 64,
frameHeight: 64,
});
export const idleDown = new AnimationClip({ name: "idleDown", frames: [0], frameRate: 1, loop: true, gridWidth: 4, frameWidth: 64, frameHeight: 64 });
export const idleRight = new AnimationClip({ name: "idleRight", frames: [8], frameRate: 1, loop: true, gridWidth: 4, frameWidth: 64, frameHeight: 64 });
export const idleUp = new AnimationClip({ name: "idleUp", frames: [12], frameRate: 1, loop: true, gridWidth: 4, frameWidth: 64, frameHeight: 64 });
export const idleLeft = new AnimationClip({ name: "idleLeft", frames: [4], frameRate: 1, loop: true, gridWidth: 4, frameWidth: 64, frameHeight: 64 });
export const walkClip = new AnimationClip({
name: "walk",
frames: [8, 9, 10, 11],
frameRate: 6,
loop: true,
gridWidth: 4,
frameWidth: 64,
frameHeight: 64,
});
export const walkClipDown = new AnimationClip({
name: "walkDown",
frames: [1, 2, 3],
frameRate: 6,
loop: true,
gridWidth: 4,
frameWidth: 64,
frameHeight: 64,
});
export const walkClipRight = new AnimationClip({
name: "walkRight",
frames: [9, 10, 11],
frameRate: 6,
loop: true,
gridWidth: 4,
frameWidth: 64,
frameHeight: 64,
});
export const walkClipLeft = new AnimationClip({
name: "walkLeft",
frames: [5, 6, 7],
frameRate: 6,
loop: true,
gridWidth: 4,
frameWidth: 64,
frameHeight: 64,
});
export const walkClipUp = new AnimationClip({
name: "walkUp",
frames: [13, 14, 15],
frameRate: 6,
loop: true,
gridWidth: 4,
frameWidth: 64,
frameHeight: 64,
});
PlayerAnimatorController.js
import { AnimatorController } from "kernelplay-js";
import { idleDown, idleRight, idleUp, idleLeft, walkClipDown, walkClipUp, walkClipRight, walkClipLeft } from "../clips/player_clips.js";
export function PlayerAnimatorController() {
return new AnimatorController()
// --- PARAMETERS ---
.addParameter("speedX", "float", 0)
.addParameter("speedY", "float", 0)
.addParameter("isAttacking", "bool", false)
// --- STATES ---
.addState("idleDown", idleDown)
.addState("idleUp", idleUp)
.addState("idleRight", idleRight)
.addState("idleLeft", idleLeft)
.addState("walkDown", walkClipDown)
.addState("walkUp", walkClipUp)
.addState("walkRight", walkClipRight)
.addState("walkLeft", walkClipLeft)
// --- TRANSITIONS: WALK -> IDLE ---
.addTransition("walkRight", "idleRight", {
conditions: [{ param: "speedX", op: "<=", value: 0.1 }],
hasExitTime: false, duration: 0
})
.addTransition("walkLeft", "idleLeft", {
conditions: [{ param: "speedX", op: ">=", value: -0.1 }],
hasExitTime: false, duration: 0
})
.addTransition("walkDown", "idleDown", {
conditions: [{ param: "speedY", op: "<=", value: 0.1 }],
hasExitTime: false, duration: 0
})
.addTransition("walkUp", "idleUp", {
conditions: [{ param: "speedY", op: ">=", value: -0.1 }],
hasExitTime: false, duration: 0
})
// --- TRANSITIONS: IDLE -> WALK ---
.addTransition("idleDown", "walkRight", { conditions: [{ param: "speedX", op: ">", value: 0.1 }], hasExitTime: false, duration: 0, priority: 2 })
.addTransition("idleUp", "walkRight", { conditions: [{ param: "speedX", op: ">", value: 0.1 }], hasExitTime: false, duration: 0, priority: 2 })
.addTransition("idleLeft", "walkRight", { conditions: [{ param: "speedX", op: ">", value: 0.1 }], hasExitTime: false, duration: 0, priority: 2 })
.addTransition("idleRight", "walkRight", { conditions: [{ param: "speedX", op: ">", value: 0.1 }], hasExitTime: false, duration: 0, priority: 2 })
.addTransition("idleDown", "walkLeft", { conditions: [{ param: "speedX", op: "<", value: -0.1 }], hasExitTime: false, duration: 0, priority: 2 })
.addTransition("idleUp", "walkLeft", { conditions: [{ param: "speedX", op: "<", value: -0.1 }], hasExitTime: false, duration: 0, priority: 2 })
.addTransition("idleLeft", "walkLeft", { conditions: [{ param: "speedX", op: "<", value: -0.1 }], hasExitTime: false, duration: 0, priority: 2 })
.addTransition("idleRight", "walkLeft", { conditions: [{ param: "speedX", op: "<", value: -0.1 }], hasExitTime: false, duration: 0, priority: 2 })
.addTransition("idleDown", "walkDown", { conditions: [{ param: "speedY", op: ">", value: 0.1 }], hasExitTime: false, duration: 0, priority: 1 })
.addTransition("idleUp", "walkDown", { conditions: [{ param: "speedY", op: ">", value: 0.1 }], hasExitTime: false, duration: 0, priority: 1 })
.addTransition("idleLeft", "walkDown", { conditions: [{ param: "speedY", op: ">", value: 0.1 }], hasExitTime: false, duration: 0, priority: 1 })
.addTransition("idleRight", "walkDown", { conditions: [{ param: "speedY", op: ">", value: 0.1 }], hasExitTime: false, duration: 0, priority: 1 })
.addTransition("idleDown", "walkUp", { conditions: [{ param: "speedY", op: "<", value: -0.1 }], hasExitTime: false, duration: 0, priority: 1 })
.addTransition("idleUp", "walkUp", { conditions: [{ param: "speedY", op: "<", value: -0.1 }], hasExitTime: false, duration: 0, priority: 1 })
.addTransition("idleLeft", "walkUp", { conditions: [{ param: "speedY", op: "<", value: -0.1 }], hasExitTime: false, duration: 0, priority: 1 })
.addTransition("idleRight", "walkUp", { conditions: [{ param: "speedY", op: "<", value: -0.1 }], hasExitTime: false, duration: 0, priority: 1 })
}
PlayerScript.js
import { ScriptComponent, Keyboard, KeyCode } from "kernelplay-js";
export class PlayerScript extends ScriptComponent {
onStart() {
this.animator = this.entity.getComponent("animator");
this.sprite = this.entity.getComponent("renderer");
this.rb = this.entity.getComponent("rigidbody2d");
this.transform = this.entity.getComponent("transform");
this.audio = this.entity.getComponent("audio");
}
start() {
super.start();
this.camera.setTarget(this.entity);
}
update(dt) {
this.rb.velocity.x = 0;
this.rb.velocity.y = 0;
if (Keyboard.isPressed(KeyCode.D) || Keyboard.isPressed("ArrowRight")) {
this.rb.velocity.x = this.speed;
} else if (Keyboard.isPressed(KeyCode.A) || Keyboard.isPressed("ArrowLeft")) {
this.rb.velocity.x = -this.speed;
}
if (Keyboard.isPressed(KeyCode.W) || Keyboard.isPressed("ArrowUp")) {
this.rb.velocity.y = -this.speed;
} else if (Keyboard.isPressed(KeyCode.S) || Keyboard.isPressed("ArrowDown")) {
this.rb.velocity.y = this.speed;
}
const isMoving = this.rb.velocity.x !== 0 || this.rb.velocity.y !== 0;
if (isMoving) {
this.audio.playLoop('run', { volume: 0.5 });
} else {
this.audio.stopLoop('run');
}
this.animator.setParameter("speedX", this.rb.velocity.x);
this.animator.setParameter("speedY", this.rb.velocity.y);
}
}