Skip to content

Commit 63add75

Browse files
committed
- extract forest level
1 parent a64a3e3 commit 63add75

File tree

11 files changed

+224
-218
lines changed

11 files changed

+224
-218
lines changed

.prettierrc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"printWidth": 120,
3+
"useTabs": false,
4+
"tabWidth": 2,
5+
"singleQuote": false
6+
}

public/assets/tilemaps/forest-map.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -179,12 +179,12 @@
179179
{
180180
"draworder":"topdown",
181181
"id":4,
182-
"name":"Colliders",
182+
"name":"colliders",
183183
"objects":[
184184
{
185185
"height":171,
186186
"id":2,
187-
"name":"decor-object",
187+
"name":"decor",
188188
"rotation":0,
189189
"type":"collider",
190190
"visible":true,
@@ -267,7 +267,7 @@
267267
{
268268
"draworder":"topdown",
269269
"id":5,
270-
"name":"Positions",
270+
"name":"positions",
271271
"objects":[
272272
{
273273
"height":0,

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

Lines changed: 21 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ export class Hero extends Phaser.GameObjects.Sprite {
77
public declare body: ArcadeBody;
88
public projectiles: Phaser.Physics.Arcade.Group;
99
private _isJumping = false;
10-
public get movingLeft(): boolean {
10+
public get isMovingLeft(): boolean {
1111
return this._cursors.left.isDown;
1212
}
13-
public get movingRight(): boolean {
13+
public get isMovingRight(): boolean {
1414
return this._cursors.right.isDown;
1515
}
1616

@@ -31,32 +31,35 @@ export class Hero extends Phaser.GameObjects.Sprite {
3131
constructor(scene: Scene, x: number, y: number) {
3232
super(scene, x, y, SpritesheetTag.HERO);
3333

34-
this.scene.add.existing(this);
35-
this.scene.physics.add.existing(this);
36-
3734
this.setScale(0.5).setDepth(2);
3835

39-
const hitboxWidth = this.width * 0.7;
40-
const hitboxHeight = this.height * 0.8;
41-
this.body.setSize(hitboxWidth, hitboxHeight);
42-
this.body.setOffset(this._offset.x, this._offset.y);
43-
4436
if (!this.scene.input.keyboard) {
4537
throw Error("Keyboard plugin is not available");
4638
}
4739

4840
this._keyboard = this.scene.input.keyboard;
4941
this._cursors = this._keyboard.createCursorKeys();
50-
this._shootKey = this._keyboard.addKey(
51-
Phaser.Input.Keyboard.KeyCodes.SPACE
52-
);
42+
this._shootKey = this._keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE);
5343
this.projectiles = this.scene.physics.add.group({
5444
allowGravity: false,
5545
});
5646

5747
this.createAnimations();
5848
}
5949

50+
public spawn(x: number, y: number): void {
51+
this.scene.add.existing(this);
52+
this.scene.physics.add.existing(this);
53+
54+
this.x = x;
55+
this.y = y;
56+
57+
const hitboxWidth = this.width * 0.7;
58+
const hitboxHeight = this.height * 0.8;
59+
this.body.setSize(hitboxWidth, hitboxHeight);
60+
this.body.setOffset(this._offset.x, this._offset.y);
61+
}
62+
6063
public update(time: number, _: number): void {
6164
this.handleMovements(time);
6265
this.handleProjectiles();
@@ -66,7 +69,7 @@ export class Hero extends Phaser.GameObjects.Sprite {
6669
const isGrounded = this.body.touching.down;
6770

6871
if (isGrounded) {
69-
if (this.movingLeft || this.movingRight) {
72+
if (this.isMovingLeft || this.isMovingRight) {
7073
GameHelper.animate(this, AnimationTag.WALK, {
7174
exceptIf: [AnimationTag.JUMP, AnimationTag.SHOOT],
7275
});
@@ -85,18 +88,17 @@ export class Hero extends Phaser.GameObjects.Sprite {
8588
if (this._isJumping && stupidHack) {
8689
this._isJumping = false;
8790
this.scene.sound.play(SfxTag.LAND);
88-
console.log("is landing", this.body.touching);
8991
this._noOfJump = this._maxNoOfJump;
9092
}
9193
}
9294

93-
if (this.movingRight) {
95+
if (this.isMovingRight) {
9496
this.body.setVelocityX(this._speed);
9597
this.setFlipX(false);
9698
this.body.setOffset(this._offset.x, this._offset.y);
9799
}
98100

99-
if (this.movingLeft) {
101+
if (this.isMovingLeft) {
100102
this.body.setVelocityX(-this._speed);
101103
this.setFlipX(true);
102104
this.body.setOffset(this._offset.x + 6, this._offset.y);
@@ -120,12 +122,7 @@ export class Hero extends Phaser.GameObjects.Sprite {
120122

121123
const sceneWidth = this.scene.cameras.main.getBounds().width;
122124

123-
if (
124-
projectile.x > sceneWidth ||
125-
projectile.x < 0 ||
126-
projectile.y > sceneWidth ||
127-
projectile.y < 0
128-
) {
125+
if (projectile.x > sceneWidth || projectile.x < 0 || projectile.y > sceneWidth || projectile.y < 0) {
129126
projectile.destroy();
130127
}
131128

@@ -194,11 +191,7 @@ export class Hero extends Phaser.GameObjects.Sprite {
194191
});
195192
const velocity = this.flipX ? -600 : 600;
196193

197-
const projectile: ArcadeSprite = this.projectiles.create(
198-
this.x,
199-
this.y,
200-
ImageTag.PROJECTILE_ARROW
201-
);
194+
const projectile: ArcadeSprite = this.projectiles.create(this.x, this.y, ImageTag.PROJECTILE_ARROW);
202195
projectile.setFlipX(this.flipX);
203196
projectile.setVelocityX(velocity);
204197
}

src/helpers/game.helper.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ export class GameHelper {
66
return window.location.hostname === "localhost";
77
}
88

9+
public static get audioIsEnabled(): boolean {
10+
return this.isDev ? false : true;
11+
}
12+
913
public static animate(
1014
sprite: Sprite,
1115
animation: AnimationTag,

src/levels/1-forest.level.ts

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
import { Scene } from "phaser";
2+
import { Hero } from "../game-objects";
3+
import { GameHelper } from "../helpers";
4+
import { Sprite, Tilemap, Tileset } from "../phaser-aliases";
5+
import {
6+
BackgroundSound,
7+
ImageTag,
8+
SfxTag,
9+
TilemapLayerTag,
10+
TilemapObjectsTag,
11+
TilemapObjectsType,
12+
TilemapTag,
13+
TilesetTag,
14+
} from "../tags";
15+
16+
const tilesetNames = ["base-tiles", "decorative-tiles", "water-animation", "tree_bright3", "tree_bright4"];
17+
type TilesetName = (typeof tilesetNames)[number];
18+
19+
export class ForestLevel {
20+
private get _physics(): Phaser.Physics.Arcade.ArcadePhysics {
21+
return this._scene.physics;
22+
}
23+
private get _mainCam(): Phaser.Cameras.Scene2D.Camera {
24+
return this._scene.cameras.main;
25+
}
26+
private _colliderGroup: Phaser.Physics.Arcade.StaticGroup;
27+
private _sceneWidth = 3200;
28+
private _map: Tilemap;
29+
private _layersConfig: Record<TilemapLayerTag, TilesetName[]> = {
30+
[TilemapLayerTag.BACKGROUND]: ["base-tiles"],
31+
[TilemapLayerTag.FOREGROUND]: ["base-tiles", "water-animation"],
32+
[TilemapLayerTag.DECORATIONS]: ["base-tiles", "decorative-tiles", "tree_bright3", "tree_bright4"],
33+
};
34+
private _tilesetsConfig: Record<TilesetName, TilesetTag> = {
35+
"base-tiles": TilesetTag.FOREST_BASE,
36+
"decorative-tiles": TilesetTag.FOREST_DECORATIVE,
37+
"water-animation": TilesetTag.FOREST_WATER_ANIMATION,
38+
tree_bright3: TilesetTag.FOREST_TREE_BRIGHT_3_ANIMATION,
39+
tree_bright4: TilesetTag.FOREST_TREE_BRIGHT_4_ANIMATION,
40+
};
41+
42+
constructor(public hero: Hero, private _scene: Scene) {
43+
this._scene.sound.play(BackgroundSound.RIVER_FLOWING_INSECTS, {
44+
loop: true,
45+
volume: GameHelper.audioIsEnabled ? 1 : 0,
46+
});
47+
48+
this._mainCam.setBounds(0, 0, this._sceneWidth, this._scene.scale.height);
49+
50+
this.addBackgrounds();
51+
52+
this._map = this._scene.make.tilemap({ key: TilemapTag.FOREST });
53+
54+
this.generateMap();
55+
56+
this._colliderGroup = this._physics.add.staticGroup();
57+
58+
this.addColliders();
59+
this.addHero();
60+
this.setCollisions();
61+
}
62+
63+
private addBackgrounds(): void {
64+
const backgrounds = [
65+
ImageTag.FOREST_BACKGROUND_DAY_1,
66+
ImageTag.FOREST_BACKGROUND_DAY_2,
67+
ImageTag.FOREST_BACKGROUND_DAY_3,
68+
ImageTag.FOREST_BACKGROUND_DAY_4,
69+
];
70+
71+
backgrounds.forEach((background) => {
72+
this._scene.add
73+
.tileSprite(0, 0, this._sceneWidth, this._scene.scale.height, background)
74+
.setOrigin(0)
75+
.setScale(1.1, 1.1);
76+
});
77+
}
78+
79+
private generateMap(): void {
80+
// assign each tileset to its name.
81+
const tilesetsMap: Record<TilesetName, Tileset> = tilesetNames.reduce<Record<TilesetName, Tileset>>(
82+
(acc, tilesetName) => {
83+
const tileset = this._map.addTilesetImage(tilesetName, this._tilesetsConfig[tilesetName], 32, 32, 1, 2);
84+
85+
if (!tileset) {
86+
throw Error("Tileset not found");
87+
}
88+
acc[tilesetName] = tileset;
89+
return acc;
90+
},
91+
{}
92+
);
93+
94+
this._map.createLayer(TilemapLayerTag.BACKGROUND, this.getTilesetForLayer(TilemapLayerTag.BACKGROUND, tilesetsMap));
95+
this._map.createLayer(TilemapLayerTag.FOREGROUND, this.getTilesetForLayer(TilemapLayerTag.FOREGROUND, tilesetsMap));
96+
this._map.createLayer(
97+
TilemapLayerTag.DECORATIONS,
98+
this.getTilesetForLayer(TilemapLayerTag.DECORATIONS, tilesetsMap)
99+
);
100+
101+
this._scene.sys.animatedTiles.init(this._map);
102+
}
103+
104+
private getTilesetForLayer(layer: TilemapLayerTag, tilesetsMap: Record<TilesetName, Tileset>): Tileset[] {
105+
return this._layersConfig[layer].map((tilesetName) => tilesetsMap[tilesetName]);
106+
}
107+
108+
private addColliders(): void {
109+
const colliders = this._map.createFromObjects(TilemapObjectsTag.COLLIDERS, {
110+
type: TilemapObjectsType.COLLIDER,
111+
classType: Sprite,
112+
});
113+
114+
this._colliderGroup.addMultiple(colliders);
115+
116+
colliders
117+
.filter((collider): collider is Sprite => collider instanceof Sprite)
118+
.forEach((collider) => {
119+
collider.alpha = 0;
120+
});
121+
}
122+
123+
private addHero(): void {
124+
const heroPosition = this._map
125+
.createFromObjects(TilemapObjectsTag.POSITIONS, {
126+
name: "hero",
127+
classType: Sprite,
128+
})
129+
.at(0);
130+
131+
if (!(heroPosition instanceof Sprite)) {
132+
throw Error("Hero position is not a sprite");
133+
}
134+
135+
heroPosition.alpha = 0;
136+
137+
this.hero.spawn(heroPosition.x, heroPosition.y);
138+
139+
this._mainCam.startFollow(this.hero);
140+
}
141+
142+
private setCollisions(): void {
143+
this._physics.add.collider(this.hero, this._colliderGroup);
144+
145+
this._physics.add.collider(this.hero.projectiles, this._colliderGroup, (projectile) => {
146+
console.log("collided");
147+
this._scene.time.addEvent({
148+
delay: 4000,
149+
callback: () => {
150+
projectile.destroy();
151+
},
152+
});
153+
this._scene.sound.play(SfxTag.ARROW_WALL_IMPACT);
154+
});
155+
}
156+
}

src/levels/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './1-forest.level';

src/main.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
11
import { Game, Types } from "phaser";
22
import { GameHelper } from "./helpers";
3-
import {
4-
Boot,
5-
GameOver,
6-
Game as MainGame,
7-
MainMenu,
8-
Preloader,
9-
} from "./scenes";
3+
import { Boot, GameOver, Game as MainGame, MainMenu, Preloader } from "./scenes";
104

115
// Find out more information about the Game Config at:
126
// https://newdocs.phaser.io/docs/latest/Phaser.Types.Core.GameConfig
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export class ArcadeSprite extends Phaser.Physics.Arcade.Sprite {}
22
export class ArcadeBody extends Phaser.Physics.Arcade.Body {}
33
export class Tile extends Phaser.Tilemaps.Tile {}
4+
export class Tileset extends Phaser.Tilemaps.Tileset {}
45
export class Tilemap extends Phaser.Tilemaps.Tilemap {}
56
export class GameObject extends Phaser.GameObjects.GameObject {}
67
export class Sprite extends Phaser.GameObjects.Sprite {}

0 commit comments

Comments
 (0)