@inglorious/engine 0.1.0 → 0.1.1
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/package.json +11 -12
- package/src/docs/ai/movement/dynamic/align.js +0 -131
- package/src/docs/ai/movement/dynamic/arrive.js +0 -88
- package/src/docs/ai/movement/dynamic/dynamic.mdx +0 -99
- package/src/docs/ai/movement/dynamic/dynamic.stories.js +0 -58
- package/src/docs/ai/movement/dynamic/evade.js +0 -72
- package/src/docs/ai/movement/dynamic/face.js +0 -90
- package/src/docs/ai/movement/dynamic/flee.js +0 -38
- package/src/docs/ai/movement/dynamic/look-where-youre-going.js +0 -114
- package/src/docs/ai/movement/dynamic/match-velocity.js +0 -92
- package/src/docs/ai/movement/dynamic/pursue.js +0 -72
- package/src/docs/ai/movement/dynamic/seek.js +0 -37
- package/src/docs/ai/movement/dynamic/wander.js +0 -71
- package/src/docs/ai/movement/kinematic/align.js +0 -122
- package/src/docs/ai/movement/kinematic/arrive.js +0 -78
- package/src/docs/ai/movement/kinematic/face.js +0 -82
- package/src/docs/ai/movement/kinematic/flee.js +0 -36
- package/src/docs/ai/movement/kinematic/kinematic.mdx +0 -67
- package/src/docs/ai/movement/kinematic/kinematic.stories.js +0 -42
- package/src/docs/ai/movement/kinematic/seek.js +0 -34
- package/src/docs/ai/movement/kinematic/wander-as-seek.js +0 -62
- package/src/docs/ai/movement/kinematic/wander.js +0 -28
- package/src/docs/bounds.js +0 -7
- package/src/docs/code-reuse.js +0 -35
- package/src/docs/collision/circles.js +0 -58
- package/src/docs/collision/collision.mdx +0 -27
- package/src/docs/collision/collision.stories.js +0 -22
- package/src/docs/collision/platform.js +0 -76
- package/src/docs/collision/tilemap.js +0 -181
- package/src/docs/empty.js +0 -1
- package/src/docs/engine.mdx +0 -81
- package/src/docs/engine.stories.js +0 -37
- package/src/docs/event-handlers.js +0 -68
- package/src/docs/framerate.js +0 -37
- package/src/docs/game.jsx +0 -15
- package/src/docs/image/image.js +0 -19
- package/src/docs/image/image.stories.js +0 -22
- package/src/docs/image/sprite.js +0 -39
- package/src/docs/image/tilemap.js +0 -84
- package/src/docs/input/controls.js +0 -67
- package/src/docs/input/gamepad.js +0 -67
- package/src/docs/input/input.mdx +0 -55
- package/src/docs/input/input.stories.js +0 -27
- package/src/docs/input/keyboard.js +0 -58
- package/src/docs/input/mouse.js +0 -32
- package/src/docs/instances.js +0 -49
- package/src/docs/player/dynamic/double-jump.js +0 -90
- package/src/docs/player/dynamic/dynamic.stories.js +0 -32
- package/src/docs/player/dynamic/jump.js +0 -83
- package/src/docs/player/dynamic/modern-controls.js +0 -57
- package/src/docs/player/dynamic/shooter-controls.js +0 -51
- package/src/docs/player/dynamic/tank-controls.js +0 -44
- package/src/docs/player/kinematic/double-jump.js +0 -90
- package/src/docs/player/kinematic/jump.js +0 -82
- package/src/docs/player/kinematic/kinematic.stories.js +0 -32
- package/src/docs/player/kinematic/modern-controls.js +0 -56
- package/src/docs/player/kinematic/shooter-controls.js +0 -48
- package/src/docs/player/kinematic/tank-controls.js +0 -42
- package/src/docs/quick-start/first-game.js +0 -49
- package/src/docs/quick-start/hello-world.js +0 -1
- package/src/docs/quick-start.mdx +0 -127
- package/src/docs/quick-start.stories.js +0 -17
- package/src/docs/recipes/add-and-remove.js +0 -71
- package/src/docs/recipes/add-instance.js +0 -42
- package/src/docs/recipes/decision-tree.js +0 -169
- package/src/docs/recipes/random-instances.js +0 -25
- package/src/docs/recipes/recipes.mdx +0 -81
- package/src/docs/recipes/recipes.stories.js +0 -37
- package/src/docs/recipes/remove-instance.js +0 -52
- package/src/docs/recipes/states.js +0 -64
- package/src/docs/ui/button.js +0 -28
- package/src/docs/ui/form.stories.js +0 -55
- package/src/docs/ui-chooser.jsx +0 -6
- package/src/docs/utils/data-structures/object.mdx +0 -47
- package/src/docs/utils/data-structures/objects.mdx +0 -30
- package/src/docs/utils/functions/functions.mdx +0 -34
- package/src/docs/utils/math/geometry/circle.mdx +0 -55
- package/src/docs/utils/math/geometry/point.mdx +0 -38
- package/src/docs/utils/math/geometry/rectangle.mdx +0 -24
- package/src/docs/utils/math/geometry/segment.mdx +0 -55
- package/src/docs/utils/math/geometry/triangle.mdx +0 -22
- package/src/docs/utils/math/linear-algebra/2d.mdx +0 -22
- package/src/docs/utils/math/linear-algebra/quaternion.mdx +0 -21
- package/src/docs/utils/math/linear-algebra/quaternions.mdx +0 -22
- package/src/docs/utils/math/linear-algebra/vector.mdx +0 -177
- package/src/docs/utils/math/linear-algebra/vectors.mdx +0 -58
- package/src/docs/utils/math/numbers.mdx +0 -76
- package/src/docs/utils/math/random.mdx +0 -35
- package/src/docs/utils/math/statistics.mdx +0 -38
- package/src/docs/utils/math/trigonometry.mdx +0 -85
- package/src/docs/utils/physics/friction.mdx +0 -20
- package/src/docs/utils/physics/gravity.mdx +0 -28
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { enableCharacter } from "@inglorious/game/decorators/character.js"
|
|
2
|
-
import { enableClampToBounds } from "@inglorious/game/decorators/clamp-to-bounds.js"
|
|
3
|
-
import { enableShooterControls } from "@inglorious/game/decorators/controls/kinematic/shooter.js"
|
|
4
|
-
import {
|
|
5
|
-
createControls,
|
|
6
|
-
enableControls,
|
|
7
|
-
} from "@inglorious/game/decorators/input/controls.js"
|
|
8
|
-
import { enableMouse } from "@inglorious/game/decorators/input/mouse.js"
|
|
9
|
-
import { pi } from "@inglorious/utils/math/trigonometry.js"
|
|
10
|
-
|
|
11
|
-
export default {
|
|
12
|
-
types: {
|
|
13
|
-
mouse: [enableMouse()],
|
|
14
|
-
|
|
15
|
-
...enableControls(),
|
|
16
|
-
|
|
17
|
-
character: [
|
|
18
|
-
enableCharacter(),
|
|
19
|
-
enableShooterControls(),
|
|
20
|
-
enableClampToBounds(),
|
|
21
|
-
],
|
|
22
|
-
},
|
|
23
|
-
|
|
24
|
-
instances: {
|
|
25
|
-
mouse: {
|
|
26
|
-
type: "mouse",
|
|
27
|
-
position: [400, 0, 300],
|
|
28
|
-
},
|
|
29
|
-
|
|
30
|
-
...createControls("input0", {
|
|
31
|
-
ArrowLeft: "left",
|
|
32
|
-
ArrowRight: "right",
|
|
33
|
-
ArrowDown: "down",
|
|
34
|
-
ArrowUp: "up",
|
|
35
|
-
KeyA: "left",
|
|
36
|
-
KeyD: "right",
|
|
37
|
-
KeyS: "down",
|
|
38
|
-
KeyW: "up",
|
|
39
|
-
}),
|
|
40
|
-
|
|
41
|
-
character: {
|
|
42
|
-
type: "character",
|
|
43
|
-
maxAngularSpeed: 2 * pi(),
|
|
44
|
-
maxSpeed: 250,
|
|
45
|
-
position: [400, 0, 300],
|
|
46
|
-
},
|
|
47
|
-
},
|
|
48
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { enableCharacter } from "@inglorious/game/decorators/character.js"
|
|
2
|
-
import { enableClampToBounds } from "@inglorious/game/decorators/clamp-to-bounds"
|
|
3
|
-
import { enableTankControls } from "@inglorious/game/decorators/controls/kinematic/tank.js"
|
|
4
|
-
import {
|
|
5
|
-
createControls,
|
|
6
|
-
enableControls,
|
|
7
|
-
} from "@inglorious/game/decorators/input/controls.js"
|
|
8
|
-
|
|
9
|
-
export default {
|
|
10
|
-
types: {
|
|
11
|
-
...enableControls(),
|
|
12
|
-
|
|
13
|
-
character: [enableCharacter(), enableTankControls(), enableClampToBounds()],
|
|
14
|
-
},
|
|
15
|
-
|
|
16
|
-
instances: {
|
|
17
|
-
...createControls("input0", {
|
|
18
|
-
ArrowUp: "up",
|
|
19
|
-
ArrowDown: "down",
|
|
20
|
-
ArrowLeft: "left",
|
|
21
|
-
ArrowRight: "right",
|
|
22
|
-
KeyW: "up",
|
|
23
|
-
KeyS: "down",
|
|
24
|
-
KeyA: "left",
|
|
25
|
-
KeyD: "right",
|
|
26
|
-
Btn12: "up",
|
|
27
|
-
Btn13: "down",
|
|
28
|
-
Btn14: "left",
|
|
29
|
-
Btn15: "right",
|
|
30
|
-
Axis0: "strafe",
|
|
31
|
-
Axis1: "upDown",
|
|
32
|
-
Axis2: "leftRight",
|
|
33
|
-
}),
|
|
34
|
-
|
|
35
|
-
character: {
|
|
36
|
-
type: "character",
|
|
37
|
-
maxAngularSpeed: 10,
|
|
38
|
-
maxSpeed: 250,
|
|
39
|
-
position: [400, 0, 300],
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
export default {
|
|
2
|
-
types: {
|
|
3
|
-
character: {
|
|
4
|
-
draw(ctx, instance) {
|
|
5
|
-
const {
|
|
6
|
-
size = 24,
|
|
7
|
-
orientation = 0,
|
|
8
|
-
stroke = "black",
|
|
9
|
-
fill = "transparent",
|
|
10
|
-
} = instance
|
|
11
|
-
|
|
12
|
-
const radius = size * 0.5
|
|
13
|
-
|
|
14
|
-
ctx.save()
|
|
15
|
-
ctx.rotate(-orientation)
|
|
16
|
-
ctx.translate(radius - 1, 0)
|
|
17
|
-
|
|
18
|
-
ctx.fillStyle = "black"
|
|
19
|
-
|
|
20
|
-
ctx.beginPath()
|
|
21
|
-
ctx.moveTo(0, 6)
|
|
22
|
-
ctx.lineTo(0, -6)
|
|
23
|
-
ctx.lineTo(6, 0)
|
|
24
|
-
ctx.fill()
|
|
25
|
-
ctx.closePath()
|
|
26
|
-
ctx.restore()
|
|
27
|
-
|
|
28
|
-
ctx.save()
|
|
29
|
-
ctx.lineWidth = 1
|
|
30
|
-
ctx.strokeStyle = stroke
|
|
31
|
-
ctx.fillStyle = fill
|
|
32
|
-
|
|
33
|
-
ctx.beginPath()
|
|
34
|
-
ctx.arc(0, 0, radius, 0, 2 * 3.14)
|
|
35
|
-
ctx.fill()
|
|
36
|
-
ctx.stroke()
|
|
37
|
-
ctx.closePath()
|
|
38
|
-
ctx.restore()
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
|
|
43
|
-
instances: {
|
|
44
|
-
character: {
|
|
45
|
-
type: "character",
|
|
46
|
-
position: [400, 0, 300],
|
|
47
|
-
},
|
|
48
|
-
},
|
|
49
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export default {}
|
package/src/docs/quick-start.mdx
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import { Canvas, Meta, Source } from '@storybook/blocks'
|
|
2
|
-
import * as QuickStart from './quick-start.stories'
|
|
3
|
-
|
|
4
|
-
import helloWorld from './quick-start/hello-world?raw'
|
|
5
|
-
import firstGame from './quick-start/first-game?raw'
|
|
6
|
-
|
|
7
|
-
<Meta of={QuickStart} />
|
|
8
|
-
|
|
9
|
-
# Quick Start Guide
|
|
10
|
-
|
|
11
|
-
Welcome to Inglorious Engine! This guide will get you up and running in minutes.
|
|
12
|
-
|
|
13
|
-
## What is Inglorious Engine?
|
|
14
|
-
|
|
15
|
-
Inglorious Engine is a **functional programming** game engine for JavaScript. Instead of object-oriented programming, it uses:
|
|
16
|
-
- **Global state** - One source of truth for your entire game
|
|
17
|
-
- **Immutability** - State changes create new state, never modify existing
|
|
18
|
-
- **Pure functions** - Predictable, testable, and reusable functions
|
|
19
|
-
- **Function composition** - Combine behaviors like building blocks
|
|
20
|
-
|
|
21
|
-
## Your First Game
|
|
22
|
-
|
|
23
|
-
### Step 1: Basic Setup
|
|
24
|
-
|
|
25
|
-
Create an HTML file with this structure:
|
|
26
|
-
|
|
27
|
-
```html
|
|
28
|
-
<!doctype html>
|
|
29
|
-
<html lang="en">
|
|
30
|
-
<body>
|
|
31
|
-
<canvas id="canvas"></canvas>
|
|
32
|
-
|
|
33
|
-
<script type="importmap">
|
|
34
|
-
{
|
|
35
|
-
"imports": {
|
|
36
|
-
"immer": "https://unpkg.com/immer@10.1.1/dist/immer.mjs",
|
|
37
|
-
"@inglorious/": "/src/",
|
|
38
|
-
"game": "./my-game.js"
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
</script>
|
|
42
|
-
|
|
43
|
-
<script type="module" src="/src/main.js"></script>
|
|
44
|
-
</body>
|
|
45
|
-
</html>
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
### Step 2: Hello World
|
|
49
|
-
|
|
50
|
-
Create `my-game.js` with the simplest possible game:
|
|
51
|
-
|
|
52
|
-
<Source dark code={helloWorld} />
|
|
53
|
-
|
|
54
|
-
<Canvas of={QuickStart.HelloWorld} />
|
|
55
|
-
|
|
56
|
-
This creates an 800x600 canvas with a light grey background. Not exciting yet, but it's working!
|
|
57
|
-
|
|
58
|
-
### Step 3: Add a Character
|
|
59
|
-
|
|
60
|
-
Let's add something to the scene:
|
|
61
|
-
|
|
62
|
-
<Source dark code={firstGame} />
|
|
63
|
-
|
|
64
|
-
<Canvas of={QuickStart.FirstGame} />
|
|
65
|
-
|
|
66
|
-
## Key Concepts
|
|
67
|
-
|
|
68
|
-
### 1. Game Configuration Structure
|
|
69
|
-
|
|
70
|
-
Every game has this structure:
|
|
71
|
-
```javascript
|
|
72
|
-
export default {
|
|
73
|
-
// Static definitions - never change
|
|
74
|
-
types: {
|
|
75
|
-
// Define what kinds of objects exist
|
|
76
|
-
},
|
|
77
|
-
|
|
78
|
-
// Dynamic state - changes during gameplay
|
|
79
|
-
instances: {
|
|
80
|
-
// Actual objects in the scene
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
### 2. Types vs Instances
|
|
86
|
-
|
|
87
|
-
- **Types**: Define what something *is* (like a blueprint)
|
|
88
|
-
- **Instances**: Define specific *examples* of that type (like actual objects)
|
|
89
|
-
|
|
90
|
-
### 3. Event Handlers
|
|
91
|
-
|
|
92
|
-
To make things move and interact, use event handlers:
|
|
93
|
-
|
|
94
|
-
```javascript
|
|
95
|
-
types: {
|
|
96
|
-
character: {
|
|
97
|
-
// Drawing logic here
|
|
98
|
-
draw(ctx, instance) { /* ... */ },
|
|
99
|
-
|
|
100
|
-
// Event handlers here
|
|
101
|
-
events: {
|
|
102
|
-
'game:update': (instance, event, { dt, config, instances }) => {
|
|
103
|
-
// This runs every frame
|
|
104
|
-
// Update position, velocity, etc.
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
## Next Steps
|
|
112
|
-
|
|
113
|
-
Now that you have a basic understanding of the engine, you're ready to dive deeper.
|
|
114
|
-
|
|
115
|
-
Continue to the **[Core Concepts](/docs/engine--docs)** page to learn about event handlers, code reuse, and other essential features.
|
|
116
|
-
|
|
117
|
-
## Why Functional Programming?
|
|
118
|
-
|
|
119
|
-
Traditional game engines use objects with methods that modify internal state. Inglorious Engine is different:
|
|
120
|
-
|
|
121
|
-
✅ **Predictable**: Same input always produces same output
|
|
122
|
-
✅ **Testable**: Pure functions are easy to test
|
|
123
|
-
✅ **Composable**: Combine behaviors like LEGO blocks
|
|
124
|
-
✅ **Debuggable**: State changes are explicit and traceable
|
|
125
|
-
✅ **Performant**: Immutability enables efficient rendering
|
|
126
|
-
|
|
127
|
-
Ready to build your first game? Let's go!
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import UiChooser from "@inglorious/docs/ui-chooser.jsx"
|
|
2
|
-
|
|
3
|
-
import firstGame from "./quick-start/first-game.js"
|
|
4
|
-
import helloWorld from "./quick-start/hello-world.js"
|
|
5
|
-
|
|
6
|
-
export default {
|
|
7
|
-
title: "Quick Start",
|
|
8
|
-
component: UiChooser,
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export const HelloWorld = {
|
|
12
|
-
args: { config: helloWorld },
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export const FirstGame = {
|
|
16
|
-
args: { config: firstGame },
|
|
17
|
-
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { enableCharacter } from "@inglorious/game/decorators/character.js"
|
|
2
|
-
import { enableMouse } from "@inglorious/game/decorators/input/mouse.js"
|
|
3
|
-
import { filter } from "@inglorious/utils/data-structures/object.js"
|
|
4
|
-
import { random } from "@inglorious/utils/math/rng.js"
|
|
5
|
-
import { pi } from "@inglorious/utils/math/trigonometry.js"
|
|
6
|
-
|
|
7
|
-
export default {
|
|
8
|
-
types: {
|
|
9
|
-
mouse: [
|
|
10
|
-
enableMouse(),
|
|
11
|
-
{
|
|
12
|
-
"scene:click"(instance, event, options) {
|
|
13
|
-
const { instances, notify } = options
|
|
14
|
-
const characters = filter(
|
|
15
|
-
instances,
|
|
16
|
-
(_, { type }) => type === "character",
|
|
17
|
-
)
|
|
18
|
-
const ids = Object.keys(characters)
|
|
19
|
-
|
|
20
|
-
const maxId = ids.length
|
|
21
|
-
? Number(ids[ids.length - 1].replace("character", ""))
|
|
22
|
-
: 0
|
|
23
|
-
|
|
24
|
-
notify({
|
|
25
|
-
id: "instance:add",
|
|
26
|
-
payload: {
|
|
27
|
-
id: `character${maxId + 1}`,
|
|
28
|
-
type: "character",
|
|
29
|
-
position: event.payload,
|
|
30
|
-
orientation: random(0, 2 * pi(), 0.01),
|
|
31
|
-
collisions: {
|
|
32
|
-
hitbox: {
|
|
33
|
-
shape: "circle",
|
|
34
|
-
radius: 12,
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
},
|
|
38
|
-
})
|
|
39
|
-
},
|
|
40
|
-
|
|
41
|
-
// this event handler is needed for React
|
|
42
|
-
"instance:click"(instance, event, { notify }) {
|
|
43
|
-
notify({ id: "instance:remove", payload: event.payload })
|
|
44
|
-
},
|
|
45
|
-
},
|
|
46
|
-
],
|
|
47
|
-
|
|
48
|
-
character: [
|
|
49
|
-
enableCharacter(),
|
|
50
|
-
{
|
|
51
|
-
// this event handler is needed in React
|
|
52
|
-
"instance:click"(instance, event, { notify }) {
|
|
53
|
-
notify({ id: "instance:remove", payload: event.payload })
|
|
54
|
-
},
|
|
55
|
-
},
|
|
56
|
-
],
|
|
57
|
-
},
|
|
58
|
-
|
|
59
|
-
instances: {
|
|
60
|
-
mouse: {
|
|
61
|
-
type: "mouse",
|
|
62
|
-
position: [400, 0, 300],
|
|
63
|
-
collisions: {
|
|
64
|
-
hitbox: {
|
|
65
|
-
shape: "circle",
|
|
66
|
-
radius: 1,
|
|
67
|
-
},
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { enableCharacter } from "@inglorious/game/decorators/character.js"
|
|
2
|
-
import { enableMouse } from "@inglorious/game/decorators/input/mouse.js"
|
|
3
|
-
import { filter } from "@inglorious/utils/data-structures/object.js"
|
|
4
|
-
import { random } from "@inglorious/utils/math/rng.js"
|
|
5
|
-
import { pi } from "@inglorious/utils/math/trigonometry.js"
|
|
6
|
-
|
|
7
|
-
export default {
|
|
8
|
-
types: {
|
|
9
|
-
mouse: [
|
|
10
|
-
enableMouse(),
|
|
11
|
-
{
|
|
12
|
-
"mouse:click"(instance, event, options) {
|
|
13
|
-
const { instances, notify } = options
|
|
14
|
-
const characters = filter(
|
|
15
|
-
instances,
|
|
16
|
-
(_, { type }) => type === "character",
|
|
17
|
-
)
|
|
18
|
-
const ids = Object.keys(characters)
|
|
19
|
-
|
|
20
|
-
notify({
|
|
21
|
-
id: "instance:add",
|
|
22
|
-
payload: {
|
|
23
|
-
id: `character${ids.length + 1}`,
|
|
24
|
-
type: "character",
|
|
25
|
-
position: event.payload,
|
|
26
|
-
orientation: random(0, 2 * pi(), 0.01),
|
|
27
|
-
},
|
|
28
|
-
})
|
|
29
|
-
},
|
|
30
|
-
},
|
|
31
|
-
],
|
|
32
|
-
|
|
33
|
-
character: [enableCharacter()],
|
|
34
|
-
},
|
|
35
|
-
|
|
36
|
-
instances: {
|
|
37
|
-
mouse: {
|
|
38
|
-
type: "mouse",
|
|
39
|
-
position: [400, 0, 300],
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
}
|
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
import arrive from "@inglorious/engine/ai/movement/kinematic/arrive.js"
|
|
2
|
-
import { enableFsm } from "@inglorious/game/decorators/fsm.js"
|
|
3
|
-
import { enableSprite } from "@inglorious/game/decorators/image/sprite.js"
|
|
4
|
-
import { enableMouse } from "@inglorious/game/decorators/input/mouse.js"
|
|
5
|
-
import { Sprite } from "@inglorious/game/sprite.js"
|
|
6
|
-
import { decide } from "@inglorious/utils/algorithms/decision-tree.js"
|
|
7
|
-
import { merge } from "@inglorious/utils/data-structures/objects.js"
|
|
8
|
-
import { length } from "@inglorious/utils/math/linear-algebra/vector.js"
|
|
9
|
-
import { subtract } from "@inglorious/utils/math/linear-algebra/vectors.js"
|
|
10
|
-
|
|
11
|
-
// A reusable decision tree node
|
|
12
|
-
const wakeUp = () => ({
|
|
13
|
-
test: ({ instance, target }) => {
|
|
14
|
-
const distance = length(subtract(target.position, instance.position))
|
|
15
|
-
return distance >= 10
|
|
16
|
-
},
|
|
17
|
-
true: () => "aware",
|
|
18
|
-
false: ({ instance }) => instance.state,
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
// Our decision tree
|
|
22
|
-
const nextState = {
|
|
23
|
-
test: ({ instance }) => instance.state,
|
|
24
|
-
idle: () => ({
|
|
25
|
-
test: ({ instance, target }) => {
|
|
26
|
-
const distance = length(subtract(target.position, instance.position))
|
|
27
|
-
return distance < 250
|
|
28
|
-
},
|
|
29
|
-
true: () => "aware",
|
|
30
|
-
false: ({ instance }) => instance.state,
|
|
31
|
-
}),
|
|
32
|
-
chasing: () => ({
|
|
33
|
-
test: ({ instance, target }) => {
|
|
34
|
-
const distance = length(subtract(target.position, instance.position))
|
|
35
|
-
return distance >= 250
|
|
36
|
-
},
|
|
37
|
-
true: () => "idle",
|
|
38
|
-
false: () => ({
|
|
39
|
-
test: ({ instance, target }) => {
|
|
40
|
-
const distance = length(subtract(target.position, instance.position))
|
|
41
|
-
return distance < 10
|
|
42
|
-
},
|
|
43
|
-
true: () => "sleepy",
|
|
44
|
-
false: ({ instance }) => instance.state,
|
|
45
|
-
}),
|
|
46
|
-
}),
|
|
47
|
-
sleepy: wakeUp,
|
|
48
|
-
sleeping: wakeUp,
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export default {
|
|
52
|
-
types: {
|
|
53
|
-
mouse: [enableMouse()],
|
|
54
|
-
|
|
55
|
-
cat: [
|
|
56
|
-
enableSprite(),
|
|
57
|
-
enableFsm({
|
|
58
|
-
idle: {
|
|
59
|
-
"game:update"(instance, event, options) {
|
|
60
|
-
const { mouse } = options.instances
|
|
61
|
-
|
|
62
|
-
Sprite.play("idle", instance, options)
|
|
63
|
-
|
|
64
|
-
instance.state = decide(nextState, { instance, target: mouse })
|
|
65
|
-
},
|
|
66
|
-
},
|
|
67
|
-
|
|
68
|
-
aware: {
|
|
69
|
-
"game:update"(instance, event, options) {
|
|
70
|
-
Sprite.play("aware", instance, options)
|
|
71
|
-
},
|
|
72
|
-
|
|
73
|
-
"sprite:animationEnd"(instance, event) {
|
|
74
|
-
const { id, spriteState } = event.payload
|
|
75
|
-
|
|
76
|
-
// always check who originated the event and which sprite is running!
|
|
77
|
-
if (id === instance.id && spriteState === "aware") {
|
|
78
|
-
instance.state = "chasing"
|
|
79
|
-
}
|
|
80
|
-
},
|
|
81
|
-
},
|
|
82
|
-
|
|
83
|
-
chasing: {
|
|
84
|
-
"game:update"(instance, event, options) {
|
|
85
|
-
const { mouse } = options.instances
|
|
86
|
-
|
|
87
|
-
merge(instance, arrive(instance, mouse, options))
|
|
88
|
-
|
|
89
|
-
const spriteState = Sprite.move8(instance)
|
|
90
|
-
Sprite.play(spriteState, instance, options)
|
|
91
|
-
|
|
92
|
-
instance.state = decide(nextState, { instance, target: mouse })
|
|
93
|
-
},
|
|
94
|
-
},
|
|
95
|
-
|
|
96
|
-
sleepy: {
|
|
97
|
-
"game:update"(instance, event, options) {
|
|
98
|
-
const { mouse } = options.instances
|
|
99
|
-
|
|
100
|
-
Sprite.play("sleepy", instance, options)
|
|
101
|
-
|
|
102
|
-
instance.state = decide(nextState, { instance, target: mouse })
|
|
103
|
-
},
|
|
104
|
-
|
|
105
|
-
"sprite:animationEnd"(instance, event) {
|
|
106
|
-
const { id, spriteState } = event.payload
|
|
107
|
-
|
|
108
|
-
// always check who originated the event and which sprite is running!
|
|
109
|
-
if (id === instance.id && spriteState === "sleepy") {
|
|
110
|
-
instance.state = "sleeping"
|
|
111
|
-
}
|
|
112
|
-
},
|
|
113
|
-
},
|
|
114
|
-
|
|
115
|
-
sleeping: {
|
|
116
|
-
"game:update"(instance, event, options) {
|
|
117
|
-
const { mouse } = options.instances
|
|
118
|
-
|
|
119
|
-
Sprite.play("sleeping", instance, options)
|
|
120
|
-
|
|
121
|
-
instance.state = decide(nextState, { instance, target: mouse })
|
|
122
|
-
},
|
|
123
|
-
},
|
|
124
|
-
}),
|
|
125
|
-
],
|
|
126
|
-
},
|
|
127
|
-
|
|
128
|
-
instances: {
|
|
129
|
-
game: {
|
|
130
|
-
pixelated: true,
|
|
131
|
-
},
|
|
132
|
-
|
|
133
|
-
mouse: {
|
|
134
|
-
type: "mouse",
|
|
135
|
-
position: [0, 0, 0],
|
|
136
|
-
},
|
|
137
|
-
|
|
138
|
-
neko: {
|
|
139
|
-
type: "cat",
|
|
140
|
-
state: "idle",
|
|
141
|
-
maxSpeed: 250,
|
|
142
|
-
position: [400, 0, 300],
|
|
143
|
-
sprite: {
|
|
144
|
-
image: {
|
|
145
|
-
id: "neko",
|
|
146
|
-
src: "/sprites/neko.png",
|
|
147
|
-
imageSize: [192, 192],
|
|
148
|
-
tileSize: [32, 32],
|
|
149
|
-
scale: 2,
|
|
150
|
-
},
|
|
151
|
-
speed: 0.2,
|
|
152
|
-
frames: {
|
|
153
|
-
idle: [4],
|
|
154
|
-
aware: [0, 4],
|
|
155
|
-
leftUp: [0x80000000 + 23, 0x80000000 + 29],
|
|
156
|
-
up: [28, 30, 28, 31],
|
|
157
|
-
rightUp: [23, 29],
|
|
158
|
-
right: [16, 22],
|
|
159
|
-
rightDown: [13, 14],
|
|
160
|
-
down: [1, 2, 1, 7],
|
|
161
|
-
leftDown: [0x80000000 + 13, 0x80000000 + 14],
|
|
162
|
-
left: [0x80000000 + 16, 0x80000000 + 22],
|
|
163
|
-
sleepy: [4, 10, 10, 3, 9, 15, 9, 15, 15],
|
|
164
|
-
sleeping: [26, 26, 27, 27],
|
|
165
|
-
},
|
|
166
|
-
},
|
|
167
|
-
},
|
|
168
|
-
},
|
|
169
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { enableCharacter } from "@inglorious/game/decorators/character.js"
|
|
2
|
-
import { random } from "@inglorious/utils/math/rng.js"
|
|
3
|
-
import { pi } from "@inglorious/utils/math/trigonometry.js"
|
|
4
|
-
|
|
5
|
-
export default {
|
|
6
|
-
types: {
|
|
7
|
-
character: [enableCharacter()],
|
|
8
|
-
},
|
|
9
|
-
|
|
10
|
-
instances: {
|
|
11
|
-
...Object.fromEntries(
|
|
12
|
-
Array(100)
|
|
13
|
-
.fill(null)
|
|
14
|
-
.map((_, index) => [
|
|
15
|
-
`character${index + 1}`,
|
|
16
|
-
{
|
|
17
|
-
id: `character${index + 1}`,
|
|
18
|
-
type: "character",
|
|
19
|
-
position: [random(0, 800), 0, random(0, 600)],
|
|
20
|
-
orientation: random(0, 2 * pi(), 0.01),
|
|
21
|
-
},
|
|
22
|
-
]),
|
|
23
|
-
),
|
|
24
|
-
},
|
|
25
|
-
}
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import { Canvas, Meta, Source } from '@storybook/blocks'
|
|
2
|
-
import * as Recipes from './recipes.stories'
|
|
3
|
-
|
|
4
|
-
import addAndRemove from './add-and-remove?raw'
|
|
5
|
-
import addInstance from './add-instance?raw'
|
|
6
|
-
import decisionTree from './decision-tree?raw'
|
|
7
|
-
import randomInstances from './random-instances?raw'
|
|
8
|
-
import removeInstance from './remove-instance?raw'
|
|
9
|
-
import states from './states?raw'
|
|
10
|
-
|
|
11
|
-
<Meta of={Recipes} />
|
|
12
|
-
|
|
13
|
-
# Recipes
|
|
14
|
-
|
|
15
|
-
Some common patterns that can be used in your game. Check the source code of each recipe and take inspiration!
|
|
16
|
-
|
|
17
|
-
## Spawning Random Instances
|
|
18
|
-
|
|
19
|
-
The game configuration is pure JavaScript, so there's not much difference between defining the instances one by one or writing some code that generates them automatically!
|
|
20
|
-
|
|
21
|
-
Here we are using a couple of utility functions from the [rng](/?path=/docs/utils-math-rng--docs) and [trigonometry](/?path=/docs/utils-math-trigonometry--docs) packages.
|
|
22
|
-
|
|
23
|
-
<Source dark code={randomInstances} />
|
|
24
|
-
|
|
25
|
-
<Canvas of={Recipes.RandomInstances} />
|
|
26
|
-
|
|
27
|
-
## Adding An Instance
|
|
28
|
-
|
|
29
|
-
Programmatically adding a new instance to the scene is as easy as sending an event to the game engine asking nicely.
|
|
30
|
-
|
|
31
|
-
Try clicking anywhere on the scene to create a new instance with random orientation.
|
|
32
|
-
|
|
33
|
-
Here, apart from some math functions, we are also using a predefined mouse [input](/?path=/docs/games-input--docs) package that allows us to position a cursor and handle its events.
|
|
34
|
-
|
|
35
|
-
<Source dark code={addInstance} />
|
|
36
|
-
|
|
37
|
-
<Canvas of={Recipes.AddInstance} />
|
|
38
|
-
|
|
39
|
-
## Removing An Instance
|
|
40
|
-
|
|
41
|
-
Removing an instance is pretty much the same: we notify the engine of our intention and we enjoy the result.
|
|
42
|
-
|
|
43
|
-
Try clicking on any instance do remove it from the scene.
|
|
44
|
-
|
|
45
|
-
<Source dark code={removeInstance} />
|
|
46
|
-
|
|
47
|
-
<Canvas of={Recipes.RemoveInstance} />
|
|
48
|
-
|
|
49
|
-
## Add And Remove
|
|
50
|
-
|
|
51
|
-
Clicking on a character will prevent the event from bubbling on the scene, so it's safe to add and remove characters by using the same strategy: click as if there's no tomorrow!
|
|
52
|
-
|
|
53
|
-
The most complex part of this code is generating new incremental ids, which is not even mandatory. You know best what's good for your game!
|
|
54
|
-
|
|
55
|
-
<Source dark code={addAndRemove} />
|
|
56
|
-
|
|
57
|
-
<Canvas of={Recipes.AddAndRemove} />
|
|
58
|
-
|
|
59
|
-
## States
|
|
60
|
-
|
|
61
|
-
Adding a states to our character is very easy, in fact every game so far was secretely using states already!
|
|
62
|
-
|
|
63
|
-
We just need to wrap our event handlers into a `states` object, set an initial `state` on our instance, and transition from one state to another inside our event handlers.
|
|
64
|
-
|
|
65
|
-
Try moving the cursor near the character to make it hunt you, and then moving it away to see the character wander around aimlessly.
|
|
66
|
-
|
|
67
|
-
This code may seem a lot, but it's just a merge of the *Arrive* and *Wander* [Kinematic AI Movement](/?path=/docs/games-ai-movement-kinematic--docs) examples so feel free to take a look at them before approaching this one.
|
|
68
|
-
|
|
69
|
-
<Source dark code={states} />
|
|
70
|
-
|
|
71
|
-
<Canvas of={Recipes.States} />
|
|
72
|
-
|
|
73
|
-
## Decision Trees
|
|
74
|
-
|
|
75
|
-
Decision trees are another sweet feature built-in inside of Inglorious Engine. The following example doesn't really need one, but why not?
|
|
76
|
-
|
|
77
|
-
This game also showcases sprites!
|
|
78
|
-
|
|
79
|
-
<Source dark code={decisionTree} />
|
|
80
|
-
|
|
81
|
-
<Canvas of={Recipes.DecisionTree} />
|