A ๐ฉโ๐ป developer-friendly entity management system for ๐น games and similarly demanding applications, based on ๐ ECS architecture.
MIT License
Bot releases are visible (Hide)
miniplex
peer dependency.Published by github-actions[bot] over 1 year ago
8a7a315: - The library has been significantly simplified and an almost mind-boggling number of bugs have beens quashed.
The main import and initialization have changed:
import { World } from "miniplex"
import createReactAPI from "miniplex-react" // !
/* It now expects a world as its argument, so you need to create one first: */
const world = new World()
const ECS = createReactAPI(world)
All lists of entities are now rendered through the upgraded <Entities>
component, which takes an array of entities or a query (or even a world) as its in
prop:
<Entities in={world.with("asteroid")}>{/* ... */}</Entities>
If you're passing in a query or a world, the component will automatically re-render when the entities appear or disappear. If you don't want this, you can also just pass in a plain array containing entities:
<Entities in={[entity1, entity2]}>{/* ... */}</Entities>
<ManagedEntities>
has been removed. You were probably not using it. If you were, you can replicate the same behavior using a combination of the <Entities>
component and a useEffect
hook.
The useEntity
hook has been renamed to useCurrentEntity
.
The world-scoped useArchetype
hook has been removed, and superseded by the new global useEntities
hook:
/* Before */
const entities = useArchetype("position", "velocity")
/* Now */
const entities = useEntities(world.with("position", "velocity"))
Published by github-actions[bot] over 1 year ago
8a7a315: Miniplex 2.0 is a complete rewrite of the library, with a heavy focus on further improving the developer experience, and squashing some significant bugs.
While it does bring some breaking changes, it will still allow you to do everything that you've been doing with 1.0; when upgrading a 1.0 project to 2.0, most changes you will need to do are related to things having been renamed.
The headline changes in 2.0:
world.with("componentName")
, chain these together, use the new without
, or even create advanced predicate-based queries using where
.Some more details on the changes:
world.createEntity
has been renamed and simplified to just world.add
(which now returns the correct type for the entity, too), and world.destroyEntity
to world.remove
.
const entity = world.add({ position: { x: 0, y: 0 } })
world.addComponent(entity, "velocity", { x: 0, y: 0 })
world.remove(entity)
The Tag
type and constant have been removed. For tag-like components, simply use true
(which Tag
was just an alias for.)
Entities added to a world no longer receive a __miniplex
component. This component has always been an internal implementation detail, but you might have used it in the past to get a unique identifier for an entity. This can now be done through world.id(entity)
, with ID lookups being available through world.entity(id)
.
Queries can now be iterated over directly. Example:
const moving = world.with("position", "velocity")
for (const { position, velocity } of moving) {
position.x += velocity.x
position.y += velocity.y
}
This is, in fact, now the recommended way to iterate over entities, since it is extremely efficient, and will make sure that the list of entities is being iterated over in reverse, which makes it safe to modify it during iteration.
You can also use this to neatly fetch the first entity from an archetype that you only expect to have a single entity in it:
const [player] = world.with("player")
The queuing functionality that was built into the World
class has been removed. If you've relied on this in the past, miniplex
now exports a queue
object that you can use instead. Example:
import { queue } from "miniplex"
queue(() => {
// Do something
})
/* Later */
queue.flush()
Please note that this is being provided to make upgrading to 2.0 a little easier, and will likely be removed in a future version.
with
and without
are the new API for building queries. Examples:
const moving = world.with("position", "velocity")
const alive = world.without("dead")
The new world.where
now allows you to build predicate-based queries! You can use this as an escape hatch for creating any kind of query based on any conditions you specify. Example:
const almostDead = world.where((entity) => entity.health < 10)
Please note that his requires entities with the health
component to be reindexed using the world.reindex
function in order to keep the archetype up to date. Please refer to the documentation for more details.
You can use where
to create a predicate-based iterator. This allows you to quickly filter a set of entities without creating new archetypes or other objects. Example:
for (const entity of world.where((entity) => entity.health < 10)) {
// Do something
}
All of these can be chained!
world
.with("position", "velocity")
.without("dead")
.where((entity) => entity.health < 10)
Entities fetched from a query will have much improved types, but you can also specify a type to narrow to via these functions' generics:
const player = world.with<Player>("player")
Miniplex provides the new Strict
and With
types which you can use to compose types from your entity main type:
type Entity = {
position: { x: number; y: number }
velocity: { x: number; y: number }
}
type Player = Strict<With<Entity, "position" | "velocity">>
const player = world.archetype<Player>("player")
The event library Miniplex uses has been changed to Eventery, which brings a change in API. Where before you would have done onEntityAdded.add(listener)
, you will now to onEntityAdded.subscribe(listener)
.
Published by github-actions[bot] over 1 year ago
@miniplex/bucket
. It can be pretty useful even outside of Miniplex, and will eventually be extracted into its own project.Published by github-actions[bot] over 1 year ago
miniplex-react
package's documentation an overhaul for the upcoming 2.0.0 release. Do give it a read, feedback welcome!Published by github-actions[bot] over 1 year ago
World.reindex(entity)
is now a public method. When creating queries that use .where(predicate)
to check entities by value, you are expected to use it to notify the world that an entity whose values you have mutated should be reindexed.Published by github-actions[bot] over 1 year ago
miniplex
package now only providing the vanilla, framework-agnostic ECS functionality, and miniplex-react
adding the React glue (and using miniplex
as a peer dependency.)Published by github-actions[bot] over 1 year ago
miniplex
package now only providing the vanilla, framework-agnostic ECS functionality, and miniplex-react
adding the React glue (and using miniplex
as a peer dependency.)Published by github-actions[bot] over 1 year ago
Bucket
class now uses Eventery for its onEntityAdded
and onEntityRemoved
events, which has a slightly different API than the library used before, since it uses .subscribe(listener)
and .unsubscribe(listener)
instead of .add
and .remove
.Published by github-actions[bot] over 1 year ago
Bucket
class now uses Eventery for its onEntityAdded
and onEntityRemoved
events, which has a slightly different API than the library used before, since it uses .subscribe(listener)
and .unsubscribe(listener)
instead of .add
and .remove
.world.archetype()
is gone. In its place, you can simply use world.with()
instead.Published by github-actions[bot] over 1 year ago
Bucket
class now uses Eventery for its onEntityAdded
and onEntityRemoved
events, which has a slightly different API than the library used before, since it uses .subscribe(listener)
and .unsubscribe(listener)
instead of .add
and .remove
.Published by github-actions[bot] over 1 year ago
ae19dd0: Aaaaah, it's Miniplex 2.0 Beta 4! The library received some major refactoring for this beta, making it significantly less complex, and at the same time easier to explain and document. Which, sadly, also means breaking changes -- but most of them are just renames, so you shouldn't have any trouble upgrading. (If you do, let me know!)
The most important changes:
world.archetype
is gone. The query API is now just .with(...components)
and .without(...components)
..where(predicate)
now returns a full query object that can be chained further (as opposed to a standalone iterator). Please note that this functionality is still experimental and will still receive upgrades and changes in the future.onEntityAdded.add(listener)
, you will now to onEntityAdded.subscribe(listener)
.1258841: All packages are now built using TypeScript 5.0.
Updated dependencies [4448080]
Updated dependencies [1258841]
Updated dependencies [4448080]
Published by github-actions[bot] almost 2 years ago
Published by github-actions[bot] almost 2 years ago
Published by github-actions[bot] almost 2 years ago
80b944f: useCurrentEntity
will now throw an error if it is invoked outside of an entity context (instead of returning undefined
).
20a0904: Upgraded to building with TypeScript 4.9.
728feb4: <Entity>
now accepts a forwarded ref that will be set to the created entity.
48f88f2: Fixed a bug (#269) where <Entity>
would destroy and recreate its entity every time it was rendered.
a96901f: Breaking Change: Removed the <Archetype>
component. Please use <Entities in={...} />
instead:
/* Before: */
<Archetype with={["enemy", "attacking"]} without="dead" />
/* After (inline): */
<Entities in={world.with("enemy", "attacking").without("dead")} />
/* After (out of band): */
const attackingEnemies = world.with("enemy", "attacking").without("dead")
<Entities in={attackingEnemies} />
Published by github-actions[bot] almost 2 years ago
Published by github-actions[bot] almost 2 years ago
Bucket.first
, a getter that returns the first entity in the bucket (or undefined
if the bucket has no entities.)Published by github-actions[bot] almost 2 years ago
Published by github-actions[bot] almost 2 years ago
Published by github-actions[bot] almost 2 years ago
42134bc: Hooray, it's the first beta release of Miniplex 2.0! While the new documentation website is still deeply work in progress, I'd like to provide you with a summary of the changes so you can start giving this thing a go in your projects.
Miniplex 2.0 is a complete rewrite of the library, but while it does bring some breaking changes, it will still allow you to do everything that you've been doing with 1.0. When upgrading a 1.0 project to 2.0, most changes you will need to do are related to things having been renamed.
The headline changes in 2.0:
Miniplex 2.0 is still in beta, so you will need to install it using the beta
tag:
npm add miniplex@beta
yarn add miniplex@beta
pnpm add miniplex@beta
world.createEntity
has been renamed and simplified to just world.add
(which now returns the correct type for the entity, too), and world.destroyEntity
to world.remove
. addComponent
and removeComponent
have not been changed.
const entity = world.add({ position: { x: 0, y: 0 } })
world.addComponent(entity, { velocity: { x: 0, y: 0 } })
world.remove(entity)
There is a new world.update
function that you may use to update an entity and make sure it is reindexed across the various archetypes. It provides a number of different overloads to provide some flexibility in how you update the entity.
world.update(entity, { position: { x: 1, y: 1 } })
world.update(entity, "position", { x: 1, y: 1 })
world.update(entity, () => ({ position: { x: 1, y: 1 } }))
world.update(entity, (e) => (e.position = { x: 1, y: 1 }))
Please keep in mind that in Miniplex, you'll typically mutate your entities anyway, so going through this function is not strictly necessary.
The Tag
type and constant have been removed. For tag-like components, simply use true
(which Tag
was just an alias for.)
Entities added to a world no longer receive a __miniplex
component. This component has always been an internal implementation detail, but you might have used it in the past to get a unique identifier for an entity. This can now be done through world.id(entity)
, with ID lookups being available through world.entity(id)
.
Archetypes can now be iterated over directly. Example:
const moving = world.archetype("position", "velocity")
for (const { position, velocity } of moving) {
position.x += velocity.x
position.y += velocity.y
}
You can use this to neatly fetch the first entity from an archetype that you only expect to have a single entity in it:
const [player] = world.archetype("player")
The queuing functionality that was built into the World
class has been removed. If you've relied on this in the past, miniplex
now exports a queue
object that you can use instead. Example:
import { queue } from "miniplex"
queue(() => {
// Do something
})
/* Later */
queue.flush()
Please note that this is being provided to make upgrading to 2.0 a little easier, and will likely be removed in a future version.
world.archetype
can now take a predicate! You can use this as an escape hatch for creating any kind of archetype based on the conditions you specify. Example:
const almostDead = world.archetype((entity) => entity.health < 10)
Please note that his requires entities with the health
component to be updated through the world.update
function in order to keep the archetype up to date.
You can use with
and without
as an alternative API for creating archetypes. Example:
const moving = world.with("position", "velocity")
const alive = world.without("dead")
You can use where
to create a predicate-based iterator. This allows you to quickly filter a set of entities without creating new archetypes or other objects. Example:
for (const entity of world.where((entity) => entity.health < 10)) {
// Do something
}
All of these can be nested!
world
.with("position", "velocity")
.without("dead")
.where((entity) => entity.health < 10)
Entities fetched from an archetype will have much improved types, but you can also specify a type to narrow to via these functions' generics:
const player = world.archetype<Player>("player")
Miniplex provides the new Strict
and With
types which you can use to compose types from your entity main type:
type Entity = {
position: { x: number; y: number }
velocity: { x: number; y: number }
}
type Player = Strict<With<Entity, "position" | "velocity">>
const player = world.archetype<Player>("player")
The React package's main import and initialization has been changed:
import { World } from "miniplex"
import { createReactAPI } from "miniplex/react" // !
/* It now expects a world as its argument, so you need to create one first: */
const world = new World()
const ECS = createReactAPI(world)
The <Archetype>
component now supports the with
and without
properties:
<Archetype with={["position", "velocity"]} without="dead">
{/* ... */}
</Archetype>
If you already have a reference to an archetype, you can pass it to the newly improved <Entities>
component to automatically render all entities contained within it, and have them automatically update when the archetype changes:
<Entities in={archetype}>{/* ... */}</Entities>
If you ever want to list a simple array of entities, you can use the same component (but it will not automatically update if the array contents change):
<Entities in={[entity1, entity2]}>{/* ... */}</Entities>
<ManagedEntities>
has been removed. You were probably not using it. If you were, you can replicate the same behavior using a combination of the <Entities>
or <Archetype>
components and a useEffect
hook.
The useEntity
hook has been renamed to useCurrentEntity
.
The world-scoped useArchetype
hook has been removed, and superceded by the new global useEntities
hook:
/* Before */
const entities = useArchetype("position", "velocity")
/* Now */
const entities = useEntities(world.with("position", "velocity"))
This is the first beta of a big new release for this library, and since it is a complete rewrite, there are bound to be some bugs and rough edges.