@safe-engine/pixi 8.3.7 → 8.4.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.
Files changed (139) hide show
  1. package/.github/workflows/npm-publish.yml +35 -0
  2. package/README.md +70 -5
  3. package/dist/app.d.ts +2 -5
  4. package/dist/app.d.ts.map +1 -1
  5. package/dist/app.js +44 -27
  6. package/dist/base/gworld.d.ts +2 -0
  7. package/dist/base/gworld.d.ts.map +1 -1
  8. package/dist/box2d-wasm/ContactListener.d.ts +12 -0
  9. package/dist/box2d-wasm/ContactListener.d.ts.map +1 -0
  10. package/dist/box2d-wasm/ContactListener.js +64 -0
  11. package/dist/box2d-wasm/PhysicsComponent.d.ts +50 -0
  12. package/dist/box2d-wasm/PhysicsComponent.d.ts.map +1 -0
  13. package/dist/box2d-wasm/PhysicsComponent.js +17 -0
  14. package/dist/box2d-wasm/PhysicsSprite.d.ts +11 -0
  15. package/dist/box2d-wasm/PhysicsSprite.d.ts.map +1 -0
  16. package/dist/box2d-wasm/PhysicsSprite.js +30 -0
  17. package/dist/box2d-wasm/PhysicsSystem.d.ts +17 -0
  18. package/dist/box2d-wasm/PhysicsSystem.d.ts.map +1 -0
  19. package/dist/box2d-wasm/PhysicsSystem.js +145 -0
  20. package/dist/box2d-wasm/debugDraw.d.ts +67 -0
  21. package/dist/box2d-wasm/debugDraw.d.ts.map +1 -0
  22. package/dist/box2d-wasm/debugDraw.js +224 -0
  23. package/dist/box2d-wasm/index.d.ts +6 -0
  24. package/dist/box2d-wasm/index.d.ts.map +1 -0
  25. package/dist/box2d-wasm/index.js +17 -0
  26. package/dist/collider/CollideComponent.d.ts.map +1 -1
  27. package/dist/collider/CollideComponent.js +12 -13
  28. package/dist/collider/CollideSystem.d.ts.map +1 -1
  29. package/dist/collider/CollideSystem.js +1 -2
  30. package/dist/collider/helper/utils.d.ts +2 -0
  31. package/dist/collider/helper/utils.d.ts.map +1 -1
  32. package/dist/collider/helper/utils.js +22 -0
  33. package/dist/collider/index.d.ts +1 -0
  34. package/dist/collider/index.d.ts.map +1 -1
  35. package/dist/collider/index.js +12 -0
  36. package/dist/components/NodeComp.d.ts +6 -4
  37. package/dist/components/NodeComp.d.ts.map +1 -1
  38. package/dist/components/NodeComp.js +13 -13
  39. package/dist/components/Scene.d.ts.map +1 -1
  40. package/dist/components/Scene.js +3 -4
  41. package/dist/core/director.d.ts.map +1 -1
  42. package/dist/core/director.js +4 -3
  43. package/dist/dragonbones/DragonBonesSystem.d.ts +2 -1
  44. package/dist/dragonbones/DragonBonesSystem.d.ts.map +1 -1
  45. package/dist/dragonbones/DragonBonesSystem.js +2 -0
  46. package/dist/dragonbones/index.d.ts +1 -0
  47. package/dist/dragonbones/index.d.ts.map +1 -1
  48. package/dist/dragonbones/index.js +7 -0
  49. package/dist/gui/GUIComponent.d.ts +0 -9
  50. package/dist/gui/GUIComponent.d.ts.map +1 -1
  51. package/dist/gui/GUIComponent.js +0 -37
  52. package/dist/gui/GUISystem.d.ts +1 -0
  53. package/dist/gui/GUISystem.d.ts.map +1 -1
  54. package/dist/gui/GUISystem.js +2 -15
  55. package/dist/index.d.ts +1 -0
  56. package/dist/index.d.ts.map +1 -1
  57. package/dist/index.js +1 -0
  58. package/dist/planck/PhysicsComponent.d.ts +7 -7
  59. package/dist/planck/PhysicsComponent.js +4 -4
  60. package/dist/planck/PhysicsSystem.js +12 -12
  61. package/dist/richtext/RichTextComp.d.ts +17 -0
  62. package/dist/richtext/RichTextComp.d.ts.map +1 -0
  63. package/dist/richtext/RichTextComp.js +38 -0
  64. package/dist/richtext/RichTextSystem.d.ts +7 -0
  65. package/dist/richtext/RichTextSystem.d.ts.map +1 -0
  66. package/dist/richtext/RichTextSystem.js +25 -0
  67. package/dist/richtext/html-text-parser.d.ts +20 -0
  68. package/dist/richtext/html-text-parser.d.ts.map +1 -0
  69. package/dist/richtext/html-text-parser.js +64 -0
  70. package/dist/richtext/index.d.ts +3 -0
  71. package/dist/richtext/index.d.ts.map +1 -0
  72. package/dist/richtext/index.js +7 -0
  73. package/dist/spine/index.d.ts +1 -0
  74. package/dist/spine/index.d.ts.map +1 -1
  75. package/dist/spine/index.js +7 -0
  76. package/package.json +9 -11
  77. package/src/@types/index.d.ts +2 -0
  78. package/src/@types/safex.d.ts +15 -0
  79. package/src/app.ts +85 -0
  80. package/src/base/EnhancedComponent.ts +37 -0
  81. package/src/base/gworld.ts +19 -0
  82. package/src/base/index.ts +3 -0
  83. package/src/base/utils.ts +23 -0
  84. package/src/box2d-wasm/ContactListener.ts +66 -0
  85. package/src/box2d-wasm/PhysicsComponent.ts +83 -0
  86. package/src/box2d-wasm/PhysicsSprite.ts +42 -0
  87. package/src/box2d-wasm/PhysicsSystem.ts +145 -0
  88. package/src/box2d-wasm/debugDraw.ts +257 -0
  89. package/src/box2d-wasm/index.ts +19 -0
  90. package/src/collider/CollideComponent.ts +257 -0
  91. package/src/collider/CollideSystem.ts +166 -0
  92. package/src/collider/helper/Intersection.ts +139 -0
  93. package/src/collider/helper/utils.ts +37 -0
  94. package/src/collider/index.ts +16 -0
  95. package/src/components/BaseComponent.ts +17 -0
  96. package/src/components/NodeComp.ts +434 -0
  97. package/src/components/Scene.ts +17 -0
  98. package/src/core/Color.ts +7 -0
  99. package/src/core/LoadingBar.ts +63 -0
  100. package/src/core/NodePool.ts +28 -0
  101. package/src/core/Size.ts +21 -0
  102. package/src/core/director.ts +11 -0
  103. package/src/core/math.ts +13 -0
  104. package/src/dragonbones/DragonBonesComponent.ts +32 -0
  105. package/src/dragonbones/DragonBonesSystem.ts +35 -0
  106. package/src/dragonbones/index.ts +11 -0
  107. package/src/gui/GUIComponent.ts +159 -0
  108. package/src/gui/GUISystem.ts +116 -0
  109. package/src/helper/utils.ts +50 -0
  110. package/src/index.ts +23 -0
  111. package/src/norender/NoRenderComponent.ts +60 -0
  112. package/src/norender/NoRenderSystem.ts +66 -0
  113. package/src/planck/PhysicsComponent.ts +83 -0
  114. package/src/planck/PhysicsSprite.ts +43 -0
  115. package/src/planck/PhysicsSystem.ts +201 -0
  116. package/src/planck/index.ts +3 -0
  117. package/src/render/RenderComponent.ts +138 -0
  118. package/src/render/RenderSystem.ts +67 -0
  119. package/src/richtext/RichTextComp.ts +46 -0
  120. package/src/richtext/RichTextSystem.ts +26 -0
  121. package/src/richtext/html-text-parser.ts +87 -0
  122. package/src/richtext/index.ts +8 -0
  123. package/src/spine/SpineComponent.ts +18 -0
  124. package/src/spine/SpineSystem.ts +30 -0
  125. package/src/spine/index.ts +11 -0
  126. package/src/spine/lib/BatchableSpineSlot.ts +138 -0
  127. package/src/spine/lib/Spine.ts +910 -0
  128. package/src/spine/lib/SpineDebugRenderer.ts +615 -0
  129. package/src/spine/lib/SpinePipe.ts +203 -0
  130. package/src/spine/lib/SpineTexture.ts +143 -0
  131. package/src/spine/lib/assets/atlasLoader.ts +158 -0
  132. package/src/spine/lib/assets/skeletonLoader.ts +81 -0
  133. package/src/spine/lib/darktint/DarkTintBatchGeometry.ts +92 -0
  134. package/src/spine/lib/darktint/DarkTintBatcher.ts +186 -0
  135. package/src/spine/lib/darktint/DarkTintShader.ts +74 -0
  136. package/src/spine/lib/darktint/darkTintBit.ts +77 -0
  137. package/src/spine/lib/index.ts +43 -0
  138. package/src/spine/lib/require-shim.ts +43 -0
  139. package/tsconfig.json +18 -0
@@ -0,0 +1,257 @@
1
+ import { Color4B, Graphics } from "..";
2
+
3
+ /**
4
+ * Forked from Box2D.js
5
+ * @see https://github.com/kripken/box2d.js/blob/f75077b/helpers/embox2d-helpers.js
6
+ * @author dmagunov + Huy Nguyen + fork contributions from Alex Birch
7
+ * @see https://github.com/kripken/box2d.js/blob/49dddd6/helpers/embox2d-html5canvas-debugDraw.js
8
+ * @author dmagunov + fork contributions from Alex Birch
9
+ * @license Zlib https://opensource.org/licenses/Zlib
10
+ * License evidence: https://github.com/kripken/box2d.js/blob/master/README.markdown#box2djs
11
+ * "box2d.js is zlib licensed, just like Box2D."
12
+ *
13
+ * @typedef {import('box2d-wasm')} Box2DFactory
14
+ * @param {CanvasRenderingContext2D} ctx
15
+ * @param {number} pixelsPerMeter
16
+ * @param {typeof Box2D & EmscriptenModule} box2D
17
+ */
18
+ export const makeDebugDraw = (graphics: Graphics, pixelsPerMeter, box2D: typeof Box2D) => {
19
+ const {
20
+ b2Color,
21
+ b2Draw: { e_shapeBit },
22
+ b2Transform,
23
+ b2Vec2,
24
+ JSDraw,
25
+ wrapPointer
26
+ } = box2D;
27
+
28
+ /**
29
+ * to replace original C++ operator =
30
+ * @param {Box2D.b2Vec2} vec
31
+ * @returns {Box2D.b2Vec2}
32
+ */
33
+ const copyVec2 = (vec: Box2D.b2Vec2) =>
34
+ new b2Vec2(vec.get_x(), vec.get_y());
35
+
36
+ /**
37
+ * to replace original C++ operator *= (float)
38
+ * @param {Box2D.b2Vec2} vec
39
+ * @param {number} scale
40
+ * @returns {Box2D.b2Vec2}
41
+ */
42
+ const scaledVec2 = (vec: Box2D.b2Vec2, scale: number) =>
43
+ new b2Vec2(scale * vec.get_x(), scale * vec.get_y());
44
+
45
+ /**
46
+ * @param {Box2D.b2Color} color
47
+ * @returns {string}
48
+ */
49
+ const getRgbStr = (color: Box2D.b2Color) => {
50
+ const red = (color.get_r() * 255) | 0;
51
+ const green = (color.get_g() * 255) | 0;
52
+ const blue = (color.get_b() * 255) | 0;
53
+ return Color4B(red, green, blue, 0.1);
54
+ };
55
+
56
+ /**
57
+ * @param {string} rgbStr
58
+ * @returns {void}
59
+ */
60
+ const setCtxColor = (rgbStr: Color4B) => {
61
+ graphics.fillStyle = rgbStr;
62
+ graphics.strokeStyle = { color: rgbStr };
63
+ };
64
+
65
+ /**
66
+ * @param {Box2D.b2Vec2[]} vertices
67
+ * @param {boolean} fill
68
+ * @returns {void}
69
+ */
70
+ const drawPolygon = (vertices, fill) => {
71
+ // console.log("drawPolygon", vertices[0].y, fill);
72
+ // graphics.poly(vertices, fill)
73
+ graphics.moveTo(vertices[vertices.length - 1].x * pixelsPerMeter,
74
+ vertices[vertices.length - 1].y * pixelsPerMeter);
75
+ vertices.forEach((v) => {
76
+ graphics.lineTo(v.x * pixelsPerMeter, v.y * pixelsPerMeter);
77
+ })
78
+ graphics.fill()
79
+ };
80
+
81
+ /**
82
+ * @param {Box2D.b2Vec2} center
83
+ * @param {number} radius
84
+ * @param {Box2D.b2Vec2} axis
85
+ * @param {boolean} fill
86
+ * @returns {void}
87
+ */
88
+ const drawCircle = (center, radius, axis, fill) => {
89
+ // graphics.circle(center.x, center.y, radius)
90
+ let angle = 0;
91
+ const angleStep = 32;
92
+ const n = 360 / angleStep;
93
+ let x = radius * Math.cos(angle * Math.PI / 180);
94
+ let y = radius * Math.sin(angle * Math.PI / 180);
95
+ graphics.moveTo(center.x + x, center.y + y);
96
+ angle += angleStep;
97
+
98
+ for (let i = 0; i < n; i++) {
99
+ x = radius * Math.cos(angle * Math.PI / 180);
100
+ y = radius * Math.sin(angle * Math.PI / 180);
101
+ graphics.lineTo(center.x + x, center.y + y);
102
+ angle += angleStep;
103
+ }
104
+ graphics.fill()
105
+ };
106
+
107
+ /**
108
+ * @param {Box2D.b2Vec2} vert1
109
+ * @param {Box2D.b2Vec2} vert2
110
+ * @returns {void}
111
+ */
112
+ const drawSegment = (vert1, vert2) => {
113
+ // console.log("drawSegment", vert1, vert2)
114
+ graphics.moveTo(vert1.get_x(), vert1.get_y());
115
+ graphics.lineTo(vert2.get_x(), vert2.get_y());
116
+ graphics.stroke();
117
+ };
118
+
119
+ /**
120
+ * @param {Box2D.b2Vec2} vertex
121
+ * @param {number} sizeMetres
122
+ * @returns {void}
123
+ */
124
+ const drawPoint = (vertex, sizeMetres) => {
125
+ const sizePixels = sizeMetres / pixelsPerMeter;
126
+ graphics.rect(
127
+ vertex.get_x() - sizePixels / 2,
128
+ vertex.get_y() - sizePixels / 2,
129
+ sizePixels,
130
+ sizePixels
131
+ );
132
+ };
133
+
134
+ /**
135
+ * @param {Box2D.b2Transform} transform
136
+ * @param {number} sizeMetres
137
+ * @returns {void}
138
+ */
139
+ const drawTransform = (transform: Box2D.b2Transform) => {
140
+ const pos = transform.get_p();
141
+ const rot = transform.get_q();
142
+
143
+ graphics.save();
144
+ graphics.translateTransform(pos.get_x(), pos.get_y());
145
+ graphics.scaleTransform(0.5, 0.5);
146
+ graphics.rotateTransform(rot.GetAngle());
147
+ // graphics.lineWidth *= 2;
148
+ graphics.restore();
149
+ }
150
+
151
+ /** {@link Box2D.b2Vec2} is a struct of `float x, y` */
152
+ const sizeOfB2Vec = Float32Array.BYTES_PER_ELEMENT * 2;
153
+
154
+ /**
155
+ * @param {number} array_p pointer to {@link Box2D.b2Vec2}
156
+ * @param {number} numElements length of array
157
+ * @param {number} sizeOfElement size of an instance of the array element
158
+ * @param {typeof Box2D.b2Vec2} ctor constructor for the array element
159
+ * @return {Box2D.b2Vec2[]}
160
+ */
161
+ const reifyArray = (array_p, numElements, sizeOfElement, ctor) =>
162
+ Array(numElements)
163
+ .fill(undefined)
164
+ .map((_, index) =>
165
+ wrapPointer(array_p + index * sizeOfElement, ctor)
166
+ );
167
+
168
+ const debugDraw = Object.assign(new JSDraw(), {
169
+ /**
170
+ * @param {number} vert1_p pointer to {@link Box2D.b2Vec2}
171
+ * @param {number} vert2_p pointer to {@link Box2D.b2Vec2}
172
+ * @param {number} color_p pointer to {@link Box2D.b2Color}
173
+ * @returns {void}
174
+ */
175
+ DrawSegment(vert1_p, vert2_p, color_p) {
176
+ const color = wrapPointer(color_p, b2Color);
177
+ setCtxColor(getRgbStr(color));
178
+ const vert1 = wrapPointer(vert1_p, b2Vec2);
179
+ const vert2 = wrapPointer(vert2_p, b2Vec2);
180
+ drawSegment(vert1, vert2);
181
+ },
182
+ /**
183
+ * @param {number} vertices_p pointer to Array<{@link Box2D.b2Vec2}>
184
+ * @param {number} vertexCount
185
+ * @param {number} color_p pointer to {@link Box2D.b2Color}
186
+ * @returns {void}
187
+ */
188
+ DrawPolygon(vertices_p, vertexCount, color_p) {
189
+ const color = wrapPointer(color_p, b2Color);
190
+ setCtxColor(getRgbStr(color));
191
+ const vertices = reifyArray(vertices_p, vertexCount, sizeOfB2Vec, b2Vec2);
192
+ drawPolygon(vertices, false);
193
+ // console.log(`DrawPolygon`, getRgbStr(color).green, vertices[0].y)
194
+ },
195
+ /**
196
+ * @param {number} vertices_p pointer to Array<{@link Box2D.b2Vec2}>
197
+ * @param {number} vertexCount
198
+ * @param {number} color_p pointer to {@link Box2D.b2Color}
199
+ * @returns {void}
200
+ */
201
+ DrawSolidPolygon(vertices_p, vertexCount, color_p) {
202
+ const color = wrapPointer(color_p, b2Color);
203
+ setCtxColor(getRgbStr(color));
204
+ const vertices = reifyArray(vertices_p, vertexCount, sizeOfB2Vec, b2Vec2);
205
+ drawPolygon(vertices, true);
206
+ },
207
+ /**
208
+ * @param {number} center_p pointer to {@link Box2D.b2Vec2}
209
+ * @param {number} radius
210
+ * @param {number} color_p pointer to {@link Box2D.b2Color}
211
+ * @returns {void}
212
+ */
213
+ DrawCircle(center_p, radius, color_p) {
214
+ const color = wrapPointer(color_p, b2Color);
215
+ setCtxColor(getRgbStr(color));
216
+ const center = wrapPointer(center_p, b2Vec2);
217
+ const dummyAxis = new b2Vec2(0, 0);
218
+ drawCircle(center, radius, dummyAxis, false);
219
+ },
220
+ /**
221
+ * @param {number} center_p pointer to {@link Box2D.b2Vec2}
222
+ * @param {number} radius
223
+ * @param {number} axis_p pointer to {@link Box2D.b2Vec2}
224
+ * @param {number} color_p pointer to {@link Box2D.b2Color}
225
+ * @returns {void}
226
+ */
227
+ DrawSolidCircle(center_p, radius, axis_p, color_p) {
228
+ const color = wrapPointer(color_p, b2Color);
229
+ setCtxColor(getRgbStr(color));
230
+ const center = wrapPointer(center_p, b2Vec2);
231
+ const axis = wrapPointer(axis_p, b2Vec2);
232
+ drawCircle(center, radius, axis, true);
233
+ },
234
+ /**
235
+ * @param {number} transform_p pointer to {@link Box2D.b2Transform}
236
+ * @returns {void}
237
+ */
238
+ DrawTransform(transform_p) {
239
+ const transform = wrapPointer(transform_p, b2Transform);
240
+ drawTransform(transform);
241
+ },
242
+ /**
243
+ * @param {number} vertex_p pointer to {@link Box2D.b2Vec2}
244
+ * @param {number} sizeMetres
245
+ * @param {number} pointer to {@link Box2D.b2Color}
246
+ * @returns {void}
247
+ */
248
+ DrawPoint(vertex_p, sizeMetres, color_p) {
249
+ const color = wrapPointer(color_p, b2Color);
250
+ setCtxColor(getRgbStr(color));
251
+ const vertex = wrapPointer(vertex_p, b2Vec2);
252
+ drawPoint(vertex, sizeMetres);
253
+ }
254
+ });
255
+ debugDraw.SetFlags(e_shapeBit);
256
+ return debugDraw;
257
+ };
@@ -0,0 +1,19 @@
1
+ import { GameWorld, Vec2 } from '..'
2
+ import { PhysicsSystem } from './PhysicsSystem'
3
+
4
+ export * from './PhysicsComponent'
5
+ export * from './PhysicsSprite'
6
+ export * from './PhysicsSystem'
7
+
8
+ export function setupPhysics(world = GameWorld.Instance, isDebugDraw?: boolean, gravity?: Vec2) {
9
+ console.log('app world', world.app)
10
+ world.systems.add(PhysicsSystem)
11
+ world.systems.configureOnce(PhysicsSystem)
12
+ world.listUpdate.push(PhysicsSystem)
13
+ if (isDebugDraw) {
14
+ world.systems.get(PhysicsSystem).addDebug()
15
+ }
16
+ if (gravity) {
17
+ world.systems.get(PhysicsSystem).gravity = gravity
18
+ }
19
+ }
@@ -0,0 +1,257 @@
1
+ import { Graphics, Point, Rectangle, Size } from 'pixi.js'
2
+
3
+ import { GameWorld } from '..'
4
+ import { NoRenderComponentX } from '../components/BaseComponent'
5
+ import { NodeComp } from '../components/NodeComp'
6
+ import { v2 } from '../helper/utils'
7
+ import { circleCircle, polygonCircle, polygonPolygon } from './helper/Intersection'
8
+ import { getMax, getMin } from './helper/utils'
9
+
10
+ function getNodeToWorldTransformAR(node: NodeComp) {
11
+ const t = node.instance.worldTransform
12
+ const x = node.instance.pivot.x * node.instance.width
13
+ const y = node.instance.pivot.y * node.instance.height
14
+ const transform = t.translate(x, y)
15
+ return transform
16
+ }
17
+
18
+ function cloneRect(origin: Rectangle) {
19
+ return new Rectangle(origin.x, origin.y, origin.width, origin.height)
20
+ }
21
+ interface ColliderProps {
22
+ offset?: Point
23
+ tag?: number
24
+ enabled?: boolean
25
+ onCollisionEnter?: (other: Collider) => void
26
+ onCollisionExit?: (other: Collider) => void
27
+ onCollisionStay?: (other: Collider) => void
28
+ }
29
+
30
+ export class Collider<T = ColliderProps> extends NoRenderComponentX<T> {
31
+ _worldPoints: Point[] = []
32
+ _worldPosition: Point
33
+ _worldRadius
34
+ _AABB: Rectangle = new Rectangle(0, 0, 0, 0)
35
+ _preAabb: Rectangle = new Rectangle(0, 0, 0, 0)
36
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
37
+ update(dt: number, draw?: Graphics) { }
38
+ getAABB() {
39
+ return this._AABB
40
+ }
41
+ get world() {
42
+ return {
43
+ points: this._worldPoints,
44
+ preAabb: this._preAabb,
45
+ }
46
+ }
47
+ }
48
+
49
+ interface BoxColliderProps extends ColliderProps {
50
+ width: number
51
+ height: number
52
+ }
53
+ export class BoxCollider extends Collider<BoxColliderProps> {
54
+
55
+ get size(): Size {
56
+ return this.props
57
+ }
58
+
59
+ set size(s: Size) {
60
+ this.props.width = s.width
61
+ this.props.height = s.height
62
+ }
63
+
64
+ update(dt, draw: Graphics) {
65
+ if (!this.node) {
66
+ return
67
+ }
68
+ const { x, y } = this.props.offset || v2()
69
+ // const hw = this.width * 0.5
70
+ // const hh = this.height * 0.5
71
+ const transform = getNodeToWorldTransformAR(this.node)
72
+ // const dx = x - hw
73
+ // const dy = y - hh
74
+ const collider = this.getComponent(Collider)
75
+ collider._worldPoints = [
76
+ v2(x, y),
77
+ v2(x, y + this.props.height),
78
+ v2(x + this.props.width, y + this.props.height),
79
+ v2(x + this.props.width, y)
80
+ ].map(p => transform.apply(p))
81
+ // console.log("_worldPoints", collider._worldPoints, rectTrs)
82
+ // collider._worldPoints = collider._worldPoints.map(p => transform.apply(p))
83
+ const listX = collider._worldPoints.map(({ x }) => x)
84
+ const listY = collider._worldPoints.map(({ y }) => y)
85
+ collider._preAabb = cloneRect(collider._AABB)
86
+ collider._AABB.x = getMin(listX)
87
+ collider._AABB.y = getMin(listY)
88
+ collider._AABB.width = getMax(listX) - collider._AABB.x
89
+ collider._AABB.height = getMax(listY) - collider._AABB.y
90
+ if (draw) {
91
+ // console.log("drawing", JSON.stringify(collider._worldPoints))
92
+ const drawList = collider._worldPoints
93
+ // draw.clear()
94
+ draw.poly(drawList)
95
+ // draw.fill({ color: '#fff', alpha: 0.3 })
96
+ }
97
+ }
98
+ }
99
+
100
+ interface CircleColliderProps extends ColliderProps {
101
+ radius: number
102
+ }
103
+
104
+ export class CircleCollider extends Collider<CircleColliderProps> {
105
+
106
+ update(dt, draw: Graphics) {
107
+ if (!this.node) {
108
+ return
109
+ }
110
+ const transform = getNodeToWorldTransformAR(this.node)
111
+ const collider = this.getComponent(Collider)
112
+ collider._worldRadius = this.props.radius * this.node.scaleX
113
+ collider._worldPosition = transform.apply(this.props.offset)
114
+ if (draw) {
115
+ const { x } = collider._worldPosition
116
+ const y = GameWorld.Instance.app.screen.height - collider._worldPosition.y
117
+ draw.rect(x, y, 2, 2)
118
+ draw.circle(x, y, collider._worldRadius)
119
+ }
120
+ collider._preAabb = cloneRect(collider._AABB)
121
+ collider._AABB.x = collider._worldPosition.x - collider._worldRadius
122
+ collider._AABB.y = collider._worldPosition.y - collider._worldRadius
123
+ collider._AABB.width = collider._worldRadius * 2
124
+ collider._AABB.height = collider._AABB.width
125
+ // draw.drawRect(p(this._AABB.x, this._AABB.y),
126
+ // p(this._worldPosition.x + this._worldRadius, this._worldPosition.y + this._worldRadius),
127
+ // Color.WHITE, 3, Color.DEBUG_BORDER_COLOR);
128
+ }
129
+ }
130
+
131
+
132
+ interface PolygonColliderProps extends ColliderProps {
133
+ points: Array<Point>
134
+ }
135
+ export class PolygonCollider extends Collider<PolygonColliderProps> {
136
+
137
+ get points(): Point[] {
138
+ const { x, y } = this.props.offset
139
+ const pointsList = this.props.points.map((p) => v2(p.x + x, p.y + y))
140
+ return pointsList
141
+ }
142
+
143
+ set points(points: Point[]) {
144
+ this.props.points = points
145
+ }
146
+
147
+ update(dt, draw: Graphics) {
148
+ if (!this.node) {
149
+ return
150
+ }
151
+ const transform = getNodeToWorldTransformAR(this.node)
152
+ const collider = this.getComponent(Collider)
153
+ collider._worldPoints = this.points.map((p) => transform.apply(p))
154
+ // log(polyPoints);
155
+ if (draw) {
156
+ const drawList = collider._worldPoints.map(({ x, y }) => v2(x, GameWorld.Instance.app.screen.height - y))
157
+ draw.poly(drawList)
158
+ }
159
+ const listX = collider._worldPoints.map(({ x }) => x)
160
+ const listY = collider._worldPoints.map(({ y }) => y)
161
+ collider._preAabb = cloneRect(collider._AABB)
162
+ collider._AABB.x = getMin(listX)
163
+ collider._AABB.y = getMin(listY)
164
+ collider._AABB.width = getMax(listX) - collider._AABB.x
165
+ collider._AABB.height = getMax(listY) - collider._AABB.y
166
+ // draw.drawRect(p(this._AABB.x, this._AABB.y), p(max(listX), max(listY)),
167
+ // Color.WHITE, 3, Color.DEBUG_BORDER_COLOR);
168
+ }
169
+ }
170
+
171
+ export enum CollisionType {
172
+ NONE,
173
+ ENTER,
174
+ STAY,
175
+ EXIT,
176
+ }
177
+
178
+ function isPolygonCollider(col: Collider) {
179
+ return col.getComponent(PolygonCollider) || col.getComponent(BoxCollider)
180
+ }
181
+ function isCircleCollider(col: Collider) {
182
+ return col.getComponent(CircleCollider)
183
+ }
184
+
185
+ export class Contract {
186
+ _collider1: Collider
187
+ _collider2: Collider
188
+ _touching: boolean
189
+ _isPolygonPolygon: boolean
190
+ _isCircleCircle: boolean
191
+ _isPolygonCircle: boolean
192
+
193
+ constructor(collider1: Collider, collider2: Collider) {
194
+ this._collider1 = collider1
195
+ this._collider2 = collider2
196
+ const isCollider1Polygon = isPolygonCollider(collider1)
197
+ const isCollider2Polygon = isPolygonCollider(collider2)
198
+ const isCollider1Circle = isCircleCollider(collider1)
199
+ const isCollider2Circle = isCircleCollider(collider2)
200
+
201
+ if (isCollider1Polygon && isCollider2Polygon) {
202
+ this._isPolygonPolygon = true
203
+ } else if (isCollider1Circle && isCollider2Circle) {
204
+ this._isCircleCircle = true
205
+ } else if (isCollider1Polygon && isCollider2Circle) {
206
+ this._isPolygonCircle = true
207
+ } else if (isCollider1Circle && isCollider2Polygon) {
208
+ this._isPolygonCircle = true
209
+ this._collider1 = collider2
210
+ this._collider2 = collider1
211
+ }
212
+ // log(this._isPolygonPolygon);
213
+ }
214
+
215
+ updateState() {
216
+ const result = this.test()
217
+
218
+ let type = CollisionType.NONE
219
+ if (result && !this._touching) {
220
+ this._touching = true
221
+ type = CollisionType.ENTER
222
+ } else if (result && this._touching) {
223
+ type = CollisionType.STAY
224
+ } else if (!result && this._touching) {
225
+ this._touching = false
226
+ type = CollisionType.EXIT
227
+ }
228
+ // console.log('updateState', result, this._touching, type)
229
+ return type
230
+ }
231
+
232
+ test() {
233
+ // if (!shouldCollider(this._collider1, this._collider2)) {
234
+ // return false
235
+ // }
236
+ // log(this._collider1.getAABB(), this._collider2.getAABB());
237
+ if (!this._collider1.getAABB().intersects(this._collider2.getAABB())) {
238
+ return false
239
+ }
240
+
241
+ if (this._isPolygonPolygon) {
242
+ return polygonPolygon(this._collider1._worldPoints, this._collider2._worldPoints)
243
+ }
244
+ if (this._isCircleCircle) {
245
+ const p1 = this._collider1
246
+ const p2 = this._collider2
247
+ return circleCircle(p1._worldPosition, p1._worldRadius, p2._worldPosition, p2._worldRadius)
248
+ }
249
+
250
+ if (this._isPolygonCircle) {
251
+ const p2 = this._collider2
252
+ return polygonCircle(this._collider1._worldPoints, p2._worldPosition, p2._worldRadius)
253
+ }
254
+
255
+ return false
256
+ }
257
+ }
@@ -0,0 +1,166 @@
1
+ import { EntityManager, EventManager, EventTypes, System } from 'entityx-ts'
2
+ import { Color, Graphics } from 'pixi.js'
3
+
4
+ import { GameWorld } from '../base'
5
+ import { NodeComp } from '../components/NodeComp'
6
+ import { BoxCollider, CircleCollider, Collider, CollisionType, Contract, PolygonCollider } from './CollideComponent'
7
+
8
+ export function enabledDebugDraw(enable = true) {
9
+ const collideSystem = GameWorld.Instance.systems.get(CollideSystem)
10
+ collideSystem.enabledDebugDraw = enable
11
+ }
12
+
13
+ export class CollideSystem implements System {
14
+ listColliders: Collider[] = []
15
+ _contracts: Contract[] = []
16
+ removeColliders: Collider[] = []
17
+ debugGraphics: Graphics
18
+ enabledDebugDraw = true
19
+ enabled = true
20
+ colliderMatrix = [[true]]
21
+
22
+ configure(event_manager: EventManager) {
23
+ event_manager.subscribe(EventTypes.ComponentAdded, BoxCollider, this.onAddCollider.bind(this))
24
+ event_manager.subscribe(EventTypes.ComponentAdded, CircleCollider, this.onAddCollider.bind(this))
25
+ event_manager.subscribe(EventTypes.ComponentAdded, PolygonCollider, this.onAddCollider.bind(this))
26
+ event_manager.subscribe(EventTypes.ComponentRemoved, BoxCollider, this.onRemoveCollider.bind(this))
27
+ event_manager.subscribe(EventTypes.ComponentRemoved, CircleCollider, this.onRemoveCollider.bind(this))
28
+ event_manager.subscribe(EventTypes.ComponentRemoved, PolygonCollider, this.onRemoveCollider.bind(this))
29
+ if (this.enabledDebugDraw) {
30
+ this.debugGraphics = new Graphics()
31
+ this.debugGraphics.setFillStyle({ color: new Color('white') })
32
+ this.debugGraphics.width = 4
33
+ this.debugGraphics.zIndex = 40
34
+ }
35
+ GameWorld.Instance.app.stage.addChild(this.debugGraphics)
36
+ }
37
+
38
+ update(entities: EntityManager, events: EventManager, dt: number) {
39
+ if (!this.enabled) {
40
+ return
41
+ }
42
+ this.listColliders.forEach((collider) => {
43
+ if (!collider.node.active) {
44
+ this.removeColliders.push(collider)
45
+ }
46
+ })
47
+ // this.removeColliders.forEach((comp) => {
48
+ // this.listColliders = this.listColliders.filter((col) => !col.isEqual(comp) && col.node.active)
49
+ // this._contracts = this._contracts.filter((contract) => {
50
+ // const col1 = contract._collider1
51
+ // const col2 = contract._collider2
52
+ // if (col1.isEqual(comp) || !col1.node.active) {
53
+ // if (contract._touching) {
54
+ // // contract._touching = false
55
+ // if (col2.onCollisionExit) {
56
+ // col2.onCollisionExit(col1)
57
+ // }
58
+ // }
59
+ // return false
60
+ // }
61
+ // if (col2.isEqual(comp) || !col2.node.active) {
62
+ // if (contract._touching) {
63
+ // // contract._touching = false
64
+ // if (col1.onCollisionExit) col1.onCollisionExit(col2)
65
+ // }
66
+ // return false
67
+ // }
68
+ // return true
69
+ // })
70
+ // })
71
+ this.removeColliders = []
72
+ const draw = this.enabledDebugDraw ? this.debugGraphics : undefined
73
+ if (this.enabledDebugDraw) {
74
+ draw.clear()
75
+ }
76
+ for (const entt of entities.entities_with_components(BoxCollider)) {
77
+ const comp = entt.getComponent(BoxCollider)
78
+ comp.update(dt, draw)
79
+ }
80
+ for (const entt of entities.entities_with_components(CircleCollider)) {
81
+ const comp = entt.getComponent(CircleCollider)
82
+ comp.update(dt, draw)
83
+ }
84
+ for (const entt of entities.entities_with_components(PolygonCollider)) {
85
+ const comp = entt.getComponent(PolygonCollider)
86
+ comp.update(dt, draw)
87
+ }
88
+ if (this.enabledDebugDraw) {
89
+ // draw.clear()
90
+ // draw.removeFromParent()
91
+ // app.stage.addChild(draw)
92
+ // console.log('enabledDebugDraw', this.debugGraphics)
93
+ // this.debugGraphics.width = 4
94
+ // this.debugGraphics.circle(500, 600, 450)
95
+ this.debugGraphics.fill({ color: new Color('white'), alpha: 0.3 })
96
+ }
97
+ // console.log(this._contracts.length)
98
+ this._contracts.forEach((contract) => {
99
+ const col1 = contract._collider1
100
+ const col2 = contract._collider2
101
+ if (!col1.node || !col2.node || !col1.node.active || !col2.node.active) {
102
+ return
103
+ }
104
+ const type = contract.updateState()
105
+ if (!col1 || !col2) {
106
+ return
107
+ }
108
+
109
+ switch (type) {
110
+ case CollisionType.ENTER: {
111
+ if (col1.props.onCollisionEnter) {
112
+ col1.props.onCollisionEnter(col2)
113
+ }
114
+ if (col2.props.onCollisionEnter) {
115
+ col2.props.onCollisionEnter(col1)
116
+ }
117
+ break
118
+ }
119
+ case CollisionType.STAY:
120
+ if (col1.props.onCollisionStay) {
121
+ col1.props.onCollisionStay(col2)
122
+ }
123
+ if (col2.props.onCollisionStay) {
124
+ col2.props.onCollisionStay(col1)
125
+ }
126
+ break
127
+ case CollisionType.EXIT:
128
+ if (col1.props.onCollisionExit) {
129
+ col1.props.onCollisionExit(col2)
130
+ }
131
+ if (col2.props.onCollisionExit) {
132
+ col2.props.onCollisionExit(col1)
133
+ }
134
+ break
135
+
136
+ default:
137
+ break
138
+ }
139
+ })
140
+ }
141
+
142
+ addCollider(colliderPhysics: Collider) {
143
+ this.listColliders.forEach((col) => {
144
+ // if (shouldCollider(col, colliderPhysics)) {
145
+ this._contracts.push(new Contract(col, colliderPhysics))
146
+ // }
147
+ })
148
+ this.listColliders.push(colliderPhysics)
149
+ }
150
+
151
+ removeCollider(colliderPhysics: Collider) {
152
+ this.removeColliders.push(colliderPhysics)
153
+ }
154
+ onAddCollider({ entity, component }) {
155
+ console.log('ComponentAddedEvent', component)
156
+ const collider = entity.assign(new Collider(component))
157
+ collider.node = entity.getComponent(NodeComp)
158
+ component.node = entity.getComponent(NodeComp)
159
+ this.addCollider(collider)
160
+ }
161
+ onRemoveCollider({ entity, component }) {
162
+ console.log('ComponentRemovedEvent', component)
163
+ const collider = entity.getComponent(Collider)
164
+ this.removeCollider(collider)
165
+ }
166
+ }