@inglorious/engine 2.1.0 → 2.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/README.md CHANGED
@@ -80,7 +80,7 @@ Here is a complete example showing how to set up and run a game using the engine
80
80
  <script type="importmap">
81
81
  {
82
82
  "imports": {
83
- "immer": "https://unpkg.com/immer@latest/dist/immer.mjs",
83
+ "mutative": "https://unpkg.com/mutative@latest/dist/mutative.esm.mjs",
84
84
  "@inglorious/utils/": "https://unpkg.com/@inglorious%2Futils@latest/src/",
85
85
  "@inglorious/store/": "https://unpkg.com/@inglorious%2Fstore@latest/src/",
86
86
  "@inglorious/engine/": "https://unpkg.com/@inglorious%2Fengine@latest/src/",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inglorious/engine",
3
- "version": "2.1.0",
3
+ "version": "2.1.1",
4
4
  "description": "A JavaScript game engine written with global state, immutability, and pure functions in mind. Have fun(ctional programming) with it!",
5
5
  "author": "IceOnFire <antony.mistretta@gmail.com> (https://ingloriouscoderz.it)",
6
6
  "license": "MIT",
@@ -30,12 +30,12 @@
30
30
  "access": "public"
31
31
  },
32
32
  "dependencies": {
33
- "@inglorious/store": "4.0.0",
34
- "@inglorious/utils": "3.5.0"
33
+ "@inglorious/store": "4.0.3",
34
+ "@inglorious/utils": "3.5.1"
35
35
  },
36
36
  "peerDependencies": {
37
- "@inglorious/store": "4.0.0",
38
- "@inglorious/utils": "3.5.0"
37
+ "@inglorious/store": "4.0.3",
38
+ "@inglorious/utils": "3.5.1"
39
39
  },
40
40
  "devDependencies": {
41
41
  "prettier": "^3.5.3",
@@ -1,10 +1,6 @@
1
1
  import { findCollision } from "@inglorious/engine/collision/detection.js"
2
2
  import { clampToBounds } from "@inglorious/engine/physics/bounds.js"
3
3
  import { zero } from "@inglorious/utils/math/vector.js"
4
- import { v } from "@inglorious/utils/v.js"
5
-
6
- const NO_Y = 0
7
- const HALF = 2
8
4
 
9
5
  export function mouse() {
10
6
  return {
@@ -35,10 +31,15 @@ export function mouse() {
35
31
  }
36
32
  }
37
33
 
38
- export function trackMouse(parent, api) {
39
- const handleMouseMove = createHandler("mouseMove", parent, api)
40
- const handleClick = createHandler("mouseClick", parent, api)
41
- const handleWheel = createHandler("mouseWheel", parent, api)
34
+ export function trackMouse(parent, api, toGamePosition) {
35
+ const handleMouseMove = createHandler(
36
+ "mouseMove",
37
+ parent,
38
+ api,
39
+ toGamePosition,
40
+ )
41
+ const handleClick = createHandler("mouseClick", parent, api, toGamePosition)
42
+ const handleWheel = createHandler("mouseWheel", parent, api, toGamePosition)
42
43
 
43
44
  return {
44
45
  onMouseMove: handleMouseMove,
@@ -56,7 +57,7 @@ export function createMouse(overrides = {}) {
56
57
  }
57
58
  }
58
59
 
59
- function createHandler(type, parent, api) {
60
+ function createHandler(type, parent, api, toGamePosition) {
60
61
  return (event) => {
61
62
  event.preventDefault()
62
63
  event.stopPropagation()
@@ -70,38 +71,7 @@ function createHandler(type, parent, api) {
70
71
  return
71
72
  }
72
73
 
73
- const payload = calculatePosition(event, parent, api)
74
- api.notify(type, payload)
74
+ const position = toGamePosition(event.clientX, event.clientY)
75
+ api.notify(type, position)
75
76
  }
76
77
  }
77
-
78
- function calculatePosition(event, parent, api) {
79
- const { clientX, clientY } = event
80
- const {
81
- left,
82
- bottom,
83
- width: canvasWidth,
84
- height: canvasHeight,
85
- } = parent.getBoundingClientRect()
86
-
87
- const x = clientX - left
88
- const y = bottom - clientY
89
-
90
- const game = api.getEntity("game")
91
- const [gameWidth, gameHeight] = game.size
92
-
93
- const scaleX = canvasWidth / gameWidth
94
- const scaleY = canvasHeight / gameHeight
95
- const scale = Math.min(scaleX, scaleY)
96
-
97
- const scaledGameWidth = gameWidth * scale
98
- const scaledGameHeight = gameHeight * scale
99
-
100
- const offsetX = (canvasWidth - scaledGameWidth) / HALF
101
- const offsetY = (canvasHeight - scaledGameHeight) / HALF
102
-
103
- const gameX = (x - offsetX) / scale
104
- const gameY = (y - offsetY) / scale
105
-
106
- return v(gameX, NO_Y, gameY)
107
- }
@@ -1,81 +1,105 @@
1
1
  import { findCollision } from "@inglorious/engine/collision/detection.js"
2
2
  import { clampToBounds } from "@inglorious/engine/physics/bounds.js"
3
- import { magnitude, zero } from "@inglorious/utils/math/vector.js"
3
+ import { magnitude } from "@inglorious/utils/math/vector.js"
4
4
  import { subtract } from "@inglorious/utils/math/vectors.js"
5
5
  import { v } from "@inglorious/utils/v.js"
6
6
 
7
- const NO_Y = 0
8
7
  const MOVEMENT_THRESHOLD = 5
9
- const HALF = 2
10
8
 
11
9
  export function touch() {
12
10
  return {
13
- create(entity, entityId) {
14
- if (entityId !== entity.id) return
15
-
16
- entity.collisions ??= {}
17
- entity.collisions.bounds ??= { shape: "point" }
18
- },
19
-
20
- touchStart(entity, position) {
11
+ touchStart(entity, { index, position }, api) {
21
12
  entity.isSwiping = false
22
- entity.position = position
23
- },
13
+ entity.positions[index] = position
24
14
 
25
- touchMove(entity, position, api) {
26
- const game = api.getEntity("game")
15
+ const entities = api.getEntities()
16
+ const touchedEntity = findCollision(
17
+ { ...entity, position: entity.positions[index] },
18
+ entities,
19
+ "touch",
20
+ )
21
+ if (touchedEntity) {
22
+ entity.targetIds[index] = touchedEntity.id
23
+ api.notify("entityTouchStart", entity.targetIds[index])
24
+ } else {
25
+ api.notify("sceneTouchStart", v(...entity.positions[index]))
26
+ }
27
+ },
27
28
 
28
- const delta = subtract(position, entity.position)
29
+ touchMove(entity, { index, position }, api) {
30
+ const delta = subtract(position, entity.positions[index])
29
31
 
30
32
  if (magnitude(delta) > MOVEMENT_THRESHOLD) {
31
33
  entity.isSwiping = true
32
34
  }
33
35
 
34
- entity.position = position
36
+ entity.positions[index] = position
37
+ const game = api.getEntity("game")
38
+ clampToBounds({ ...entity, position: entity.positions[index] }, game.size)
35
39
 
36
- clampToBounds(entity, game.size)
40
+ if (entity.targetIds[index]) {
41
+ api.notify("entityTouchMove", {
42
+ targetId: entity.targetIds[index],
43
+ position: v(...entity.positions[index]),
44
+ })
45
+ }
37
46
  },
38
47
 
39
- touchEnd(entity, _, api) {
48
+ touchEnd(entity, { index }, api) {
40
49
  if (entity.isSwiping) {
41
- api.notify("swipe", v(...entity.position))
42
50
  entity.isSwiping = false
43
- return
44
51
  }
45
52
 
46
- const entities = api.getEntities()
47
- const clickedEntity = findCollision(entity, entities)
48
- if (clickedEntity) {
49
- api.notify("entityTouch", clickedEntity.id)
53
+ if (entity.targetIds[index]) {
54
+ api.notify("entityTouchEnd", {
55
+ targetId: entity.targetIds[index],
56
+ position: v(...entity.positions[index]),
57
+ })
58
+ entity.targetIds[index] = undefined
50
59
  } else {
51
- api.notify("sceneTouch", v(...entity.position))
60
+ api.notify("sceneTouchEnd", v(...entity.positions[index]))
52
61
  }
53
62
  },
54
63
  }
55
64
  }
56
65
 
57
- export function trackTouch(parent, api) {
58
- const handleTouchStart = createHandler("touchStart", parent, api)
59
- const handleTouchMove = createHandler("touchMove", parent, api)
60
- const handleTouchEnd = createHandler("touchEnd", parent, api)
66
+ export function trackTouch(parent, api, toGamePosition) {
67
+ const handleTouchStart = createHandler(
68
+ "touchStart",
69
+ parent,
70
+ api,
71
+ toGamePosition,
72
+ )
73
+ const handleTouchMove = createHandler(
74
+ "touchMove",
75
+ parent,
76
+ api,
77
+ toGamePosition,
78
+ )
79
+ const handleTouchEnd = createHandler("touchEnd", parent, api, toGamePosition)
61
80
 
62
81
  return {
63
82
  onTouchStart: handleTouchStart,
64
83
  onTouchMove: handleTouchMove,
84
+ onTouchCancel: handleTouchEnd,
65
85
  onTouchEnd: handleTouchEnd,
66
86
  }
67
87
  }
68
88
 
69
- export function createTouch(overrides = {}) {
89
+ export function createTouch() {
70
90
  return {
71
91
  type: "touch",
72
92
  layer: 999, // A high layer value to ensure it's always rendered on top
73
- position: zero(),
74
- ...overrides,
93
+ positions: [],
94
+ collisions: {
95
+ bounds: { shape: "point" },
96
+ touch: { shape: "circle", radius: 44 },
97
+ },
98
+ targetIds: [],
75
99
  }
76
100
  }
77
101
 
78
- function createHandler(type, parent, api) {
102
+ function createHandler(type, parent, api, toGamePosition) {
79
103
  return (event) => {
80
104
  event.preventDefault()
81
105
  event.stopPropagation()
@@ -84,44 +108,14 @@ function createHandler(type, parent, api) {
84
108
  return
85
109
  }
86
110
 
87
- if (type === "touchEnd") {
88
- api.notify(type)
89
- return
90
- }
91
-
92
- const payload = calculatePosition(event, parent, api)
93
- api.notify(type, payload)
94
- }
95
- }
96
-
97
- function calculatePosition(event, parent, api) {
98
- const [touch] = event.touches
99
- const { clientX, clientY } = touch
100
- const {
101
- left,
102
- bottom,
103
- width: canvasWidth,
104
- height: canvasHeight,
105
- } = parent.getBoundingClientRect()
111
+ // touchend doesn't have touches anymore, but it has changedTouches
112
+ const touches = event.touches.length ? event.touches : event.changedTouches
106
113
 
107
- const x = clientX - left
108
- const y = bottom - clientY
114
+ Array.from(touches).forEach((touch, index) => {
115
+ const { clientX, clientY } = touch
109
116
 
110
- const game = api.getEntity("game")
111
- const [gameWidth, gameHeight] = game.size
112
-
113
- const scaleX = canvasWidth / gameWidth
114
- const scaleY = canvasHeight / gameHeight
115
- const scale = Math.min(scaleX, scaleY)
116
-
117
- const scaledGameWidth = gameWidth * scale
118
- const scaledGameHeight = gameHeight * scale
119
-
120
- const offsetX = (canvasWidth - scaledGameWidth) / HALF
121
- const offsetY = (canvasHeight - scaledGameHeight) / HALF
122
-
123
- const gameX = (x - offsetX) / scale
124
- const gameY = (y - offsetY) / scale
125
-
126
- return v(gameX, NO_Y, gameY)
117
+ const position = toGamePosition(clientX, clientY)
118
+ api.notify(type, { index, position })
119
+ })
120
+ }
127
121
  }