@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 CHANGED
@@ -31,9 +31,9 @@ npm install @inglorious/engine
31
31
 
32
32
  ## API
33
33
 
34
- ### `new Engine(gameConfig)`
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
- - `renderer` (object, optional): A renderer entity responsible for drawing the game. It must implement `getSystems()` and `init(engine)` methods.
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@10.1.1/dist/immer.mjs",
85
- "@inglorious/utils/": "https://unpkg.com/@inglorious%2Futils@1.1.0/",
86
- "@inglorious/store/": "https://unpkg.com/@inglorious%2Fstore@2.0.0/",
87
- "@inglorious/engine/": "https://unpkg.com/@inglorious%2Fengine@0.7.0/",
88
- "@inglorious/renderers/": "https://unpkg.com/@inglorious%2Frenderer-2d@0.2.0/",
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@0.7.0/main.js"
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.7.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/utils": "1.1.0",
36
- "@inglorious/store": "2.0.0"
33
+ "@inglorious/store": "3.0.0",
34
+ "@inglorious/utils": "2.1.0"
37
35
  },
38
36
  "devDependencies": {
39
37
  "prettier": "^3.5.3",
@@ -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
- playSound(entity, name) {
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
- source.connect(audioContext.destination)
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
  }
@@ -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 { extend } from "@inglorious/utils/data-structures/objects.js"
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(gameConfig) {
45
- this._config = extend(DEFAULT_GAME_CONFIG, gameConfig)
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
- this._config.types.audio.init(this._config.entities.audio),
91
- this._config.renderer?.init(this),
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 { Renderer2D } from "@inglorious/renderer-2d/index.js"
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 = new Renderer2D(canvas)
8
- const engine = new Engine({ ...game, renderer })
7
+ const renderer = createRenderer(canvas)
8
+ const engine = new Engine(renderer, game)
9
9
  await engine.init()
10
10
  engine.start()
11
11
  })