Custom Components
KAPLAY uses a flexible component system that helps you compose game objects logic.
Let’s take a look at how the default component lifespan()
is implemented:
function lifespan(time) {
let timer = 0;
return {
id: "lifespan",
update() {
timer -= dt();
if (timer <= 0) {
destroy(this);
}
},
};
}
Components are just functions that returns an object. The return object will contain all the exposed states, methods, and event hooks of the component.
In this case, the lifespan()
component returns:
id
: unique identification of the comp, used in require,.unuse()
, etc.update()
: an event hook that’ll run every frame while the component is attached to a Game Object.
Also, all this
inside the component functions refer to the Game Object
it’s attached to.
Special Fields
Here’s a list of all special fields you can use in your component:
function myComp(dataNumber: number) {
// Use closed local variable for internal data
let data = dataNumber;
return {
id: "mycomp",
// If this comp requires other comps to work, we use comp ids
require: ["area", "pos"],
// Runs when the obj is added to scene
add() {
debug.log("Hi! This should only be fire once.", data);
},
// Runs every frame
update() {
// We're using a method from "pos" comp here, so we declare require "pos" above
this.move(200, 0);
},
// Runs every frame, after update
draw() {
drawLine(this.pos, mousePos());
},
// Runs when obj is destroyed
destroy() {
debug.log("Oh bye :<");
},
// What to display in debug inspect mode
inspect() {
return "some state that deserves to be shown in inspect mode";
},
};
}
// Usage
const obj = add([
sprite("bean"),
pos(0, 0),
area(),
myComp(123), // <- here
]);
All KAPLAY built-in components are implemented this way. You can check the source code here. Also Check out the component demo.
TypeScript
If you’re using TypeScript, we recommend creating an specific type for your component:
// You can import types from kaplay package
import type { Comp } from "kaplay";
interface MyCustomComp extends Comp {
// you just need to declare new stuff here
myCustomProp: number;
myCustomMethod: (arg: string) => void;
}
function myCustomComp(): MyCustomComp {
return {
id: "myCustomComp",
myCustomProp: 123,
myCustomMethod(arg) {
console.log(arg);
},
};
}
When you want to use this
inside any component method, but TypeScript doesn’t
know what type it is, you can type this
to GameObj<MyCustomComp>
:
function myCustomComp(): MyCustomComp {
return {
id: "myCustomComp",
myCustomMethod(this: GameObj<MyCustomComp>) {
this.myCustomProp; // typed from MyCustomComp
this.tags; // typed from GameObjRaw
},
};
}
Even better if you got a component that requires other components, you can use
GameObj<YourComp | OtherComp>
to type this
.
function specie(specie: string): SpecieComp {
return {
id: "specie",
require: ["color"],
specie: specie,
add() {
this.tag(this.specie); // add specie tag
}
evolve(this: GameObj<SpecieComp>, specie: string) {
this.untag(this.specie); // remove original specie tag
this.specie = specie;
this.tag(specie);
},
evil(this: GameObj<SpecieComp | ColorComp>) {
// this.color is typed
this.color = Color.RED;
},
};
}