]>
Commit | Line | Data |
---|---|---|
1 | import { System } from '@serpentity/serpentity'; | |
2 | import { Body, Constraint, SAT, World } from 'matter-js'; | |
3 | ||
4 | import GrabberNode from '../nodes/grabber'; | |
5 | import GrabbableNode from '../nodes/grabbable'; | |
6 | ||
7 | import Config from '../config'; | |
8 | ||
9 | const internals = { | |
10 | kGrabRadius: 50, | |
11 | kNoEngine: 'No matter js physics engine found. Make sure you set the `engine` key in the config object when initializing.' | |
12 | }; | |
13 | ||
14 | /** | |
15 | * Handles grabbing between entities | |
16 | * | |
17 | * @extends {external:Serpentity.System} | |
18 | * @class GrabSystem | |
19 | * @param {object} config a configuration object to extend. | |
20 | */ | |
21 | export default class GrabSystem extends System { | |
22 | ||
23 | constructor(config = {}) { | |
24 | ||
25 | super(); | |
26 | ||
27 | /** | |
28 | * The node collection of grabbers | |
29 | * | |
30 | * @property {external:Serpentity.NodeCollection} grabbers | |
31 | * @instance | |
32 | * @memberof GrabSystem | |
33 | */ | |
34 | this.grabbers = null; | |
35 | ||
36 | /** | |
37 | * The node collection of grabbables | |
38 | * | |
39 | * @property {external:Serpentity.NodeCollection} grabbables | |
40 | * @instance | |
41 | * @memberof GrabSystem | |
42 | */ | |
43 | this.grabbables = null; | |
44 | ||
45 | /** | |
46 | * The matter-js engine we will use to add and remove constraints | |
47 | * | |
48 | * @property {external:MatterJs.Engine} engine | |
49 | * @instance | |
50 | * @memberof GrabSystem | |
51 | */ | |
52 | this.engine = config.engine; | |
53 | ||
54 | if (!this.engine) { | |
55 | throw new Error(internals.kNoEngine); | |
56 | } | |
57 | } | |
58 | ||
59 | /** | |
60 | * Initializes system when added. Requests grabber and grabbable nodes | |
61 | * | |
62 | * @function added | |
63 | * @memberof GrabSystem | |
64 | * @instance | |
65 | * @param {external:Serpentity.Engine} engine the serpentity engine to | |
66 | * which we are getting added | |
67 | */ | |
68 | added(engine) { | |
69 | ||
70 | this.grabbers = engine.getNodes(GrabberNode); | |
71 | this.grabbables = engine.getNodes(GrabbableNode); | |
72 | } | |
73 | ||
74 | /** | |
75 | * Clears system resources when removed. | |
76 | * | |
77 | * @function removed | |
78 | * @instance | |
79 | * @memberof GrabSystem | |
80 | */ | |
81 | removed() { | |
82 | ||
83 | this.grabbers = null; | |
84 | this.grabbables = null; | |
85 | } | |
86 | ||
87 | /** | |
88 | * Runs on every update of the loop. Triggers grab and manages cooldown | |
89 | * | |
90 | * @function update | |
91 | * @instance | |
92 | * @param {Number} currentFrameDuration the duration of the current | |
93 | * frame | |
94 | * @memberof GrabSystem | |
95 | */ | |
96 | update(currentFrameDuration) { | |
97 | ||
98 | for (const grabber of this.grabbers) { | |
99 | ||
100 | const grab = grabber.grab; | |
101 | ||
102 | if (grab.grabbing && !grab.locked) { | |
103 | this._grab(grabber); | |
104 | } | |
105 | ||
106 | const isGrabReleased = !grab.grabbing || grab.currentCooldown >= grab.cooldown; | |
107 | if (grab.constraint && isGrabReleased) { | |
108 | this._release(grabber); | |
109 | } | |
110 | ||
111 | if (!grab.grabbing && grab.locked && grab.currentCooldown >= grab.cooldown) { | |
112 | this._unlock(grabber); | |
113 | } | |
114 | ||
115 | if (grab.locked) { | |
116 | grab.currentCooldown += currentFrameDuration; | |
117 | } | |
118 | ||
119 | grab.grabbing = 0; | |
120 | } | |
121 | } | |
122 | ||
123 | // Executes the dash action | |
124 | ||
125 | _grab(grabber) { | |
126 | ||
127 | const grab = grabber.grab; | |
128 | ||
129 | grab.locked = true; | |
130 | grab.currentCooldown = 0; | |
131 | ||
132 | Body.setPosition(grabber.grabArea.area, grabber.body.body.position); | |
133 | ||
134 | for (const grabbable of this.grabbables) { | |
135 | ||
136 | if (grabbable.entity === grabber.entity) { | |
137 | continue; | |
138 | } | |
139 | ||
140 | const collision = SAT.collides(grabber.grabArea.area, grabbable.body.body); | |
141 | if (collision.collided) { | |
142 | grab.constraint = this._createConstraint(grabber.body.body, grabbable.body.body); | |
143 | } | |
144 | } | |
145 | } | |
146 | ||
147 | // Executes the unlock action | |
148 | ||
149 | _unlock(grabber) { | |
150 | ||
151 | grabber.grab.locked = false; | |
152 | } | |
153 | ||
154 | // Releases a constraint | |
155 | ||
156 | _release(grabber) { | |
157 | ||
158 | World.remove(this.engine.world, grabber.grab.constraint); | |
159 | grabber.grab.currentCooldown = 0; | |
160 | grabber.grab.constraint = null; | |
161 | } | |
162 | ||
163 | // Performs a grab between two entities | |
164 | ||
165 | _createConstraint(grabber, grabbable) { | |
166 | ||
167 | const constraint = Constraint.create({ // Attach the sensor to the body | |
168 | bodyA: grabber, | |
169 | bodyB: grabbable, | |
170 | damping: 0, | |
171 | length: internals.kGrabRadius / Config.meterSize, | |
172 | stiffness: 1 | |
173 | }); | |
174 | ||
175 | World.add(this.engine.world, [constraint]); | |
176 | ||
177 | return constraint; | |
178 | } | |
179 | } |