Skip to content

Commit 6d74de0

Browse files
committed
Add queue target behavior
1 parent 7d0d2c1 commit 6d74de0

File tree

3 files changed

+260
-38
lines changed

3 files changed

+260
-38
lines changed

examples/moveto/queue_targets.bat

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
set main=./examples/moveto/queue_targets.js
2+
cd ..
3+
cd ..
4+
npm run dev

examples/moveto/queue_targets.js

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import phaser from 'phaser/src/phaser.js';
2+
import MoveToPlugin from '../../plugins/moveto-plugin.js';
3+
4+
class Demo extends Phaser.Scene {
5+
constructor() {
6+
super({
7+
key: 'examples'
8+
})
9+
}
10+
11+
preload() { }
12+
13+
create() {
14+
var hexPoints = function (cx, cy, radius) {
15+
var points = [];
16+
for (var i = 0; i < 6; i++) {
17+
var angle = (Math.PI * 2 * i) / 6;
18+
points.push({
19+
x: cx + Math.cos(angle) * radius,
20+
y: cy + Math.sin(angle) * radius
21+
});
22+
}
23+
return points;
24+
};
25+
26+
var points0 = hexPoints(250, 300, 120);
27+
var points1 = hexPoints(550, 300, 120);
28+
29+
var dot0 = this.add.circle(points0[0].x, points0[0].y, 16, 0xffffff);
30+
var dot1 = this.add.circle(points1[0].x, points1[0].y, 16, 0xffcc00);
31+
32+
dot0.moveTo = this.plugins.get('rexMoveTo').add(dot0, {
33+
speed: 300
34+
});
35+
dot1.moveTo = this.plugins.get('rexMoveTo').add(dot1, {
36+
speed: 300,
37+
appendMode: true
38+
});
39+
40+
var index0 = 1;
41+
dot0.moveTo.on('complete', function () {
42+
index0 = (index0 + 1) % points0.length;
43+
var next0 = points0[index0];
44+
dot0.moveTo.moveTo(next0.x, next0.y);
45+
});
46+
47+
var queueDot1 = function () {
48+
for (var i = 1; i <= points1.length; i++) {
49+
var next1 = points1[i % points1.length];
50+
dot1.moveTo.moveTo(next1.x, next1.y);
51+
}
52+
};
53+
dot1.moveTo.on('complete', function () {
54+
queueDot1();
55+
});
56+
57+
dot0.moveTo.moveTo(points0[index0].x, points0[index0].y);
58+
queueDot1();
59+
}
60+
61+
update() { }
62+
}
63+
64+
var config = {
65+
type: Phaser.AUTO,
66+
parent: 'phaser-example',
67+
width: 800,
68+
height: 600,
69+
scale: {
70+
mode: Phaser.Scale.FIT,
71+
autoCenter: Phaser.Scale.CENTER_BOTH,
72+
},
73+
scene: Demo,
74+
plugins: {
75+
global: [{
76+
key: 'rexMoveTo',
77+
plugin: MoveToPlugin,
78+
start: true
79+
}]
80+
}
81+
};
82+
83+
var game = new Phaser.Game(config);

plugins/behaviors/moveto/MoveTo.js

Lines changed: 173 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const GetValue = Phaser.Utils.Objects.GetValue;
44
const DistanceBetween = Phaser.Math.Distance.Between;
55
const Lerp = Phaser.Math.Linear;
66
const AngleBetween = Phaser.Math.Angle.Between;
7-
7+
const arriveEpsilon = 0.0001;
88

