feat: Implement basic level 1 with tilemap, enemies, pathfinding and wave system
- Added level 1 assets (tilemap, terrain, enemy sprites) - Implemented Level1 scene with tilemap loading and collision detection - Integrated EasyStar.js for enemy pathfinding between spawn and end points - Created wave manager system to handle enemy spawning schedules - Added basic enemy configuration and animation support - Set up game structure with Phaser 3 framework - Added web server startup script for local development This commit establishes the foundational level 1 gameplay including map rendering, collision detection, enemy spawning mechanics, and pathfinding behavior using EasyStar.js for AI movement.
This commit is contained in:
commit
9bf0b55f33
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
|
|
@ -0,0 +1,111 @@
|
||||||
|
{ "compressionlevel":-1,
|
||||||
|
"height":9,
|
||||||
|
"infinite":true,
|
||||||
|
"layers":[
|
||||||
|
{
|
||||||
|
"chunks":[
|
||||||
|
{
|
||||||
|
"data":[2, 1, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
2, 1, 2, 1, 1, 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
2, 1, 1, 1, 2, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
2, 2, 2, 2, 2, 2, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
"height":16,
|
||||||
|
"width":16,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
}],
|
||||||
|
"height":16,
|
||||||
|
"id":1,
|
||||||
|
"name":"main",
|
||||||
|
"opacity":1,
|
||||||
|
"startx":0,
|
||||||
|
"starty":0,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":16,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chunks":[
|
||||||
|
{
|
||||||
|
"data":[0, 0, 16, 0, 16, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
16, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 16, 16, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
"height":16,
|
||||||
|
"width":16,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
}],
|
||||||
|
"height":16,
|
||||||
|
"id":2,
|
||||||
|
"name":"platforms",
|
||||||
|
"opacity":1,
|
||||||
|
"startx":0,
|
||||||
|
"starty":0,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":16,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
}],
|
||||||
|
"nextlayerid":3,
|
||||||
|
"nextobjectid":1,
|
||||||
|
"orientation":"orthogonal",
|
||||||
|
"renderorder":"right-down",
|
||||||
|
"tiledversion":"1.11.2",
|
||||||
|
"tileheight":200,
|
||||||
|
"tilesets":[
|
||||||
|
{
|
||||||
|
"columns":5,
|
||||||
|
"firstgid":1,
|
||||||
|
"image":"terrain.png",
|
||||||
|
"imageheight":1000,
|
||||||
|
"imagewidth":1000,
|
||||||
|
"margin":0,
|
||||||
|
"name":"terrain",
|
||||||
|
"spacing":0,
|
||||||
|
"tilecount":25,
|
||||||
|
"tileheight":200,
|
||||||
|
"tiles":[
|
||||||
|
{
|
||||||
|
"id":1,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"collides",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
}]
|
||||||
|
}],
|
||||||
|
"tilewidth":200
|
||||||
|
}],
|
||||||
|
"tilewidth":200,
|
||||||
|
"type":"map",
|
||||||
|
"version":"1.10",
|
||||||
|
"width":16
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 213 KiB |
Binary file not shown.
|
|
@ -0,0 +1,58 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<map version="1.10" tiledversion="1.11.2" orientation="orthogonal" renderorder="right-down" width="16" height="9" tilewidth="200" tileheight="200" infinite="1" nextlayerid="3" nextobjectid="1">
|
||||||
|
<editorsettings>
|
||||||
|
<export target="../level1.json" format="json"/>
|
||||||
|
</editorsettings>
|
||||||
|
<tileset firstgid="1" name="terrain" tilewidth="200" tileheight="200" tilecount="25" columns="5">
|
||||||
|
<image source="../terrain.png" width="1000" height="1000"/>
|
||||||
|
<tile id="1">
|
||||||
|
<properties>
|
||||||
|
<property name="collides" type="bool" value="true"/>
|
||||||
|
</properties>
|
||||||
|
</tile>
|
||||||
|
</tileset>
|
||||||
|
<layer id="1" name="main" width="16" height="9">
|
||||||
|
<data encoding="csv">
|
||||||
|
<chunk x="0" y="0" width="16" height="16">
|
||||||
|
2,1,2,2,2,2,2,2,0,0,0,0,0,0,0,0,
|
||||||
|
2,1,2,1,1,1,2,2,0,0,0,0,0,0,0,0,
|
||||||
|
2,1,1,1,2,1,1,2,0,0,0,0,0,0,0,0,
|
||||||
|
2,2,2,2,2,2,1,2,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||||
|
</chunk>
|
||||||
|
</data>
|
||||||
|
</layer>
|
||||||
|
<layer id="2" name="platforms" width="16" height="9">
|
||||||
|
<data encoding="csv">
|
||||||
|
<chunk x="0" y="0" width="16" height="16">
|
||||||
|
0,0,16,0,16,0,16,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,16,0,0,0,16,0,0,0,0,0,0,0,0,0,
|
||||||
|
16,0,0,0,16,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,16,16,0,16,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||||
|
</chunk>
|
||||||
|
</data>
|
||||||
|
</layer>
|
||||||
|
</map>
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>Alien Rush</title>
|
||||||
|
<style>
|
||||||
|
body { margin: 0; background-color: black; }
|
||||||
|
canvas { display: block; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="game-container"></div>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/phaser@v3.90.0/dist/phaser.min.js"></script>
|
||||||
|
<script src="./src/EasyStar.js"></script>
|
||||||
|
<script type="module" src="./src/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,60 @@
|
||||||
|
import { WaveManager } from '../support/waveManager.js';
|
||||||
|
|
||||||
|
export class Level1 extends Phaser.Scene {
|
||||||
|
constructor() {
|
||||||
|
super({ key: 'Level1' });
|
||||||
|
}
|
||||||
|
|
||||||
|
init(data) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
preload() {
|
||||||
|
this.load.tilemapTiledJSON('level1', 'assets/level1.json');
|
||||||
|
this.load.image('terrain', 'assets/terrain.png');
|
||||||
|
this.load.spritesheet('basic-enemies', 'assets/basic-enemies.png', {
|
||||||
|
frameWidth: 50,
|
||||||
|
frameHeight: 50
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
create() {
|
||||||
|
this.levelMap = this.make.tilemap({ key: 'level1' });
|
||||||
|
const terrainTiles = this.levelMap.addTilesetImage('terrain', 'terrain');
|
||||||
|
this.mainLayer = this.levelMap.createLayer('main', terrainTiles)
|
||||||
|
.setCollisionByProperty({ collides: true });
|
||||||
|
this.platformsLayer = this.levelMap.createLayer('platforms', terrainTiles);
|
||||||
|
|
||||||
|
this.waveManager = new WaveManager(this, 1, 1);
|
||||||
|
|
||||||
|
this.enemies = this.physics.add.group();
|
||||||
|
|
||||||
|
this.physics.add.collider(this.enemies, this.mainLayer);
|
||||||
|
this.physics.add.collider(this.enemies, this.platformsLayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
moveJoshAlongPath(path) {
|
||||||
|
let currentIndex = 0;
|
||||||
|
|
||||||
|
const moveNextStep = () => {
|
||||||
|
if (currentIndex >= path.length - 1) return; // Reached target
|
||||||
|
|
||||||
|
const nextPoint = path[++currentIndex];
|
||||||
|
|
||||||
|
// Move josh to the next point
|
||||||
|
this.tweens.add({
|
||||||
|
targets: this.josh,
|
||||||
|
x: nextPoint.x * 200 + 100,
|
||||||
|
y: nextPoint.y * 200 + 100,
|
||||||
|
duration: 2000,
|
||||||
|
onComplete: moveNextStep
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
moveNextStep();
|
||||||
|
}
|
||||||
|
|
||||||
|
update(time, delta) {
|
||||||
|
this.waveManager.update(time, delta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { Level1 } from './levels/level1.js';
|
||||||
|
|
||||||
|
const GAME_CONFIG = {
|
||||||
|
type: Phaser.AUTO,
|
||||||
|
scale: {
|
||||||
|
mode: Phaser.Scale.FIT,
|
||||||
|
autoCenter: Phaser.Scale.CENTER_BOTH,
|
||||||
|
width: 1600,
|
||||||
|
height: 900,
|
||||||
|
parent: 'game-container'
|
||||||
|
},
|
||||||
|
parent: 'game-container',
|
||||||
|
backgroundColor: '#bb7432ff',
|
||||||
|
scene: [
|
||||||
|
Level1
|
||||||
|
],
|
||||||
|
physics: {
|
||||||
|
default: 'arcade',
|
||||||
|
arcade: {
|
||||||
|
gravity: { y: 0 },
|
||||||
|
debug: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create the game instance
|
||||||
|
const game = new Phaser.Game(GAME_CONFIG);
|
||||||
|
|
||||||
|
console.log('Alien Rush game initialized successfully!');
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
import { ENEMIES_CONFIG } from "./enemiesConfig.js";
|
||||||
|
|
||||||
|
export class Enemies {
|
||||||
|
|
||||||
|
constructor(scene, type, x, y, path) {
|
||||||
|
this.scene = scene;
|
||||||
|
this.type = type;
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.path = path;
|
||||||
|
this.spread = ENEMIES_CONFIG[type].spread;
|
||||||
|
this.speedLow = ENEMIES_CONFIG[type].speedLow;
|
||||||
|
this.speedHigh = ENEMIES_CONFIG[type].speedHigh;
|
||||||
|
this.baseSprite = ENEMIES_CONFIG[type].spriteStart;
|
||||||
|
|
||||||
|
this.spawnEnemy();
|
||||||
|
}
|
||||||
|
|
||||||
|
spawnEnemy() {
|
||||||
|
const randX = Phaser.Math.Between(-this.spread, this.spread);
|
||||||
|
const randY = Phaser.Math.Between(-this.spread, this.spread);
|
||||||
|
const randSpeed = Phaser.Math.Between(this.speedLow, this.speedHigh);
|
||||||
|
const spawnX = (this.x * 200) + 100 + randX;
|
||||||
|
const spawnY = (this.y * 200) + 100 + randY;
|
||||||
|
|
||||||
|
// Create enemy and store reference
|
||||||
|
const enemy = this.scene.add.sprite(spawnX, spawnY, ENEMIES_CONFIG[this.type].spriteSheet, ENEMIES_CONFIG[this.type].spriteStart);
|
||||||
|
|
||||||
|
// Create Animations
|
||||||
|
this.createAnim('side', ENEMIES_CONFIG[this.type].spriteStart, ENEMIES_CONFIG[this.type].spriteStart+2);
|
||||||
|
this.createAnim('up', ENEMIES_CONFIG[this.type].spriteStart+6, ENEMIES_CONFIG[this.type].spriteStart+7);
|
||||||
|
this.createAnim('down', ENEMIES_CONFIG[this.type].spriteStart+3, ENEMIES_CONFIG[this.type].spriteStart+5);
|
||||||
|
this.createAnim('die', ENEMIES_CONFIG[this.type].spriteStart+8, ENEMIES_CONFIG[this.type].spriteStart+9, 0);
|
||||||
|
|
||||||
|
enemy.props = {
|
||||||
|
'offsetX': randX,
|
||||||
|
'offsetY': randY,
|
||||||
|
'path': this.path,
|
||||||
|
'pathPhase': 0,
|
||||||
|
'speed': randSpeed,
|
||||||
|
'health': ENEMIES_CONFIG[this.type].health,
|
||||||
|
'type': this.type
|
||||||
|
};
|
||||||
|
|
||||||
|
this.scene.enemies.add(enemy);
|
||||||
|
|
||||||
|
enemy.play(`${this.type}-side`);
|
||||||
|
}
|
||||||
|
|
||||||
|
createAnim(type, start, end, repeat = -1) {
|
||||||
|
if (!this.scene.anims.get(`${this.type}-${type}`)) {
|
||||||
|
this.scene.anims.create({
|
||||||
|
key: `${this.type}-${type}`,
|
||||||
|
frames: this.scene.anims.generateFrameNumbers(ENEMIES_CONFIG[this.type].spriteSheet, {
|
||||||
|
start: start,
|
||||||
|
end: end,
|
||||||
|
}),
|
||||||
|
frameRate: 5,
|
||||||
|
repeat: repeat
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
export const ENEMIES_CONFIG = {
|
||||||
|
'basic1': {
|
||||||
|
'spread': 25,
|
||||||
|
'health': 100,
|
||||||
|
'speedLow': 25,
|
||||||
|
'speedHigh': 35,
|
||||||
|
'spriteStart': 0,
|
||||||
|
'spriteSheet': 'basic-enemies'
|
||||||
|
},
|
||||||
|
'basic2': {
|
||||||
|
'spread': 0,
|
||||||
|
'health': 300,
|
||||||
|
'speedLow': 45,
|
||||||
|
'speedHigh': 55,
|
||||||
|
'spriteStart': 0,
|
||||||
|
'spriteSheet': 'basic-enemies'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
export const WAVE_CONFIG = {
|
||||||
|
// Level
|
||||||
|
1: {
|
||||||
|
//Spawn Point
|
||||||
|
spawnX: 1,
|
||||||
|
spawnY: 0,
|
||||||
|
endX: 6,
|
||||||
|
endY: 3,
|
||||||
|
// Wave
|
||||||
|
1: {
|
||||||
|
// Schedule
|
||||||
|
1: {
|
||||||
|
begin: 0,
|
||||||
|
basic1: 5
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
begin: 15,
|
||||||
|
basic1: 1
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
begin: 30,
|
||||||
|
basic1: 5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Wave
|
||||||
|
2: {
|
||||||
|
// Schedule
|
||||||
|
1: {
|
||||||
|
begin: 0,
|
||||||
|
basic1: 5
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
begin: 15,
|
||||||
|
basic1: 5
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
begin: 30,
|
||||||
|
basic1: 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,194 @@
|
||||||
|
import { WAVE_CONFIG } from './waveConfig.js'
|
||||||
|
import { Enemies } from './enemies.js'
|
||||||
|
|
||||||
|
export class WaveManager {
|
||||||
|
|
||||||
|
constructor(scene, level, wave) {
|
||||||
|
this.scene = scene;
|
||||||
|
this.level = level;
|
||||||
|
this.wave = wave;
|
||||||
|
this.schedule = 0;
|
||||||
|
this.scheduleInfo = null;
|
||||||
|
this.spawnX = WAVE_CONFIG[this.level].spawnX;
|
||||||
|
this.spawnY = WAVE_CONFIG[this.level].spawnY;
|
||||||
|
this.endX = WAVE_CONFIG[this.level].endX;
|
||||||
|
this.endY = WAVE_CONFIG[this.level].endY;
|
||||||
|
this.path = null;
|
||||||
|
|
||||||
|
this.waveTimer = 0;
|
||||||
|
this.waveScheduleStartTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
waveScheduleStartTime() {
|
||||||
|
this.schedule++;
|
||||||
|
this.scheduleInfo = WAVE_CONFIG[this.level][this.wave][this.schedule]
|
||||||
|
this.waveStart = this.scheduleInfo.begin * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
nextWave() {
|
||||||
|
this.wave++;
|
||||||
|
this.waveTimer = 0;
|
||||||
|
this.schedule = 0;
|
||||||
|
this.waveScheduleStartTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
gridToLocation(num, offset = 0) {
|
||||||
|
return num * 200 + 100 + offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
update(time, delta) {
|
||||||
|
this.waveTimer += delta;
|
||||||
|
|
||||||
|
// Handle Enemy Pathing
|
||||||
|
this.scene.enemies.children.iterate((enemy) => {
|
||||||
|
const path = enemy.props.path;
|
||||||
|
const pathPhase = enemy.props.pathPhase;
|
||||||
|
const speed = enemy.props.speed;
|
||||||
|
const offsetX = enemy.props.offsetX;
|
||||||
|
const offsetY = enemy.props.offsetY;
|
||||||
|
|
||||||
|
// Only move if we have a valid path and pathPhase
|
||||||
|
if (path && pathPhase !== undefined) {
|
||||||
|
// Calculate movement for this frame
|
||||||
|
const segmentSpeed = speed; // Normalize by delta time
|
||||||
|
|
||||||
|
// Get current and next points in the path
|
||||||
|
const currentPoint = path[pathPhase];
|
||||||
|
const nextPoint = path[pathPhase + 1];
|
||||||
|
|
||||||
|
if (currentPoint && nextPoint) {
|
||||||
|
// Calculate direction vector from current to next point
|
||||||
|
const dx = this.gridToLocation(nextPoint.x, offsetX) - this.gridToLocation(currentPoint.x, offsetX);
|
||||||
|
const dy = this.gridToLocation(nextPoint.y, offsetY) - this.gridToLocation(currentPoint.y, offsetY);
|
||||||
|
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||||
|
|
||||||
|
// Normalize the direction
|
||||||
|
if (distance > 0) {
|
||||||
|
const normalizedDx = dx / distance;
|
||||||
|
const normalizedDy = dy / distance;
|
||||||
|
|
||||||
|
const velocX = normalizedDx * segmentSpeed;
|
||||||
|
const velocY = normalizedDy * segmentSpeed;
|
||||||
|
// Move towards next point
|
||||||
|
enemy.body.setVelocity(velocX, velocY);
|
||||||
|
|
||||||
|
if (Math.abs(velocY) > Math.abs(velocX)) {
|
||||||
|
if (velocY > 0) {
|
||||||
|
enemy.play(`${enemy.props.type}-down`, true);
|
||||||
|
} else {
|
||||||
|
enemy.play(`${enemy.props.type}-up`, true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (velocX > 0) {
|
||||||
|
enemy.play(`${enemy.props.type}-side`, true);
|
||||||
|
enemy.setFlipX(true);
|
||||||
|
} else {
|
||||||
|
enemy.play(`${enemy.props.type}-side`, true);
|
||||||
|
enemy.setFlipX(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we've reached the next point
|
||||||
|
const distToNextPoint = Math.sqrt(
|
||||||
|
Math.pow(enemy.x - this.gridToLocation(nextPoint.x, offsetX), 2) +
|
||||||
|
Math.pow(enemy.y - this.gridToLocation(nextPoint.y, offsetY), 2)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (distToNextPoint < segmentSpeed * 0.5) { // Threshold for reaching point
|
||||||
|
enemy.props.pathPhase++; // Move to next path segment
|
||||||
|
|
||||||
|
// If we've reached the end of the path, remove the enemy or handle accordingly
|
||||||
|
if (enemy.props.pathPhase >= path.length - 1) {
|
||||||
|
// Enemy reached destination - you might want to handle this differently
|
||||||
|
enemy.body.setVelocity(0, 0);
|
||||||
|
// Remove enemy from scene or mark for removal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// We're at the last point in the path
|
||||||
|
enemy.body.setVelocity(0, 0);
|
||||||
|
}
|
||||||
|
} else if (currentPoint && !nextPoint) {
|
||||||
|
// Last point in path - stop moving
|
||||||
|
enemy.body.setVelocity(0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle Waves and Schedules
|
||||||
|
if (this.waveTimer >= this.waveStart) {
|
||||||
|
console.log('Wave',this.wave,'Schedule',this.schedule);
|
||||||
|
// Make path synchronous
|
||||||
|
this.makePath().then(() => {
|
||||||
|
// Spawn enemies after path is ready
|
||||||
|
this.spawnSchedule();
|
||||||
|
|
||||||
|
if (WAVE_CONFIG[this.level][this.wave].hasOwnProperty(this.schedule + 1)) {
|
||||||
|
this.waveScheduleStartTime();
|
||||||
|
} else if (WAVE_CONFIG[this.level].hasOwnProperty(this.wave+1)) {
|
||||||
|
this.nextWave();
|
||||||
|
} else {
|
||||||
|
console.log('LEVEL COMPLETE');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
spawnSchedule() {
|
||||||
|
if (this.scheduleInfo.hasOwnProperty('basic1')) {
|
||||||
|
console.log('Spawn',this.scheduleInfo.basic1,'Basic1 enemies');
|
||||||
|
for (let e = 0; e < this.scheduleInfo.basic1; e++) {
|
||||||
|
const enemy = new Enemies(this.scene, 'basic1', this.spawnX, this.spawnY, this.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
makePath() {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
this.easyStar = new EasyStar.js();
|
||||||
|
|
||||||
|
// Set up the grid for pathfinding
|
||||||
|
const width = this.scene.levelMap.width;
|
||||||
|
const height = this.scene.levelMap.height;
|
||||||
|
const grid = [];
|
||||||
|
|
||||||
|
// Create a grid based on collision data
|
||||||
|
for (let y = 0; y < height; y++) {
|
||||||
|
grid[y] = [];
|
||||||
|
for (let x = 0; x < width; x++) {
|
||||||
|
// Check if the tile at this position is colliding
|
||||||
|
const tile = this.scene.mainLayer.getTileAt(x, y);
|
||||||
|
if (tile && tile.properties.collides) {
|
||||||
|
grid[y][x] = 1; // Blocked
|
||||||
|
} else {
|
||||||
|
grid[y][x] = 0; // Free space
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.easyStar.setGrid(grid);
|
||||||
|
|
||||||
|
this.easyStar.setAcceptableTiles([0]); // Only allow movement on tiles with value 0
|
||||||
|
|
||||||
|
this.easyStar.findPath(
|
||||||
|
this.spawnX,
|
||||||
|
this.spawnY,
|
||||||
|
this.endX,
|
||||||
|
this.endY,
|
||||||
|
(path) => {
|
||||||
|
if (path === null) {
|
||||||
|
console.log("No path found");
|
||||||
|
this.path = null;
|
||||||
|
} else {
|
||||||
|
this.path = path;
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.easyStar.calculate();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
python -m http.server 8000
|
||||||
Loading…
Reference in New Issue