refact round
This commit is contained in:
@@ -1,5 +1,20 @@
|
||||
import Phaser from "phaser";
|
||||
|
||||
type PhysicsBodyHolder =
|
||||
| Phaser.Types.Physics.Arcade.GameObjectWithBody
|
||||
| Phaser.Physics.Arcade.Body
|
||||
| Phaser.Physics.Arcade.StaticBody
|
||||
| Phaser.Tilemaps.Tile;
|
||||
|
||||
function getBody(target: PhysicsBodyHolder): Phaser.Physics.Arcade.Body | Phaser.Physics.Arcade.StaticBody {
|
||||
if (target instanceof Phaser.Physics.Arcade.Body || target instanceof Phaser.Physics.Arcade.StaticBody) {
|
||||
return target;
|
||||
}
|
||||
return (target as Phaser.Types.Physics.Arcade.GameObjectWithBody).body as
|
||||
| Phaser.Physics.Arcade.Body
|
||||
| Phaser.Physics.Arcade.StaticBody;
|
||||
}
|
||||
|
||||
export class Platform extends Phaser.GameObjects.TileSprite {
|
||||
static readonly TEXTURE_KEY = "platform";
|
||||
|
||||
@@ -12,15 +27,12 @@ export class Platform extends Phaser.GameObjects.TileSprite {
|
||||
}
|
||||
|
||||
// Phaser collider processCallback: only collide when the mover is descending onto the top.
|
||||
static canLand(
|
||||
mover: Phaser.Types.Physics.Arcade.GameObjectWithBody,
|
||||
platform: Phaser.Types.Physics.Arcade.GameObjectWithBody,
|
||||
): boolean {
|
||||
const moverBody = mover.body as Phaser.Physics.Arcade.Body;
|
||||
const platBody = platform.body as Phaser.Physics.Arcade.StaticBody;
|
||||
static canLand: Phaser.Types.Physics.Arcade.ArcadePhysicsCallback = (mover, platform) => {
|
||||
const moverBody = getBody(mover) as Phaser.Physics.Arcade.Body;
|
||||
const platBody = getBody(platform) as Phaser.Physics.Arcade.StaticBody;
|
||||
return (
|
||||
moverBody.velocity.y >= 0 &&
|
||||
moverBody.bottom - moverBody.deltaY() <= platBody.top
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
11
src/entities/obstacles/ObstacleSpikeBase.ts
Normal file
11
src/entities/obstacles/ObstacleSpikeBase.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import Phaser from "phaser";
|
||||
|
||||
export abstract class ObstacleSpikeBase extends Phaser.GameObjects.Image {
|
||||
declare body: Phaser.Physics.Arcade.StaticBody;
|
||||
|
||||
constructor(scene: Phaser.Scene, x: number, y: number, textureKey: string) {
|
||||
super(scene, x, y, textureKey);
|
||||
scene.add.existing(this);
|
||||
scene.physics.add.existing(this, true);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,12 @@
|
||||
import Phaser from "phaser";
|
||||
import { ObstacleSpikeBase } from "./ObstacleSpikeBase";
|
||||
|
||||
export class ObstacleSpikeNormal extends Phaser.GameObjects.Image {
|
||||
export class ObstacleSpikeNormal extends ObstacleSpikeBase {
|
||||
static readonly TEXTURE_KEY = "spike-normal";
|
||||
static readonly WIDTH = 40;
|
||||
static readonly HEIGHT = 20;
|
||||
|
||||
declare body: Phaser.Physics.Arcade.StaticBody;
|
||||
|
||||
constructor(scene: Phaser.Scene, x: number, y: number) {
|
||||
super(scene, x, y, ObstacleSpikeNormal.TEXTURE_KEY);
|
||||
scene.add.existing(this);
|
||||
scene.physics.add.existing(this, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
import Phaser from "phaser";
|
||||
import { ObstacleSpikeBase } from "./ObstacleSpikeBase";
|
||||
|
||||
export class ObstacleSpikeTricky extends Phaser.GameObjects.Image {
|
||||
export class ObstacleSpikeTricky extends ObstacleSpikeBase {
|
||||
static readonly TEXTURE_KEY = "spike-tricky";
|
||||
static readonly WIDTH = 40;
|
||||
static readonly HEIGHT = 20;
|
||||
static readonly TRIGGER_DISTANCE = 55;
|
||||
static readonly DISPLACEMENT = 100;
|
||||
|
||||
declare body: Phaser.Physics.Arcade.StaticBody;
|
||||
|
||||
private moved = false;
|
||||
|
||||
constructor(scene: Phaser.Scene, x: number, y: number) {
|
||||
super(scene, x, y, ObstacleSpikeTricky.TEXTURE_KEY);
|
||||
scene.add.existing(this);
|
||||
scene.physics.add.existing(this, true);
|
||||
}
|
||||
|
||||
react(playerX: number): void {
|
||||
react(playerX: number, _playerY: number): void {
|
||||
if (this.moved) return;
|
||||
const dx = this.x - playerX;
|
||||
if (Math.abs(dx) < ObstacleSpikeTricky.TRIGGER_DISTANCE) {
|
||||
|
||||
@@ -1,18 +1,6 @@
|
||||
import { defineLevel } from "../lib/levelTypes";
|
||||
|
||||
export const level01 = defineLevel("Spike Hop", [
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
".E........^........P",
|
||||
"####################",
|
||||
"####################",
|
||||
|
||||
@@ -1,18 +1,6 @@
|
||||
import { defineLevel } from "../lib/levelTypes";
|
||||
|
||||
export const level02 = defineLevel("Trap Bridge", [
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
".E.................P",
|
||||
"#######~~~##########",
|
||||
"#######~~~##########",
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
import { defineLevel } from "../lib/levelTypes";
|
||||
|
||||
export const level03 = defineLevel("Trickster Stairs", [
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
".E..................",
|
||||
"#####...............",
|
||||
"....#####...........",
|
||||
|
||||
@@ -1,18 +1,6 @@
|
||||
import { defineLevel } from "../lib/levelTypes";
|
||||
|
||||
export const level04 = defineLevel("Spike Field", [
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
".E....^...^...^....P",
|
||||
"####################",
|
||||
"####################",
|
||||
|
||||
@@ -1,18 +1,6 @@
|
||||
import { defineLevel } from "../lib/levelTypes";
|
||||
|
||||
export const level05 = defineLevel("Pit Crossing", [
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
".E.................P",
|
||||
"####...######...####",
|
||||
"####^^^######^^^####",
|
||||
|
||||
@@ -1,18 +1,6 @@
|
||||
import { defineLevel } from "../lib/levelTypes";
|
||||
|
||||
export const level06 = defineLevel("Trickster", [
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
".E..........T......P",
|
||||
"####################",
|
||||
"####################",
|
||||
|
||||
@@ -1,18 +1,6 @@
|
||||
import { defineLevel } from "../lib/levelTypes";
|
||||
|
||||
export const level07 = defineLevel("Bridge Hopping", [
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
".E.................P",
|
||||
"####...#####...#####",
|
||||
"####...#####...#####",
|
||||
|
||||
@@ -1,18 +1,6 @@
|
||||
import { defineLevel } from "../lib/levelTypes";
|
||||
|
||||
export const level08 = defineLevel("Mixed Hazards", [
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
".E..^......T....^..P",
|
||||
"####################",
|
||||
"####################",
|
||||
|
||||
@@ -1,18 +1,6 @@
|
||||
import { defineLevel } from "../lib/levelTypes";
|
||||
|
||||
export const level09 = defineLevel("Vanishing Bridges", [
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
".E.................P",
|
||||
"####~~~####~~~######",
|
||||
"####~~~####~~~######",
|
||||
|
||||
@@ -1,18 +1,6 @@
|
||||
import { defineLevel } from "../lib/levelTypes";
|
||||
|
||||
export const level10 = defineLevel("Final Gauntlet", [
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
"....................",
|
||||
".E......T..........P",
|
||||
"####...####~~~######",
|
||||
"####^^^####...######",
|
||||
|
||||
@@ -18,18 +18,29 @@ export interface LoadedLevel {
|
||||
trickySpikes: ObstacleSpikeTricky[];
|
||||
}
|
||||
|
||||
interface Cell {
|
||||
row: number;
|
||||
col: number;
|
||||
}
|
||||
const cellCenterX = (col: number): number => col * TILE_SIZE + TILE_SIZE / 2;
|
||||
const cellBottomY = (row: number, entityHeight: number): number =>
|
||||
(row + 1) * TILE_SIZE - entityHeight / 2;
|
||||
|
||||
export class LevelLoader {
|
||||
constructor(private readonly scene: Phaser.Scene) {}
|
||||
|
||||
load(matrix: LevelMatrix): LoadedLevel {
|
||||
return {
|
||||
player: this.buildPlayer(matrix),
|
||||
exit: this.buildExit(matrix),
|
||||
player: this.buildSingleton(
|
||||
matrix,
|
||||
TileType.Player,
|
||||
Player.SIZE,
|
||||
(x, y) => new Player(this.scene, x, y),
|
||||
"Player",
|
||||
),
|
||||
exit: this.buildSingleton(
|
||||
matrix,
|
||||
TileType.Exit,
|
||||
Exit.HEIGHT,
|
||||
(x, y) => new Exit(this.scene, x, y),
|
||||
"Exit",
|
||||
),
|
||||
platforms: this.buildHorizontalRuns(
|
||||
matrix,
|
||||
TileType.Platform,
|
||||
@@ -40,16 +51,18 @@ export class LevelLoader {
|
||||
TileType.DisappearingPlatform,
|
||||
(x, y, w, h) => new ObstaclePlatformDisappearing(this.scene, x, y, w, h),
|
||||
),
|
||||
spikes: this.buildSingleCells(matrix, TileType.Spike, (col, row) => {
|
||||
const x = col * TILE_SIZE + TILE_SIZE / 2;
|
||||
const y = (row + 1) * TILE_SIZE - ObstacleSpikeNormal.HEIGHT / 2;
|
||||
return new ObstacleSpikeNormal(this.scene, x, y);
|
||||
}),
|
||||
trickySpikes: this.buildSingleCells(matrix, TileType.TrickySpike, (col, row) => {
|
||||
const x = col * TILE_SIZE + TILE_SIZE / 2;
|
||||
const y = (row + 1) * TILE_SIZE - ObstacleSpikeTricky.HEIGHT / 2;
|
||||
return new ObstacleSpikeTricky(this.scene, x, y);
|
||||
}),
|
||||
spikes: this.buildSingleCells(
|
||||
matrix,
|
||||
TileType.Spike,
|
||||
ObstacleSpikeNormal.HEIGHT,
|
||||
(x, y) => new ObstacleSpikeNormal(this.scene, x, y),
|
||||
),
|
||||
trickySpikes: this.buildSingleCells(
|
||||
matrix,
|
||||
TileType.TrickySpike,
|
||||
ObstacleSpikeTricky.HEIGHT,
|
||||
(x, y) => new ObstacleSpikeTricky(this.scene, x, y),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -81,39 +94,34 @@ export class LevelLoader {
|
||||
private buildSingleCells<T>(
|
||||
matrix: LevelMatrix,
|
||||
tileType: TileType,
|
||||
factory: (col: number, row: number) => T,
|
||||
entityHeight: number,
|
||||
factory: (x: number, y: number) => T,
|
||||
): T[] {
|
||||
const result: T[] = [];
|
||||
for (let row = 0; row < matrix.length; row++) {
|
||||
for (let col = 0; col < matrix[row].length; col++) {
|
||||
if (matrix[row][col] === tileType) result.push(factory(col, row));
|
||||
if (matrix[row][col] === tileType) {
|
||||
result.push(factory(cellCenterX(col), cellBottomY(row, entityHeight)));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private buildPlayer(matrix: LevelMatrix): Player {
|
||||
const cell = this.findTile(matrix, TileType.Player);
|
||||
if (!cell) throw new Error("Level matrix has no Player tile");
|
||||
const x = cell.col * TILE_SIZE + TILE_SIZE / 2;
|
||||
const y = (cell.row + 1) * TILE_SIZE - Player.SIZE / 2;
|
||||
return new Player(this.scene, x, y);
|
||||
}
|
||||
|
||||
private buildExit(matrix: LevelMatrix): Exit {
|
||||
const cell = this.findTile(matrix, TileType.Exit);
|
||||
if (!cell) throw new Error("Level matrix has no Exit tile");
|
||||
const x = cell.col * TILE_SIZE + TILE_SIZE / 2;
|
||||
const y = (cell.row + 1) * TILE_SIZE - Exit.HEIGHT / 2;
|
||||
return new Exit(this.scene, x, y);
|
||||
}
|
||||
|
||||
private findTile(matrix: LevelMatrix, tile: TileType): Cell | null {
|
||||
private buildSingleton<T>(
|
||||
matrix: LevelMatrix,
|
||||
tileType: TileType,
|
||||
entityHeight: number,
|
||||
factory: (x: number, y: number) => T,
|
||||
label: string,
|
||||
): T {
|
||||
for (let row = 0; row < matrix.length; row++) {
|
||||
for (let col = 0; col < matrix[row].length; col++) {
|
||||
if (matrix[row][col] === tile) return { row, col };
|
||||
if (matrix[row][col] === tileType) {
|
||||
return factory(cellCenterX(col), cellBottomY(row, entityHeight));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
throw new Error(`Level matrix has no ${label} tile`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@ const CHAR_TO_TILE: Record<string, TileType> = {
|
||||
"T": TileType.TrickySpike,
|
||||
};
|
||||
|
||||
const EMPTY_ROW = ".".repeat(LEVEL_COLS);
|
||||
|
||||
function parseLevel(rows: string[]): LevelMatrix {
|
||||
return rows.map((row, rowIdx) =>
|
||||
[...row].map((c, colIdx) => {
|
||||
@@ -42,5 +44,12 @@ function parseLevel(rows: string[]): LevelMatrix {
|
||||
}
|
||||
|
||||
export function defineLevel(name: string, rows: string[]): LevelDef {
|
||||
return { name, matrix: parseLevel(rows) };
|
||||
if (rows.length > LEVEL_ROWS) {
|
||||
throw new Error(
|
||||
`Level "${name}" has ${rows.length} rows, max is ${LEVEL_ROWS}`,
|
||||
);
|
||||
}
|
||||
const padCount = LEVEL_ROWS - rows.length;
|
||||
const padded = padCount > 0 ? [...Array(padCount).fill(EMPTY_ROW), ...rows] : rows;
|
||||
return { name, matrix: parseLevel(padded) };
|
||||
}
|
||||
|
||||
@@ -7,26 +7,69 @@ import {
|
||||
ObstacleSpikeTricky,
|
||||
} from "../entities/obstacles";
|
||||
|
||||
interface SpriteAsset {
|
||||
key: string;
|
||||
path: string;
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
const SPRITE_ASSETS: SpriteAsset[] = [
|
||||
{ key: Player.TEXTURE_KEY, path: "/sprites/player.svg", width: Player.SIZE, height: Player.SIZE },
|
||||
{ key: Exit.TEXTURE_KEY, path: "/sprites/exit.svg", width: Exit.WIDTH, height: Exit.HEIGHT },
|
||||
{ key: Platform.TEXTURE_KEY, path: "/sprites/platform.svg", width: 40, height: 40 },
|
||||
{
|
||||
key: ObstacleSpikeNormal.TEXTURE_KEY,
|
||||
path: "/sprites/spike-normal.svg",
|
||||
width: ObstacleSpikeNormal.WIDTH,
|
||||
height: ObstacleSpikeNormal.HEIGHT,
|
||||
},
|
||||
{
|
||||
key: ObstacleSpikeTricky.TEXTURE_KEY,
|
||||
path: "/sprites/spike-tricky.svg",
|
||||
width: ObstacleSpikeTricky.WIDTH,
|
||||
height: ObstacleSpikeTricky.HEIGHT,
|
||||
},
|
||||
];
|
||||
|
||||
const TITLE_STYLE: Phaser.Types.GameObjects.Text.TextStyle = {
|
||||
fontFamily: "Arial Black, Arial",
|
||||
fontSize: "78px",
|
||||
color: "#ffd700",
|
||||
fontStyle: "bold",
|
||||
stroke: "#7a5500",
|
||||
strokeThickness: 6,
|
||||
};
|
||||
|
||||
const SUBTITLE_STYLE: Phaser.Types.GameObjects.Text.TextStyle = {
|
||||
fontFamily: "Arial",
|
||||
fontSize: "18px",
|
||||
color: "#aaaaaa",
|
||||
fontStyle: "italic",
|
||||
};
|
||||
|
||||
const PROMPT_STYLE: Phaser.Types.GameObjects.Text.TextStyle = {
|
||||
fontFamily: "Arial",
|
||||
fontSize: "26px",
|
||||
color: "#ffffff",
|
||||
fontStyle: "bold",
|
||||
};
|
||||
|
||||
const HINT_STYLE: Phaser.Types.GameObjects.Text.TextStyle = {
|
||||
fontFamily: "Arial",
|
||||
fontSize: "14px",
|
||||
color: "#888888",
|
||||
};
|
||||
|
||||
export class BootScene extends Phaser.Scene {
|
||||
constructor() {
|
||||
super({ key: "BootScene" });
|
||||
}
|
||||
|
||||
preload(): void {
|
||||
this.load.svg(Player.TEXTURE_KEY, "/sprites/player.svg", { width: 32, height: 32 });
|
||||
this.load.svg(Exit.TEXTURE_KEY, "/sprites/exit.svg", {
|
||||
width: Exit.WIDTH,
|
||||
height: Exit.HEIGHT,
|
||||
});
|
||||
this.load.svg(Platform.TEXTURE_KEY, "/sprites/platform.svg", { width: 40, height: 40 });
|
||||
this.load.svg(ObstacleSpikeNormal.TEXTURE_KEY, "/sprites/spike-normal.svg", {
|
||||
width: ObstacleSpikeNormal.WIDTH,
|
||||
height: ObstacleSpikeNormal.HEIGHT,
|
||||
});
|
||||
this.load.svg(ObstacleSpikeTricky.TEXTURE_KEY, "/sprites/spike-tricky.svg", {
|
||||
width: ObstacleSpikeTricky.WIDTH,
|
||||
height: ObstacleSpikeTricky.HEIGHT,
|
||||
});
|
||||
for (const asset of SPRITE_ASSETS) {
|
||||
this.load.svg(asset.key, asset.path, { width: asset.width, height: asset.height });
|
||||
}
|
||||
}
|
||||
|
||||
create(): void {
|
||||
@@ -38,48 +81,16 @@ export class BootScene extends Phaser.Scene {
|
||||
|
||||
this.add.tileSprite(cx, h - 20, w, 40, Platform.TEXTURE_KEY);
|
||||
|
||||
this.add
|
||||
.text(cx, 130, "TRICKSTER", {
|
||||
fontFamily: "Arial Black, Arial",
|
||||
fontSize: "78px",
|
||||
color: "#ffd700",
|
||||
fontStyle: "bold",
|
||||
stroke: "#7a5500",
|
||||
strokeThickness: 6,
|
||||
})
|
||||
.setOrigin(0.5);
|
||||
|
||||
this.add
|
||||
.text(cx, 200, "TILES", {
|
||||
fontFamily: "Arial Black, Arial",
|
||||
fontSize: "78px",
|
||||
color: "#ffd700",
|
||||
fontStyle: "bold",
|
||||
stroke: "#7a5500",
|
||||
strokeThickness: 6,
|
||||
})
|
||||
.setOrigin(0.5);
|
||||
|
||||
this.add
|
||||
.text(cx, 270, "a pixel-art platformer", {
|
||||
fontFamily: "Arial",
|
||||
fontSize: "18px",
|
||||
color: "#aaaaaa",
|
||||
fontStyle: "italic",
|
||||
})
|
||||
.setOrigin(0.5);
|
||||
this.add.text(cx, 130, "TRICKSTER", TITLE_STYLE).setOrigin(0.5);
|
||||
this.add.text(cx, 200, "TILES", TITLE_STYLE).setOrigin(0.5);
|
||||
this.add.text(cx, 270, "a pixel-art platformer", SUBTITLE_STYLE).setOrigin(0.5);
|
||||
|
||||
this.add.image(cx - 80, 360, Player.TEXTURE_KEY);
|
||||
this.add.image(cx, 374, ObstacleSpikeNormal.TEXTURE_KEY);
|
||||
this.add.image(cx + 80, 374, ObstacleSpikeTricky.TEXTURE_KEY);
|
||||
|
||||
const startText = this.add
|
||||
.text(cx, 450, "PRESS SPACE TO START", {
|
||||
fontFamily: "Arial",
|
||||
fontSize: "26px",
|
||||
color: "#ffffff",
|
||||
fontStyle: "bold",
|
||||
})
|
||||
.text(cx, 450, "PRESS SPACE TO START", PROMPT_STYLE)
|
||||
.setOrigin(0.5);
|
||||
|
||||
this.tweens.add({
|
||||
@@ -91,11 +102,7 @@ export class BootScene extends Phaser.Scene {
|
||||
});
|
||||
|
||||
this.add
|
||||
.text(cx, 510, "← → / A D to move SPACE / ↑ / W to jump", {
|
||||
fontFamily: "Arial",
|
||||
fontSize: "14px",
|
||||
color: "#888888",
|
||||
})
|
||||
.text(cx, 510, "← → / A D to move SPACE / ↑ / W to jump", HINT_STYLE)
|
||||
.setOrigin(0.5);
|
||||
|
||||
const start = () => this.scene.start("GameScene");
|
||||
|
||||
@@ -14,9 +14,14 @@ interface SceneData {
|
||||
levelIndex?: number;
|
||||
}
|
||||
|
||||
interface Reactive {
|
||||
react(playerX: number, playerY: number): void;
|
||||
}
|
||||
|
||||
export class GameScene extends Phaser.Scene {
|
||||
private static readonly TRANSITION_DELAY_MS = 1200;
|
||||
private static readonly FALL_LIMIT_PADDING = 80;
|
||||
private static readonly WORLD_BOUNDS_BOTTOM_PADDING = 200;
|
||||
|
||||
private levelIndex = 0;
|
||||
private player!: Player;
|
||||
@@ -25,6 +30,7 @@ export class GameScene extends Phaser.Scene {
|
||||
private disappearing: ObstaclePlatformDisappearing[] = [];
|
||||
private spikes: ObstacleSpikeNormal[] = [];
|
||||
private trickySpikes: ObstacleSpikeTricky[] = [];
|
||||
private reactives: Reactive[] = [];
|
||||
private levelCompleted = false;
|
||||
private dying = false;
|
||||
private hudText!: Phaser.GameObjects.Text;
|
||||
@@ -42,10 +48,16 @@ export class GameScene extends Phaser.Scene {
|
||||
this.disappearing = [];
|
||||
this.spikes = [];
|
||||
this.trickySpikes = [];
|
||||
this.reactives = [];
|
||||
}
|
||||
|
||||
create(): void {
|
||||
this.physics.world.setBounds(0, 0, this.scale.width, this.scale.height + 200);
|
||||
this.physics.world.setBounds(
|
||||
0,
|
||||
0,
|
||||
this.scale.width,
|
||||
this.scale.height + GameScene.WORLD_BOUNDS_BOTTOM_PADDING,
|
||||
);
|
||||
|
||||
const def = LEVELS[this.levelIndex];
|
||||
const loader = new LevelLoader(this);
|
||||
@@ -56,6 +68,7 @@ export class GameScene extends Phaser.Scene {
|
||||
this.disappearing = loaded.disappearing;
|
||||
this.spikes = loaded.spikes;
|
||||
this.trickySpikes = loaded.trickySpikes;
|
||||
this.reactives = [...loaded.disappearing, ...loaded.trickySpikes];
|
||||
|
||||
this.physics.add.collider(this.player, this.platforms, undefined, Platform.canLand);
|
||||
this.physics.add.collider(this.player, this.disappearing, undefined, Platform.canLand);
|
||||
@@ -85,11 +98,8 @@ export class GameScene extends Phaser.Scene {
|
||||
if (this.levelCompleted || this.dying) return;
|
||||
this.player.update();
|
||||
|
||||
for (const platform of this.disappearing) {
|
||||
platform.react(this.player.x, this.player.y);
|
||||
}
|
||||
for (const spike of this.trickySpikes) {
|
||||
spike.react(this.player.x);
|
||||
for (const entity of this.reactives) {
|
||||
entity.react(this.player.x, this.player.y);
|
||||
}
|
||||
|
||||
if (this.player.y > this.scale.height + GameScene.FALL_LIMIT_PADDING) {
|
||||
|
||||
Reference in New Issue
Block a user