Skip to content

Commit

Permalink
Merge pull request #44 from ayebear/alpha5
Browse files Browse the repository at this point in the history
Next alpha version
  • Loading branch information
ayebear authored Apr 10, 2021
2 parents 045c3a0 + 0ae079e commit 00a41ca
Show file tree
Hide file tree
Showing 24 changed files with 2,833 additions and 3,018 deletions.
76 changes: 36 additions & 40 deletions .esdoc.json
Original file line number Diff line number Diff line change
@@ -1,41 +1,37 @@
{
"source": "./src",
"destination": "./docs",
"plugins": [
{
"name": "esdoc-ecmascript-proposal-plugin",
"option": {
"all": true
}
},
{
"name": "esdoc-undocumented-identifier-plugin",
"option": {
"enable": true
}
},
{
"name": "esdoc-unexported-identifier-plugin",
"option": {
"enable": true
}
},
{
"name": "esdoc-standard-plugin",
"option": {
"undocumentIdentifier": {
"enable": false
},
"unexportedIdentifier": {
"enable": true
}
}
}
],
"includes": [
"./*.js"
],
"excludes": [
"(test|benchmarks)"
]
}
"source": "./src",
"destination": "./docs",
"plugins": [
{
"name": "esdoc-ecmascript-proposal-plugin",
"option": {
"all": true
}
},
{
"name": "esdoc-undocumented-identifier-plugin",
"option": {
"enable": true
}
},
{
"name": "esdoc-unexported-identifier-plugin",
"option": {
"enable": true
}
},
{
"name": "esdoc-standard-plugin",
"option": {
"undocumentIdentifier": {
"enable": false
},
"unexportedIdentifier": {
"enable": true
}
}
}
],
"includes": ["./*.js"],
"excludes": ["(test|benchmarks)"]
}
3 changes: 3 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules/
coverage/
docs/
9 changes: 9 additions & 0 deletions .prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"printWidth": 80,
"tabWidth": 2,
"singleQuote": true,
"trailingComma": "es5",
"semi": false,
"useTabs": false,
"arrowParens": "avoid"
}
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
language: node_js
node_js:
- "10"
- '10'
branches:
only:
- master
- master
182 changes: 36 additions & 146 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@

### Table Of Contents

