diff --git a/README.md b/README.md new file mode 100644 index 0000000..4ccab66 --- /dev/null +++ b/README.md @@ -0,0 +1,71 @@ +# CINC 游戏项目 + +## 项目介绍 +CINC是一个基于Phaser.js开发的2D策略/模拟游戏项目。该项目采用模块化架构设计,实现了单位管理、选择控制、移动命令等核心游戏功能,支持键盘和鼠标操作,以及虚拟摇杆控制。 + +## 功能特性 +- **单位系统**:创建和管理游戏单位,支持单位移动和碰撞检测 +- **选择系统**:支持单选和框选多个单位 +- **命令系统**:处理单位移动命令,支持相对移动和目标点移动 +- **输入系统**:统一处理键盘和鼠标输入 +- **响应式设计**:适配不同屏幕尺寸 +- **虚拟摇杆**:支持触摸设备操作 + +## 技术栈 +- **游戏引擎**:Phaser.js +- **开发语言**:ES6+ JavaScript (模块化) +- **渲染技术**:HTML5 Canvas +- **物理系统**:Phaser Arcade Physics + +## 项目结构 +``` +├── config/ # 游戏配置文件 +│ └── gameConfig.js +├── index.html # 入口HTML文件 +├── main.js # 游戏初始化入口 +├── objects/ # 游戏对象定义 +│ └── Unit.js # 单位类 +├── phaser.min.js # Phaser.js库 +├── scenes/ # 游戏场景 +│ └── GameScene.js +├── systems/ # 游戏系统 +│ ├── CommandSystem.js # 命令处理系统 +│ ├── ControlMode.js # 控制模式管理 +│ ├── InputSystem.js # 输入处理系统 +│ └── SelectionSystem.js # 单位选择系统 +├── ui/ # 用户界面组件 +│ └── VirtualJoystick.js # 虚拟摇杆 +└── utils/ # 工具文件 +``` + +## 运行方法 +1. 使用Python启动本地HTTP服务器: +```bash +python -m http.server 8000 +``` + +2. 在浏览器中访问: +``` +http://localhost:8000 +``` + +## 游戏操作 +- **单选单位**:鼠标左键点击单位 +- **框选单位**:按住鼠标左键拖动框选多个单位 +- **移动命令**:点击空地指定移动目标点 +- **键盘控制**:使用WASD或方向键进行相对移动(需先选中单位) + +## 开发说明 +项目采用模块化设计,各系统之间通过事件系统通信,便于扩展和维护。主要系统职责: + +- **InputSystem**:处理所有输入事件,转换为游戏命令 +- **SelectionSystem**:管理单位选择状态 +- **CommandSystem**:执行具体游戏命令 +- **Unit**:单位对象,处理自身逻辑和渲染 + +## 扩展建议 +- 添加单位属性系统(生命值、攻击力等) +- 实现战斗系统 +- 添加地图和关卡系统 +- 实现资源管理系统 +- 添加UI界面(控制面板、状态栏等) \ No newline at end of file diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 0000000..2ce4814 Binary files /dev/null and b/favicon.ico differ diff --git a/ui/MiniMap.js b/ui/MiniMap.js new file mode 100644 index 0000000..849a3cc --- /dev/null +++ b/ui/MiniMap.js @@ -0,0 +1,129 @@ +// ui/MiniMap.js + +export class MiniMap { + constructor(scene, options = {}) { + this.scene = scene; + this.worldSize = options.worldSize || 2000; + this.mapSize = options.mapSize || 200; + this.x = options.x || 10; + this.y = options.y || 10; + + // 计算缩放比例(将世界坐标转换为小地图坐标) + this.scale = this.mapSize / (this.worldSize * 2); + + // 创建小地图容器 + this.container = scene.add.container(this.x, this.y); + this.container.setScrollFactor(0, 0); // 小地图固定在屏幕上 + + // 创建小地图背景 + this.background = scene.add.rectangle(0, 0, this.mapSize, this.mapSize, 0x000000, 0.5); + this.background.setOrigin(0, 0); + this.background.setStrokeStyle(1, 0xffffff, 0.8); + this.container.add(this.background); + + // 创建视口指示器 + this.viewport = scene.add.rectangle(0, 0, 0, 0, 0xffffff, 0.3); + this.viewport.setOrigin(0, 0); + this.viewport.setStrokeStyle(1, 0xffffff, 1); + this.container.add(this.viewport); + + // 创建单位标记数组 + this.unitMarkers = []; + + // 设置交互 + this.setupInteractions(); + + // 更新小地图(初始调用) + this.update(); + } + + setupInteractions() { + // 启用容器交互 + this.container.setInteractive(new Phaser.Geom.Rectangle(0, 0, this.mapSize, this.mapSize), Phaser.Geom.Rectangle.Contains); + + // 点击小地图移动相机 + this.container.on('pointerdown', (pointer) => { + this.handleMapClick(pointer); + }); + + // 拖动小地图移动相机 + this.container.on('pointermove', (pointer) => { + if (pointer.isDown) { + this.handleMapDrag(pointer); + } + }); + } + + handleMapClick(pointer) { + // 将小地图坐标转换为世界坐标 + const worldX = (pointer.x - this.x) / this.scale - this.worldSize; + const worldY = (pointer.y - this.y) / this.scale - this.worldSize; + + // 移动相机到点击位置 + this.scene.cameras.main.centerOn(worldX, worldY); + } + + handleMapDrag(pointer) { + // 将小地图坐标转换为世界坐标 + const worldX = (pointer.x - this.x) / this.scale - this.worldSize; + const worldY = (pointer.y - this.y) / this.scale - this.worldSize; + + // 移动相机到拖动位置 + this.scene.cameras.main.centerOn(worldX, worldY); + } + + update() { + // 更新视口指示器位置和大小 + this.updateViewport(); + + // 更新单位标记 + this.updateUnitMarkers(); + } + + updateViewport() { + const camera = this.scene.cameras.main; + + // 计算相机视口在世界中的位置和大小 + const viewportLeft = camera.scrollX; + const viewportTop = camera.scrollY; + const viewportWidth = camera.width * camera.zoom; + const viewportHeight = camera.height * camera.zoom; + + // 将世界坐标转换为小地图坐标 + const mapLeft = (viewportLeft + this.worldSize) * this.scale; + const mapTop = (viewportTop + this.worldSize) * this.scale; + const mapWidth = viewportWidth * this.scale; + const mapHeight = viewportHeight * this.scale; + + // 更新视口指示器 + this.viewport.setPosition(mapLeft, mapTop); + this.viewport.setSize(mapWidth, mapHeight); + } + + updateUnitMarkers() { + // 清除现有标记 + this.unitMarkers.forEach(marker => { + marker.destroy(); + }); + this.unitMarkers = []; + + // 获取所有单位 + if (this.scene.unitGroup) { + const units = this.scene.unitGroup.getChildren(); + + units.forEach(unit => { + // 将单位世界坐标转换为小地图坐标 + const mapX = (unit.x + this.worldSize) * this.scale; + const mapY = (unit.y + this.worldSize) * this.scale; + + // 创建单位标记 + const marker = this.scene.add.circle(mapX, mapY, 2, 0xff0000, 1); + marker.setOrigin(0.5, 0.5); + + // 将标记添加到容器和数组中 + this.container.add(marker); + this.unitMarkers.push(marker); + }); + } + } +} \ No newline at end of file