Skip to content

Commit edf9e68

Browse files
committed
- ennemy patrolling and chasing
1 parent b4edb71 commit edf9e68

File tree

6 files changed

+115
-55
lines changed

6 files changed

+115
-55
lines changed

esbuild/build.prod.mjs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,12 @@ import clean from "esbuild-plugin-clean";
33
import copy from "esbuild-plugin-copy";
44
import inlineImage from "esbuild-plugin-inline-image";
55

6-
const pluginDirectory = "./public/assets/plugins";
7-
86
const builder = async () => {
97
await build({
108
entryPoints: ["./src/main.ts"],
119
bundle: true,
1210
minify: true,
1311
sourcemap: false,
14-
// target: ["chrome58", "firefox57", "safari11", "edge16"],
1512
outfile: "./dist/bundle.min.js",
1613
plugins: [
1714
clean({

src/game-objects/ennemy.game-object.ts

Lines changed: 88 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,120 @@
11
import { Scene } from "phaser";
22
import { depthsConfig } from "../configs";
3+
import { GameHelper } from "../helpers";
34
import { ArcadeBody, Sprite } from "../phaser-aliases";
45
import { AnimationTag, EnnemyTag } from "../tags";
6+
import { Hero } from "./hero.game-object";
7+
8+
export interface EnnemyConfig {
9+
scene: Scene;
10+
x: number;
11+
y: number;
12+
patrolDistance: number;
13+
chaseDistance: number;
14+
speed: number;
15+
sprite: EnnemyTag;
16+
}
517

618
export class Ennemy extends Sprite {
719
public declare body: ArcadeBody;
820

21+
private _patrolDistance: number;
22+
private _chaseDistance: number;
23+
private _player: Hero;
24+
private _startingX: number;
25+
private _spriteTag: EnnemyTag;
26+
private _patrolDirection = 1;
927
private _speed = 60;
10-
private _startingPoint = { x: 0, y: 0 };
11-
private _endingPoint = { x: 0, y: 0 };
12-
private _isMovingToEnd = true;
28+
private _patrolTween: Phaser.Tweens.Tween | null = null;
1329

14-
constructor(private _scene: Scene, private _spriteTag: EnnemyTag) {
15-
super(_scene, 0, 0, _spriteTag);
30+
private get _isPatrolling(): boolean {
31+
return this._patrolTween !== null;
32+
}
1633

17-
this.setDepth(depthsConfig.ennemies);
34+
constructor(config: EnnemyConfig, player: Hero) {
35+
super(config.scene, config.x, config.y, config.sprite);
36+
this._startingX = config.x;
37+
this._patrolDistance = config.patrolDistance;
38+
this._chaseDistance = config.chaseDistance;
39+
this._speed = config.speed;
40+
this._spriteTag = config.sprite;
1841

19-
this.createAnimations();
20-
}
42+
this._player = player;
2143

22-
public spawn(x: number, y: number): void {
2344
this.scene.add.existing(this);
2445
this.scene.physics.add.existing(this);
2546

26-
this.play(AnimationTag.ENNEMY_IDLE);
27-
2847
this.flipX = true;
29-
this.x = x;
30-
this.y = y;
3148

32-
this._startingPoint = { x, y };
33-
}
49+
this.setDepth(depthsConfig.ennemies);
3450

35-
public patrol(endingPoint: { x: number; y: number }): void {
36-
this._endingPoint = endingPoint;
51+
this.createAnimations();
52+
this.startPatrol();
3753
this.play(AnimationTag.ENNEMY_MOVING);
54+
}
3855

39-
this._scene.events.on("update", () => {
40-
if (this._isMovingToEnd) {
41-
this.goToTarget(this._endingPoint.x, this._endingPoint.y);
56+
public update(): void {
57+
this.updateFlipX();
4258

43-
const distance = Phaser.Math.Distance.Between(this.x, this.y, this._endingPoint.x, this._endingPoint.y);
59+
const distanceToPlayer = Phaser.Math.Distance.Between(this.x, this.y, this._player.x, this._player.y);
4460

45-
if (distance < 10) {
46-
this._isMovingToEnd = false;
47-
}
48-
} else {
49-
this.goToTarget(this._startingPoint.x, this._startingPoint.y);
61+
if (distanceToPlayer <= this._chaseDistance) {
62+
this.stopPatrol();
63+
this.chasePlayer();
64+
} //
65+
else if (distanceToPlayer > this._chaseDistance && this._patrolTween === null) {
66+
this.returnToStart();
67+
}
68+
}
5069

51-
const distance = Phaser.Math.Distance.Between(this.x, this.y, this._startingPoint.x, this._startingPoint.y);
70+
private startPatrol(): void {
71+
if (this._patrolTween !== null) {
72+
return;
73+
}
5274

53-
if (distance < 10) {
54-
this._isMovingToEnd = true;
55-
}
56-
}
75+
this._patrolDirection = Math.sign(this._patrolDistance);
5776

58-
this.flipX = this.body.velocity.x < 0;
77+
this._patrolTween = this.scene.tweens.add({
78+
targets: this,
79+
x: this._startingX + this._patrolDistance,
80+
duration: Math.abs(this._patrolDistance) * 10,
81+
yoyo: true,
82+
repeat: -1,
83+
onYoyo: () => (this._patrolDirection *= -1),
84+
onRepeat: () => (this._patrolDirection *= -1),
5985
});
6086
}
6187

62-
public goToTarget(targetX: number, targetY: number): void {
63-
this.scene.physics.moveTo(this, targetX, targetY, this._speed);
88+
private stopPatrol(): void {
89+
if (this._patrolTween !== null) {
90+
this._patrolTween.stop();
91+
this._patrolTween = null;
92+
}
93+
}
94+
95+
private chasePlayer(): void {
96+
const direction = Math.sign(this._player.x - this.x);
97+
this.body.setVelocityX(direction * this._speed);
98+
}
99+
100+
private returnToStart(): void {
101+
if (!GameHelper.isCloseEnough(this.x, this._startingX)) {
102+
const direction = Math.sign(this._startingX - this.x);
103+
this.body.setVelocityX(direction * this._speed);
104+
} else {
105+
console.log("reached start");
106+
this.body.setVelocityX(0);
107+
this.startPatrol();
108+
}
109+
}
110+
111+
private updateFlipX(): void {
112+
// Patrol is using a tween, which updates X position instead of velocity
113+
if (this._isPatrolling) {
114+
this.setFlipX(this._patrolDirection < 0);
115+
} else {
116+
this.setFlipX(this.body.velocity.x < 0);
117+
}
64118
}
65119

66120
private createAnimations(): void {

src/game-objects/hero.game-object.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ import { AnimationTag, ImageTag, SfxTag, SpritesheetTag } from "../tags";
77
export class Hero extends Sprite {
88
public declare body: ArcadeBody;
99
public projectiles: Phaser.Physics.Arcade.Group;
10-
private _isJumping = false;
1110
public get isMovingLeft(): boolean {
1211
return this._cursors.left.isDown;
1312
}
1413
public get isMovingRight(): boolean {
1514
return this._cursors.right.isDown;
1615
}
16+
public get speed(): number {
17+
return this._speed;
18+
}
1719

1820
private _speed = 160;
1921
private _jumpSpeed = 360;
@@ -23,6 +25,7 @@ export class Hero extends Sprite {
2325
private _cursors: Phaser.Types.Input.Keyboard.CursorKeys;
2426
private _keyboard: Phaser.Input.Keyboard.KeyboardPlugin;
2527
private _shootKey: Phaser.Input.Keyboard.Key;
28+
private _isJumping = false;
2629
// That's SHIT. I need to understand better how it works and make it more dynamic
2730
private _offset = {
2831
x: 8,

src/helpers/game.helper.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,8 @@ export class GameHelper {
3939

4040
sprite.anims.play(animation, ignoreIfPlaying);
4141
}
42+
43+
public static isCloseEnough(value1: number, value2: number, epsilon: number = 1): boolean {
44+
return Math.abs(value1 - value2) <= epsilon;
45+
}
4246
}

src/levels/1-forest.level.ts

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Scene } from "phaser";
22
import { depthsConfig } from "../configs";
3-
import { Ennemy, Hero } from "../game-objects";
3+
import { Ennemy, EnnemyConfig, Hero } from "../game-objects";
44
import { GameHelper } from "../helpers";
55
import { Sprite, Tilemap, Tileset } from "../phaser-aliases";
66
import {
@@ -122,6 +122,12 @@ export class ForestLevel {
122122
this.setCollisions();
123123
}
124124

125+
public update(): void {
126+
this._ennemies.getChildren().forEach((ennemy) => {
127+
ennemy.update();
128+
});
129+
}
130+
125131
private addBackgrounds(): void {
126132
const backgrounds = [
127133
ImageTag.FOREST_BACKGROUND_DAY_1,
@@ -200,13 +206,6 @@ export class ForestLevel {
200206
}
201207

202208
private addEnnemies(): void {
203-
const ennemies = this._map
204-
.createFromObjects(TilemapObjectsTag.POSITIONS, {
205-
type: "ennemy",
206-
classType: Sprite,
207-
})
208-
.filter((ennemy): ennemy is Sprite => ennemy instanceof Sprite);
209-
210209
const positions = this._map.objects.find(
211210
(objectLayer) => objectLayer.name === TilemapObjectsTag.POSITIONS
212211
)?.objects;
@@ -221,19 +220,20 @@ export class ForestLevel {
221220
if (!patrol.x || !patrol.y || !patrol.polyline || patrol.polyline.length < 2) {
222221
throw Error("Invalid patrol object");
223222
}
224-
console.log("patrol", patrol);
225223

226224
const { x, y, polyline } = patrol;
227225

228-
const ennemyObject = new Ennemy(this._scene, patrol.name as EnnemyTag);
229-
ennemyObject.spawn(x, y);
230-
231-
const endingPoint = {
232-
x: polyline[1].x + x,
233-
y: polyline[1].y + y,
226+
const ennemyConfig: EnnemyConfig = {
227+
x,
228+
y,
229+
chaseDistance: 200,
230+
patrolDistance: polyline[1].x,
231+
speed: this.hero.speed - 30,
232+
sprite: patrol.name as EnnemyTag,
233+
scene: this._scene,
234234
};
235235

236-
ennemyObject.patrol(endingPoint);
236+
const ennemyObject = new Ennemy(ennemyConfig, this.hero);
237237

238238
this._ennemies.add(ennemyObject);
239239
});

src/scenes/game.scene.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export class Game extends Scene {
88
private _hero: Hero | undefined;
99
private _keyboard: Phaser.Input.Keyboard.KeyboardPlugin | undefined;
1010
private _toggleDebugKey: Phaser.Input.Keyboard.Key | undefined;
11+
private _currentLevel: ForestLevel | undefined;
1112

1213
constructor() {
1314
super(SceneTag.GAME);
@@ -28,7 +29,7 @@ export class Game extends Scene {
2829
this.createDebug();
2930

3031
this._hero = new Hero(this);
31-
new ForestLevel(this._hero, this);
32+
this._currentLevel = new ForestLevel(this._hero, this);
3233
}
3334

3435
/**
@@ -47,6 +48,7 @@ export class Game extends Scene {
4748
}
4849

4950
this._hero?.update(time, delta);
51+
this._currentLevel?.update();
5052
}
5153

5254
private createDebug(): void {

0 commit comments

Comments
 (0)