All notable changes to this project will be documented in this file.
The format is (mostly) based on Keep a Changelog, and this project adheres to Semantic Versioning.
- Breaking changes are marked with: (!).
- Jump to v3001 changelog.
-
Added
KAPLAYOpt.types
,kaplayTypes()
andOpt
to config specific TypeScript Advanced Features (TAF) - @lajbelkaplay({ types: kaplayTypes< // Opt<> is optional but recommended to get autocomplete Opt<{ scenes: {}; // define scenes and arguments strictScenes: true; // you can only use defined scenes }> >(), });
-
Added
TypesOpt.scenes
to type scenes and parameters - @lajbelconst k = kaplay({ types: kaplayTypes< Opt<{ scenes: { game: [gamemode: "normal" | "hard"]; gameOver: [score: number, highScore: number]; }; }> >(), }); // If you trigger autocomplete it shows "game" or "gameOver" k.scene("game", (gamemode) => { // gamemode is now type "normal" | "hard" // @ts-expect-error Argument of type 'string' is not assignable // to parameter of type 'number'. k.go("gameOver", "10", 10); // });
The methods that support this are:
scene
go
onSceneLeave
getSceneName
-
Added
TypesOpt.strictScenes
to make usable scenes just the ones defined - @lajbelconst k = kaplay({ types: kaplayTypes< Opt<{ scenes: { game: [gamemode: "normal" | "hard"]; gameOver: [score: number, highScore: number]; }; strictScenes: true; }> >(), }); // @ts-expect-error Argument of type '"hi"' is not assignable to // parameter of type '"game" | "gameOver"'. k.scene("hi", () => {});
-
Added named animations - @mflerackers
By giving a name to an animation, you can define more than one animation
const anim = obj.animation.get("idle"); anim.animate("pos", [0, 5, 0], { relative: true });
-
Added
screenshotToBlob()
to get a screenshot as aBlob
- @dragoncoder047 -
Added
getButtons()
to get the input binding buttons definition - @lajbel -
Added
RuleSystem
,DecisionTree
andStateMachine
for enemy AI - @mflerackers -
Added constraint components for distance, translation, rotation, scale and transform constraints - @mflerackers
-
Added inverse kinematics constraint components using FABRIK and CCD, the latter one can use bone constraints to constrain the angle - @mflerackers
-
Added skew to Mat23, transformation stack, RenderProps, GameObjRaw as well as a component - @mflerackers
-
Added texture uniforms, in order to access more than one texture at a time in shaders - @mflerackers
- Now error screen should be instantly shown - @lajbel
- Now, you can use
color(c)
with a hexadecimal literal number (ex: 0x00ff00) - @lajbel// blue frog add([ sprite("bean"), color(0x0000ff), ]);
- (!)
KAPLAYCtx
doesn't use generics anymore. Now,KAPLAYCtxT
uses them - @lajbel - Now,
kaplay
will returnKAPLAYCtx
orKAPLAYCtxT
depending if it's using Advanced TypeScript Features or not - @lajbel loadShader()
now also checks for link errors as well as compile errors and reports them rather than just silently trying to use a borked shader - @dragoncoder047- The debug
record()
function now records with sound enabled like it should - @dragoncoder047 - Now
KAPLAYOpt.spriteAtlasPadding
is set to2
by default - @lajbel - Transformation and drawing is split now, so the transform can be modified before drawing - @mflerackers
-
Added
GameObjRaw.serialize()
for serializing the game object and its components. - @mflerackers, @lajbelconst bean = add([sprite("prefab")]); const beanSerialized = bean.serialize();
-
Added
createPrefab()
for serializing an object and register it (or not) as a prefab from a Game Object. - @mflerackers, @lajbelconst beanObj = add([sprite("bean")]); // Serialize game object and register it as a prefab asset createPrefab("bean", beanObj); addPrefab("bean"); // Just get serialized data const serializedBean = createPrefab(beanObj); addPrefab(beanObj);
-
Added
addPrefab()
for creating an object previously serialized - @mflerackers, @lajbelloadPrefab("bean", "/bean.kaprefab"); addPrefab("bean");
-
Added new scene methods
pushScene()
andpopScene()
, for stack behaviour in scenes - @itzKiwiSky -
Added
throwError()
for throwing custom errors to the blue screen, even errors KAPLAY can't handle. - @lajbel -
Added
insertionSort()
- @dragoncoder047 -
Added a mapping for PS5 (DualSense) gamepads, so now you can bind actions to the touchpad press (only works in Chrome for some reason) - @dragoncoder047
- Now
GameObjRaw.exists()
work for nested objects - Now moving mouse changes the value of
getLastInputDevice()
- @amyspark-ng - (!) Renamed
KAPLAYOpt.tagsAsComponents
toKAPLAYOpt.tagComponentIds
- @lajbel
- Fixed shader error messages - @dragoncoder047
- Fixed compatibility issues when calculating font height with missing TextMetrics props - @imaginarny
- Added
loadSpriteFromFont()
for loading a bitmap font from a loaded sprite. - @dragoncoder047
- Improved various doc entries. - Many contributors
- Fixed
AreaComp#onClick()
attaching events to app, instead of object, so event wasn't being paused withobj.paused
- @lajbel - Fixed all touch events having a bad transformation - @lajbel
- Fixed sprite scaling not working properly with
KAPLAYOpt.letterbox
- @mflerackers - Fixed "add" event running twice in
addLevel()
tiles - @lajbel - Fixed blend component having a wrong ID - @lajbel
- (!)
loadPedit()
was removed - @lajbel
This version changelog covers versions 4000.0.0-alpha.0 through 4000.0.0-alpha.19, as we didn't have a concise changelog strategy before.
-
Added
fakeMouse()
to create a fake mouse cursor - @lajbelconst myCursor = add([fakeMouse(), sprite("kat"), pos(100, 100)]); myCursor.press(); // trigger onClick events if the mouse is over myCursor.release(); myCursor.moveBy(vec2(100, 200)); // move as your wish
-
Added
system()
to replace internal events or create new - @mflerackerssystem("collision", () => { // system code }, [SystemPhase.AfterFixedUpdate, SystemPhase.AfterUpdate]),
-
Added
ellipse()
component - @mflerackers -
Added circle and (rotated) ellipse collision shapes - @mflerackers
-
Added
clipLineToRect()
- @mflerackers -
Added
obj.setParent()
to change the parent of a game object - @mflerackers -
Added restitution and friction to physics - @mflerackers
-
All game objects have methods
onTag()
andonUntag()
for watching tag changes - @mflerackers -
Added
SystemPhase
enum to identify different lifecycle events in the game loop that systems can hook into - @mflerackers -
Added Blend mode is selectable to change how sprites are composited on top of each other - @mflerackers
-
Added Picture API to cache drawing of selected objects - @mflerackers
-
Added
drawCanvas()
- @mflerackers -
Added
video()
component to embed a video file into the game - @mflerackers -
Added
level()
component and parent argument toaddLevel()
- @KeSuave -
Allow the
text()
component to change the font and apply shaders per-character - @dragoncoder047 -
Allow characters in text to be scaled and have the text flow around it with
stretchInPlace: false
- @dragoncoder047 -
Expose the formatted text parsing functions to allow manipulation of formatted text - @dragoncoder047
-
Now you can use the frames of a sprite in an atlas also as a font - @dragoncoder047
-
More errors raised during object creation are caught and cause the blue crash screen - @lajbel
-
The blue crash screen will no longer fail to draw if the error message contains brackets - @dragoncoder047
-
Now you can use the global option
inspectOnlyActive: false
to prevent paused objects from showing in the debug inspect view, this is useful if you are swapping out objects for different views - @dragoncoder047 -
The
OffScreenComp
now has an optionoffscreenDistance
to change the distance at which an object is considered off-screen - @dragoncoder047 -
Now you can cherry-pick specific frames of a sprite sheet by using the
frames
list, instead of being limited to consecutive framesstart
andend
- @dragoncoder047 -
wave()
can now go back and forth between any value that is able to be used withlerp()
- @dragoncoder047, @mflerackers -
The
TextInputComp
has more events:focus
,blur
,input
, andchange
, to better interact with the text input state - @dragoncoder047 -
Areas no longer struggle with parents whose transform inst't up-to-date - @mflerackers
-
Exported step and smoothstep - @mflerackers
-
Small circles and arcs use now less points than larger ones - @mflerackers
-
Added pushMatrix, storeMatrix and loadIdentity to the stack functions - @mflerackers
-
Typed
StateComp
- @amyspark-ng -
Added bias to line drawing, which controls the offset from the center of the line - @mflerackers
-
Added
SpriteAnimPlayOpt.preventRestart
to allowSpriteComp.play()
to be called from anonUpdate()
and not reset the animation to frame 0 - @dragoncoder047
- (!) - Now
z()
is global instead of relative - @mflerackers - (!) Layers now work globally, no longer only between siblings - @mflerackers
- (!): Changed default behavior to
kaplay({ tagsAsComponents: false })
- The physics engine creates less garbage - @mflerackers
- Tag-based events are slightly faster - @dragoncoder047
- Moved camera to the shader - @mflerackers
- Replaced the Separating Axis Theorem (SAT) collision detection module with the
Gilbert–Johnson–Keerthi
(
GJK
) algorithm, which is faster - @mflerackers - Now if you pass a nullish value to
.use()
it throws an error - Improved TypeScript in game objects - @amyspark-ng, @lajbel, @KeSuave
- Added/updated JSDoc comments to some members - @ErikGXDev, @dragoncoder047
- The
textInput
component'sisFocused
property is now a one-hot lockout, setting it to true (focused) will clear focus from all the other text inputs - @dragoncoder047 - Changed the API of
HealthComp
- @amyspark-ng - CapsLock now affects
TextInputComp
- @amyspark-ng
GameObjRaw.exists()
now correctly returns false if the parent was destroyed but obj wasn't - @dragoncoder047Vec2.dot()
now actually does the Correct Calculation™ - @andrenanninga- Fixed
debug.timeScale
not affectingdt()
scale - @lajbel - Fixed
wait()
'sTimerComp.onEnd()
being waiting for twice the duration - @dragoncoder047 - Fixed non-focused
TextInputComp
backspace - @KeSuave - Fixed 9slice sprites behaving wrong when using
Anchor
- @mflerackers - Fixed rendering glitches with outlines on circles - @mflerackers
- Fixed
setCursorLocked(true)
throwing error if the browser is using the old non-Promise-based API return value - @imaginarny - Fixed
PatrolComp
not going to last waypoint - @nojaf - Fixed various TypeScript types - @amyspark-ng, @lajbel, @KeSuave
- (!)
make()
was sent to doom - @lajbel
- Fixed compatibility issues when calculating font height with missing TextMetrics props - @imaginarny
- Fixed
AreaComp#onClick()
attaching events to app, instead of object, so event wasn't being paused withobj.paused
- @lajbel - Fixed all touch events having a bad transform - @lajbel
- Removed beant - @lajbel
- Fixed TexPacker loading big images - @lajbel, @mflerackers
- Various fixes and improvements - All Contributors
- New way to import globals in JS
/dist/types.d.ts
- Removed beant - @lajbel
- Various fixes and improvements - All Contributors
- Removed beant - @lajbel
- Various fixes and improvements - All contributors
- Various fixes and improvements - All contributors
- Various fixes and improvements - All contributors
- Various fixes and improvements - All contributors
- Blockers - @lajbel
-
Added CSS Colors! 🎨 - @lajbel (based on @dragoncoder047 idea)
color("slateblue"); color("red"); color("wheat"); color("tomato"); // yum!
-
Added
loadHappy()
font to load a default font, happy :D - @lajbelkaplay({ font: "happy" }); loadHappy(); add([text("ohhi")]);
- Random errors - @lajbel
- General type bugs - @lajbel
-
Added new option in
LoadSpriteOpt
for loading sprites in an individual spritesheet - @chqs-gitloadSprite("player", "sprites/player.png", { singular: true, });
-
Frame option for load animations with singular frames - @dragoncoder047
loadSpriteAtlas("/examples/sprites/dungeon.png", { wizard: { x: 128, y: 140, width: 144, height: 28, sliceX: 9, anims: { bouncy: { frames: [8, 5, 0, 3, 2, 3, 0, 5], speed: 10, loop: true, }, }, }, }); add([sprite("wizard", { anim: "bouncy" }), pos(100, 100)]);
- Args were not being passed to global
trigger()
- @lajbel - AreaComp.onClick now returns the correct type, KEventController, instead of void - @lajbel
- Lifespan was using async - @lajbel
- Wrong calculation in Vector.dot() - @andrenanninga
- Fixed pointer lock undefined catch error for non-promise version - @imaginarny
- (examples) Added a new
particle
example! - @lajbel
- Improved
lifespan()
explanation - @lajbel - (examples)
particle
example renamed tolifespan
- @lajbel
- Fixed a bug where
lifespan()
was working incorrectly - @lajbel
- Fixed a bug where alpha channel wasn't correctly setted - @mflerackers
- Added
kaplay({ sprit
eAtlasPadding })
for setting the space between the sprites in the sprite atlas - @marianyp
kaplay({
spriteAtlasPadding: 10, // 10 pixels of space between each sprite
});
- Now you cannot pass parameters that are not a component or string to
.use()
. Otherwise it will throw an error - @lajbel
- Fixed a bug where font atlas were working strange - @mflerackers
-
Added
trigger(event, tag, ...args)
for global triggering events on a specific tag - @lajbeltrigger("shoot", "target", 140); on("shoot", "target", (obj, score) => { obj.destroy(); debug.log(140); // every bomb was 140 score points! });
-
Added
{ override?: true }
inCharTransform
for overridding text styles - @dragoncoder047add([ pos(100, 150), text("With override: Hello [foo]styled[/foo] text", { transform: { color: BLACK, // Default text color for every character }, styles: { foo: { color: RED, // [foo] will be red override: true, // will override the black def color }, }, }), ]);
-
Added
TextCompOpt.identAll
boolean to indent every new line - @dragoncoder047 -
Added TypeScript definition for all App Events and missing Game Object Events - @lajbel
- Fixed an incorrect mention to the component in
TextInputComp
type - @dragoncoder047
-
Added tags and components separation in
KAPLAYOpt.tagsAsComponents
-
Added
GameObjRaw.is()
,GameObjRaw.tag()
andGameObjRaw.untag()
to check, add and remove tags -
Added
GameObjRaw.has()
to check if a game object has a component tags -
Added events for listen to comps being removed or added
onUse()
andonUnused()
-
Added
cancel()
to cancel the current eventonKeyPress("space", () => { // do something // cancel the event return cancel(); });
-
Added
getDefaultLayer()
to get the default layer -
Added
getLayers()
to get the layers list -
Added many JSDoc specifiers on many functions (@require, @deprecated, @since, @group, etc)
- Added
.use()
,.unuse()
and.has()
toGameObjRaw
, to add, remove and check components. This only works withKAPLAYOpt.tagsAsComponents
set totrue
- Deprecated camera methods
camScale()
,camPos()
andcamRot()
in favor ofsetCamScale()
,getCamScale()
,setCamPos()
,getCamPos()
,setCamRot()
andgetCamRot()
- Deprecated
camTransform()
in favor ofgetCamTransform()
- Deprecated
camFlash()
in favor offlash()
, for ashake()
-like name
- Fixed artifacts present in some TrueType fonts
- Fixed
.use()
and.unuse()
with area components
-
Added
getTreeRoot()
to get the game's root object, which is the parent of all other objects// get the root object const root = getTreeRoot(); root.add(); // same as add() root.get(); // same as get()
-
Added Buttons API for using Input bindings,
onButtonPress()
,onButtonRelease()
,onButtonDown()
, and it's corresponding boolean versions,isButtonPressed()
,isButtonDown()
andisButtonReleased()
kaplay({ // bind your buttons buttons: { jump: { keyboard: ["space", "up"], keyboardCode: "Space", // you can also use key codes gamepad: ["south"], }, }, }); onButtonPress("jump", () => { player.jump(); });
-
Added
getButton(btn)
andsetButton(btn)
to get and set button bindings// ["space", "up"] debug.log(getButton("jump").keyboard); // change the jump button in keyboard to "w" setButton("jump", { keyboard: ["w"], // gamepad binding is not changed });
-
Added
getLastInputDeviceType()
to get what was the last pressed deviceonButtonPress(() => { const lastInputDevice = getLastInputDeviceType(); // keyboard, mouse or gamepad // change icons, etc });
-
Added
pressButton(btn)
andreleaseButton(btn)
to simulate button press and releasepressButton("jump"); // triggers onButtonPress and starts onButtonDown releaseButton("jump"); // triggers onButtonRelease and stops onButtonDown
-
Added
GameObjRaw.tags
to get a game object's tagsconst obj = add([sprite("bean"), "enemy", "dangerous"]); // get the tags debug.log(obj.tags); // ["enemy", "dangerous"]
-
Added the
animate()
component to animate the properties of an object using keyframes. Check out Animation Example// prop to animate, frames, options rotatingBean.animate("angle", [0, 360], { duration: 2, direction: "forward", });
-
Readded
layers()
and thelayer()
componentBefore the
z()
component, there was alayer()
component that allowed you to control the draw order of objects. It was removed in v3000, but now it's back from the void.// define the layers layers( [ "bg", "game", "ui", // the default layer ], "game", ); // use the layer component add([sprite("bg"), layer("bg")]);
-
Added
SpriteComp.hasAnim()
to check if an animation existsconst obj = add([sprite("bean", { anim: "walk" })]); // check if an animation exists debug.log(obj.hasAnim("walk")); // true
-
Added
SpriteComp.getAnim()
for get any animation dataloadSprite("bean", "bean.png", { sliceX: 4, sliceY: 1, anims: { walk: { from: 0, to: 3, }, }, }); const obj = add([sprite("bean")]); // get the animation data debug.log(obj.getAnim("walk")); // { from: 0, to: 3 }
-
Added
SpriteComp.getCurAnim()
to get the current animation dataconst obj = add([sprite("bean", { anim: "walk" })]); // get the current animation name debug.log(obj.getCurAnim().name); // "walk"
-
Added
camFlash()
to flash the screencamFlash(0.5, 0.5, 0.5, 0.5);
-
Added support for radius in individual corners for
RectComp,radius
add([ rect(100, 100, { radius: [10, 20, 30, 40], }), ]);
-
Added
loadMusic()
to load streaming audio (doesn't block in loading screen)loadMusic("bgm", "bgm.mp3"); // play the music play("bgm");
-
Added
Vec2.fromArray()
to convert an array to aVec2
const point = Vec2.fromArray([100, 200]); // vec2(100, 200);
-
Added
Vec2.toArray()
to convert aVec2
to an arrayconst point = vec2(100, 200); const arr = point.toArray(); // [100, 200]
-
Added
chooseMultiple()
to choose a random element from an arrayconst numbers = [1, 2, 3, 4, 5]; const random = chooseMultiple(numbers, 3); // [3, 1, 5]
-
Added
shuffle()
to shuffle an arrayconst numbers = [1, 2, 3, 4, 5]; shuffle(numbers); // [3, 1, 5, 2, 4]
-
Added
KAPLAYOpt.debugKey
for customizing the key used to toggle debug modekaplay({ debugKey: "l", });
-
Added compatibility with custom properties in debug mode
const obj = add([ sprite("bean"), { health: 100, // on debug.inspect damage: 10, // on debug.inspect hp() { this.health -= this.damage; }, // not on debug.inspect }, ]); // see the custom properties in debug mode debug.inspect = true;
-
Added effector components:
areaEffector()
,buoyancyEffector()
,pointEffector()
,surfaceEffector()
-
Added
constantForce()
component -
Added
pathfinder()
component to calculate a list of waypoints on a graph -
Added
patrol()
component to move along a list of waypoints -
Added
sentry()
component to notify when certain objects are in sight -
Added
NavMesh
class for pathfinding on a mesh -
Added
particles()
component to emit and draw particles -
Added
SpriteComp.animFrame
to get the frame of the current animation (not on the spritesheet) -
Added
outline()
,shader()
, andarea()
properties todebug.inspect
-
Added
getSceneName()
to get the current scene name -
Added
Color.toArray()
to convert a color to an array -
Added
raycast
andLevelComp.raycast
method to level -
Added support for textured polygons
-
Added support for concave polygon drawing
-
Added support for arrays in uniforms
-
Added support for texture larger than 2048x2048
-
Added support for gravity direction
-
Added line join (bevel, miter, round) and line caps (square, round)
-
Added quadratic bezier and Catmull-Rom evaluation
-
Added evaluation of the first and second derivatives for all splines
-
Added higher order easing functions linear, steps and cubic-bezier
-
Now collision checks are only done if there's area objects
-
Now you can use arrays in all input handlers
onKeyPress(["w", "up"], () => { player.jump(); });
-
Now gamepad events return what gamepad triggered the action
onGamepadButtonPress("south", (btn, gp) => { console.log(gp.index); // gamepad number on navigator's gamepad list });
-
Now
ScaleComp
andSpriteComp
uses setters/getters for it's stateconst obj = add([sprite("bean"), scale(2)]); // set it with = syntax obj.scale = vec2(3, 4); obj.sprite = "bag";
-
Now you can type
get()
with a type parameter and passing component typesconst player = get<BodyComp>("player");
-
Now you can pass an
AudioBuffer
toloadSound()
-
Now
debug.log()
accepts multiple argument of any type, likeconsole.log()
-
Now
Key
also accepts a string as an acceptable value -
Now
text()
component doesn't require to pass a string -
Now
camScale()
andcamPos()
accept only 1 number as parameter -
Now
shake()
can be called without args -
Now
loadShader()
andloadShaderURL()
accepts null for unused parameters -
Now
RectCompOpt
accepts a array of numbers forradius
- Deprecated
kaboom()
in favor ofkaplay()
(you can still usekaboom*
) - Deprecated
SpriteComp.curAnim()
in favor ofSpriteComp.getCurAnim().name
- Deprecated
fadeIn
component in favor ofOpacityComp.fadeIn()
- Deprecated
Event
,EventHandler
andEventController
in favor ofKEvent
,KEventHandler
andKEventController
- (!) Removed compatibility to use two KAPLAY frames in the same page
- (!) Many TypeScript definitions were fixed, if you use TypeScript now maybe you see new errors that make your code strict
- Fix error screen not showing with not Error object
- Fix error where debug screen was scaling bad the blue rectangles
- Fix error where error screen was not showing when the error was thrown in a input event
- Fix error where fonts was cropped in the bottom
- Fix an error where
stay()
object loose their input events on scene change