@inglorious/engine 0.1.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/LICENSE +9 -0
- package/README.md +72 -0
- package/package.json +76 -0
- package/src/docs/ai/movement/dynamic/align.js +131 -0
- package/src/docs/ai/movement/dynamic/arrive.js +88 -0
- package/src/docs/ai/movement/dynamic/dynamic.mdx +99 -0
- package/src/docs/ai/movement/dynamic/dynamic.stories.js +58 -0
- package/src/docs/ai/movement/dynamic/evade.js +72 -0
- package/src/docs/ai/movement/dynamic/face.js +90 -0
- package/src/docs/ai/movement/dynamic/flee.js +38 -0
- package/src/docs/ai/movement/dynamic/look-where-youre-going.js +114 -0
- package/src/docs/ai/movement/dynamic/match-velocity.js +92 -0
- package/src/docs/ai/movement/dynamic/pursue.js +72 -0
- package/src/docs/ai/movement/dynamic/seek.js +37 -0
- package/src/docs/ai/movement/dynamic/wander.js +71 -0
- package/src/docs/ai/movement/kinematic/align.js +122 -0
- package/src/docs/ai/movement/kinematic/arrive.js +78 -0
- package/src/docs/ai/movement/kinematic/face.js +82 -0
- package/src/docs/ai/movement/kinematic/flee.js +36 -0
- package/src/docs/ai/movement/kinematic/kinematic.mdx +67 -0
- package/src/docs/ai/movement/kinematic/kinematic.stories.js +42 -0
- package/src/docs/ai/movement/kinematic/seek.js +34 -0
- package/src/docs/ai/movement/kinematic/wander-as-seek.js +62 -0
- package/src/docs/ai/movement/kinematic/wander.js +28 -0
- package/src/docs/bounds.js +7 -0
- package/src/docs/code-reuse.js +35 -0
- package/src/docs/collision/circles.js +58 -0
- package/src/docs/collision/collision.mdx +27 -0
- package/src/docs/collision/collision.stories.js +22 -0
- package/src/docs/collision/platform.js +76 -0
- package/src/docs/collision/tilemap.js +181 -0
- package/src/docs/empty.js +1 -0
- package/src/docs/engine.mdx +81 -0
- package/src/docs/engine.stories.js +37 -0
- package/src/docs/event-handlers.js +68 -0
- package/src/docs/framerate.js +37 -0
- package/src/docs/game.jsx +15 -0
- package/src/docs/image/image.js +19 -0
- package/src/docs/image/image.stories.js +22 -0
- package/src/docs/image/sprite.js +39 -0
- package/src/docs/image/tilemap.js +84 -0
- package/src/docs/input/controls.js +67 -0
- package/src/docs/input/gamepad.js +67 -0
- package/src/docs/input/input.mdx +55 -0
- package/src/docs/input/input.stories.js +27 -0
- package/src/docs/input/keyboard.js +58 -0
- package/src/docs/input/mouse.js +32 -0
- package/src/docs/instances.js +49 -0
- package/src/docs/player/dynamic/double-jump.js +90 -0
- package/src/docs/player/dynamic/dynamic.stories.js +32 -0
- package/src/docs/player/dynamic/jump.js +83 -0
- package/src/docs/player/dynamic/modern-controls.js +57 -0
- package/src/docs/player/dynamic/shooter-controls.js +51 -0
- package/src/docs/player/dynamic/tank-controls.js +44 -0
- package/src/docs/player/kinematic/double-jump.js +90 -0
- package/src/docs/player/kinematic/jump.js +82 -0
- package/src/docs/player/kinematic/kinematic.stories.js +32 -0
- package/src/docs/player/kinematic/modern-controls.js +56 -0
- package/src/docs/player/kinematic/shooter-controls.js +48 -0
- package/src/docs/player/kinematic/tank-controls.js +42 -0
- package/src/docs/quick-start/first-game.js +49 -0
- package/src/docs/quick-start/hello-world.js +1 -0
- package/src/docs/quick-start.mdx +127 -0
- package/src/docs/quick-start.stories.js +17 -0
- package/src/docs/recipes/add-and-remove.js +71 -0
- package/src/docs/recipes/add-instance.js +42 -0
- package/src/docs/recipes/decision-tree.js +169 -0
- package/src/docs/recipes/random-instances.js +25 -0
- package/src/docs/recipes/recipes.mdx +81 -0
- package/src/docs/recipes/recipes.stories.js +37 -0
- package/src/docs/recipes/remove-instance.js +52 -0
- package/src/docs/recipes/states.js +64 -0
- package/src/docs/ui/button.js +28 -0
- package/src/docs/ui/form.stories.js +55 -0
- package/src/docs/ui-chooser.jsx +6 -0
- package/src/docs/utils/data-structures/object.mdx +47 -0
- package/src/docs/utils/data-structures/objects.mdx +30 -0
- package/src/docs/utils/functions/functions.mdx +34 -0
- package/src/docs/utils/math/geometry/circle.mdx +55 -0
- package/src/docs/utils/math/geometry/point.mdx +38 -0
- package/src/docs/utils/math/geometry/rectangle.mdx +24 -0
- package/src/docs/utils/math/geometry/segment.mdx +55 -0
- package/src/docs/utils/math/geometry/triangle.mdx +22 -0
- package/src/docs/utils/math/linear-algebra/2d.mdx +22 -0
- package/src/docs/utils/math/linear-algebra/quaternion.mdx +21 -0
- package/src/docs/utils/math/linear-algebra/quaternions.mdx +22 -0
- package/src/docs/utils/math/linear-algebra/vector.mdx +177 -0
- package/src/docs/utils/math/linear-algebra/vectors.mdx +58 -0
- package/src/docs/utils/math/numbers.mdx +76 -0
- package/src/docs/utils/math/random.mdx +35 -0
- package/src/docs/utils/math/statistics.mdx +38 -0
- package/src/docs/utils/math/trigonometry.mdx +85 -0
- package/src/docs/utils/physics/friction.mdx +20 -0
- package/src/docs/utils/physics/gravity.mdx +28 -0
- package/src/engine/ai/movement/dynamic/align.js +63 -0
- package/src/engine/ai/movement/dynamic/arrive.js +43 -0
- package/src/engine/ai/movement/dynamic/evade.js +38 -0
- package/src/engine/ai/movement/dynamic/face.js +20 -0
- package/src/engine/ai/movement/dynamic/flee.js +45 -0
- package/src/engine/ai/movement/dynamic/look-where-youre-going.js +17 -0
- package/src/engine/ai/movement/dynamic/match-velocity.js +50 -0
- package/src/engine/ai/movement/dynamic/pursue.js +38 -0
- package/src/engine/ai/movement/dynamic/seek.js +44 -0
- package/src/engine/ai/movement/dynamic/wander.js +32 -0
- package/src/engine/ai/movement/kinematic/align.js +37 -0
- package/src/engine/ai/movement/kinematic/arrive.js +42 -0
- package/src/engine/ai/movement/kinematic/face.js +20 -0
- package/src/engine/ai/movement/kinematic/flee.js +26 -0
- package/src/engine/ai/movement/kinematic/seek.js +26 -0
- package/src/engine/ai/movement/kinematic/seek.test.js +42 -0
- package/src/engine/ai/movement/kinematic/wander-as-seek.js +31 -0
- package/src/engine/ai/movement/kinematic/wander.js +27 -0
- package/src/engine/collision/detection.js +115 -0
- package/src/engine/loop/animation-frame.js +26 -0
- package/src/engine/loop/elapsed.js +23 -0
- package/src/engine/loop/fixed.js +28 -0
- package/src/engine/loop/flash.js +14 -0
- package/src/engine/loop/lag.js +27 -0
- package/src/engine/loop.js +15 -0
- package/src/engine/movement/dynamic/modern.js +24 -0
- package/src/engine/movement/dynamic/tank.js +43 -0
- package/src/engine/movement/kinematic/modern.js +16 -0
- package/src/engine/movement/kinematic/modern.test.js +27 -0
- package/src/engine/movement/kinematic/tank.js +27 -0
- package/src/engine/store.js +174 -0
- package/src/engine/store.test.js +256 -0
- package/src/engine.js +74 -0
- package/src/game/animation.js +26 -0
- package/src/game/bounds.js +66 -0
- package/src/game/decorators/character.js +5 -0
- package/src/game/decorators/clamp-to-bounds.js +15 -0
- package/src/game/decorators/collisions.js +24 -0
- package/src/game/decorators/controls/dynamic/modern.js +48 -0
- package/src/game/decorators/controls/dynamic/shooter.js +47 -0
- package/src/game/decorators/controls/dynamic/tank.js +55 -0
- package/src/game/decorators/controls/kinematic/modern.js +49 -0
- package/src/game/decorators/controls/kinematic/shooter.js +45 -0
- package/src/game/decorators/controls/kinematic/tank.js +52 -0
- package/src/game/decorators/debug/collisions.js +32 -0
- package/src/game/decorators/double-jump.js +70 -0
- package/src/game/decorators/fps.js +30 -0
- package/src/game/decorators/fsm.js +27 -0
- package/src/game/decorators/fsm.test.js +56 -0
- package/src/game/decorators/game.js +11 -0
- package/src/game/decorators/image/image.js +5 -0
- package/src/game/decorators/image/sprite.js +5 -0
- package/src/game/decorators/image/tilemap.js +5 -0
- package/src/game/decorators/input/controls.js +27 -0
- package/src/game/decorators/input/gamepad.js +74 -0
- package/src/game/decorators/input/input.js +41 -0
- package/src/game/decorators/input/keyboard.js +49 -0
- package/src/game/decorators/input/mouse.js +65 -0
- package/src/game/decorators/jump.js +72 -0
- package/src/game/decorators/platform.js +5 -0
- package/src/game/decorators/ui/button.js +21 -0
- package/src/game/sprite.js +119 -0
- package/src/main.js +5 -0
- package/src/ui/canvas/absolute-position.js +17 -0
- package/src/ui/canvas/character.js +35 -0
- package/src/ui/canvas/form/button.js +25 -0
- package/src/ui/canvas/fps.js +18 -0
- package/src/ui/canvas/image/hitmask.js +37 -0
- package/src/ui/canvas/image/image.js +37 -0
- package/src/ui/canvas/image/sprite.js +49 -0
- package/src/ui/canvas/image/tilemap.js +64 -0
- package/src/ui/canvas/mouse.js +37 -0
- package/src/ui/canvas/shapes/circle.js +31 -0
- package/src/ui/canvas/shapes/rectangle.js +31 -0
- package/src/ui/canvas.js +81 -0
- package/src/ui/react/game/character/character.module.scss +17 -0
- package/src/ui/react/game/character/index.jsx +30 -0
- package/src/ui/react/game/cursor/cursor.module.scss +47 -0
- package/src/ui/react/game/cursor/index.jsx +20 -0
- package/src/ui/react/game/form/fields/field/field.module.scss +5 -0
- package/src/ui/react/game/form/fields/field/index.jsx +56 -0
- package/src/ui/react/game/form/fields/fields.module.scss +48 -0
- package/src/ui/react/game/form/fields/index.jsx +12 -0
- package/src/ui/react/game/form/form.module.scss +18 -0
- package/src/ui/react/game/form/index.jsx +22 -0
- package/src/ui/react/game/fps/index.jsx +16 -0
- package/src/ui/react/game/game.jsx +71 -0
- package/src/ui/react/game/index.jsx +29 -0
- package/src/ui/react/game/platform/index.jsx +30 -0
- package/src/ui/react/game/platform/platform.module.scss +7 -0
- package/src/ui/react/game/scene/index.jsx +25 -0
- package/src/ui/react/game/scene/scene.module.scss +9 -0
- package/src/ui/react/game/sprite/index.jsx +58 -0
- package/src/ui/react/game/sprite/sprite.module.css +3 -0
- package/src/ui/react/game/stats/index.jsx +22 -0
- package/src/ui/react/hocs/with-absolute-position/index.jsx +20 -0
- package/src/ui/react/hocs/with-absolute-position/with-absolute-position.module.scss +5 -0
- package/src/ui/react/index.jsx +9 -0
- package/src/utils/algorithms/decision-tree.js +24 -0
- package/src/utils/algorithms/decision-tree.test.js +102 -0
- package/src/utils/algorithms/path-finding.js +155 -0
- package/src/utils/algorithms/path-finding.test.js +151 -0
- package/src/utils/algorithms/types.d.ts +28 -0
- package/src/utils/data-structures/array.js +83 -0
- package/src/utils/data-structures/array.test.js +173 -0
- package/src/utils/data-structures/board.js +159 -0
- package/src/utils/data-structures/board.test.js +242 -0
- package/src/utils/data-structures/boolean.js +9 -0
- package/src/utils/data-structures/heap.js +164 -0
- package/src/utils/data-structures/heap.test.js +103 -0
- package/src/utils/data-structures/object.js +102 -0
- package/src/utils/data-structures/object.test.js +121 -0
- package/src/utils/data-structures/objects.js +48 -0
- package/src/utils/data-structures/objects.test.js +99 -0
- package/src/utils/data-structures/tree.js +36 -0
- package/src/utils/data-structures/tree.test.js +33 -0
- package/src/utils/data-structures/types.d.ts +4 -0
- package/src/utils/functions/functions.js +19 -0
- package/src/utils/functions/functions.test.js +23 -0
- package/src/utils/math/geometry/circle.js +117 -0
- package/src/utils/math/geometry/circle.test.js +97 -0
- package/src/utils/math/geometry/hitmask.js +39 -0
- package/src/utils/math/geometry/hitmask.test.js +84 -0
- package/src/utils/math/geometry/line.js +35 -0
- package/src/utils/math/geometry/line.test.js +49 -0
- package/src/utils/math/geometry/platform.js +42 -0
- package/src/utils/math/geometry/platform.test.js +133 -0
- package/src/utils/math/geometry/point.js +71 -0
- package/src/utils/math/geometry/point.test.js +81 -0
- package/src/utils/math/geometry/rectangle.js +45 -0
- package/src/utils/math/geometry/rectangle.test.js +42 -0
- package/src/utils/math/geometry/segment.js +80 -0
- package/src/utils/math/geometry/segment.test.js +183 -0
- package/src/utils/math/geometry/triangle.js +15 -0
- package/src/utils/math/geometry/triangle.test.js +11 -0
- package/src/utils/math/geometry/types.d.ts +23 -0
- package/src/utils/math/linear-algebra/2d.js +28 -0
- package/src/utils/math/linear-algebra/2d.test.js +17 -0
- package/src/utils/math/linear-algebra/quaternion.js +22 -0
- package/src/utils/math/linear-algebra/quaternion.test.js +25 -0
- package/src/utils/math/linear-algebra/quaternions.js +20 -0
- package/src/utils/math/linear-algebra/quaternions.test.js +29 -0
- package/src/utils/math/linear-algebra/types.d.ts +4 -0
- package/src/utils/math/linear-algebra/vector.js +302 -0
- package/src/utils/math/linear-algebra/vector.test.js +257 -0
- package/src/utils/math/linear-algebra/vectors.js +122 -0
- package/src/utils/math/linear-algebra/vectors.test.js +65 -0
- package/src/utils/math/numbers.js +90 -0
- package/src/utils/math/numbers.test.js +137 -0
- package/src/utils/math/rng.js +44 -0
- package/src/utils/math/rng.test.js +39 -0
- package/src/utils/math/statistics.js +43 -0
- package/src/utils/math/statistics.test.js +47 -0
- package/src/utils/math/trigonometry.js +89 -0
- package/src/utils/math/trigonometry.test.js +52 -0
- package/src/utils/physics/acceleration.js +63 -0
- package/src/utils/physics/friction.js +30 -0
- package/src/utils/physics/friction.test.js +44 -0
- package/src/utils/physics/gravity.js +71 -0
- package/src/utils/physics/gravity.test.js +80 -0
- package/src/utils/physics/jump.js +41 -0
- package/src/utils/physics/velocity.js +38 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright © 2025 Inglorious Coderz Srl.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Inglorious Engine
|
|
2
|
+
|
|
3
|
+
[](https://opensource.org/licenses/MIT)
|
|
4
|
+
|
|
5
|
+
A JavaScript game engine written with global state, immutability, and pure functions in mind. Have fun(ctional programming) with it!
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Functional & Data-Oriented**: Uses a single, immutable state object as the source of truth, inspired by functional programming principles.
|
|
10
|
+
- **Composable by Design**: Build complex behaviors by composing pure functions and decorators, offering a powerful alternative to inheritance.
|
|
11
|
+
- **Renderer Agnostic**: The engine is headless. You can use any rendering technology you like, from Canvas2D and HTML to React components.
|
|
12
|
+
- **Zero Build Step**: Write plain JavaScript and run it directly in the browser. No complex build configurations to worry about.
|
|
13
|
+
|
|
14
|
+
## Documentation
|
|
15
|
+
|
|
16
|
+
The best way to get started is with the official documentation, which includes a **[Quick Start Guide](https://inglorious-engine.vercel.app/?path=/docs/quick-start--docs)**.
|
|
17
|
+
|
|
18
|
+
Full documentation is available at: **[https://inglorious-engine.vercel.app/](https://inglorious-engine.vercel.app/)**
|
|
19
|
+
|
|
20
|
+
## Why Functional Programming?
|
|
21
|
+
|
|
22
|
+
What makes this engine different from all the others is that, instead of Object Oriented Programming (OOP), which seems the most obvious choice for a game engine, this one is based on Functional Programming (FP).
|
|
23
|
+
|
|
24
|
+
FP has many advantages:
|
|
25
|
+
|
|
26
|
+
1. A single source of truth means that the game state is just one huge JSON structure. This entails that you have control over the whole game state at every moment, instead of having pieces of state scattered through multiple objects. To overcome the issue of scattered state, many modern game engines use an approach that is called Data-Oriented. Well, guess what: functional programming has always used it.
|
|
27
|
+
2. Immutability of state means that every time you want to change something a new state will be created with that change, instead of modifying the state directly. Isn't that slow? Nope, the new state is a shallow copy of the old one. Also, immutability makes it easier to compare what was before with what is now, and in certain scenarios (such as webapps) it allows for very performant re-rendering techniques.
|
|
28
|
+
3. Pure functions are functions that return a value that depends only on the input parameters, no side-effects involved. They are the most predictable, testable, and reusable functions, so why should we rely on void methods belonging to a specific class?
|
|
29
|
+
4. Another important concept related to FP is function composition, which means combining multiple functions together and then applying them to some input. Think of it as a pipeline of operations: you take x, then do some transformation on it, then pass the result to some other function, and so on. This can be used as a way more powerful tool than object inheritance: you can combine multiple behaviours on some object, in a way that is very similar to the Decorator pattern we have in OOP.
|
|
30
|
+
|
|
31
|
+
## Contributing
|
|
32
|
+
|
|
33
|
+
We welcome contributions from the community! Whether you're fixing a bug, adding a feature, or improving the documentation, your help is appreciated.
|
|
34
|
+
|
|
35
|
+
### Development Setup
|
|
36
|
+
|
|
37
|
+
1. Fork and clone the repository.
|
|
38
|
+
2. Install dependencies using pnpm:
|
|
39
|
+
```bash
|
|
40
|
+
pnpm install
|
|
41
|
+
```
|
|
42
|
+
3. Run the Storybook documentation locally:
|
|
43
|
+
```bash
|
|
44
|
+
pnpm storybook
|
|
45
|
+
```
|
|
46
|
+
4. Run the unit tests:
|
|
47
|
+
```bash
|
|
48
|
+
pnpm test
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Code Style
|
|
52
|
+
|
|
53
|
+
The project uses ESLint for linting and Prettier for formatting. Please ensure your code adheres to the project's style to avoid introducing errors.
|
|
54
|
+
|
|
55
|
+
A note on code style, particularly regarding "magic numbers" for vector components:
|
|
56
|
+
|
|
57
|
+
```js
|
|
58
|
+
const X = 0
|
|
59
|
+
const Y = 1
|
|
60
|
+
const Z = 2
|
|
61
|
+
const x = instance.position[X]
|
|
62
|
+
const y = instance.position[Y]
|
|
63
|
+
const z = instance.position[Z]
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
I find it cleaner to do like so:
|
|
67
|
+
|
|
68
|
+
```js
|
|
69
|
+
const [x, y, z] = instance.position
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
There are a few exceptions: in the `/docs` folder I prefer the first version because not everyone is used to destructuring and I wanted to make the examples as readable as possible for people coming from, say, Godot. In that case I would put the `X`, `Y`, and `Z` constants on top of the file, right below the imports.
|
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@inglorious/engine",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A JavaScript game engine written with global state, immutability, and pure functions in mind. Have fun(ctional programming) with it!",
|
|
5
|
+
"author": "IceOnFire <antony.mistretta@gmail.com> (https://ingloriouscoderz.it)",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/IngloriousCoderz/inglorious-engine.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://inglorious-engine.vercel.app/",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"game-engine",
|
|
14
|
+
"functional-programming",
|
|
15
|
+
"gamedev",
|
|
16
|
+
"javascript",
|
|
17
|
+
"ecs",
|
|
18
|
+
"data-oriented"
|
|
19
|
+
],
|
|
20
|
+
"type": "module",
|
|
21
|
+
"files": [
|
|
22
|
+
"src",
|
|
23
|
+
"README.md",
|
|
24
|
+
"LICENSE"
|
|
25
|
+
],
|
|
26
|
+
"exports": {
|
|
27
|
+
"./*": "./src/*"
|
|
28
|
+
},
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
|
|
34
|
+
"format": "prettier --write '**/*.{js,jsx}'",
|
|
35
|
+
"test:watch": "vitest",
|
|
36
|
+
"test": "vitest run",
|
|
37
|
+
"start": "serve",
|
|
38
|
+
"storybook": "storybook dev -p 6006",
|
|
39
|
+
"build-storybook": "storybook build",
|
|
40
|
+
"prepare": "husky"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"immer": "^10.1.1",
|
|
44
|
+
"react": "^19.0.0",
|
|
45
|
+
"react-dom": "^19.0.0",
|
|
46
|
+
"react-redux": "^9.2.0"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@chromatic-com/storybook": "^3.2.6",
|
|
50
|
+
"@eslint/js": "^9.23.0",
|
|
51
|
+
"@storybook/addon-essentials": "^8.6.12",
|
|
52
|
+
"@storybook/addon-interactions": "^8.6.12",
|
|
53
|
+
"@storybook/addon-links": "^8.6.12",
|
|
54
|
+
"@storybook/blocks": "^8.6.12",
|
|
55
|
+
"@storybook/manager-api": "^8.6.12",
|
|
56
|
+
"@storybook/react": "^8.6.12",
|
|
57
|
+
"@storybook/react-vite": "^8.6.12",
|
|
58
|
+
"@storybook/test": "^8.6.12",
|
|
59
|
+
"@storybook/theming": "^8.6.12",
|
|
60
|
+
"@types/react": "^18.2.15",
|
|
61
|
+
"@types/react-dom": "^18.2.7",
|
|
62
|
+
"@vitejs/plugin-react": "^4.0.3",
|
|
63
|
+
"eslint": "^9.23.0",
|
|
64
|
+
"eslint-plugin-react": "^7.37.4",
|
|
65
|
+
"eslint-plugin-simple-import-sort": "^10.0.0",
|
|
66
|
+
"eslint-plugin-storybook": "^0.12.0",
|
|
67
|
+
"globals": "^16.0.0",
|
|
68
|
+
"husky": "^9.1.7",
|
|
69
|
+
"prettier": "^3.5.3",
|
|
70
|
+
"sass": "^1.66.1",
|
|
71
|
+
"serve": "^14.2.4",
|
|
72
|
+
"storybook": "^8.6.12",
|
|
73
|
+
"vite": "^4.4.5",
|
|
74
|
+
"vitest": "^0.34.3"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import align, {
|
|
2
|
+
DEFAULT_SLOW_RADIUS,
|
|
3
|
+
DEFAULT_TARGET_RADIUS,
|
|
4
|
+
DEFAULT_TIME_TO_TARGET,
|
|
5
|
+
} from "@inglorious/engine/ai/movement/dynamic/align.js"
|
|
6
|
+
import { clampToBounds } from "@inglorious/game/bounds.js"
|
|
7
|
+
import { enableCharacter } from "@inglorious/game/decorators/character.js"
|
|
8
|
+
import {
|
|
9
|
+
createControls,
|
|
10
|
+
enableControls,
|
|
11
|
+
} from "@inglorious/game/decorators/input/controls.js"
|
|
12
|
+
import { enableMouse } from "@inglorious/game/decorators/input/mouse.js"
|
|
13
|
+
import { merge } from "@inglorious/utils/data-structures/objects.js"
|
|
14
|
+
import { clamp } from "@inglorious/utils/math/numbers.js"
|
|
15
|
+
import { pi } from "@inglorious/utils/math/trigonometry.js"
|
|
16
|
+
|
|
17
|
+
export default {
|
|
18
|
+
types: {
|
|
19
|
+
mouse: [
|
|
20
|
+
enableMouse(),
|
|
21
|
+
{
|
|
22
|
+
"field:change"(instance, event) {
|
|
23
|
+
const { id, value } = event.payload
|
|
24
|
+
if (id === "targetOrientation") {
|
|
25
|
+
instance.orientation = -value * pi()
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
"game:update"(instance, event, options) {
|
|
30
|
+
const { input0 } = options.instances
|
|
31
|
+
|
|
32
|
+
if (input0.left || input0.up) {
|
|
33
|
+
instance.orientation += 0.1
|
|
34
|
+
} else if (input0.right || input0.down) {
|
|
35
|
+
instance.orientation -= 0.1
|
|
36
|
+
}
|
|
37
|
+
instance.orientation = clamp(instance.orientation, -pi(), pi())
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
|
|
42
|
+
...enableControls(),
|
|
43
|
+
|
|
44
|
+
character: [
|
|
45
|
+
enableCharacter(),
|
|
46
|
+
{
|
|
47
|
+
"game:update"(instance, event, { dt, instances }) {
|
|
48
|
+
const target = instances.mouse
|
|
49
|
+
const { fields } = instances.parameters.groups.align
|
|
50
|
+
|
|
51
|
+
merge(
|
|
52
|
+
instance,
|
|
53
|
+
align(instance, target, {
|
|
54
|
+
dt,
|
|
55
|
+
targetRadius: fields.targetRadius.value,
|
|
56
|
+
slowRadius: fields.slowRadius.value,
|
|
57
|
+
timeToTarget: fields.timeToTarget.value,
|
|
58
|
+
}),
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
clampToBounds(instance, instances.game.bounds)
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
|
|
66
|
+
form: {
|
|
67
|
+
"field:change"(instance, event) {
|
|
68
|
+
const { id, value } = event.payload
|
|
69
|
+
instance.groups.align.fields[id].value = value
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
instances: {
|
|
75
|
+
mouse: {
|
|
76
|
+
type: "mouse",
|
|
77
|
+
position: [400, 0, 300],
|
|
78
|
+
orientation: 0,
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
...createControls("input0", {
|
|
82
|
+
ArrowLeft: "left",
|
|
83
|
+
ArrowRight: "right",
|
|
84
|
+
ArrowDown: "down",
|
|
85
|
+
ArrowUp: "up",
|
|
86
|
+
}),
|
|
87
|
+
|
|
88
|
+
character: {
|
|
89
|
+
type: "character",
|
|
90
|
+
maxAngularSpeed: pi() / 4,
|
|
91
|
+
maxAngularAcceleration: 10,
|
|
92
|
+
position: [400, 0, 300],
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
parameters: {
|
|
96
|
+
type: "form",
|
|
97
|
+
position: [800 - 328, 0, 600],
|
|
98
|
+
groups: {
|
|
99
|
+
align: {
|
|
100
|
+
title: "Dynamic Align",
|
|
101
|
+
fields: {
|
|
102
|
+
targetRadius: {
|
|
103
|
+
label: "Target Radius",
|
|
104
|
+
inputType: "number",
|
|
105
|
+
defaultValue: DEFAULT_TARGET_RADIUS,
|
|
106
|
+
},
|
|
107
|
+
slowRadius: {
|
|
108
|
+
label: "Slow Radius",
|
|
109
|
+
inputType: "number",
|
|
110
|
+
defaultValue: DEFAULT_SLOW_RADIUS,
|
|
111
|
+
},
|
|
112
|
+
timeToTarget: {
|
|
113
|
+
label: "Time To Target",
|
|
114
|
+
inputType: "number",
|
|
115
|
+
step: 0.1,
|
|
116
|
+
defaultValue: DEFAULT_TIME_TO_TARGET,
|
|
117
|
+
},
|
|
118
|
+
targetOrientation: {
|
|
119
|
+
label: "Target Orientation",
|
|
120
|
+
inputType: "number",
|
|
121
|
+
step: 0.25,
|
|
122
|
+
min: -1,
|
|
123
|
+
max: 1,
|
|
124
|
+
defaultValue: 0,
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import arrive, {
|
|
2
|
+
DEFAULT_SLOW_RADIUS,
|
|
3
|
+
DEFAULT_TARGET_RADIUS,
|
|
4
|
+
DEFAULT_TIME_TO_TARGET,
|
|
5
|
+
} from "@inglorious/engine/ai/movement/dynamic/arrive.js"
|
|
6
|
+
import { clampToBounds } from "@inglorious/game/bounds.js"
|
|
7
|
+
import { enableCharacter } from "@inglorious/game/decorators/character.js"
|
|
8
|
+
import { enableMouse } from "@inglorious/game/decorators/input/mouse.js"
|
|
9
|
+
import { merge } from "@inglorious/utils/data-structures/objects.js"
|
|
10
|
+
|
|
11
|
+
export default {
|
|
12
|
+
types: {
|
|
13
|
+
mouse: [enableMouse()],
|
|
14
|
+
|
|
15
|
+
character: [
|
|
16
|
+
enableCharacter(),
|
|
17
|
+
{
|
|
18
|
+
"game:update"(instance, event, { dt, instances }) {
|
|
19
|
+
const target = instances.mouse
|
|
20
|
+
const { fields } = instances.parameters.groups.arrive
|
|
21
|
+
|
|
22
|
+
merge(
|
|
23
|
+
instance,
|
|
24
|
+
arrive(instance, target, {
|
|
25
|
+
dt,
|
|
26
|
+
targetRadius: fields.targetRadius.value,
|
|
27
|
+
slowRadius: fields.slowRadius.value,
|
|
28
|
+
timeToTarget: fields.timeToTarget.value,
|
|
29
|
+
}),
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
clampToBounds(instance, instances.game.bounds)
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
|
|
37
|
+
form: {
|
|
38
|
+
"field:change"(instance, event) {
|
|
39
|
+
const { id, value } = event.payload
|
|
40
|
+
instance.groups.arrive.fields[id].value = value
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
instances: {
|
|
46
|
+
mouse: {
|
|
47
|
+
type: "mouse",
|
|
48
|
+
position: [400, 0, 300],
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
character: {
|
|
52
|
+
type: "character",
|
|
53
|
+
maxAcceleration: 1000,
|
|
54
|
+
maxSpeed: 250,
|
|
55
|
+
position: [400, 0, 300],
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
parameters: {
|
|
59
|
+
type: "form",
|
|
60
|
+
position: [800 - 328, 0, 600],
|
|
61
|
+
groups: {
|
|
62
|
+
arrive: {
|
|
63
|
+
title: "Dynamic Arrive",
|
|
64
|
+
fields: {
|
|
65
|
+
targetRadius: {
|
|
66
|
+
label: "Target Radius",
|
|
67
|
+
inputType: "number",
|
|
68
|
+
step: 0.1,
|
|
69
|
+
defaultValue: DEFAULT_TARGET_RADIUS,
|
|
70
|
+
},
|
|
71
|
+
slowRadius: {
|
|
72
|
+
label: "Slow Radius",
|
|
73
|
+
inputType: "number",
|
|
74
|
+
step: 0.1,
|
|
75
|
+
defaultValue: DEFAULT_SLOW_RADIUS,
|
|
76
|
+
},
|
|
77
|
+
timeToTarget: {
|
|
78
|
+
label: "Time To Target",
|
|
79
|
+
inputType: "number",
|
|
80
|
+
step: 0.1,
|
|
81
|
+
defaultValue: DEFAULT_TIME_TO_TARGET,
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { Canvas, Meta, Source } from '@storybook/blocks'
|
|
2
|
+
import * as Dynamic from './dynamic.stories'
|
|
3
|
+
|
|
4
|
+
import arrive from './arrive?raw'
|
|
5
|
+
import flee from './flee?raw'
|
|
6
|
+
import pursue from './pursue?raw'
|
|
7
|
+
import evade from './evade?raw'
|
|
8
|
+
import seek from './seek?raw'
|
|
9
|
+
import wander from './wander?raw'
|
|
10
|
+
import lookWhereYoureGoing from './look-where-youre-going?raw'
|
|
11
|
+
import face from './face?raw'
|
|
12
|
+
import align from './align?raw'
|
|
13
|
+
import matchVelocity from './match-velocity?raw'
|
|
14
|
+
|
|
15
|
+
<Meta of={Dynamic} />
|
|
16
|
+
|
|
17
|
+
# Dynamic Movement
|
|
18
|
+
|
|
19
|
+
The algorithms described here make an NPC instance move by applying forces, which in turn affect its acceleration and velocity. The movement is much smoother and more realistic than its [kinematic](/docs/ai-movement-kinematic--docs) counterpart.
|
|
20
|
+
|
|
21
|
+
Every algorithm returns a steering object, which contains a linear and an angular acceleration. These can be used to update the instance's velocity and orientation.
|
|
22
|
+
|
|
23
|
+
Most examples here make use of user [input](/?path=/docs/games-input--docs), so check those out if you haven't already.
|
|
24
|
+
|
|
25
|
+
## Seek
|
|
26
|
+
|
|
27
|
+
The seek algorithm makes the instance chase a target. The movement is much smoother than its kinematic counterpart.
|
|
28
|
+
|
|
29
|
+
<Source dark code={seek} />
|
|
30
|
+
|
|
31
|
+
<Canvas of={Dynamic.Seek} />
|
|
32
|
+
|
|
33
|
+
## Flee
|
|
34
|
+
|
|
35
|
+
The flee algorithm is the exact opposite of seek: the instance will run away from the target.
|
|
36
|
+
|
|
37
|
+
<Source dark code={flee} />
|
|
38
|
+
|
|
39
|
+
<Canvas of={Dynamic.Flee} />
|
|
40
|
+
|
|
41
|
+
## Arrive
|
|
42
|
+
|
|
43
|
+
The arrive algorithm is a more refined version of seek: the instance will slow down as it approaches the target, and the movement is much smoother.
|
|
44
|
+
|
|
45
|
+
<Source dark code={arrive} />
|
|
46
|
+
|
|
47
|
+
<Canvas of={Dynamic.Arrive} />
|
|
48
|
+
|
|
49
|
+
## Pursue
|
|
50
|
+
|
|
51
|
+
The pursue algorithm is an enhanced version of seek. It predicts the future position of a moving target and seeks that position.
|
|
52
|
+
|
|
53
|
+
<Source dark code={pursue} />
|
|
54
|
+
|
|
55
|
+
<Canvas of={Dynamic.Pursue} />
|
|
56
|
+
|
|
57
|
+
## Evade
|
|
58
|
+
|
|
59
|
+
Evade is the opposite of pursue. It predicts the future position of a moving target and flees from it.
|
|
60
|
+
|
|
61
|
+
<Source dark code={evade} />
|
|
62
|
+
|
|
63
|
+
<Canvas of={Dynamic.Evade} />
|
|
64
|
+
|
|
65
|
+
## Wander
|
|
66
|
+
|
|
67
|
+
The wander algorithm makes the instance move around randomly, but in a smooth and realistic way.
|
|
68
|
+
|
|
69
|
+
<Source dark code={wander} />
|
|
70
|
+
|
|
71
|
+
<Canvas of={Dynamic.Wander} />
|
|
72
|
+
|
|
73
|
+
## Look Where You're Going
|
|
74
|
+
|
|
75
|
+
A simple but effective algorithm that aligns the character's orientation with its direction of movement.
|
|
76
|
+
|
|
77
|
+
<Source dark code={lookWhereYoureGoing} />
|
|
78
|
+
|
|
79
|
+
<Canvas of={Dynamic.LookWhereYoureGoing} />
|
|
80
|
+
|
|
81
|
+
## Align and Face
|
|
82
|
+
|
|
83
|
+
The `align` algorithm makes the instance rotate to match the target's orientation, while `face` makes it rotate to look at the target's position. They are smoother versions of their kinematic counterparts.
|
|
84
|
+
|
|
85
|
+
<Source dark code={align} />
|
|
86
|
+
|
|
87
|
+
<Canvas of={Dynamic.Align} />
|
|
88
|
+
|
|
89
|
+
<Source dark code={face} />
|
|
90
|
+
|
|
91
|
+
<Canvas of={Dynamic.Face} />
|
|
92
|
+
|
|
93
|
+
## Match Velocity
|
|
94
|
+
|
|
95
|
+
This algorithm adjusts the instance's velocity to match the target's velocity. It's a key component for flocking behaviors.
|
|
96
|
+
|
|
97
|
+
<Source dark code={matchVelocity} />
|
|
98
|
+
|
|
99
|
+
<Canvas of={Dynamic.MatchVelocity} />
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import UiChooser from "@inglorious/docs/ui-chooser.jsx"
|
|
2
|
+
|
|
3
|
+
import align from "./align.js"
|
|
4
|
+
import arrive from "./arrive.js"
|
|
5
|
+
import evade from "./evade.js"
|
|
6
|
+
import face from "./face.js"
|
|
7
|
+
import flee from "./flee.js"
|
|
8
|
+
import lookWhereYoureGoing from "./look-where-youre-going.js"
|
|
9
|
+
import matchVelocity from "./match-velocity.js"
|
|
10
|
+
import pursue from "./pursue.js"
|
|
11
|
+
import seek from "./seek.js"
|
|
12
|
+
import wander from "./wander.js"
|
|
13
|
+
|
|
14
|
+
export default {
|
|
15
|
+
title: "Engine/AI/Movement/Dynamic",
|
|
16
|
+
component: UiChooser,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const Seek = {
|
|
20
|
+
args: { config: seek },
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const Flee = {
|
|
24
|
+
args: { config: flee },
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const Arrive = {
|
|
28
|
+
args: { config: arrive },
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const Pursue = {
|
|
32
|
+
args: { config: pursue },
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const Evade = {
|
|
36
|
+
args: { config: evade },
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const Wander = {
|
|
40
|
+
args: { config: wander },
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const LookWhereYoureGoing = {
|
|
44
|
+
name: "Look Where You're Going",
|
|
45
|
+
args: { config: lookWhereYoureGoing },
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const Align = {
|
|
49
|
+
args: { config: align },
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const Face = {
|
|
53
|
+
args: { config: face },
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const MatchVelocity = {
|
|
57
|
+
args: { config: matchVelocity },
|
|
58
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import evade, {
|
|
2
|
+
DEFAULT_MAX_PREDICTION,
|
|
3
|
+
} from "@inglorious/engine/ai/movement/dynamic/evade.js"
|
|
4
|
+
import { clampToBounds } from "@inglorious/game/bounds.js"
|
|
5
|
+
import { enableCharacter } from "@inglorious/game/decorators/character.js"
|
|
6
|
+
import { enableMouse } from "@inglorious/game/decorators/input/mouse.js"
|
|
7
|
+
import { merge } from "@inglorious/utils/data-structures/objects.js"
|
|
8
|
+
|
|
9
|
+
export default {
|
|
10
|
+
types: {
|
|
11
|
+
mouse: [enableMouse()],
|
|
12
|
+
|
|
13
|
+
character: [
|
|
14
|
+
enableCharacter(),
|
|
15
|
+
{
|
|
16
|
+
"game:update"(instance, event, { dt, instances }) {
|
|
17
|
+
const target = instances.mouse
|
|
18
|
+
const { fields } = instances.parameters.groups.evade
|
|
19
|
+
|
|
20
|
+
merge(
|
|
21
|
+
instance,
|
|
22
|
+
evade(instance, target, {
|
|
23
|
+
dt,
|
|
24
|
+
maxPrediction: fields.maxPrediction.value,
|
|
25
|
+
}),
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
clampToBounds(instance, instances.game.bounds)
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
|
|
33
|
+
form: {
|
|
34
|
+
"field:change"(instance, event) {
|
|
35
|
+
const { id, value } = event.payload
|
|
36
|
+
instance.groups.evade.fields[id].value = value
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
instances: {
|
|
42
|
+
mouse: {
|
|
43
|
+
type: "mouse",
|
|
44
|
+
position: [400, 0, 300],
|
|
45
|
+
velocity: [0, 0, 0],
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
character: {
|
|
49
|
+
type: "character",
|
|
50
|
+
maxAcceleration: 1000,
|
|
51
|
+
maxSpeed: 250,
|
|
52
|
+
position: [400, 0, 300],
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
parameters: {
|
|
56
|
+
type: "form",
|
|
57
|
+
position: [800 - 343, 0, 600],
|
|
58
|
+
groups: {
|
|
59
|
+
evade: {
|
|
60
|
+
title: "Evade",
|
|
61
|
+
fields: {
|
|
62
|
+
maxPrediction: {
|
|
63
|
+
label: "Max Prediction",
|
|
64
|
+
inputType: "number",
|
|
65
|
+
defaultValue: DEFAULT_MAX_PREDICTION,
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_SLOW_RADIUS,
|
|
3
|
+
DEFAULT_TARGET_RADIUS,
|
|
4
|
+
DEFAULT_TIME_TO_TARGET,
|
|
5
|
+
} from "@inglorious/engine/ai/movement/dynamic/align.js"
|
|
6
|
+
import face from "@inglorious/engine/ai/movement/dynamic/face.js"
|
|
7
|
+
import { clampToBounds } from "@inglorious/game/bounds.js"
|
|
8
|
+
import { enableCharacter } from "@inglorious/game/decorators/character.js"
|
|
9
|
+
import { enableMouse } from "@inglorious/game/decorators/input/mouse.js"
|
|
10
|
+
import { merge } from "@inglorious/utils/data-structures/objects.js"
|
|
11
|
+
import { pi } from "@inglorious/utils/math/trigonometry.js"
|
|
12
|
+
|
|
13
|
+
export default {
|
|
14
|
+
types: {
|
|
15
|
+
mouse: [enableMouse()],
|
|
16
|
+
|
|
17
|
+
character: [
|
|
18
|
+
enableCharacter(),
|
|
19
|
+
{
|
|
20
|
+
"game:update"(instance, event, { dt, instances }) {
|
|
21
|
+
const target = instances.mouse
|
|
22
|
+
const { fields } = instances.parameters.groups.face
|
|
23
|
+
|
|
24
|
+
merge(
|
|
25
|
+
instance,
|
|
26
|
+
face(instance, target, {
|
|
27
|
+
dt,
|
|
28
|
+
targetRadius: fields.targetRadius.value,
|
|
29
|
+
slowRadius: fields.slowRadius.value,
|
|
30
|
+
timeToTarget: fields.timeToTarget.value,
|
|
31
|
+
}),
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
clampToBounds(instance, instances.game.bounds)
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
|
|
39
|
+
form: {
|
|
40
|
+
"field:change"(instance, event) {
|
|
41
|
+
const { id, value } = event.payload
|
|
42
|
+
instance.groups.face.fields[id].value = value
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
instances: {
|
|
48
|
+
mouse: {
|
|
49
|
+
type: "mouse",
|
|
50
|
+
position: [400, 0, 300],
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
character: {
|
|
54
|
+
type: "character",
|
|
55
|
+
maxAngularSpeed: pi() / 4,
|
|
56
|
+
maxAngularAcceleration: 1000,
|
|
57
|
+
position: [400, 0, 300],
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
parameters: {
|
|
61
|
+
type: "form",
|
|
62
|
+
position: [800 - 328, 0, 600],
|
|
63
|
+
groups: {
|
|
64
|
+
face: {
|
|
65
|
+
title: "Face",
|
|
66
|
+
fields: {
|
|
67
|
+
targetRadius: {
|
|
68
|
+
label: "Target Radius",
|
|
69
|
+
inputType: "number",
|
|
70
|
+
step: 0.1,
|
|
71
|
+
defaultValue: DEFAULT_TARGET_RADIUS,
|
|
72
|
+
},
|
|
73
|
+
slowRadius: {
|
|
74
|
+
label: "Slow Radius",
|
|
75
|
+
inputType: "number",
|
|
76
|
+
step: 0.1,
|
|
77
|
+
defaultValue: DEFAULT_SLOW_RADIUS,
|
|
78
|
+
},
|
|
79
|
+
timeToTarget: {
|
|
80
|
+
label: "Time To Target",
|
|
81
|
+
inputType: "number",
|
|
82
|
+
step: 0.1,
|
|
83
|
+
defaultValue: DEFAULT_TIME_TO_TARGET,
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
}
|