feat(STS) 添加角色技能测试场景

This commit is contained in:
chicrobot committed 2026-01-14 02:38:24 +08:00
1 parent f16f7b6bbd
commit e0ba6f1c9e
6 files changed
+810 -3

No files matched your search

BIN
View File
Binary file not shown.
+4 -1
View File
@@ -28,6 +28,8 @@ import CharacterAnimationTestScene from './scenes/testScenes/CharacterAnimationT
import MapEditTestScene from './scenes/testScenes/MapEditTestScene.js'; import MapEditTestScene from './scenes/testScenes/MapEditTestScene.js';
// 导入种田功能测试场景 // 导入种田功能测试场景
import FarmingTestScene from './scenes/testScenes/FarmingTestScene.js'; import FarmingTestScene from './scenes/testScenes/FarmingTestScene.js';
// 导入技能测试场景
import SkillTestScene from './scenes/testScenes/SkillTestScene.js';
// 创建游戏的最终配置对象 // 创建游戏的最终配置对象
const config = { const config = {
@@ -51,7 +53,8 @@ const config = {
MapEditSinglePicTestScene, MapEditSinglePicTestScene,
CharacterAnimationTestScene, CharacterAnimationTestScene,
MapEditTestScene, MapEditTestScene,
FarmingTestScene FarmingTestScene,
SkillTestScene
] ]
}; };
+1
View File
@@ -20,6 +20,7 @@ export default class BootScene extends Phaser.Scene {
// 测试场景列表 // 测试场景列表
const testScenes = [ const testScenes = [
{ key: 'SkillTestScene', name: '测试场景15:技能移动效果测试' },
{ key: 'FarmingTestScene', name: '测试场景14:种田功能测试' }, { key: 'FarmingTestScene', name: '测试场景14:种田功能测试' },
{ key: 'CharacterAnimationTestScene', name: '测试场景13:角色动画测试' }, { key: 'CharacterAnimationTestScene', name: '测试场景13:角色动画测试' },
{ key: 'MapEditTestScene', name: '测试场景12:地图编辑测试' }, { key: 'MapEditTestScene', name: '测试场景12:地图编辑测试' },
+760
View File
@@ -0,0 +1,760 @@
// 游戏场景文件:这是游戏的主要场景,包含了游戏的核心逻辑和玩法实现
// 导入单位类:用于创建游戏中的角色/单位
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 SkillTestScene extends Phaser.Scene {
// 构造函数:初始化场景,设置场景的唯一键值
constructor() {
super({ key: 'SkillTestScene' });
// 角色相关变量
this.characters = [];
this.currentlyControlled = null; // 当前控制的角色
this.isControllingPuppet = false; // 是否正在控制傀儡
this.puppet = null; // 元歌的傀儡
// 技能相关变量
this.skills = {};
}
// 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);
}
// === 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,
pointer: 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.pointer !== 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.input.on('pointerup', (ptr) => {
// 只有在技能轮盘激活时才处理
if (!this.skillWheel.isActive || this.skillWheel.pointer !== ptr) {
return;
}
// 完成技能释放
this.finishSkillCast();
});
}
/**
* 开始技能施法
*/
startSkillCast(pointer) {
// 设置技能轮盘状态
this.skillWheel.isActive = true;
this.skillWheel.pointer = 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;
}
}
/**
* 完成技能施法
*/
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) {
// 记录傀儡当前位置
const puppetX = this.puppet.x;
const puppetY = this.puppet.y;
// 获取本体拉近到傀儡一定范围内的坐标
const closeX = this.puppet.x - 50 * Math.sign(this.puppet.x - yuange.x);
const closeY = this.puppet.y - 50 * Math.sign(this.puppet.y - yuange.y);
// 傀儡移动到本体拉近后的位置
this.puppet.setPosition(closeX, closeY);
this.puppet.setVelocity(0, 0);
this.puppet.clearTarget();
// 直接设置本体位置到傀儡原本位置
yuange.setPosition(puppetX, puppetY);
yuange.setVelocity(0, 0);
}
break;
case 4: // 技能4:傀儡快速移动并拉本体
if (this.puppet && this.isControllingPuppet) {
// 使用轮盘相对方向计算目标位置
const maxDistance = 300;
const distance = Math.sqrt(directionX * directionX + directionY * directionY);
let ratio = maxDistance / distance;
if (isNaN(ratio) || ratio > 1) ratio = 1;
// 计算目标位置:傀儡当前位置 + 方向向量 * 最大距离
const targetX = this.puppet.x + directionX * ratio;
const targetY = this.puppet.y + directionY * ratio;
// 傀儡移动到目标位置
// this.puppet.setTarget(targetX, targetY);
this.puppet.setPosition(targetX, targetY);
this.puppet.setVelocity(0, 0);
this.puppet.clearTarget();
// 本体瞬间移动到傀儡附近
yuange.x = this.puppet.x - 50 * Math.sign(this.puppet.x - yuange.x);
yuange.y = this.puppet.y - 50 * Math.sign(this.puppet.y - yuange.y);
}
break;
}
} else {
// 控制本体时的技能
switch (skillIndex) {
case 1: // 技能1:扔出傀儡
if (!this.puppet) {
// 使用轮盘相对方向计算目标位置
const maxDistance = 300;
const distance = Math.sqrt(directionX * directionX + directionY * directionY);
let ratio = maxDistance / distance;
if (isNaN(ratio) || ratio > 1) ratio = 1;
// 计算目标位置:元歌当前位置 + 方向向量 * 最大距离
const targetX = yuange.x + directionX * ratio;
const targetY = yuange.y + directionY * ratio;
// 创建傀儡直接在指定位置生成,不需要从本体位置开始移动
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 4: // 技能4:本体向技能指向方向移动
// 确保是本体在控制
if (!this.isControllingPuppet) {
// 使用轮盘相对方向计算目标位置
const maxDistance = 400;
const distance = Math.sqrt(directionX * directionX + directionY * directionY);
let ratio = maxDistance / distance;
if (isNaN(ratio) || ratio > 1) ratio = 1;
// 计算目标位置:元歌当前位置 + 方向向量 * 最大距离
const targetX = yuange.x + directionX * ratio;
const targetY = yuange.y + directionY * ratio;
// 短暂停顿(0.5秒)
yuange.setVelocity(0, 0);
// 延迟后直接传送,不需要移动过程
this.time.delayedCall(500, () => {
yuange.setPosition(targetX, targetY);
yuange.setVelocity(0, 0);
}, [], this);
}
break;
}
}
}
/**
* 释放貂蝉技能
* @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;
}
}
/**
* 切换回元歌本体
*/
switchToYuange() {
if (this.puppet) {
// 删除傀儡
this.puppet.destroy();
this.puppet = null;
}
// 切换回元歌本体控制
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);
}
}
}
+2 -2
View File
@@ -26,8 +26,8 @@ export class CommandSystem {
// 遍历所有目标单位,调用它们的moveRelative方法进行相对移动 // 遍历所有目标单位,调用它们的moveRelative方法进行相对移动
targets.forEach(unit => { targets.forEach(unit => {
// 确保unit是有效的Unit实例 // 确保unit是有效的Unit实例,并且body存在(防止已销毁的单位被调用)
if (unit && typeof unit.moveRelative === 'function') { if (unit && typeof unit.moveRelative === 'function' && unit.body) {
unit.moveRelative(dir.x, dir.y); unit.moveRelative(dir.x, dir.y);
} }
}); });
+43
View File
@@ -309,11 +309,54 @@ export class InputSystem {
return this.isMouseOnMiniMap(pointer) || return this.isMouseOnMiniMap(pointer) ||
this.isMouseOnJoystick(pointer) || this.isMouseOnJoystick(pointer) ||
this.isMouseOnSkillWheel(pointer) ||
this.isMouseOnSidebar(pointer) || this.isMouseOnSidebar(pointer) ||
this.isMouseOnInventoryUI(pointer) || this.isMouseOnInventoryUI(pointer) ||
this.isMouseOnItemListUI(pointer); this.isMouseOnItemListUI(pointer);
} }
/**
* 检查鼠标是否在技能轮盘上
* @param {Object} pointer - 指针事件对象
* @returns {boolean} - 如果鼠标在技能轮盘上返回true,否则返回false
*/
isMouseOnSkillWheel(pointer) {
// 检查场景中是否有技能轮盘
if (!this.scene.skillWheel) {
return false;
}
// 获取技能轮盘实例
const skillWheel = this.scene.skillWheel;
// 获取鼠标在屏幕上的坐标
const mouseX = pointer.x;
const mouseY = pointer.y;
// 计算技能轮盘的边界(包括轮盘和操作区域)
const skillWheelRadius = skillWheel.radius * 3; // 操作区域是轮盘半径的三倍,考虑技能按钮的位置
const skillWheelLeft = skillWheel.centerX - skillWheelRadius; // 轮盘左边界
const skillWheelRight = skillWheel.centerX + skillWheelRadius; // 轮盘右边界
const skillWheelTop = skillWheel.centerY - skillWheelRadius; // 轮盘上边界
const skillWheelBottom = skillWheel.centerY + skillWheelRadius; // 轮盘下边界
// 检查鼠标是否在操作区域内
if (mouseX >= skillWheelLeft && mouseX <= skillWheelRight &&
mouseY >= skillWheelTop && mouseY <= skillWheelBottom) {
return true;
}
// 检查是否点击在技能按钮上
for (const button of skillWheel.skillButtons) {
const buttonBounds = button.getBounds();
if (mouseX >= buttonBounds.x && mouseX <= buttonBounds.right &&
mouseY >= buttonBounds.y && mouseY <= buttonBounds.bottom) {
return true;
}
}
return false;
}
/** /**
* 检查当前是否处于种植或收获模式 * 检查当前是否处于种植或收获模式
* @returns {boolean} - 如果处于种植或收获模式返回true,否则返回false * @returns {boolean} - 如果处于种植或收获模式返回true,否则返回false