@inglorious/engine 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -10
- package/package.json +4 -6
- package/src/behaviors/audio.js +30 -3
- package/src/core/engine.js +19 -12
- package/src/main.js +3 -3
package/README.md
CHANGED
|
@@ -31,9 +31,9 @@ npm install @inglorious/engine
|
|
|
31
31
|
|
|
32
32
|
## API
|
|
33
33
|
|
|
34
|
-
### `new Engine(
|
|
34
|
+
### `new Engine(...gameConfigs)`
|
|
35
35
|
|
|
36
|
-
Creates a new `Engine` instance.
|
|
36
|
+
Creates a new `Engine` instance, given one or more configuration objects.
|
|
37
37
|
|
|
38
38
|
**Parameters:**
|
|
39
39
|
|
|
@@ -41,10 +41,9 @@ Creates a new `Engine` instance.
|
|
|
41
41
|
- `loop` (object, optional): Configuration for the game loop.
|
|
42
42
|
- `type` (string, optional): The type of loop to use (`animationFrame` or `fixed`). Defaults to `animationFrame`.
|
|
43
43
|
- `fps` (number, optional): The target frames per second. Only used with the `fixed` loop type. Defaults to `60`.
|
|
44
|
-
- `systems` (array, optional): An array of system objects, which define behaviors for the whole state.
|
|
45
44
|
- `types` (object, optional): A map of entity types.
|
|
46
45
|
- `entities` (object, optional): A map of initial entities.
|
|
47
|
-
- `
|
|
46
|
+
- `systems` (array, optional): An array of system objects, which define behaviors for the whole state.
|
|
48
47
|
|
|
49
48
|
**Returns:**
|
|
50
49
|
|
|
@@ -81,11 +80,11 @@ Here is a complete example showing how to set up and run a game using the engine
|
|
|
81
80
|
<script type="importmap">
|
|
82
81
|
{
|
|
83
82
|
"imports": {
|
|
84
|
-
"immer": "https://unpkg.com/immer@
|
|
85
|
-
"@inglorious/utils/": "https://unpkg.com/@inglorious%2Futils@
|
|
86
|
-
"@inglorious/store/": "https://unpkg.com/@inglorious%2Fstore@
|
|
87
|
-
"@inglorious/engine/": "https://unpkg.com/@inglorious%2Fengine@
|
|
88
|
-
"@inglorious/renderers/": "https://unpkg.com/@inglorious%2Frenderer-2d@
|
|
83
|
+
"immer": "https://unpkg.com/immer@latest/dist/immer.mjs",
|
|
84
|
+
"@inglorious/utils/": "https://unpkg.com/@inglorious%2Futils@latest/src/",
|
|
85
|
+
"@inglorious/store/": "https://unpkg.com/@inglorious%2Fstore@latest/src/",
|
|
86
|
+
"@inglorious/engine/": "https://unpkg.com/@inglorious%2Fengine@latest/src/",
|
|
87
|
+
"@inglorious/renderers/": "https://unpkg.com/@inglorious%2Frenderer-2d@latest/src/",
|
|
89
88
|
"game": "/game.js"
|
|
90
89
|
}
|
|
91
90
|
}
|
|
@@ -93,7 +92,7 @@ Here is a complete example showing how to set up and run a game using the engine
|
|
|
93
92
|
|
|
94
93
|
<script
|
|
95
94
|
type="module"
|
|
96
|
-
src="https://unpkg.com/@inglorious%2Fengine@
|
|
95
|
+
src="https://unpkg.com/@inglorious%2Fengine@latest/src/main.js"
|
|
97
96
|
></script>
|
|
98
97
|
</body>
|
|
99
98
|
</html>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inglorious/engine",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "A JavaScript game engine written with global state, immutability, and pure functions in mind. Have fun(ctional programming) with it!",
|
|
5
5
|
"author": "IceOnFire <antony.mistretta@gmail.com> (https://ingloriouscoderz.it)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -24,16 +24,14 @@
|
|
|
24
24
|
"./*": "./src/*"
|
|
25
25
|
},
|
|
26
26
|
"files": [
|
|
27
|
-
"src"
|
|
28
|
-
"README.md",
|
|
29
|
-
"LICENSE"
|
|
27
|
+
"src"
|
|
30
28
|
],
|
|
31
29
|
"publishConfig": {
|
|
32
30
|
"access": "public"
|
|
33
31
|
},
|
|
34
32
|
"dependencies": {
|
|
35
|
-
"@inglorious/
|
|
36
|
-
"@inglorious/
|
|
33
|
+
"@inglorious/store": "3.0.0",
|
|
34
|
+
"@inglorious/utils": "2.1.0"
|
|
37
35
|
},
|
|
38
36
|
"devDependencies": {
|
|
39
37
|
"prettier": "^3.5.3",
|
package/src/behaviors/audio.js
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
|
+
const DEFAULT_VOLUME = 1
|
|
2
|
+
|
|
1
3
|
export function audio() {
|
|
2
4
|
const audioContext = new (window.AudioContext || window.webkitAudioContext)()
|
|
5
|
+
|
|
3
6
|
const audioBufferCache = new Map()
|
|
7
|
+
const activeSources = new Map()
|
|
4
8
|
|
|
5
9
|
return {
|
|
6
10
|
async init(entity) {
|
|
7
11
|
const sounds = entity.sounds || {}
|
|
8
12
|
|
|
9
13
|
await Promise.all(
|
|
10
|
-
Object.entries(sounds).map(async ([name, url]) => {
|
|
14
|
+
Object.entries(sounds).map(async ([name, { url }]) => {
|
|
11
15
|
const response = await fetch(url)
|
|
12
16
|
const arrayBuffer = await response.arrayBuffer()
|
|
13
17
|
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer)
|
|
@@ -16,13 +20,36 @@ export function audio() {
|
|
|
16
20
|
)
|
|
17
21
|
},
|
|
18
22
|
|
|
19
|
-
|
|
23
|
+
soundPlay(entity, name) {
|
|
24
|
+
const { volume = DEFAULT_VOLUME, loop } = entity.sounds[name]
|
|
20
25
|
const audioBuffer = audioBufferCache.get(name)
|
|
21
26
|
|
|
22
27
|
const source = audioContext.createBufferSource()
|
|
28
|
+
const gainNode = audioContext.createGain()
|
|
29
|
+
|
|
23
30
|
source.buffer = audioBuffer
|
|
24
|
-
|
|
31
|
+
gainNode.gain.value = volume
|
|
32
|
+
|
|
33
|
+
source.connect(gainNode)
|
|
34
|
+
gainNode.connect(audioContext.destination)
|
|
35
|
+
|
|
36
|
+
source.loop = loop
|
|
25
37
|
source.start()
|
|
38
|
+
|
|
39
|
+
activeSources.set(name, source)
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
soundStop(entity, name) {
|
|
43
|
+
const source = activeSources.get(name)
|
|
44
|
+
source?.stop()
|
|
45
|
+
activeSources.delete(name)
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
stop() {
|
|
49
|
+
for (const source of activeSources.values()) {
|
|
50
|
+
source.stop()
|
|
51
|
+
}
|
|
52
|
+
activeSources.clear()
|
|
26
53
|
},
|
|
27
54
|
}
|
|
28
55
|
}
|
package/src/core/engine.js
CHANGED
|
@@ -2,7 +2,9 @@ import { audio } from "@inglorious/engine/behaviors/audio.js"
|
|
|
2
2
|
import { game } from "@inglorious/engine/behaviors/game.js"
|
|
3
3
|
import { createApi } from "@inglorious/store/api.js"
|
|
4
4
|
import { createStore } from "@inglorious/store/store.js"
|
|
5
|
-
import {
|
|
5
|
+
import { augmentType } from "@inglorious/store/types.js"
|
|
6
|
+
import { isArray } from "@inglorious/utils/data-structures/array.js"
|
|
7
|
+
import { extendWith } from "@inglorious/utils/data-structures/objects.js"
|
|
6
8
|
|
|
7
9
|
import { coreEvents } from "./core-events.js"
|
|
8
10
|
import { disconnectDevTools, initDevTools, sendAction } from "./dev-tools.js"
|
|
@@ -21,7 +23,7 @@ const DEFAULT_GAME_CONFIG = {
|
|
|
21
23
|
|
|
22
24
|
types: {
|
|
23
25
|
game: [game()],
|
|
24
|
-
audio: audio(),
|
|
26
|
+
audio: [audio()],
|
|
25
27
|
},
|
|
26
28
|
|
|
27
29
|
entities: {
|
|
@@ -41,8 +43,8 @@ export class Engine {
|
|
|
41
43
|
* @param {Object} [gameConfig] - Game-specific configuration.
|
|
42
44
|
* @param {Object} [renderer] - UI entity responsible for rendering. It must have a `render` method.
|
|
43
45
|
*/
|
|
44
|
-
constructor(
|
|
45
|
-
this._config =
|
|
46
|
+
constructor(...gameConfigs) {
|
|
47
|
+
this._config = extendWith(merger, DEFAULT_GAME_CONFIG, ...gameConfigs)
|
|
46
48
|
|
|
47
49
|
// Determine devMode from the entities config.
|
|
48
50
|
const devMode = this._config.entities.game?.devMode
|
|
@@ -50,9 +52,6 @@ export class Engine {
|
|
|
50
52
|
|
|
51
53
|
// Add user-defined systems
|
|
52
54
|
const systems = [...(this._config.systems ?? [])]
|
|
53
|
-
if (this._config.renderer) {
|
|
54
|
-
systems.push(...this._config.renderer.getSystems())
|
|
55
|
-
}
|
|
56
55
|
|
|
57
56
|
this._store = createStore({ ...this._config, systems })
|
|
58
57
|
|
|
@@ -86,10 +85,13 @@ export class Engine {
|
|
|
86
85
|
}
|
|
87
86
|
|
|
88
87
|
async init() {
|
|
89
|
-
return Promise.all(
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
88
|
+
return Promise.all(
|
|
89
|
+
Object.values(this._config.entities).map((entity) => {
|
|
90
|
+
const originalType = this._config.types[entity.type]
|
|
91
|
+
const type = augmentType(originalType)
|
|
92
|
+
return type.init?.(entity, null, this._api)
|
|
93
|
+
}),
|
|
94
|
+
)
|
|
93
95
|
}
|
|
94
96
|
|
|
95
97
|
/**
|
|
@@ -108,7 +110,6 @@ export class Engine {
|
|
|
108
110
|
this._api.notify("stop")
|
|
109
111
|
this._store.update(this._api)
|
|
110
112
|
this._loop.stop()
|
|
111
|
-
this._config.renderer?.destroy()
|
|
112
113
|
this.isRunning = false
|
|
113
114
|
}
|
|
114
115
|
|
|
@@ -145,3 +146,9 @@ export class Engine {
|
|
|
145
146
|
}
|
|
146
147
|
}
|
|
147
148
|
}
|
|
149
|
+
|
|
150
|
+
function merger(targetValue, sourceValue) {
|
|
151
|
+
if (isArray(targetValue) && isArray(sourceValue)) {
|
|
152
|
+
return [...targetValue, ...sourceValue]
|
|
153
|
+
}
|
|
154
|
+
}
|
package/src/main.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Engine } from "@inglorious/engine/core/engine.js"
|
|
2
|
-
import {
|
|
2
|
+
import { createRenderer } from "@inglorious/renderer-2d/index.js"
|
|
3
3
|
import game from "game"
|
|
4
4
|
|
|
5
5
|
const canvas = document.getElementById("canvas")
|
|
6
6
|
window.addEventListener("load", async () => {
|
|
7
|
-
const renderer =
|
|
8
|
-
const engine = new Engine(
|
|
7
|
+
const renderer = createRenderer(canvas)
|
|
8
|
+
const engine = new Engine(renderer, game)
|
|
9
9
|
await engine.init()
|
|
10
10
|
engine.start()
|
|
11
11
|
})
|