Platformer Templates
Ready-to-use templates for building fast, responsive 2D platformer games.
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 { Platform } from "../prefabes/Platform.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(300, 200));
this.spawn(Platform, 100, 400);
this.spawn(Platform, 400, 300);
this.spawn(Platform, 700, 200);
this.spawn(Platform, 700, 500);
}
}
Camera.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,
bounds: {
minX: -1000,
maxX: 1000,
minY: 0,
maxY: 600
},
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: 300, y: 200 },
scale: { x: 1.4, y: 1.4 }
}));
this.addComponent("rigidbody2d", new Rigidbody2DComponent({
useGravity: true,
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,
}));
}
}
Platform.js
import { TransformComponent, SpriteComponent, ColliderComponent } from "kernelplay-js";
export function Platform(entity, x, y) {
entity.name = "Platform";
entity.tag = "platform";
entity.addComponent("transform", new TransformComponent({
position: { x: x, y: y },
scale: { x: 5, y: 1 }
}));
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: "./assets/brick.jpg",
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 walkClip = new AnimationClip({
name: "walk",
frames: [8, 9, 10, 11],
frameRate: 6,
loop: true,
gridWidth: 4,
frameWidth: 64,
frameHeight: 64,
});
export const jumpClip = new AnimationClip({
name: "jump",
frames: [9],
frameRate: 1,
loop: true,
gridWidth: 4,
frameWidth: 64,
frameHeight: 64,
});
PlayerAnimatorController.js
import { AnimatorController } from "kernelplay-js";
import { idleClip, walkClip, jumpClip } from "../clips/player_clips.js"
export function PlayerAnimatorController() {
return new AnimatorController()
.addParameter("speed", "float", 0)
.addParameter("isGrounded", "bool", false)
.addParameter("jump", "trigger")
.addState("idle", idleClip)
.addState("walk", walkClip)
.addState("jump", jumpClip)
// idle → walk: must be moving AND grounded
.addTransition("idle", "walk", {
conditions: [
{ param: "speed", op: ">", value: 0.1 },
{ param: "isGrounded", op: "true" }, // ← grounded check
],
hasExitTime: false,
duration: 0,
})
// walk → idle: stopped OR not grounded
.addTransition("walk", "idle", {
conditions: [
{ param: "speed", op: "<=", value: 0.1 },
],
hasExitTime: false,
duration: 0,
})
// walk → jump if leaves ground (e.g. walks off a ledge)
.addTransition("walk", "jump", {
conditions: [
{ param: "isGrounded", op: "false" }, // ← fell off ledge
],
hasExitTime: false,
duration: 0,
})
// AnyState → jump on trigger
.addAnyStateTransition("jump", {
conditions: [{ param: "jump", op: "trigger" }],
hasExitTime: false,
priority: 10,
})
// jump → idle only when grounded again
.addTransition("jump", "idle", {
conditions: [
{ param: "isGrounded", op: "true" }, // ← wait for landing
],
hasExitTime: false,
duration: 0,
});
}
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;
if (Keyboard.isPressed(KeyCode.A) || Keyboard.isPressed(KeyCode.ArrowLeft)) {
this.rb.velocity.x = -this.speed;
this.sprite.flipX = true;
}
if (Keyboard.isPressed(KeyCode.D) || Keyboard.isPressed(KeyCode.ArrowRight)) {
this.rb.velocity.x = this.speed;
this.sprite.flipX = false;
}
const isMoving = this.rb.velocity.x !== 0;
this.animator.setParameter("speed", isMoving ? 1 : 0);
this.animator.setParameter("isGrounded", this.rb.isGrounded);
if (this.rb.isGrounded && Keyboard.wasPressed(KeyCode.Space)) {
this.rb.addForce(0, -this.force, "impulse");
this.audio.stopLoop('run'); // cut run sound immediately
this.audio.playOneShot('jump', { volume: 0.1 });
this.animator.setTrigger("jump");
}
if (isMoving && this.rb.isGrounded) {
this.audio.playLoop('run', { volume: 0.5 });
} else {
this.audio.stopLoop('run');
}
}
}