alchemy-engine v0.3.0
:alembic: alchemy engine
👾 Easily make 2D browser games with pixi.js
:sparkles: Features
- 100% Type-Safe
- Batteries included (get started using one CLI command)
- Reactive - Re-render view when state changes
- Object pooling
- Timer - Run logic every X ticks
- Animations
- Debug overlay
- Keyboard input
- Sounds
- Sprite sheet generation
- Scenes
- CLI - Create project and components
- CI - Workflows to build, test and deploy to
itch.io - Screen shake
- Uses
vitefor a super fast and modern dev server
Getting started
Run the init command from within a Git repo
npx alchemy@latest initThis will:
- Copy template files
- Add
alchemy-engineandpixi.jsas dependencies
API Docs
Module API
These are imported directly from alchemy-engine
create
Convenience functions to create Pixi objects
sprite
import { sprite } from 'alchemy-engine'
sprite(container, getTexture('./square-1')])animatedSprite
import { animatedSprite } from 'alchemy-engine'
animatedSprite(container, getTextures(['./square-1', './square-2']))text
import { text } from 'alchemy-engine'
text(container, textStyle, 'Hello world')htmlText
import { htmlText } from 'alchemy-engine'
htmlText(container, textStyle, 'Hello world')bitmapText
import { bitmapText } from 'alchemy-engine'
bitmapText(container, textStyle, 'Hello world')container
import { container } from 'alchemy-engine'
container(_container)graphics
import { graphics } from 'alchemy-engine'
graphics(container)rectangle
import { rectangle } from 'alchemy-engine'
rectangle(container, { x: 0, y: 0, width: 10, height: 10 })event
onClick
Convenience functions for mouse input
import { onClick } from 'alchemy-engine'
onClick(container, () => {
console.log('Clicked!')
})onHover
import { onHover } from 'alchemy-engine'
onHover(container, {
onOver() {
console.log('Hovered!')
},
onOut() {
console.log('Not hovered!')
},
})sync
sync
syncPosition
keys
arrowKeys
Constants for all arrow keys
import { arrowKeys } from 'alchemy-engine'
export const keys = ['a', 'w', 's', 'd', ...arrowKeys] as constposition
TODO
getAllChildren
TODO
getAllLeafChildren
TODO
debug
logObject
Nicely log a Pixi object. Set label property for best result.
import { logObject } from 'alchemy-engine'
const sprite = new Sprite()
sprite.label = 'sprite'
logObject(sprite)boundsToString
Enables easier logging of sprite bounds
import { boundsToString } from 'alchemy-engine'
console.log(boundsToString(sprite))contains
Check if a point is within the bounds of an object
import { contains } from 'alchemy-engine'
if (contains(sprite, { x: 1, y: 1 })) {
// point is within bounds of sprite
}intersects
Check if the bounds of two objects are intersecting
import { intersects } from 'alchemy-engine'
if (intersects(sprite1, sprite2)) {
// sprites are intersecting
}type guards
isAnimatedSprite
import { isAnimatedSprite } from 'alchemy-engine'
if (isAnimatedSprite(sprite)) {
// sprite is of type AnimatedSprite
}loadDataFromImage
This function can be used to for example load a level from image data
import { loadDataFromImage } from 'alchemy-engine'
import map from '~/public/asset/map.png?url'
const { pixels, width, height } = await loadDataFromImage(map)
console.log(pixels)
// ['255-255-255', '0-0-0']pool
Scene API
The arguments passed to a scene
{
textures,
container,
input,
state,
timer,
sound,
app,
timer,
useScreenShake,
}: ScenegetTexture
Get a texture
function myScene(scene: Scene) {
sprite(scene.container, scene.getTexture('./texture-1'))
}getTextures
Get multiple textures
function myScene(scene: Scene) {
animatedSprite(
scene.container,
scene.getTextures(['./texture-1', './texture-2']),
)
}useScreenShake
Enable the use of screen shake
screenShake takes a number between 0 and 1
const screenShake = useScreenShake(container)
screenShake(0.5)animate
These functions all require an onUpdate and duration argument
Optionally you can pass a startValue (default: 0) and endValue (default: 1)
- sine
- easeOut
- easeIn
- linear
app
The Pixi Application instance
music
Record<MusicName, Howl>Example:
scene.music.bgm.loop(true).play()sound
Record<SoundName, Howl>Example:
scene.sound.coin.play()setScene
Takes sceneKey as an argument
setScene('mainMenu')global
timer
A timer that doesn't get cancelled when changing scenes
container
A scene specific Pixi container. Will be destroyed when scene is changed.
state
Set state to trigger sync and subscribe functions
internalState
subscribe, subscribeKey, proxy
Re-exported from valtio
Changes from valtio versions:
- Triggers once up front when called
- Unsubscribes when changing scene
timer
delay
Resolves a promise after X ticks
// Wait 100 updates
await scene.timer.delay(100)repeatUntil
Execute a callback every update until duration is reached
await scene.timer.repeatUntil(3, (time, deltaTime) => {})repeatEvery
Execute a callback indefinitely every interval updates
Returns a cancel function
const cancel = repeatEvery(3, (time, deltaTime) => {})input
debouncedKey
scene.input.debouncedKey(
'd',
() => {
s.position.x += 1
},
// Delay between key presses
10,
)isKeyDown
Check if a key is currently being pressed
scene.timer.repeatEvery(1, () => {
if (scene.input.isKeyDown(['a', 'ArrowLeft'])) {
s.position.x -= 1
}
if (scene.input.isKeyDown(['d', 'ArrowRight'])) {
s.position.x += 1
}
})random
There is a built-in seedable random module.
To set the seed, pass it in to createGame
createGame({
randomSeed: 99,
})The module uses the same API as park-miller, with some additions:
chance(percentage) => boolean
CLI
dev
Start the dev server. Listens to changes to source code, sprite, src/public/asset/sound and src/public/asset/music folders.
npx alchemy devsprite
Generate sprite sheet
npx alchemy spritesound
Load sounds
npx alchemy soundLocal development
Run ./go.sh to test that things work
See also
- State machine: xstate
- Noise simplex-noise
8 months ago
9 months ago
9 months ago
9 months ago
10 months ago
10 months ago
10 months ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago