1
1
import * as SAT from "./sat.js" ;
2
2
import ResponseObject from "./response.js" ;
3
3
import Vector2d from "./../math/vector2.js" ;
4
- import { game } from "../index.js" ;
5
4
import Bounds from "./bounds.js" ;
6
5
7
-
8
6
// a dummy object when using Line for raycasting
9
7
let dummyObj = {
10
8
pos : new Vector2d ( 0 , 0 ) ,
@@ -16,167 +14,196 @@ let dummyObj = {
16
14
}
17
15
} ;
18
16
17
+ // some cache bounds object used for collision detection
19
18
let boundsA = new Bounds ( ) ;
20
19
let boundsB = new Bounds ( ) ;
21
20
22
- // the global response object used for collisions
23
- let globalResponse = new ResponseObject ( ) ;
24
-
25
21
/**
26
- * a function used to determine if two objects should collide (based on both respective objects collision mask and type).<br>
27
- * you can redefine this function if you need any specific rules over what should collide with what.
28
- * @ignore
29
- * @param {Renderable } a - a reference to the object A.
30
- * @param {Renderable } b - a reference to the object B.
31
- * @returns {boolean } true if they should collide, false otherwise
22
+ * the Detector class contains methods for detecting collisions between bodies using a broadphase algorithm.
32
23
*/
33
- function shouldCollide ( a , b ) {
34
- var bodyA = a . body ,
35
- bodyB = b . body ;
36
- return (
37
- a !== b &&
38
- a . isKinematic !== true && b . isKinematic !== true &&
39
- typeof bodyA === "object" && typeof bodyB === "object" &&
40
- bodyA . shapes . length > 0 && bodyB . shapes . length > 0 &&
41
- ! ( bodyA . isStatic === true && bodyB . isStatic === true ) &&
42
- ( bodyA . collisionMask & bodyB . collisionType ) !== 0 &&
43
- ( bodyA . collisionType & bodyB . collisionMask ) !== 0
44
- ) ;
45
- }
46
-
47
-
48
-
49
- /**
50
- * find all the collisions for the specified object
51
- * @ignore
52
- * @param {Renderable } objA - object to be tested for collision
53
- * @param {ResponseObject } [response] - a user defined response object that will be populated if they intersect.
54
- * @returns {boolean } in case of collision, false otherwise
55
- */
56
- export function collisionCheck ( objA , response = globalResponse ) {
57
- var collisionCounter = 0 ;
58
- // retreive a list of potential colliding objects from the game world
59
- var candidates = game . world . broadphase . retrieve ( objA ) ;
24
+ export default class Detector {
25
+ /**
26
+ * @param {Container } world - the physic world this detector is bind to
27
+ */
28
+ constructor ( world ) {
29
+ // @ignore
30
+ this . world = world ;
31
+
32
+ /**
33
+ * the default response object used for collisions
34
+ * (will be automatically populated by the collides functions)
35
+ * @type {ResponseObject }
36
+ */
37
+ this . response = new ResponseObject ( ) ;
38
+ }
60
39
61
- boundsA . addBounds ( objA . getBounds ( ) , true ) ;
62
- boundsA . addBounds ( objA . body . getBounds ( ) ) ;
40
+ /**
41
+ * determine if two objects should collide (based on both respective objects body collision mask and type).<br>
42
+ * you can redefine this function if you need any specific rules over what should collide with what.
43
+ * @param {Renderable } a - a reference to the object A.
44
+ * @param {Renderable } b - a reference to the object B.
45
+ * @returns {boolean } true if they should collide, false otherwise
46
+ */
47
+ shouldCollide ( a , b ) {
48
+ var bodyA = a . body ,
49
+ bodyB = b . body ;
50
+ return (
51
+ ( typeof bodyA === "object" && typeof bodyB === "object" ) &&
52
+ a !== b &&
53
+ a . isKinematic !== true && b . isKinematic !== true &&
54
+ bodyA . shapes . length > 0 && bodyB . shapes . length > 0 &&
55
+ ! ( bodyA . isStatic === true && bodyB . isStatic === true ) &&
56
+ ( bodyA . collisionMask & bodyB . collisionType ) !== 0 &&
57
+ ( bodyA . collisionType & bodyB . collisionMask ) !== 0
58
+ ) ;
59
+ }
63
60
64
- candidates . forEach ( ( objB ) => {
65
- // check if both objects "should" collide
66
- if ( shouldCollide ( objA , objB ) ) {
61
+ /**
62
+ * detect collision between two bodies.
63
+ * @param {Body } bodyA - a reference to body A.
64
+ * @param {Body } bodyB - a reference to body B.
65
+ * @returns {Boolean } true if colliding
66
+ */
67
+ collides ( bodyA , bodyB , response = this . response ) {
68
+ // for each shape in body A
69
+ for ( var indexA = bodyA . shapes . length , shapeA ; indexA -- , ( shapeA = bodyA . shapes [ indexA ] ) ; ) {
70
+ // for each shape in body B
71
+ for ( var indexB = bodyB . shapes . length , shapeB ; indexB -- , ( shapeB = bodyB . shapes [ indexB ] ) ; ) {
72
+ // full SAT collision check
73
+ if ( SAT [ "test" + shapeA . shapeType + shapeB . shapeType ] . call (
74
+ this ,
75
+ bodyA . ancestor , // a reference to the object A
76
+ shapeA ,
77
+ bodyB . ancestor , // a reference to the object B
78
+ shapeB ,
79
+ // clear response object before reusing
80
+ response . clear ( ) ) === true
81
+ ) {
67
82
68
- boundsB . addBounds ( objB . getBounds ( ) , true ) ;
69
- boundsB . addBounds ( objB . body . getBounds ( ) ) ;
83
+ // set the shape index
84
+ response . indexShapeA = indexA ;
85
+ response . indexShapeB = indexB ;
70
86
71
- // fast AABB check if both bounding boxes are overlaping
72
- if ( boundsA . overlaps ( boundsB ) ) {
73
- // for each shape in body A
74
- objA . body . shapes . forEach ( ( shapeA , indexA ) => {
75
- // for each shape in body B
76
- objB . body . shapes . forEach ( ( shapeB , indexB ) => {
77
- // full SAT collision check
78
- if ( SAT [ "test" + shapeA . shapeType + shapeB . shapeType ] . call (
79
- this ,
80
- objA , // a reference to the object A
81
- shapeA ,
82
- objB , // a reference to the object B
83
- shapeB ,
84
- // clear response object before reusing
85
- response . clear ( ) ) === true
86
- ) {
87
- // we touched something !
88
- collisionCounter ++ ;
89
-
90
- // set the shape index
91
- response . indexShapeA = indexA ;
92
- response . indexShapeB = indexB ;
93
-
94
- // execute the onCollision callback
95
- if ( objA . onCollision && objA . onCollision ( response , objB ) !== false && objA . body . isStatic === false ) {
96
- objA . body . respondToCollision . call ( objA . body , response ) ;
97
- }
98
- if ( objB . onCollision && objB . onCollision ( response , objA ) !== false && objB . body . isStatic === false ) {
99
- objB . body . respondToCollision . call ( objB . body , response ) ;
100
- }
101
- }
102
- } ) ;
103
- } ) ;
87
+ return true ;
88
+ }
104
89
}
105
90
}
106
- } ) ;
107
- // we could return the amount of objects we collided with ?
108
- return collisionCounter > 0 ;
109
- }
110
-
111
- /**
112
- * Checks for object colliding with the given line
113
- * @ignore
114
- * @param {Line } line - line to be tested for collision
115
- * @param {Array.<Renderable> } [result] - a user defined array that will be populated with intersecting physic objects.
116
- * @returns {Array.<Renderable> } an array of intersecting physic objects
117
- * @example
118
- * // define a line accross the viewport
119
- * var ray = new me.Line(
120
- * // absolute position of the line
121
- * 0, 0, [
122
- * // starting point relative to the initial position
123
- * new me.Vector2d(0, 0),
124
- * // ending point
125
- * new me.Vector2d(me.game.viewport.width, me.game.viewport.height)
126
- * ]);
127
- *
128
- * // check for collition
129
- * result = me.collision.rayCast(ray);
130
- *
131
- * if (result.length > 0) {
132
- * // ...
133
- * }
134
- */
135
- export function rayCast ( line , result = [ ] ) {
136
- var collisionCounter = 0 ;
137
-
138
- // retrieve a list of potential colliding objects from the game world
139
- var candidates = game . world . broadphase . retrieve ( line ) ;
140
-
141
- for ( var i = candidates . length , objB ; i -- , ( objB = candidates [ i ] ) ; ) {
142
-
143
- // fast AABB check if both bounding boxes are overlaping
144
- if ( objB . body && line . getBounds ( ) . overlaps ( objB . getBounds ( ) ) ) {
91
+ return false ;
92
+ }
145
93
146
- // go trough all defined shapes in B (if any)
147
- var bLen = objB . body . shapes . length ;
148
- if ( objB . body . shapes . length === 0 ) {
149
- continue ;
94
+ /**
95
+ * find all the collisions for the specified object using a broadphase algorithm
96
+ * @ignore
97
+ * @param {Renderable } objA - object to be tested for collision
98
+ * @returns {boolean } in case of collision, false otherwise
99
+ */
100
+ collisions ( objA ) {
101
+ var collisionCounter = 0 ;
102
+ // retreive a list of potential colliding objects from the game world
103
+ var candidates = this . world . broadphase . retrieve ( objA ) ;
104
+
105
+ boundsA . addBounds ( objA . getBounds ( ) , true ) ;
106
+ boundsA . addBounds ( objA . body . getBounds ( ) ) ;
107
+
108
+ candidates . forEach ( ( objB ) => {
109
+ // check if both objects "should" collide
110
+ if ( this . shouldCollide ( objA , objB ) ) {
111
+
112
+ boundsB . addBounds ( objB . getBounds ( ) , true ) ;
113
+ boundsB . addBounds ( objB . body . getBounds ( ) ) ;
114
+
115
+ // fast AABB check if both bounding boxes are overlaping
116
+ if ( boundsA . overlaps ( boundsB ) ) {
117
+
118
+ if ( this . collides ( objA . body , objB . body ) ) {
119
+ // we touched something !
120
+ collisionCounter ++ ;
121
+
122
+ // execute the onCollision callback
123
+ if ( objA . onCollision && objA . onCollision ( this . response , objB ) !== false && objA . body . isStatic === false ) {
124
+ objA . body . respondToCollision . call ( objA . body , this . response ) ;
125
+ }
126
+ if ( objB . onCollision && objB . onCollision ( this . response , objA ) !== false && objB . body . isStatic === false ) {
127
+ objB . body . respondToCollision . call ( objB . body , this . response ) ;
128
+ }
129
+ }
130
+ }
150
131
}
132
+ } ) ;
133
+ // we could return the amount of objects we collided with ?
134
+ return collisionCounter > 0 ;
135
+ }
151
136
152
- var shapeA = line ;
137
+ /**
138
+ * Checks for object colliding with the given line
139
+ * @ignore
140
+ * @param {Line } line - line to be tested for collision
141
+ * @param {Array.<Renderable> } [result] - a user defined array that will be populated with intersecting physic objects.
142
+ * @returns {Array.<Renderable> } an array of intersecting physic objects
143
+ * @example
144
+ * // define a line accross the viewport
145
+ * var ray = new me.Line(
146
+ * // absolute position of the line
147
+ * 0, 0, [
148
+ * // starting point relative to the initial position
149
+ * new me.Vector2d(0, 0),
150
+ * // ending point
151
+ * new me.Vector2d(me.game.viewport.width, me.game.viewport.height)
152
+ * ]);
153
+ *
154
+ * // check for collition
155
+ * result = me.collision.rayCast(ray);
156
+ *
157
+ * if (result.length > 0) {
158
+ * // ...
159
+ * }
160
+ */
161
+ rayCast ( line , result = [ ] ) {
162
+ var collisionCounter = 0 ;
163
+
164
+ // retrieve a list of potential colliding objects from the game world
165
+ var candidates = this . world . broadphase . retrieve ( line ) ;
166
+
167
+ for ( var i = candidates . length , objB ; i -- , ( objB = candidates [ i ] ) ; ) {
153
168
154
- // go through all defined shapes in B
155
- var indexB = 0 ;
156
- do {
157
- var shapeB = objB . body . getShape ( indexB ) ;
169
+ // fast AABB check if both bounding boxes are overlaping
170
+ if ( objB . body && line . getBounds ( ) . overlaps ( objB . getBounds ( ) ) ) {
158
171
159
- // full SAT collision check
160
- if ( SAT [ "test" + shapeA . shapeType + shapeB . shapeType ]
161
- . call (
162
- this ,
163
- dummyObj , // a reference to the object A
164
- shapeA ,
165
- objB , // a reference to the object B
166
- shapeB
167
- ) ) {
168
- // we touched something !
169
- result [ collisionCounter ] = objB ;
170
- collisionCounter ++ ;
172
+ // go trough all defined shapes in B (if any)
173
+ var bLen = objB . body . shapes . length ;
174
+ if ( objB . body . shapes . length === 0 ) {
175
+ continue ;
171
176
}
172
- indexB ++ ;
173
- } while ( indexB < bLen ) ;
177
+
178
+ var shapeA = line ;
179
+
180
+ // go through all defined shapes in B
181
+ var indexB = 0 ;
182
+ do {
183
+ var shapeB = objB . body . getShape ( indexB ) ;
184
+
185
+ // full SAT collision check
186
+ if ( SAT [ "test" + shapeA . shapeType + shapeB . shapeType ]
187
+ . call (
188
+ this ,
189
+ dummyObj , // a reference to the object A
190
+ shapeA ,
191
+ objB , // a reference to the object B
192
+ shapeB
193
+ ) ) {
194
+ // we touched something !
195
+ result [ collisionCounter ] = objB ;
196
+ collisionCounter ++ ;
197
+ }
198
+ indexB ++ ;
199
+ } while ( indexB < bLen ) ;
200
+ }
174
201
}
175
- }
176
202
177
- // cap result in case it was not empty
178
- result . length = collisionCounter ;
203
+ // cap result in case it was not empty
204
+ result . length = collisionCounter ;
179
205
180
- // return the list of colliding objects
181
- return result ;
206
+ // return the list of colliding objects
207
+ return result ;
208
+ }
182
209
}
0 commit comments