Files
CINC/scenes/testScenes/AttackSkillTestScene.js
T

1786 lines
63 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 游戏场景文件:这是游戏的主要场景,包含了游戏的核心逻辑和玩法实现
// 导入单位类:用于创建游戏中的角色/单位
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;
// 多点触控相关变量
this.activeTouches = new Map(); // 存储活跃的触摸点
this.maxTouchPoints = 10; // 最大触摸点数量
this.touchVisualizations = []; // 触摸点可视化元素
this.joysticks = []; // 多个虚拟摇杆
this.dualJoystickEnabled = false; // 禁用双摇杆,只保留左侧移动摇杆
// 技能释放状态
this.isCastingSkill = false; // 是否正在释放技能
}
// 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.initMultiTouch();
// 创建左侧移动摇杆
this.virtualJoystick = new VirtualJoystick(this, true);
this.virtualJoystick.centerX = 150;
this.virtualJoystick.centerY = this.scale.height - 150;
this.virtualJoystick.base.setPosition(this.virtualJoystick.centerX, this.virtualJoystick.centerY);
this.virtualJoystick.knob.setPosition(this.virtualJoystick.centerX, this.virtualJoystick.centerY);
this.joysticks.push(this.virtualJoystick);
// 创建触摸点可视化
this.createTouchVisualizations();
// === 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();
}
// 更新双摇杆状态
this.joysticks.forEach(joystick => {
if (joystick.update) {
joystick.update();
}
});
// 相机锁定逻辑:如果有锁定状态且有当前控制的角色,则让相机跟随角色
import('../../systems/ControlMode.js').then(mod => {
if (mod.cameraLocked && this.currentlyControlled) {
this.cameras.main.startFollow(this.currentlyControlled);
} else {
this.cameras.main.stopFollow();
}
});
// 当技能轮盘激活时,每帧更新技能预览,确保角色移动时指示器也会更新
if (this.skillWheel && this.skillWheel.isActive && this.currentlyControlled) {
this.updateSkillPreview();
}
}
/**
* 创建技能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)
* @param {Object} direction 技能释放方向(可选)
*/
castSkill(skillIndex, direction = null) {
// 检查是否有当前控制的角色
if (!this.currentlyControlled) return;
// 检查是否正在释放技能(特别是四技能)
if (this.isCastingSkill) return;
// 根据当前控制角色类型和技能索引释放对应技能
if (this.currentlyControlled.type === 'yuange' || this.currentlyControlled.type === 'yuange-puppet') {
this.castYuangeSkill(skillIndex, direction);
} else if (this.currentlyControlled.type === 'diaochan') {
this.castDiaochanSkill(skillIndex, direction);
}
}
/**
* 释放元歌技能
* @param {number} skillIndex 技能索引(1-4)
* @param {Object} direction 技能释放方向(可选)
*/
castYuangeSkill(skillIndex, direction = null) {
const yuange = this.characters.find(char => char.name === 'yuange');
// 计算技能释放方向
let directionX, directionY;
if (direction) {
// 使用传入的方向参数(来自右侧摇杆)
directionX = direction.x * 100;
directionY = direction.y * 100;
} else {
// 基于技能轮盘中心到指针的相对方向
const pointer = this.skillWheel.activePointer || this.input.activePointer;
directionX = pointer.x - this.skillWheel.centerX;
directionY = pointer.y - this.skillWheel.centerY;
}
// 对于一技能和四技能,设置canMove为false,确保角色不能移动
if ((skillIndex === 1 || skillIndex === 4) && !this.isControllingPuppet) {
yuange.canMove = false;
yuange.setVelocity(0, 0);
}
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:傀儡快速移动并拉本体 / 切换到本体控制(当处于技能3控制状态时)
if (this.puppet && this.isControllingPuppet) {
// 检查是否处于3技能第二次按下后的控制期间
const isInSkill3Control = this.puppetSkill3State && this.puppetSkill3State.hasSecondCast;
if (isInSkill3Control) {
// 处于3技能控制期间:只切换到本体控制,不执行移动和拉近逻辑
// 切换到本体控制(保持傀儡存在)
this.switchToYuange(false);
} 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 = 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:扔出傀儡或收回傀儡
// 禁用移动能力
yuange.canMove = false;
// 停顿
yuange.setVelocity(0, 0);
// 延迟(0.2秒)后释放技能
this.time.delayedCall(200, () => {
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);
});
}
// 立即恢复透明度
yuange.alpha = 1;
}, [], this);
break;
case 2: // 技能2:扇形飞镖攻击
this.castFanDartSkill();
break;
case 3: // 技能3:十字斩
this.castCrossSlashSkill();
break;
case 4: // 技能4:本体向技能指向方向移动
// 确保是本体在控制
if (!this.isControllingPuppet) {
// 设置技能释放状态为true,防止释放其他技能
this.isCastingSkill = true;
// 使用轮盘相对方向计算目标位置
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.tweens.add({
targets: yuange,
alpha: 0.3, // 透明度变为30%
duration: 450, // 持续450毫秒,比延迟时间略短
ease: 'Linear', // 线性变化
onComplete: function() {
// 特效完成后不做任何操作,等待延迟回调
}
});
// 延迟后直接传送,不需要移动过程
this.time.delayedCall(500, () => {
yuange.setPosition(targetX, targetY);
yuange.setVelocity(0, 0);
// 立即恢复透明度
yuange.alpha = 1;
// 技能释放完成后,恢复移动能力
yuange.canMove = true;
// 技能释放完成,设置技能释放状态为false
this.isCastingSkill = false;
}, [], this);
}
break;
}
}
}
/**
* 释放扇形飞镖技能
*/
castFanDartSkill() {
if (!this.currentlyControlled) return;
// 计算技能释放方向(基于技能轮盘中心到指针的相对方向)
const pointer = this.skillWheel.activePointer || 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.skillWheel.activePointer || 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.puppetDestructionTimer = 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('当前控制: 元歌');
// 恢复本体的移动能力
yuange.canMove = true;
yuange.setVelocity(0, 0);
// 更新技能轮盘显示为元歌本体的技能
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
};
// 清除计时器引用
this.puppetDestructionTimer = null;
}
});
// 这里可以添加实际的敌人眩晕逻辑
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.skillWheel.activePointer || 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.skillWheel.activePointer || 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) {
// 清除定时销毁任务
if (this.puppetDestructionTimer) {
this.puppetDestructionTimer.remove();
this.puppetDestructionTimer = null;
}
// 删除傀儡
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('当前控制: 元歌');
// 恢复本体的移动能力
const yuange = this.currentlyControlled;
if (yuange) {
yuange.canMove = true;
yuange.setVelocity(0, 0);
}
// 更新技能轮盘显示为元歌本体的技能
this.updateSkillWheelDisplay();
// 更新ControlMode的选中列表,确保虚拟摇杆能控制本体
import('../../systems/ControlMode.js').then(mod => {
mod.clearSelection();
mod.addToSelection(this.currentlyControlled);
mod.setActiveUnit(this.currentlyControlled);
// 自动锁定相机到本体
mod.setCameraLocked(true);
});
}
/**
* 初始化多点触控处理
*/
initMultiTouch() {
// 启用多点触控
this.input.addPointer(this.maxTouchPoints - 1); // 默认为1,所以需要添加9个
// 监听触摸开始事件
this.input.on('pointerdown', (pointer) => {
this.activeTouches.set(pointer.id, {
x: pointer.x,
y: pointer.y,
time: Date.now()
});
this.updateTouchVisualization();
});
// 监听触摸移动事件
this.input.on('pointermove', (pointer) => {
if (this.activeTouches.has(pointer.id)) {
this.activeTouches.set(pointer.id, {
x: pointer.x,
y: pointer.y,
time: Date.now()
});
this.updateTouchVisualization();
}
});
// 监听触摸结束事件
this.input.on('pointerup', (pointer) => {
this.activeTouches.delete(pointer.id);
this.updateTouchVisualization();
});
// 监听触摸离开屏幕事件
this.input.on('pointerout', (pointer) => {
// 只有当触摸点不是技能轮盘的activePointer时,才从activeTouches中删除
if (!this.skillWheel || !this.skillWheel.isActive || this.skillWheel.activePointer !== pointer) {
this.activeTouches.delete(pointer.id);
this.updateTouchVisualization();
}
});
}
/**
* 创建双虚拟摇杆
*/
createDualJoysticks() {
// 创建左侧移动摇杆
const leftJoystick = new VirtualJoystick(this, true);
leftJoystick.centerX = 150;
leftJoystick.centerY = this.scale.height - 150;
leftJoystick.base.setPosition(leftJoystick.centerX, leftJoystick.centerY);
leftJoystick.knob.setPosition(leftJoystick.centerX, leftJoystick.centerY);
this.joysticks.push(leftJoystick);
}
/**
* 创建触摸点可视化
*/
createTouchVisualizations() {
// 创建触摸点可视化元素
for (let i = 0; i < this.maxTouchPoints; i++) {
const visualization = this.add.circle(0, 0, 30, 0xffffff, 0.5);
visualization.setStrokeStyle(2, 0x00ff00, 1);
visualization.setVisible(false);
visualization.setScrollFactor(0);
visualization.setDepth(102);
const label = this.add.text(0, 0, i.toString(), {
fontSize: '14px',
fill: '#000000',
fontFamily: 'Arial',
fontWeight: 'bold'
});
label.setOrigin(0.5, 0.5);
label.setVisible(false);
label.setScrollFactor(0);
label.setDepth(103);
this.touchVisualizations.push({ circle: visualization, label: label });
}
}
/**
* 更新触摸点可视化
*/
updateTouchVisualization() {
// 重置所有可视化元素
this.touchVisualizations.forEach(viz => {
viz.circle.setVisible(false);
viz.label.setVisible(false);
});
// 更新活跃触摸点的可视化
let i = 0;
for (const [id, touch] of this.activeTouches.entries()) {
if (i < this.touchVisualizations.length) {
const viz = this.touchVisualizations[i];
viz.circle.setPosition(touch.x, touch.y);
viz.circle.setVisible(true);
viz.label.setPosition(touch.x, touch.y);
viz.label.setText(id.toString());
viz.label.setVisible(true);
i++;
}
}
}
/**
* 处理右侧摇杆输入
*/
handleRightJoystickInput(direction) {
// 处理右侧摇杆输入
// 这里可以实现攻击、技能释放等操作
if (Math.abs(direction.x) > 0.1 || Math.abs(direction.y) > 0.1) {
// 创建一个临时的指示箭头
this.createDirectionIndicator(direction);
// 如果有当前控制的角色,使用右侧摇杆方向释放技能
if (this.currentlyControlled) {
// 默认使用技能2(飞镖攻击)
this.castSkill(2, direction);
}
}
}
/**
* 创建方向指示箭头
*/
createDirectionIndicator(direction) {
// 创建方向指示箭头
const centerX = this.scale.width - 150;
const centerY = this.scale.height - 150;
const length = 100;
const endX = centerX + direction.x * length;
const endY = centerY + direction.y * length;
// 检查是否已存在指示箭头
if (this.directionIndicator) {
this.directionIndicator.clear();
} else {
this.directionIndicator = this.add.graphics();
this.directionIndicator.setScrollFactor(0);
this.directionIndicator.setDepth(9);
}
// 绘制箭头
this.directionIndicator.lineStyle(3, 0xffff00, 1);
this.directionIndicator.beginPath();
this.directionIndicator.moveTo(centerX, centerY);
this.directionIndicator.lineTo(endX, endY);
this.directionIndicator.strokePath();
// 3秒后清除箭头
this.time.delayedCall(300, () => {
if (this.directionIndicator) {
this.directionIndicator.clear();
}
});
}
/**
* 窗口大小变化时的处理函数
* 当浏览器窗口大小改变时,调整游戏画布的大小以适应新窗口
*/
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);
}
// 更新摇杆位置
this.joysticks.forEach((joystick) => {
// 左侧摇杆
joystick.centerX = 150;
joystick.centerY = window.innerHeight - 150;
if (joystick.base) {
joystick.base.setPosition(joystick.centerX, joystick.centerY);
}
if (joystick.knob) {
joystick.knob.setPosition(joystick.centerX, joystick.centerY);
}
});
}
}