Phatty
Unity-style Entites and Components for Phaser JS
Install / Use
/learn @grischaerbe/PhattyREADME

A lightweight, TypeScript-based entity component system for Phaser games, inspired by Unity's GameObjects. Phatty provides a structured way to organize game logic using entities and components, making your Phaser games more maintainable and modular.
Features
- 🎮 Unity-style component system for Phaser
- 🔄 Lifecycle management (create, update, destroy, …)
- 🎯 Type-safe component references
- 📦 Component dependency management
- 🔊 Event system integration
- ⚡ Priority-based component execution order
Installation
npm install phatty
Quick Start
See this StackBlitz for a full example:
<a href="https://stackblitz.com/edit/phatty-example?file=src%2Fmain.ts"> <img src="https://developer.stackblitz.com/img/open_in_stackblitz.svg" alt="Open in StackBlitz" /> </a>Creating a Scene
To use Phatty, you need to extend the Scene class. It's a regular Phaser
scene, but with an EntitySystem instance.
import { Scene } from 'phatty'
// Create an entity in your Phaser scene
class GameScene extends Scene {
create() {
const player = this.entities.create()
}
}
Creating an Entity
Entities are created by calling this.entities.create(). They follow the same
lifecycle as the scene, and are automatically destroyed when the scene is
destroyed.
const player = this.entities.create()
Creating a Component
Components are created by extending the Component class. They are the building
blocks of what makes up an entity. They can have parameters, and can reference
and even require other components.
class PlayerComponent extends Component {
constructor(public speed: number) {
super()
}
create() {
// Set up component and get references to other components
const sprite = this.entity.components.get(SpriteComponent)
}
update(time: number, delta: number) {
// Update logic
}
destroy() {
// Cleanup
}
}
Adding Components
// Add a component to the player entity, with parameters
player.components.add(PlayerComponent, 100)
Destroying an Entity
// Destroy the player entity
player.destroy()
Documentation
Core Concepts
Scene
A Phatty scene is a regular Phaser scene, but with an EntitySystem instance
which allows you to create, manage and query entities.
import { Scene } from 'phatty'
class GameScene extends Scene {
create() {
const player = this.entities.create()
}
}
Entity
An Entity represents a game object in your scene. It manages components and their lifecycle.
// Create an entity in your Phaser scene
const entity = this.entities.create()
Component
Components are the building blocks of what makes up an entity. They can have parameters, and can reference and even require other components.
Component Lifecycle
A component offers life cycle functions that are called at the appropriate times in the entity's lifecycle.
class PlayerComponent extends Component {
constructor(public speed: number) {
super()
// The constructor is called immediately when the component is added
// to an entity via `entity.components.add(PlayerComponent, 100)`.
// You have access to the entity that the component is added to
// via `this.entity`. You can also access the scene that the entity
// belongs to via `this.entity.scene`. Typically, you'll want to set
// up any entirely internal state in the constructor.
}
create() {
// Set up component and get references to other components. This is
// called on the first update after the component is added but before
// the first update loop.
const sprite = this.entity.components.get(SpriteComponent)
}
update(time: number, delta: number) {
// Update logic. This is called every frame as long as the component
// is not sleeping, not paused, and not destroyed.
}
sleep() {
// This is called when the scene this component is attached to is put to
// sleep.
}
wake() {
// This is called when the scene this component is attached to is woken up
// from sleep.
}
pause() {
// This is called when the scene this component is attached to is paused.
}
resume() {
// This is called when the scene this component is attached to is resumed.
}
destroy() {
// This is called when the component is destroyed because
// - The scene it belongs to is destroyed or shut down or
// - The entity it belongs to is destroyed with `entity.destroy()`
}
}
Component Priority
Components are updated in priority order, where:
- Lower numbers execute first
- Default priority is 0
- Priorities can be negative
class InputComponent extends Component {
public priority = -10
// Processes input first
}
class PhysicsComponent extends Component {
public priority = 0
// Updates physics based on input
}
class CameraComponent extends Component {
public priority = 10
// Updates camera position after physics
}
Component Dependencies
The required metadata ensures component dependencies are met:
class SpriteComponent extends Component {
required: [TransformComponent]
create() {
// If there's no TransformComponent, this will throw an error
const transform = this.entity.components.get(TransformComponent)
}
}
Entity Query Builder
The Query Builder provides a fluent interface for finding entities based on their components. It supports both positive and negative component matching, and offers several methods to get results.
// Find entities with a specific component
const players = this.entities.query.with(PlayerComponent).all()
// Find entities by complex criteria
const activeSoldiers = this.entities.query
.with(PlayerComponent, (e) => e.type === 'soldier')
.without(DeadComponent)
.all()
// Find entities by the presence of multiple components
const deadEnemies = this.entities.query.with([EnemyComponent, DeadComponent]).all()
// Find entities by the absence of multiple components
const alivePlayers = this.entities.query
.with(PlayerComponent)
.without([DeadComponent, AiComponent])
.all()
// Find the first matching entity
const player = this.entities.query.with(PlayerComponent).first()
// Check if any entities match
const hasActivePlayers = this.entities.query.with(PlayerComponent).without(DeadComponent).exists()
// Count matching entities
const activeEnemyCount = this.entities.query.with(EnemyComponent).without(DeadComponent).count()
The Query Builder supports the following methods:
-
with(component, where?): Find entities that have the specified component, optionally filtered by a predicate on the component instance. -
with([componentA, componentB, ...], where?): Find entities that have all specified components, optionally filtered by a predicate on the array of component instances. -
without(component, where?): Exclude entities that have the specified component, optionally filtered by a predicate on the component instance. If the predicate returnstrue, the entity will be excluded. -
without([componentA, componentB, ...], where?): Exclude entities that have all specified components, optionally filtered by a predicate on the array of component instances. If the predicate returnstrue, the entity will be excluded. -
first(): Get the first matching entity. -
all(): Get all matching entities. -
count(): Count matching entities. -
exists(): Check if any entities match.
API Reference
Scene
class Scene extends Phaser.Scene {
entities: EntitySystem
}
EntitySystem
class EntitySystem {
// Events: 'create', 'destroy'
events: Phaser.Events.EventEmitter
query: QueryBuilder
create(): Entity
find(options: EntityQueryOptions): Entity | undefined
findAll(options: EntityQueryOptions): Entity[]
}
QueryBuilder
class QueryBuilder {
with<CC extends ComponentConstructor>(
component: CC,
where?: (component: InstanceType<CC>) => boolean
): this
with<const ComponentConstructors extends [ComponentConstructor, ...ComponentConstructor[]]>(
components: ComponentConstructors,
where?: (components: {
[K in keyof ComponentConstructors]: InstanceType<ComponentConstructors[K]>
}) => boolean
): this
without<CC extends ComponentConstructor>(
component: CC,
where?: (component: InstanceType<CC>) => boolean
): this
without<const ComponentConstructors extends [ComponentConstructor, ...ComponentConstructor[]]>(
components: ComponentConstructors,
where?: (components: {
[K in keyof ComponentConstructors]: InstanceType<ComponentConstructors[K]>
}) => boolean
): this
first(): Entity | undefined
all(): Entity[]
count(): number
exists(): boolean
}
Entity
class Entity {
constructor(scene: Phaser.Scene)
components: ComponentSystem
destroy(): void
}
ComponentSystem
class ComponentSystem {
// Adding/Removing Components
add<T extends ComponentConstructor>(
Component: T,
...args: ConstructorParameters<T>
): InstanceType<T>
remove<T extends Component>(Component: ComponentConstructor<T>): void
clear(): void
// Querying Components
get<T extends Component>(Component: ComponentConstructor<T>): T
find<T extends Compon
Related Skills
node-connect
352.5kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
111.3kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
352.5kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
352.5kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
