mirror of
http://junepve.top:3000/chicrobot/CINC.git
synced 2026-10-29 23:41:29 +08:00
1491 lines
53 KiB
JavaScript
1491 lines
53 KiB
JavaScript
// 游戏场景文件:这是游戏的主要场景,包含了游戏的核心逻辑和玩法实现
|
||
|
||
// 导入单位类:用于创建游戏中的角色/单位
|
||
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 = '#7d2100ff'; // 设置填充颜色
|
||
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);
|
||
}
|
||
}
|
||
} |