@inglorious/engine 0.1.1 → 0.3.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 +75 -72
- package/package.json +15 -37
- package/src/{engine/ai → ai}/movement/dynamic/align.js +9 -9
- package/src/{engine/ai → ai}/movement/dynamic/arrive.js +9 -10
- package/src/{engine/ai → ai}/movement/dynamic/evade.js +9 -9
- package/src/{engine/ai → ai}/movement/dynamic/face.js +5 -6
- package/src/{engine/ai/movement/dynamic/seek.js → ai/movement/dynamic/flee.js} +8 -7
- package/src/ai/movement/dynamic/look-where-youre-going.js +16 -0
- package/src/{engine/ai → ai}/movement/dynamic/match-velocity.js +9 -8
- package/src/{engine/ai → ai}/movement/dynamic/pursue.js +9 -9
- package/src/{engine/ai/movement/dynamic/flee.js → ai/movement/dynamic/seek.js} +7 -8
- package/src/{engine/ai → ai}/movement/dynamic/wander.js +9 -10
- package/src/{engine/ai → ai}/movement/kinematic/align.js +7 -7
- package/src/{engine/ai → ai}/movement/kinematic/arrive.js +8 -8
- package/src/{engine/ai → ai}/movement/kinematic/face.js +5 -6
- package/src/{engine/ai → ai}/movement/kinematic/flee.js +5 -5
- package/src/{engine/ai → ai}/movement/kinematic/seek.js +5 -5
- package/src/{engine/ai → ai}/movement/kinematic/seek.test.js +10 -10
- package/src/{engine/ai → ai}/movement/kinematic/wander-as-seek.js +9 -9
- package/src/{engine/ai → ai}/movement/kinematic/wander.js +5 -5
- package/src/animation/sprite.js +101 -0
- package/src/animation/ticker.js +38 -0
- package/src/behaviors/camera.js +68 -0
- package/src/behaviors/controls/dynamic/modern.js +76 -0
- package/src/behaviors/controls/dynamic/shooter.js +84 -0
- package/src/behaviors/controls/dynamic/tank.js +69 -0
- package/src/behaviors/controls/event-handlers.js +17 -0
- package/src/behaviors/controls/kinematic/modern.js +76 -0
- package/src/behaviors/controls/kinematic/shooter.js +82 -0
- package/src/behaviors/controls/kinematic/tank.js +67 -0
- package/src/behaviors/debug/collision.js +29 -0
- package/src/behaviors/fps.js +29 -0
- package/src/behaviors/fsm.js +33 -0
- package/src/{game/decorators → behaviors}/fsm.test.js +15 -22
- package/src/behaviors/game.js +15 -0
- package/src/behaviors/input/controls.js +37 -0
- package/src/behaviors/input/gamepad.js +114 -0
- package/src/behaviors/input/input.js +48 -0
- package/src/behaviors/input/keyboard.js +64 -0
- package/src/behaviors/input/mouse.js +91 -0
- package/src/behaviors/physics/bouncy.js +25 -0
- package/src/behaviors/physics/clamped.js +36 -0
- package/src/{game/decorators/collisions.js → behaviors/physics/collidable.js} +3 -7
- package/src/behaviors/physics/jumpable.js +145 -0
- package/src/behaviors/ui/button.js +17 -0
- package/src/collision/detection.js +110 -0
- package/src/core/api.js +34 -0
- package/src/core/dev-tools.js +135 -0
- package/src/core/engine.js +119 -0
- package/src/core/loop.js +15 -0
- package/src/{engine/loop → core/loops}/animation-frame.js +1 -2
- package/src/{engine/loop → core/loops}/elapsed.js +1 -2
- package/src/{engine/loop → core/loops}/fixed.js +1 -2
- package/src/{engine/loop → core/loops}/flash.js +1 -2
- package/src/{engine/loop → core/loops}/lag.js +1 -2
- package/src/core/select.js +26 -0
- package/src/core/store.js +178 -0
- package/src/core/store.test.js +110 -0
- package/src/main.js +7 -2
- package/src/{engine/movement → movement}/dynamic/modern.js +3 -6
- package/src/{engine/movement → movement}/dynamic/tank.js +9 -9
- package/src/{engine/movement → movement}/kinematic/modern.js +3 -3
- package/src/movement/kinematic/modern.test.js +27 -0
- package/src/{engine/movement → movement}/kinematic/tank.js +5 -5
- package/src/physics/bounds.js +138 -0
- package/src/physics/position.js +43 -0
- package/src/physics/position.test.js +80 -0
- package/src/systems/sprite-animation.js +27 -0
- package/src/engine/ai/movement/dynamic/look-where-youre-going.js +0 -17
- package/src/engine/collision/detection.js +0 -115
- package/src/engine/loop.js +0 -15
- package/src/engine/movement/kinematic/modern.test.js +0 -27
- package/src/engine/store.js +0 -174
- package/src/engine/store.test.js +0 -256
- package/src/engine.js +0 -74
- package/src/game/animation.js +0 -26
- package/src/game/bounds.js +0 -66
- package/src/game/decorators/character.js +0 -5
- package/src/game/decorators/clamp-to-bounds.js +0 -15
- package/src/game/decorators/controls/dynamic/modern.js +0 -48
- package/src/game/decorators/controls/dynamic/shooter.js +0 -47
- package/src/game/decorators/controls/dynamic/tank.js +0 -55
- package/src/game/decorators/controls/kinematic/modern.js +0 -49
- package/src/game/decorators/controls/kinematic/shooter.js +0 -45
- package/src/game/decorators/controls/kinematic/tank.js +0 -52
- package/src/game/decorators/debug/collisions.js +0 -32
- package/src/game/decorators/double-jump.js +0 -70
- package/src/game/decorators/fps.js +0 -30
- package/src/game/decorators/fsm.js +0 -27
- package/src/game/decorators/game.js +0 -11
- package/src/game/decorators/image/image.js +0 -5
- package/src/game/decorators/image/sprite.js +0 -5
- package/src/game/decorators/image/tilemap.js +0 -5
- package/src/game/decorators/input/controls.js +0 -27
- package/src/game/decorators/input/gamepad.js +0 -74
- package/src/game/decorators/input/input.js +0 -41
- package/src/game/decorators/input/keyboard.js +0 -49
- package/src/game/decorators/input/mouse.js +0 -65
- package/src/game/decorators/jump.js +0 -72
- package/src/game/decorators/platform.js +0 -5
- package/src/game/decorators/ui/button.js +0 -21
- package/src/game/sprite.js +0 -119
- package/src/ui/canvas/absolute-position.js +0 -17
- package/src/ui/canvas/character.js +0 -35
- package/src/ui/canvas/form/button.js +0 -25
- package/src/ui/canvas/fps.js +0 -18
- package/src/ui/canvas/image/hitmask.js +0 -37
- package/src/ui/canvas/image/image.js +0 -37
- package/src/ui/canvas/image/sprite.js +0 -49
- package/src/ui/canvas/image/tilemap.js +0 -64
- package/src/ui/canvas/mouse.js +0 -37
- package/src/ui/canvas/shapes/circle.js +0 -31
- package/src/ui/canvas/shapes/rectangle.js +0 -31
- package/src/ui/canvas.js +0 -81
- package/src/ui/react/game/character/character.module.scss +0 -17
- package/src/ui/react/game/character/index.jsx +0 -30
- package/src/ui/react/game/cursor/cursor.module.scss +0 -47
- package/src/ui/react/game/cursor/index.jsx +0 -20
- package/src/ui/react/game/form/fields/field/field.module.scss +0 -5
- package/src/ui/react/game/form/fields/field/index.jsx +0 -56
- package/src/ui/react/game/form/fields/fields.module.scss +0 -48
- package/src/ui/react/game/form/fields/index.jsx +0 -12
- package/src/ui/react/game/form/form.module.scss +0 -18
- package/src/ui/react/game/form/index.jsx +0 -22
- package/src/ui/react/game/fps/index.jsx +0 -16
- package/src/ui/react/game/game.jsx +0 -71
- package/src/ui/react/game/index.jsx +0 -29
- package/src/ui/react/game/platform/index.jsx +0 -30
- package/src/ui/react/game/platform/platform.module.scss +0 -7
- package/src/ui/react/game/scene/index.jsx +0 -25
- package/src/ui/react/game/scene/scene.module.scss +0 -9
- package/src/ui/react/game/sprite/index.jsx +0 -58
- package/src/ui/react/game/sprite/sprite.module.css +0 -3
- package/src/ui/react/game/stats/index.jsx +0 -22
- package/src/ui/react/hocs/with-absolute-position/index.jsx +0 -20
- package/src/ui/react/hocs/with-absolute-position/with-absolute-position.module.scss +0 -5
- package/src/ui/react/index.jsx +0 -9
- package/src/utils/algorithms/decision-tree.js +0 -24
- package/src/utils/algorithms/decision-tree.test.js +0 -102
- package/src/utils/algorithms/path-finding.js +0 -155
- package/src/utils/algorithms/path-finding.test.js +0 -151
- package/src/utils/algorithms/types.d.ts +0 -28
- package/src/utils/data-structures/array.js +0 -83
- package/src/utils/data-structures/array.test.js +0 -173
- package/src/utils/data-structures/board.js +0 -159
- package/src/utils/data-structures/board.test.js +0 -242
- package/src/utils/data-structures/boolean.js +0 -9
- package/src/utils/data-structures/heap.js +0 -164
- package/src/utils/data-structures/heap.test.js +0 -103
- package/src/utils/data-structures/object.js +0 -102
- package/src/utils/data-structures/object.test.js +0 -121
- package/src/utils/data-structures/objects.js +0 -48
- package/src/utils/data-structures/objects.test.js +0 -99
- package/src/utils/data-structures/tree.js +0 -36
- package/src/utils/data-structures/tree.test.js +0 -33
- package/src/utils/data-structures/types.d.ts +0 -4
- package/src/utils/functions/functions.js +0 -19
- package/src/utils/functions/functions.test.js +0 -23
- package/src/utils/math/geometry/circle.js +0 -117
- package/src/utils/math/geometry/circle.test.js +0 -97
- package/src/utils/math/geometry/hitmask.js +0 -39
- package/src/utils/math/geometry/hitmask.test.js +0 -84
- package/src/utils/math/geometry/line.js +0 -35
- package/src/utils/math/geometry/line.test.js +0 -49
- package/src/utils/math/geometry/platform.js +0 -42
- package/src/utils/math/geometry/platform.test.js +0 -133
- package/src/utils/math/geometry/point.js +0 -71
- package/src/utils/math/geometry/point.test.js +0 -81
- package/src/utils/math/geometry/rectangle.js +0 -45
- package/src/utils/math/geometry/rectangle.test.js +0 -42
- package/src/utils/math/geometry/segment.js +0 -80
- package/src/utils/math/geometry/segment.test.js +0 -183
- package/src/utils/math/geometry/triangle.js +0 -15
- package/src/utils/math/geometry/triangle.test.js +0 -11
- package/src/utils/math/geometry/types.d.ts +0 -23
- package/src/utils/math/linear-algebra/2d.js +0 -28
- package/src/utils/math/linear-algebra/2d.test.js +0 -17
- package/src/utils/math/linear-algebra/quaternion.js +0 -22
- package/src/utils/math/linear-algebra/quaternion.test.js +0 -25
- package/src/utils/math/linear-algebra/quaternions.js +0 -20
- package/src/utils/math/linear-algebra/quaternions.test.js +0 -29
- package/src/utils/math/linear-algebra/types.d.ts +0 -4
- package/src/utils/math/linear-algebra/vector.js +0 -302
- package/src/utils/math/linear-algebra/vector.test.js +0 -257
- package/src/utils/math/linear-algebra/vectors.js +0 -122
- package/src/utils/math/linear-algebra/vectors.test.js +0 -65
- package/src/utils/math/numbers.js +0 -90
- package/src/utils/math/numbers.test.js +0 -137
- package/src/utils/math/rng.js +0 -44
- package/src/utils/math/rng.test.js +0 -39
- package/src/utils/math/statistics.js +0 -43
- package/src/utils/math/statistics.test.js +0 -47
- package/src/utils/math/trigonometry.js +0 -89
- package/src/utils/math/trigonometry.test.js +0 -52
- package/src/utils/physics/acceleration.js +0 -63
- package/src/utils/physics/friction.js +0 -30
- package/src/utils/physics/friction.test.js +0 -44
- package/src/utils/physics/gravity.js +0 -71
- package/src/utils/physics/gravity.test.js +0 -80
- package/src/utils/physics/jump.js +0 -41
- package/src/utils/physics/velocity.js +0 -38
|
@@ -14,28 +14,28 @@ const DEFAULT_MAX_SPEED = 0
|
|
|
14
14
|
|
|
15
15
|
const MIN_SPEED = 0
|
|
16
16
|
|
|
17
|
-
export
|
|
18
|
-
|
|
17
|
+
export function arrive(
|
|
18
|
+
entity,
|
|
19
19
|
target,
|
|
20
|
+
dt,
|
|
20
21
|
{
|
|
21
|
-
dt,
|
|
22
22
|
targetRadius = DEFAULT_TARGET_RADIUS,
|
|
23
23
|
timeToTarget = DEFAULT_TIME_TO_TARGET,
|
|
24
|
-
},
|
|
24
|
+
} = {},
|
|
25
25
|
) {
|
|
26
|
-
const maxSpeed =
|
|
26
|
+
const maxSpeed = entity.maxSpeed ?? DEFAULT_MAX_SPEED
|
|
27
27
|
|
|
28
|
-
const direction = subtract(target.position,
|
|
28
|
+
const direction = subtract(target.position, entity.position)
|
|
29
29
|
const distance = magnitude(direction)
|
|
30
30
|
|
|
31
31
|
if (distance < targetRadius) {
|
|
32
|
-
return
|
|
32
|
+
return entity
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
let velocity = divide(direction, timeToTarget)
|
|
36
36
|
velocity = clamp(velocity, MIN_SPEED, maxSpeed)
|
|
37
37
|
|
|
38
|
-
const position = sum(
|
|
38
|
+
const position = sum(entity.position, multiply(velocity, dt))
|
|
39
39
|
const orientation = angle(velocity)
|
|
40
40
|
|
|
41
41
|
return { velocity, position, orientation }
|
|
@@ -1,20 +1,19 @@
|
|
|
1
|
+
import { align } from "@inglorious/engine/ai/movement/kinematic/align.js"
|
|
1
2
|
import {
|
|
2
3
|
angle,
|
|
3
4
|
magnitude,
|
|
4
5
|
} from "@inglorious/utils/math/linear-algebra/vector.js"
|
|
5
6
|
import { subtract } from "@inglorious/utils/math/linear-algebra/vectors.js"
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export default function face(instance, target, options) {
|
|
10
|
-
const direction = subtract(target.position, instance.position)
|
|
8
|
+
export function face(entity, target, dt, options) {
|
|
9
|
+
const direction = subtract(target.position, entity.position)
|
|
11
10
|
const distance = magnitude(direction)
|
|
12
11
|
|
|
13
12
|
if (!distance) {
|
|
14
|
-
return
|
|
13
|
+
return entity
|
|
15
14
|
}
|
|
16
15
|
|
|
17
16
|
const orientation = angle(direction)
|
|
18
17
|
|
|
19
|
-
return align(
|
|
18
|
+
return align(entity, { ...target, orientation }, dt, options)
|
|
20
19
|
}
|
|
@@ -8,18 +8,18 @@ import { subtract, sum } from "@inglorious/utils/math/linear-algebra/vectors.js"
|
|
|
8
8
|
|
|
9
9
|
const DEFAULT_MAX_SPEED = 0
|
|
10
10
|
|
|
11
|
-
export
|
|
12
|
-
const maxSpeed =
|
|
11
|
+
export function flee(entity, target, dt) {
|
|
12
|
+
const maxSpeed = entity.maxSpeed ?? DEFAULT_MAX_SPEED
|
|
13
13
|
|
|
14
|
-
const direction = subtract(
|
|
14
|
+
const direction = subtract(entity.position, target.position)
|
|
15
15
|
const distance = magnitude(direction)
|
|
16
16
|
|
|
17
17
|
if (!distance) {
|
|
18
|
-
return
|
|
18
|
+
return entity
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
const velocity = setMagnitude(direction, maxSpeed)
|
|
22
|
-
const position = sum(
|
|
22
|
+
const position = sum(entity.position, multiply(velocity, dt))
|
|
23
23
|
const orientation = angle(velocity)
|
|
24
24
|
|
|
25
25
|
return { velocity, position, orientation }
|
|
@@ -8,18 +8,18 @@ import { subtract, sum } from "@inglorious/utils/math/linear-algebra/vectors.js"
|
|
|
8
8
|
|
|
9
9
|
const DEFAULT_MAX_SPEED = 0
|
|
10
10
|
|
|
11
|
-
export
|
|
12
|
-
const maxSpeed =
|
|
11
|
+
export function seek(entity, target, dt) {
|
|
12
|
+
const maxSpeed = entity.maxSpeed ?? DEFAULT_MAX_SPEED
|
|
13
13
|
|
|
14
|
-
const direction = subtract(target.position,
|
|
14
|
+
const direction = subtract(target.position, entity.position)
|
|
15
15
|
const distance = magnitude(direction)
|
|
16
16
|
|
|
17
17
|
if (!distance) {
|
|
18
|
-
return
|
|
18
|
+
return entity
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
const velocity = setMagnitude(direction, maxSpeed)
|
|
22
|
-
const position = sum(
|
|
22
|
+
const position = sum(entity.position, multiply(velocity, dt))
|
|
23
23
|
const orientation = angle(velocity)
|
|
24
24
|
|
|
25
25
|
return { velocity, position, orientation }
|
|
@@ -1,42 +1,42 @@
|
|
|
1
1
|
import { expect, test } from "vitest"
|
|
2
2
|
|
|
3
|
-
import seek from "./seek.js"
|
|
3
|
+
import { seek } from "./seek.js"
|
|
4
4
|
|
|
5
5
|
test("it should move toward the target", () => {
|
|
6
|
-
const
|
|
6
|
+
const entity = { maxSpeed: 1, position: [0, 0, 0] }
|
|
7
7
|
const target = { position: [2, 0, 0] }
|
|
8
|
-
const
|
|
8
|
+
const dt = 1
|
|
9
9
|
const expectedResult = {
|
|
10
10
|
position: [1, 0, 0],
|
|
11
11
|
velocity: [1, 0, 0],
|
|
12
12
|
orientation: 0,
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
expect(seek(
|
|
15
|
+
expect(seek(entity, target, dt)).toStrictEqual(expectedResult)
|
|
16
16
|
})
|
|
17
17
|
|
|
18
18
|
test("it should eventually reach the target", () => {
|
|
19
|
-
const
|
|
19
|
+
const entity = { maxSpeed: 1, position: [0, 0, 0] }
|
|
20
20
|
const target = { position: [2, 0, 0] }
|
|
21
|
-
const
|
|
21
|
+
const dt = 2
|
|
22
22
|
const expectedResult = {
|
|
23
23
|
position: [2, 0, 0],
|
|
24
24
|
velocity: [1, 0, 0],
|
|
25
25
|
orientation: 0,
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
expect(seek(
|
|
28
|
+
expect(seek(entity, target, dt)).toStrictEqual(expectedResult)
|
|
29
29
|
})
|
|
30
30
|
|
|
31
31
|
test("it should overshoot when speed is too high", () => {
|
|
32
|
-
const
|
|
32
|
+
const entity = { maxSpeed: 10, position: [0, 0, 0] }
|
|
33
33
|
const target = { position: [2, 0, 0] }
|
|
34
|
-
const
|
|
34
|
+
const dt = 1
|
|
35
35
|
const expectedResult = {
|
|
36
36
|
position: [10, 0, 0],
|
|
37
37
|
velocity: [10, 0, 0],
|
|
38
38
|
orientation: 0,
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
expect(seek(
|
|
41
|
+
expect(seek(entity, target, dt)).toStrictEqual(expectedResult)
|
|
42
42
|
})
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { seek } from "@inglorious/engine/ai/movement/kinematic/seek.js"
|
|
1
2
|
import {
|
|
2
3
|
fromAngle,
|
|
3
4
|
multiply,
|
|
@@ -5,27 +6,26 @@ import {
|
|
|
5
6
|
import { sum } from "@inglorious/utils/math/linear-algebra/vectors.js"
|
|
6
7
|
import { randomBinomial } from "@inglorious/utils/math/rng.js"
|
|
7
8
|
|
|
8
|
-
import seek from "./seek.js"
|
|
9
|
-
|
|
10
9
|
export const DEFAULT_WANDER_RADIUS = 10
|
|
11
10
|
|
|
12
11
|
const DEFAULT_MAX_ANGULAR_SPEED = 0
|
|
13
12
|
|
|
14
13
|
const DEFAULT_ORIENTATION = 0
|
|
15
14
|
|
|
16
|
-
export
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
export function wanderAsSeek(
|
|
16
|
+
entity,
|
|
17
|
+
dt,
|
|
18
|
+
{ wanderRadius = DEFAULT_WANDER_RADIUS } = {},
|
|
19
19
|
) {
|
|
20
|
-
const maxAngularSpeed =
|
|
20
|
+
const maxAngularSpeed = entity.maxAngularSpeed ?? DEFAULT_MAX_ANGULAR_SPEED
|
|
21
21
|
|
|
22
|
-
let orientation =
|
|
22
|
+
let orientation = entity.orientation ?? DEFAULT_ORIENTATION
|
|
23
23
|
orientation += randomBinomial() * maxAngularSpeed
|
|
24
24
|
|
|
25
25
|
const position = sum(
|
|
26
|
-
|
|
26
|
+
entity.position,
|
|
27
27
|
multiply(fromAngle(orientation), wanderRadius),
|
|
28
28
|
)
|
|
29
29
|
|
|
30
|
-
return seek(
|
|
30
|
+
return seek(entity, { position }, dt)
|
|
31
31
|
}
|
|
@@ -11,16 +11,16 @@ const DEFAULT_MAX_ANGULAR_SPEED = 0
|
|
|
11
11
|
|
|
12
12
|
const DEFAULT_ORIENTATION = 0
|
|
13
13
|
|
|
14
|
-
export
|
|
15
|
-
const maxSpeed =
|
|
16
|
-
const maxAngularSpeed =
|
|
14
|
+
export function wander(entity, dt) {
|
|
15
|
+
const maxSpeed = entity.maxSpeed ?? DEFAULT_MAX_SPEED
|
|
16
|
+
const maxAngularSpeed = entity.maxAngularSpeed ?? DEFAULT_MAX_ANGULAR_SPEED
|
|
17
17
|
|
|
18
|
-
let orientation =
|
|
18
|
+
let orientation = entity.orientation ?? DEFAULT_ORIENTATION
|
|
19
19
|
orientation += randomBinomial() * maxAngularSpeed
|
|
20
20
|
|
|
21
21
|
const velocity = createVector(maxSpeed, orientation)
|
|
22
22
|
|
|
23
|
-
const position = sum(
|
|
23
|
+
const position = sum(entity.position, multiply(velocity, dt))
|
|
24
24
|
orientation = angle(velocity)
|
|
25
25
|
|
|
26
26
|
return { velocity, position, orientation }
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/* eslint-disable no-magic-numbers */
|
|
2
|
+
import { Ticker } from "@inglorious/engine/animation/ticker.js"
|
|
3
|
+
import { mod } from "@inglorious/utils/math/numbers.js"
|
|
4
|
+
import { pi, toRange } from "@inglorious/utils/math/trigonometry.js"
|
|
5
|
+
|
|
6
|
+
export const Sprite = {
|
|
7
|
+
fromAngle,
|
|
8
|
+
move2,
|
|
9
|
+
move4,
|
|
10
|
+
move6,
|
|
11
|
+
move8,
|
|
12
|
+
play,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function move2(entity) {
|
|
16
|
+
return fromAngle(entity, ["right", "left"]) ?? "right"
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function move4(entity) {
|
|
20
|
+
return fromAngle(entity, ["right", "up", "left", "down"]) ?? "down"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function move6(entity) {
|
|
24
|
+
return (
|
|
25
|
+
fromAngle(entity, [
|
|
26
|
+
"right",
|
|
27
|
+
"rightUp",
|
|
28
|
+
"leftUp",
|
|
29
|
+
"left",
|
|
30
|
+
"leftDown",
|
|
31
|
+
"rightDown",
|
|
32
|
+
]) ?? "down"
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function move8(entity) {
|
|
37
|
+
return (
|
|
38
|
+
fromAngle(entity, [
|
|
39
|
+
"right",
|
|
40
|
+
"rightUp",
|
|
41
|
+
"up",
|
|
42
|
+
"leftUp",
|
|
43
|
+
"left",
|
|
44
|
+
"leftDown",
|
|
45
|
+
"down",
|
|
46
|
+
"rightDown",
|
|
47
|
+
]) ?? "down"
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function play(animation, { entity, dt, notify }) {
|
|
52
|
+
const missing = [
|
|
53
|
+
animation == null && "'animation'",
|
|
54
|
+
entity == null && "'entity'",
|
|
55
|
+
dt == null && "'dt'",
|
|
56
|
+
notify == null && "'notify'",
|
|
57
|
+
]
|
|
58
|
+
.filter(Boolean)
|
|
59
|
+
.join(", ")
|
|
60
|
+
if (missing.length) {
|
|
61
|
+
throw new Error(`Sprite.play is missing mandatory parameters: ${missing}`)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
Ticker.tick({
|
|
65
|
+
target: entity.sprite,
|
|
66
|
+
state: animation,
|
|
67
|
+
dt,
|
|
68
|
+
onTick: (sprite) => {
|
|
69
|
+
const { frames, state: animation } = sprite
|
|
70
|
+
const framesLength = frames[animation].length
|
|
71
|
+
sprite.value = mod(sprite.value + 1, framesLength)
|
|
72
|
+
if (sprite.value === framesLength - 1) {
|
|
73
|
+
notify("spriteAnimationEnd", { entityId: entity.id, animation })
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Determines a sprite state from an orientation angle, based on a list of states.
|
|
81
|
+
* The states are assumed to be ordered starting from the right (0 radians) and
|
|
82
|
+
* proceeding counter-clockwise.
|
|
83
|
+
*
|
|
84
|
+
* @param {object} entity - The entity with an orientation.
|
|
85
|
+
* @param {string[]} states - An array of state names corresponding to directions.
|
|
86
|
+
* @returns {string} The calculated state name.
|
|
87
|
+
*/
|
|
88
|
+
function fromAngle(entity, states) {
|
|
89
|
+
const directions = states.length
|
|
90
|
+
const slice = (2 * pi()) / directions
|
|
91
|
+
|
|
92
|
+
// Normalize orientation to [0, 2*PI)
|
|
93
|
+
const normalizedOrientation = mod(toRange(entity.orientation), 2 * pi())
|
|
94
|
+
|
|
95
|
+
// Shift by half a slice so that 0 rad is in the middle of the first slice, then find the index
|
|
96
|
+
const index = Math.floor(
|
|
97
|
+
mod(normalizedOrientation + slice / 2, 2 * pi()) / slice,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
return states[index] ?? entity.sprite.state
|
|
101
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const DEFAULT_STATE = "default"
|
|
2
|
+
const DEFAULT_VALUE = 0
|
|
3
|
+
const COUNTER_RESET = 0
|
|
4
|
+
|
|
5
|
+
export const Ticker = { tick }
|
|
6
|
+
|
|
7
|
+
function tick({ target, state = DEFAULT_STATE, dt, onTick, ...options }) {
|
|
8
|
+
const missing = [target == null && "'target'", dt == null && "'dt'"]
|
|
9
|
+
.filter(Boolean)
|
|
10
|
+
.join(", ")
|
|
11
|
+
if (missing.length) {
|
|
12
|
+
throw new Error(`Ticker.tick is missing mandatory parameters: ${missing}`)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const { speed, defaultValue = DEFAULT_VALUE } = target
|
|
16
|
+
|
|
17
|
+
// The `state` property on a sprite is used to declare the desired animation.
|
|
18
|
+
// The Ticker needs its own internal property to track the *current* animation
|
|
19
|
+
// to detect when it changes. We'll use `target.animation`.
|
|
20
|
+
if (state !== target.animation) {
|
|
21
|
+
target.animation = state
|
|
22
|
+
target.counter = COUNTER_RESET
|
|
23
|
+
target.value = defaultValue
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Always ensure the public `state` property reflects the intended animation for `onTick`.
|
|
27
|
+
target.state = state
|
|
28
|
+
|
|
29
|
+
// Ensure properties are initialized on the first run for a given target.
|
|
30
|
+
target.counter ??= COUNTER_RESET
|
|
31
|
+
target.value ??= defaultValue
|
|
32
|
+
|
|
33
|
+
target.counter += dt
|
|
34
|
+
if (target.counter >= speed) {
|
|
35
|
+
target.counter = COUNTER_RESET
|
|
36
|
+
onTick?.(target, dt, options)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { arrive } from "@inglorious/engine/ai/movement/dynamic/arrive.js"
|
|
2
|
+
import {
|
|
3
|
+
defaults,
|
|
4
|
+
extend,
|
|
5
|
+
merge,
|
|
6
|
+
} from "@inglorious/utils/data-structures/objects.js"
|
|
7
|
+
import { lerp } from "@inglorious/utils/math/linear-interpolation.js"
|
|
8
|
+
|
|
9
|
+
const DEFAULT_PARAMS = {
|
|
10
|
+
zoom: 1,
|
|
11
|
+
zoomSpeed: 0.1,
|
|
12
|
+
zoomSensitivity: 5,
|
|
13
|
+
minZoom: 0.5,
|
|
14
|
+
maxZoom: 4,
|
|
15
|
+
targetId: null,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const X = 0
|
|
19
|
+
const Y = 1
|
|
20
|
+
|
|
21
|
+
export function camera(params) {
|
|
22
|
+
params = extend(DEFAULT_PARAMS, params)
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
start(entity) {
|
|
26
|
+
defaults(entity, params)
|
|
27
|
+
entity.targetZoom = entity.zoom
|
|
28
|
+
// Cache the initial size to calculate the viewport in dev mode
|
|
29
|
+
if (entity.size) {
|
|
30
|
+
entity.baseSize = [...entity.size]
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
update(entity, dt, api) {
|
|
35
|
+
// Follow target
|
|
36
|
+
if (entity.targetId) {
|
|
37
|
+
const target = api.getEntity(entity.targetId)
|
|
38
|
+
if (target) {
|
|
39
|
+
// The camera will "arrive" at the target's position.
|
|
40
|
+
// You can tweak the parameters in the entity definition
|
|
41
|
+
// to change how the camera follows the target.
|
|
42
|
+
merge(entity, arrive(entity, target, dt))
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Smooth zoom
|
|
47
|
+
if (entity.zoom !== entity.targetZoom) {
|
|
48
|
+
entity.zoom = lerp(entity.zoom, entity.targetZoom, entity.zoomSpeed)
|
|
49
|
+
|
|
50
|
+
// Update the visual size of the camera's viewport for dev mode
|
|
51
|
+
if (entity.baseSize) {
|
|
52
|
+
entity.size[X] = entity.baseSize[X] / entity.zoom
|
|
53
|
+
entity.size[Y] = entity.baseSize[Y] / entity.zoom
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
mouseWheel(entity, { deltaY }) {
|
|
59
|
+
const delta = Math.sign(deltaY)
|
|
60
|
+
// Scrolling down (positive deltaY) should zoom out (decrease zoom value)
|
|
61
|
+
entity.targetZoom -= delta * entity.zoomSpeed * entity.zoomSensitivity
|
|
62
|
+
entity.targetZoom = Math.max(
|
|
63
|
+
entity.minZoom,
|
|
64
|
+
Math.min(entity.maxZoom, entity.targetZoom),
|
|
65
|
+
)
|
|
66
|
+
},
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { modernMove } from "@inglorious/engine/movement/dynamic/modern.js"
|
|
2
|
+
import { extend, merge } from "@inglorious/utils/data-structures/objects.js"
|
|
3
|
+
import { zero } from "@inglorious/utils/math/linear-algebra/vector.js"
|
|
4
|
+
|
|
5
|
+
import { createMovementEventHandlers } from "../event-handlers.js"
|
|
6
|
+
|
|
7
|
+
const DEFAULT_PARAMS = {
|
|
8
|
+
maxAcceleration: 500,
|
|
9
|
+
}
|
|
10
|
+
const X = 0
|
|
11
|
+
const Z = 2
|
|
12
|
+
|
|
13
|
+
export function modernAcceleration(params) {
|
|
14
|
+
params = extend(DEFAULT_PARAMS, params)
|
|
15
|
+
|
|
16
|
+
return (type) =>
|
|
17
|
+
extend(type, {
|
|
18
|
+
...createMovementEventHandlers([
|
|
19
|
+
"moveLeft",
|
|
20
|
+
"moveRight",
|
|
21
|
+
"moveUp",
|
|
22
|
+
"moveDown",
|
|
23
|
+
"moveLeftRight",
|
|
24
|
+
"moveUpDown",
|
|
25
|
+
]),
|
|
26
|
+
|
|
27
|
+
start(entity, api) {
|
|
28
|
+
type.start?.(entity, api)
|
|
29
|
+
|
|
30
|
+
entity.maxAcceleration ??= params.maxAcceleration
|
|
31
|
+
entity.movement ??= {}
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
update(entity, dt, api) {
|
|
35
|
+
type.update?.(entity, dt, api)
|
|
36
|
+
|
|
37
|
+
const { movement, maxAcceleration } = entity
|
|
38
|
+
entity.acceleration = zero()
|
|
39
|
+
|
|
40
|
+
if (movement.moveLeft) {
|
|
41
|
+
entity.acceleration[X] = -maxAcceleration
|
|
42
|
+
}
|
|
43
|
+
if (movement.moveRight) {
|
|
44
|
+
entity.acceleration[X] = maxAcceleration
|
|
45
|
+
}
|
|
46
|
+
if (movement.moveUp) {
|
|
47
|
+
entity.acceleration[Z] = maxAcceleration
|
|
48
|
+
}
|
|
49
|
+
if (movement.moveDown) {
|
|
50
|
+
entity.acceleration[Z] = -maxAcceleration
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (movement.moveLeftRight) {
|
|
54
|
+
entity.acceleration[X] += movement.moveLeftRight * maxAcceleration
|
|
55
|
+
}
|
|
56
|
+
if (movement.moveUpDown) {
|
|
57
|
+
entity.acceleration[Z] += -movement.moveUpDown * maxAcceleration
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function modernControls(params) {
|
|
64
|
+
const accelerationBehavior = modernAcceleration(params)
|
|
65
|
+
|
|
66
|
+
return (type) => {
|
|
67
|
+
const newType = accelerationBehavior(type)
|
|
68
|
+
|
|
69
|
+
return extend(newType, {
|
|
70
|
+
update(entity, dt, api) {
|
|
71
|
+
newType.update?.(entity, dt, api)
|
|
72
|
+
merge(entity, modernMove(entity, dt))
|
|
73
|
+
},
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { face } from "@inglorious/engine/ai/movement/dynamic/face.js"
|
|
2
|
+
import { tankMove } from "@inglorious/engine/movement/dynamic/tank.js"
|
|
3
|
+
import { extend, merge } from "@inglorious/utils/data-structures/objects.js"
|
|
4
|
+
import { zero } from "@inglorious/utils/math/linear-algebra/vector.js"
|
|
5
|
+
import { pi } from "@inglorious/utils/math/trigonometry.js"
|
|
6
|
+
|
|
7
|
+
import { createMovementEventHandlers } from "../event-handlers.js"
|
|
8
|
+
|
|
9
|
+
const FULL_CIRCLE = 2
|
|
10
|
+
const DEFAULT_PARAMS = {
|
|
11
|
+
maxSpeed: 250,
|
|
12
|
+
maxAngularSpeed: FULL_CIRCLE * pi(),
|
|
13
|
+
maxAcceleration: 500,
|
|
14
|
+
}
|
|
15
|
+
const X = 0
|
|
16
|
+
const Z = 2
|
|
17
|
+
|
|
18
|
+
export function shooterControls(params) {
|
|
19
|
+
params = extend(DEFAULT_PARAMS, params)
|
|
20
|
+
|
|
21
|
+
const DEADZONE = 0.1
|
|
22
|
+
const NO_MOVEMENT = 0
|
|
23
|
+
|
|
24
|
+
return (type) =>
|
|
25
|
+
extend(type, {
|
|
26
|
+
...createMovementEventHandlers([
|
|
27
|
+
"moveLeft",
|
|
28
|
+
"moveRight",
|
|
29
|
+
"moveUp",
|
|
30
|
+
"moveDown",
|
|
31
|
+
"strafe",
|
|
32
|
+
"move",
|
|
33
|
+
"turn",
|
|
34
|
+
]),
|
|
35
|
+
|
|
36
|
+
start(entity, api) {
|
|
37
|
+
type.start?.(entity, api)
|
|
38
|
+
|
|
39
|
+
entity.maxSpeed ??= params.maxSpeed
|
|
40
|
+
entity.maxAngularSpeed ??= params.maxAngularSpeed
|
|
41
|
+
entity.maxAcceleration ??= params.maxAcceleration
|
|
42
|
+
entity.movement ??= {}
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
update(entity, dt, api) {
|
|
46
|
+
const mouse = api.getEntity("mouse")
|
|
47
|
+
|
|
48
|
+
const { movement, maxAngularSpeed, maxAcceleration } = entity
|
|
49
|
+
entity.acceleration = zero()
|
|
50
|
+
|
|
51
|
+
if (movement.moveLeft) {
|
|
52
|
+
entity.acceleration[Z] = -maxAcceleration
|
|
53
|
+
}
|
|
54
|
+
if (movement.moveRight) {
|
|
55
|
+
entity.acceleration[Z] = maxAcceleration
|
|
56
|
+
}
|
|
57
|
+
if (movement.moveUp) {
|
|
58
|
+
entity.acceleration[X] = maxAcceleration
|
|
59
|
+
}
|
|
60
|
+
if (movement.moveDown) {
|
|
61
|
+
entity.acceleration[X] = -maxAcceleration
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (movement.strafe) {
|
|
65
|
+
entity.acceleration[Z] += movement.strafe * maxAcceleration
|
|
66
|
+
}
|
|
67
|
+
if (movement.move) {
|
|
68
|
+
entity.acceleration[X] += -movement.move * maxAcceleration
|
|
69
|
+
}
|
|
70
|
+
if (movement.turn) {
|
|
71
|
+
entity.orientation += -movement.turn * maxAngularSpeed * dt
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const isUsingAnalogMovement =
|
|
75
|
+
Math.abs(movement.move ?? NO_MOVEMENT) > DEADZONE ||
|
|
76
|
+
Math.abs(movement.strafe ?? NO_MOVEMENT) > DEADZONE
|
|
77
|
+
|
|
78
|
+
if (!isUsingAnalogMovement) {
|
|
79
|
+
merge(entity, face(entity, mouse, dt))
|
|
80
|
+
}
|
|
81
|
+
merge(entity, tankMove(entity, dt))
|
|
82
|
+
},
|
|
83
|
+
})
|
|
84
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { tankMove } from "@inglorious/engine/movement/dynamic/tank.js"
|
|
2
|
+
import { extend, merge } from "@inglorious/utils/data-structures/objects.js"
|
|
3
|
+
import { zero } from "@inglorious/utils/math/linear-algebra/vector.js"
|
|
4
|
+
|
|
5
|
+
import { createMovementEventHandlers } from "../event-handlers.js"
|
|
6
|
+
|
|
7
|
+
const DEFAULT_PARAMS = {
|
|
8
|
+
maxSpeed: 250,
|
|
9
|
+
maxAngularSpeed: 10,
|
|
10
|
+
maxAcceleration: 500,
|
|
11
|
+
}
|
|
12
|
+
const X = 0
|
|
13
|
+
const Z = 2
|
|
14
|
+
|
|
15
|
+
export function tankControls(params) {
|
|
16
|
+
params = extend(DEFAULT_PARAMS, params)
|
|
17
|
+
|
|
18
|
+
return (type) =>
|
|
19
|
+
extend(type, {
|
|
20
|
+
...createMovementEventHandlers([
|
|
21
|
+
"turnLeft",
|
|
22
|
+
"turnRight",
|
|
23
|
+
"moveForward",
|
|
24
|
+
"moveBackward",
|
|
25
|
+
"strafe",
|
|
26
|
+
"move",
|
|
27
|
+
"turn",
|
|
28
|
+
]),
|
|
29
|
+
|
|
30
|
+
start(entity, api) {
|
|
31
|
+
type.start?.(entity, api)
|
|
32
|
+
|
|
33
|
+
entity.maxSpeed ??= params.maxSpeed
|
|
34
|
+
entity.maxAngularSpeed ??= params.maxAngularSpeed
|
|
35
|
+
entity.maxAcceleration ??= params.maxAcceleration
|
|
36
|
+
entity.movement ??= {}
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
update(entity, dt) {
|
|
40
|
+
const { movement, maxAngularSpeed, maxAcceleration } = entity
|
|
41
|
+
entity.acceleration = zero()
|
|
42
|
+
|
|
43
|
+
if (movement.turnLeft) {
|
|
44
|
+
entity.orientation += maxAngularSpeed * dt
|
|
45
|
+
}
|
|
46
|
+
if (movement.turnRight) {
|
|
47
|
+
entity.orientation -= maxAngularSpeed * dt
|
|
48
|
+
}
|
|
49
|
+
if (movement.moveForward) {
|
|
50
|
+
entity.acceleration[X] = maxAcceleration
|
|
51
|
+
}
|
|
52
|
+
if (movement.moveBackward) {
|
|
53
|
+
entity.acceleration[X] = -maxAcceleration
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (movement.strafe != null) {
|
|
57
|
+
entity.acceleration[Z] += movement.strafe * maxAcceleration
|
|
58
|
+
}
|
|
59
|
+
if (movement.move) {
|
|
60
|
+
entity.acceleration[X] += -movement.move * maxAcceleration
|
|
61
|
+
}
|
|
62
|
+
if (movement.turn) {
|
|
63
|
+
entity.orientation += -movement.turn * maxAngularSpeed * dt
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
merge(entity, tankMove(entity, dt))
|
|
67
|
+
},
|
|
68
|
+
})
|
|
69
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function createMovementEventHandlers(events) {
|
|
2
|
+
return events.reduce((acc, eventName) => {
|
|
3
|
+
acc[eventName] = (entity, { entityId, value }) => {
|
|
4
|
+
if (entityId === entity.id) {
|
|
5
|
+
entity.movement[eventName] = value ?? true
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
acc[`${eventName}End`] = (entity, { entityId }) => {
|
|
10
|
+
if (entityId === entity.id) {
|
|
11
|
+
entity.movement[eventName] = false
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return acc
|
|
16
|
+
}, {})
|
|
17
|
+
}
|