mirror of
http://junepve.top:3000/chicrobot/CINC.git
synced 2026-10-29 21:31:29 +08:00
feat(item) 优化移动及添加测试场景
This commit is contained in:
1 parent
2f7210f47e
commit
b9a5180293
15 files changed
+1428
-62
No files matched your search
@@ -8,6 +8,8 @@ CINC是一个基于Phaser.js开发的2D策略/模拟游戏项目。该项目采
|
||||
- **选择系统**:支持单选和框选多个单位
|
||||
- **命令系统**:处理单位移动命令,支持相对移动和目标点移动
|
||||
- **输入系统**:统一处理键盘和鼠标输入
|
||||
- **物品系统**:支持物品的创建、拾取、使用和管理
|
||||
- **侧边栏UI**:显示游戏信息、角色属性、物品详情和交互提示
|
||||
- **响应式设计**:适配不同屏幕尺寸
|
||||
- **虚拟摇杆**:支持触摸设备操作
|
||||
|
||||
@@ -21,20 +23,34 @@ CINC是一个基于Phaser.js开发的2D策略/模拟游戏项目。该项目采
|
||||
```
|
||||
├── config/ # 游戏配置文件
|
||||
│ └── gameConfig.js
|
||||
├── data/ # 游戏数据文件
|
||||
│ └── items.json # 物品数据定义
|
||||
├── index.html # 入口HTML文件
|
||||
├── items/ # 物品系统
|
||||
│ ├── Item.js # 物品类
|
||||
│ └── ItemManager.js # 物品管理器
|
||||
├── main.js # 游戏初始化入口
|
||||
├── objects/ # 游戏对象定义
|
||||
│ └── Unit.js # 单位类
|
||||
├── phaser.min.js # Phaser.js库
|
||||
├── scenes/ # 游戏场景
|
||||
│ └── GameScene.js
|
||||
│ ├── BootScene.js # 启动场景
|
||||
│ ├── GameScene.js # 主游戏场景
|
||||
│ ├── ItemTestScene.js # 物品系统测试场景
|
||||
│ ├── MovementTestScene1.js # 移动测试场景1
|
||||
│ ├── MovementTestScene2.js # 移动测试场景2
|
||||
│ ├── MovementTestScene3.js # 移动测试场景3
|
||||
│ ├── MovementTestScene4.js # 移动测试场景4
|
||||
│ ├── MovementTestScene5.js # 移动测试场景5
|
||||
│ └── MovementTestScene6.js # 移动测试场景6
|
||||
├── systems/ # 游戏系统
|
||||
│ ├── CommandSystem.js # 命令处理系统
|
||||
│ ├── ControlMode.js # 控制模式管理
|
||||
│ ├── InputSystem.js # 输入处理系统
|
||||
│ └── SelectionSystem.js # 单位选择系统
|
||||
├── ui/ # 用户界面组件
|
||||
│ └── VirtualJoystick.js # 虚拟摇杆
|
||||
│ ├── MiniMap.js # 小地图组件
|
||||
│ └── VirtualJoystick.js # 虚拟摇杆
|
||||
└── utils/ # 工具文件
|
||||
```
|
||||
|
||||
@@ -50,22 +66,60 @@ http://localhost:8000
|
||||
```
|
||||
|
||||
## 游戏操作
|
||||
### 单位操作
|
||||
- **单选单位**:鼠标左键点击单位
|
||||
- **框选单位**:按住鼠标左键拖动框选多个单位
|
||||
- **移动命令**:点击空地指定移动目标点
|
||||
- **键盘控制**:使用WASD或方向键进行相对移动(需先选中单位)
|
||||
|
||||
### 物品操作
|
||||
- **查看物品信息**:鼠标左键点击物品
|
||||
- **拾取物品**:点击物品后按E键
|
||||
- **快捷使用物品**:点击物品后按Q键
|
||||
- **创建随机物品**:按1键
|
||||
- **创建食物类物品**:按2键
|
||||
- **创建工具类物品**:按3键
|
||||
- **清理所有物品**:按4键
|
||||
|
||||
### 场景导航
|
||||
- **返回启动场景**:按空格键
|
||||
|
||||
### 相机控制
|
||||
- **移动视角**:使用WASD或方向键
|
||||
- **小地图交互**:点击小地图快速移动视角,拖拽小地图调整视角
|
||||
|
||||
## 开发说明
|
||||
项目采用模块化设计,各系统之间通过事件系统通信,便于扩展和维护。主要系统职责:
|
||||
|
||||
- **InputSystem**:处理所有输入事件,转换为游戏命令
|
||||
- **SelectionSystem**:管理单位选择状态
|
||||
- **CommandSystem**:执行具体游戏命令
|
||||
- **InputSystem**:处理所有输入事件,转换为游戏命令,支持键盘、鼠标和虚拟摇杆输入
|
||||
- **SelectionSystem**:管理单位选择状态,支持单选和框选
|
||||
- **CommandSystem**:执行具体游戏命令,处理单位移动和交互
|
||||
- **ItemManager**:加载和管理物品数据,创建和维护物品实例
|
||||
- **Unit**:单位对象,处理自身逻辑和渲染
|
||||
- **Item**:物品对象,处理物品交互和使用逻辑
|
||||
|
||||
## 扩展建议
|
||||
- 添加单位属性系统(生命值、攻击力等)
|
||||
- 实现战斗系统
|
||||
- 添加地图和关卡系统
|
||||
- 实现资源管理系统
|
||||
- 添加UI界面(控制面板、状态栏等)
|
||||
- **物品系统扩展**:
|
||||
- 实现背包系统,管理拾取的物品
|
||||
- 添加物品合成和制作系统
|
||||
- 实现物品耐久度和消耗机制
|
||||
- 添加更多物品类型和属性
|
||||
- **战斗系统**:
|
||||
- 实现单位属性系统(生命值、攻击力等)
|
||||
- 添加攻击和防御机制
|
||||
- 实现战斗动画和特效
|
||||
- **地图和关卡系统**:
|
||||
- 实现地图生成和加载
|
||||
- 添加关卡目标和胜利条件
|
||||
- 实现地形和障碍物系统
|
||||
- **资源管理系统**:
|
||||
- 添加资源采集和生产机制
|
||||
- 实现资源存储和消耗系统
|
||||
- **UI界面扩展**:
|
||||
- 实现背包界面
|
||||
- 添加角色属性面板
|
||||
- 实现技能和升级系统界面
|
||||
- 添加游戏菜单和设置界面
|
||||
- **多人游戏**:
|
||||
- 实现本地多人游戏
|
||||
- 添加网络多人游戏支持
|
||||
@@ -18,6 +18,10 @@ export default class Unit extends Phaser.Physics.Arcade.Sprite {
|
||||
this.isSelected = false;
|
||||
this.selectedIndicator = null;
|
||||
this.createSelectedIndicator();
|
||||
|
||||
// 目标地点显示
|
||||
this.targetIndicator = null;
|
||||
this.pathGraphics = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -35,6 +39,30 @@ export default class Unit extends Phaser.Physics.Arcade.Sprite {
|
||||
this.selectedIndicator.setScrollFactor(1, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建目标地点指示器
|
||||
*/
|
||||
createTargetIndicator() {
|
||||
if (!this.targetIndicator) {
|
||||
// 创建目标地点指示器(红色圆点)
|
||||
this.targetIndicator = this.scene.add.circle(0, 0, 8, 0xff0000, 1);
|
||||
this.targetIndicator.setOrigin(0.5, 0.5);
|
||||
this.targetIndicator.setScrollFactor(1, 1);
|
||||
this.targetIndicator.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建寻路路径图形
|
||||
*/
|
||||
createPathGraphics() {
|
||||
if (!this.pathGraphics) {
|
||||
// 创建图形对象用于绘制路径
|
||||
this.pathGraphics = this.scene.add.graphics();
|
||||
this.pathGraphics.setScrollFactor(1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置单位选中状态
|
||||
* @param {boolean} isSelected
|
||||
@@ -44,6 +72,41 @@ export default class Unit extends Phaser.Physics.Arcade.Sprite {
|
||||
if (this.selectedIndicator) {
|
||||
this.selectedIndicator.setVisible(isSelected);
|
||||
}
|
||||
|
||||
// 只有选中时才显示目标地点和路径
|
||||
this.updateTargetVisibility();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新目标地点和路径的可见性
|
||||
*/
|
||||
updateTargetVisibility() {
|
||||
if (this.targetIndicator) {
|
||||
this.targetIndicator.setVisible(this.isSelected && this.target !== null);
|
||||
}
|
||||
if (this.pathGraphics) {
|
||||
this.pathGraphics.clear();
|
||||
// 只有选中且有目标时才绘制路径
|
||||
if (this.isSelected && this.target !== null) {
|
||||
this.drawPath();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制寻路路径
|
||||
*/
|
||||
drawPath() {
|
||||
if (!this.pathGraphics || !this.target) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.pathGraphics.clear();
|
||||
this.pathGraphics.lineStyle(2, 0x00ff00, 0.8);
|
||||
this.pathGraphics.beginPath();
|
||||
this.pathGraphics.moveTo(this.x, this.y);
|
||||
this.pathGraphics.lineTo(this.target.x, this.target.y);
|
||||
this.pathGraphics.stroke();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,6 +123,18 @@ export default class Unit extends Phaser.Physics.Arcade.Sprite {
|
||||
const clampedY = Math.max(worldBounds.y, Math.min(worldBounds.bottom, y));
|
||||
|
||||
this.target = { x: clampedX, y: clampedY };
|
||||
|
||||
// 创建目标指示器和路径图形(如果不存在)
|
||||
this.createTargetIndicator();
|
||||
this.createPathGraphics();
|
||||
|
||||
// 更新目标指示器位置
|
||||
if (this.targetIndicator) {
|
||||
this.targetIndicator.setPosition(this.target.x, this.target.y);
|
||||
}
|
||||
|
||||
// 更新目标可见性和路径绘制
|
||||
this.updateTargetVisibility();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,6 +143,14 @@ export default class Unit extends Phaser.Physics.Arcade.Sprite {
|
||||
clearTarget() {
|
||||
this.target = null;
|
||||
this.setVelocity(0, 0);
|
||||
|
||||
// 隐藏目标指示器和清除路径
|
||||
if (this.targetIndicator) {
|
||||
this.targetIndicator.setVisible(false);
|
||||
}
|
||||
if (this.pathGraphics) {
|
||||
this.pathGraphics.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,6 +182,11 @@ export default class Unit extends Phaser.Physics.Arcade.Sprite {
|
||||
if (this.selectedIndicator && this.isSelected) {
|
||||
this.selectedIndicator.setPosition(this.x, this.y);
|
||||
}
|
||||
|
||||
// 更新路径绘制(单位位置变化时)
|
||||
if (this.isSelected && this.target !== null) {
|
||||
this.drawPath();
|
||||
}
|
||||
}
|
||||
|
||||
updateMovement() {
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
// 启动场景:用于选择进入不同的测试场景
|
||||
import GameScene from './GameScene.js';
|
||||
|
||||
export default class BootScene extends Phaser.Scene {
|
||||
constructor() {
|
||||
super({ key: 'BootScene' });
|
||||
}
|
||||
|
||||
create() {
|
||||
// 设置背景色
|
||||
this.cameras.main.setBackgroundColor('#000000');
|
||||
|
||||
// 添加标题文本
|
||||
this.add.text(this.sys.game.config.width / 2, 100, '测试场景菜单', {
|
||||
fontSize: '32px',
|
||||
fill: '#ffffff',
|
||||
fontFamily: 'Arial'
|
||||
}).setOrigin(0.5, 0.5);
|
||||
|
||||
// 测试场景列表
|
||||
const testScenes = [
|
||||
{ key: 'MovementTestScene1', name: '测试场景1:相对移动(键盘控制)' },
|
||||
{ key: 'MovementTestScene2', name: '测试场景2:目标点移动' },
|
||||
{ key: 'MovementTestScene3', name: '测试场景3:单位选择与移动命令' },
|
||||
{ key: 'MovementTestScene4', name: '测试场景4:虚拟摇杆控制' },
|
||||
{ key: 'MovementTestScene5', name: '测试场景5:移动更新逻辑' },
|
||||
{ key: 'MovementTestScene6', name: '测试场景6:小地图测试' },
|
||||
{ key: 'ItemTestScene', name: '测试场景7:物品系统测试' },
|
||||
{ key: 'GameScene', name: '返回主游戏场景' }
|
||||
];
|
||||
|
||||
// 创建按钮网格
|
||||
const buttonWidth = 400;
|
||||
const buttonHeight = 50;
|
||||
const buttonSpacing = 20;
|
||||
const startY = 200;
|
||||
|
||||
testScenes.forEach((scene, index) => {
|
||||
// 创建按钮背景
|
||||
const button = this.add.rectangle(
|
||||
this.sys.game.config.width / 2,
|
||||
startY + index * (buttonHeight + buttonSpacing),
|
||||
buttonWidth,
|
||||
buttonHeight,
|
||||
0x333333
|
||||
).setOrigin(0.5, 0.5);
|
||||
|
||||
// 添加按钮文本
|
||||
const text = this.add.text(
|
||||
this.sys.game.config.width / 2,
|
||||
startY + index * (buttonHeight + buttonSpacing),
|
||||
scene.name,
|
||||
{
|
||||
fontSize: '18px',
|
||||
fill: '#ffffff',
|
||||
fontFamily: 'Arial'
|
||||
}
|
||||
).setOrigin(0.5, 0.5);
|
||||
|
||||
// 使按钮可交互
|
||||
button.setInteractive({
|
||||
useHandCursor: true
|
||||
});
|
||||
|
||||
// 添加点击事件
|
||||
button.on('pointerdown', () => {
|
||||
this.scene.start(scene.key);
|
||||
});
|
||||
|
||||
// 添加悬停效果
|
||||
button.on('pointerover', () => {
|
||||
button.setFillStyle(0x555555);
|
||||
});
|
||||
|
||||
button.on('pointerout', () => {
|
||||
button.setFillStyle(0x333333);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -94,7 +94,7 @@ export default class GameScene extends Phaser.Scene {
|
||||
this.miniMap = new MiniMap(this, {
|
||||
worldSize: worldSize, // 游戏世界大小
|
||||
mapSize: 200, // 小地图显示大小
|
||||
x: this.sys.game.config.width - mapSize - 10, // 小地图在屏幕上的x坐标
|
||||
x: this.sys.game.config.width - 200 - 10, // 小地图在屏幕上的x坐标
|
||||
y: 10 // 小地图在屏幕上的y坐标
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
// 测试场景1:相对移动(键盘控制)
|
||||
import Unit from '../objects/Unit.js';
|
||||
import { InputSystem } from '../systems/InputSystem.js';
|
||||
import { SelectionSystem } from '../systems/SelectionSystem.js';
|
||||
import { CommandSystem } from '../systems/CommandSystem.js';
|
||||
|
||||
export default class MovementTestScene1 extends Phaser.Scene {
|
||||
constructor() {
|
||||
super({ key: 'MovementTestScene1' });
|
||||
}
|
||||
|
||||
create() {
|
||||
// 创建玩家纹理
|
||||
if (!this.textures.exists('player')) {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = 32;
|
||||
canvas.height = 32;
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.fillStyle = '#000000ff';
|
||||
ctx.beginPath();
|
||||
ctx.arc(16, 16, 14, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
this.textures.addCanvas('player', canvas);
|
||||
}
|
||||
|
||||
// 物理世界和相机边界
|
||||
const worldSize = 1000;
|
||||
this.physics.world.setBounds(-worldSize, -worldSize, worldSize * 2, worldSize * 2);
|
||||
this.cameras.main.setBounds(-worldSize, -worldSize, worldSize * 2, worldSize * 2);
|
||||
this.cameras.main.centerOn(0, 0);
|
||||
|
||||
// 初始化系统 - 只开启键盘移动,禁用框选
|
||||
this.inputSystem = new InputSystem(this, {
|
||||
keyboard: true,
|
||||
mouse: false,
|
||||
joystick: false,
|
||||
selection: false
|
||||
});
|
||||
this.selectionSystem = new SelectionSystem(this);
|
||||
this.commandSystem = new CommandSystem(this);
|
||||
|
||||
// 创建单位组
|
||||
this.unitGroup = this.add.group();
|
||||
|
||||
// 创建一个测试单位
|
||||
const unit = new Unit(this, 0, 0, 'player');
|
||||
this.unitGroup.add(unit);
|
||||
|
||||
// 自动选中单位
|
||||
this.events.emit('select-unit', unit);
|
||||
|
||||
// 添加碰撞检测
|
||||
this.physics.add.collider(this.unitGroup, this.unitGroup);
|
||||
|
||||
// 添加场景说明文本
|
||||
const titleText = this.add.text(10, 10, '测试场景1:相对移动(键盘控制)', {
|
||||
fontSize: '20px',
|
||||
fill: '#ffffff'
|
||||
});
|
||||
titleText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||
titleText.setDepth(10); // 设置深度,确保显示在游戏元素上方
|
||||
|
||||
const moveText = this.add.text(10, 40, '使用WASD或方向键移动单位', {
|
||||
fontSize: '16px',
|
||||
fill: '#ffffff'
|
||||
});
|
||||
moveText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||
moveText.setDepth(10); // 设置深度,确保显示在游戏元素上方
|
||||
|
||||
const spaceText = this.add.text(10, 70, '按空格键返回菜单', {
|
||||
fontSize: '16px',
|
||||
fill: '#ffffff'
|
||||
});
|
||||
spaceText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||
spaceText.setDepth(10); // 设置深度,确保显示在游戏元素上方
|
||||
|
||||
// 空格键切换场景
|
||||
this.input.keyboard.on('keydown-SPACE', () => {
|
||||
this.scene.start('BootScene');
|
||||
});
|
||||
}
|
||||
|
||||
update() {
|
||||
this.inputSystem.update();
|
||||
|
||||
this.unitGroup.getChildren().forEach(unit => {
|
||||
if (unit.update) {
|
||||
unit.update();
|
||||
}
|
||||
unit.updateMovement();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
// 测试场景2:目标点移动
|
||||
import Unit from '../objects/Unit.js';
|
||||
import { InputSystem } from '../systems/InputSystem.js';
|
||||
import { SelectionSystem } from '../systems/SelectionSystem.js';
|
||||
import { CommandSystem } from '../systems/CommandSystem.js';
|
||||
|
||||
export default class MovementTestScene2 extends Phaser.Scene {
|
||||
constructor() {
|
||||
super({ key: 'MovementTestScene2' });
|
||||
}
|
||||
|
||||
create() {
|
||||
// 创建玩家纹理
|
||||
if (!this.textures.exists('player')) {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = 32;
|
||||
canvas.height = 32;
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.fillStyle = '#000000ff';
|
||||
ctx.beginPath();
|
||||
ctx.arc(16, 16, 14, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
this.textures.addCanvas('player', canvas);
|
||||
}
|
||||
|
||||
// 物理世界和相机边界
|
||||
const worldSize = 1000;
|
||||
this.physics.world.setBounds(-worldSize, -worldSize, worldSize * 2, worldSize * 2);
|
||||
this.cameras.main.setBounds(-worldSize, -worldSize, worldSize * 2, worldSize * 2);
|
||||
this.cameras.main.centerOn(0, 0);
|
||||
|
||||
// 初始化系统 - 只开启鼠标移动,禁用框选
|
||||
this.inputSystem = new InputSystem(this, {
|
||||
keyboard: false,
|
||||
mouse: true,
|
||||
joystick: false,
|
||||
selection: false
|
||||
});
|
||||
this.selectionSystem = new SelectionSystem(this);
|
||||
this.commandSystem = new CommandSystem(this);
|
||||
|
||||
// 创建单位组
|
||||
this.unitGroup = this.add.group();
|
||||
|
||||
// 创建一个测试单位
|
||||
const unit = new Unit(this, 0, 0, 'player');
|
||||
this.unitGroup.add(unit);
|
||||
|
||||
// 自动选中单位
|
||||
this.events.emit('select-unit', unit);
|
||||
|
||||
// 添加碰撞检测
|
||||
this.physics.add.collider(this.unitGroup, this.unitGroup);
|
||||
|
||||
// 添加场景说明文本
|
||||
const titleText = this.add.text(10, 10, '测试场景2:目标点移动', {
|
||||
fontSize: '20px',
|
||||
fill: '#ffffff'
|
||||
});
|
||||
titleText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||
titleText.setDepth(10); // 设置深度,确保显示在游戏元素上方
|
||||
|
||||
const moveText = this.add.text(10, 40, '点击鼠标右键或点击空地指定移动目标点', {
|
||||
fontSize: '16px',
|
||||
fill: '#ffffff'
|
||||
});
|
||||
moveText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||
moveText.setDepth(10); // 设置深度,确保显示在游戏元素上方
|
||||
|
||||
const spaceText = this.add.text(10, 70, '按空格键返回菜单', {
|
||||
fontSize: '16px',
|
||||
fill: '#ffffff'
|
||||
});
|
||||
spaceText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||
spaceText.setDepth(10); // 设置深度,确保显示在游戏元素上方
|
||||
|
||||
// 空格键切换场景
|
||||
this.input.keyboard.on('keydown-SPACE', () => {
|
||||
this.scene.start('BootScene');
|
||||
});
|
||||
}
|
||||
|
||||
update() {
|
||||
this.inputSystem.update();
|
||||
|
||||
this.unitGroup.getChildren().forEach(unit => {
|
||||
if (unit.update) {
|
||||
unit.update();
|
||||
}
|
||||
unit.updateMovement();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
// 测试场景3:单位选择与移动命令
|
||||
import Unit from '../objects/Unit.js';
|
||||
import { InputSystem } from '../systems/InputSystem.js';
|
||||
import { SelectionSystem } from '../systems/SelectionSystem.js';
|
||||
import { CommandSystem } from '../systems/CommandSystem.js';
|
||||
|
||||
export default class MovementTestScene3 extends Phaser.Scene {
|
||||
constructor() {
|
||||
super({ key: 'MovementTestScene3' });
|
||||
}
|
||||
|
||||
create() {
|
||||
// 创建玩家纹理
|
||||
if (!this.textures.exists('player')) {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = 32;
|
||||
canvas.height = 32;
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.fillStyle = '#000000ff';
|
||||
ctx.beginPath();
|
||||
ctx.arc(16, 16, 14, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
this.textures.addCanvas('player', canvas);
|
||||
}
|
||||
|
||||
// 物理世界和相机边界
|
||||
const worldSize = 1000;
|
||||
this.physics.world.setBounds(-worldSize, -worldSize, worldSize * 2, worldSize * 2);
|
||||
this.cameras.main.setBounds(-worldSize, -worldSize, worldSize * 2, worldSize * 2);
|
||||
this.cameras.main.centerOn(0, 0);
|
||||
|
||||
// 初始化系统 - 开启鼠标选择、移动和框选功能
|
||||
this.inputSystem = new InputSystem(this, {
|
||||
keyboard: false,
|
||||
mouse: true,
|
||||
joystick: false,
|
||||
selection: true
|
||||
});
|
||||
this.selectionSystem = new SelectionSystem(this);
|
||||
this.commandSystem = new CommandSystem(this);
|
||||
|
||||
// 创建单位组
|
||||
this.unitGroup = this.add.group();
|
||||
|
||||
// 创建多个测试单位
|
||||
const unitCount = 5;
|
||||
let firstUnit = null;
|
||||
for (let i = 0; i < unitCount; i++) {
|
||||
const x = -200 + i * 100;
|
||||
const y = 0;
|
||||
const unit = new Unit(this, x, y, 'player');
|
||||
this.unitGroup.add(unit);
|
||||
if (i === 0) {
|
||||
firstUnit = unit;
|
||||
}
|
||||
}
|
||||
|
||||
// 自动选中第一个单位
|
||||
if (firstUnit) {
|
||||
this.events.emit('select-unit', firstUnit);
|
||||
}
|
||||
|
||||
// 添加碰撞检测
|
||||
this.physics.add.collider(this.unitGroup, this.unitGroup);
|
||||
|
||||
// 添加场景说明文本
|
||||
const titleText = this.add.text(10, 10, '测试场景3:单位选择与移动命令', {
|
||||
fontSize: '20px',
|
||||
fill: '#ffffff'
|
||||
});
|
||||
titleText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||
titleText.setDepth(10); // 设置深度,确保显示在游戏元素上方
|
||||
|
||||
const selectText = this.add.text(10, 40, '左键点击单选单位,拖拽框选多个单位', {
|
||||
fontSize: '16px',
|
||||
fill: '#ffffff'
|
||||
});
|
||||
selectText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||
selectText.setDepth(10); // 设置深度,确保显示在游戏元素上方
|
||||
|
||||
const moveText = this.add.text(10, 70, '右键或点击空地对选中单位下达移动命令', {
|
||||
fontSize: '16px',
|
||||
fill: '#ffffff'
|
||||
});
|
||||
moveText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||
moveText.setDepth(10); // 设置深度,确保显示在游戏元素上方
|
||||
|
||||
const spaceText = this.add.text(10, 100, '按空格键返回菜单', {
|
||||
fontSize: '16px',
|
||||
fill: '#ffffff'
|
||||
});
|
||||
spaceText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||
spaceText.setDepth(10); // 设置深度,确保显示在游戏元素上方
|
||||
|
||||
// 空格键切换场景
|
||||
this.input.keyboard.on('keydown-SPACE', () => {
|
||||
this.scene.start('BootScene');
|
||||
});
|
||||
}
|
||||
|
||||
update() {
|
||||
this.inputSystem.update();
|
||||
|
||||
this.unitGroup.getChildren().forEach(unit => {
|
||||
if (unit.update) {
|
||||
unit.update();
|
||||
}
|
||||
unit.updateMovement();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
// 测试场景4:虚拟摇杆控制
|
||||
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';
|
||||
|
||||
export default class MovementTestScene4 extends Phaser.Scene {
|
||||
constructor() {
|
||||
super({ key: 'MovementTestScene4' });
|
||||
}
|
||||
|
||||
create() {
|
||||
// 创建玩家纹理
|
||||
if (!this.textures.exists('player')) {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = 32;
|
||||
canvas.height = 32;
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.fillStyle = '#000000ff';
|
||||
ctx.beginPath();
|
||||
ctx.arc(16, 16, 14, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
this.textures.addCanvas('player', canvas);
|
||||
}
|
||||
|
||||
// 物理世界和相机边界
|
||||
const worldSize = 1000;
|
||||
this.physics.world.setBounds(-worldSize, -worldSize, worldSize * 2, worldSize * 2);
|
||||
this.cameras.main.setBounds(-worldSize, -worldSize, worldSize * 2, worldSize * 2);
|
||||
this.cameras.main.centerOn(0, 0);
|
||||
|
||||
// 初始化系统 - 只开启虚拟摇杆移动,禁用框选
|
||||
this.inputSystem = new InputSystem(this, {
|
||||
keyboard: false,
|
||||
mouse: false,
|
||||
joystick: true,
|
||||
selection: false
|
||||
});
|
||||
this.selectionSystem = new SelectionSystem(this);
|
||||
this.commandSystem = new CommandSystem(this);
|
||||
this.virtualJoystick = new VirtualJoystick(this, true);
|
||||
|
||||
// 创建单位组
|
||||
this.unitGroup = this.add.group();
|
||||
|
||||
// 创建一个测试单位
|
||||
const unit = new Unit(this, 0, 0, 'player');
|
||||
this.unitGroup.add(unit);
|
||||
|
||||
// 自动选中单位
|
||||
this.events.emit('select-unit', unit);
|
||||
|
||||
// 添加碰撞检测
|
||||
this.physics.add.collider(this.unitGroup, this.unitGroup);
|
||||
|
||||
// 添加场景说明文本
|
||||
const titleText = this.add.text(10, 10, '测试场景4:虚拟摇杆控制', {
|
||||
fontSize: '20px',
|
||||
fill: '#ffffff'
|
||||
});
|
||||
titleText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||
titleText.setDepth(10); // 设置深度,确保显示在游戏元素上方
|
||||
|
||||
const moveText = this.add.text(10, 40, '使用屏幕右下角的虚拟摇杆控制单位移动', {
|
||||
fontSize: '16px',
|
||||
fill: '#ffffff'
|
||||
});
|
||||
moveText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||
moveText.setDepth(10); // 设置深度,确保显示在游戏元素上方
|
||||
|
||||
const spaceText = this.add.text(10, 70, '按空格键返回菜单', {
|
||||
fontSize: '16px',
|
||||
fill: '#ffffff'
|
||||
});
|
||||
spaceText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||
spaceText.setDepth(10); // 设置深度,确保显示在游戏元素上方
|
||||
|
||||
// 空格键切换场景
|
||||
this.input.keyboard.on('keydown-SPACE', () => {
|
||||
this.scene.start('BootScene');
|
||||
});
|
||||
}
|
||||
|
||||
update() {
|
||||
this.inputSystem.update();
|
||||
|
||||
this.unitGroup.getChildren().forEach(unit => {
|
||||
if (unit.update) {
|
||||
unit.update();
|
||||
}
|
||||
unit.updateMovement();
|
||||
});
|
||||
|
||||
if (this.virtualJoystick) {
|
||||
this.virtualJoystick.update();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
// 测试场景5:移动更新逻辑
|
||||
import Unit from '../objects/Unit.js';
|
||||
import { InputSystem } from '../systems/InputSystem.js';
|
||||
import { SelectionSystem } from '../systems/SelectionSystem.js';
|
||||
import { CommandSystem } from '../systems/CommandSystem.js';
|
||||
|
||||
export default class MovementTestScene5 extends Phaser.Scene {
|
||||
constructor() {
|
||||
super({ key: 'MovementTestScene5' });
|
||||
}
|
||||
|
||||
create() {
|
||||
// 创建玩家纹理
|
||||
if (!this.textures.exists('player')) {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = 32;
|
||||
canvas.height = 32;
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.fillStyle = '#000000ff';
|
||||
ctx.beginPath();
|
||||
ctx.arc(16, 16, 14, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
this.textures.addCanvas('player', canvas);
|
||||
}
|
||||
|
||||
// 物理世界和相机边界
|
||||
const worldSize = 1000;
|
||||
this.physics.world.setBounds(-worldSize, -worldSize, worldSize * 2, worldSize * 2);
|
||||
this.cameras.main.setBounds(-worldSize, -worldSize, worldSize * 2, worldSize * 2);
|
||||
this.cameras.main.centerOn(0, 0);
|
||||
|
||||
// 初始化系统 - 这个场景单位自动移动,禁用所有输入和框选
|
||||
this.inputSystem = new InputSystem(this, {
|
||||
keyboard: false,
|
||||
mouse: false,
|
||||
joystick: false,
|
||||
selection: false
|
||||
});
|
||||
this.selectionSystem = new SelectionSystem(this);
|
||||
this.commandSystem = new CommandSystem(this);
|
||||
|
||||
// 创建单位组
|
||||
this.unitGroup = this.add.group();
|
||||
|
||||
// 创建多个测试单位,分布在不同位置
|
||||
const unitPositions = [
|
||||
{ x: -300, y: -300 },
|
||||
{ x: 300, y: -300 },
|
||||
{ x: -300, y: 300 },
|
||||
{ x: 300, y: 300 }
|
||||
];
|
||||
|
||||
// 自动选中第一个单位
|
||||
let firstUnit = null;
|
||||
|
||||
unitPositions.forEach(pos => {
|
||||
const unit = new Unit(this, pos.x, pos.y, 'player');
|
||||
// 为每个单位设置不同的目标点,观察它们的移动
|
||||
const targetX = Math.random() * 400 - 200;
|
||||
const targetY = Math.random() * 400 - 200;
|
||||
unit.setTarget(targetX, targetY);
|
||||
this.unitGroup.add(unit);
|
||||
|
||||
// 保存第一个单位的引用
|
||||
if (!firstUnit) {
|
||||
firstUnit = unit;
|
||||
}
|
||||
});
|
||||
|
||||
// 自动选中第一个单位
|
||||
if (firstUnit) {
|
||||
this.events.emit('select-unit', firstUnit);
|
||||
}
|
||||
|
||||
// 添加碰撞检测
|
||||
this.physics.add.collider(this.unitGroup, this.unitGroup);
|
||||
|
||||
// 添加场景说明文本
|
||||
const titleText = this.add.text(10, 10, '测试场景5:移动更新逻辑', {
|
||||
fontSize: '20px',
|
||||
fill: '#ffffff'
|
||||
});
|
||||
titleText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||
titleText.setDepth(10); // 设置深度,确保显示在游戏元素上方
|
||||
|
||||
const autoMoveText = this.add.text(10, 40, '观察单位自动向随机目标点移动', {
|
||||
fontSize: '16px',
|
||||
fill: '#ffffff'
|
||||
});
|
||||
autoMoveText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||
autoMoveText.setDepth(10); // 设置深度,确保显示在游戏元素上方
|
||||
|
||||
const pathText = this.add.text(10, 70, '单位会自动计算路径并在到达后停止', {
|
||||
fontSize: '16px',
|
||||
fill: '#ffffff'
|
||||
});
|
||||
pathText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||
pathText.setDepth(10); // 设置深度,确保显示在游戏元素上方
|
||||
|
||||
const spaceText = this.add.text(10, 100, '按空格键返回菜单', {
|
||||
fontSize: '16px',
|
||||
fill: '#ffffff'
|
||||
});
|
||||
spaceText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||
spaceText.setDepth(10); // 设置深度,确保显示在游戏元素上方
|
||||
|
||||
// 空格键切换场景
|
||||
this.input.keyboard.on('keydown-SPACE', () => {
|
||||
this.scene.start('BootScene');
|
||||
});
|
||||
}
|
||||
|
||||
update() {
|
||||
this.inputSystem.update();
|
||||
|
||||
this.unitGroup.getChildren().forEach(unit => {
|
||||
if (unit.update) {
|
||||
unit.update();
|
||||
}
|
||||
unit.updateMovement();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
// 测试场景6:小地图测试
|
||||
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 { MiniMap } from '../ui/MiniMap.js';
|
||||
|
||||
export default class MovementTestScene6 extends Phaser.Scene {
|
||||
constructor() {
|
||||
super({ key: 'MovementTestScene6' });
|
||||
}
|
||||
|
||||
create() {
|
||||
// 创建玩家纹理
|
||||
if (!this.textures.exists('player')) {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = 32;
|
||||
canvas.height = 32;
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.fillStyle = '#000000ff';
|
||||
ctx.beginPath();
|
||||
ctx.arc(16, 16, 14, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
this.textures.addCanvas('player', canvas);
|
||||
}
|
||||
|
||||
// 物理世界和相机边界
|
||||
const worldSize = 2000;
|
||||
this.physics.world.setBounds(-worldSize, -worldSize, worldSize * 2, worldSize * 2);
|
||||
this.cameras.main.setBounds(-worldSize, -worldSize, worldSize * 2, worldSize * 2);
|
||||
this.cameras.main.centerOn(0, 0);
|
||||
|
||||
// 初始化系统 - 开启鼠标移动和框选
|
||||
this.inputSystem = new InputSystem(this, {
|
||||
keyboard: true,
|
||||
mouse: true,
|
||||
joystick: false,
|
||||
selection: true
|
||||
});
|
||||
this.selectionSystem = new SelectionSystem(this);
|
||||
this.commandSystem = new CommandSystem(this);
|
||||
|
||||
// 创建单位组
|
||||
this.unitGroup = this.add.group();
|
||||
|
||||
// 创建多个测试单位,分布在不同位置
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const x = Math.random() * 2000 - 1000;
|
||||
const y = Math.random() * 2000 - 1000;
|
||||
const unit = new Unit(this, x, y, 'player');
|
||||
this.unitGroup.add(unit);
|
||||
|
||||
// 自动选中第一个单位
|
||||
if (i === 0) {
|
||||
this.events.emit('select-unit', unit);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加碰撞检测
|
||||
this.physics.add.collider(this.unitGroup, this.unitGroup);
|
||||
|
||||
// 创建小地图
|
||||
this.miniMap = new MiniMap(this, {
|
||||
worldSize: worldSize,
|
||||
mapSize: 200,
|
||||
x: this.sys.game.config.width - 210,
|
||||
y: 10
|
||||
});
|
||||
|
||||
// 添加场景说明文本
|
||||
const titleText = this.add.text(10, 10, '测试场景6:小地图测试', {
|
||||
fontSize: '20px',
|
||||
fill: '#ffffff'
|
||||
});
|
||||
titleText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||
titleText.setDepth(10); // 设置深度,确保显示在游戏元素上方
|
||||
|
||||
const miniMapText = this.add.text(10, 40, '使用鼠标拖拽小地图或点击小地图快速移动视角', {
|
||||
fontSize: '16px',
|
||||
fill: '#ffffff'
|
||||
});
|
||||
miniMapText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||
miniMapText.setDepth(10); // 设置深度,确保显示在游戏元素上方
|
||||
|
||||
const controlsText = this.add.text(10, 70, 'WASD或方向键移动视角,鼠标右键移动单位', {
|
||||
fontSize: '16px',
|
||||
fill: '#ffffff'
|
||||
});
|
||||
controlsText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||
controlsText.setDepth(10); // 设置深度,确保显示在游戏元素上方
|
||||
|
||||
const spaceText = this.add.text(10, 100, '按空格键返回菜单', {
|
||||
fontSize: '16px',
|
||||
fill: '#ffffff'
|
||||
});
|
||||
spaceText.setScrollFactor(0, 0); // 固定在屏幕上,不随相机移动
|
||||
spaceText.setDepth(10); // 设置深度,确保显示在游戏元素上方
|
||||
|
||||
// 空格键切换场景
|
||||
this.input.keyboard.on('keydown-SPACE', () => {
|
||||
this.scene.start('BootScene');
|
||||
});
|
||||
|
||||
// 添加相机移动控制
|
||||
this.cameraSpeed = 2;
|
||||
this.cameraKeys = this.input.keyboard.addKeys({
|
||||
up: Phaser.Input.Keyboard.KeyCodes.W,
|
||||
down: Phaser.Input.Keyboard.KeyCodes.S,
|
||||
left: Phaser.Input.Keyboard.KeyCodes.A,
|
||||
right: Phaser.Input.Keyboard.KeyCodes.D
|
||||
});
|
||||
}
|
||||
|
||||
update() {
|
||||
this.inputSystem.update();
|
||||
|
||||
// 更新相机移动
|
||||
if (this.cameraKeys.left.isDown) {
|
||||
this.cameras.main.scrollX -= this.cameraSpeed;
|
||||
}
|
||||
if (this.cameraKeys.right.isDown) {
|
||||
this.cameras.main.scrollX += this.cameraSpeed;
|
||||
}
|
||||
if (this.cameraKeys.up.isDown) {
|
||||
this.cameras.main.scrollY -= this.cameraSpeed;
|
||||
}
|
||||
if (this.cameraKeys.down.isDown) {
|
||||
this.cameras.main.scrollY += this.cameraSpeed;
|
||||
}
|
||||
|
||||
this.unitGroup.getChildren().forEach(unit => {
|
||||
if (unit.update) {
|
||||
unit.update();
|
||||
}
|
||||
unit.updateMovement();
|
||||
});
|
||||
|
||||
// 更新小地图显示
|
||||
if (this.miniMap) {
|
||||
this.miniMap.update();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,10 @@ export class CommandSystem {
|
||||
|
||||
// 遍历所有目标单位,调用它们的moveRelative方法进行相对移动
|
||||
targets.forEach(unit => {
|
||||
unit.moveRelative(dir.x, dir.y);
|
||||
// 确保unit是有效的Unit实例
|
||||
if (unit && typeof unit.moveRelative === 'function') {
|
||||
unit.moveRelative(dir.x, dir.y);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -37,7 +40,10 @@ export class CommandSystem {
|
||||
|
||||
// 遍历所有目标单位,调用它们的setTarget方法设置移动目标点
|
||||
targets.forEach(unit => {
|
||||
unit.setTarget(x, y); // ← 使用单位的新接口方法
|
||||
// 确保unit是有效的Unit实例
|
||||
if (unit && typeof unit.setTarget === 'function') {
|
||||
unit.setTarget(x, y); // ← 使用单位的新接口方法
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,11 @@ export let activeUnit = null;
|
||||
// 初始值为空数组,表示没有选中任何单位
|
||||
export let selectedUnits = [];
|
||||
|
||||
// 相机锁定状态
|
||||
// 用于控制相机是否锁定到选中单位
|
||||
// 初始值为false,表示未锁定
|
||||
export let cameraLocked = false;
|
||||
|
||||
// 设置当前激活单位的函数
|
||||
// @param {Object} unit - 要设置为激活状态的单位对象
|
||||
export function setActiveUnit(unit) {
|
||||
@@ -41,4 +46,15 @@ export function clearSelection() {
|
||||
activeUnit = null;
|
||||
// 将选中的单位列表重置为空数组(取消所有选择)
|
||||
selectedUnits = [];
|
||||
// 取消相机锁定
|
||||
setCameraLocked(false);
|
||||
}
|
||||
|
||||
// 设置相机锁定状态的函数
|
||||
export function setCameraLocked(locked) {
|
||||
cameraLocked = locked;
|
||||
// 将锁定状态暴露到window对象上,以便InputSystem访问
|
||||
if (typeof window !== 'undefined') {
|
||||
window.cameraLocked = locked;
|
||||
}
|
||||
}
|
||||
+145
-38
@@ -7,7 +7,8 @@ import { clearSelection, setActiveUnit } from './ControlMode.js';
|
||||
export class InputSystem {
|
||||
// 构造函数:初始化输入系统
|
||||
// @param {Object} scene - 游戏场景实例
|
||||
constructor(scene) {
|
||||
// @param {Object} config - 输入配置选项
|
||||
constructor(scene, config = {}) {
|
||||
// 保存场景引用,用于访问游戏世界和发送事件
|
||||
this.scene = scene;
|
||||
|
||||
@@ -15,6 +16,14 @@ export class InputSystem {
|
||||
// 初始值为null,表示当前没有进行框选操作
|
||||
this.selectionStart = null;
|
||||
|
||||
// 移动方式开关配置
|
||||
this.enabledModes = {
|
||||
keyboard: config.keyboard !== false, // 键盘控制
|
||||
mouse: config.mouse !== false, // 鼠标点击移动
|
||||
joystick: config.joystick !== false, // 虚拟摇杆控制
|
||||
selection: config.selection !== false // 框选功能
|
||||
};
|
||||
|
||||
// 初始化键盘输入
|
||||
// 获取Phaser的键盘输入管理器
|
||||
const kb = scene.input.keyboard;
|
||||
@@ -53,28 +62,31 @@ export class InputSystem {
|
||||
|
||||
// update方法:每帧自动调用(约60次/秒),用于处理持续的输入
|
||||
update() {
|
||||
// 创建一个方向对象,用于存储移动方向
|
||||
const dir = { x: 0, y: 0 };
|
||||
|
||||
// 检查左移键是否被按下,如果是则x方向减1(向左移动)
|
||||
if (this.isKeyDown(this.keys.left)) dir.x -= 1;
|
||||
// 检查右移键是否被按下,如果是则x方向加1(向右移动)
|
||||
if (this.isKeyDown(this.keys.right)) dir.x += 1;
|
||||
// 检查上移键是否被按下,如果是则y方向减1(向上移动)
|
||||
if (this.isKeyDown(this.keys.up)) dir.y -= 1;
|
||||
// 检查下移键是否被按下,如果是则y方向加1(向下移动)
|
||||
if (this.isKeyDown(this.keys.down)) dir.y += 1;
|
||||
// 只有在键盘模式启用时才处理键盘输入
|
||||
if (this.enabledModes.keyboard) {
|
||||
// 创建一个方向对象,用于存储移动方向
|
||||
const dir = { x: 0, y: 0 };
|
||||
|
||||
// 检查左移键是否被按下,如果是则x方向减1(向左移动)
|
||||
if (this.isKeyDown(this.keys.left)) dir.x -= 1;
|
||||
// 检查右移键是否被按下,如果是则x方向加1(向右移动)
|
||||
if (this.isKeyDown(this.keys.right)) dir.x += 1;
|
||||
// 检查上移键是否被按下,如果是则y方向减1(向上移动)
|
||||
if (this.isKeyDown(this.keys.up)) dir.y -= 1;
|
||||
// 检查下移键是否被按下,如果是则y方向加1(向下移动)
|
||||
if (this.isKeyDown(this.keys.down)) dir.y += 1;
|
||||
|
||||
// 无论是否有按键按下,都发送移动事件
|
||||
// 当dir.x和dir.y都为0时,单位会停止移动
|
||||
this.scene.events.emit('move-relative', dir);
|
||||
// 无论是否有按键按下,都发送移动事件
|
||||
// 当dir.x和dir.y都为0时,单位会停止移动
|
||||
this.scene.events.emit('move-relative', dir);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理鼠标/触摸按下事件
|
||||
// @param {Object} pointer - 指针事件对象,包含鼠标/触摸的位置等信息
|
||||
onPointerDown(pointer) {
|
||||
// 检查鼠标是否在小地图或摇杆上,如果是则不处理框选
|
||||
if (this.isMouseOnMiniMap(pointer) || this.isMouseOnJoystick(pointer)) {
|
||||
// 检查鼠标是否在小地图、摇杆或侧边栏上,如果是则不处理
|
||||
if (this.isMouseOnMiniMap(pointer) || this.isMouseOnJoystick(pointer) || this.isMouseOnSidebar(pointer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -98,34 +110,60 @@ export class InputSystem {
|
||||
}
|
||||
});
|
||||
|
||||
// 检查是否点中了某个物品
|
||||
let clickedItem = null;
|
||||
// 遍历场景中的所有物品
|
||||
if (this.scene.itemManager && this.scene.itemManager.getActiveItems) {
|
||||
this.scene.itemManager.getActiveItems().forEach(item => {
|
||||
// 获取物品的边界矩形
|
||||
const bounds = item.getBounds();
|
||||
// 检查鼠标世界坐标是否在物品的边界内
|
||||
if (worldX >= bounds.x && worldX <= bounds.right &&
|
||||
worldY >= bounds.y && worldY <= bounds.bottom) {
|
||||
// 如果点中了物品,存储该物品
|
||||
clickedItem = item;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (clickedUnit) {
|
||||
// 如果点中了单位,发送选择单位事件
|
||||
this.scene.events.emit('select-unit', clickedUnit);
|
||||
} else {
|
||||
// 如果没有点中单位,开始框选或移动指令
|
||||
// 存储点击位置,用于判断是否是拖动操作
|
||||
this.dragStart = { x: worldX, y: worldY, clickedUnit: clickedUnit };
|
||||
// 不设置selectionStart,避免触发框选
|
||||
this.selectionStart = null;
|
||||
} else if (clickedItem) {
|
||||
// 如果点中了物品,不执行后续操作,避免触发移动
|
||||
this.selectionStart = null;
|
||||
this.dragStart = null;
|
||||
} else if (this.enabledModes.selection && !this.isCameraLocked()) {
|
||||
// 如果没有点中单位,且框选功能启用,且相机未锁定,开始框选操作
|
||||
// 存储框选开始时的世界坐标
|
||||
this.selectionStart = { x: worldX, y: worldY };
|
||||
// 发送选择开始事件
|
||||
this.scene.events.emit('selection-start', this.selectionStart);
|
||||
// 没有点中单位,清除dragStart
|
||||
this.dragStart = null;
|
||||
}
|
||||
} else if (pointer.rightButtonDown()) {
|
||||
// 如果是右键按下,发送移动到指定点的事件
|
||||
} else if (pointer.rightButtonDown() && this.enabledModes.mouse) {
|
||||
// 如果是右键按下且鼠标模式启用,发送移动到指定点的事件
|
||||
const worldX = pointer.worldX;
|
||||
const worldY = pointer.worldY;
|
||||
this.scene.events.emit('move-to-point', worldX, worldY);
|
||||
// 右键点击,清除dragStart
|
||||
this.dragStart = null;
|
||||
}
|
||||
}
|
||||
|
||||
// 处理鼠标/触摸移动事件
|
||||
// @param {Object} pointer - 指针事件对象
|
||||
onPointerMove(pointer) {
|
||||
// 检查鼠标是否在小地图或摇杆上,如果是则不处理框选
|
||||
if (this.isMouseOnMiniMap(pointer) || this.isMouseOnJoystick(pointer)) {
|
||||
// 检查鼠标是否在小地图、摇杆或侧边栏上,如果是则不处理
|
||||
if (this.isMouseOnMiniMap(pointer) || this.isMouseOnJoystick(pointer) || this.isMouseOnSidebar(pointer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果当前正在进行框选操作
|
||||
if (this.selectionStart) {
|
||||
// 如果当前正在进行框选操作且框选功能启用
|
||||
if (this.selectionStart && this.enabledModes.selection) {
|
||||
// 获取当前鼠标在游戏世界中的坐标
|
||||
const currentWorldX = pointer.worldX;
|
||||
const currentWorldY = pointer.worldY;
|
||||
@@ -143,23 +181,48 @@ export class InputSystem {
|
||||
Math.abs(currentWorldY - this.selectionStart.y)
|
||||
);
|
||||
|
||||
// 发送选择更新事件,更新框选区域的显示
|
||||
this.scene.events.emit('selection-update', rect);
|
||||
// 只有当框选区域足够大时才发送选择更新事件
|
||||
if (rect.width > 5 || rect.height > 5) {
|
||||
// 发送选择更新事件,更新框选区域的显示
|
||||
this.scene.events.emit('selection-start', this.selectionStart);
|
||||
this.scene.events.emit('selection-update', rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理鼠标/触摸释放事件
|
||||
// @param {Object} pointer - 指针事件对象
|
||||
onPointerUp(pointer) {
|
||||
// 检查鼠标是否在小地图或摇杆上,如果是则不处理框选
|
||||
if (this.isMouseOnMiniMap(pointer) || this.isMouseOnJoystick(pointer)) {
|
||||
// 重置框选起始点
|
||||
// 检查鼠标是否在小地图、摇杆或侧边栏上,如果是则不处理
|
||||
if (this.isMouseOnMiniMap(pointer) || this.isMouseOnJoystick(pointer) || this.isMouseOnSidebar(pointer)) {
|
||||
// 重置框选起始点和拖动状态
|
||||
this.selectionStart = null;
|
||||
this.dragStart = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果当前正在进行框选操作
|
||||
if (this.selectionStart) {
|
||||
// 处理拖动单位的逻辑
|
||||
if (this.dragStart) {
|
||||
// 获取当前鼠标在游戏世界中的坐标
|
||||
const currentWorldX = pointer.worldX;
|
||||
const currentWorldY = pointer.worldY;
|
||||
|
||||
// 计算拖动距离
|
||||
const dx = currentWorldX - this.dragStart.x;
|
||||
const dy = currentWorldY - this.dragStart.y;
|
||||
const dragDistance = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
// 如果拖动距离超过阈值(10像素),视为拖动操作
|
||||
if (dragDistance > 10 && this.enabledModes.mouse) {
|
||||
// 发送移动到指定点的事件,让单位移动到释放位置
|
||||
this.scene.events.emit('move-to-point', currentWorldX, currentWorldY);
|
||||
}
|
||||
|
||||
// 重置拖动状态
|
||||
this.dragStart = null;
|
||||
}
|
||||
// 如果当前正在进行框选操作且框选功能启用
|
||||
else if (this.selectionStart && this.enabledModes.selection) {
|
||||
// 获取当前鼠标在游戏世界中的坐标
|
||||
const currentWorldX = pointer.worldX;
|
||||
const currentWorldY = pointer.worldY;
|
||||
@@ -173,8 +236,8 @@ export class InputSystem {
|
||||
);
|
||||
|
||||
// 如果框选区域太小(宽度和高度都小于10像素),视为点击地面移动
|
||||
if (rect.width < 10 && rect.height < 10) {
|
||||
// 发送移动到指定点的事件
|
||||
if (rect.width < 10 && rect.height < 10 && this.enabledModes.mouse) {
|
||||
// 发送移动到指定点的事件(仅在鼠标模式启用时)
|
||||
this.scene.events.emit('move-to-point', pointer.worldX, pointer.worldY);
|
||||
} else {
|
||||
// 否则执行框选操作
|
||||
@@ -203,11 +266,12 @@ export class InputSystem {
|
||||
const mouseX = pointer.x;
|
||||
const mouseY = pointer.y;
|
||||
|
||||
// 计算小地图的边界
|
||||
// 计算小地图的边界(包括锁定按钮区域)
|
||||
const mapLeft = miniMap.x; // 小地图左边界
|
||||
const mapRight = miniMap.x + miniMap.mapSize; // 小地图右边界
|
||||
const mapTop = miniMap.y; // 小地图上边界
|
||||
const mapBottom = miniMap.y + miniMap.mapSize; // 小地图下边界
|
||||
// 小地图下边界,包括锁定按钮(按钮高度25,边距5)
|
||||
const mapBottom = miniMap.y + miniMap.mapSize + 30;
|
||||
|
||||
// 检查鼠标坐标是否在小地图边界内
|
||||
return mouseX >= mapLeft && mouseX <= mapRight &&
|
||||
@@ -242,4 +306,47 @@ export class InputSystem {
|
||||
return mouseX >= joystickLeft && mouseX <= joystickRight &&
|
||||
mouseY >= joystickTop && mouseY <= joystickBottom;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查相机是否被锁定到某个角色
|
||||
* @returns {boolean} - 如果相机被锁定返回true,否则返回false
|
||||
*/
|
||||
isCameraLocked() {
|
||||
// 检查ControlMode模块是否存在且cameraLocked属性已定义
|
||||
if (typeof window !== 'undefined' && window.cameraLocked !== undefined) {
|
||||
return window.cameraLocked;
|
||||
}
|
||||
|
||||
// 检查场景中是否有小地图且处于锁定状态
|
||||
if (this.scene.miniMap && this.scene.miniMap.isLocked) {
|
||||
return this.scene.miniMap.isLocked;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查鼠标是否在侧边栏上
|
||||
* @param {Phaser.Input.Pointer} pointer - 指针事件对象
|
||||
* @returns {boolean} - 如果鼠标在侧边栏上返回true,否则返回false
|
||||
*/
|
||||
isMouseOnSidebar(pointer) {
|
||||
// 检查场景中是否有侧边栏
|
||||
if (!this.scene.sidebar) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 获取侧边栏实例
|
||||
const sidebar = this.scene.sidebar;
|
||||
// 获取鼠标在屏幕上的坐标
|
||||
const mouseX = pointer.x;
|
||||
const mouseY = pointer.y;
|
||||
|
||||
// 获取侧边栏的边界
|
||||
const bounds = sidebar.getBounds();
|
||||
|
||||
// 检查鼠标坐标是否在侧边栏边界内
|
||||
return mouseX >= bounds.x && mouseX <= bounds.right &&
|
||||
mouseY >= bounds.y && mouseY <= bounds.bottom;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,354 @@
|
||||
// 移动逻辑测试场景
|
||||
// 本文件将当前移动逻辑拆分为不同的测试场景
|
||||
|
||||
// 测试场景1: 相对移动(键盘控制)
|
||||
// 测试内容:
|
||||
// 1. 键盘方向键输入检测
|
||||
// 2. 方向向量归一化
|
||||
// 3. 移动速度控制
|
||||
// 4. 停止移动逻辑
|
||||
function testRelativeMovement() {
|
||||
console.log("=== 测试场景1: 相对移动(键盘控制) ===");
|
||||
|
||||
// 模拟键盘输入
|
||||
const testCases = [
|
||||
{ dir: { x: 1, y: 0 }, description: "向右移动" },
|
||||
{ dir: { x: 0, y: 1 }, description: "向下移动" },
|
||||
{ dir: { x: -1, y: 0 }, description: "向左移动" },
|
||||
{ dir: { x: 0, y: -1 }, description: "向上移动" },
|
||||
{ dir: { x: 1, y: 1 }, description: "向右下对角线移动" },
|
||||
{ dir: { x: 0, y: 0 }, description: "停止移动" }
|
||||
];
|
||||
|
||||
testCases.forEach(testCase => {
|
||||
console.log(`测试: ${testCase.description}`);
|
||||
console.log(`输入方向: (${testCase.dir.x}, ${testCase.dir.y})`);
|
||||
|
||||
// 计算归一化向量
|
||||
const magnitude = Math.sqrt(testCase.dir.x * testCase.dir.x + testCase.dir.y * testCase.dir.y);
|
||||
let normalizedDir = { x: 0, y: 0 };
|
||||
if (magnitude > 0) {
|
||||
normalizedDir = {
|
||||
x: testCase.dir.x / magnitude,
|
||||
y: testCase.dir.y / magnitude
|
||||
};
|
||||
}
|
||||
|
||||
console.log(`归一化方向: (${normalizedDir.x.toFixed(2)}, ${normalizedDir.y.toFixed(2)})`);
|
||||
|
||||
// 计算速度向量
|
||||
const speed = 150;
|
||||
const velocity = {
|
||||
x: normalizedDir.x * speed,
|
||||
y: normalizedDir.y * speed
|
||||
};
|
||||
|
||||
console.log(`速度向量: (${velocity.x.toFixed(2)}, ${velocity.y.toFixed(2)})`);
|
||||
console.log("------------------------");
|
||||
});
|
||||
}
|
||||
|
||||
// 测试场景2: 目标点移动
|
||||
// 测试内容:
|
||||
// 1. 目标点设置
|
||||
// 2. 世界边界限制
|
||||
// 3. 移动路径计算
|
||||
// 4. 到达目标点检测
|
||||
function testMoveToPoint() {
|
||||
console.log("=== 测试场景2: 目标点移动 ===");
|
||||
|
||||
// 模拟世界边界
|
||||
const worldBounds = { x: 0, y: 0, width: 1000, height: 800 };
|
||||
|
||||
// 测试用例
|
||||
const testCases = [
|
||||
{
|
||||
unitPos: { x: 100, y: 100 },
|
||||
targetPos: { x: 500, y: 400 },
|
||||
description: "正常范围内移动"
|
||||
},
|
||||
{
|
||||
unitPos: { x: 100, y: 100 },
|
||||
targetPos: { x: 1200, y: 900 },
|
||||
description: "超出世界边界的目标点"
|
||||
},
|
||||
{
|
||||
unitPos: { x: 500, y: 400 },
|
||||
targetPos: { x: 505, y: 405 },
|
||||
description: "接近当前位置的目标点"
|
||||
}
|
||||
];
|
||||
|
||||
testCases.forEach(testCase => {
|
||||
console.log(`测试: ${testCase.description}`);
|
||||
console.log(`单位位置: (${testCase.unitPos.x}, ${testCase.unitPos.y})`);
|
||||
console.log(`目标位置: (${testCase.targetPos.x}, ${testCase.targetPos.y})`);
|
||||
|
||||
// 应用世界边界限制
|
||||
const clampedTarget = {
|
||||
x: Math.max(worldBounds.x, Math.min(worldBounds.width, testCase.targetPos.x)),
|
||||
y: Math.max(worldBounds.y, Math.min(worldBounds.height, testCase.targetPos.y))
|
||||
};
|
||||
|
||||
console.log(`限制后目标: (${clampedTarget.x}, ${clampedTarget.y})`);
|
||||
|
||||
// 计算距离和方向
|
||||
const dx = clampedTarget.x - testCase.unitPos.x;
|
||||
const dy = clampedTarget.y - testCase.unitPos.y;
|
||||
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
console.log(`距离: ${distance.toFixed(2)}`);
|
||||
|
||||
// 检查是否到达目标
|
||||
const arrived = distance < 5;
|
||||
console.log(`是否到达目标: ${arrived}`);
|
||||
|
||||
if (!arrived) {
|
||||
// 计算移动方向和速度
|
||||
const angle = Math.atan2(dy, dx);
|
||||
const speed = 150;
|
||||
const velocity = {
|
||||
x: Math.cos(angle) * speed,
|
||||
y: Math.sin(angle) * speed
|
||||
};
|
||||
console.log(`移动速度: (${velocity.x.toFixed(2)}, ${velocity.y.toFixed(2)})`);
|
||||
}
|
||||
|
||||
console.log("------------------------");
|
||||
});
|
||||
}
|
||||
|
||||
// 测试场景3: 单位选择与移动命令
|
||||
// 测试内容:
|
||||
// 1. 单个单位选择
|
||||
// 2. 多个单位选择
|
||||
// 3. 移动命令分发
|
||||
function testSelectionAndCommand() {
|
||||
console.log("=== 测试场景3: 单位选择与移动命令 ===");
|
||||
|
||||
// 模拟单位和选择状态
|
||||
const mockUnits = [
|
||||
{ id: 1, name: "单位1", moveRelative: () => {}, setTarget: () => {} },
|
||||
{ id: 2, name: "单位2", moveRelative: () => {}, setTarget: () => {} },
|
||||
{ id: 3, name: "单位3", moveRelative: () => {}, setTarget: () => {} }
|
||||
];
|
||||
|
||||
// 测试用例
|
||||
const testCases = [
|
||||
{
|
||||
selectedUnits: [],
|
||||
activeUnit: mockUnits[0],
|
||||
command: "move-relative",
|
||||
dir: { x: 1, y: 0 },
|
||||
description: "无选中单位,使用激活单位"
|
||||
},
|
||||
{
|
||||
selectedUnits: [mockUnits[0], mockUnits[1]],
|
||||
activeUnit: mockUnits[0],
|
||||
command: "move-to-point",
|
||||
target: { x: 500, y: 400 },
|
||||
description: "多个选中单位,执行目标点移动"
|
||||
},
|
||||
{
|
||||
selectedUnits: [mockUnits[2]],
|
||||
activeUnit: mockUnits[0],
|
||||
command: "move-relative",
|
||||
dir: { x: 0, y: -1 },
|
||||
description: "单个选中单位,执行相对移动"
|
||||
}
|
||||
];
|
||||
|
||||
testCases.forEach(testCase => {
|
||||
console.log(`测试: ${testCase.description}`);
|
||||
console.log(`激活单位: ${testCase.activeUnit.name}`);
|
||||
console.log(`选中单位: ${testCase.selectedUnits.map(u => u.name).join(", ")}`);
|
||||
|
||||
// 确定命令目标
|
||||
const targets = testCase.selectedUnits.length > 0 ? testCase.selectedUnits : [testCase.activeUnit];
|
||||
console.log(`命令目标: ${targets.map(u => u.name).join(", ")}`);
|
||||
|
||||
if (testCase.command === "move-relative") {
|
||||
console.log(`执行相对移动: (${testCase.dir.x}, ${testCase.dir.y})`);
|
||||
} else if (testCase.command === "move-to-point") {
|
||||
console.log(`执行目标点移动: (${testCase.target.x}, ${testCase.target.y})`);
|
||||
}
|
||||
|
||||
console.log("------------------------");
|
||||
});
|
||||
}
|
||||
|
||||
// 测试场景4: 虚拟摇杆控制
|
||||
// 测试内容:
|
||||
// 1. 摇杆激活检测
|
||||
// 2. 摇杆方向计算
|
||||
// 3. 归一化方向输出
|
||||
// 4. 停止移动处理
|
||||
function testVirtualJoystick() {
|
||||
console.log("=== 测试场景4: 虚拟摇杆控制 ===");
|
||||
|
||||
// 模拟摇杆配置
|
||||
const joystickConfig = { centerX: 100, centerY: 100, radius: 50 };
|
||||
|
||||
// 测试用例
|
||||
const testCases = [
|
||||
{
|
||||
pointerPos: { x: 120, y: 100 },
|
||||
description: "向右轻微拖动"
|
||||
},
|
||||
{
|
||||
pointerPos: { x: 160, y: 100 },
|
||||
description: "向右拖动超出摇杆范围"
|
||||
},
|
||||
{
|
||||
pointerPos: { x: 135, y: 135 },
|
||||
description: "向右下对角线拖动"
|
||||
},
|
||||
{
|
||||
pointerPos: { x: 100, y: 100 },
|
||||
description: "摇杆中心位置(停止)"
|
||||
}
|
||||
];
|
||||
|
||||
testCases.forEach(testCase => {
|
||||
console.log(`测试: ${testCase.description}`);
|
||||
console.log(`摇杆中心: (${joystickConfig.centerX}, ${joystickConfig.centerY})`);
|
||||
console.log(`指针位置: (${testCase.pointerPos.x}, ${testCase.pointerPos.y})`);
|
||||
|
||||
// 计算方向和距离
|
||||
const dx = testCase.pointerPos.x - joystickConfig.centerX;
|
||||
const dy = testCase.pointerPos.y - joystickConfig.centerY;
|
||||
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
console.log(`距离: ${distance.toFixed(2)}`);
|
||||
|
||||
// 限制在摇杆范围内
|
||||
let limitedDx = dx;
|
||||
let limitedDy = dy;
|
||||
if (distance > joystickConfig.radius) {
|
||||
const angle = Math.atan2(dy, dx);
|
||||
limitedDx = Math.cos(angle) * joystickConfig.radius;
|
||||
limitedDy = Math.sin(angle) * joystickConfig.radius;
|
||||
console.log(`限制后偏移: (${limitedDx.toFixed(2)}, ${limitedDy.toFixed(2)})`);
|
||||
}
|
||||
|
||||
// 计算归一化方向
|
||||
const dirX = limitedDx / joystickConfig.radius;
|
||||
const dirY = limitedDy / joystickConfig.radius;
|
||||
console.log(`归一化方向: (${dirX.toFixed(2)}, ${dirY.toFixed(2)})`);
|
||||
|
||||
console.log("------------------------");
|
||||
});
|
||||
}
|
||||
|
||||
// 测试场景5: 移动更新逻辑
|
||||
// 测试内容:
|
||||
// 1. 每帧移动更新
|
||||
// 2. 位置更新计算
|
||||
// 3. 目标到达检测
|
||||
function testMovementUpdate() {
|
||||
console.log("=== 测试场景5: 移动更新逻辑 ===");
|
||||
|
||||
// 模拟单位和时间增量
|
||||
const mockUnit = {
|
||||
x: 100,
|
||||
y: 100,
|
||||
target: { x: 500, y: 400 },
|
||||
setVelocity: (vx, vy) => { mockUnit.velocity = { x: vx, y: vy }; },
|
||||
setPosition: (x, y) => { mockUnit.x = x; mockUnit.y = y; }
|
||||
};
|
||||
|
||||
const speed = 150;
|
||||
const deltaTime = 1/60; // 60fps
|
||||
|
||||
console.log("初始状态:");
|
||||
console.log(`单位位置: (${mockUnit.x}, ${mockUnit.y})`);
|
||||
console.log(`目标位置: (${mockUnit.target.x}, ${mockUnit.target.y})`);
|
||||
|
||||
// 模拟几帧的移动更新
|
||||
for (let i = 0; i < 5; i++) {
|
||||
console.log(`\n第${i+1}帧更新:`);
|
||||
|
||||
const dx = mockUnit.target.x - mockUnit.x;
|
||||
const dy = mockUnit.target.y - mockUnit.y;
|
||||
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
console.log(`距离目标: ${distance.toFixed(2)}`);
|
||||
|
||||
if (distance < 5) {
|
||||
// 到达目标
|
||||
mockUnit.setPosition(mockUnit.target.x, mockUnit.target.y);
|
||||
mockUnit.setVelocity(0, 0);
|
||||
mockUnit.target = null;
|
||||
console.log("到达目标,停止移动");
|
||||
break;
|
||||
} else {
|
||||
// 继续移动
|
||||
const angle = Math.atan2(dy, dx);
|
||||
const velocity = {
|
||||
x: Math.cos(angle) * speed,
|
||||
y: Math.sin(angle) * speed
|
||||
};
|
||||
|
||||
mockUnit.setVelocity(velocity.x, velocity.y);
|
||||
|
||||
// 更新位置
|
||||
mockUnit.x += velocity.x * deltaTime;
|
||||
mockUnit.y += velocity.y * deltaTime;
|
||||
|
||||
console.log(`速度: (${velocity.x.toFixed(2)}, ${velocity.y.toFixed(2)})`);
|
||||
console.log(`新位置: (${mockUnit.x.toFixed(2)}, ${mockUnit.y.toFixed(2)})`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log("\n最终状态:");
|
||||
console.log(`单位位置: (${mockUnit.x}, ${mockUnit.y})`);
|
||||
console.log(`目标状态: ${mockUnit.target ? "有目标" : "无目标"}`);
|
||||
}
|
||||
|
||||
// 运行所有测试场景
|
||||
function runAllTests() {
|
||||
console.log("开始运行移动逻辑测试场景\n");
|
||||
|
||||
testRelativeMovement();
|
||||
console.log("\n" + "=".repeat(50) + "\n");
|
||||
|
||||
testMoveToPoint();
|
||||
console.log("\n" + "=".repeat(50) + "\n");
|
||||
|
||||
testSelectionAndCommand();
|
||||
console.log("\n" + "=".repeat(50) + "\n");
|
||||
|
||||
testVirtualJoystick();
|
||||
console.log("\n" + "=".repeat(50) + "\n");
|
||||
|
||||
testMovementUpdate();
|
||||
|
||||
console.log("\n" + "=".repeat(50));
|
||||
console.log("所有测试场景运行完成");
|
||||
}
|
||||
|
||||
// 导出测试函数
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = {
|
||||
testRelativeMovement,
|
||||
testMoveToPoint,
|
||||
testSelectionAndCommand,
|
||||
testVirtualJoystick,
|
||||
testMovementUpdate,
|
||||
runAllTests
|
||||
};
|
||||
// Node.js环境下自动运行测试
|
||||
runAllTests();
|
||||
} else {
|
||||
// 浏览器环境下自动运行测试
|
||||
window.movementTests = {
|
||||
runAllTests,
|
||||
testRelativeMovement,
|
||||
testMoveToPoint,
|
||||
testSelectionAndCommand,
|
||||
testVirtualJoystick,
|
||||
testMovementUpdate
|
||||
};
|
||||
// 浏览器环境下自动运行测试
|
||||
runAllTests();
|
||||
}
|
||||
+10
-11
@@ -1,6 +1,6 @@
|
||||
// ui/VirtualJoystick.js
|
||||
export class VirtualJoystick {
|
||||
constructor(scene) {
|
||||
constructor(scene, enabled = true) {
|
||||
this.scene = scene;
|
||||
this.isActive = false;
|
||||
this.base = null;
|
||||
@@ -9,16 +9,14 @@ export class VirtualJoystick {
|
||||
this.centerX = 0;
|
||||
this.centerY = 0;
|
||||
this.radius = 50;
|
||||
this.enabled = enabled;
|
||||
|
||||
// 仅在触屏设备显示
|
||||
// if ('ontouchstart' in window) {
|
||||
// this.create();
|
||||
// this.bindEvents();
|
||||
// }
|
||||
|
||||
// 在所有设备上都显示摇杆
|
||||
this.create();
|
||||
this.bindEvents();
|
||||
// 仅在启用时创建摇杆
|
||||
if (this.enabled) {
|
||||
// 在所有设备上都显示摇杆
|
||||
this.create();
|
||||
this.bindEvents();
|
||||
}
|
||||
}
|
||||
|
||||
create() {
|
||||
@@ -84,7 +82,8 @@ export class VirtualJoystick {
|
||||
* 更新方法,用于在游戏循环中持续发送移动指令
|
||||
*/
|
||||
update() {
|
||||
if (this.isActive && this.pointer) {
|
||||
// 仅在启用时更新
|
||||
if (this.enabled && this.isActive && this.pointer) {
|
||||
const dx = this.pointer.x - this.centerX;
|
||||
const dy = this.pointer.y - this.centerY;
|
||||
|
||||
|
||||
Reference in new issue
Block a user