// 游戏场景文件:这是游戏的主要场景,包含了游戏的核心逻辑和玩法实现 // 导入单位类:用于创建游戏中的角色/单位 import Unit from '../../objects/Unit.js'; // 导入输入系统:处理玩家的键盘和鼠标输入 import { InputSystem } from '../../systems/InputSystem.js'; // 导入选择系统:处理单位的选择逻辑 import { SelectionSystem } from '../../systems/SelectionSystem.js'; // 导入命令系统:处理玩家对单位的命令(如移动、攻击等) import { CommandSystem } from '../../systems/CommandSystem.js'; // 导入虚拟摇杆:用于移动设备上的操作控制 import { VirtualJoystick } from '../../ui/VirtualJoystick.js'; // 导入小地图:显示游戏世界的缩略图 import { MiniMap } from '../../ui/MiniMap.js'; // 定义SkillTestScene类,继承自Phaser.Scene(Phaser框架的场景类) export default class AttackSkillTestScene extends Phaser.Scene { // 构造函数:初始化场景,设置场景的唯一键值 constructor() { super({ key: 'AttackSkillTestScene' }); // 角色相关变量 this.characters = []; this.currentlyControlled = null; // 当前控制的角色 this.isControllingPuppet = false; // 是否正在控制傀儡 this.puppet = null; // 元歌的傀儡 // 技能相关变量 this.skills = {}; // 技能预览相关变量 this.skillPreview = null; } // create方法:当场景创建时自动调用,用于初始化游戏元素 create() { // === 0. 添加窗口大小变化监听 === // 监听浏览器窗口大小变化事件,当窗口大小改变时调用resizeGame方法 window.addEventListener('resize', this.resizeGame.bind(this)); // === 1. 创建玩家纹理=== // 检查是否已经存在名为'player'的纹理,如果不存在则创建 if (!this.textures.exists('player')) { // 创建一个32x32像素的HTML5 Canvas元素 const canvas = document.createElement('canvas'); canvas.width = 32; // 设置Canvas宽度 canvas.height = 32; // 设置Canvas高度 // 获取Canvas的2D绘图上下文 const ctx = canvas.getContext('2d'); ctx.fillStyle = '#000000ff'; // 设置填充颜色为完全不透明的黑色 ctx.beginPath(); // 开始绘制路径 // 绘制一个圆形:圆心(16,16),半径14,从0到2π(完整圆形) ctx.arc(16, 16, 14, 0, Math.PI * 2); ctx.fill(); // 填充圆形 // 将创建的Canvas添加到Phaser的纹理管理器中,命名为'player' this.textures.addCanvas('player', canvas); } // 创建飞镖纹理 if (!this.textures.exists('dart')) { const canvas = document.createElement('canvas'); canvas.width = 16; canvas.height = 16; const ctx = canvas.getContext('2d'); ctx.fillStyle = '#ff6600'; ctx.beginPath(); ctx.moveTo(8, 0); ctx.lineTo(16, 8); ctx.lineTo(8, 16); ctx.lineTo(0, 8); ctx.closePath(); ctx.fill(); this.textures.addCanvas('dart', canvas); } // === 2. 物理世界和相机边界=== // 定义游戏世界的大小 const worldSize = 2000; // 设置物理世界的边界:从(-worldSize,-worldSize)到(worldSize*2, worldSize*2) // 这样游戏世界的中心就是(0,0),范围是从-2000到2000,总共4000x4000大小 this.physics.world.setBounds(-worldSize, -worldSize, worldSize * 2, worldSize * 2); // 设置相机的边界,与物理世界边界相同 this.cameras.main.setBounds(-worldSize, -worldSize, worldSize * 2, worldSize * 2); // 设置相机起始位置在地图中心 // 使用centerOn方法将相机中心定位在地图坐标(0, 0) this.cameras.main.centerOn(0, 0); // 地图中心坐标是(0, 0) // 添加地图网格 this.createGrid(); // === 3. 初始化系统 === // 创建输入系统实例,禁用鼠标点击移动和框选功能 this.inputSystem = new InputSystem(this, { mouse: false, // 禁用鼠标点击移动 selection: false // 禁用框选功能 }); // 创建选择系统实例 this.selectionSystem = new SelectionSystem(this); // 创建命令系统实例 this.commandSystem = new CommandSystem(this); // 禁用点击移动和框选功能 // 阻止选择单位事件触发移动 this.events.on('select-unit', (unit) => { // 只允许单位选择,不执行任何移动操作 console.log('单位已选中:', unit.name); }); // 阻止移动到指定点事件 this.events.on('move-to-point', (x, y) => { // 不执行任何移动操作 return false; }); // 阻止选择开始事件 this.events.on('selection-start', (pos) => { // 不执行框选操作 return false; }); // 阻止选择更新事件 this.events.on('selection-update', (rect) => { // 不更新框选区域 return false; }); // 阻止选择结束事件 this.events.on('selection-end', (rect) => { // 不执行框选结束操作 return false; }); // 创建虚拟摇杆实例 this.virtualJoystick = new VirtualJoystick(this); // === 4. 创建角色 === // 创建一个单位组,用于管理游戏中的所有角色 this.unitGroup = this.add.group(); // 创建元歌 const yuange = new Unit(this, -500, 0, 'player'); yuange.name = 'yuange'; yuange.type = 'yuange'; this.unitGroup.add(yuange); this.characters.push(yuange); // 创建貂蝉 const diaochan = new Unit(this, 500, 0, 'player'); diaochan.name = 'diaochan'; diaochan.type = 'diaochan'; this.unitGroup.add(diaochan); this.characters.push(diaochan); // 默认控制元歌 this.currentlyControlled = yuange; yuange.setSelected(true); // 将默认角色添加到ControlMode的选中列表中,以便虚拟摇杆可以控制 import('../../systems/ControlMode.js').then(mod => { mod.clearSelection(); mod.addToSelection(yuange); mod.setActiveUnit(yuange); // 默认锁定相机到元歌 mod.setCameraLocked(true); }); // === 5. 添加角色之间的碰撞检测 === // 为单位组添加碰撞检测,使得角色之间可以相互碰撞 this.physics.add.collider(this.unitGroup, this.unitGroup); // === 6. 创建技能轮盘 === // 创建技能轮盘实例 this.skillWheel = { isActive: false, base: null, knob: null, activePointer: null, centerX: 0, centerY: 0, radius: 50, enabled: true, activeSkill: null, skillButtons: [], skillInfo: { 1: { key: 'Q', color: 0x00ff00 }, 2: { key: 'W', color: 0x00ffff }, 3: { key: 'E', color: 0xff00ff }, 4: { key: 'R', color: 0xffff00 } } }; // 创建技能轮盘UI this.createSkillWheel(); // 绑定技能轮盘事件 this.bindSkillWheelEvents(); // === 7. 添加角色切换按键 === const kb = this.input.keyboard; this.switchKey = kb.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE); kb.on('keydown-SPACE', () => this.switchCharacter()); // === 7. 添加技能UI提示 === this.createSkillUI(); // === 6. 创建小地图 === // 创建小地图实例,设置其位置和大小 this.miniMap = new MiniMap(this, { worldSize: worldSize, // 游戏世界大小 mapSize: 200, // 小地图显示大小 x: this.sys.game.config.width - 200 - 10, // 小地图在屏幕上的x坐标 y: 10 // 小地图在屏幕上的y坐标 }); } // 创建地图网格 createGrid() { // 创建一个图形对象用于绘制网格 const graphics = this.add.graphics(); // 设置网格线样式 graphics.lineStyle(1, 0x555555, 0.3); // 定义网格参数 const gridSize = 50; // 网格大小 const worldSize = 2000; // 世界大小,与物理世界边界一致 // 绘制垂直线 for (let x = -worldSize; x <= worldSize; x += gridSize) { graphics.moveTo(x, -worldSize); graphics.lineTo(x, worldSize); } // 绘制水平线 for (let y = -worldSize; y <= worldSize; y += gridSize) { graphics.moveTo(-worldSize, y); graphics.lineTo(worldSize, y); } // 绘制完成 graphics.strokePath(); // 确保网格在所有游戏对象之下 graphics.setDepth(-10); // 让网格随相机移动 graphics.setScrollFactor(1); } // update方法:每帧自动调用(约60次/秒),用于更新游戏状态 update() { // 更新输入系统,处理玩家的键盘和鼠标输入 this.inputSystem.update(); // 更新所有单位的移动逻辑和选中状态 // 获取单位组中的所有子元素(即所有单位) this.unitGroup.getChildren().forEach(unit => { // 如果单位有自己的update方法,则调用它 if (unit.update) { unit.update(); } // 调用单位的updateMovement方法,更新单位的移动状态 unit.updateMovement(); }); // 更新小地图显示 if (this.miniMap) { this.miniMap.update(); } // 更新虚拟摇杆状态 if (this.virtualJoystick) { this.virtualJoystick.update(); } // 相机锁定逻辑:如果有锁定状态且有当前控制的角色,则让相机跟随角色 import('../../systems/ControlMode.js').then(mod => { if (mod.cameraLocked && this.currentlyControlled) { this.cameras.main.startFollow(this.currentlyControlled); } else { this.cameras.main.stopFollow(); } }); } /** * 创建技能UI提示 */ createSkillUI() { // 创建技能提示文本 this.skillInfoText = this.add.text(10, this.sys.game.config.height - 100, '技能施法: 点击右侧技能轮盘拖放\n按空格键切换角色', { fontSize: '16px', fill: '#ffffff', fontFamily: 'Arial' }) .setScrollFactor(0) // 固定UI,不随滚动移动 .setDepth(100); // 创建当前控制角色提示 this.controlInfoText = this.add.text(10, this.sys.game.config.height - 60, `当前控制: ${this.currentlyControlled.name === 'yuange' ? '元歌' : '貂蝉'}`, { fontSize: '18px', fill: '#00ff00', fontFamily: 'Arial' }) .setScrollFactor(0) .setDepth(100); } /** * 创建技能轮盘 */ createSkillWheel() { // 技能轮盘基础位置(右下角) this.skillWheel.centerX = this.scale.width - 100; this.skillWheel.centerY = this.scale.height - 100; // 创建技能轮盘基础(默认隐藏,选中技能时显示) this.skillWheel.base = this.add.circle(this.skillWheel.centerX, this.skillWheel.centerY, this.skillWheel.radius, 0x444444, 0.5); this.skillWheel.base.setScrollFactor(0); // 不随相机移动 this.skillWheel.base.setDepth(100); this.skillWheel.base.setVisible(false); // 创建技能按钮(扇形排列:技能1-4在右下角左上方,大半径扇形分布) const skills = [1, 2, 3, 4]; const startAngle = -Math.PI; // 起始角度(-90度,左) const endAngle = -Math.PI / 2; // 结束角度(0度,上) const angleRange = endAngle - startAngle; const angleStep = angleRange / (skills.length - 1); this.skillWheel.skillButtons = []; for (let i = 0; i < skills.length; i++) { const skillIndex = skills[i]; // 计算技能按钮的角度(扇形排列) const angle = startAngle + i * angleStep; const distance = 120; // 增大技能按钮距离中心的距离 const x = this.skillWheel.centerX + Math.cos(angle) * distance; const y = this.skillWheel.centerY + Math.sin(angle) * distance; // 创建技能按钮 const button = this.add.circle(x, y, 25, this.skillWheel.skillInfo[skillIndex].color, 0.6); button.setScrollFactor(0); button.setDepth(100); // 添加技能按键文本(使用数字1-4代替Q-W-E-R) const text = this.add.text(x, y, skillIndex.toString(), { fontSize: '14px', fill: '#000000', fontFamily: 'Arial', fontWeight: 'bold' }) .setOrigin(0.5, 0.5) .setScrollFactor(0) .setDepth(101); // 保存技能信息 button.skillIndex = skillIndex; button.skillKey = this.skillWheel.skillInfo[skillIndex].key; button.x = x; button.y = y; // 添加点击事件 button.setInteractive({ useHandCursor: true }); button.on('pointerdown', (pointer) => { // 选中技能后,将轮盘中心设置为技能按钮的中心位置 this.skillWheel.centerX = x; this.skillWheel.centerY = y; this.skillWheel.base.setPosition(x, y); this.skillWheel.base.setVisible(true); this.skillWheel.activeSkill = skillIndex; this.startSkillCast(pointer); }); this.skillWheel.skillButtons.push(button); } // 创建轮盘旋钮 this.skillWheel.knob = this.add.circle(this.skillWheel.centerX, this.skillWheel.centerY, this.skillWheel.radius * 0.5, 0xff0000, 0.8); this.skillWheel.knob.setScrollFactor(0); this.skillWheel.knob.setDepth(101); this.skillWheel.knob.setVisible(false); } /** * 绑定技能轮盘事件 */ bindSkillWheelEvents() { // 鼠标移动事件 this.input.on('pointermove', (ptr) => { // 只有在技能轮盘激活时才处理 if (!this.skillWheel.isActive || this.skillWheel.activePointer !== ptr) { return; } // 更新旋钮位置(与移动轮盘相同的逻辑) const dx = ptr.x - this.skillWheel.centerX; const dy = ptr.y - this.skillWheel.centerY; const distance = Math.sqrt(dx * dx + dy * dy); if (distance > this.skillWheel.radius) { // 限制在圆内 const angle = Math.atan2(dy, dx); const knobX = this.skillWheel.centerX + Math.cos(angle) * this.skillWheel.radius; const knobY = this.skillWheel.centerY + Math.sin(angle) * this.skillWheel.radius; this.skillWheel.knob.setPosition(knobX, knobY); } else { this.skillWheel.knob.setPosition(ptr.x, ptr.y); } // 技能释放预览:当技能轮盘激活时,显示技能释放预览 this.updateSkillPreview(); }); // 鼠标释放事件 this.input.on('pointerup', (ptr) => { // 只有在技能轮盘激活时才处理 if (!this.skillWheel.isActive || this.skillWheel.activePointer !== ptr) { return; } // 完成技能释放 this.finishSkillCast(); // 清除技能预览 this.clearSkillPreview(); }); } /** * 开始技能施法 */ startSkillCast(pointer) { // 设置技能轮盘状态 this.skillWheel.isActive = true; this.skillWheel.activePointer = pointer; // 显示旋钮和轮盘基础 this.skillWheel.knob.setVisible(true); this.skillWheel.base.setVisible(true); // 设置旋钮初始位置为轮盘中心 this.skillWheel.knob.setPosition(this.skillWheel.centerX, this.skillWheel.centerY); // 根据技能索引改变旋钮颜色 if (this.skillWheel.activeSkill && this.skillWheel.skillInfo[this.skillWheel.activeSkill]) { this.skillWheel.knob.fillColor = this.skillWheel.skillInfo[this.skillWheel.activeSkill].color; } // 初始化技能预览 this.updateSkillPreview(); } /** * 完成技能施法 */ finishSkillCast() { // 检查是否有激活的技能 if (!this.skillWheel.activeSkill) { return; } // 释放技能 this.castSkill(this.skillWheel.activeSkill); // 重置技能轮盘状态 this.skillWheel.isActive = false; this.skillWheel.pointer = null; this.skillWheel.activeSkill = null; // 隐藏旋钮和轮盘基础 this.skillWheel.knob.setVisible(false); this.skillWheel.base.setVisible(false); // 将旋钮和基础重置到默认位置(技能按钮扇形区域的中心) this.skillWheel.centerX = this.scale.width - 200; this.skillWheel.centerY = this.scale.height - 200; this.skillWheel.base.setPosition(this.skillWheel.centerX, this.skillWheel.centerY); this.skillWheel.knob.setPosition(this.skillWheel.centerX, this.skillWheel.centerY); } /** * 更新技能轮盘显示 */ updateSkillWheelDisplay() { // 根据当前控制的角色设置技能信息 if (this.isControllingPuppet) { // 傀儡的技能设置 this.skillWheel.skillInfo = { 1: { key: 'Q', color: 0xff0000 }, // 收回傀儡 2: { key: 'W', color: 0x00ffff }, // 交换位置 3: { key: 'E', color: 0x0000ff }, // 占位 4: { key: 'R', color: 0xffff00 } // 快速移动 }; } else if (this.currentlyControlled && this.currentlyControlled.type === 'yuange') { // 元歌本体的技能设置 this.skillWheel.skillInfo = { 1: { key: 'Q', color: 0x00ff00 }, // 扔出傀儡 2: { key: 'W', color: 0x00ffff }, // 二技能:飞镖攻击 3: { key: 'E', color: 0xff00ff }, // 三技能:十字斩 4: { key: 'R', color: 0xffff00 } // 本体移动 }; } else if (this.currentlyControlled && this.currentlyControlled.type === 'diaochan') { // 貂蝉的技能设置 this.skillWheel.skillInfo = { 1: { key: 'Q', color: 0x00ff00 }, // 占位 2: { key: 'W', color: 0x00ffff }, // 移动 3: { key: 'E', color: 0xff00ff }, // 占位 4: { key: 'R', color: 0xffff00 } // 占位 }; } // 更新现有技能按钮的颜色,不重新创建 this.skillWheel.skillButtons.forEach(button => { const skillIndex = button.skillIndex; if (this.skillWheel.skillInfo[skillIndex]) { button.fillColor = this.skillWheel.skillInfo[skillIndex].color; } }); } /** * 切换控制的角色 */ switchCharacter() { if (this.isControllingPuppet) { // 如果正在控制傀儡,先切换回本体 this.switchToYuange(); } else { // 否则切换到另一个角色 const currentIndex = this.characters.indexOf(this.currentlyControlled); const nextIndex = (currentIndex + 1) % this.characters.length; // 取消当前角色的选中状态 this.currentlyControlled.setSelected(false); // 切换到下一个角色 this.currentlyControlled = this.characters[nextIndex]; this.currentlyControlled.setSelected(true); // 更新UI提示 this.controlInfoText.setText(`当前控制: ${this.currentlyControlled.name === 'yuange' ? '元歌' : '貂蝉'}`); // 更新技能轮盘显示 this.updateSkillWheelDisplay(); // 更新ControlMode的选中列表 import('../../systems/ControlMode.js').then(mod => { mod.clearSelection(); mod.addToSelection(this.currentlyControlled); mod.setActiveUnit(this.currentlyControlled); // 自动锁定相机到新角色 mod.setCameraLocked(true); }); } } /** * 释放技能 * @param {number} skillIndex 技能索引(1-4) */ castSkill(skillIndex) { // 检查是否有当前控制的角色 if (!this.currentlyControlled) return; // 根据当前控制角色类型和技能索引释放对应技能 if (this.currentlyControlled.type === 'yuange' || this.currentlyControlled.type === 'yuange-puppet') { this.castYuangeSkill(skillIndex); } else if (this.currentlyControlled.type === 'diaochan') { this.castDiaochanSkill(skillIndex); } } /** * 释放元歌技能 * @param {number} skillIndex 技能索引(1-4) */ castYuangeSkill(skillIndex) { const yuange = this.characters.find(char => char.name === 'yuange'); // 计算技能释放方向(基于技能轮盘中心到指针的相对方向) const pointer = this.input.activePointer; // 计算轮盘中心到指针的方向向量 const directionX = pointer.x - this.skillWheel.centerX; const directionY = pointer.y - this.skillWheel.centerY; if (this.isControllingPuppet) { // 控制傀儡时的技能 switch (skillIndex) { case 1: // 技能1:收回傀儡 this.switchToYuange(); break; case 2: // 技能2:本体拉近并交换位置 if (this.puppet) { // 检查是否处于3技能第二次按下后的控制期间 const isInSkill3Control = this.puppetSkill3State && this.puppetSkill3State.hasSecondCast; if (isInSkill3Control) { // 处于3技能控制期间:只执行本体拉近,不执行位置互换,然后切换到本体控制 const puppetX = this.puppet.x; const puppetY = this.puppet.y; // 计算本体与傀儡之间的距离 const dx = yuange.x - puppetX; const dy = yuange.y - puppetY; // 计算归一化方向向量 const distance = Math.sqrt(dx * dx + dy * dy); let normalizedX = 0; let normalizedY = 0; if (distance > 0) { normalizedX = dx / distance; normalizedY = dy / distance; } // 计算拉近后的位置(在本体到傀儡的直线上,距离傀儡fixedDistance的位置) const fixedDistance = 200; const closeX = puppetX + normalizedX * fixedDistance; const closeY = puppetY + normalizedY * fixedDistance; // 本体移动到拉近后的位置 yuange.setPosition(closeX, closeY); yuange.setVelocity(0, 0); // 切换到本体控制(保持傀儡存在) this.switchToYuange(false); } else { // 正常情况:执行完整的拉近和交换位置逻辑 // 记录傀儡当前位置 const puppetX = this.puppet.x; const puppetY = this.puppet.y; // 计算本体与傀儡之间的距离 const dx = yuange.x - puppetX; const dy = yuange.y - puppetY; const distance = Math.sqrt(dx * dx + dy * dy); // 固定拉近距离阈值 const fixedDistance = 200; if (distance <= fixedDistance) { // 如果距离小于等于阈值,直接交换位置 // 记录本体当前位置 const yuangeX = yuange.x; const yuangeY = yuange.y; // 傀儡移动到本体位置 this.puppet.setPosition(yuangeX, yuangeY); this.puppet.setVelocity(0, 0); this.puppet.clearTarget(); // 本体移动到傀儡原本位置 yuange.setPosition(puppetX, puppetY); yuange.setVelocity(0, 0); } else { // 如果距离大于阈值,执行拉近逻辑 // 计算归一化方向向量 const normalizedX = dx / distance; const normalizedY = dy / distance; // 计算拉近后的位置(在本体到傀儡的直线上,距离傀儡fixedDistance的位置) const closeX = puppetX + normalizedX * fixedDistance; const closeY = puppetY + normalizedY * fixedDistance; // 傀儡移动到拉近后的位置 this.puppet.setPosition(closeX, closeY); this.puppet.setVelocity(0, 0); this.puppet.clearTarget(); // 直接设置本体位置到傀儡原本位置 yuange.setPosition(puppetX, puppetY); yuange.setVelocity(0, 0); } } } break; case 3: // 技能3:控制特效 // 检查是否已经初始化技能3的状态 if (!this.puppetSkill3State) { this.puppetSkill3State = { hasFirstCast: false, hasSecondCast: false, firstCastTime: 0, secondCastTime: 0 }; } if (!this.puppetSkill3State.hasFirstCast) { // 第一次按下:减速周围敌人 this.castPuppetSkill3First(); } else if (!this.puppetSkill3State.hasSecondCast && (Date.now() - this.puppetSkill3State.firstCastTime) < 5000) { // 5秒内第二次按下 // 第二次按下:眩晕周围敌人,傀儡不可移动,一段时间后销毁 this.castPuppetSkill3Second(); } break; case 4: // 技能4:傀儡快速移动并拉本体 if (this.puppet && this.isControllingPuppet) { // 使用轮盘相对方向计算目标位置 const maxDistance = 200; const distance = Math.sqrt(directionX * directionX + directionY * directionY); // 计算归一化方向向量 let normalizedX = 0; let normalizedY = 0; if (distance > 0) { normalizedX = directionX / distance; normalizedY = directionY / distance; } // 计算目标位置:傀儡当前位置 + 归一化方向向量 * 最大距离 const targetX = this.puppet.x + normalizedX * maxDistance; const targetY = this.puppet.y + normalizedY * maxDistance; // 记录傀儡的初始位置 const initialPuppetX = this.puppet.x; const initialPuppetY = this.puppet.y; // 傀儡移动到目标位置 this.puppet.setPosition(targetX, targetY); this.puppet.setVelocity(0, 0); this.puppet.clearTarget(); // 计算傀儡位移的方向向量 const puppetDisplacementX = targetX - initialPuppetX; const puppetDisplacementY = targetY - initialPuppetY; const puppetDisplacementDistance = Math.sqrt(puppetDisplacementX * puppetDisplacementX + puppetDisplacementY * puppetDisplacementY); // 计算本体应该移动的位置(在傀儡位移的直线上,距离傀儡一定距离) if (puppetDisplacementDistance > 0) { // 本体与傀儡之间的固定距离 const bodyDistance = 150; // 计算本体移动的方向向量(与傀儡位移相同的方向) const bodyMoveX = (puppetDisplacementX / puppetDisplacementDistance) * bodyDistance; const bodyMoveY = (puppetDisplacementY / puppetDisplacementDistance) * bodyDistance; // 本体沿着傀儡位移的直线移动 yuange.x = this.puppet.x - bodyMoveX; yuange.y = this.puppet.y - bodyMoveY; } else { // 如果傀儡没有移动,保持本体位置不变 } yuange.setVelocity(0, 0); } break; } } else { // 控制本体时的技能 switch (skillIndex) { case 1: // 技能1:扔出傀儡或收回傀儡 if (this.puppet) { // 有傀儡存在:立即收回并销毁傀儡 this.switchToYuange(); } else { // 没有傀儡:创建新傀儡 // 使用轮盘相对方向计算目标位置 const maxDistance = 200; const distance = Math.sqrt(directionX * directionX + directionY * directionY); // 计算归一化方向向量 let normalizedX = 0; let normalizedY = 0; if (distance > 0) { normalizedX = directionX / distance; normalizedY = directionY / distance; } // 计算目标位置:元歌当前位置 + 归一化方向向量 * 最大距离 const targetX = yuange.x + normalizedX * maxDistance; const targetY = yuange.y + normalizedY * maxDistance; // 创建傀儡直接在指定位置生成,不需要从本体位置开始移动 this.puppet = new Unit(this, targetX, targetY, 'player'); this.puppet.name = 'yuange-puppet'; this.puppet.type = 'yuange-puppet'; this.puppet.setScale(0.8); this.puppet.tint = 0x00ffff; // 用蓝色区分傀儡 this.unitGroup.add(this.puppet); // 切换控制到傀儡 this.isControllingPuppet = true; yuange.setSelected(false); this.currentlyControlled = this.puppet; this.controlInfoText.setText('当前控制: 元歌傀儡'); // 更新技能轮盘显示为傀儡的技能 this.updateSkillWheelDisplay(); // 更新ControlMode的选中列表,确保虚拟摇杆能控制傀儡 import('../../systems/ControlMode.js').then(mod => { mod.clearSelection(); mod.addToSelection(this.puppet); mod.setActiveUnit(this.puppet); // 自动锁定相机到傀儡 mod.setCameraLocked(true); }); } break; case 2: // 技能2:扇形飞镖攻击 this.castFanDartSkill(); break; case 3: // 技能3:十字斩 this.castCrossSlashSkill(); break; case 4: // 技能4:本体向技能指向方向移动 // 确保是本体在控制 if (!this.isControllingPuppet) { // 使用轮盘相对方向计算目标位置 const maxDistance = 200; const distance = Math.sqrt(directionX * directionX + directionY * directionY); // 计算归一化方向向量 let normalizedX = 0; let normalizedY = 0; if (distance > 0) { normalizedX = directionX / distance; normalizedY = directionY / distance; } // 计算目标位置:元歌当前位置 + 归一化方向向量 * 最大距离 const targetX = yuange.x + normalizedX * maxDistance; const targetY = yuange.y + normalizedY * maxDistance; // 短暂停顿(0.5秒) yuange.setVelocity(0, 0); // 延迟后直接传送,不需要移动过程 this.time.delayedCall(500, () => { yuange.setPosition(targetX, targetY); yuange.setVelocity(0, 0); }, [], this); } break; } } } /** * 释放扇形飞镖技能 */ castFanDartSkill() { if (!this.currentlyControlled) return; // 计算技能释放方向(基于技能轮盘中心到指针的相对方向) const pointer = this.input.activePointer; const directionX = pointer.x - this.skillWheel.centerX; const directionY = pointer.y - this.skillWheel.centerY; // 计算技能释放角度 const skillAngle = Math.atan2(directionY, directionX); // 扇形角度范围:45度(转换为弧度) const fanAngle = Phaser.Math.DegToRad(45); // 飞镖数量 const dartCount = 4; // 飞镖速度 const dartSpeed = 1000; // 飞镖飞行距离 const dartDistance = 400; // 计算每个飞镖的角度间隔 const angleStep = fanAngle / (dartCount - 1); // 计算起始角度(扇形的左边界) const startAngle = skillAngle - fanAngle / 2; // 创建飞镖 for (let i = 0; i < dartCount; i++) { // 计算当前飞镖的角度 const currentAngle = startAngle + i * angleStep; // 计算飞镖的初始位置 const offset = 0; // 偏移距离 const dartX = this.currentlyControlled.x + Math.cos(currentAngle) * offset; const dartY = this.currentlyControlled.y + Math.sin(currentAngle) * offset; // 创建飞镖精灵 const dart = this.physics.add.sprite(dartX, dartY, 'dart'); dart.setRotation(currentAngle); // 计算飞镖的速度向量 const velocityX = Math.cos(currentAngle) * dartSpeed; const velocityY = Math.sin(currentAngle) * dartSpeed; // 设置飞镖速度 dart.setVelocity(velocityX, velocityY); // 限制飞镖飞行距离 this.time.delayedCall(dartDistance / dartSpeed * 1000, () => { if (dart && dart.active) { dart.destroy(); } }); // 移除飞镖碰撞检测 } } /** * 释放十字斩技能 */ castCrossSlashSkill() { if (!this.currentlyControlled) return; // 计算技能释放方向(基于技能轮盘中心到指针的相对方向) const pointer = this.input.activePointer; const directionX = pointer.x - this.skillWheel.centerX; const directionY = pointer.y - this.skillWheel.centerY; // 计算技能释放角度 const skillAngle = Math.atan2(directionY, directionX); // 计算归一化方向向量 const distance = Math.sqrt(directionX * directionX + directionY * directionY); let normalizedX = 0; let normalizedY = 0; if (distance > 0) { normalizedX = directionX / distance; normalizedY = directionY / distance; } // 十字斩放置距离范围 const minDistance = 0; // 最小距离(角色自身位置) const maxDistance = 200; // 最大距离 // 计算轮盘按钮与中心的距离百分比(相对于轮盘半径) const distancePercentage = Math.min(distance / this.skillWheel.radius, 1); // 根据距离百分比计算十字斩实际释放距离 const crossSlashDistance = minDistance + (maxDistance - minDistance) * distancePercentage; // 本体反向位移距离(固定值) const bodyDisplacementDistance = 100; // 计算十字斩位置 const crossSlashX = this.currentlyControlled.x + normalizedX * crossSlashDistance; const crossSlashY = this.currentlyControlled.y + normalizedY * crossSlashDistance; // 创建十字斩效果 this.createCrossSlashEffect(crossSlashX, crossSlashY, skillAngle); // 本体向相反方向位移 const bodyDisplacementX = -normalizedX * bodyDisplacementDistance; const bodyDisplacementY = -normalizedY * bodyDisplacementDistance; this.currentlyControlled.x += bodyDisplacementX; this.currentlyControlled.y += bodyDisplacementY; this.currentlyControlled.setVelocity(0, 0); } /** * 傀儡技能3第一次释放:减速周围敌人 */ castPuppetSkill3First() { if (!this.puppet) return; const puppetX = this.puppet.x; const puppetY = this.puppet.y; const slowRadius = 150; // 记录第一次释放的时间 this.puppetSkill3State.hasFirstCast = true; this.puppetSkill3State.firstCastTime = Date.now(); // 创建减速特效(蓝色圆形) const slowEffect = this.add.graphics(); slowEffect.setScrollFactor(1); slowEffect.setDepth(50); // 绘制减速范围 slowEffect.lineStyle(2, 0x0000ff, 0.8); slowEffect.beginPath(); slowEffect.arc(puppetX, puppetY, slowRadius, 0, Math.PI * 2); slowEffect.strokePath(); // 添加脉冲动画 this.tweens.add({ targets: slowEffect, alpha: 0, // 移除scale动画,避免特效移动 // scale: 1.5, duration: 1000, ease: 'Power1', onComplete: () => { slowEffect.destroy(); } }); // 这里可以添加实际的敌人减速逻辑 console.log('傀儡技能3第一次释放:减速周围敌人'); } /** * 傀儡技能3第二次释放:眩晕周围敌人 */ castPuppetSkill3Second() { if (!this.puppet) return; const puppetX = this.puppet.x; const puppetY = this.puppet.y; const stunRadius = 150; // 记录第二次释放的时间 this.puppetSkill3State.hasSecondCast = true; this.puppetSkill3State.secondCastTime = Date.now(); // 创建眩晕特效(紫色圆形) this.stunEffect = this.add.graphics(); this.stunEffect.setScrollFactor(1); this.stunEffect.setDepth(50); // 绘制眩晕范围 this.stunEffect.lineStyle(2, 0xff00ff, 0.8); this.stunEffect.beginPath(); this.stunEffect.arc(puppetX, puppetY, stunRadius, 0, Math.PI * 2); this.stunEffect.strokePath(); // 第二次释放后:傀儡不可移动 this.puppet.canMove = false; // 额外设置速度为0,确保傀儡不会移动 this.puppet.setVelocity(0, 0); this.puppet.clearTarget(); // 一段时间后自动销毁傀儡,切换到本体控制 this.time.delayedCall(3000, () => { if (this.puppet) { // 销毁眩晕特效 if (this.stunEffect) { this.stunEffect.destroy(); this.stunEffect = null; } // 销毁傀儡 this.puppet.destroy(); this.puppet = null; // 切换到本体控制 this.isControllingPuppet = false; const yuange = this.characters.find(char => char.name === 'yuange'); if (yuange) { this.currentlyControlled = yuange; yuange.setSelected(true); // 更新UI提示 this.controlInfoText.setText('当前控制: 元歌'); // 更新技能轮盘显示为元歌本体的技能 this.updateSkillWheelDisplay(); // 更新选择系统并锁定相机到本体 import('../../systems/ControlMode.js').then(mod => { mod.clearSelection(); mod.addToSelection(yuange); mod.setActiveUnit(yuange); // 自动锁定相机到本体 mod.setCameraLocked(true); }); } // 重置技能3状态 this.puppetSkill3State = { hasFirstCast: false, hasSecondCast: false, firstCastTime: 0, secondCastTime: 0 }; } }); // 这里可以添加实际的敌人眩晕逻辑 console.log('傀儡技能3第二次释放:眩晕周围敌人'); } /** * 创建十字斩视觉效果 */ createCrossSlashEffect(x, y, angle) { // 创建十字斩图形 const crossSlash = this.add.graphics(); crossSlash.setScrollFactor(1); crossSlash.setDepth(50); // 十字斩尺寸 const crossSize = 80; // 设置十字斩位置 crossSlash.setPosition(x, y); // 绘制十字(相对于位置点) crossSlash.lineStyle(4, 0xff00ff, 1); // 水平线 crossSlash.beginPath(); crossSlash.moveTo(-crossSize/2, 0); crossSlash.lineTo(crossSize/2, 0); crossSlash.strokePath(); // 垂直线 crossSlash.beginPath(); crossSlash.moveTo(0, -crossSize/2); crossSlash.lineTo(0, crossSize/2); crossSlash.strokePath(); // 旋转十字斩以匹配技能方向(调整角度使连线位于夹角中间) crossSlash.rotation = angle - Math.PI/4; // 十字斩持续时间 const duration = 1000; // 淡出效果 this.tweens.add({ targets: crossSlash, alpha: 0, duration: duration, onComplete: () => { crossSlash.destroy(); } }); } /** * 更新技能预览效果 */ updateSkillPreview() { // 清除之前的预览 this.clearSkillPreview(); if (!this.currentlyControlled) return; // 计算技能释放方向(基于技能轮盘中心到指针的相对方向) const pointer = this.input.activePointer; const directionX = pointer.x - this.skillWheel.centerX; const directionY = pointer.y - this.skillWheel.centerY; // 计算技能释放角度 const skillAngle = Math.atan2(directionY, directionX); // 计算归一化方向向量 const distance = Math.sqrt(directionX * directionX + directionY * directionY); let normalizedX = 0; let normalizedY = 0; if (distance > 0) { normalizedX = directionX / distance; normalizedY = directionY / distance; } // 创建预览图形 this.skillPreview = this.add.graphics(); this.skillPreview.setScrollFactor(1); this.skillPreview.setDepth(99); // 根据当前激活的技能显示不同的预览效果 if (this.skillWheel.activeSkill === 1 && !this.isControllingPuppet) { // 一技能:扔出傀儡预览 const maxDistance = 200; // 计算目标位置 const targetX = this.currentlyControlled.x + normalizedX * maxDistance; const targetY = this.currentlyControlled.y + normalizedY * maxDistance; // 绘制移动路径 this.skillPreview.lineStyle(2, 0x00ff00, 0.5); this.skillPreview.beginPath(); this.skillPreview.moveTo(this.currentlyControlled.x, this.currentlyControlled.y); this.skillPreview.lineTo(targetX, targetY); this.skillPreview.strokePath(); // 绘制目标位置指示器 this.skillPreview.lineStyle(2, 0x00ff00, 1); this.skillPreview.strokeCircle(targetX, targetY, 15); this.skillPreview.fillStyle(0x00ff00, 0.2); this.skillPreview.fillCircle(targetX, targetY, 15); } else if (this.skillWheel.activeSkill === 3 && !this.isControllingPuppet) { // 三技能:十字斩预览 const minDistance = 0; // 最小距离(角色自身位置) const maxDistance = 200; // 最大距离 const maxBodyDisplacementDistance = 100; // 最大本体位移距离 // 计算轮盘按钮与中心的距离百分比(相对于轮盘半径) const distancePercentage = Math.min(distance / this.skillWheel.radius, 1); // 根据距离百分比计算十字斩实际释放距离 const crossSlashDistance = minDistance + (maxDistance - minDistance) * distancePercentage; // 本体反向位移距离(与十字斩释放距离成比例) const bodyDisplacementDistance = maxBodyDisplacementDistance * distancePercentage; // 计算十字斩位置 const crossSlashX = this.currentlyControlled.x + normalizedX * crossSlashDistance; const crossSlashY = this.currentlyControlled.y + normalizedY * crossSlashDistance; // 计算本体位移后的位置 const bodyDisplacementX = -normalizedX * bodyDisplacementDistance; const bodyDisplacementY = -normalizedY * bodyDisplacementDistance; const bodyEndX = this.currentlyControlled.x + bodyDisplacementX; const bodyEndY = this.currentlyControlled.y + bodyDisplacementY; // 绘制十字斩预览 this.skillPreview.lineStyle(4, 0xff00ff, 0.5); // 计算旋转后的四个端点坐标(调整角度使连线位于夹角中间) const crossSize = 40; const adjustedAngle = skillAngle - Math.PI/4; // 水平线左端点 const hLeftX = crossSlashX + Math.cos(adjustedAngle - Math.PI/2) * crossSize; const hLeftY = crossSlashY + Math.sin(adjustedAngle - Math.PI/2) * crossSize; // 水平右端点 const hRightX = crossSlashX + Math.cos(adjustedAngle + Math.PI/2) * crossSize; const hRightY = crossSlashY + Math.sin(adjustedAngle + Math.PI/2) * crossSize; // 垂直线上端点 const vTopX = crossSlashX + Math.cos(adjustedAngle) * crossSize; const vTopY = crossSlashY + Math.sin(adjustedAngle) * crossSize; // 垂直线下端点 const vBottomX = crossSlashX + Math.cos(adjustedAngle + Math.PI) * crossSize; const vBottomY = crossSlashY + Math.sin(adjustedAngle + Math.PI) * crossSize; // 绘制水平线 this.skillPreview.beginPath(); this.skillPreview.moveTo(hLeftX, hLeftY); this.skillPreview.lineTo(hRightX, hRightY); this.skillPreview.strokePath(); // 绘制垂直线 this.skillPreview.beginPath(); this.skillPreview.moveTo(vTopX, vTopY); this.skillPreview.lineTo(vBottomX, vBottomY); this.skillPreview.strokePath(); // 绘制十字斩方向 // 移除本体与技能的连线 } else if (this.skillWheel.activeSkill === 4 && !this.isControllingPuppet) { // 四技能:本体移动预览 const maxDistance = 200; // 计算目标位置 const targetX = this.currentlyControlled.x + normalizedX * maxDistance; const targetY = this.currentlyControlled.y + normalizedY * maxDistance; // 绘制移动路径 this.skillPreview.lineStyle(2, 0xffff00, 0.5); this.skillPreview.beginPath(); this.skillPreview.moveTo(this.currentlyControlled.x, this.currentlyControlled.y); this.skillPreview.lineTo(targetX, targetY); this.skillPreview.strokePath(); // 绘制目标位置指示器 this.skillPreview.lineStyle(2, 0xffff00, 1); this.skillPreview.strokeCircle(targetX, targetY, 15); this.skillPreview.fillStyle(0xffff00, 0.2); this.skillPreview.fillCircle(targetX, targetY, 15); } else if (this.skillWheel.activeSkill === 3 && this.isControllingPuppet) { // 傀儡技能3预览:控制特效范围 const effectRadius = 150; // 绘制技能范围 this.skillPreview.lineStyle(2, 0xffff00, 0.5); this.skillPreview.beginPath(); this.skillPreview.arc(this.currentlyControlled.x, this.currentlyControlled.y, effectRadius, 0, Math.PI * 2); this.skillPreview.strokePath(); } else if (this.skillWheel.activeSkill === 2 && !this.isControllingPuppet) { // 二技能:扇形飞镖攻击预览 // 扇形角度范围:45度(转换为弧度) const fanAngle = Phaser.Math.DegToRad(45); // 飞镖数量 const dartCount = 5; // 飞镖飞行距离 const dartDistance = 400; // 计算每个飞镖的角度间隔 const angleStep = fanAngle / (dartCount - 1); // 计算起始角度(扇形的左边界) const startAngle = skillAngle - fanAngle / 2; // 绘制扇形预览区域 this.skillPreview.lineStyle(2, 0x00ffff, 0.5); this.skillPreview.beginPath(); this.skillPreview.moveTo(this.currentlyControlled.x, this.currentlyControlled.y); // 绘制扇形边界 for (let i = 0; i <= 30; i++) { const angle = startAngle + (i / 30) * fanAngle; const x = this.currentlyControlled.x + Math.cos(angle) * dartDistance; const y = this.currentlyControlled.y + Math.sin(angle) * dartDistance; this.skillPreview.lineTo(x, y); } this.skillPreview.lineTo(this.currentlyControlled.x, this.currentlyControlled.y); this.skillPreview.strokePath(); // 绘制飞镖轨迹预览 for (let i = 0; i < dartCount; i++) { // 计算当前飞镖的角度 const currentAngle = startAngle + i * angleStep; // 计算飞镖终点位置 const endX = this.currentlyControlled.x + Math.cos(currentAngle) * dartDistance; const endY = this.currentlyControlled.y + Math.sin(currentAngle) * dartDistance; // 绘制轨迹线 this.skillPreview.lineStyle(1, 0xffff00, 0.3); this.skillPreview.beginPath(); this.skillPreview.moveTo(this.currentlyControlled.x, this.currentlyControlled.y); this.skillPreview.lineTo(endX, endY); this.skillPreview.strokePath(); // 绘制飞镖预览 const previewDart = this.add.sprite(this.currentlyControlled.x, this.currentlyControlled.y, 'dart'); previewDart.setRotation(currentAngle); previewDart.setAlpha(0.5); previewDart.setScrollFactor(1); previewDart.setDepth(99); // 保存到预览对象中,以便后续清除 if (!this.skillPreview.previewObjects) { this.skillPreview.previewObjects = []; } this.skillPreview.previewObjects.push(previewDart); } } else if (this.skillWheel.activeSkill === 2 && this.isControllingPuppet) { // 傀儡二技能:交换位置预览 const yuange = this.characters.find(char => char.name === 'yuange'); if (yuange) { // 绘制交换位置路径 this.skillPreview.lineStyle(2, 0x00ffff, 0.5); this.skillPreview.beginPath(); this.skillPreview.moveTo(this.currentlyControlled.x, this.currentlyControlled.y); this.skillPreview.lineTo(yuange.x, yuange.y); this.skillPreview.strokePath(); // 绘制双向箭头,表示交换 this.skillPreview.lineStyle(2, 0x00ffff, 1); this.skillPreview.beginPath(); // 箭头1 const arrow1X = this.currentlyControlled.x + (yuange.x - this.currentlyControlled.x) * 0.3; const arrow1Y = this.currentlyControlled.y + (yuange.y - this.currentlyControlled.y) * 0.3; this.skillPreview.moveTo(arrow1X, arrow1Y); this.skillPreview.lineTo(arrow1X + 10, arrow1Y - 10); this.skillPreview.moveTo(arrow1X, arrow1Y); this.skillPreview.lineTo(arrow1X - 10, arrow1Y - 10); // 箭头2 const arrow2X = this.currentlyControlled.x + (yuange.x - this.currentlyControlled.x) * 0.7; const arrow2Y = this.currentlyControlled.y + (yuange.y - this.currentlyControlled.y) * 0.7; this.skillPreview.moveTo(arrow2X, arrow2Y); this.skillPreview.lineTo(arrow2X + 10, arrow2Y + 10); this.skillPreview.moveTo(arrow2X, arrow2Y); this.skillPreview.lineTo(arrow2X - 10, arrow2Y + 10); this.skillPreview.strokePath(); } } else if (this.skillWheel.activeSkill === 4 && this.isControllingPuppet) { // 傀儡四技能:傀儡移动预览 const maxDistance = 200; // 计算目标位置 const targetX = this.currentlyControlled.x + normalizedX * maxDistance; const targetY = this.currentlyControlled.y + normalizedY * maxDistance; // 绘制移动路径 this.skillPreview.lineStyle(2, 0x00ffff, 0.5); this.skillPreview.beginPath(); this.skillPreview.moveTo(this.currentlyControlled.x, this.currentlyControlled.y); this.skillPreview.lineTo(targetX, targetY); this.skillPreview.strokePath(); // 绘制目标位置指示器 this.skillPreview.lineStyle(2, 0x00ffff, 1); this.skillPreview.strokeCircle(targetX, targetY, 15); this.skillPreview.fillStyle(0x00ffff, 0.2); this.skillPreview.fillCircle(targetX, targetY, 15); } } /** * 清除技能预览效果 */ clearSkillPreview() { if (this.skillPreview) { // 清除预览对象 if (this.skillPreview.previewObjects) { this.skillPreview.previewObjects.forEach(obj => { if (obj && obj.active) { obj.destroy(); } }); } // 清除预览图形 this.skillPreview.destroy(); this.skillPreview = null; } } /** * 释放貂蝉技能 * @param {number} skillIndex 技能索引(1-4) */ castDiaochanSkill(skillIndex) { const diaochan = this.currentlyControlled; // 计算技能释放方向(基于技能轮盘中心到指针的相对方向) const pointer = this.input.activePointer; // 计算轮盘中心到指针的方向向量 const directionX = pointer.x - this.skillWheel.centerX; const directionY = pointer.y - this.skillWheel.centerY; switch (skillIndex) { case 2: // 技能2:向指定方向移动 // 使用轮盘相对方向计算移动方向 const maxDistance = 250; const distance = Math.sqrt(directionX * directionX + directionY * directionY); let ratio = maxDistance / distance; if (isNaN(ratio) || ratio > 1) ratio = 1; // 计算移动距离 const moveX = directionX * ratio; const moveY = directionY * ratio; // 立即向目标方向移动一定距离 diaochan.x += moveX; diaochan.y += moveY; break; } } /** * 切换回元歌本体 * @param {boolean} destroyPuppet - 是否销毁傀儡,默认为true */ switchToYuange(destroyPuppet = true) { if (destroyPuppet && this.puppet) { // 删除傀儡 this.puppet.destroy(); this.puppet = null; // 销毁控制特效 if (this.stunEffect) { this.stunEffect.destroy(); this.stunEffect = null; } // 重置技能3状态 this.puppetSkill3State = { hasFirstCast: false, hasSecondCast: false, firstCastTime: 0, secondCastTime: 0 }; } // 切换回元歌本体控制 this.isControllingPuppet = false; this.currentlyControlled = this.characters.find(char => char.name === 'yuange'); this.currentlyControlled.setSelected(true); // 更新UI提示 this.controlInfoText.setText('当前控制: 元歌'); // 更新技能轮盘显示为元歌本体的技能 this.updateSkillWheelDisplay(); // 更新ControlMode的选中列表,确保虚拟摇杆能控制本体 import('../../systems/ControlMode.js').then(mod => { mod.clearSelection(); mod.addToSelection(this.currentlyControlled); mod.setActiveUnit(this.currentlyControlled); // 自动锁定相机到本体 mod.setCameraLocked(true); }); } /** * 窗口大小变化时的处理函数 * 当浏览器窗口大小改变时,调整游戏画布的大小以适应新窗口 */ resizeGame() { // 调用Phaser的scale.resize方法,将游戏画布大小调整为当前窗口大小 this.scale.resize(window.innerWidth, window.innerHeight); // 更新UI位置 if (this.skillInfoText) { this.skillInfoText.setPosition(10, window.innerHeight - 100); } if (this.controlInfoText) { this.controlInfoText.setPosition(10, window.innerHeight - 60); } } }