@inglorious/engine 7.0.0 → 8.0.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 +115 -115
- package/package.json +4 -4
- package/src/behaviors/controls/dynamic/modern.js +39 -40
- package/src/behaviors/controls/dynamic/shooter.js +52 -53
- package/src/behaviors/controls/dynamic/tank.js +45 -46
- package/src/behaviors/controls/kinematic/modern.js +39 -40
- package/src/behaviors/controls/kinematic/shooter.js +51 -52
- package/src/behaviors/controls/kinematic/tank.js +44 -45
- package/src/behaviors/debug/collision.js +18 -21
- package/src/behaviors/fsm.js +2 -4
- package/src/behaviors/physics/bouncy.js +13 -14
- package/src/behaviors/physics/clamped.js +21 -22
- package/src/behaviors/physics/jumpable.js +108 -111
- package/src/core/engine.js +6 -5
- package/src/core/middlewares/entity-pool/entity-pool-middleware.js +9 -5
|
@@ -21,126 +21,123 @@ const NO_PENETRAION = 0
|
|
|
21
21
|
export function jumpable(params) {
|
|
22
22
|
params = extend(DEFAULT_PARAMS, params)
|
|
23
23
|
|
|
24
|
-
return (type) =>
|
|
25
|
-
|
|
26
|
-
create(entity, entityId, api)
|
|
27
|
-
type.create?.(entity, entityId, api)
|
|
24
|
+
return (type) => ({
|
|
25
|
+
create(entity, entityId, api) {
|
|
26
|
+
type.create?.(entity, entityId, api)
|
|
28
27
|
|
|
29
|
-
|
|
28
|
+
if (entityId !== entity.id) return
|
|
30
29
|
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
defaults(entity, params)
|
|
31
|
+
entity.jumpsLeft ??= entity.maxJumps
|
|
33
32
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
entity.velocity ??= zero()
|
|
34
|
+
entity.vy ??= 0
|
|
35
|
+
},
|
|
37
36
|
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
jump(entity, entityId, api) {
|
|
38
|
+
type.jump?.(entity, entityId, api)
|
|
40
39
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
40
|
+
if (entityId === entity.id && entity.jumpsLeft) {
|
|
41
|
+
entity.vy = jump(entity)
|
|
42
|
+
entity.groundObject = undefined
|
|
43
|
+
entity.jumpsLeft--
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
update(entity, dt, api) {
|
|
48
|
+
type.update?.(entity, dt, api)
|
|
49
|
+
|
|
50
|
+
const entities = api.getEntities()
|
|
51
|
+
|
|
52
|
+
entity.collisions ??= {}
|
|
53
|
+
entity.collisions.platform ??= {}
|
|
54
|
+
entity.collisions.platform.shape ??= "rectangle"
|
|
55
|
+
|
|
56
|
+
let width, height
|
|
57
|
+
if (entity.collisions.platform.shape === "circle") {
|
|
58
|
+
width = entity.collisions.platform.radius * DOUBLE
|
|
59
|
+
height = entity.collisions.platform.radius * DOUBLE
|
|
60
|
+
} else {
|
|
61
|
+
;[width, height] = entity.size
|
|
62
|
+
}
|
|
63
|
+
const [prevX, prevY] = [...entity.position]
|
|
64
|
+
|
|
65
|
+
// 1. HORIZONTAL MOVEMENT & RESOLUTION
|
|
66
|
+
entity.position[X] += entity.velocity[X] * dt
|
|
67
|
+
const entityLeft = entity.position[X] - width / HALF
|
|
68
|
+
const entityRight = entity.position[X] + width / HALF
|
|
69
|
+
|
|
70
|
+
const collisionX = findCollision(entity, entities, "platform")
|
|
71
|
+
if (collisionX) {
|
|
72
|
+
const vx = entity.velocity[X]
|
|
73
|
+
|
|
74
|
+
// Check if moving right and crossing the platform's left edge
|
|
75
|
+
const prevRight = prevX + width / HALF
|
|
76
|
+
const platformLeft = collisionX.position[X] - collisionX.size[X] / HALF
|
|
77
|
+
if (vx > NO_VELOCITY && prevRight <= platformLeft) {
|
|
78
|
+
const penetration = entityRight - platformLeft
|
|
79
|
+
if (penetration > NO_PENETRAION) entity.position[X] -= penetration
|
|
80
|
+
entity.velocity[X] = 0
|
|
63
81
|
}
|
|
64
|
-
const [prevX, prevY] = [...entity.position]
|
|
65
|
-
|
|
66
|
-
// 1. HORIZONTAL MOVEMENT & RESOLUTION
|
|
67
|
-
entity.position[X] += entity.velocity[X] * dt
|
|
68
|
-
const entityLeft = entity.position[X] - width / HALF
|
|
69
|
-
const entityRight = entity.position[X] + width / HALF
|
|
70
|
-
|
|
71
|
-
const collisionX = findCollision(entity, entities, "platform")
|
|
72
|
-
if (collisionX) {
|
|
73
|
-
const vx = entity.velocity[X]
|
|
74
|
-
|
|
75
|
-
// Check if moving right and crossing the platform's left edge
|
|
76
|
-
const prevRight = prevX + width / HALF
|
|
77
|
-
const platformLeft =
|
|
78
|
-
collisionX.position[X] - collisionX.size[X] / HALF
|
|
79
|
-
if (vx > NO_VELOCITY && prevRight <= platformLeft) {
|
|
80
|
-
const penetration = entityRight - platformLeft
|
|
81
|
-
if (penetration > NO_PENETRAION) entity.position[X] -= penetration
|
|
82
|
-
entity.velocity[X] = 0
|
|
83
|
-
}
|
|
84
82
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
entity.velocity[X] = 0
|
|
93
|
-
}
|
|
83
|
+
// Check if moving left and crossing the platform's right edge
|
|
84
|
+
const prevLeft = prevX - width / HALF
|
|
85
|
+
const platformRight = collisionX.position[X] + collisionX.size[X] / HALF
|
|
86
|
+
if (vx < NO_VELOCITY && prevLeft >= platformRight) {
|
|
87
|
+
const penetration = platformRight - entityLeft
|
|
88
|
+
if (penetration > NO_PENETRAION) entity.position[X] += penetration
|
|
89
|
+
entity.velocity[X] = 0
|
|
94
90
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// 2. VERTICAL MOVEMENT & RESOLUTION
|
|
94
|
+
const wasOnGround = entity.groundObject
|
|
95
|
+
entity.groundObject = undefined
|
|
96
|
+
|
|
97
|
+
const { vy, position: nextGravityPosition } = applyGravity(entity, dt)
|
|
98
|
+
entity.vy = vy
|
|
99
|
+
entity.position[Y] = nextGravityPosition[Y]
|
|
100
|
+
|
|
101
|
+
const collisionY = findCollision(entity, entities, "platform")
|
|
102
|
+
if (collisionY) {
|
|
103
|
+
const prevBottom = prevY - height / HALF
|
|
104
|
+
const platformTop = collisionY.position[Y] + collisionY.size[Y] / HALF
|
|
105
|
+
|
|
106
|
+
// Landing on top of a platform (one-way platform logic)
|
|
107
|
+
if (entity.vy <= NO_VELOCITY && prevBottom >= platformTop) {
|
|
108
|
+
const entityBottom = entity.position[Y] - height / HALF
|
|
109
|
+
const penetration = platformTop - entityBottom
|
|
110
|
+
if (penetration > NO_PENETRAION) entity.position[Y] += penetration
|
|
111
|
+
entity.vy = 0
|
|
112
|
+
entity.groundObject = collisionY
|
|
113
|
+
entity.jumpsLeft = entity.maxJumps
|
|
114
|
+
|
|
115
|
+
// Only notify on the frame we actually land, not every frame we're on the ground.
|
|
116
|
+
if (!wasOnGround) {
|
|
117
|
+
api.notify("land", {
|
|
118
|
+
entityId: entity.id,
|
|
119
|
+
targetId: collisionY.id,
|
|
120
|
+
})
|
|
125
121
|
}
|
|
126
|
-
|
|
127
|
-
// Hitting head on bottom of a platform
|
|
128
|
-
// else if (entity.vy > 0) {
|
|
129
|
-
// const prevTop = prevY + height / HALF
|
|
130
|
-
// const platformBottom =
|
|
131
|
-
// collisionY.position[Y] - collisionY.size[Y] / HALF
|
|
132
|
-
// if (prevTop <= platformBottom) {
|
|
133
|
-
// const entityTop = entity.position[Y] + height / HALF
|
|
134
|
-
// const penetration = entityTop - platformBottom
|
|
135
|
-
// if (penetration > 0) entity.position[Y] -= penetration
|
|
136
|
-
// entity.vy = 0
|
|
137
|
-
// }
|
|
138
|
-
// }
|
|
139
122
|
}
|
|
140
123
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
124
|
+
// Hitting head on bottom of a platform
|
|
125
|
+
// else if (entity.vy > 0) {
|
|
126
|
+
// const prevTop = prevY + height / HALF
|
|
127
|
+
// const platformBottom =
|
|
128
|
+
// collisionY.position[Y] - collisionY.size[Y] / HALF
|
|
129
|
+
// if (prevTop <= platformBottom) {
|
|
130
|
+
// const entityTop = entity.position[Y] + height / HALF
|
|
131
|
+
// const penetration = entityTop - platformBottom
|
|
132
|
+
// if (penetration > 0) entity.position[Y] -= penetration
|
|
133
|
+
// entity.vy = 0
|
|
134
|
+
// }
|
|
135
|
+
// }
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
entity.orientation = magnitude(entity.velocity)
|
|
139
|
+
? angle(entity.velocity)
|
|
140
|
+
: entity.orientation
|
|
141
|
+
},
|
|
142
|
+
})
|
|
146
143
|
}
|
package/src/core/engine.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { audio } from "@inglorious/engine/behaviors/audio.js"
|
|
2
2
|
import { game } from "@inglorious/engine/behaviors/game.js"
|
|
3
3
|
import { images } from "@inglorious/engine/behaviors/images.js"
|
|
4
|
+
import { createApi } from "@inglorious/store/api.js"
|
|
4
5
|
import {
|
|
5
6
|
connectDevTools,
|
|
6
7
|
disconnectDevTools,
|
|
@@ -69,7 +70,6 @@ export class Engine {
|
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
this._store = createStore({ ...this._config, middlewares, mode: "batched" })
|
|
72
|
-
this._api = this._store.getApi()
|
|
73
73
|
this._loop = new Loop[this._config.loop.type]()
|
|
74
74
|
|
|
75
75
|
if (this._devMode) {
|
|
@@ -78,11 +78,12 @@ export class Engine {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
async init() {
|
|
81
|
+
const api = createApi(this._store)
|
|
81
82
|
return Promise.all(
|
|
82
83
|
Object.values(this._config.entities).map((entity) => {
|
|
83
84
|
const originalType = this._config.types[entity.type]
|
|
84
85
|
const type = augmentType(originalType)
|
|
85
|
-
return type.init?.(entity, null,
|
|
86
|
+
return type.init?.(entity, null, api)
|
|
86
87
|
}),
|
|
87
88
|
)
|
|
88
89
|
}
|
|
@@ -91,7 +92,7 @@ export class Engine {
|
|
|
91
92
|
* Starts the game engine, initializing the loop and notifying the store.
|
|
92
93
|
*/
|
|
93
94
|
start() {
|
|
94
|
-
this.
|
|
95
|
+
this._store.notify("start")
|
|
95
96
|
this._loop.start(this, ONE_SECOND / this._config.loop.fps)
|
|
96
97
|
}
|
|
97
98
|
|
|
@@ -99,7 +100,7 @@ export class Engine {
|
|
|
99
100
|
* Stops the game engine, halting the loop and notifying the store.
|
|
100
101
|
*/
|
|
101
102
|
stop() {
|
|
102
|
-
this.
|
|
103
|
+
this._store.notify("stop")
|
|
103
104
|
this._store.update()
|
|
104
105
|
this._loop.stop()
|
|
105
106
|
}
|
|
@@ -109,7 +110,7 @@ export class Engine {
|
|
|
109
110
|
* @param {number} dt - Delta time since the last update in milliseconds.
|
|
110
111
|
*/
|
|
111
112
|
update(dt) {
|
|
112
|
-
this.
|
|
113
|
+
this._store.notify("update", dt)
|
|
113
114
|
const processedEvents = this._store.update()
|
|
114
115
|
const entities = this._store.getState()
|
|
115
116
|
|
|
@@ -1,20 +1,24 @@
|
|
|
1
|
+
import { createApi } from "@inglorious/store/api.js"
|
|
1
2
|
import { EventMap } from "@inglorious/store/event-map.js"
|
|
2
3
|
|
|
3
4
|
import { EntityPools } from "./entity-pools"
|
|
4
5
|
|
|
5
6
|
export function entityPoolMiddleware() {
|
|
6
|
-
return (store
|
|
7
|
+
return (store) => {
|
|
7
8
|
const pools = new EntityPools()
|
|
8
|
-
const types =
|
|
9
|
+
const types = store.getTypes()
|
|
9
10
|
const eventMap = new EventMap()
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
store.extras ??= {}
|
|
13
|
+
store.extras.getAllActivePoolEntities = () => pools.getAllActiveEntities()
|
|
12
14
|
|
|
13
|
-
const game =
|
|
15
|
+
const game = store.getState().game
|
|
14
16
|
if (game.devMode) {
|
|
15
|
-
|
|
17
|
+
store.extras.getEntityPoolsStats = () => pools.getStats()
|
|
16
18
|
}
|
|
17
19
|
|
|
20
|
+
const api = createApi(store)
|
|
21
|
+
|
|
18
22
|
return (next) => (event) => {
|
|
19
23
|
switch (event.type) {
|
|
20
24
|
case "spawn": {
|