99
class MoveTo extends TickTask {
1010
constructor(gameObject, config) {
@@ -16,34 +16,51 @@ class MoveTo extends TickTask {
1616
}
1717

1818
resetFromJSON(o) {
19+
this.isCompleted = GetValue(o, 'isCompleted', true);
1920
this.isRunning = GetValue(o, 'isRunning', false);
2021
this.setEnable(GetValue(o, 'enable', true));
2122
this.timeScale = GetValue(o, 'timeScale', 1);
2223
this.setSpeed(GetValue(o, 'speed', 400));
2324
this.setRotateToTarget(GetValue(o, 'rotateToTarget', false));
24-
this.targetX = GetValue(o, 'targetX', 0);
25-
this.targetY = GetValue(o, 'targetY', 0);
25+
this.targetX = GetValue(o, 'targetX', null); // Invalid
26+
this.targetY = GetValue(o, 'targetY', null);
27+
this.appendMode = GetValue(o, 'appendMode', false);
28+
this.targets = GetValue(o, 'targets', []); // {x,y}[]
29+
2630
return this;
2731
}
2832

2933
toJSON() {
3034
return {
35+
isCompleted: this.isCompleted,
3136
isRunning: this.isRunning,
3237
enable: this.enable,
3338
timeScale: this.timeScale,
3439
speed: this.speed,
3540
rotateToTarget: this.rotateToTarget,
3641
targetX: this.targetX,
3742
targetY: this.targetY,
38-
tickingMode: this.tickingMode
43+
tickingMode: this.tickingMode,
44+
appendMode: this.appendMode,
45+
targets: this.targets
3946
};
4047
}
4148

42-
setEnable(e) {
43-
if (e == undefined) {
44-
e = true;
49+
get lastTargetPosition() {
50+
var queuedLength = this.targets.length;
51+
if (queuedLength === 0) {
52+
return { x: this.targetX, y: this.targetY };
53+
} else {
54+
var lastTarget = this.targets[queuedLength - 1];
55+
return { x: lastTarget.x, y: lastTarget.y };
4556
}
46-
this.enable = e;
57+
}
58+
59+
setEnable(enable) {
60+
if (enable == undefined) {
61+
enable = true;
62+
}
63+
this.enable = enable;
4764
return this;
4865
}
4966

@@ -57,27 +74,75 @@ class MoveTo extends TickTask {
5774
return this;
5875
}
5976

77+
setAppendMode(appendMode) {
78+
this.appendMode = !!appendMode;
79+
80+
if (!this.appendMode) {
81+
this.clearTargets();
82+
}
83+
return this;
84+
}
85+
86+
clearTargets() {
87+
this.targets.length = 0;
88+
return this;
89+
}
90+
6091
moveTo(x, y) {
92+
if (x === undefined) {
93+
if (!this.isCompleted) { // Resume
94+
super.start();
95+
} else {
96+
// Does not have target position, do nothing
97+
}
98+
return this;
99+
}
100+
61101
if (typeof (x) !== 'number') {
62102
var config = x;
63103
x = config.x;
64104
y = config.y;
65105
}
66106

67-
this.targetX = x;
68-
this.targetY = y;
69-
super.start();
70-
this.emit('start', this.parent, this);
107+
var isNewTask = false;
108+
if (this.appendMode) {
109+
if (this.isCompleted) { // New task
110+
this.targetX = x;
111+
this.targetY = y;
112+
isNewTask = true;
113+
114+
} else {
115+
this.targets.push({ x, y });
116+
117+
}
118+
119+
} else {
120+
121+
this.targetX = x;
122+
this.targetY = y;
123+
isNewTask = true;
124+
125+
}
126+
127+
if (isNewTask) {
128+
super.start();
129+
this.emit('start', this.parent, this);
130+
}
131+
71132
return this;
72133
}
73134

74135
moveFrom(x, y) {
136+
// This method will clear queue targets
137+
75138
if (typeof (x) !== 'number') {
76139
var config = x;
77140
x = config.x;
78141
y = config.y;
79142
}
80143

144+
this.stop();
145+
81146
var gameObject = this.parent;
82147
var targetX = gameObject.x;
83148
var targetY = gameObject.y;
@@ -90,10 +155,38 @@ class MoveTo extends TickTask {
90155
}
91156

92157
moveToward(angle, distance) {
93-
var gameObject = this.parent;
94-
var targetX = gameObject.x + Math.cos(angle) * distance;
95-
var targetY = gameObject.y + Math.sin(angle) * distance;
158+
var referencePosition;
159+
160+
if (this.appendMode && !this.isCompleted) {
161+
referencePosition = this.lastTargetPosition;
162+
163+
} else {
164+
referencePosition = this.parent; // gameObject
165+
}
166+
167+
var targetX = referencePosition.x + Math.cos(angle) * distance;
168+
var targetY = referencePosition.y + Math.sin(angle) * distance;
96169
this.moveTo(targetX, targetY);
170+
171+
return this;
172+
}
173+
174+
start() {
175+
this.isCompleted = false;
176+
super.start();
177+
return this;
178+
}
179+
180+
stop() {
181+
super.stop();
182+
this.clearTargets();
183+
this.isCompleted = true;
184+
return this;
185+
}
186+
187+
complete() {
188+
super.complete();
189+
this.isCompleted = true;
97190
return this;
98191
}
99192

@@ -102,41 +195,83 @@ class MoveTo extends TickTask {
102195
return this;
103196
}
104197

105-
var gameObject = this.parent;
106-
if (!gameObject.active) {
198+
if (this.targetX == null || this.targetY == null) {
199+
this.stop();
107200
return this;
108201
}
109202

110-
var curX = gameObject.x,
111-
curY = gameObject.y;
112-
var targetX = this.targetX,
113-
targetY = this.targetY;
114-
if ((curX === targetX) && (curY === targetY)) {
115-
this.complete();
203+
var gameObject = this.parent;
204+
if (!gameObject.active) {
116205
return this;
117206
}
118207

119208
if ((this.speed === 0) || (delta === 0) || (this.timeScale === 0)) {
120209
return this;
121210
}
122211

123-
var dt = (delta * this.timeScale) / 1000;
124-
var movingDist = this.speed * dt;
125-
var distToTarget = DistanceBetween(curX, curY, targetX, targetY);
126-
var newX, newY;
127-
if (movingDist < distToTarget) {
128-
var t = movingDist / distToTarget;
129-
newX = Lerp(curX, targetX, t);
130-
newY = Lerp(curY, targetY, t);
131-
} else {
132-
newX = targetX;
133-
newY = targetY;
134-
}
212+
var deltaSeconds = (delta * this.timeScale) / 1000;
213+
var remainingDistanceBudget = this.speed * deltaSeconds;
214+
215+
// Consume remainingDistanceBudget across multiple targets in the same tick
216+
while (remainingDistanceBudget > 0) {
217+
var currentX = gameObject.x;
218+
var currentY = gameObject.y;
219+
220+
var targetX = this.targetX;
221+
var targetY = this.targetY;
222+
223+
var distanceToTarget = DistanceBetween(currentX, currentY, targetX, targetY);
224+
225+
// If already on the current target, switch to next target or complete
226+
if (distanceToTarget <= arriveEpsilon) {
227+
if (this.targets.length > 0) {
228+
var nextTarget = this.targets.shift();
229+
this.targetX = nextTarget.x;
230+
this.targetY = nextTarget.y;
231+
continue;
232+
}
233+
234+
this.complete();
235+
return this;
236+
}
237+
238+
// Move partially toward target
239+
else if (remainingDistanceBudget < distanceToTarget) {
240+
var t = remainingDistanceBudget / distanceToTarget;
241+
var newX = Lerp(currentX, targetX, t);
242+
var newY = Lerp(currentY, targetY, t);
243+
244+
gameObject.setPosition(newX, newY);
245+
246+
if (this.rotateToTarget) {
247+
gameObject.rotation = AngleBetween(currentX, currentY, newX, newY);
248+
}
135249

136-
gameObject.setPosition(newX, newY);
137-
if (this.rotateToTarget) {
138-
gameObject.rotation = AngleBetween(curX, curY, newX, newY);
250+
remainingDistanceBudget = 0;
251+
break;
252+
}
253+
254+
// Reach target and still have remaining distance budget
255+
gameObject.setPosition(targetX, targetY);
256+
257+
if (this.rotateToTarget) {
258+
gameObject.rotation = AngleBetween(currentX, currentY, targetX, targetY);
259+
}
260+
261+
remainingDistanceBudget -= distanceToTarget;
262+
263+
// Continue to next target if any, otherwise complete
264+
if (this.targets.length > 0) {
265+
var nextTargetAfterReach = this.targets.shift();
266+
this.targetX = nextTargetAfterReach.x;
267+
this.targetY = nextTargetAfterReach.y;
268+
continue;
269+
}
270+
271+
this.complete();
272+
return this;
139273
}
274+
140275
return this;
141276
}
142277
}

0 commit comments

Comments
 (0)