- [About](#about)
- [Features](#features)
- [Terminology](#terminology)
- [License](#license)
- [Author](#author)
- [Instructions](#instructions)
- [Setup](#setup)
- [Documentation](#documentation)
- [Examples](#examples)
- [About](#about)
- [Features](#features)
- [Terminology](#terminology)
- [License](#license)
- [Author](#author)
- [Instructions](#instructions)
- [Setup](#setup)
- [Documentation](#documentation)
- [Examples](#examples)

## About

Expand All @@ -24,37 +24,29 @@ This entity system is designed to be as simple as possible, while still having u

### Features

- **Simple query syntax**
- `world.each('a', 'b', ({a, b}) => { a.foo = b.bar })`
- See the examples below for more advanced usage, or the [reference docs](https://ayebear.com/picoes/class/src/world.js~World.html#instance-method-each)
- **No formal declarations required**
- Can create components and entities in a world and query on them, without needing to define structured systems and components
- **Strings as component keys**
- No need to manually define component keys, or manually include component classes to use them
- **Automatic dependency injection for systems**
- No need to pass state to each system, can have a single context that gets injected into all systems automatically
- **High performance indexing options**
- SimpleIndex (Default): O(1) component add/remove, O(m) query time
- Where `m` is the smallest size component index
- MemoizedQueryIndex: O(q) component add/remove, O(1) average query time (memoized), O(n) worst query time (initial)
- Where `q` is the total number of memoized queries
- And `n` is the total number of entities
- _Note: Above time complexities are amortized assuming the number of components used is a known constant_
- Can also write your own and pass it to the World constructor! Needs clear, add, remove, and query.
- **Prototypes**
- Allows entity definitions to be data-driven, outside of code
- **Simple query syntax**
- `world.each('a', 'b', ({a, b}) => { a.foo = b.bar })`
- See the examples below for more advanced usage, or the [reference docs](https://ayebear.com/picoes/class/src/world.js~World.html#instance-method-each)
- **No formal declarations required**
- Can create **unlimited** (within memory limits) components and entities in a world and query on them, without needing to define structured systems and components
- **Strings as component keys**
- No need to manually define component keys, or manually include component classes to use them
- **Automatic dependency injection for systems**
- No need to pass state to each system, can have a single context that gets injected into all systems automatically
- **Balanced performance**
- See [ECS benchmark comparison](https://github.com/noctjs/ecs-benchmark)
- Entity/Component adding/removing performance is decent with PicoES, which is important for many games.
- Active research and work is being done to significantly improve PicoES performance as much as possible without making it harder to use.

### Terminology

- **Component:** Holds some related data
- Example: Position, Velocity, Health
- **Entity:** Refers to a collection of components
- Example: Position + Health could represent a player
- **Prototype:** A template of components used for creating entities
- Example: Player could contain Position, Velocity, and Health
- **System:** Logic loop that processes entities
- Example: Movement system which handles positions and velocities
- **World:** Lets you register components, systems, and prototypes in a self-contained object - which avoids the use of singletons. This is also where you can create entities from.
- **Component:** Holds some related data
- Example: Position, Velocity, Health
- **Entity:** Refers to a collection of components
- Example: Position + Health could represent a player
- **System:** Logic loop that processes entities
- Example: Movement system which handles positions and velocities
- **World:** The entry point of all PicoES features. Can register components/systems and create/query entities in a self-contained object - which avoids the use of singletons.

### License

Expand Down Expand Up @@ -84,136 +76,34 @@ npm i -D picoes

### Documentation

The full reference documentation can be found here:

[PicoES Documentation](https://ayebear.com/picoes)

### Examples

#### Shorthand anonymous components and systems

```javascript
// import { World } from 'picoes'
const { World } = require('picoes')
import { World } from 'picoes'

// Create a world to store entities in
const world = new World()

// Create player with anonymous health component
// Create a player entity with health component
const player = world.entity().set('health', { value: 100 })

// Create enemies
world.entity().set('damages', 10)
world.entity().set('damages', 30)

// Apply damage
// Apply damage to player from enemies
world.each('damages', ({ damages }) => {
player.get('health').value -= damages
player.get('health').value -= damages
})

// Player now has reduced health
console.assert(player.get('health').value === 60)
```

#### Full component and system definitions

```javascript
// const { World } = require('picoes')
import { World } from 'picoes'

// Create a world to store entities in
const world = new World()

// Define and register components
class Vec2 {
constructor(x = 0, y = 0) {
this.x = x
this.y = y
}
}
world.component('position', Vec2)
world.component('velocity', Vec2)
world.component('health', class {
constructor(start = 100) {
this.value = start
}
})

// Example of using onCreate and onRemove
world.component('sprite', class {
onCreate(texture) {
// this.entity is auto-injected into registered components
// It is not available in the constructor, but is available in onCreate
this.container = this.entity.get('gameContainer')
this.sprite = new Sprite(texture)
this.container.add(this.sprite)
}

onRemove() {
this.container.remove(this.sprite)
}
})

// Define systems
// Log statements are to show flow order below
class MovementSystem {
init(...args) {
// Context is available here as well
console.log('init() called with args:', ...args)
}

run(dt) {
console.log(`run(${dt}) called`)
world.each('position', 'velocity', ({ position, velocity }, entity) => {
console.log(`each() called for entity ${entity.id}`)
position.x += velocity.x * dt
position.y += velocity.y * dt
})
}
}

// Register systems
world.system(MovementSystem, 'extra', 'args')

// Create entity without prototype
const entityA = world.entity().set('position').set('velocity')
console.assert(entityA.has('position'))
console.assert(entityA.has('velocity'))

// Create entity with prototype (results are the same as above)
world.prototype({
Movable: {
position: {},
velocity: {},
},
})
const entityB = world.entity('Movable')
console.assert(entityB.has('position'))
console.assert(entityB.has('velocity'))

// This will re-create the component using the constructor
entityB.set('position', 100, 100)

// This set a property in the existing component
entityA.get('position').x = 100

// Set velocities by using update()
entityA.update('velocity', { x: 10, y: 10 })
entityB.update('velocity', { x: -10, y: -10 })

// Run systems (pass one second for dt)
world.run(1.0)

// Since the movement system ran once, the positions changed by the amount of their velocity
console.assert(entityA.get('position').x === 110)
console.assert(entityA.get('position').y === 10)
console.assert(entityB.get('position').x === 90)
console.assert(entityB.get('position').y === 90)
```

Expected output:

```
init() called with args: extra args
run(1) called
each() called for entity 1
each() called for entity 2
```
More complete examples coming with final 1.0.0 release! For now, refer to the [full documentation](https://ayebear.com/picoes).
5 changes: 2 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
exports.World = require('./src/world.js').World
exports.SimpleIndex = require('./src/simple_index.js').SimpleIndex
exports.MemoizedQueryIndex = require('./src/memoized_query_index.js').MemoizedQueryIndex
import { World } from './src/world.js'
export { World }
30 changes: 21 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
{
"name": "picoes",
"version": "1.0.0-alpha4",
"description": "Pico Entity System for JavaScript (ES6).",
"main": "./index.js",
"version": "1.0.0-alpha7",
"description": "Pico Entity System for JavaScript",
"main": "index.js",
"files": [
"src",
"index.js"
],
"scripts": {
"test": "jest --coverage",
"doc": "node ./node_modules/.bin/esdoc",
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage",
"doc": "node --experimental-vm-modules ./node_modules/.bin/esdoc",
"deploy": "gh-pages -d docs"
},
"repository": {
Expand All @@ -17,8 +21,7 @@
"component",
"system",
"ecs",
"picoes",
"es6"
"picoes"
],
"author": "Eric Hebert",
"license": "MIT",
Expand All @@ -34,7 +37,16 @@
"esdoc-undocumented-identifier-plugin": "^1.0.0",
"esdoc-unexported-identifier-plugin": "^1.0.0",
"gh-pages": "^3.1.0",
"jest": "^26.6.3"
"husky": "^6.0.0",
"jest": "^26.6.3",
"prettier": "2.2.1",
"pretty-quick": "^3.1.0"
},
"dependencies": {}
"dependencies": {},
"type": "module",
"husky": {
"hooks": {
"pre-commit": "pretty-quick --staged"
}
}
}
Loading

0 comments on commit 00a41ca

Please sign in to comment.