diff --git a/main.js b/main.js index 62d11a4..99572d7 100644 --- a/main.js +++ b/main.js @@ -32,6 +32,8 @@ import FarmingTestScene from './scenes/testScenes/FarmingTestScene.js'; import SkillTestScene from './scenes/testScenes/SkillTestScene.js'; // 导入攻击技能测试场景 import AttackSkillTestScene from './scenes/testScenes/AttackSkillTestScene.js'; +// 导入迷雾测试场景 +import FogOfWarTestScene from './scenes/testScenes/FogOfWarTestScene.js'; // 导入菜单场景 import MenuScene from './scenes/MenuScene.js'; @@ -60,7 +62,8 @@ const config = { MapEditTestScene, FarmingTestScene, SkillTestScene, - AttackSkillTestScene + AttackSkillTestScene, + FogOfWarTestScene ] }; diff --git a/scenes/BootScene.js b/scenes/BootScene.js index 2f87d88..0d9f108 100644 --- a/scenes/BootScene.js +++ b/scenes/BootScene.js @@ -17,6 +17,7 @@ export default class BootScene extends Phaser.Scene { // 测试场景列表 const testScenes = [ + { key: 'FogOfWarTestScene', name: '测试场景17:迷雾系统测试' }, { key: 'AttackSkillTestScene', name: '测试场景16:攻击技能测试' }, { key: 'SkillTestScene', name: '测试场景15:技能移动效果测试' }, { key: 'FarmingTestScene', name: '测试场景14:种田功能测试' }, diff --git a/scenes/MenuScene.js b/scenes/MenuScene.js index 7ff6a26..ffa37cb 100644 --- a/scenes/MenuScene.js +++ b/scenes/MenuScene.js @@ -417,6 +417,13 @@ export default class MenuScene extends Phaser.Scene { // 添加开始按钮点击事件 startClickArea.on('pointerdown', () => { console.log('Start game clicked'); + + // 清除所有与涟漪相关的延迟调用,避免场景切换后仍然执行 + this.rippleTimeouts.forEach(timeoutId => { + clearTimeout(timeoutId); + }); + this.rippleTimeouts = []; + // 添加进入场景前的过渡动画 this.cameras.main.fadeOut(500, 0, 0, 0); this.cameras.main.once('camerafadeoutcomplete', () => { @@ -582,7 +589,10 @@ export default class MenuScene extends Phaser.Scene { for (let i = 0; i < rippleCount; i++) { // 随机延迟开始 const timeoutId = setTimeout(() => { - this.createSingleRipple(screenWidth, screenHeight); + // 确保场景仍然存在且 rippleGroup 存在 + if (this.scene && this.rippleGroup) { + this.createSingleRipple(screenWidth, screenHeight); + } }, Phaser.Math.Between(0, 3000)); // 将timeout ID保存到数组中 @@ -609,7 +619,7 @@ export default class MenuScene extends Phaser.Scene { // 延迟创建后续的涟漪层 const timeoutId = setTimeout(() => { // 只有在雨天效果开启时,才创建新的涟漪 - if (this.isRainEnabled) { + if (this.isRainEnabled && this.rippleGroup) { // 创建涟漪圆圈(只保留边框) const ripple = this.add.circle( x, @@ -662,4 +672,20 @@ export default class MenuScene extends Phaser.Scene { this.rippleTimeouts.push(timeoutId); } } + + /** + * 场景关闭时的清理工作 + */ + shutdown() { + // 清除所有与涟漪相关的延迟调用 + this.rippleTimeouts.forEach(timeoutId => { + clearTimeout(timeoutId); + }); + + // 清空timeout数组 + this.rippleTimeouts = []; + + // 调用父类的shutdown方法 + super.shutdown(); + } } \ No newline at end of file diff --git a/scenes/testScenes/FogOfWarTestScene.js b/scenes/testScenes/FogOfWarTestScene.js new file mode 100644 index 0000000..c8b4057 --- /dev/null +++ b/scenes/testScenes/FogOfWarTestScene.js @@ -0,0 +1,253 @@ +// 测试场景:迷雾测试 +import Unit from '../../objects/Unit.js'; +import { InputSystem } from '../../systems/InputSystem.js'; +import { SelectionSystem } from '../../systems/SelectionSystem.js'; +import { CommandSystem } from '../../systems/CommandSystem.js'; + +export default class FogOfWarTestScene extends Phaser.Scene { + constructor() { + super({ key: 'FogOfWarTestScene' }); + } + + create() { + // 创建玩家纹理 + if (!this.textures.exists('player')) { + const canvas = document.createElement('canvas'); + canvas.width = 32; + canvas.height = 32; + const ctx = canvas.getContext('2d'); + ctx.fillStyle = '#000000ff'; + ctx.beginPath(); + ctx.arc(16, 16, 14, 0, Math.PI * 2); + ctx.fill(); + this.textures.addCanvas('player', canvas); + } + + // 创建敌人纹理 + if (!this.textures.exists('enemy')) { + const canvas = document.createElement('canvas'); + canvas.width = 32; + canvas.height = 32; + const ctx = canvas.getContext('2d'); + ctx.fillStyle = '#ff0000ff'; + ctx.beginPath(); + ctx.arc(16, 16, 14, 0, Math.PI * 2); + ctx.fill(); + this.textures.addCanvas('enemy', canvas); + } + + // 物理世界和相机边界 + const worldSize = 2000; + this.physics.world.setBounds(-worldSize, -worldSize, worldSize * 2, worldSize * 2); + this.cameras.main.setBounds(-worldSize, -worldSize, worldSize * 2, worldSize * 2); + this.cameras.main.centerOn(0, 0); + + // 初始化系统 - 开启鼠标移动和框选 + this.inputSystem = new InputSystem(this, { + keyboard: true, + mouse: true, + joystick: false, + selection: true + }); + this.selectionSystem = new SelectionSystem(this); + this.commandSystem = new CommandSystem(this); + + // 创建单位组 + this.unitGroup = this.add.group(); + + // 创建玩家单位 + this.playerUnit = new Unit(this, 0, 0, 'player'); + this.playerUnit.setDepth(2); // 确保玩家在迷雾之上 + this.unitGroup.add(this.playerUnit); + + // 自动选中玩家单位 + this.events.emit('select-unit', this.playerUnit); + + // 创建多个敌人单位,分布在不同位置 + for (let i = 0; i < 10; i++) { + const x = Math.random() * 1600 - 800; + const y = Math.random() * 1600 - 800; + const enemyUnit = new Unit(this, x, y, 'enemy'); + enemyUnit.setDepth(2); // 确保敌人在迷雾之上 + this.unitGroup.add(enemyUnit); + } + + // 添加碰撞检测 + this.physics.add.collider(this.unitGroup, this.unitGroup); + + // 创建迷雾系统 + this.createFogOfWar(worldSize); + + // 添加场景说明文本 + const titleText = this.add.text(10, 10, '测试场景:迷雾测试', { + fontSize: '20px', + fill: '#ffffff' + }); + titleText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动 + titleText.setDepth(10); // 设置深度,确保显示在游戏元素上方 + + const fogText = this.add.text(10, 40, '玩家周围会显示视野,迷雾会随玩家移动而变化', { + fontSize: '16px', + fill: '#ffffff' + }); + fogText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动 + fogText.setDepth(10); // 设置深度,确保显示在游戏元素上方 + + const controlsText = this.add.text(10, 70, 'WASD或方向键移动视角,鼠标右键移动单位', { + fontSize: '16px', + fill: '#ffffff' + }); + controlsText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动 + controlsText.setDepth(10); // 设置深度,确保显示在游戏元素上方 + + const spaceText = this.add.text(10, 100, '按空格键返回菜单', { + fontSize: '16px', + fill: '#ffffff' + }); + spaceText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动 + spaceText.setDepth(10); // 设置深度,确保显示在游戏元素上方 + + // 空格键切换场景 + this.input.keyboard.on('keydown-SPACE', () => { + this.scene.start('BootScene'); + }); + + // 添加返回菜单按钮 + this.createReturnButton(); + + // 添加相机移动控制 + this.cameraSpeed = 2; + this.cameraKeys = this.input.keyboard.addKeys({ + up: Phaser.Input.Keyboard.KeyCodes.W, + down: Phaser.Input.Keyboard.KeyCodes.S, + left: Phaser.Input.Keyboard.KeyCodes.A, + right: Phaser.Input.Keyboard.KeyCodes.D + }); + } + + /** + * 创建返回菜单按钮 + */ + createReturnButton() { + // 创建按钮背景 + const button = this.add.rectangle(10, 130, 150, 40, 0x333333); + button.setOrigin(0, 0); + button.setScrollFactor(0, 0); + button.setDepth(10); + button.setInteractive({ useHandCursor: true }); + + // 按钮文本 + const buttonText = this.add.text(25, 140, '返回菜单', { + fontSize: '16px', + fill: '#ffffff', + fontFamily: 'Arial' + }); + buttonText.setScrollFactor(0, 0); + buttonText.setDepth(11); + + // 按钮事件 + button.on('pointerdown', () => { + this.scene.start('BootScene'); + }); + + button.on('pointerover', () => { + button.setFillStyle(0x555555); + }); + + button.on('pointerout', () => { + button.setFillStyle(0x333333); + }); + } + + /** + * 创建迷雾系统 + * @param {number} worldSize - 世界大小 + */ + createFogOfWar(worldSize) { + // 创建迷雾纹理 + if (!this.textures.exists('fogTexture')) { + const canvas = document.createElement('canvas'); + canvas.width = 64; + canvas.height = 64; + const ctx = canvas.getContext('2d'); + + // 创建渐变效果 + const gradient = ctx.createRadialGradient(32, 32, 0, 32, 32, 32); + gradient.addColorStop(0, 'rgba(0, 0, 0, 0)'); // 中心透明 + gradient.addColorStop(0.7, 'rgba(0, 0, 0, 0.8)'); // 边缘不透明 + + ctx.fillStyle = gradient; + ctx.fillRect(0, 0, 64, 64); + + this.textures.addCanvas('fogTexture', canvas); + } + + // 创建迷雾图层 + this.fogLayer = this.add.graphics(); + this.fogLayer.setDepth(1); // 迷雾在单位之下 + + // 视野范围 + this.viewRange = 300; + + // 更新迷雾 + this.updateFogOfWar(); + } + + /** + * 更新迷雾 + */ + updateFogOfWar() { + if (!this.playerUnit) return; + + // 清除当前迷雾 + this.fogLayer.clear(); + + // 获取玩家位置 + const playerX = this.playerUnit.x; + const playerY = this.playerUnit.y; + + // 创建迷雾遮罩 + this.fogLayer.fillStyle(0x000000, 0.8); + this.fogLayer.fillRect(-2000, -2000, 4000, 4000); + + // 创建视野区域(圆形) + this.fogLayer.fillStyle(0x000000, 0); + this.fogLayer.setBlendMode(Phaser.BlendModes.ERASE); + + // 绘制视野圆 + this.fogLayer.beginPath(); + this.fogLayer.arc(playerX, playerY, this.viewRange, 0, Math.PI * 2); + this.fogLayer.fillPath(); + + // 重置混合模式 + this.fogLayer.setBlendMode(Phaser.BlendModes.NORMAL); + } + + update() { + this.inputSystem.update(); + + // 更新相机移动 + if (this.cameraKeys.left.isDown) { + this.cameras.main.scrollX -= this.cameraSpeed; + } + if (this.cameraKeys.right.isDown) { + this.cameras.main.scrollX += this.cameraSpeed; + } + if (this.cameraKeys.up.isDown) { + this.cameras.main.scrollY -= this.cameraSpeed; + } + if (this.cameraKeys.down.isDown) { + this.cameras.main.scrollY += this.cameraSpeed; + } + + this.unitGroup.getChildren().forEach(unit => { + if (unit.update) { + unit.update(); + } + unit.updateMovement(); + }); + + // 更新迷雾 + this.updateFogOfWar(); + } +} \ No newline at end of file