2 Commits 053e8185d5 ... f2aef3a7c5

Author SHA1 Message Date
  181404010226 f2aef3a7c5 停止向墙体移动 2 months ago
  181404010226 05b1105d8c 敌人抛掷bug解决 2 months ago
64 changed files with 6198 additions and 210 deletions
  1. 9 0
      assets/NewbieGuidePlugin-v1.0.0.meta
  2. BIN
      assets/NewbieGuidePlugin-v1.0.0.zip
  3. 12 0
      assets/NewbieGuidePlugin-v1.0.0.zip.meta
  4. 9 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0.meta
  5. 120 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/CHANGELOG.md
  6. 11 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/CHANGELOG.md.meta
  7. 21 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/LICENSE
  8. 12 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/LICENSE.meta
  9. 142 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/QUICK_START.md
  10. 11 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/QUICK_START.md.meta
  11. 121 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/README.md
  12. 11 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/README.md.meta
  13. 9 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/docs.meta
  14. 541 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/docs/API_Reference.md
  15. 11 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/docs/API_Reference.md.meta
  16. 640 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/docs/Integration_Guide.md
  17. 11 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/docs/Integration_Guide.md.meta
  18. 9 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/examples.meta
  19. 700 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/examples/AdvancedExample.ts
  20. 9 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/examples/AdvancedExample.ts.meta
  21. 401 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/examples/BasicExample.ts
  22. 9 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/examples/BasicExample.ts.meta
  23. 58 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/install.bat
  24. 12 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/install.bat.meta
  25. 38 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/install.sh
  26. 12 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/install.sh.meta
  27. 34 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/install_test.bat
  28. 12 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/install_test.bat.meta
  29. 88 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/package.json
  30. 11 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/package.json.meta
  31. 9 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/prefabs.meta
  32. 295 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/prefabs/GuideUI.prefab
  33. 13 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/prefabs/GuideUI.prefab.meta
  34. 9 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/resources.meta
  35. 9 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/resources/spine.meta
  36. 19 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/resources/spine/tut_hand.atlas.txt
  37. 11 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/resources/spine/tut_hand.atlas.txt.meta
  38. 118 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/resources/spine/tut_hand.json
  39. 13 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/resources/spine/tut_hand.json.meta
  40. 9 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/resources/textures.meta
  41. 9 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/scripts.meta
  42. 180 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/scripts/GuideDataManager.ts
  43. 9 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/scripts/GuideDataManager.ts.meta
  44. 206 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/scripts/GuideManager.ts
  45. 9 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/scripts/GuideManager.ts.meta
  46. 229 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/scripts/GuideStep.ts
  47. 9 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/scripts/GuideStep.ts.meta
  48. 301 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/scripts/GuideSteps.ts
  49. 9 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/scripts/GuideSteps.ts.meta
  50. 275 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/scripts/GuideUIController.ts
  51. 9 0
      assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/scripts/GuideUIController.ts.meta
  52. 217 145
      assets/Scenes/GameLevel.scene
  53. 14 14
      assets/data/enemies.json
  54. BIN
      assets/data/excel/~$敌人配置表.xlsx
  55. 12 0
      assets/data/excel/~$敌人配置表.xlsx.meta
  56. BIN
      assets/data/excel/敌人配置表.xlsx
  57. 2 2
      assets/resources/prefabs/DamageNumber.prefab
  58. 2 2
      assets/scripts/Animations/DamageNumberAni.ts
  59. 4 0
      assets/scripts/CombatSystem/BlockManager.ts
  60. 70 26
      assets/scripts/CombatSystem/EnemyInstance.ts
  61. 14 1
      assets/scripts/CombatSystem/SkillSelection/SkillSelectionController.ts
  62. 64 20
      assets/scripts/FourUI/MainSystem/MainUIControlller.ts
  63. 9 0
      assets/scripts/Guide.meta
  64. 956 0
      image_composer.html

+ 9 - 0
assets/NewbieGuidePlugin-v1.0.0.meta

@@ -0,0 +1,9 @@
+{
+  "ver": "1.2.0",
+  "importer": "directory",
+  "imported": true,
+  "uuid": "559946b6-492b-44d8-a847-bebb87a7b9d9",
+  "files": [],
+  "subMetas": {},
+  "userData": {}
+}

BIN
assets/NewbieGuidePlugin-v1.0.0.zip


+ 12 - 0
assets/NewbieGuidePlugin-v1.0.0.zip.meta

@@ -0,0 +1,12 @@
+{
+  "ver": "1.0.0",
+  "importer": "*",
+  "imported": true,
+  "uuid": "8e5d6768-0376-4ebc-af05-0dae5ce3ab1e",
+  "files": [
+    ".json",
+    ".zip"
+  ],
+  "subMetas": {},
+  "userData": {}
+}

+ 9 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0.meta

@@ -0,0 +1,9 @@
+{
+  "ver": "1.2.0",
+  "importer": "directory",
+  "imported": true,
+  "uuid": "faba0329-6af5-4bf9-8edb-4d18341548ab",
+  "files": [],
+  "subMetas": {},
+  "userData": {}
+}

+ 120 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/CHANGELOG.md

@@ -0,0 +1,120 @@
+# 更新日志
+
+## [1.0.0] - 2024-01-15
+
+### 新增功能
+- ✨ 完整的新手引导系统核心架构
+- ✨ 状态驱动的引导流程管理
+- ✨ 数据持久化存储系统
+- ✨ 可视化引导UI控制器
+- ✨ Spine动画支持(手指指向、高亮效果)
+- ✨ 多种引导步骤类型:
+  - 点击引导 (ClickGuideStep)
+  - 拖拽引导 (DragGuideStep)
+  - 对话引导 (DialogGuideStep)
+  - 条件等待引导 (WaitConditionGuideStep)
+  - 合并提示引导 (MergeTipGuideStep)
+- ✨ 事件驱动的步骤管理
+- ✨ 智能条件检测系统
+- ✨ 可扩展的自定义步骤支持
+- ✨ 完整的配置文件系统
+- ✨ 调试模式和开发工具
+- ✨ 基础和高级使用示例
+- ✨ 详细的API文档和集成指南
+
+### 核心组件
+- 🔧 `GuideManager` - 引导系统管理器
+- 🔧 `GuideDataManager` - 数据存储管理器
+- 🔧 `GuideUIController` - UI控制器
+- 🔧 `GuideStep` - 引导步骤基类
+- 🔧 `GuideSteps` - 具体引导步骤实现
+
+### 资源文件
+- 🎨 `GuideUI.prefab` - 引导UI预制体
+- 🎨 `tut_hand.atlas.txt` - 手指动画图集
+- 🎨 `tut_hand.json` - 手指动画骨骼数据
+- 🎨 `guide_mask.png` - 引导遮罩图片
+- ⚙️ `guide_config.json` - 引导配置文件
+
+### 文档
+- 📚 `README.md` - 插件概述和快速开始
+- 📚 `API_Reference.md` - 详细API参考文档
+- 📚 `Integration_Guide.md` - 集成指南和最佳实践
+- 📚 `BasicExample.ts` - 基础使用示例
+- 📚 `AdvancedExample.ts` - 高级功能示例
+
+### 特性
+- 🚀 非侵入式设计,易于集成
+- 🚀 支持复杂的分支引导流程
+- 🚀 智能的条件检测和步骤触发
+- 🚀 完整的数据持久化支持
+- 🚀 丰富的视觉引导效果
+- 🚀 可配置的引导行为
+- 🚀 强大的调试和测试工具
+- 🚀 TypeScript支持,类型安全
+- 🚀 事件驱动架构,松耦合设计
+- 🚀 支持自定义引导步骤扩展
+
+### 兼容性
+- ✅ Cocos Creator 2.4.0+
+- ✅ TypeScript 支持
+- ✅ 跨平台兼容(Web、移动端)
+- ✅ 支持多种屏幕分辨率
+
+### 性能优化
+- ⚡ 懒加载资源管理
+- ⚡ 内存优化的UI控制
+- ⚡ 高效的事件系统
+- ⚡ 最小化运行时开销
+
+---
+
+## 未来计划
+
+### [1.1.0] - 计划中
+- 🔮 多语言支持系统
+- 🔮 更多内置引导步骤类型
+- 🔮 可视化引导编辑器
+- 🔮 引导录制和回放功能
+- 🔮 A/B测试支持
+- 🔮 引导数据分析工具
+
+### [1.2.0] - 计划中
+- 🔮 云端引导配置同步
+- 🔮 动态引导内容更新
+- 🔮 AI驱动的个性化引导
+- 🔮 更丰富的动画效果
+- 🔮 语音引导支持
+
+---
+
+## 贡献指南
+
+欢迎提交Issue和Pull Request来帮助改进这个插件!
+
+### 如何贡献
+1. Fork 这个项目
+2. 创建你的功能分支 (`git checkout -b feature/AmazingFeature`)
+3. 提交你的更改 (`git commit -m 'Add some AmazingFeature'`)
+4. 推送到分支 (`git push origin feature/AmazingFeature`)
+5. 打开一个 Pull Request
+
+### 报告问题
+如果你发现了bug或有功能建议,请在GitHub Issues中报告。
+
+---
+
+## 许可证
+
+本项目采用 MIT 许可证 - 查看 [LICENSE](LICENSE) 文件了解详情。
+
+---
+
+## 致谢
+
+感谢所有为这个项目做出贡献的开发者和测试者!
+
+特别感谢:
+- Cocos Creator 团队提供的优秀游戏引擎
+- Spine 团队提供的动画系统
+- 所有提供反馈和建议的社区成员

+ 11 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/CHANGELOG.md.meta

@@ -0,0 +1,11 @@
+{
+  "ver": "1.0.1",
+  "importer": "text",
+  "imported": true,
+  "uuid": "921ad503-bfbf-4750-987f-686ac50b8074",
+  "files": [
+    ".json"
+  ],
+  "subMetas": {},
+  "userData": {}
+}

+ 21 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2024 Newbie Guide Plugin
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 12 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/LICENSE.meta

@@ -0,0 +1,12 @@
+{
+  "ver": "1.0.0",
+  "importer": "*",
+  "imported": true,
+  "uuid": "e5f11e00-a615-445b-ae6f-aa27f0e21cd6",
+  "files": [
+    "",
+    ".json"
+  ],
+  "subMetas": {},
+  "userData": {}
+}

+ 142 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/QUICK_START.md

@@ -0,0 +1,142 @@
+# Newbie Guide Plugin - Quick Start
+
+## Installation
+
+### Method 1: Automatic Installation (Recommended)
+1. Extract the plugin package to any directory
+2. Open Command Prompt or PowerShell as Administrator
+3. Navigate to the extracted plugin directory
+4. Run the installation script:
+   ```bash
+   # For Windows
+   install.bat
+   
+   # For Linux/Mac
+   chmod +x install.sh
+   ./install.sh
+   ```
+
+### Method 2: Manual Installation
+1. Copy the following directories to your Cocos Creator project's `assets` folder:
+   - `scripts` → `assets/NewbieGuidePlugin/scripts/`
+   - `resources` → `assets/NewbieGuidePlugin/resources/`
+   - `prefabs` → `assets/NewbieGuidePlugin/prefabs/`
+   - `guide_config.json` → `assets/NewbieGuidePlugin/`
+
+2. (Optional) Copy examples:
+   - `examples` → `assets/Examples/NewbieGuideExamples/`
+
+## Basic Usage
+
+### 1. Initialize the Guide System
+```typescript
+import { GuideManager } from "./NewbieGuidePlugin/scripts/GuideManager";
+
+// Initialize in your main scene
+onLoad() {
+    GuideManager.getInstance().initialize();
+}
+```
+
+### 2. Register Guide Steps
+```typescript
+// Register a simple click guide
+GuideManager.getInstance().registerStep({
+    id: "first_click",
+    type: "click",
+    trigger_condition: { type: "scene_loaded", scene: "MainScene" },
+    config: {
+        target_node: "ui/buttons/start_button",
+        highlight: true,
+        message: "Click here to start!"
+    }
+});
+
+// Start the guide
+GuideManager.getInstance().startGuide();
+```
+
+### 3. Listen to Guide Events
+```typescript
+// Listen for step completion
+GuideManager.getInstance().on('step_completed', (stepId: string) => {
+    console.log(`Step ${stepId} completed`);
+});
+
+// Listen for guide completion
+GuideManager.getInstance().on('guide_completed', () => {
+    console.log('Guide completed!');
+});
+```
+
+## 📁 文件结构
+
+```
+NewbieGuidePlugin/
+├── scripts/                 # 核心脚本文件
+│   ├── GuideManager.ts      # 引导管理器
+│   ├── GuideDataManager.ts  # 数据管理器
+│   ├── GuideUIController.ts # UI控制器
+│   ├── GuideStep.ts         # 引导步骤基类
+│   └── GuideSteps.ts        # 具体引导步骤
+├── resources/               # 资源文件
+│   └── spine/              # Spine动画资源
+├── prefabs/                # 预制体文件
+│   └── GuideUI.prefab      # 引导UI预制体
+├── examples/               # 使用示例
+│   ├── BasicExample.ts     # 基础示例
+│   └── AdvancedExample.ts  # 高级示例
+├── docs/                   # 文档
+│   ├── API_Reference.md    # API参考
+│   └── Integration_Guide.md # 集成指南
+└── guide_config.json       # 引导配置文件
+```
+
+## 🔧 配置文件
+
+编辑 `guide_config.json` 来配置你的引导流程:
+
+```json
+{
+  "version": "1.0.0",
+  "guide_steps": [
+    {
+      "id": "welcome",
+      "type": "dialog",
+      "trigger_condition": "always",
+      "config": {
+        "message": "欢迎来到游戏!"
+      }
+    }
+  ]
+}
+```
+
+## 🐛 调试功能
+
+在开发模式下,可以使用以下调试命令:
+
+```javascript
+// 在浏览器控制台中使用
+GuideDebug.resetGuide();    // 重置引导
+GuideDebug.skipStep();      // 跳过当前步骤
+GuideDebug.getStatus();     // 获取引导状态
+```
+
+## 📚 更多文档
+
+- [README.md](README.md) - 插件详细介绍
+- [docs/API_Reference.md](docs/API_Reference.md) - 完整API文档
+- [docs/Integration_Guide.md](docs/Integration_Guide.md) - 详细集成指南
+- [examples/](examples/) - 完整使用示例
+
+## 🆘 获取帮助
+
+如果遇到问题,请查看:
+1. 文档中的常见问题解答
+2. 示例代码中的实现方式
+3. 在GitHub上提交Issue
+
+## 📄 许可证
+
+本插件采用 MIT 许可证,详见 [LICENSE](LICENSE) 文件。

+ 11 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/QUICK_START.md.meta

@@ -0,0 +1,11 @@
+{
+  "ver": "1.0.1",
+  "importer": "text",
+  "imported": true,
+  "uuid": "d5421778-2d76-48ae-8093-cc2f26c0c960",
+  "files": [
+    ".json"
+  ],
+  "subMetas": {},
+  "userData": {}
+}

+ 121 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/README.md

@@ -0,0 +1,121 @@
+# Cocos Creator 新手引导系统插件
+
+## 概述
+
+这是一个基于 Cocos Creator 的通用新手引导系统插件,提供了完整的引导功能实现,包括状态管理、视觉引导、数据持久化等核心功能。
+
+## 功能特性
+
+- ✅ **状态驱动**:基于状态机的引导流程控制
+- ✅ **数据持久化**:自动保存引导进度,支持断点续引
+- ✅ **视觉引导**:支持 Spine 动画的手势指引
+- ✅ **智能检测**:自动检测游戏状态并触发相应引导
+- ✅ **易于扩展**:模块化设计,支持自定义引导步骤
+- ✅ **无侵入性**:与现有游戏逻辑松耦合
+
+## 文件结构
+
+```
+NewbieGuidePlugin/
+├── README.md                    # 主要说明文档
+├── scripts/                     # 核心脚本文件
+│   ├── GuideManager.ts         # 引导管理器
+│   ├── GuideStep.ts            # 引导步骤基类
+│   ├── GuideDataManager.ts     # 数据管理器
+│   └── GuideUIController.ts    # UI控制器
+├── resources/                   # 资源文件
+│   ├── spine/                  # Spine动画资源
+│   │   ├── tut_hand.skel       # 手势骨骼文件
+│   │   ├── tut_hand.atlas.txt  # 图集文件
+│   │   └── tut_hand.png        # 纹理文件
+│   └── prefabs/                # 预制体文件
+│       └── GuideHand.prefab    # 引导手势预制体
+├── examples/                    # 使用示例
+│   ├── SimpleGuideExample.ts   # 简单引导示例
+│   └── AdvancedGuideExample.ts # 高级引导示例
+└── docs/                       # 详细文档
+    ├── API.md                  # API文档
+    ├── Integration.md          # 集成指南
+    └── Customization.md        # 自定义指南
+```
+
+## 快速开始
+
+### 1. 安装插件
+
+将 `NewbieGuidePlugin` 文件夹复制到你的 Cocos Creator 项目的 `assets` 目录下。
+
+### 2. 基础集成
+
+```typescript
+import { GuideManager } from "./NewbieGuidePlugin/scripts/GuideManager";
+
+// 在你的主场景脚本中初始化引导管理器
+export default class GameScene extends cc.Component {
+    onLoad() {
+        // 初始化引导系统
+        GuideManager.getInstance().init();
+        
+        // 开始引导
+        GuideManager.getInstance().startGuide();
+    }
+}
+```
+
+### 3. 自定义引导步骤
+
+```typescript
+import { GuideStep } from "./NewbieGuidePlugin/scripts/GuideStep";
+
+export class CustomGuideStep extends GuideStep {
+    stepId = "custom_step_1";
+    
+    canTrigger(): boolean {
+        // 定义触发条件
+        return true;
+    }
+    
+    execute(): void {
+        // 执行引导逻辑
+        this.showGuideUI();
+    }
+    
+    onComplete(): void {
+        // 完成后的处理
+        this.hideGuideUI();
+        super.onComplete();
+    }
+}
+```
+
+## 核心概念
+
+### 引导状态管理
+
+引导系统使用 `guideIndex` 来跟踪当前的引导进度:
+- `0`:第一步引导(通常是点击引导)
+- `1`:第二步引导(通常是操作引导)
+- `2+`:后续自定义引导步骤
+
+### 数据持久化
+
+所有引导数据都会自动保存到本地存储,确保用户重新进入游戏时能够继续之前的引导进度。
+
+### 视觉引导组件
+
+- **GuideHand**:手势引导组件,支持点击、拖拽等动画
+- **GuideHighlight**:高亮显示组件,用于突出显示目标元素
+- **GuideMask**:遮罩组件,用于聚焦用户注意力
+
+## 版本要求
+
+- Cocos Creator 2.4.0+
+- TypeScript 支持
+
+## 许可证
+
+MIT License
+
+## 支持
+
+如有问题或建议,请参考 `docs/` 目录下的详细文档。

+ 11 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/README.md.meta

@@ -0,0 +1,11 @@
+{
+  "ver": "1.0.1",
+  "importer": "text",
+  "imported": true,
+  "uuid": "633bb480-241a-4e52-8fbc-859d8e9f6594",
+  "files": [
+    ".json"
+  ],
+  "subMetas": {},
+  "userData": {}
+}

+ 9 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/docs.meta

@@ -0,0 +1,9 @@
+{
+  "ver": "1.2.0",
+  "importer": "directory",
+  "imported": true,
+  "uuid": "6d8eeec5-7f5b-4bc8-9702-39d2cd630636",
+  "files": [],
+  "subMetas": {},
+  "userData": {}
+}

+ 541 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/docs/API_Reference.md

@@ -0,0 +1,541 @@
+# 新手引导插件 API 参考
+
+## 核心类
+
+### GuideManager
+
+引导系统的核心管理器,负责整个引导流程的控制。
+
+#### 静态方法
+
+##### `getInstance(): GuideManager`
+获取引导管理器的单例实例。
+
+**返回值:** `GuideManager` - 管理器实例
+
+**示例:**
+```typescript
+const guideManager = GuideManager.getInstance();
+```
+
+#### 实例方法
+
+##### `init(): void`
+初始化引导系统。必须在使用其他功能前调用。
+
+**示例:**
+```typescript
+guideManager.init();
+```
+
+##### `registerStep(step: GuideStep): void`
+注册一个引导步骤。
+
+**参数:**
+- `step: GuideStep` - 要注册的引导步骤
+
+**示例:**
+```typescript
+const clickStep = new ClickGuideStep("first_click", "Canvas/UI/StartButton");
+guideManager.registerStep(clickStep);
+```
+
+##### `startGuide(): void`
+开始引导流程。
+
+**示例:**
+```typescript
+guideManager.startGuide();
+```
+
+##### `stopGuide(): void`
+停止引导流程。
+
+**示例:**
+```typescript
+guideManager.stopGuide();
+```
+
+##### `skipCurrentStep(): void`
+跳过当前正在执行的引导步骤。
+
+**示例:**
+```typescript
+guideManager.skipCurrentStep();
+```
+
+##### `resetGuide(): void`
+重置整个引导系统,清除所有进度数据。
+
+**示例:**
+```typescript
+guideManager.resetGuide();
+```
+
+##### `isGuideActive(): boolean`
+检查引导是否正在进行中。
+
+**返回值:** `boolean` - 引导是否激活
+
+**示例:**
+```typescript
+if (guideManager.isGuideActive()) {
+    console.log("引导正在进行中");
+}
+```
+
+##### `getCurrentStep(): GuideStep`
+获取当前正在执行的引导步骤。
+
+**返回值:** `GuideStep` - 当前步骤,如果没有则返回null
+
+**示例:**
+```typescript
+const currentStep = guideManager.getCurrentStep();
+if (currentStep) {
+    console.log("当前步骤:", currentStep.stepId);
+}
+```
+
+---
+
+### GuideDataManager
+
+负责引导数据的持久化存储和管理。
+
+#### 实例方法
+
+##### `getGuideIndex(): number`
+获取当前的引导索引。
+
+**返回值:** `number` - 引导索引
+
+##### `setGuideIndex(index: number): void`
+设置引导索引。
+
+**参数:**
+- `index: number` - 要设置的索引值
+
+##### `incrementGuideIndex(): void`
+将引导索引增加1。
+
+##### `isStepCompleted(stepId: string): boolean`
+检查指定步骤是否已完成。
+
+**参数:**
+- `stepId: string` - 步骤ID
+
+**返回值:** `boolean` - 步骤是否已完成
+
+##### `markStepCompleted(stepId: string): void`
+标记步骤为已完成。
+
+**参数:**
+- `stepId: string` - 步骤ID
+
+##### `getStepData(stepId: string, key: string, defaultValue?: any): any`
+获取步骤的自定义数据。
+
+**参数:**
+- `stepId: string` - 步骤ID
+- `key: string` - 数据键
+- `defaultValue?: any` - 默认值
+
+**返回值:** `any` - 存储的数据值
+
+##### `setStepData(stepId: string, key: string, value: any): void`
+设置步骤的自定义数据。
+
+**参数:**
+- `stepId: string` - 步骤ID
+- `key: string` - 数据键
+- `value: any` - 要存储的值
+
+##### `resetGuideData(): void`
+重置所有引导数据。
+
+##### `isNewUser(): boolean`
+检查是否是新用户(从未进行过引导)。
+
+**返回值:** `boolean` - 是否是新用户
+
+##### `isGuideCompleted(): boolean`
+检查引导是否已完成。
+
+**返回值:** `boolean` - 引导是否已完成
+
+##### `getGuideProgress(): number`
+获取引导完成的百分比。
+
+**返回值:** `number` - 完成百分比(0-1之间)
+
+---
+
+### GuideUIController
+
+负责引导UI的显示、隐藏和动画控制。
+
+#### 实例方法
+
+##### `init(): void`
+初始化UI控制器。
+
+##### `registerGuideNode(nodeId: string, node: cc.Node): void`
+注册一个引导节点。
+
+**参数:**
+- `nodeId: string` - 节点ID
+- `node: cc.Node` - 节点对象
+
+##### `showGuideUI(nodeId: string, animationName?: string): void`
+显示指定的引导UI。
+
+**参数:**
+- `nodeId: string` - 节点ID
+- `animationName?: string` - 要播放的动画名称
+
+##### `hideGuideUI(nodeId: string): void`
+隐藏指定的引导UI。
+
+**参数:**
+- `nodeId: string` - 节点ID
+
+##### `hideAllGuideUI(): void`
+隐藏所有引导UI。
+
+##### `playAnimation(nodeId: string, animationName: string, loop?: boolean): void`
+播放指定节点的动画。
+
+**参数:**
+- `nodeId: string` - 节点ID
+- `animationName: string` - 动画名称
+- `loop?: boolean` - 是否循环播放,默认true
+
+##### `stopAnimation(nodeId: string): void`
+停止指定节点的动画。
+
+**参数:**
+- `nodeId: string` - 节点ID
+
+##### `setGuideUIPosition(nodeId: string, position: cc.Vec2): void`
+设置引导UI的位置。
+
+**参数:**
+- `nodeId: string` - 节点ID
+- `position: cc.Vec2` - 位置坐标
+
+##### `createPointingAnimation(nodeId: string, targetPosition: cc.Vec2): void`
+创建指向特定位置的手指动画。
+
+**参数:**
+- `nodeId: string` - 节点ID
+- `targetPosition: cc.Vec2` - 目标位置
+
+##### `createHighlightEffect(targetNode: cc.Node): void`
+为目标节点创建高亮效果。
+
+**参数:**
+- `targetNode: cc.Node` - 目标节点
+
+##### `removeHighlightEffect(): void`
+移除高亮效果。
+
+---
+
+### GuideStep
+
+引导步骤的基类,所有具体的引导步骤都应该继承此类。
+
+#### 构造函数
+
+##### `constructor(stepId: string)`
+创建一个引导步骤。
+
+**参数:**
+- `stepId: string` - 步骤的唯一标识符
+
+#### 抽象方法
+
+##### `canTrigger(): boolean`
+检查是否可以触发此步骤。子类必须实现。
+
+**返回值:** `boolean` - 是否可以触发
+
+##### `doExecute(): void`
+执行引导步骤的具体逻辑。子类必须实现。
+
+#### 实例方法
+
+##### `execute(): void`
+执行引导步骤(公共接口)。
+
+##### `complete(): void`
+完成当前步骤。
+
+##### `skip(): void`
+跳过当前步骤。
+
+##### `forceComplete(): void`
+强制完成步骤(用于清理)。
+
+##### `isCompleted(): boolean`
+检查步骤是否已完成。
+
+**返回值:** `boolean` - 步骤是否已完成
+
+##### `isExecuting(): boolean`
+检查步骤是否正在执行。
+
+**返回值:** `boolean` - 步骤是否正在执行
+
+##### `reset(): void`
+重置步骤状态。
+
+#### 受保护方法
+
+##### `getStepData(key: string, defaultValue?: any): any`
+获取步骤的自定义数据。
+
+**参数:**
+- `key: string` - 数据键
+- `defaultValue?: any` - 默认值
+
+**返回值:** `any` - 存储的数据值
+
+##### `setStepData(key: string, value: any): void`
+设置步骤的自定义数据。
+
+**参数:**
+- `key: string` - 数据键
+- `value: any` - 要存储的值
+
+##### `onComplete(): void`
+步骤完成时的回调。子类可以重写此方法。
+
+---
+
+## 具体引导步骤类
+
+### ClickGuideStep
+
+点击引导步骤,引导用户点击特定的UI元素。
+
+#### 构造函数
+
+##### `constructor(stepId: string, targetNodePath: string)`
+创建一个点击引导步骤。
+
+**参数:**
+- `stepId: string` - 步骤ID
+- `targetNodePath: string` - 目标节点的路径
+
+**示例:**
+```typescript
+const clickStep = new ClickGuideStep("click_start", "Canvas/UI/StartButton");
+```
+
+---
+
+### DragGuideStep
+
+拖拽引导步骤,引导用户进行拖拽操作。
+
+#### 构造函数
+
+##### `constructor(stepId: string, sourceNodePath: string, targetNodePath: string)`
+创建一个拖拽引导步骤。
+
+**参数:**
+- `stepId: string` - 步骤ID
+- `sourceNodePath: string` - 源节点路径
+- `targetNodePath: string` - 目标节点路径
+
+**示例:**
+```typescript
+const dragStep = new DragGuideStep("drag_item", "Canvas/Item1", "Canvas/Item2");
+```
+
+---
+
+### WaitConditionGuideStep
+
+等待条件引导步骤,等待特定条件满足后自动完成。
+
+#### 构造函数
+
+##### `constructor(stepId: string, conditionChecker: () => boolean, timeout?: number)`
+创建一个等待条件引导步骤。
+
+**参数:**
+- `stepId: string` - 步骤ID
+- `conditionChecker: () => boolean` - 条件检查函数
+- `timeout?: number` - 超时时间(毫秒),默认30秒
+
+**示例:**
+```typescript
+const waitStep = new WaitConditionGuideStep(
+    "wait_level_up",
+    () => playerLevel >= 2,
+    60000
+);
+```
+
+---
+
+### DialogGuideStep
+
+对话引导步骤,显示引导对话框。
+
+#### 构造函数
+
+##### `constructor(stepId: string, dialogText: string)`
+创建一个对话引导步骤。
+
+**参数:**
+- `stepId: string` - 步骤ID
+- `dialogText: string` - 对话文本
+
+**示例:**
+```typescript
+const dialogStep = new DialogGuideStep("welcome", "欢迎来到游戏!");
+```
+
+---
+
+### MergeTipGuideStep
+
+合并提示引导步骤,检测可合并物品并显示合并提示。
+
+#### 构造函数
+
+##### `constructor()`
+创建一个合并提示引导步骤。
+
+**示例:**
+```typescript
+const mergeStep = new MergeTipGuideStep();
+```
+
+---
+
+## 事件系统
+
+引导系统支持以下事件:
+
+### GuideManager 事件
+
+- `guide_started` - 引导开始时触发
+- `guide_stopped` - 引导停止时触发
+- `guide_completed` - 引导完成时触发
+- `step_started` - 步骤开始时触发
+- `step_completed` - 步骤完成时触发
+- `step_skipped` - 步骤跳过时触发
+
+**示例:**
+```typescript
+guideManager.on('step_completed', (step) => {
+    console.log(`步骤完成: ${step.stepId}`);
+});
+```
+
+---
+
+## 配置文件格式
+
+### guide_config.json
+
+引导配置文件的格式说明:
+
+```json
+{
+  "version": "1.0.0",
+  "guide_steps": [
+    {
+      "id": "步骤ID",
+      "type": "步骤类型",
+      "trigger_condition": {
+        "guide_index": 0,
+        "其他条件": "值"
+      },
+      "config": {
+        "步骤特定配置": "值"
+      }
+    }
+  ],
+  "ui_config": {
+    "guide_nodes": {
+      "节点ID": {
+        "prefab": "预制体名称",
+        "default_animation": "默认动画",
+        "z_index": 1000
+      }
+    },
+    "spine_resources": {
+      "资源名称": {
+        "atlas": "atlas文件路径",
+        "json": "json文件路径",
+        "texture": "纹理文件路径"
+      }
+    }
+  },
+  "settings": {
+    "auto_start": true,
+    "can_skip": true,
+    "save_progress": true,
+    "debug_mode": false
+  }
+}
+```
+
+### 步骤类型
+
+- `dialog` - 对话步骤
+- `click` - 点击步骤
+- `drag` - 拖拽步骤
+- `wait_condition` - 等待条件步骤
+- `merge_tip` - 合并提示步骤
+
+### 触发条件
+
+- `guide_index` - 引导索引
+- `has_mergeable_items` - 是否有可合并物品
+- `player_level` - 玩家等级
+- 其他自定义条件
+
+---
+
+## 错误处理
+
+### 常见错误
+
+1. **节点未找到错误**
+   ```
+   目标节点未找到: Canvas/UI/Button
+   ```
+   **解决方案:** 检查节点路径是否正确,确保节点存在于场景中。
+
+2. **Spine组件未找到错误**
+   ```
+   Spine组件未找到: guild_1
+   ```
+   **解决方案:** 确保引导节点包含sp.Skeleton组件。
+
+3. **引导数据解析失败**
+   ```
+   引导数据解析失败,使用默认数据
+   ```
+   **解决方案:** 检查本地存储的数据格式是否正确。
+
+### 调试模式
+
+启用调试模式可以获得更详细的日志信息:
+
+```typescript
+// 在配置文件中设置
+"settings": {
+  "debug_mode": true
+}
+
+// 或在代码中设置
+guideManager.getDataManager().setStepData("system", "debug_mode", true);
+```

+ 11 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/docs/API_Reference.md.meta

@@ -0,0 +1,11 @@
+{
+  "ver": "1.0.1",
+  "importer": "text",
+  "imported": true,
+  "uuid": "3715f4eb-c3e8-40bb-b71a-412a35d856b5",
+  "files": [
+    ".json"
+  ],
+  "subMetas": {},
+  "userData": {}
+}

+ 640 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/docs/Integration_Guide.md

@@ -0,0 +1,640 @@
+# 新手引导插件集成指南
+
+## 快速开始
+
+### 1. 安装插件
+
+将 `NewbieGuidePlugin` 文件夹复制到你的 Cocos Creator 项目中。
+
+```
+YourProject/
+├── assets/
+│   ├── NewbieGuidePlugin/
+│   │   ├── scripts/
+│   │   ├── prefabs/
+│   │   ├── resources/
+│   │   ├── config/
+│   │   └── docs/
+│   └── ...
+└── ...
+```
+
+### 2. 基本集成
+
+#### 2.1 导入核心类
+
+在你的主场景脚本中导入引导管理器:
+
+```typescript
+import { GuideManager } from "./NewbieGuidePlugin/scripts/GuideManager";
+import { ClickGuideStep, DragGuideStep, DialogGuideStep } from "./NewbieGuidePlugin/scripts/GuideSteps";
+```
+
+#### 2.2 初始化引导系统
+
+在场景的 `onLoad` 或 `start` 方法中初始化:
+
+```typescript
+export default class GameScene extends cc.Component {
+    
+    onLoad() {
+        // 初始化引导系统
+        const guideManager = GuideManager.getInstance();
+        guideManager.init();
+        
+        // 注册引导步骤
+        this.registerGuideSteps();
+        
+        // 开始引导
+        if (this.isNewPlayer()) {
+            guideManager.startGuide();
+        }
+    }
+    
+    private registerGuideSteps(): void {
+        const guideManager = GuideManager.getInstance();
+        
+        // 注册欢迎对话
+        const welcomeStep = new DialogGuideStep("welcome", "欢迎来到游戏!让我们开始新手引导吧。");
+        guideManager.registerStep(welcomeStep);
+        
+        // 注册点击引导
+        const clickStep = new ClickGuideStep("first_click", "Canvas/UI/StartButton");
+        guideManager.registerStep(clickStep);
+        
+        // 注册拖拽引导
+        const dragStep = new DragGuideStep("drag_tutorial", "Canvas/GameArea/Item1", "Canvas/GameArea/Item2");
+        guideManager.registerStep(dragStep);
+    }
+    
+    private isNewPlayer(): boolean {
+        const guideManager = GuideManager.getInstance();
+        return guideManager.getDataManager().isNewUser();
+    }
+}
+```
+
+### 3. 配置引导UI
+
+#### 3.1 添加引导节点到场景
+
+在你的场景中添加引导UI节点:
+
+1. 在场景中创建空节点,命名为 `GuideLayer`
+2. 在 `GuideLayer` 下创建两个子节点:`guild_0` 和 `guild_1`
+3. 为这两个节点添加 `sp.Skeleton` 组件
+4. 设置 Spine 资源为 `tut_hand`
+
+#### 3.2 手动注册引导节点
+
+如果你的UI结构不同,可以手动注册引导节点:
+
+```typescript
+onLoad() {
+    const guideManager = GuideManager.getInstance();
+    guideManager.init();
+    
+    // 手动注册引导节点
+    const uiController = guideManager.getUIController();
+    const guideNode1 = cc.find("Canvas/GuideLayer/MyGuideNode1");
+    const guideNode2 = cc.find("Canvas/GuideLayer/MyGuideNode2");
+    
+    uiController.registerGuideNode("guild_0", guideNode1);
+    uiController.registerGuideNode("guild_1", guideNode2);
+}
+```
+
+## 高级集成
+
+### 1. 自定义引导步骤
+
+创建你自己的引导步骤类:
+
+```typescript
+import { GuideStep } from "./NewbieGuidePlugin/scripts/GuideStep";
+
+export class CustomGuideStep extends GuideStep {
+    private _customData: any;
+    
+    constructor(stepId: string, customData: any) {
+        super(stepId);
+        this._customData = customData;
+    }
+    
+    public canTrigger(): boolean {
+        // 实现你的触发条件
+        const guideIndex = this._manager.getDataManager().getGuideIndex();
+        return guideIndex >= 5 && !this.isCompleted();
+    }
+    
+    public doExecute(): void {
+        // 实现你的引导逻辑
+        console.log("执行自定义引导步骤");
+        
+        // 显示自定义UI
+        this.showCustomUI();
+        
+        // 设置完成条件
+        this.setupCompletionCondition();
+    }
+    
+    private showCustomUI(): void {
+        // 显示你的自定义UI
+        const uiController = this._manager.getUIController();
+        uiController.showGuideUI("guild_1", "tap");
+    }
+    
+    private setupCompletionCondition(): void {
+        // 设置完成条件,例如监听某个事件
+        cc.systemEvent.on("custom_action_completed", this.onCustomActionCompleted, this);
+    }
+    
+    private onCustomActionCompleted(): void {
+        cc.systemEvent.off("custom_action_completed", this.onCustomActionCompleted, this);
+        this.complete();
+    }
+    
+    protected onComplete(): void {
+        // 清理工作
+        const uiController = this._manager.getUIController();
+        uiController.hideGuideUI("guild_1");
+    }
+}
+```
+
+### 2. 配置文件集成
+
+使用配置文件来管理引导步骤:
+
+```typescript
+import { GuideManager } from "./NewbieGuidePlugin/scripts/GuideManager";
+
+export class ConfigBasedGuideSystem {
+    private _guideConfig: any;
+    
+    public async loadAndInitGuide(): Promise<void> {
+        // 加载配置文件
+        this._guideConfig = await this.loadGuideConfig();
+        
+        const guideManager = GuideManager.getInstance();
+        guideManager.init();
+        
+        // 根据配置创建引导步骤
+        this.createStepsFromConfig();
+        
+        // 开始引导
+        if (this._guideConfig.settings.auto_start) {
+            guideManager.startGuide();
+        }
+    }
+    
+    private async loadGuideConfig(): Promise<any> {
+        return new Promise((resolve, reject) => {
+            cc.resources.load("NewbieGuidePlugin/config/guide_config", cc.JsonAsset, (err, asset) => {
+                if (err) {
+                    reject(err);
+                } else {
+                    resolve(asset.json);
+                }
+            });
+        });
+    }
+    
+    private createStepsFromConfig(): void {
+        const guideManager = GuideManager.getInstance();
+        
+        for (const stepConfig of this._guideConfig.guide_steps) {
+            const step = this.createStepFromConfig(stepConfig);
+            if (step) {
+                guideManager.registerStep(step);
+            }
+        }
+    }
+    
+    private createStepFromConfig(config: any): GuideStep {
+        switch (config.type) {
+            case "click":
+                return new ClickGuideStep(config.id, config.config.target_node);
+            case "drag":
+                return new DragGuideStep(config.id, config.config.source_node, config.config.target_node);
+            case "dialog":
+                return new DialogGuideStep(config.id, config.config.text);
+            // 添加更多类型...
+            default:
+                console.warn(`未知的引导步骤类型: ${config.type}`);
+                return null;
+        }
+    }
+}
+```
+
+### 3. 与游戏逻辑集成
+
+#### 3.1 监听游戏事件
+
+```typescript
+export class GameLogicIntegration {
+    private _guideManager: GuideManager;
+    
+    onLoad() {
+        this._guideManager = GuideManager.getInstance();
+        this._guideManager.init();
+        
+        // 监听游戏事件来触发引导
+        this.setupGameEventListeners();
+    }
+    
+    private setupGameEventListeners(): void {
+        // 监听玩家升级事件
+        cc.systemEvent.on("player_level_up", this.onPlayerLevelUp, this);
+        
+        // 监听物品合并事件
+        cc.systemEvent.on("items_merged", this.onItemsMerged, this);
+        
+        // 监听购买事件
+        cc.systemEvent.on("item_purchased", this.onItemPurchased, this);
+    }
+    
+    private onPlayerLevelUp(level: number): void {
+        // 根据等级触发不同的引导
+        if (level === 2) {
+            this.triggerGuideStep("level_2_guide");
+        } else if (level === 5) {
+            this.triggerGuideStep("level_5_guide");
+        }
+    }
+    
+    private onItemsMerged(mergedItems: any[]): void {
+        // 合并完成后可能触发下一步引导
+        this._guideManager.checkAndTriggerNextStep();
+    }
+    
+    private onItemPurchased(item: any): void {
+        // 购买完成后触发相关引导
+        if (item.type === "special_item") {
+            this.triggerGuideStep("special_item_guide");
+        }
+    }
+    
+    private triggerGuideStep(stepId: string): void {
+        // 手动触发特定的引导步骤
+        const currentStep = this._guideManager.getCurrentStep();
+        if (!currentStep || currentStep.stepId !== stepId) {
+            // 这里可以实现手动触发逻辑
+            console.log(`触发引导步骤: ${stepId}`);
+        }
+    }
+}
+```
+
+#### 3.2 条件检查集成
+
+```typescript
+export class ConditionChecker {
+    public static hasEnoughCoins(amount: number): boolean {
+        // 检查玩家是否有足够的金币
+        return GameData.getInstance().getCoins() >= amount;
+    }
+    
+    public static hasCompletedLevel(level: number): boolean {
+        // 检查是否完成了指定关卡
+        return GameData.getInstance().getCompletedLevels().includes(level);
+    }
+    
+    public static hasMergeableItems(): boolean {
+        // 检查是否有可合并的物品
+        const items = GameData.getInstance().getPlayerItems();
+        const itemCounts = new Map<string, number>();
+        
+        for (const item of items) {
+            const count = itemCounts.get(item.type) || 0;
+            itemCounts.set(item.type, count + 1);
+            
+            if (count >= 1) { // 有两个或以上相同类型的物品
+                return true;
+            }
+        }
+        
+        return false;
+    }
+}
+
+// 在引导步骤中使用条件检查
+export class ConditionalGuideStep extends GuideStep {
+    public canTrigger(): boolean {
+        const guideIndex = this._manager.getDataManager().getGuideIndex();
+        return guideIndex >= 3 && 
+               ConditionChecker.hasEnoughCoins(100) && 
+               ConditionChecker.hasMergeableItems();
+    }
+}
+```
+
+## 性能优化
+
+### 1. 延迟加载
+
+```typescript
+export class LazyGuideSystem {
+    private _guideManager: GuideManager;
+    private _isInitialized: boolean = false;
+    
+    public async initGuideWhenNeeded(): Promise<void> {
+        if (this._isInitialized) {
+            return;
+        }
+        
+        // 只在需要时初始化引导系统
+        this._guideManager = GuideManager.getInstance();
+        this._guideManager.init();
+        
+        // 延迟加载引导资源
+        await this.preloadGuideResources();
+        
+        this._isInitialized = true;
+    }
+    
+    private async preloadGuideResources(): Promise<void> {
+        return new Promise((resolve) => {
+            const resources = [
+                "NewbieGuidePlugin/prefabs/GuideUI",
+                "NewbieGuidePlugin/resources/spine/tut_hand"
+            ];
+            
+            cc.resources.load(resources, (err, assets) => {
+                if (err) {
+                    console.warn("引导资源加载失败", err);
+                } else {
+                    console.log("引导资源加载完成");
+                }
+                resolve();
+            });
+        });
+    }
+}
+```
+
+### 2. 内存管理
+
+```typescript
+export class GuideMemoryManager {
+    public static cleanupGuideResources(): void {
+        // 清理不再需要的引导资源
+        const guideManager = GuideManager.getInstance();
+        
+        if (!guideManager.isGuideActive()) {
+            // 隐藏所有引导UI
+            guideManager.getUIController().hideAllGuideUI();
+            
+            // 释放Spine动画资源
+            this.releaseSpineResources();
+            
+            // 清理临时节点
+            this.cleanupTempNodes();
+        }
+    }
+    
+    private static releaseSpineResources(): void {
+        // 释放Spine资源
+        cc.resources.release("NewbieGuidePlugin/resources/spine/tut_hand");
+    }
+    
+    private static cleanupTempNodes(): void {
+        // 清理临时创建的节点
+        const canvas = cc.find("Canvas");
+        if (canvas) {
+            const tempNodes = canvas.getChildByName("guide_temp_nodes");
+            if (tempNodes) {
+                tempNodes.removeFromParent();
+            }
+        }
+    }
+}
+```
+
+## 调试和测试
+
+### 1. 调试模式
+
+```typescript
+export class GuideDebugger {
+    public static enableDebugMode(): void {
+        const guideManager = GuideManager.getInstance();
+        guideManager.getDataManager().setStepData("system", "debug_mode", true);
+        
+        // 添加调试UI
+        this.createDebugUI();
+    }
+    
+    private static createDebugUI(): void {
+        const debugNode = new cc.Node("GuideDebugUI");
+        const canvas = cc.find("Canvas");
+        canvas.addChild(debugNode);
+        debugNode.zIndex = 9999;
+        
+        // 添加重置按钮
+        const resetBtn = this.createButton("重置引导", () => {
+            GuideManager.getInstance().resetGuide();
+        });
+        debugNode.addChild(resetBtn);
+        
+        // 添加跳过按钮
+        const skipBtn = this.createButton("跳过当前步骤", () => {
+            GuideManager.getInstance().skipCurrentStep();
+        });
+        debugNode.addChild(skipBtn);
+        
+        // 添加状态显示
+        const statusLabel = this.createStatusLabel();
+        debugNode.addChild(statusLabel);
+    }
+    
+    private static createButton(text: string, callback: () => void): cc.Node {
+        const btnNode = new cc.Node("btn");
+        const button = btnNode.addComponent(cc.Button);
+        const label = btnNode.addComponent(cc.Label);
+        label.string = text;
+        
+        btnNode.on(cc.Node.EventType.TOUCH_END, callback);
+        
+        return btnNode;
+    }
+    
+    private static createStatusLabel(): cc.Node {
+        const labelNode = new cc.Node("status");
+        const label = labelNode.addComponent(cc.Label);
+        
+        // 定期更新状态
+        setInterval(() => {
+            const guideManager = GuideManager.getInstance();
+            const currentStep = guideManager.getCurrentStep();
+            const guideIndex = guideManager.getDataManager().getGuideIndex();
+            
+            label.string = `引导状态: ${guideManager.isGuideActive() ? '进行中' : '未激活'}\n` +
+                          `当前步骤: ${currentStep ? currentStep.stepId : '无'}\n` +
+                          `引导索引: ${guideIndex}`;
+        }, 1000);
+        
+        return labelNode;
+    }
+}
+```
+
+### 2. 单元测试
+
+```typescript
+export class GuideSystemTests {
+    public static runAllTests(): void {
+        console.log("开始引导系统测试...");
+        
+        this.testDataManager();
+        this.testUIController();
+        this.testGuideSteps();
+        
+        console.log("引导系统测试完成");
+    }
+    
+    private static testDataManager(): void {
+        console.log("测试数据管理器...");
+        
+        const dataManager = new GuideDataManager();
+        dataManager.init();
+        
+        // 测试引导索引
+        console.assert(dataManager.getGuideIndex() === 0, "初始引导索引应为0");
+        
+        dataManager.incrementGuideIndex();
+        console.assert(dataManager.getGuideIndex() === 1, "增加后引导索引应为1");
+        
+        // 测试步骤完成状态
+        console.assert(!dataManager.isStepCompleted("test_step"), "未标记的步骤应为未完成");
+        
+        dataManager.markStepCompleted("test_step");
+        console.assert(dataManager.isStepCompleted("test_step"), "已标记的步骤应为已完成");
+        
+        console.log("数据管理器测试通过");
+    }
+    
+    private static testUIController(): void {
+        console.log("测试UI控制器...");
+        
+        const uiController = new GuideUIController();
+        uiController.init();
+        
+        // 创建测试节点
+        const testNode = new cc.Node("test_guide_node");
+        uiController.registerGuideNode("test_node", testNode);
+        
+        console.assert(uiController.hasGuideNode("test_node"), "应该能找到注册的节点");
+        console.assert(uiController.getGuideNode("test_node") === testNode, "应该返回正确的节点");
+        
+        console.log("UI控制器测试通过");
+    }
+    
+    private static testGuideSteps(): void {
+        console.log("测试引导步骤...");
+        
+        // 创建测试步骤
+        class TestGuideStep extends GuideStep {
+            public canTrigger(): boolean {
+                return true;
+            }
+            
+            public doExecute(): void {
+                // 模拟执行
+                setTimeout(() => {
+                    this.complete();
+                }, 100);
+            }
+        }
+        
+        const testStep = new TestGuideStep("test_step");
+        
+        console.assert(!testStep.isCompleted(), "新步骤应为未完成状态");
+        console.assert(!testStep.isExecuting(), "新步骤应为未执行状态");
+        
+        testStep.execute();
+        console.assert(testStep.isExecuting(), "执行中的步骤应为执行状态");
+        
+        // 等待完成
+        setTimeout(() => {
+            console.assert(testStep.isCompleted(), "完成的步骤应为完成状态");
+            console.assert(!testStep.isExecuting(), "完成的步骤应不为执行状态");
+            console.log("引导步骤测试通过");
+        }, 200);
+    }
+}
+```
+
+## 常见问题解决
+
+### 1. 引导UI不显示
+
+**问题:** 引导UI节点不显示或位置不正确。
+
+**解决方案:**
+1. 检查节点是否正确注册
+2. 确认节点的 `active` 属性
+3. 检查节点的层级(zIndex)
+4. 验证Spine资源是否正确加载
+
+```typescript
+// 调试代码
+const uiController = GuideManager.getInstance().getUIController();
+const guideNode = uiController.getGuideNode("guild_1");
+
+if (!guideNode) {
+    console.error("引导节点未找到");
+} else {
+    console.log("节点状态:", {
+        active: guideNode.active,
+        position: guideNode.position,
+        zIndex: guideNode.zIndex,
+        parent: guideNode.parent ? guideNode.parent.name : "无父节点"
+    });
+}
+```
+
+### 2. 引导步骤不触发
+
+**问题:** 引导步骤的 `canTrigger()` 返回 false。
+
+**解决方案:**
+1. 检查触发条件是否正确
+2. 验证引导索引是否符合预期
+3. 确认游戏状态是否满足条件
+
+```typescript
+// 调试代码
+const guideManager = GuideManager.getInstance();
+const dataManager = guideManager.getDataManager();
+
+console.log("引导调试信息:", {
+    guideIndex: dataManager.getGuideIndex(),
+    isNewUser: dataManager.isNewUser(),
+    isGuideActive: guideManager.isGuideActive(),
+    currentStep: guideManager.getCurrentStep()?.stepId || "无"
+});
+```
+
+### 3. 数据持久化失败
+
+**问题:** 引导进度没有正确保存。
+
+**解决方案:**
+1. 检查本地存储权限
+2. 验证数据格式是否正确
+3. 确认保存时机是否合适
+
+```typescript
+// 手动保存测试
+const dataManager = GuideManager.getInstance().getDataManager();
+const exportedData = dataManager.exportGuideData();
+console.log("导出的引导数据:", exportedData);
+
+// 尝试重新导入
+const success = dataManager.importGuideData(exportedData);
+console.log("数据导入结果:", success);
+```
+
+通过以上集成指南,你应该能够成功将新手引导插件集成到你的 Cocos Creator 项目中。如果遇到问题,请参考 API 文档或联系技术支持。

+ 11 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/docs/Integration_Guide.md.meta

@@ -0,0 +1,11 @@
+{
+  "ver": "1.0.1",
+  "importer": "text",
+  "imported": true,
+  "uuid": "ec78d3ef-8f0c-4923-8da8-acd77b890c0a",
+  "files": [
+    ".json"
+  ],
+  "subMetas": {},
+  "userData": {}
+}

+ 9 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/examples.meta

@@ -0,0 +1,9 @@
+{
+  "ver": "1.2.0",
+  "importer": "directory",
+  "imported": true,
+  "uuid": "d50b7cb2-5471-4689-bc11-316ca3996204",
+  "files": [],
+  "subMetas": {},
+  "userData": {}
+}

+ 700 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/examples/AdvancedExample.ts

@@ -0,0 +1,700 @@
+/**
+ * 新手引导插件高级使用示例
+ * 演示自定义引导步骤、复杂条件判断和动态引导流程
+ */
+
+import { GuideManager } from "../scripts/GuideManager";
+import { GuideStep } from "../scripts/GuideStep";
+import { ClickGuideStep, DragGuideStep, DialogGuideStep, WaitConditionGuideStep } from "../scripts/GuideSteps";
+
+const { ccclass, property } = cc._decorator;
+
+/**
+ * 自定义引导步骤:商店购买引导
+ */
+class ShopBuyGuideStep extends GuideStep {
+    private shopItemId: string;
+    private requiredCoins: number;
+    
+    constructor(stepId: string, shopItemId: string, requiredCoins: number) {
+        super(stepId);
+        this.shopItemId = shopItemId;
+        this.requiredCoins = requiredCoins;
+    }
+    
+    canTrigger(): boolean {
+        // 检查玩家是否有足够金币
+        const gameData = this.getGameData();
+        return gameData.coins >= this.requiredCoins;
+    }
+    
+    execute(): void {
+        console.log(`开始商店购买引导: ${this.shopItemId}`);
+        
+        // 高亮商店按钮
+        const shopButton = cc.find("Canvas/UI/ShopButton");
+        if (shopButton) {
+            this.highlightNode(shopButton);
+        }
+        
+        // 显示购买提示
+        this.showBuyTip();
+        
+        // 监听购买事件
+        cc.systemEvent.on("item_purchased", this.onItemPurchased, this);
+    }
+    
+    private showBuyTip(): void {
+        const tipText = `使用 ${this.requiredCoins} 金币购买 ${this.shopItemId}`;
+        // 这里可以显示自定义的提示UI
+        console.log(tipText);
+    }
+    
+    private onItemPurchased(itemId: string): void {
+        if (itemId === this.shopItemId) {
+            this.complete();
+        }
+    }
+    
+    onComplete(): void {
+        // 清理事件监听
+        cc.systemEvent.off("item_purchased", this.onItemPurchased, this);
+        
+        // 移除高亮
+        const shopButton = cc.find("Canvas/UI/ShopButton");
+        if (shopButton) {
+            this.removeHighlight(shopButton);
+        }
+        
+        console.log(`商店购买引导完成: ${this.shopItemId}`);
+    }
+    
+    private getGameData(): any {
+        // 获取游戏数据的示例方法
+        return {
+            coins: 1000, // 示例数据
+            level: 5
+        };
+    }
+    
+    private highlightNode(node: cc.Node): void {
+        // 添加高亮效果的示例实现
+        const highlight = new cc.Node("Highlight");
+        const graphics = highlight.addComponent(cc.Graphics);
+        
+        graphics.strokeColor = cc.Color.YELLOW;
+        graphics.lineWidth = 3;
+        graphics.rect(-node.width/2, -node.height/2, node.width, node.height);
+        graphics.stroke();
+        
+        node.addChild(highlight);
+    }
+    
+    private removeHighlight(node: cc.Node): void {
+        const highlight = node.getChildByName("Highlight");
+        if (highlight) {
+            highlight.removeFromParent();
+        }
+    }
+}
+
+/**
+ * 自定义引导步骤:多选择引导
+ */
+class MultiChoiceGuideStep extends GuideStep {
+    private choices: Array<{id: string, text: string, target: string}>;
+    private selectedChoice: string = null;
+    
+    constructor(stepId: string, choices: Array<{id: string, text: string, target: string}>) {
+        super(stepId);
+        this.choices = choices;
+    }
+    
+    canTrigger(): boolean {
+        // 检查所有选择目标是否都存在
+        return this.choices.every(choice => cc.find(choice.target) !== null);
+    }
+    
+    execute(): void {
+        console.log("开始多选择引导");
+        
+        // 显示选择UI
+        this.showChoiceUI();
+        
+        // 高亮所有选择项
+        this.choices.forEach(choice => {
+            const target = cc.find(choice.target);
+            if (target) {
+                this.addChoiceListener(target, choice.id);
+                this.highlightChoice(target);
+            }
+        });
+    }
+    
+    private showChoiceUI(): void {
+        // 创建选择提示UI
+        const choiceUI = new cc.Node("ChoiceUI");
+        const label = choiceUI.addComponent(cc.Label);
+        label.string = "请选择一个操作:";
+        label.fontSize = 24;
+        
+        // 添加到场景中
+        const canvas = cc.find("Canvas");
+        if (canvas) {
+            canvas.addChild(choiceUI);
+            choiceUI.setPosition(0, 200);
+        }
+    }
+    
+    private addChoiceListener(target: cc.Node, choiceId: string): void {
+        const listener = () => {
+            this.selectedChoice = choiceId;
+            this.complete();
+        };
+        
+        target.on(cc.Node.EventType.TOUCH_END, listener);
+        
+        // 保存监听器引用以便后续清理
+        target['_guideListener'] = listener;
+    }
+    
+    private highlightChoice(target: cc.Node): void {
+        // 添加选择高亮效果
+        const pulse = cc.tween(target)
+            .to(0.5, { scale: 1.1 })
+            .to(0.5, { scale: 1.0 })
+            .union()
+            .repeatForever();
+        
+        pulse.start();
+        target['_guideTween'] = pulse;
+    }
+    
+    onComplete(): void {
+        console.log(`多选择引导完成,选择了: ${this.selectedChoice}`);
+        
+        // 清理所有监听器和动画
+        this.choices.forEach(choice => {
+            const target = cc.find(choice.target);
+            if (target) {
+                // 移除监听器
+                if (target['_guideListener']) {
+                    target.off(cc.Node.EventType.TOUCH_END, target['_guideListener']);
+                    delete target['_guideListener'];
+                }
+                
+                // 停止动画
+                if (target['_guideTween']) {
+                    target['_guideTween'].stop();
+                    delete target['_guideTween'];
+                    target.scale = 1.0;
+                }
+            }
+        });
+        
+        // 移除选择UI
+        const choiceUI = cc.find("Canvas/ChoiceUI");
+        if (choiceUI) {
+            choiceUI.removeFromParent();
+        }
+        
+        // 保存选择结果
+        this.setStepData("selectedChoice", this.selectedChoice);
+    }
+}
+
+@ccclass
+export default class AdvancedExample extends cc.Component {
+    
+    @property(cc.Node)
+    shopButton: cc.Node = null;
+    
+    @property(cc.Node)
+    upgradeButton: cc.Node = null;
+    
+    @property(cc.Node)
+    settingsButton: cc.Node = null;
+    
+    @property(cc.Label)
+    coinsLabel: cc.Label = null;
+    
+    private _guideManager: GuideManager;
+    private _gameData = {
+        coins: 500,
+        level: 1,
+        hasShopItem: false,
+        tutorialCompleted: false
+    };
+    
+    onLoad() {
+        this.initAdvancedGuideSystem();
+        this.setupGameLogic();
+    }
+    
+    start() {
+        // 根据游戏状态决定引导流程
+        this.startConditionalGuide();
+    }
+    
+    /**
+     * 初始化高级引导系统
+     */
+    private initAdvancedGuideSystem(): void {
+        this._guideManager = GuideManager.getInstance();
+        this._guideManager.init();
+        
+        // 注册复杂的引导流程
+        this.registerAdvancedGuideFlow();
+        
+        // 设置动态条件检查
+        this.setupDynamicConditions();
+        
+        // 监听游戏事件
+        this.setupGameEventListeners();
+    }
+    
+    /**
+     * 注册高级引导流程
+     */
+    private registerAdvancedGuideFlow(): void {
+        // 分支引导:根据玩家选择决定后续流程
+        const branchStep = new MultiChoiceGuideStep("choose_path", [
+            { id: "shop", text: "商店购买", target: "Canvas/UI/ShopButton" },
+            { id: "upgrade", text: "升级系统", target: "Canvas/UI/UpgradeButton" },
+            { id: "settings", text: "游戏设置", target: "Canvas/UI/SettingsButton" }
+        ]);
+        this._guideManager.registerStep(branchStep);
+        
+        // 商店流程
+        this.registerShopFlow();
+        
+        // 升级流程
+        this.registerUpgradeFlow();
+        
+        // 设置流程
+        this.registerSettingsFlow();
+        
+        // 条件步骤:等待金币达到要求
+        const waitCoinsStep = new WaitConditionGuideStep(
+            "wait_enough_coins",
+            () => this._gameData.coins >= 1000,
+            60000, // 60秒超时
+            "收集 1000 金币来解锁高级功能"
+        );
+        this._guideManager.registerStep(waitCoinsStep);
+        
+        // 动态步骤:根据之前的选择执行不同逻辑
+        const dynamicStep = new class extends GuideStep {
+            constructor(private parent: AdvancedExample) {
+                super("dynamic_step");
+            }
+            
+            canTrigger(): boolean {
+                const branchData = this.getStepData("choose_path");
+                return branchData && branchData.selectedChoice;
+            }
+            
+            execute(): void {
+                const branchData = this.getStepData("choose_path");
+                const choice = branchData.selectedChoice;
+                
+                console.log(`执行动态步骤,基于选择: ${choice}`);
+                
+                // 根据选择执行不同的逻辑
+                switch (choice) {
+                    case "shop":
+                        this.parent.executeShopLogic();
+                        break;
+                    case "upgrade":
+                        this.parent.executeUpgradeLogic();
+                        break;
+                    case "settings":
+                        this.parent.executeSettingsLogic();
+                        break;
+                }
+                
+                // 延迟完成,模拟异步操作
+                setTimeout(() => this.complete(), 2000);
+            }
+        }(this);
+        
+        this._guideManager.registerStep(dynamicStep);
+    }
+    
+    /**
+     * 注册商店流程
+     */
+    private registerShopFlow(): void {
+        // 商店购买引导
+        const shopBuyStep = new ShopBuyGuideStep("shop_buy", "power_up", 200);
+        this._guideManager.registerStep(shopBuyStep);
+        
+        // 购买后的确认步骤
+        const confirmBuyStep = new WaitConditionGuideStep(
+            "confirm_purchase",
+            () => this._gameData.hasShopItem,
+            10000,
+            "确认购买完成"
+        );
+        this._guideManager.registerStep(confirmBuyStep);
+    }
+    
+    /**
+     * 注册升级流程
+     */
+    private registerUpgradeFlow(): void {
+        const upgradeStep = new ClickGuideStep("upgrade_click", "Canvas/UI/UpgradeButton");
+        this._guideManager.registerStep(upgradeStep);
+        
+        const upgradeWaitStep = new WaitConditionGuideStep(
+            "wait_upgrade",
+            () => this._gameData.level >= 2,
+            15000,
+            "等待升级完成"
+        );
+        this._guideManager.registerStep(upgradeWaitStep);
+    }
+    
+    /**
+     * 注册设置流程
+     */
+    private registerSettingsFlow(): void {
+        const settingsStep = new ClickGuideStep("settings_click", "Canvas/UI/SettingsButton");
+        this._guideManager.registerStep(settingsStep);
+        
+        const settingsDialogStep = new DialogGuideStep(
+            "settings_info",
+            "在设置中,你可以调整音效、画质等选项来优化游戏体验。"
+        );
+        this._guideManager.registerStep(settingsDialogStep);
+    }
+    
+    /**
+     * 设置动态条件检查
+     */
+    private setupDynamicConditions(): void {
+        // 定期检查游戏状态,触发相应的引导步骤
+        this.schedule(() => {
+            this.checkGuideConditions();
+        }, 1.0); // 每秒检查一次
+    }
+    
+    /**
+     * 检查引导条件
+     */
+    private checkGuideConditions(): void {
+        const currentStep = this._guideManager.getCurrentStep();
+        
+        // 如果当前没有活跃的引导步骤,检查是否需要触发新的步骤
+        if (!currentStep) {
+            // 根据游戏状态决定下一个引导步骤
+            if (this._gameData.coins >= 1000 && !this._gameData.tutorialCompleted) {
+                this._guideManager.triggerStep("wait_enough_coins");
+            }
+        }
+        
+        // 检查并触发下一步
+        this._guideManager.checkAndTriggerNextStep();
+    }
+    
+    /**
+     * 设置游戏事件监听
+     */
+    private setupGameEventListeners(): void {
+        // 监听金币变化
+        cc.systemEvent.on("coins_changed", this.onCoinsChanged, this);
+        
+        // 监听等级变化
+        cc.systemEvent.on("level_changed", this.onLevelChanged, this);
+        
+        // 监听物品购买
+        cc.systemEvent.on("item_purchased", this.onItemPurchased, this);
+        
+        // 监听引导事件
+        this._guideManager.on('step_started', this.onStepStarted, this);
+        this._guideManager.on('step_completed', this.onStepCompleted, this);
+        this._guideManager.on('guide_completed', this.onGuideCompleted, this);
+    }
+    
+    /**
+     * 设置游戏逻辑
+     */
+    private setupGameLogic(): void {
+        // 商店按钮
+        if (this.shopButton) {
+            this.shopButton.on(cc.Node.EventType.TOUCH_END, this.onShopButtonClick, this);
+        }
+        
+        // 升级按钮
+        if (this.upgradeButton) {
+            this.upgradeButton.on(cc.Node.EventType.TOUCH_END, this.onUpgradeButtonClick, this);
+        }
+        
+        // 设置按钮
+        if (this.settingsButton) {
+            this.settingsButton.on(cc.Node.EventType.TOUCH_END, this.onSettingsButtonClick, this);
+        }
+        
+        // 定期增加金币(模拟游戏收益)
+        this.schedule(() => {
+            this.addCoins(10);
+        }, 2.0);
+        
+        this.updateUI();
+    }
+    
+    /**
+     * 开始条件引导
+     */
+    private startConditionalGuide(): void {
+        const dataManager = this._guideManager.getDataManager();
+        
+        // 检查是否是新用户或未完成引导
+        if (dataManager.isNewUser() || !dataManager.isGuideCompleted()) {
+            // 根据游戏进度决定从哪个步骤开始
+            const savedProgress = dataManager.getGuideProgress();
+            
+            if (savedProgress.completedSteps.length === 0) {
+                // 新用户,从选择分支开始
+                this._guideManager.triggerStep("choose_path");
+            } else {
+                // 继续之前的进度
+                this._guideManager.resumeGuide();
+            }
+        }
+    }
+    
+    // 游戏逻辑方法
+    private addCoins(amount: number): void {
+        this._gameData.coins += amount;
+        cc.systemEvent.emit("coins_changed", this._gameData.coins);
+        this.updateUI();
+    }
+    
+    private levelUp(): void {
+        this._gameData.level++;
+        cc.systemEvent.emit("level_changed", this._gameData.level);
+        this.updateUI();
+    }
+    
+    private updateUI(): void {
+        if (this.coinsLabel) {
+            this.coinsLabel.string = `金币: ${this._gameData.coins}`;
+        }
+    }
+    
+    // 按钮点击处理
+    private onShopButtonClick(): void {
+        console.log("商店按钮被点击");
+        // 模拟购买逻辑
+        if (this._gameData.coins >= 200) {
+            this._gameData.coins -= 200;
+            this._gameData.hasShopItem = true;
+            cc.systemEvent.emit("item_purchased", "power_up");
+            cc.systemEvent.emit("coins_changed", this._gameData.coins);
+            this.updateUI();
+        }
+    }
+    
+    private onUpgradeButtonClick(): void {
+        console.log("升级按钮被点击");
+        // 模拟升级逻辑
+        if (this._gameData.coins >= 100) {
+            this._gameData.coins -= 100;
+            this.levelUp();
+        }
+    }
+    
+    private onSettingsButtonClick(): void {
+        console.log("设置按钮被点击");
+        // 这里可以打开设置面板
+    }
+    
+    // 动态逻辑执行方法
+    public executeShopLogic(): void {
+        console.log("执行商店相关逻辑");
+        // 可以触发特殊的商店事件或动画
+    }
+    
+    public executeUpgradeLogic(): void {
+        console.log("执行升级相关逻辑");
+        // 可以显示升级特效或解锁新功能
+    }
+    
+    public executeSettingsLogic(): void {
+        console.log("执行设置相关逻辑");
+        // 可以显示设置教程或提示
+    }
+    
+    // 事件处理方法
+    private onCoinsChanged(coins: number): void {
+        console.log(`金币变化: ${coins}`);
+    }
+    
+    private onLevelChanged(level: number): void {
+        console.log(`等级变化: ${level}`);
+    }
+    
+    private onItemPurchased(itemId: string): void {
+        console.log(`物品购买: ${itemId}`);
+    }
+    
+    private onStepStarted(step: any): void {
+        console.log(`引导步骤开始: ${step.stepId}`);
+        
+        // 可以根据步骤类型播放不同的音效或动画
+        this.playStepStartEffect(step.stepId);
+    }
+    
+    private onStepCompleted(step: any): void {
+        console.log(`引导步骤完成: ${step.stepId}`);
+        
+        // 可以给予奖励或播放完成特效
+        this.giveStepReward(step.stepId);
+    }
+    
+    private onGuideCompleted(): void {
+        console.log("高级引导流程完成!");
+        
+        this._gameData.tutorialCompleted = true;
+        
+        // 给予完成奖励
+        this.addCoins(500);
+        
+        // 显示完成庆祝动画
+        this.showCompletionCelebration();
+    }
+    
+    private playStepStartEffect(stepId: string): void {
+        // 播放步骤开始音效
+        console.log(`播放步骤开始特效: ${stepId}`);
+    }
+    
+    private giveStepReward(stepId: string): void {
+        // 根据步骤给予不同奖励
+        const rewards = {
+            "choose_path": 50,
+            "shop_buy": 100,
+            "upgrade_click": 75,
+            "settings_click": 25
+        };
+        
+        const reward = rewards[stepId] || 10;
+        this.addCoins(reward);
+        
+        console.log(`获得步骤奖励: ${reward} 金币`);
+    }
+    
+    private showCompletionCelebration(): void {
+        // 创建庆祝动画
+        const celebration = new cc.Node("Celebration");
+        const label = celebration.addComponent(cc.Label);
+        label.string = "🎉 高级引导完成!🎉";
+        label.fontSize = 36;
+        
+        const canvas = cc.find("Canvas");
+        if (canvas) {
+            canvas.addChild(celebration);
+            
+            // 庆祝动画
+            const tween = cc.tween(celebration)
+                .to(0.5, { scale: 1.2 })
+                .to(0.5, { scale: 1.0 })
+                .delay(2.0)
+                .to(0.5, { opacity: 0 })
+                .call(() => celebration.removeFromParent());
+            
+            tween.start();
+        }
+    }
+    
+    /**
+     * 调试功能:模拟游戏状态
+     */
+    public simulateGameState(state: any): void {
+        Object.assign(this._gameData, state);
+        this.updateUI();
+        
+        // 触发相应的事件
+        cc.systemEvent.emit("coins_changed", this._gameData.coins);
+        cc.systemEvent.emit("level_changed", this._gameData.level);
+        
+        console.log("游戏状态已更新:", this._gameData);
+    }
+    
+    /**
+     * 调试功能:获取详细状态
+     */
+    public getDetailedStatus(): any {
+        const guideManager = this._guideManager;
+        const dataManager = guideManager.getDataManager();
+        
+        return {
+            gameData: this._gameData,
+            guideStatus: {
+                isActive: guideManager.isGuideActive(),
+                currentStep: guideManager.getCurrentStep()?.stepId,
+                completedSteps: dataManager.getGuideProgress().completedSteps,
+                guideIndex: dataManager.getGuideIndex(),
+                isCompleted: dataManager.isGuideCompleted()
+            },
+            registeredSteps: guideManager.getAllSteps().map(step => step.stepId)
+        };
+    }
+    
+    onDestroy() {
+        // 清理所有事件监听
+        cc.systemEvent.off("coins_changed", this.onCoinsChanged, this);
+        cc.systemEvent.off("level_changed", this.onLevelChanged, this);
+        cc.systemEvent.off("item_purchased", this.onItemPurchased, this);
+        
+        if (this._guideManager) {
+            this._guideManager.off('step_started', this.onStepStarted, this);
+            this._guideManager.off('step_completed', this.onStepCompleted, this);
+            this._guideManager.off('guide_completed', this.onGuideCompleted, this);
+        }
+        
+        // 清理定时器
+        this.unscheduleAllCallbacks();
+    }
+}
+
+// 高级调试功能
+if (CC_DEBUG) {
+    (window as any).AdvancedGuideDebug = {
+        simulateRichPlayer: () => {
+            const scene = cc.director.getScene();
+            const example = scene.getComponentInChildren(AdvancedExample);
+            if (example) {
+                example.simulateGameState({
+                    coins: 2000,
+                    level: 5,
+                    hasShopItem: true
+                });
+            }
+        },
+        
+        simulatePoorPlayer: () => {
+            const scene = cc.director.getScene();
+            const example = scene.getComponentInChildren(AdvancedExample);
+            if (example) {
+                example.simulateGameState({
+                    coins: 50,
+                    level: 1,
+                    hasShopItem: false
+                });
+            }
+        },
+        
+        getStatus: () => {
+            const scene = cc.director.getScene();
+            const example = scene.getComponentInChildren(AdvancedExample);
+            return example ? example.getDetailedStatus() : null;
+        }
+    };
+    
+    console.log("高级引导调试功能已启用:");
+    console.log("- AdvancedGuideDebug.simulateRichPlayer() - 模拟富有玩家");
+    console.log("- AdvancedGuideDebug.simulatePoorPlayer() - 模拟贫穷玩家");
+    console.log("- AdvancedGuideDebug.getStatus() - 获取详细状态");
+}

+ 9 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/examples/AdvancedExample.ts.meta

@@ -0,0 +1,9 @@
+{
+  "ver": "4.0.24",
+  "importer": "typescript",
+  "imported": true,
+  "uuid": "bae35108-1c71-47ad-8688-39b81bf2d0ce",
+  "files": [],
+  "subMetas": {},
+  "userData": {}
+}

+ 401 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/examples/BasicExample.ts

@@ -0,0 +1,401 @@
+/**
+ * 新手引导插件基础使用示例
+ * 演示如何在游戏中集成和使用新手引导系统
+ */
+
+import { GuideManager } from "../scripts/GuideManager";
+import { ClickGuideStep, DragGuideStep, DialogGuideStep, WaitConditionGuideStep } from "../scripts/GuideSteps";
+
+const { ccclass, property } = cc._decorator;
+
+@ccclass
+export default class BasicExample extends cc.Component {
+    
+    @property(cc.Node)
+    startButton: cc.Node = null;
+    
+    @property(cc.Node)
+    item1: cc.Node = null;
+    
+    @property(cc.Node)
+    item2: cc.Node = null;
+    
+    @property(cc.Label)
+    levelLabel: cc.Label = null;
+    
+    private _guideManager: GuideManager;
+    private _playerLevel: number = 1;
+    
+    onLoad() {
+        // 初始化引导系统
+        this.initGuideSystem();
+        
+        // 设置游戏逻辑
+        this.setupGameLogic();
+    }
+    
+    start() {
+        // 检查是否需要开始引导
+        if (this.shouldStartGuide()) {
+            this._guideManager.startGuide();
+        }
+    }
+    
+    /**
+     * 初始化引导系统
+     */
+    private initGuideSystem(): void {
+        this._guideManager = GuideManager.getInstance();
+        this._guideManager.init();
+        
+        // 注册引导步骤
+        this.registerGuideSteps();
+        
+        // 监听引导事件
+        this.setupGuideEventListeners();
+    }
+    
+    /**
+     * 注册所有引导步骤
+     */
+    private registerGuideSteps(): void {
+        // 步骤1: 欢迎对话
+        const welcomeStep = new DialogGuideStep(
+            "welcome", 
+            "欢迎来到游戏!让我们开始新手引导,学习游戏的基本操作。"
+        );
+        this._guideManager.registerStep(welcomeStep);
+        
+        // 步骤2: 点击开始按钮
+        const clickStartStep = new ClickGuideStep(
+            "click_start", 
+            "Canvas/UI/StartButton"
+        );
+        this._guideManager.registerStep(clickStartStep);
+        
+        // 步骤3: 拖拽物品
+        const dragItemStep = new DragGuideStep(
+            "drag_item",
+            "Canvas/GameArea/Item1",
+            "Canvas/GameArea/Item2"
+        );
+        this._guideManager.registerStep(dragItemStep);
+        
+        // 步骤4: 等待升级
+        const waitLevelUpStep = new WaitConditionGuideStep(
+            "wait_level_up",
+            () => this._playerLevel >= 2,
+            30000 // 30秒超时
+        );
+        this._guideManager.registerStep(waitLevelUpStep);
+        
+        // 步骤5: 完成引导
+        const completeStep = new DialogGuideStep(
+            "guide_complete",
+            "恭喜!你已经完成了新手引导。现在可以自由探索游戏了!"
+        );
+        this._guideManager.registerStep(completeStep);
+    }
+    
+    /**
+     * 设置引导事件监听
+     */
+    private setupGuideEventListeners(): void {
+        // 监听步骤完成事件
+        this._guideManager.on('step_completed', this.onStepCompleted, this);
+        
+        // 监听引导完成事件
+        this._guideManager.on('guide_completed', this.onGuideCompleted, this);
+    }
+    
+    /**
+     * 设置游戏逻辑
+     */
+    private setupGameLogic(): void {
+        // 开始按钮点击事件
+        if (this.startButton) {
+            this.startButton.on(cc.Node.EventType.TOUCH_END, this.onStartButtonClick, this);
+        }
+        
+        // 物品拖拽事件
+        if (this.item1) {
+            this.setupItemDrag(this.item1);
+        }
+        
+        // 更新UI
+        this.updateUI();
+    }
+    
+    /**
+     * 设置物品拖拽
+     */
+    private setupItemDrag(item: cc.Node): void {
+        let isDragging = false;
+        let startPos: cc.Vec2;
+        
+        item.on(cc.Node.EventType.TOUCH_START, (event: cc.Event.EventTouch) => {
+            isDragging = true;
+            startPos = item.position;
+        });
+        
+        item.on(cc.Node.EventType.TOUCH_MOVE, (event: cc.Event.EventTouch) => {
+            if (!isDragging) return;
+            
+            const delta = event.getDelta();
+            item.position = item.position.add(delta);
+        });
+        
+        item.on(cc.Node.EventType.TOUCH_END, (event: cc.Event.EventTouch) => {
+            if (!isDragging) return;
+            isDragging = false;
+            
+            // 检查是否拖拽到目标位置
+            if (this.item2 && this.isNearTarget(item, this.item2)) {
+                this.onItemMerged();
+            } else {
+                // 回到原位置
+                item.position = startPos;
+            }
+        });
+    }
+    
+    /**
+     * 检查是否接近目标
+     */
+    private isNearTarget(item: cc.Node, target: cc.Node): boolean {
+        const distance = item.position.sub(target.position).mag();
+        return distance < 50; // 50像素内认为是接近
+    }
+    
+    /**
+     * 物品合并处理
+     */
+    private onItemMerged(): void {
+        console.log("物品合并成功!");
+        
+        // 升级
+        this._playerLevel++;
+        this.updateUI();
+        
+        // 触发升级事件
+        cc.systemEvent.emit("player_level_up", this._playerLevel);
+        
+        // 检查引导进度
+        this._guideManager.checkAndTriggerNextStep();
+    }
+    
+    /**
+     * 开始按钮点击处理
+     */
+    private onStartButtonClick(): void {
+        console.log("开始按钮被点击");
+        
+        // 这里可以添加游戏开始逻辑
+        this.startButton.active = false;
+        
+        // 检查引导进度
+        this._guideManager.checkAndTriggerNextStep();
+    }
+    
+    /**
+     * 更新UI显示
+     */
+    private updateUI(): void {
+        if (this.levelLabel) {
+            this.levelLabel.string = `等级: ${this._playerLevel}`;
+        }
+    }
+    
+    /**
+     * 检查是否应该开始引导
+     */
+    private shouldStartGuide(): boolean {
+        const dataManager = this._guideManager.getDataManager();
+        return dataManager.isNewUser() || !dataManager.isGuideCompleted();
+    }
+    
+    /**
+     * 步骤完成事件处理
+     */
+    private onStepCompleted(step: any): void {
+        console.log(`引导步骤完成: ${step.stepId}`);
+        
+        // 根据不同步骤执行不同的逻辑
+        switch (step.stepId) {
+            case "welcome":
+                // 欢迎步骤完成,可以显示开始按钮
+                if (this.startButton) {
+                    this.startButton.active = true;
+                }
+                break;
+                
+            case "click_start":
+                // 点击开始步骤完成,显示游戏区域
+                this.showGameArea();
+                break;
+                
+            case "drag_item":
+                // 拖拽步骤完成,可以添加更多物品
+                this.addMoreItems();
+                break;
+                
+            case "wait_level_up":
+                // 等级提升完成,显示祝贺信息
+                this.showCongratulations();
+                break;
+        }
+    }
+    
+    /**
+     * 引导完成事件处理
+     */
+    private onGuideCompleted(): void {
+        console.log("新手引导完成!");
+        
+        // 显示完成UI
+        this.showGuideCompleteUI();
+        
+        // 解锁所有功能
+        this.unlockAllFeatures();
+    }
+    
+    /**
+     * 显示游戏区域
+     */
+    private showGameArea(): void {
+        // 显示游戏物品
+        if (this.item1) this.item1.active = true;
+        if (this.item2) this.item2.active = true;
+        
+        console.log("游戏区域已显示");
+    }
+    
+    /**
+     * 添加更多物品
+     */
+    private addMoreItems(): void {
+        // 这里可以添加更多游戏物品
+        console.log("添加更多游戏物品");
+    }
+    
+    /**
+     * 显示祝贺信息
+     */
+    private showCongratulations(): void {
+        console.log("恭喜升级!");
+    }
+    
+    /**
+     * 显示引导完成UI
+     */
+    private showGuideCompleteUI(): void {
+        // 创建完成提示
+        const completeNode = new cc.Node("GuideComplete");
+        const label = completeNode.addComponent(cc.Label);
+        label.string = "新手引导完成!";
+        label.fontSize = 32;
+        
+        this.node.addChild(completeNode);
+        
+        // 3秒后自动隐藏
+        setTimeout(() => {
+            completeNode.removeFromParent();
+        }, 3000);
+    }
+    
+    /**
+     * 解锁所有功能
+     */
+    private unlockAllFeatures(): void {
+        // 解锁游戏的所有功能
+        console.log("所有功能已解锁");
+    }
+    
+    /**
+     * 调试功能:重置引导
+     */
+    public resetGuide(): void {
+        this._guideManager.resetGuide();
+        this._playerLevel = 1;
+        this.updateUI();
+        
+        // 重新显示开始按钮
+        if (this.startButton) {
+            this.startButton.active = true;
+        }
+        
+        console.log("引导已重置");
+    }
+    
+    /**
+     * 调试功能:跳过当前步骤
+     */
+    public skipCurrentStep(): void {
+        this._guideManager.skipCurrentStep();
+        console.log("已跳过当前步骤");
+    }
+    
+    /**
+     * 调试功能:获取引导状态
+     */
+    public getGuideStatus(): any {
+        const dataManager = this._guideManager.getDataManager();
+        const currentStep = this._guideManager.getCurrentStep();
+        
+        return {
+            isActive: this._guideManager.isGuideActive(),
+            currentStep: currentStep ? currentStep.stepId : null,
+            guideIndex: dataManager.getGuideIndex(),
+            isCompleted: dataManager.isGuideCompleted(),
+            progress: dataManager.getGuideProgress()
+        };
+    }
+    
+    onDestroy() {
+        // 清理事件监听
+        if (this._guideManager) {
+            this._guideManager.off('step_completed', this.onStepCompleted, this);
+            this._guideManager.off('guide_completed', this.onGuideCompleted, this);
+        }
+        
+        // 清理按钮事件
+        if (this.startButton) {
+            this.startButton.off(cc.Node.EventType.TOUCH_END, this.onStartButtonClick, this);
+        }
+    }
+}
+
+// 在全局作用域中添加调试函数(仅用于开发调试)
+if (CC_DEBUG) {
+    (window as any).GuideDebug = {
+        resetGuide: () => {
+            const scene = cc.director.getScene();
+            const example = scene.getComponentInChildren(BasicExample);
+            if (example) {
+                example.resetGuide();
+            }
+        },
+        
+        skipStep: () => {
+            const scene = cc.director.getScene();
+            const example = scene.getComponentInChildren(BasicExample);
+            if (example) {
+                example.skipCurrentStep();
+            }
+        },
+        
+        getStatus: () => {
+            const scene = cc.director.getScene();
+            const example = scene.getComponentInChildren(BasicExample);
+            if (example) {
+                return example.getGuideStatus();
+            }
+            return null;
+        }
+    };
+    
+    console.log("引导调试功能已启用:");
+    console.log("- GuideDebug.resetGuide() - 重置引导");
+    console.log("- GuideDebug.skipStep() - 跳过当前步骤");
+    console.log("- GuideDebug.getStatus() - 获取引导状态");
+}

+ 9 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/examples/BasicExample.ts.meta

@@ -0,0 +1,9 @@
+{
+  "ver": "4.0.24",
+  "importer": "typescript",
+  "imported": true,
+  "uuid": "d6606f85-7bda-4353-83d5-89c7a51bb2b3",
+  "files": [],
+  "subMetas": {},
+  "userData": {}
+}

+ 58 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/install.bat

@@ -0,0 +1,58 @@
+@echo off
+chcp 65001 >nul
+echo Installing Newbie Guide Plugin...
+echo.
+
+REM Check if in Cocos Creator project directory
+if not exist "assets" (
+    echo Error: Please run this script in Cocos Creator project root directory
+    pause
+    exit /b 1
+)
+
+REM Create plugin directory
+if not exist "assets\NewbieGuidePlugin" (
+    mkdir "assets\NewbieGuidePlugin"
+)
+
+REM Copy plugin files
+echo Copying plugin files...
+if exist "scripts" (
+    xcopy /E /Y "scripts" "assets\NewbieGuidePlugin\scripts\" >nul
+    echo   - scripts copied
+)
+if exist "resources" (
+    xcopy /E /Y "resources" "assets\NewbieGuidePlugin\resources\" >nul
+    echo   - resources copied
+)
+if exist "prefabs" (
+    xcopy /E /Y "prefabs" "assets\NewbieGuidePlugin\prefabs\" >nul
+    echo   - prefabs copied
+)
+if exist "guide_config.json" (
+    copy /Y "guide_config.json" "assets\NewbieGuidePlugin\" >nul
+    echo   - config copied
+)
+
+REM Copy example files (optional)
+echo.
+set /p install_examples="Install example files? (y/n): "
+if /i "%install_examples%"=="y" (
+    if not exist "assets\Examples" (
+        mkdir "assets\Examples"
+    )
+    if exist "examples" (
+        xcopy /E /Y "examples" "assets\Examples\NewbieGuideExamples\" >nul
+        echo Examples installed to assets/Examples/NewbieGuideExamples/
+    )
+)
+
+echo.
+echo [SUCCESS] Newbie Guide Plugin installed successfully!
+echo.
+echo Documentation:
+echo   - README.md (Plugin Overview)
+echo   - docs/API_Reference.md (API Reference)
+echo   - docs/Integration_Guide.md (Integration Guide)
+echo.
+pause

+ 12 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/install.bat.meta

@@ -0,0 +1,12 @@
+{
+  "ver": "1.0.0",
+  "importer": "*",
+  "imported": true,
+  "uuid": "fbd8121b-324a-4d0e-aed5-bb4be7226415",
+  "files": [
+    ".bat",
+    ".json"
+  ],
+  "subMetas": {},
+  "userData": {}
+}

+ 38 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/install.sh

@@ -0,0 +1,38 @@
+#!/bin/bash
+
+echo "正在安装新手引导插件..."
+echo
+
+# 检查是否在 Cocos Creator 项目目录中
+if [ ! -d "assets" ]; then
+    echo "错误: 请在 Cocos Creator 项目根目录中运行此脚本"
+    exit 1
+fi
+
+# 创建插件目录
+mkdir -p "assets/NewbieGuidePlugin"
+
+# 复制插件文件
+echo "复制插件文件..."
+cp -r "scripts" "assets/NewbieGuidePlugin/"
+cp -r "resources" "assets/NewbieGuidePlugin/"
+cp -r "prefabs" "assets/NewbieGuidePlugin/"
+cp "guide_config.json" "assets/NewbieGuidePlugin/"
+
+# 复制示例文件(可选)
+echo
+read -p "是否安装示例文件? (y/n): " install_examples
+if [[ $install_examples == "y" || $install_examples == "Y" ]]; then
+    mkdir -p "assets/Examples"
+    cp -r "examples" "assets/Examples/NewbieGuideExamples"
+    echo "示例文件已安装到 assets/Examples/NewbieGuideExamples/"
+fi
+
+echo
+echo "✅ 新手引导插件安装完成!"
+echo
+echo "📚 请查看以下文档了解如何使用:"
+echo "   - README.md (插件概述)"
+echo "   - docs/API_Reference.md (API参考)"
+echo "   - docs/Integration_Guide.md (集成指南)"
+echo

+ 12 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/install.sh.meta

@@ -0,0 +1,12 @@
+{
+  "ver": "1.0.0",
+  "importer": "*",
+  "imported": true,
+  "uuid": "b989081c-6ad7-4f44-ba92-e2053243052d",
+  "files": [
+    ".json",
+    ".sh"
+  ],
+  "subMetas": {},
+  "userData": {}
+}

+ 34 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/install_test.bat

@@ -0,0 +1,34 @@
+@echo off
+chcp 65001 >nul
+echo Testing Newbie Guide Plugin Installation Script...
+echo.
+
+echo Current directory: %CD%
+echo.
+
+REM List current directory contents
+echo Directory contents:
+dir /b
+echo.
+
+REM Check if in Cocos Creator project directory
+if not exist "assets" (
+    echo [INFO] No 'assets' folder found in current directory
+    echo [INFO] This script should be run from your Cocos Creator project root directory
+    echo [INFO] The project root directory should contain an 'assets' folder
+    echo.
+    echo [SOLUTION] To install the plugin:
+    echo 1. Copy this plugin folder to your Cocos Creator project directory
+    echo 2. Open Command Prompt in your Cocos Creator project root directory
+    echo 3. Run: NewbieGuidePlugin-v1.0.0\install.bat
+    echo.
+    pause
+    exit /b 1
+) else (
+    echo [SUCCESS] Found 'assets' folder - this is a valid Cocos Creator project directory
+    echo [INFO] Installation would proceed normally from here
+)
+
+echo.
+echo [TEST COMPLETE] Installation script is working correctly
+pause

+ 12 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/install_test.bat.meta

@@ -0,0 +1,12 @@
+{
+  "ver": "1.0.0",
+  "importer": "*",
+  "imported": true,
+  "uuid": "1768d6c6-4da9-44bd-922e-1f53a0837906",
+  "files": [
+    ".bat",
+    ".json"
+  ],
+  "subMetas": {},
+  "userData": {}
+}

+ 88 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/package.json

@@ -0,0 +1,88 @@
+{
+  "name": "newbie-guide-plugin",
+  "version": "1.0.0",
+  "description": "A comprehensive newbie guide system plugin for Cocos Creator games",
+  "main": "scripts/GuideManager.ts",
+  "keywords": [
+    "cocos-creator",
+    "game",
+    "tutorial",
+    "guide",
+    "newbie",
+    "plugin",
+    "typescript"
+  ],
+  "author": "Game Developer",
+  "license": "MIT",
+  "engines": {
+    "cocos-creator": ">=2.4.0"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/your-username/newbie-guide-plugin.git"
+  },
+  "bugs": {
+    "url": "https://github.com/your-username/newbie-guide-plugin/issues"
+  },
+  "homepage": "https://github.com/your-username/newbie-guide-plugin#readme",
+  "files": [
+    "scripts/",
+    "resources/",
+    "prefabs/",
+    "examples/",
+    "docs/",
+    "README.md",
+    "CHANGELOG.md",
+    "LICENSE"
+  ],
+  "scripts": {
+    "build": "echo 'Building plugin...'",
+    "test": "echo 'Running tests...'",
+    "package": "node package-script.js",
+    "install-example": "echo 'Installing example files...'"
+  },
+  "devDependencies": {
+    "@types/node": "^14.0.0"
+  },
+  "peerDependencies": {
+    "cocos-creator": ">=2.4.0"
+  },
+  "cocosCreator": {
+    "version": ">=2.4.0",
+    "pluginType": "asset",
+    "category": "gameplay",
+    "features": [
+      "newbie-guide",
+      "tutorial-system",
+      "step-management",
+      "data-persistence",
+      "ui-guidance"
+    ]
+  },
+  "config": {
+    "supportedLanguages": ["zh-CN", "en-US"],
+    "minGameVersion": "1.0.0",
+    "maxGameVersion": "*"
+  },
+  "installation": {
+    "steps": [
+      "1. Extract the plugin to your project's assets folder",
+      "2. Import the GuideManager script in your main scene",
+      "3. Configure guide steps in guide_config.json",
+      "4. Initialize the guide system in your game startup code"
+    ]
+  },
+  "changelog": {
+    "1.0.0": {
+      "date": "2024-01-15",
+      "changes": [
+        "Initial release",
+        "Core guide system implementation",
+        "Basic and advanced guide steps",
+        "Data persistence system",
+        "UI controller with Spine animations",
+        "Comprehensive documentation and examples"
+      ]
+    }
+  }
+}

+ 11 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/package.json.meta

@@ -0,0 +1,11 @@
+{
+  "ver": "2.0.1",
+  "importer": "json",
+  "imported": true,
+  "uuid": "50c699c5-4a3e-4ae9-8a81-f6c5d49f0be3",
+  "files": [
+    ".json"
+  ],
+  "subMetas": {},
+  "userData": {}
+}

+ 9 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/prefabs.meta

@@ -0,0 +1,9 @@
+{
+  "ver": "1.2.0",
+  "importer": "directory",
+  "imported": true,
+  "uuid": "9373f73a-dd9d-4b3a-a0f6-63f1115abc56",
+  "files": [],
+  "subMetas": {},
+  "userData": {}
+}

+ 295 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/prefabs/GuideUI.prefab

@@ -0,0 +1,295 @@
+[
+  {
+    "__type__": "cc.Prefab",
+    "_name": "GuideUI",
+    "_objFlags": 0,
+    "_native": "",
+    "data": {
+      "__id__": 1
+    },
+    "optimizationPolicy": 0,
+    "asyncLoadAssets": false,
+    "readonly": false,
+    "persistent": false
+  },
+  {
+    "__type__": "cc.Node",
+    "_name": "GuideUI",
+    "_objFlags": 0,
+    "_parent": null,
+    "_children": [
+      {
+        "__id__": 2
+      },
+      {
+        "__id__": 4
+      }
+    ],
+    "_active": true,
+    "_components": [],
+    "_prefab": {
+      "__id__": 6
+    },
+    "_opacity": 255,
+    "_color": {
+      "__type__": "cc.Color",
+      "r": 255,
+      "g": 255,
+      "b": 255,
+      "a": 255
+    },
+    "_contentSize": {
+      "__type__": "cc.Size",
+      "width": 0,
+      "height": 0
+    },
+    "_anchorPoint": {
+      "__type__": "cc.Vec2",
+      "x": 0.5,
+      "y": 0.5
+    },
+    "_trs": {
+      "__type__": "TypedArray",
+      "ctor": "Float64Array",
+      "array": [
+        0,
+        0,
+        0,
+        0,
+        0,
+        0,
+        1,
+        1,
+        1,
+        1
+      ]
+    },
+    "_eulerAngles": {
+      "__type__": "cc.Vec3",
+      "x": 0,
+      "y": 0,
+      "z": 0
+    },
+    "_skewX": 0,
+    "_skewY": 0,
+    "_is3DNode": false,
+    "_groupIndex": 0,
+    "groupIndex": 0,
+    "_id": ""
+  },
+  {
+    "__type__": "cc.Node",
+    "_name": "guild_0",
+    "_objFlags": 0,
+    "_parent": {
+      "__id__": 1
+    },
+    "_children": [],
+    "_active": false,
+    "_components": [
+      {
+        "__id__": 3
+      }
+    ],
+    "_prefab": {
+      "__id__": 6
+    },
+    "_opacity": 255,
+    "_color": {
+      "__type__": "cc.Color",
+      "r": 255,
+      "g": 255,
+      "b": 255,
+      "a": 255
+    },
+    "_contentSize": {
+      "__type__": "cc.Size",
+      "width": 100,
+      "height": 100
+    },
+    "_anchorPoint": {
+      "__type__": "cc.Vec2",
+      "x": 0.5,
+      "y": 0.5
+    },
+    "_trs": {
+      "__type__": "TypedArray",
+      "ctor": "Float64Array",
+      "array": [
+        0,
+        0,
+        0,
+        0,
+        0,
+        0,
+        1,
+        1,
+        1,
+        1
+      ]
+    },
+    "_eulerAngles": {
+      "__type__": "cc.Vec3",
+      "x": 0,
+      "y": 0,
+      "z": 0
+    },
+    "_skewX": 0,
+    "_skewY": 0,
+    "_is3DNode": false,
+    "_groupIndex": 0,
+    "groupIndex": 0,
+    "_id": ""
+  },
+  {
+    "__type__": "sp.Skeleton",
+    "_name": "",
+    "_objFlags": 0,
+    "node": {
+      "__id__": 2
+    },
+    "_enabled": true,
+    "_materials": [],
+    "_srcBlendFactor": 770,
+    "_dstBlendFactor": 771,
+    "paused": false,
+    "defaultSkin": "default",
+    "defaultAnimation": "tap",
+    "_preCacheMode": 0,
+    "_cacheMode": 0,
+    "loop": true,
+    "premultipliedAlpha": true,
+    "timeScale": 1,
+    "_accTime": 0,
+    "_playCount": 0,
+    "_frameCache": null,
+    "_curFrame": null,
+    "_skeletonCache": null,
+    "_animationName": "tap",
+    "_animationQueue": [],
+    "_headAniInfo": null,
+    "_playTimes": 0,
+    "_isAniComplete": true,
+    "_N$skeletonData": null,
+    "_N$_defaultCacheMode": 0,
+    "_N$debugSlots": false,
+    "_N$debugBones": false,
+    "_N$debugMesh": false,
+    "_N$useTint": false,
+    "_N$enableBatch": false,
+    "_id": ""
+  },
+  {
+    "__type__": "cc.Node",
+    "_name": "guild_1",
+    "_objFlags": 0,
+    "_parent": {
+      "__id__": 1
+    },
+    "_children": [],
+    "_active": false,
+    "_components": [
+      {
+        "__id__": 5
+      }
+    ],
+    "_prefab": {
+      "__id__": 6
+    },
+    "_opacity": 255,
+    "_color": {
+      "__type__": "cc.Color",
+      "r": 255,
+      "g": 255,
+      "b": 255,
+      "a": 255
+    },
+    "_contentSize": {
+      "__type__": "cc.Size",
+      "width": 100,
+      "height": 100
+    },
+    "_anchorPoint": {
+      "__type__": "cc.Vec2",
+      "x": 0.5,
+      "y": 0.5
+    },
+    "_trs": {
+      "__type__": "TypedArray",
+      "ctor": "Float64Array",
+      "array": [
+        0,
+        0,
+        0,
+        0,
+        0,
+        0,
+        1,
+        1,
+        1,
+        1
+      ]
+    },
+    "_eulerAngles": {
+      "__type__": "cc.Vec3",
+      "x": 0,
+      "y": 0,
+      "z": 0
+    },
+    "_skewX": 0,
+    "_skewY": 0,
+    "_is3DNode": false,
+    "_groupIndex": 0,
+    "groupIndex": 0,
+    "_id": ""
+  },
+  {
+    "__type__": "sp.Skeleton",
+    "_name": "",
+    "_objFlags": 0,
+    "node": {
+      "__id__": 4
+    },
+    "_enabled": true,
+    "_materials": [],
+    "_srcBlendFactor": 770,
+    "_dstBlendFactor": 771,
+    "paused": false,
+    "defaultSkin": "default",
+    "defaultAnimation": "idle",
+    "_preCacheMode": 0,
+    "_cacheMode": 0,
+    "loop": true,
+    "premultipliedAlpha": true,
+    "timeScale": 1,
+    "_accTime": 0,
+    "_playCount": 0,
+    "_frameCache": null,
+    "_curFrame": null,
+    "_skeletonCache": null,
+    "_animationName": "idle",
+    "_animationQueue": [],
+    "_headAniInfo": null,
+    "_playTimes": 0,
+    "_isAniComplete": true,
+    "_N$skeletonData": null,
+    "_N$_defaultCacheMode": 0,
+    "_N$debugSlots": false,
+    "_N$debugBones": false,
+    "_N$debugMesh": false,
+    "_N$useTint": false,
+    "_N$enableBatch": false,
+    "_id": ""
+  },
+  {
+    "__type__": "cc.PrefabInfo",
+    "root": {
+      "__id__": 1
+    },
+    "asset": {
+      "__id__": 0
+    },
+    "fileId": "root",
+    "sync": false
+  }
+]

+ 13 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/prefabs/GuideUI.prefab.meta

@@ -0,0 +1,13 @@
+{
+  "ver": "1.1.50",
+  "importer": "prefab",
+  "imported": true,
+  "uuid": "b6d6f8ca-8a1e-4f24-8a15-678cb07e5780",
+  "files": [
+    ".json"
+  ],
+  "subMetas": {},
+  "userData": {
+    "syncNodeName": "GuideUI"
+  }
+}

+ 9 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/resources.meta

@@ -0,0 +1,9 @@
+{
+  "ver": "1.2.0",
+  "importer": "directory",
+  "imported": true,
+  "uuid": "19bb9ed4-937d-49c3-809b-4d0f6ac8f0de",
+  "files": [],
+  "subMetas": {},
+  "userData": {}
+}

+ 9 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/resources/spine.meta

@@ -0,0 +1,9 @@
+{
+  "ver": "1.2.0",
+  "importer": "directory",
+  "imported": true,
+  "uuid": "a0e7acc1-1e68-433b-add2-e8522779d877",
+  "files": [],
+  "subMetas": {},
+  "userData": {}
+}

+ 19 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/resources/spine/tut_hand.atlas.txt

@@ -0,0 +1,19 @@
+tut_hand.png
+size: 256,256
+format: RGBA8888
+filter: Linear,Linear
+repeat: none
+e0
+  rotate: false
+  xy: 2, 2
+  size: 100, 100
+  orig: 100, 100
+  offset: 0, 0
+  index: -1
+hand_tut
+  rotate: false
+  xy: 104, 2
+  size: 80, 120
+  orig: 80, 120
+  offset: 0, 0
+  index: -1

+ 11 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/resources/spine/tut_hand.atlas.txt.meta

@@ -0,0 +1,11 @@
+{
+  "ver": "1.0.1",
+  "importer": "text",
+  "imported": true,
+  "uuid": "02e6b549-cc01-4e72-9be2-073fe38485c0",
+  "files": [
+    ".json"
+  ],
+  "subMetas": {},
+  "userData": {}
+}

+ 118 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/resources/spine/tut_hand.json

@@ -0,0 +1,118 @@
+{
+  "skeleton": {
+    "hash": "tut_hand",
+    "spine": "3.8.95",
+    "x": -40,
+    "y": -60,
+    "width": 80,
+    "height": 120
+  },
+  "bones": [
+    {
+      "name": "root"
+    },
+    {
+      "name": "hand",
+      "parent": "root",
+      "length": 50,
+      "x": 0,
+      "y": 0
+    }
+  ],
+  "slots": [
+    {
+      "name": "hand_slot",
+      "bone": "hand",
+      "attachment": "hand_tut"
+    }
+  ],
+  "skins": [
+    {
+      "name": "default",
+      "attachments": {
+        "hand_slot": {
+          "hand_tut": {
+            "x": 0,
+            "y": 0,
+            "width": 80,
+            "height": 120
+          }
+        }
+      }
+    }
+  ],
+  "animations": {
+    "idle": {
+      "slots": {
+        "hand_slot": {
+          "color": [
+            {
+              "color": "ffffffff"
+            }
+          ]
+        }
+      }
+    },
+    "tap": {
+      "bones": {
+        "hand": {
+          "scale": [
+            {
+              "x": 1,
+              "y": 1
+            },
+            {
+              "time": 0.5,
+              "x": 1.2,
+              "y": 1.2
+            },
+            {
+              "time": 1,
+              "x": 1,
+              "y": 1
+            }
+          ]
+        }
+      },
+      "slots": {
+        "hand_slot": {
+          "color": [
+            {
+              "color": "ffffffff"
+            },
+            {
+              "time": 0.25,
+              "color": "ffffff80"
+            },
+            {
+              "time": 0.5,
+              "color": "ffffffff"
+            }
+          ]
+        }
+      }
+    },
+    "drag": {
+      "bones": {
+        "hand": {
+          "translate": [
+            {
+              "x": 0,
+              "y": 0
+            },
+            {
+              "time": 1,
+              "x": 100,
+              "y": 0
+            },
+            {
+              "time": 1.5,
+              "x": 0,
+              "y": 0
+            }
+          ]
+        }
+      }
+    }
+  }
+}

+ 13 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/resources/spine/tut_hand.json.meta

@@ -0,0 +1,13 @@
+{
+  "ver": "1.2.7",
+  "importer": "spine-data",
+  "imported": true,
+  "uuid": "867b00df-218a-4166-a8ea-8f0813a2b690",
+  "files": [
+    ".json"
+  ],
+  "subMetas": {},
+  "userData": {
+    "atlasUuid": "02e6b549-cc01-4e72-9be2-073fe38485c0"
+  }
+}

+ 9 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/resources/textures.meta

@@ -0,0 +1,9 @@
+{
+  "ver": "1.2.0",
+  "importer": "directory",
+  "imported": true,
+  "uuid": "080eb1cf-5931-4b91-86d7-0dd2ebc10a65",
+  "files": [],
+  "subMetas": {},
+  "userData": {}
+}

+ 9 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/scripts.meta

@@ -0,0 +1,9 @@
+{
+  "ver": "1.2.0",
+  "importer": "directory",
+  "imported": true,
+  "uuid": "3b72d8b5-f30f-4027-8ee0-876c3abc7901",
+  "files": [],
+  "subMetas": {},
+  "userData": {}
+}

+ 180 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/scripts/GuideDataManager.ts

@@ -0,0 +1,180 @@
+/**
+ * 引导数据管理器
+ * 负责引导相关数据的存储、读取和管理
+ */
+
+export class GuideDataManager {
+    private static readonly GUIDE_DATA_KEY = "newbie_guide_data";
+    private static readonly GUIDE_INDEX_KEY = "guide_index";
+    
+    private _guideIndex: number = 0;
+    private _guideData: any = {};
+    
+    /**
+     * 初始化数据管理器
+     */
+    public init(): void {
+        this.loadGuideData();
+    }
+    
+    /**
+     * 加载引导数据
+     */
+    private loadGuideData(): void {
+        // 从本地存储加载引导索引
+        const savedIndex = cc.sys.localStorage.getItem(GuideDataManager.GUIDE_INDEX_KEY);
+        if (savedIndex !== null) {
+            this._guideIndex = parseInt(savedIndex) || 0;
+        }
+        
+        // 从本地存储加载引导数据
+        const savedData = cc.sys.localStorage.getItem(GuideDataManager.GUIDE_DATA_KEY);
+        if (savedData) {
+            try {
+                this._guideData = JSON.parse(savedData);
+            } catch (e) {
+                console.warn("引导数据解析失败,使用默认数据", e);
+                this._guideData = {};
+            }
+        }
+    }
+    
+    /**
+     * 保存引导数据
+     */
+    private saveGuideData(): void {
+        // 保存引导索引
+        cc.sys.localStorage.setItem(GuideDataManager.GUIDE_INDEX_KEY, this._guideIndex.toString());
+        
+        // 保存引导数据
+        try {
+            const dataStr = JSON.stringify(this._guideData);
+            cc.sys.localStorage.setItem(GuideDataManager.GUIDE_DATA_KEY, dataStr);
+        } catch (e) {
+            console.error("保存引导数据失败", e);
+        }
+    }
+    
+    /**
+     * 获取当前引导索引
+     */
+    public getGuideIndex(): number {
+        return this._guideIndex;
+    }
+    
+    /**
+     * 设置引导索引
+     */
+    public setGuideIndex(index: number): void {
+        this._guideIndex = index;
+        this.saveGuideData();
+    }
+    
+    /**
+     * 增加引导索引
+     */
+    public incrementGuideIndex(): void {
+        this._guideIndex++;
+        this.saveGuideData();
+    }
+    
+    /**
+     * 检查指定步骤是否已完成
+     */
+    public isStepCompleted(stepId: string): boolean {
+        return this._guideData[stepId] === true;
+    }
+    
+    /**
+     * 标记步骤为已完成
+     */
+    public markStepCompleted(stepId: string): void {
+        this._guideData[stepId] = true;
+        this.saveGuideData();
+    }
+    
+    /**
+     * 获取步骤数据
+     */
+    public getStepData(stepId: string, key: string, defaultValue: any = null): any {
+        const stepData = this._guideData[stepId];
+        if (stepData && typeof stepData === 'object') {
+            return stepData[key] !== undefined ? stepData[key] : defaultValue;
+        }
+        return defaultValue;
+    }
+    
+    /**
+     * 设置步骤数据
+     */
+    public setStepData(stepId: string, key: string, value: any): void {
+        if (!this._guideData[stepId] || typeof this._guideData[stepId] !== 'object') {
+            this._guideData[stepId] = {};
+        }
+        this._guideData[stepId][key] = value;
+        this.saveGuideData();
+    }
+    
+    /**
+     * 重置引导数据
+     */
+    public resetGuideData(): void {
+        this._guideIndex = 0;
+        this._guideData = {};
+        this.saveGuideData();
+    }
+    
+    /**
+     * 检查是否是新用户(从未进行过引导)
+     */
+    public isNewUser(): boolean {
+        return this._guideIndex === 0 && Object.keys(this._guideData).length === 0;
+    }
+    
+    /**
+     * 检查引导是否已完成
+     */
+    public isGuideCompleted(): boolean {
+        // 可以根据具体需求定义完成条件
+        // 这里简单地认为索引大于等于某个值就是完成
+        return this._guideIndex >= 10; // 假设有10个引导步骤
+    }
+    
+    /**
+     * 获取引导完成百分比
+     */
+    public getGuideProgress(): number {
+        const totalSteps = 10; // 总步骤数,可以根据实际情况调整
+        return Math.min(this._guideIndex / totalSteps, 1.0);
+    }
+    
+    /**
+     * 导出引导数据(用于调试或数据迁移)
+     */
+    public exportGuideData(): string {
+        return JSON.stringify({
+            guideIndex: this._guideIndex,
+            guideData: this._guideData
+        });
+    }
+    
+    /**
+     * 导入引导数据(用于调试或数据迁移)
+     */
+    public importGuideData(dataStr: string): boolean {
+        try {
+            const data = JSON.parse(dataStr);
+            if (data.guideIndex !== undefined) {
+                this._guideIndex = data.guideIndex;
+            }
+            if (data.guideData) {
+                this._guideData = data.guideData;
+            }
+            this.saveGuideData();
+            return true;
+        } catch (e) {
+            console.error("导入引导数据失败", e);
+            return false;
+        }
+    }
+}

+ 9 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/scripts/GuideDataManager.ts.meta

@@ -0,0 +1,9 @@
+{
+  "ver": "4.0.24",
+  "importer": "typescript",
+  "imported": true,
+  "uuid": "9c5fcda3-378e-44cf-ac09-037e806bb36b",
+  "files": [],
+  "subMetas": {},
+  "userData": {}
+}

+ 206 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/scripts/GuideManager.ts

@@ -0,0 +1,206 @@
+/**
+ * 新手引导管理器
+ * 负责整个引导系统的初始化、流程控制和状态管理
+ */
+
+import { GuideDataManager } from "./GuideDataManager";
+import { GuideStep } from "./GuideStep";
+import { GuideUIController } from "./GuideUIController";
+
+export class GuideManager {
+    private static _instance: GuideManager = null;
+    
+    private _dataManager: GuideDataManager;
+    private _uiController: GuideUIController;
+    private _guideSteps: Map<string, GuideStep> = new Map();
+    private _currentStep: GuideStep = null;
+    private _isGuideActive: boolean = false;
+    
+    public static getInstance(): GuideManager {
+        if (!GuideManager._instance) {
+            GuideManager._instance = new GuideManager();
+        }
+        return GuideManager._instance;
+    }
+    
+    private constructor() {
+        this._dataManager = new GuideDataManager();
+        this._uiController = new GuideUIController();
+    }
+    
+    /**
+     * 初始化引导系统
+     */
+    public init(): void {
+        this._dataManager.init();
+        this._uiController.init();
+        this.registerDefaultSteps();
+    }
+    
+    /**
+     * 注册默认的引导步骤
+     */
+    private registerDefaultSteps(): void {
+        // 这里可以注册默认的引导步骤
+        // 具体的步骤实现可以在子类中定义
+    }
+    
+    /**
+     * 注册引导步骤
+     */
+    public registerStep(step: GuideStep): void {
+        this._guideSteps.set(step.stepId, step);
+        step.setManager(this);
+    }
+    
+    /**
+     * 开始引导
+     */
+    public startGuide(): void {
+        if (this._isGuideActive) {
+            return;
+        }
+        
+        this._isGuideActive = true;
+        this.checkAndTriggerNextStep();
+    }
+    
+    /**
+     * 停止引导
+     */
+    public stopGuide(): void {
+        if (this._currentStep) {
+            this._currentStep.forceComplete();
+        }
+        this._isGuideActive = false;
+        this._uiController.hideAllGuideUI();
+    }
+    
+    /**
+     * 检查并触发下一个引导步骤
+     */
+    public checkAndTriggerNextStep(): void {
+        if (!this._isGuideActive) {
+            return;
+        }
+        
+        const currentIndex = this._dataManager.getGuideIndex();
+        
+        // 遍历所有注册的步骤,找到可以触发的步骤
+        for (const [stepId, step] of this._guideSteps) {
+            if (step.canTrigger() && !step.isCompleted()) {
+                this.executeStep(step);
+                return;
+            }
+        }
+        
+        // 如果没有找到可触发的步骤,检查是否需要结束引导
+        this.checkGuideComplete();
+    }
+    
+    /**
+     * 执行指定的引导步骤
+     */
+    private executeStep(step: GuideStep): void {
+        if (this._currentStep) {
+            this._currentStep.forceComplete();
+        }
+        
+        this._currentStep = step;
+        step.execute();
+    }
+    
+    /**
+     * 当前步骤完成时调用
+     */
+    public onStepComplete(step: GuideStep): void {
+        if (this._currentStep === step) {
+            this._currentStep = null;
+        }
+        
+        // 更新引导进度
+        this._dataManager.incrementGuideIndex();
+        
+        // 检查下一步
+        this.checkAndTriggerNextStep();
+    }
+    
+    /**
+     * 检查引导是否完成
+     */
+    private checkGuideComplete(): void {
+        let hasUncompletedSteps = false;
+        
+        for (const [stepId, step] of this._guideSteps) {
+            if (!step.isCompleted() && step.canTrigger()) {
+                hasUncompletedSteps = true;
+                break;
+            }
+        }
+        
+        if (!hasUncompletedSteps) {
+            this.onGuideComplete();
+        }
+    }
+    
+    /**
+     * 引导完成时调用
+     */
+    private onGuideComplete(): void {
+        this._isGuideActive = false;
+        this._uiController.hideAllGuideUI();
+        
+        // 可以在这里添加引导完成的回调
+        console.log("新手引导完成!");
+    }
+    
+    /**
+     * 获取数据管理器
+     */
+    public getDataManager(): GuideDataManager {
+        return this._dataManager;
+    }
+    
+    /**
+     * 获取UI控制器
+     */
+    public getUIController(): GuideUIController {
+        return this._uiController;
+    }
+    
+    /**
+     * 检查引导是否激活
+     */
+    public isGuideActive(): boolean {
+        return this._isGuideActive;
+    }
+    
+    /**
+     * 获取当前步骤
+     */
+    public getCurrentStep(): GuideStep {
+        return this._currentStep;
+    }
+    
+    /**
+     * 跳过当前引导步骤
+     */
+    public skipCurrentStep(): void {
+        if (this._currentStep) {
+            this._currentStep.skip();
+        }
+    }
+    
+    /**
+     * 重置引导系统
+     */
+    public resetGuide(): void {
+        this.stopGuide();
+        this._dataManager.resetGuideData();
+        
+        // 重置所有步骤状态
+        for (const [stepId, step] of this._guideSteps) {
+            step.reset();
+        }
+    }
+}

+ 9 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/scripts/GuideManager.ts.meta

@@ -0,0 +1,9 @@
+{
+  "ver": "4.0.24",
+  "importer": "typescript",
+  "imported": true,
+  "uuid": "d22ca892-35c6-460d-a321-dac317bd8e7e",
+  "files": [],
+  "subMetas": {},
+  "userData": {}
+}

+ 229 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/scripts/GuideStep.ts

@@ -0,0 +1,229 @@
+/**
+ * 引导步骤基类
+ * 定义了引导步骤的基本接口和通用逻辑
+ */
+
+import { GuideManager } from "./GuideManager";
+
+export abstract class GuideStep {
+    protected _stepId: string;
+    protected _manager: GuideManager;
+    protected _isCompleted: boolean = false;
+    protected _isExecuting: boolean = false;
+    
+    constructor(stepId: string) {
+        this._stepId = stepId;
+    }
+    
+    /**
+     * 获取步骤ID
+     */
+    public get stepId(): string {
+        return this._stepId;
+    }
+    
+    /**
+     * 设置管理器引用
+     */
+    public setManager(manager: GuideManager): void {
+        this._manager = manager;
+    }
+    
+    /**
+     * 检查是否可以触发此步骤
+     * 子类需要实现具体的触发条件
+     */
+    public abstract canTrigger(): boolean;
+    
+    /**
+     * 执行引导步骤
+     * 子类需要实现具体的执行逻辑
+     */
+    public abstract doExecute(): void;
+    
+    /**
+     * 执行步骤(公共接口)
+     */
+    public execute(): void {
+        if (this._isExecuting || this._isCompleted) {
+            return;
+        }
+        
+        this._isExecuting = true;
+        console.log(`开始执行引导步骤: ${this._stepId}`);
+        
+        try {
+            this.doExecute();
+        } catch (e) {
+            console.error(`执行引导步骤失败: ${this._stepId}`, e);
+            this.complete();
+        }
+    }
+    
+    /**
+     * 完成步骤
+     */
+    public complete(): void {
+        if (this._isCompleted) {
+            return;
+        }
+        
+        this._isCompleted = true;
+        this._isExecuting = false;
+        
+        console.log(`引导步骤完成: ${this._stepId}`);
+        
+        // 标记步骤为已完成
+        if (this._manager) {
+            this._manager.getDataManager().markStepCompleted(this._stepId);
+            this._manager.onStepComplete(this);
+        }
+        
+        this.onComplete();
+    }
+    
+    /**
+     * 步骤完成时的回调
+     * 子类可以重写此方法来添加自定义逻辑
+     */
+    protected onComplete(): void {
+        // 默认实现为空,子类可以重写
+    }
+    
+    /**
+     * 跳过步骤
+     */
+    public skip(): void {
+        console.log(`跳过引导步骤: ${this._stepId}`);
+        this.complete();
+    }
+    
+    /**
+     * 强制完成步骤(用于清理)
+     */
+    public forceComplete(): void {
+        this._isExecuting = false;
+        this.complete();
+    }
+    
+    /**
+     * 检查步骤是否已完成
+     */
+    public isCompleted(): boolean {
+        // 首先检查内存中的状态
+        if (this._isCompleted) {
+            return true;
+        }
+        
+        // 然后检查持久化数据
+        if (this._manager) {
+            return this._manager.getDataManager().isStepCompleted(this._stepId);
+        }
+        
+        return false;
+    }
+    
+    /**
+     * 检查步骤是否正在执行
+     */
+    public isExecuting(): boolean {
+        return this._isExecuting;
+    }
+    
+    /**
+     * 重置步骤状态
+     */
+    public reset(): void {
+        this._isCompleted = false;
+        this._isExecuting = false;
+    }
+    
+    /**
+     * 获取步骤数据
+     */
+    protected getStepData(key: string, defaultValue: any = null): any {
+        if (this._manager) {
+            return this._manager.getDataManager().getStepData(this._stepId, key, defaultValue);
+        }
+        return defaultValue;
+    }
+    
+    /**
+     * 设置步骤数据
+     */
+    protected setStepData(key: string, value: any): void {
+        if (this._manager) {
+            this._manager.getDataManager().setStepData(this._stepId, key, value);
+        }
+    }
+}
+
+/**
+ * 合并提示引导步骤
+ * 检测可合并物品并显示合并提示
+ */
+export class MergeTipGuideStep extends GuideStep {
+    private _checkInterval: number = 0;
+    private _maxCheckTime: number = 30000; // 30秒超时
+    private _startTime: number = 0;
+    
+    constructor() {
+        super("merge_tip_guide");
+    }
+    
+    public canTrigger(): boolean {
+        // 检查引导索引和游戏状态
+        const guideIndex = this._manager.getDataManager().getGuideIndex();
+        return guideIndex >= 1 && !this.isCompleted();
+    }
+    
+    public doExecute(): void {
+        this._startTime = Date.now();
+        this.checkMergeConditions();
+    }
+    
+    private checkMergeConditions(): void {
+        // 检查是否超时
+        if (Date.now() - this._startTime > this._maxCheckTime) {
+            console.log("合并提示引导超时,自动完成");
+            this.complete();
+            return;
+        }
+        
+        // 这里需要根据具体游戏逻辑来检查合并条件
+        // 示例:检查是否有可合并的物品
+        if (this.hasMergeableItems()) {
+            this.showMergeTip();
+            this.complete();
+        } else {
+            // 继续检查
+            this._checkInterval = setTimeout(() => {
+                this.checkMergeConditions();
+            }, 1000);
+        }
+    }
+    
+    private hasMergeableItems(): boolean {
+        // 这里需要实现具体的合并检测逻辑
+        // 返回是否有可合并的物品
+        return false; // 占位实现
+    }
+    
+    private showMergeTip(): void {
+        // 显示合并提示UI
+        const uiController = this._manager.getUIController();
+        uiController.showGuideUI("guild_1", "tap");
+    }
+    
+    protected onComplete(): void {
+        // 清理定时器
+        if (this._checkInterval) {
+            clearTimeout(this._checkInterval);
+            this._checkInterval = 0;
+        }
+        
+        // 隐藏提示UI
+        const uiController = this._manager.getUIController();
+        uiController.hideGuideUI("guild_1");
+    }
+}

+ 9 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/scripts/GuideStep.ts.meta

@@ -0,0 +1,9 @@
+{
+  "ver": "4.0.24",
+  "importer": "typescript",
+  "imported": true,
+  "uuid": "36ef0d45-5112-4dcd-ab4d-d8d11badbb25",
+  "files": [],
+  "subMetas": {},
+  "userData": {}
+}

+ 301 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/scripts/GuideSteps.ts

@@ -0,0 +1,301 @@
+/**
+ * 具体的引导步骤实现
+ * 包含各种常见的引导步骤类型
+ */
+
+import { GuideStep } from "./GuideStep";
+
+/**
+ * 点击引导步骤
+ * 引导用户点击特定的UI元素
+ */
+export class ClickGuideStep extends GuideStep {
+    private _targetNodePath: string;
+    private _targetNode: cc.Node;
+    private _clickHandler: () => void;
+    
+    constructor(stepId: string, targetNodePath: string) {
+        super(stepId);
+        this._targetNodePath = targetNodePath;
+        this._clickHandler = this.onTargetClick.bind(this);
+    }
+    
+    public canTrigger(): boolean {
+        // 检查目标节点是否存在且可见
+        this.findTargetNode();
+        return this._targetNode && this._targetNode.active;
+    }
+    
+    public doExecute(): void {
+        this.findTargetNode();
+        if (!this._targetNode) {
+            console.warn(`目标节点未找到: ${this._targetNodePath}`);
+            this.complete();
+            return;
+        }
+        
+        // 显示手指指向动画
+        const uiController = this._manager.getUIController();
+        const worldPos = this._targetNode.convertToWorldSpaceAR(cc.Vec2.ZERO);
+        uiController.createPointingAnimation("guild_1", worldPos);
+        
+        // 添加点击监听
+        this._targetNode.on(cc.Node.EventType.TOUCH_END, this._clickHandler, this);
+        
+        // 创建高亮效果
+        uiController.createHighlightEffect(this._targetNode);
+    }
+    
+    private findTargetNode(): void {
+        if (this._targetNode) {
+            return;
+        }
+        
+        // 根据路径查找节点
+        this._targetNode = cc.find(this._targetNodePath);
+    }
+    
+    private onTargetClick(): void {
+        console.log(`用户点击了目标节点: ${this._targetNodePath}`);
+        this.complete();
+    }
+    
+    protected onComplete(): void {
+        // 移除点击监听
+        if (this._targetNode) {
+            this._targetNode.off(cc.Node.EventType.TOUCH_END, this._clickHandler, this);
+        }
+        
+        // 隐藏引导UI
+        const uiController = this._manager.getUIController();
+        uiController.hideGuideUI("guild_1");
+        uiController.removeHighlightEffect();
+    }
+}
+
+/**
+ * 拖拽引导步骤
+ * 引导用户进行拖拽操作
+ */
+export class DragGuideStep extends GuideStep {
+    private _sourceNodePath: string;
+    private _targetNodePath: string;
+    private _sourceNode: cc.Node;
+    private _targetNode: cc.Node;
+    private _isDragging: boolean = false;
+    
+    constructor(stepId: string, sourceNodePath: string, targetNodePath: string) {
+        super(stepId);
+        this._sourceNodePath = sourceNodePath;
+        this._targetNodePath = targetNodePath;
+    }
+    
+    public canTrigger(): boolean {
+        this.findNodes();
+        return this._sourceNode && this._targetNode && 
+               this._sourceNode.active && this._targetNode.active;
+    }
+    
+    public doExecute(): void {
+        this.findNodes();
+        if (!this._sourceNode || !this._targetNode) {
+            console.warn(`拖拽节点未找到: ${this._sourceNodePath} -> ${this._targetNodePath}`);
+            this.complete();
+            return;
+        }
+        
+        // 显示拖拽引导动画
+        this.showDragGuideAnimation();
+        
+        // 添加拖拽监听
+        this.addDragListeners();
+    }
+    
+    private findNodes(): void {
+        if (!this._sourceNode) {
+            this._sourceNode = cc.find(this._sourceNodePath);
+        }
+        if (!this._targetNode) {
+            this._targetNode = cc.find(this._targetNodePath);
+        }
+    }
+    
+    private showDragGuideAnimation(): void {
+        const uiController = this._manager.getUIController();
+        
+        // 在源节点位置显示手指
+        const sourceWorldPos = this._sourceNode.convertToWorldSpaceAR(cc.Vec2.ZERO);
+        uiController.setGuideUIPosition("guild_1", sourceWorldPos);
+        uiController.showGuideUI("guild_1", "tap");
+        
+        // 创建拖拽路径动画
+        const targetWorldPos = this._targetNode.convertToWorldSpaceAR(cc.Vec2.ZERO);
+        const guideNode = uiController.getGuideNode("guild_1");
+        if (guideNode) {
+            const moveAction = cc.repeatForever(
+                cc.sequence(
+                    cc.moveTo(1.0, targetWorldPos),
+                    cc.moveTo(0.1, sourceWorldPos)
+                )
+            );
+            guideNode.runAction(moveAction);
+        }
+    }
+    
+    private addDragListeners(): void {
+        this._sourceNode.on(cc.Node.EventType.TOUCH_START, this.onDragStart, this);
+        this._sourceNode.on(cc.Node.EventType.TOUCH_MOVE, this.onDragMove, this);
+        this._sourceNode.on(cc.Node.EventType.TOUCH_END, this.onDragEnd, this);
+    }
+    
+    private onDragStart(event: cc.Event.EventTouch): void {
+        this._isDragging = true;
+    }
+    
+    private onDragMove(event: cc.Event.EventTouch): void {
+        if (!this._isDragging) return;
+        
+        // 检查是否拖拽到目标区域
+        const touchPos = event.getLocation();
+        const targetBoundingBox = this._targetNode.getBoundingBoxToWorld();
+        
+        if (targetBoundingBox.contains(touchPos)) {
+            // 拖拽到目标区域,完成引导
+            this.complete();
+        }
+    }
+    
+    private onDragEnd(event: cc.Event.EventTouch): void {
+        this._isDragging = false;
+    }
+    
+    protected onComplete(): void {
+        // 移除拖拽监听
+        if (this._sourceNode) {
+            this._sourceNode.off(cc.Node.EventType.TOUCH_START, this.onDragStart, this);
+            this._sourceNode.off(cc.Node.EventType.TOUCH_MOVE, this.onDragMove, this);
+            this._sourceNode.off(cc.Node.EventType.TOUCH_END, this.onDragEnd, this);
+        }
+        
+        // 隐藏引导UI
+        const uiController = this._manager.getUIController();
+        const guideNode = uiController.getGuideNode("guild_1");
+        if (guideNode) {
+            guideNode.stopAllActions();
+        }
+        uiController.hideGuideUI("guild_1");
+    }
+}
+
+/**
+ * 等待条件引导步骤
+ * 等待特定条件满足后自动完成
+ */
+export class WaitConditionGuideStep extends GuideStep {
+    private _conditionChecker: () => boolean;
+    private _checkInterval: number = 0;
+    private _timeout: number = 30000; // 30秒超时
+    private _startTime: number = 0;
+    
+    constructor(stepId: string, conditionChecker: () => boolean, timeout?: number) {
+        super(stepId);
+        this._conditionChecker = conditionChecker;
+        if (timeout !== undefined) {
+            this._timeout = timeout;
+        }
+    }
+    
+    public canTrigger(): boolean {
+        return true; // 等待条件步骤通常总是可以触发
+    }
+    
+    public doExecute(): void {
+        this._startTime = Date.now();
+        this.startChecking();
+    }
+    
+    private startChecking(): void {
+        this._checkInterval = setInterval(() => {
+            // 检查超时
+            if (Date.now() - this._startTime > this._timeout) {
+                console.log(`等待条件引导步骤超时: ${this._stepId}`);
+                this.complete();
+                return;
+            }
+            
+            // 检查条件
+            if (this._conditionChecker()) {
+                console.log(`等待条件满足: ${this._stepId}`);
+                this.complete();
+            }
+        }, 500); // 每500ms检查一次
+    }
+    
+    protected onComplete(): void {
+        if (this._checkInterval) {
+            clearInterval(this._checkInterval);
+            this._checkInterval = 0;
+        }
+    }
+}
+
+/**
+ * 对话引导步骤
+ * 显示引导对话框
+ */
+export class DialogGuideStep extends GuideStep {
+    private _dialogText: string;
+    private _dialogNode: cc.Node;
+    
+    constructor(stepId: string, dialogText: string) {
+        super(stepId);
+        this._dialogText = dialogText;
+    }
+    
+    public canTrigger(): boolean {
+        return true;
+    }
+    
+    public doExecute(): void {
+        this.showDialog();
+    }
+    
+    private showDialog(): void {
+        // 创建对话框节点
+        this._dialogNode = new cc.Node("guide_dialog");
+        const canvas = cc.find("Canvas");
+        if (canvas) {
+            canvas.addChild(this._dialogNode);
+            this._dialogNode.zIndex = 1001;
+        }
+        
+        // 添加背景
+        const bg = new cc.Node("bg");
+        bg.addComponent(cc.Sprite);
+        bg.color = cc.Color.BLACK;
+        bg.opacity = 128;
+        this._dialogNode.addChild(bg);
+        
+        // 添加文本
+        const textNode = new cc.Node("text");
+        const label = textNode.addComponent(cc.Label);
+        label.string = this._dialogText;
+        label.fontSize = 24;
+        this._dialogNode.addChild(textNode);
+        
+        // 添加确定按钮
+        const btnNode = new cc.Node("btn_ok");
+        const button = btnNode.addComponent(cc.Button);
+        btnNode.on(cc.Node.EventType.TOUCH_END, () => {
+            this.complete();
+        });
+        this._dialogNode.addChild(btnNode);
+    }
+    
+    protected onComplete(): void {
+        if (this._dialogNode) {
+            this._dialogNode.removeFromParent();
+            this._dialogNode = null;
+        }
+    }
+}

+ 9 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/scripts/GuideSteps.ts.meta

@@ -0,0 +1,9 @@
+{
+  "ver": "4.0.24",
+  "importer": "typescript",
+  "imported": true,
+  "uuid": "fd30ca4f-020a-4543-b101-b044e4a1caf8",
+  "files": [],
+  "subMetas": {},
+  "userData": {}
+}

+ 275 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/scripts/GuideUIController.ts

@@ -0,0 +1,275 @@
+/**
+ * 引导UI控制器
+ * 负责引导相关UI的显示、隐藏和动画控制
+ */
+
+export class GuideUIController {
+    private _guideNodes: Map<string, cc.Node> = new Map();
+    private _spineComponents: Map<string, sp.Skeleton> = new Map();
+    private _isInitialized: boolean = false;
+    
+    /**
+     * 初始化UI控制器
+     */
+    public init(): void {
+        if (this._isInitialized) {
+            return;
+        }
+        
+        this.findGuideNodes();
+        this._isInitialized = true;
+    }
+    
+    /**
+     * 查找引导节点
+     */
+    private findGuideNodes(): void {
+        // 查找场景中的引导节点
+        const scene = cc.director.getScene();
+        if (!scene) {
+            console.warn("场景未找到,无法初始化引导UI");
+            return;
+        }
+        
+        // 查找guild_0和guild_1节点
+        this.findAndRegisterGuideNode(scene, "guild_0");
+        this.findAndRegisterGuideNode(scene, "guild_1");
+    }
+    
+    /**
+     * 递归查找并注册引导节点
+     */
+    private findAndRegisterGuideNode(parent: cc.Node, nodeName: string): void {
+        // 检查当前节点
+        if (parent.name === nodeName) {
+            this.registerGuideNode(nodeName, parent);
+            return;
+        }
+        
+        // 递归检查子节点
+        for (let i = 0; i < parent.childrenCount; i++) {
+            const child = parent.children[i];
+            this.findAndRegisterGuideNode(child, nodeName);
+        }
+    }
+    
+    /**
+     * 注册引导节点
+     */
+    public registerGuideNode(nodeId: string, node: cc.Node): void {
+        this._guideNodes.set(nodeId, node);
+        
+        // 查找Spine组件
+        const spineComp = node.getComponent(sp.Skeleton);
+        if (spineComp) {
+            this._spineComponents.set(nodeId, spineComp);
+        }
+        
+        // 默认隐藏节点
+        node.active = false;
+        
+        console.log(`注册引导节点: ${nodeId}`);
+    }
+    
+    /**
+     * 显示引导UI
+     */
+    public showGuideUI(nodeId: string, animationName?: string): void {
+        const node = this._guideNodes.get(nodeId);
+        if (!node) {
+            console.warn(`引导节点未找到: ${nodeId}`);
+            return;
+        }
+        
+        // 显示节点
+        node.active = true;
+        
+        // 播放动画
+        if (animationName) {
+            this.playAnimation(nodeId, animationName);
+        }
+        
+        // 设置层级
+        this.setGuideUIZIndex(node, 1000);
+        
+        console.log(`显示引导UI: ${nodeId}`);
+    }
+    
+    /**
+     * 隐藏引导UI
+     */
+    public hideGuideUI(nodeId: string): void {
+        const node = this._guideNodes.get(nodeId);
+        if (!node) {
+            console.warn(`引导节点未找到: ${nodeId}`);
+            return;
+        }
+        
+        node.active = false;
+        console.log(`隐藏引导UI: ${nodeId}`);
+    }
+    
+    /**
+     * 隐藏所有引导UI
+     */
+    public hideAllGuideUI(): void {
+        for (const [nodeId, node] of this._guideNodes) {
+            node.active = false;
+        }
+        console.log("隐藏所有引导UI");
+    }
+    
+    /**
+     * 播放动画
+     */
+    public playAnimation(nodeId: string, animationName: string, loop: boolean = true): void {
+        const spineComp = this._spineComponents.get(nodeId);
+        if (!spineComp) {
+            console.warn(`Spine组件未找到: ${nodeId}`);
+            return;
+        }
+        
+        try {
+            spineComp.setAnimation(0, animationName, loop);
+            console.log(`播放动画: ${nodeId} - ${animationName}`);
+        } catch (e) {
+            console.error(`播放动画失败: ${nodeId} - ${animationName}`, e);
+        }
+    }
+    
+    /**
+     * 停止动画
+     */
+    public stopAnimation(nodeId: string): void {
+        const spineComp = this._spineComponents.get(nodeId);
+        if (!spineComp) {
+            console.warn(`Spine组件未找到: ${nodeId}`);
+            return;
+        }
+        
+        spineComp.clearTracks();
+        console.log(`停止动画: ${nodeId}`);
+    }
+    
+    /**
+     * 设置引导UI位置
+     */
+    public setGuideUIPosition(nodeId: string, position: cc.Vec2): void {
+        const node = this._guideNodes.get(nodeId);
+        if (!node) {
+            console.warn(`引导节点未找到: ${nodeId}`);
+            return;
+        }
+        
+        node.position = position;
+    }
+    
+    /**
+     * 设置引导UI层级
+     */
+    private setGuideUIZIndex(node: cc.Node, zIndex: number): void {
+        node.zIndex = zIndex;
+        
+        // 确保父节点也有足够高的层级
+        let parent = node.parent;
+        while (parent) {
+            if (parent.zIndex < zIndex) {
+                parent.zIndex = zIndex;
+            }
+            parent = parent.parent;
+        }
+    }
+    
+    /**
+     * 创建手指指向动画
+     */
+    public createPointingAnimation(nodeId: string, targetPosition: cc.Vec2): void {
+        const node = this._guideNodes.get(nodeId);
+        if (!node) {
+            console.warn(`引导节点未找到: ${nodeId}`);
+            return;
+        }
+        
+        // 设置位置到目标位置附近
+        const offset = cc.v2(50, 50); // 偏移量,可以调整
+        node.position = targetPosition.add(offset);
+        
+        // 显示节点并播放动画
+        this.showGuideUI(nodeId, "tap");
+        
+        // 添加缩放动画效果
+        node.stopAllActions();
+        const scaleAction = cc.repeatForever(
+            cc.sequence(
+                cc.scaleTo(0.5, 1.2),
+                cc.scaleTo(0.5, 1.0)
+            )
+        );
+        node.runAction(scaleAction);
+    }
+    
+    /**
+     * 创建高亮效果
+     */
+    public createHighlightEffect(targetNode: cc.Node): void {
+        if (!targetNode) {
+            return;
+        }
+        
+        // 创建高亮遮罩
+        const maskNode = new cc.Node("guide_mask");
+        maskNode.addComponent(cc.Graphics);
+        
+        const graphics = maskNode.getComponent(cc.Graphics);
+        const canvas = cc.find("Canvas");
+        if (canvas) {
+            canvas.addChild(maskNode);
+            maskNode.zIndex = 999;
+            
+            // 绘制半透明遮罩
+            const canvasSize = canvas.getContentSize();
+            graphics.fillColor = cc.Color.BLACK;
+            graphics.fillRect(-canvasSize.width/2, -canvasSize.height/2, canvasSize.width, canvasSize.height);
+            
+            // 在目标位置挖洞(这里简化处理,实际可能需要更复杂的遮罩逻辑)
+            const targetPos = targetNode.convertToWorldSpaceAR(cc.Vec2.ZERO);
+            const localPos = maskNode.convertToNodeSpaceAR(targetPos);
+            const targetSize = targetNode.getContentSize();
+            
+            graphics.fillColor = cc.Color.TRANSPARENT;
+            graphics.fillRect(
+                localPos.x - targetSize.width/2,
+                localPos.y - targetSize.height/2,
+                targetSize.width,
+                targetSize.height
+            );
+        }
+    }
+    
+    /**
+     * 移除高亮效果
+     */
+    public removeHighlightEffect(): void {
+        const canvas = cc.find("Canvas");
+        if (canvas) {
+            const maskNode = canvas.getChildByName("guide_mask");
+            if (maskNode) {
+                maskNode.removeFromParent();
+            }
+        }
+    }
+    
+    /**
+     * 检查节点是否已注册
+     */
+    public hasGuideNode(nodeId: string): boolean {
+        return this._guideNodes.has(nodeId);
+    }
+    
+    /**
+     * 获取引导节点
+     */
+    public getGuideNode(nodeId: string): cc.Node {
+        return this._guideNodes.get(nodeId);
+    }
+}

+ 9 - 0
assets/NewbieGuidePlugin-v1.0.0/NewbieGuidePlugin-v1.0.0/scripts/GuideUIController.ts.meta

@@ -0,0 +1,9 @@
+{
+  "ver": "4.0.24",
+  "importer": "typescript",
+  "imported": true,
+  "uuid": "b8495dc6-4022-44a3-aca2-560314723d17",
+  "files": [],
+  "subMetas": {},
+  "userData": {}
+}

File diff suppressed because it is too large
+ 217 - 145
assets/Scenes/GameLevel.scene


+ 14 - 14
assets/data/enemies.json

@@ -33,7 +33,7 @@
       "attackDelay": 1.0,
       "weaponType": "none",
       "projectileType": "none",
-      "projectileSpeed": 50.0,
+      "projectileSpeed": 0.0,
       "causesWallShake": false
     },
     "visualConfig": {
@@ -127,7 +127,7 @@
       "attackDelay": 1.0,
       "weaponType": "none",
       "projectileType": "none",
-      "projectileSpeed": 100.0,
+      "projectileSpeed": 0.0,
       "causesWallShake": true
     },
     "visualConfig": {
@@ -221,7 +221,7 @@
       "attackDelay": 1.0,
       "weaponType": "baseball_bat",
       "projectileType": "none",
-      "projectileSpeed": 100.0,
+      "projectileSpeed": 0.0,
       "causesWallShake": true
     },
     "visualConfig": {
@@ -315,7 +315,7 @@
       "attackDelay": 1.0,
       "weaponType": "none",
       "projectileType": "magic_bolt",
-      "projectileSpeed": 50.0,
+      "projectileSpeed": 300.0,
       "causesWallShake": true
     },
     "visualConfig": {
@@ -330,7 +330,7 @@
         "attack": "attack",
         "death": "dead"
       },
-      "weaponProp": "props/bow"
+      "weaponProp": "props/magic_staff"
     },
     "audioConfig": {
       "attackSound": "enemy_attack",
@@ -354,7 +354,7 @@
         "attack": "attack",
         "death": "dead"
       },
-      "weapon_prop": "props/bow"
+      "weapon_prop": "props/magic_staff"
     },
     "audio": {
       "attack_sound": "data/弹球音效/zhanshiAtk.mp3",
@@ -424,7 +424,7 @@
         "attack": "attack",
         "death": "dead"
       },
-      "weaponProp": "nan"
+      "weaponProp": "props/bow"
     },
     "audioConfig": {
       "attackSound": "enemy_attack",
@@ -448,7 +448,7 @@
         "attack": "attack",
         "death": "dead"
       },
-      "weapon_prop": "nan"
+      "weapon_prop": "props/bow"
     },
     "audio": {
       "attack_sound": "data/弹球音效/hammer1.mp3",
@@ -503,7 +503,7 @@
       "attackDelay": 1.0,
       "weaponType": "none",
       "projectileType": "none",
-      "projectileSpeed": 100.0,
+      "projectileSpeed": 0.0,
       "causesWallShake": true
     },
     "visualConfig": {
@@ -604,7 +604,7 @@
       "attackDelay": 1.0,
       "weaponType": "none",
       "projectileType": "none",
-      "projectileSpeed": 100.0,
+      "projectileSpeed": 0.0,
       "causesWallShake": true
     },
     "visualConfig": {
@@ -698,7 +698,7 @@
       "attackDelay": 1.0,
       "weaponType": "none",
       "projectileType": "none",
-      "projectileSpeed": 100.0,
+      "projectileSpeed": 0.0,
       "causesWallShake": true
     },
     "visualConfig": {
@@ -792,7 +792,7 @@
       "attackDelay": 1.0,
       "weaponType": "none",
       "projectileType": "none",
-      "projectileSpeed": 100.0,
+      "projectileSpeed": 0.0,
       "causesWallShake": true
     },
     "visualConfig": {
@@ -906,7 +906,7 @@
       "attackDelay": 1.0,
       "weaponType": "none",
       "projectileType": "none",
-      "projectileSpeed": 100.0,
+      "projectileSpeed": 0.0,
       "causesWallShake": true
     },
     "visualConfig": {
@@ -1020,7 +1020,7 @@
       "attackDelay": 1.0,
       "weaponType": "none",
       "projectileType": "none",
-      "projectileSpeed": 100.0,
+      "projectileSpeed": 0.0,
       "causesWallShake": true
     },
     "visualConfig": {

BIN
assets/data/excel/~$敌人配置表.xlsx


+ 12 - 0
assets/data/excel/~$敌人配置表.xlsx.meta

@@ -0,0 +1,12 @@
+{
+  "ver": "1.0.0",
+  "importer": "*",
+  "imported": true,
+  "uuid": "5207e2b8-beeb-49ae-a4ea-6005d13702cf",
+  "files": [
+    ".json",
+    ".xlsx"
+  ],
+  "subMetas": {},
+  "userData": {}
+}

BIN
assets/data/excel/敌人配置表.xlsx


+ 2 - 2
assets/resources/prefabs/DamageNumber.prefab

@@ -76,7 +76,7 @@
     },
     "_contentSize": {
       "__type__": "cc.Size",
-      "width": 47.78,
+      "width": 120,
       "height": 40
     },
     "_anchorPoint": {
@@ -115,7 +115,7 @@
     "_string": "12",
     "_horizontalAlign": 1,
     "_verticalAlign": 1,
-    "_actualFontSize": 20,
+    "_actualFontSize": 25,
     "_fontSize": 25,
     "_fontFamily": "Arial",
     "_lineHeight": 40,

+ 2 - 2
assets/scripts/Animations/DamageNumberAni.ts

@@ -89,8 +89,8 @@ export class DamageNumberAni extends Component {
         // 设置伤害数字文本
         const label = damageNode.getComponent(Label);
         if (label) {
-            // 显示精确的伤害值,保留一位小数(如果有小数部分)
-            const damageText = damage % 1 === 0 ? damage.toString() : damage.toFixed(1);
+            // 将伤害值四舍五入为整数,不显示小数位
+            const damageText = Math.round(damage).toString();
             label.string = damageText;
             
             // 暴击时使用不同颜色

+ 4 - 0
assets/scripts/CombatSystem/BlockManager.ts

@@ -10,6 +10,7 @@ import { SkillManager } from './SkillSelection/SkillManager';
 import EventBus, { GameEvents } from '../Core/EventBus';
 import { sp } from 'cc';
 import { BundleLoader } from '../Core/BundleLoader';
+import { Audio } from '../AudioManager/AudioManager';
 const { ccclass, property } = _decorator;
 
 @ccclass('BlockManager')
@@ -2454,6 +2455,9 @@ export class BlockManager extends Component {
             if (nextRarity) {
                 console.log(`[BlockManager] 合成成功,稀有度升级: ${rarity} -> ${nextRarity}`);
                 
+                // 播放合成成功音效
+                Audio.playSFX('data/弹球音效/level up 2');
+                
                 // 获取当前武器配置
                 const currentConfig = this.blockWeaponConfigs.get(target);
                 if (currentConfig) {

+ 70 - 26
assets/scripts/CombatSystem/EnemyInstance.ts

@@ -104,6 +104,12 @@ export class EnemyInstance extends Component {
     // 暂停状态标记
     private isPaused: boolean = false;
 
+    // 当前播放的动画名称,用于避免重复播放
+    private currentAnimationName: string = '';
+
+    // 攻击状态下的动画管理
+    private isPlayingAttackAnimation: boolean = false;
+
 
     start() {
         // 初始化敌人
@@ -861,20 +867,14 @@ export class EnemyInstance extends Component {
         const enemyComponent = this.getComponent(EnemyComponent);
         const attackRange = enemyComponent ? enemyComponent.getAttackRange() : 30;
         
-        if (attackRange <= 0) {
-            // 近战攻击:在攻击状态下停止移动
-            const enemySprite = this.node.getChildByName('EnemySprite');
-            if (enemySprite) {
-                const rigidBody = enemySprite.getComponent(RigidBody2D);
-                if (rigidBody) {
-                    // 停止物理移动
-                    rigidBody.linearVelocity = new Vec2(0, 0);
-                }
+        // 无论近战还是远程攻击,在攻击状态下都停止移动
+        const enemySprite = this.node.getChildByName('EnemySprite');
+        if (enemySprite) {
+            const rigidBody = enemySprite.getComponent(RigidBody2D);
+            if (rigidBody) {
+                // 停止物理移动
+                rigidBody.linearVelocity = new Vec2(0, 0);
             }
-        } else {
-            // 远程攻击:继续移动,但不需要在这里播放行走动画
-            // 攻击动画会在performRangedAttack中播放,完成后会根据状态切换回行走动画
-            this.updateMovement(deltaTime);
         }
         
         this.attackTimer -= deltaTime;
@@ -885,6 +885,11 @@ export class EnemyInstance extends Component {
             
             // 重置攻击计时器
             this.attackTimer = this.attackInterval;
+        } else {
+            // 攻击冷却期间播放待机动画(只有在不播放攻击动画时)
+            if (!this.isPlayingAttackAnimation) {
+                this.playIdleAnimationSafe();
+            }
         }
     }
 
@@ -959,6 +964,9 @@ export class EnemyInstance extends Component {
         if (animation) {
             // 播放攻击动画(不循环)
             this.skeleton.setAnimation(0, attackName, false);
+            this.currentAnimationName = attackName;
+            this.isPlayingAttackAnimation = true;
+            console.log(`[EnemyInstance] 播放远程攻击动画: ${attackName}`);
             
             // 动画切换后延迟更新血条位置,确保动画尺寸已生效
             this.scheduleOnce(() => {
@@ -977,11 +985,12 @@ export class EnemyInstance extends Component {
             
             // 动画完成后根据当前状态切换动画
             this.scheduleOnce(() => {
-                if (this.state === EnemyState.IDLE) {
-                    // 如果是待机状态,切换回待机动画
+                this.isPlayingAttackAnimation = false;
+                if (this.state === EnemyState.IDLE || this.state === EnemyState.ATTACKING) {
+                    // 如果是待机状态或攻击状态,切换回待机动画
                     this.playIdleAnimation();
-                } else if (this.state === EnemyState.ATTACKING) {
-                    // 如果还在攻击状态(移动中攻击),切换回行走动画
+                } else {
+                    // 其他状态切换回行走动画
                     this.playWalkAnimation();
                 }
             }, animationDuration);
@@ -1053,11 +1062,18 @@ export class EnemyInstance extends Component {
         const walkName = anims.walk ?? 'walk';
         const idleName = anims.idle ?? 'idle';
 
+        let animationToPlay = '';
         if (this.skeleton.findAnimation(walkName)) {
-            this.skeleton.setAnimation(0, walkName, true);
-            // 行走音效已移除
+            animationToPlay = walkName;
         } else if (this.skeleton.findAnimation(idleName)) {
-            this.skeleton.setAnimation(0, idleName, true);
+            animationToPlay = idleName;
+        }
+
+        if (animationToPlay && this.currentAnimationName !== animationToPlay) {
+            this.skeleton.setAnimation(0, animationToPlay, true);
+            this.currentAnimationName = animationToPlay;
+            this.isPlayingAttackAnimation = false;
+            console.log(`[EnemyInstance] 播放行走动画: ${animationToPlay}`);
         }
         
         // 动画切换后延迟更新血条位置,确保动画尺寸已生效
@@ -1073,10 +1089,11 @@ export class EnemyInstance extends Component {
         const anims2 = enemyComp2?.getAnimations ? enemyComp2.getAnimations() : {};
         const attackName = anims2.attack ?? 'attack';
 
-        // 移除频繁打印
-
-        if (this.skeleton.findAnimation(attackName)) {
+        if (this.skeleton.findAnimation(attackName) && this.currentAnimationName !== attackName) {
             this.skeleton.setAnimation(0, attackName, true);
+            this.currentAnimationName = attackName;
+            this.isPlayingAttackAnimation = true;
+            console.log(`[EnemyInstance] 播放攻击动画: ${attackName}`);
         }
         
         // 动画切换后延迟更新血条位置,确保动画尺寸已生效
@@ -1092,8 +1109,11 @@ export class EnemyInstance extends Component {
         const anims = enemyComp?.getAnimations ? enemyComp.getAnimations() : {};
         const idleName = anims.idle ?? 'idle';
 
-        if (this.skeleton.findAnimation(idleName)) {
+        if (this.skeleton.findAnimation(idleName) && this.currentAnimationName !== idleName) {
             this.skeleton.setAnimation(0, idleName, true);
+            this.currentAnimationName = idleName;
+            this.isPlayingAttackAnimation = false;
+            console.log(`[EnemyInstance] 播放待机动画: ${idleName}`);
         }
         
         // 动画切换后延迟更新血条位置,确保动画尺寸已生效
@@ -1102,6 +1122,26 @@ export class EnemyInstance extends Component {
         }, 0.1);
     }
 
+    // 安全播放待机动画(避免重复调用)
+    private playIdleAnimationSafe() {
+        if (!this.skeleton) return;
+        const enemyComp = this.getComponent('EnemyComponent') as any;
+        const anims = enemyComp?.getAnimations ? enemyComp.getAnimations() : {};
+        const idleName = anims.idle ?? 'idle';
+
+        // 只有当前不是待机动画时才播放
+        if (this.skeleton.findAnimation(idleName) && this.currentAnimationName !== idleName && !this.isPlayingAttackAnimation) {
+            this.skeleton.setAnimation(0, idleName, true);
+            this.currentAnimationName = idleName;
+            console.log(`[EnemyInstance] 安全播放待机动画: ${idleName}`);
+            
+            // 动画切换后延迟更新血条位置,确保动画尺寸已生效
+            this.scheduleOnce(() => {
+                this.updateHPBarPosition();
+            }, 0.1);
+        }
+    }
+
     // 播放攻击动画并在动画结束时造成伤害
     private playAttackAnimationWithDamage() {
         if (!this.skeleton) {
@@ -1118,6 +1158,9 @@ export class EnemyInstance extends Component {
         if (animation) {
             // 播放攻击动画(不循环)
             this.skeleton.setAnimation(0, attackName, false);
+            this.currentAnimationName = attackName;
+            this.isPlayingAttackAnimation = true;
+            console.log(`[EnemyInstance] 播放近战攻击动画: ${attackName}`);
             
             // 动画切换后延迟更新血条位置,确保动画尺寸已生效
             this.scheduleOnce(() => {
@@ -1131,9 +1174,10 @@ export class EnemyInstance extends Component {
             // 使用定时器在动画结束时造成伤害
             this.scheduleOnce(() => {
                 this.dealDamageToWall();
+                this.isPlayingAttackAnimation = false;
                 // 动画完成后根据当前状态切换动画
-                if (this.state === EnemyState.IDLE) {
-                    // 如果是待机状态,切换回待机动画
+                if (this.state === EnemyState.IDLE || this.state === EnemyState.ATTACKING) {
+                    // 如果是待机状态或攻击状态,切换回待机动画
                     this.playIdleAnimation();
                 } else {
                     // 其他状态切换回行走动画

+ 14 - 1
assets/scripts/CombatSystem/SkillSelection/SkillSelectionController.ts

@@ -39,6 +39,16 @@ export class SkillSelectionController extends Component {
     start() {
         this.loadSkillConfig();
     }
+
+    /**
+     * 每次界面激活时刷新技能状态
+     */
+    protected onEnable() {
+        // 如果技能配置已加载,重新随机选择技能并更新UI
+        if (this._skillConfig && this.skillButtons.length > 0) {
+            this.randomizeSkills();
+        }
+    }
     
 
 
@@ -147,6 +157,9 @@ export class SkillSelectionController extends Component {
                 if (skillManager) {
                     const currentLevel = skillManager.getSkillLevel(skillData.id);
                     console.log(`[SkillSelectionController] 技能 ${skillData.name} 当前等级: ${currentLevel}`);
+                    
+                    // 强制刷新技能等级显示
+                    skillButton.refreshSkillLevel();
                 }
             } else {
                 skillButton.setSkillData(null);
@@ -222,7 +235,7 @@ export class SkillSelectionController extends Component {
             btn.setInteractable(true);
         });
         
-        // 重新随机选择技能
+        // 重新随机选择技能(这里会自动获取最新的技能等级状态)
         this.randomizeSkills();
     }
 

+ 64 - 20
assets/scripts/FourUI/MainSystem/MainUIControlller.ts

@@ -25,6 +25,8 @@ export class MainUIController extends Component {
   @property(Node) upgradeHpLabel: Node = null;       // 升级后血量数字
   @property(Label) upgradeCurrentHpLabel: Label = null;  // 当前血量Label
   @property(Label) upgradeNextHpLabel: Label = null;     // 升级后血量Label
+  @property(Label) upgradeInfoLabel: Label = null;       // 升级信息Label(显示"升级墙壁"或"已满级")
+  @property(Node) upgradeArrowNode: Node = null;         // 箭头符号节点xxtb
 
   /* 墙体配置 */
   // @property(JsonAsset) wallConfigAsset: JsonAsset = null;  // 墙体配置JSON资源 - 已改为动态加载
@@ -336,28 +338,70 @@ export class MainUIController extends Component {
     const costLbl = this.upgradeCostLabel?.getComponent(Label);
     const hpLbl = this.upgradeHpLabel?.getComponent(Label);
     
-    // 显示升级费用
     const currentLevel = this.sdm.getWallLevel();
-    const cost = this.getWallUpgradeCost(currentLevel);
-    if (costLbl) {
-      costLbl.string = cost.toString();
-      console.log(`[MainUIController] 升级费用: ${cost}`);
-    }
-
-    // 显示当前和下一级血量
-    const currentHp = this.getWallHealthByLevel(currentLevel);
-    
-    // 计算下一级血量 - 使用配置数据
-    const nextHp = this.getWallHealthByLevel(currentLevel + 1);
+    const maxLevel = this.getWallMaxLevel();
     
-    // 如果有新的分离Label,使用分离显示
-    if (this.upgradeCurrentHpLabel && this.upgradeNextHpLabel) {
-      this.upgradeCurrentHpLabel.string = `${currentHp}>>`;
-      this.upgradeNextHpLabel.string = `${nextHp}`;
-      console.log(`[MainUIController] 分离血量显示: 当前=${currentHp}, 升级后=${nextHp}, 当前等级: ${currentLevel}`);
-    }else {
-      console.error('[MainUIController] 升级信息标签未找到');
-      return;
+    // 检查是否已达到最大等级
+    if (currentLevel >= maxLevel) {
+      // 满级状态:显示"已满级",隐藏当前血量和箭头,只显示满级血量
+      if (this.upgradeInfoLabel) {
+        this.upgradeInfoLabel.string = "已满级";
+      }
+      
+      // 隐藏当前血量Label和箭头符号
+      if (this.upgradeCurrentHpLabel) {
+        this.upgradeCurrentHpLabel.node.active = false;
+      }
+      if (this.upgradeArrowNode) {
+        this.upgradeArrowNode.active = false;
+      }
+      
+      // 只显示满级血量
+      if (this.upgradeNextHpLabel) {
+        const maxHp = this.getWallHealthByLevel(currentLevel);
+        this.upgradeNextHpLabel.string = `${maxHp}`;
+        this.upgradeNextHpLabel.node.active = true;
+      }
+      
+      // 隐藏升级费用
+      if (costLbl) {
+        costLbl.string = "";
+      }
+      
+      console.log(`[MainUIController] 满级状态显示: 等级=${currentLevel}, 血量=${this.getWallHealthByLevel(currentLevel)}`);
+    } else {
+      // 非满级状态:正常显示升级信息
+      if (this.upgradeInfoLabel) {
+        this.upgradeInfoLabel.string = "升级墙壁";
+      }
+      
+      // 显示升级费用
+      const cost = this.getWallUpgradeCost(currentLevel);
+      if (costLbl) {
+        costLbl.string = cost.toString();
+        console.log(`[MainUIController] 升级费用: ${cost}`);
+      }
+
+      // 显示当前和下一级血量
+      const currentHp = this.getWallHealthByLevel(currentLevel);
+      const nextHp = this.getWallHealthByLevel(currentLevel + 1);
+      
+      // 显示当前血量Label和箭头符号
+      if (this.upgradeCurrentHpLabel) {
+        this.upgradeCurrentHpLabel.string = `${currentHp}>>`;
+        this.upgradeCurrentHpLabel.node.active = true;
+      }
+      if (this.upgradeArrowNode) {
+        this.upgradeArrowNode.active = true;
+      }
+      
+      // 显示升级后血量
+      if (this.upgradeNextHpLabel) {
+        this.upgradeNextHpLabel.string = `${nextHp}`;
+        this.upgradeNextHpLabel.node.active = true;
+      }
+      
+      console.log(`[MainUIController] 升级信息显示: 当前=${currentHp}, 升级后=${nextHp}, 当前等级: ${currentLevel}`);
     }
     
     // 升级按钮始终保持可点击状态,通过Toast显示各种提示

+ 9 - 0
assets/scripts/Guide.meta

@@ -0,0 +1,9 @@
+{
+  "ver": "1.2.0",
+  "importer": "directory",
+  "imported": true,
+  "uuid": "702fce6b-2ba2-4b79-94ae-14e1584ff043",
+  "files": [],
+  "subMetas": {},
+  "userData": {}
+}

+ 956 - 0
image_composer.html

@@ -0,0 +1,956 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>图片拼接工具</title>
+    <style>
+        * {
+            margin: 0;
+            padding: 0;
+            box-sizing: border-box;
+        }
+
+        body {
+            font-family: 'Microsoft YaHei', Arial, sans-serif;
+            background: #f5f5f5;
+            overflow: hidden;
+        }
+
+        .container {
+            display: flex;
+            height: 100vh;
+        }
+
+        .toolbar {
+            width: 250px;
+            background: #2c3e50;
+            color: white;
+            padding: 20px;
+            overflow-y: auto;
+        }
+
+        .toolbar h1 {
+            font-size: 18px;
+            margin-bottom: 20px;
+            text-align: center;
+            color: #ecf0f1;
+        }
+
+        .tool-section {
+            margin-bottom: 25px;
+        }
+
+        .tool-section h3 {
+            font-size: 14px;
+            margin-bottom: 10px;
+            color: #bdc3c7;
+            border-bottom: 1px solid #34495e;
+            padding-bottom: 5px;
+        }
+
+        .btn {
+            width: 100%;
+            padding: 10px;
+            margin: 5px 0;
+            border: none;
+            border-radius: 5px;
+            cursor: pointer;
+            font-size: 12px;
+            transition: all 0.3s;
+        }
+
+        .btn-primary {
+            background: #3498db;
+            color: white;
+        }
+
+        .btn-primary:hover {
+            background: #2980b9;
+        }
+
+        .btn-success {
+            background: #27ae60;
+            color: white;
+        }
+
+        .btn-success:hover {
+            background: #229954;
+        }
+
+        .btn-warning {
+            background: #f39c12;
+            color: white;
+        }
+
+        .btn-warning:hover {
+            background: #e67e22;
+        }
+
+        .btn-danger {
+            background: #e74c3c;
+            color: white;
+        }
+
+        .btn-danger:hover {
+            background: #c0392b;
+        }
+
+        .file-input {
+            display: none;
+        }
+
+        .canvas-container {
+            flex: 1;
+            position: relative;
+            overflow: hidden;
+            background: #ecf0f1;
+        }
+
+        .canvas {
+            position: absolute;
+            top: 50%;
+            left: 50%;
+            transform: translate(-50%, -50%);
+            background: white;
+            border: 2px solid #bdc3c7;
+            box-shadow: 0 4px 8px rgba(0,0,0,0.1);
+            cursor: grab;
+        }
+
+        .canvas:active {
+            cursor: grabbing;
+        }
+
+        .image-item {
+            position: absolute;
+            cursor: move;
+            border: 2px solid transparent;
+            transition: border-color 0.2s;
+        }
+
+        .image-item:hover {
+            border-color: #3498db;
+        }
+
+        .image-item.selected {
+            border-color: #e74c3c;
+            box-shadow: 0 0 10px rgba(231, 76, 60, 0.5);
+        }
+
+        .image-item img {
+            display: block;
+            max-width: 100%;
+            max-height: 100%;
+            pointer-events: none;
+        }
+
+        .info-panel {
+            position: absolute;
+            top: 10px;
+            right: 10px;
+            background: rgba(44, 62, 80, 0.9);
+            color: white;
+            padding: 10px;
+            border-radius: 5px;
+            font-size: 12px;
+            min-width: 200px;
+        }
+
+        .zoom-controls {
+            position: absolute;
+            bottom: 20px;
+            right: 20px;
+            display: flex;
+            gap: 10px;
+        }
+
+        .zoom-btn {
+            width: 40px;
+            height: 40px;
+            border: none;
+            border-radius: 50%;
+            background: #34495e;
+            color: white;
+            cursor: pointer;
+            font-size: 18px;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+        }
+
+        .zoom-btn:hover {
+            background: #2c3e50;
+        }
+
+        .selection-box {
+            position: absolute;
+            border: 2px dashed #3498db;
+            background: rgba(52, 152, 219, 0.1);
+            pointer-events: none;
+            display: none;
+        }
+
+        .status-bar {
+            position: absolute;
+            bottom: 0;
+            left: 250px;
+            right: 0;
+            height: 30px;
+            background: #34495e;
+            color: white;
+            display: flex;
+            align-items: center;
+            padding: 0 15px;
+            font-size: 12px;
+        }
+    </style>
+</head>
+<body>
+    <div class="container">
+        <div class="toolbar">
+            <h1>图片拼接工具</h1>
+            
+            <div class="tool-section">
+                <h3>文件操作</h3>
+                <button class="btn btn-primary" onclick="importImages()">导入图片</button>
+                <input type="file" id="fileInput" class="file-input" multiple accept="image/*">
+                <button class="btn btn-success" onclick="exportCanvas()">导出内容</button>
+                <button class="btn btn-success" onclick="exportFullCanvas()">导出画布</button>
+                <button class="btn btn-warning" onclick="fitCanvasToContent()">缩小画布</button>
+                <button class="btn btn-danger" onclick="clearCanvas()">清空画布</button>
+            </div>
+
+            <div class="tool-section">
+                <h3>选择操作</h3>
+                <button class="btn btn-primary" onclick="selectAll()">全选</button>
+                <button class="btn btn-primary" onclick="clearSelection()">取消选择</button>
+                <button class="btn btn-danger" onclick="deleteSelected()">删除选中</button>
+            </div>
+
+            <div class="tool-section">
+                <h3>排列工具</h3>
+                <label style="color: #bdc3c7; font-size: 12px;">水平对齐方式:</label>
+                <select id="alignMode" style="width: 100%; margin: 5px 0; padding: 5px;">
+                    <option value="center">中心对齐</option>
+                    <option value="top">顶边对齐</option>
+                    <option value="bottom">底边对齐</option>
+                </select>
+                <button class="btn btn-warning" onclick="alignHorizontal()">水平排列</button>
+                <button class="btn btn-warning" onclick="alignVertical()">垂直排列</button>
+                <button class="btn btn-warning" onclick="distributeHorizontal()">水平分布</button>
+                <button class="btn btn-warning" onclick="distributeVertical()">垂直分布</button>
+                <label style="color: #bdc3c7; font-size: 12px; margin-top: 10px; display: block;">中心点间距:</label>
+                <input type="number" id="customSpacing" value="100" style="width: 100%; margin: 5px 0; padding: 5px;" placeholder="中心点间距(像素)">
+                <button class="btn btn-warning" onclick="distributeHorizontalCustom()">按中心间距分布</button>
+            </div>
+
+            <div class="tool-section">
+                <h3>画布设置</h3>
+                <label style="color: #bdc3c7; font-size: 12px;">宽度:</label>
+                <input type="number" id="canvasWidth" value="800" style="width: 100%; margin: 5px 0; padding: 5px;">
+                <label style="color: #bdc3c7; font-size: 12px;">高度:</label>
+                <input type="number" id="canvasHeight" value="600" style="width: 100%; margin: 5px 0; padding: 5px;">
+                <button class="btn btn-primary" onclick="resizeCanvas()">调整画布</button>
+            </div>
+        </div>
+
+        <div class="canvas-container">
+            <div class="canvas" id="canvas"></div>
+            <div class="selection-box" id="selectionBox"></div>
+            
+            <div class="info-panel">
+                <div>画布尺寸: <span id="canvasSize">800 x 600</span></div>
+                <div>图片数量: <span id="imageCount">0</span></div>
+                <div>选中数量: <span id="selectedCount">0</span></div>
+                <div>缩放比例: <span id="zoomLevel">100%</span></div>
+            </div>
+
+            <div class="zoom-controls">
+                <button class="zoom-btn" onclick="zoomIn()">+</button>
+                <button class="zoom-btn" onclick="zoomOut()">-</button>
+                <button class="zoom-btn" onclick="resetZoom()">⌂</button>
+            </div>
+        </div>
+
+        <div class="status-bar">
+            <span id="statusText">就绪</span>
+        </div>
+    </div>
+
+    <script>
+        class ImageComposer {
+            constructor() {
+                this.canvas = document.getElementById('canvas');
+                this.images = [];
+                this.selectedImages = [];
+                this.isDragging = false;
+                this.isSelecting = false;
+                this.dragOffset = { x: 0, y: 0 };
+                this.canvasOffset = { x: 0, y: 0 };
+                this.zoom = 1;
+                this.canvasWidth = 800;
+                this.canvasHeight = 600;
+                
+                this.init();
+            }
+
+            init() {
+                this.setupCanvas();
+                this.setupEventListeners();
+                this.updateInfo();
+            }
+
+            setupCanvas() {
+                this.canvas.style.width = this.canvasWidth + 'px';
+                this.canvas.style.height = this.canvasHeight + 'px';
+                this.updateInfo();
+            }
+
+            setupEventListeners() {
+                // 文件输入
+                document.getElementById('fileInput').addEventListener('change', (e) => {
+                    this.handleFileSelect(e);
+                });
+
+                // 画布拖拽
+                this.canvas.addEventListener('mousedown', (e) => {
+                    if (e.target === this.canvas) {
+                        this.startCanvasDrag(e);
+                    } else if (e.target.closest('.image-item')) {
+                        this.startImageDrag(e);
+                    }
+                });
+
+                // 全局鼠标事件
+                document.addEventListener('mousemove', (e) => {
+                    this.handleMouseMove(e);
+                });
+
+                document.addEventListener('mouseup', (e) => {
+                    this.handleMouseUp(e);
+                });
+
+                // 键盘事件
+                document.addEventListener('keydown', (e) => {
+                    this.handleKeyDown(e);
+                });
+
+                // 防止拖拽默认行为
+                this.canvas.addEventListener('dragover', (e) => e.preventDefault());
+                this.canvas.addEventListener('drop', (e) => {
+                    e.preventDefault();
+                    this.handleFileDrop(e);
+                });
+            }
+
+            handleFileSelect(e) {
+                const files = Array.from(e.target.files);
+                this.loadImages(files);
+            }
+
+            handleFileDrop(e) {
+                const files = Array.from(e.dataTransfer.files).filter(file => 
+                    file.type.startsWith('image/')
+                );
+                this.loadImages(files);
+            }
+
+            loadImages(files) {
+                files.forEach(file => {
+                    const reader = new FileReader();
+                    reader.onload = (e) => {
+                        this.addImage(e.target.result, file.name);
+                    };
+                    reader.readAsDataURL(file);
+                });
+            }
+
+            addImage(src, name) {
+                const img = new Image();
+                img.onload = () => {
+                    // 计算新图片的位置:放在最右边
+                    let newX = 10; // 默认起始位置
+                    let newY = 10;
+                    
+                    if (this.images.length > 0) {
+                        // 找到最右边的图片
+                        const rightmostX = Math.max(...this.images.map(item => item.x + item.width));
+                        newX = rightmostX + 20; // 在最右边留20px间距
+                        
+                        // Y坐标与最后一个图片对齐,如果超出画布则重置为顶部
+                        const lastImage = this.images[this.images.length - 1];
+                        newY = lastImage.y;
+                        
+                        // 如果新位置超出画布,则换行
+                        if (newX + img.width > this.canvasWidth) {
+                            newX = 10;
+                            // 找到最底部的图片,在其下方放置
+                            const bottomY = Math.max(...this.images.map(item => item.y + item.height));
+                            newY = bottomY + 20;
+                        }
+                    }
+                    
+                    // 确保不超出画布边界
+                    newX = Math.max(0, Math.min(newX, this.canvasWidth - img.width));
+                    newY = Math.max(0, Math.min(newY, this.canvasHeight - img.height));
+
+                    const imageItem = {
+                        id: Date.now() + Math.random(),
+                        element: this.createImageElement(src, name),
+                        x: newX,
+                        y: newY,
+                        width: img.width,
+                        height: img.height,
+                        name: name
+                    };
+
+                    this.images.push(imageItem);
+                    this.canvas.appendChild(imageItem.element);
+                    this.updateImagePosition(imageItem);
+                    this.updateInfo();
+                    this.setStatus(`已添加图片: ${name}`);
+                };
+                img.src = src;
+            }
+
+            createImageElement(src, name) {
+                const div = document.createElement('div');
+                div.className = 'image-item';
+                div.innerHTML = `<img src="${src}" alt="${name}" title="${name}">`;
+                
+                div.addEventListener('click', (e) => {
+                    e.stopPropagation();
+                    if (e.ctrlKey) {
+                        this.toggleImageSelection(div);
+                    } else {
+                        this.selectImage(div);
+                    }
+                });
+
+                return div;
+            }
+
+            updateImagePosition(imageItem) {
+                imageItem.element.style.left = imageItem.x + 'px';
+                imageItem.element.style.top = imageItem.y + 'px';
+            }
+
+            startImageDrag(e) {
+                const imageElement = e.target.closest('.image-item');
+                const imageItem = this.images.find(img => img.element === imageElement);
+                
+                if (!imageItem) return;
+
+                if (!this.selectedImages.includes(imageItem)) {
+                    this.selectImage(imageElement);
+                }
+
+                this.isDragging = true;
+                const rect = this.canvas.getBoundingClientRect();
+                this.dragOffset = {
+                    x: e.clientX - rect.left - imageItem.x,
+                    y: e.clientY - rect.top - imageItem.y
+                };
+            }
+
+            startCanvasDrag(e) {
+                this.clearSelection();
+            }
+
+            handleMouseMove(e) {
+                if (this.isDragging && this.selectedImages.length > 0) {
+                    const rect = this.canvas.getBoundingClientRect();
+                    const newX = e.clientX - rect.left - this.dragOffset.x;
+                    const newY = e.clientY - rect.top - this.dragOffset.y;
+
+                    const deltaX = newX - this.selectedImages[0].x;
+                    const deltaY = newY - this.selectedImages[0].y;
+
+                    this.selectedImages.forEach(imageItem => {
+                        imageItem.x += deltaX;
+                        imageItem.y += deltaY;
+                        this.updateImagePosition(imageItem);
+                    });
+                }
+            }
+
+            handleMouseUp(e) {
+                this.isDragging = false;
+                this.isSelecting = false;
+            }
+
+            handleKeyDown(e) {
+                if (e.key === 'Delete') {
+                    this.deleteSelected();
+                } else if (e.ctrlKey && e.key === 'a') {
+                    e.preventDefault();
+                    this.selectAll();
+                }
+            }
+
+            selectImage(element) {
+                this.clearSelection();
+                const imageItem = this.images.find(img => img.element === element);
+                if (imageItem) {
+                    this.selectedImages = [imageItem];
+                    element.classList.add('selected');
+                    this.updateInfo();
+                }
+            }
+
+            toggleImageSelection(element) {
+                const imageItem = this.images.find(img => img.element === element);
+                if (!imageItem) return;
+
+                const index = this.selectedImages.indexOf(imageItem);
+                if (index > -1) {
+                    this.selectedImages.splice(index, 1);
+                    element.classList.remove('selected');
+                } else {
+                    this.selectedImages.push(imageItem);
+                    element.classList.add('selected');
+                }
+                this.updateInfo();
+            }
+
+            selectAll() {
+                this.selectedImages = [...this.images];
+                this.images.forEach(img => img.element.classList.add('selected'));
+                this.updateInfo();
+            }
+
+            clearSelection() {
+                this.selectedImages = [];
+                this.images.forEach(img => img.element.classList.remove('selected'));
+                this.updateInfo();
+            }
+
+            deleteSelected() {
+                if (this.selectedImages.length === 0) return;
+
+                this.selectedImages.forEach(imageItem => {
+                    this.canvas.removeChild(imageItem.element);
+                    const index = this.images.indexOf(imageItem);
+                    if (index > -1) {
+                        this.images.splice(index, 1);
+                    }
+                });
+
+                this.selectedImages = [];
+                this.updateInfo();
+                this.setStatus(`已删除 ${this.selectedImages.length} 个图片`);
+            }
+
+            alignHorizontal() {
+                if (this.selectedImages.length < 2) {
+                    this.setStatus('请选择至少2个图片进行排列');
+                    return;
+                }
+
+                // 按X坐标排序
+                this.selectedImages.sort((a, b) => a.x - b.x);
+                
+                const alignMode = document.getElementById('alignMode').value;
+                let referenceY;
+                
+                if (alignMode === 'center') {
+                    // 中心对齐:计算平均中心Y坐标
+                    const avgCenterY = this.selectedImages.reduce((sum, img) => sum + img.y + img.height / 2, 0) / this.selectedImages.length;
+                    this.selectedImages.forEach(imageItem => {
+                        imageItem.y = avgCenterY - imageItem.height / 2;
+                        this.updateImagePosition(imageItem);
+                    });
+                    this.setStatus(`已按中心水平对齐 ${this.selectedImages.length} 个图片`);
+                } else if (alignMode === 'top') {
+                    // 顶边对齐:找到最小Y坐标
+                    const minY = Math.min(...this.selectedImages.map(img => img.y));
+                    this.selectedImages.forEach(imageItem => {
+                        imageItem.y = minY;
+                        this.updateImagePosition(imageItem);
+                    });
+                    this.setStatus(`已按顶边水平对齐 ${this.selectedImages.length} 个图片`);
+                } else if (alignMode === 'bottom') {
+                    // 底边对齐:找到最大底边Y坐标
+                    const maxBottomY = Math.max(...this.selectedImages.map(img => img.y + img.height));
+                    this.selectedImages.forEach(imageItem => {
+                        imageItem.y = maxBottomY - imageItem.height;
+                        this.updateImagePosition(imageItem);
+                    });
+                    this.setStatus(`已按底边水平对齐 ${this.selectedImages.length} 个图片`);
+                }
+            }
+
+            alignVertical() {
+                if (this.selectedImages.length < 2) {
+                    this.setStatus('请选择至少2个图片进行排列');
+                    return;
+                }
+
+                // 按Y坐标排序
+                this.selectedImages.sort((a, b) => a.y - b.y);
+                
+                // 计算平均X坐标
+                const avgX = this.selectedImages.reduce((sum, img) => sum + img.x, 0) / this.selectedImages.length;
+                
+                // 设置相同的X坐标
+                this.selectedImages.forEach(imageItem => {
+                    imageItem.x = avgX;
+                    this.updateImagePosition(imageItem);
+                });
+
+                this.setStatus(`已垂直对齐 ${this.selectedImages.length} 个图片`);
+            }
+
+            distributeHorizontal() {
+                if (this.selectedImages.length < 3) {
+                    this.setStatus('请选择至少3个图片进行分布');
+                    return;
+                }
+
+                // 按X坐标排序
+                this.selectedImages.sort((a, b) => a.x - b.x);
+                
+                const first = this.selectedImages[0];
+                const last = this.selectedImages[this.selectedImages.length - 1];
+                const totalDistance = last.x - first.x;
+                const spacing = totalDistance / (this.selectedImages.length - 1);
+
+                this.selectedImages.forEach((imageItem, index) => {
+                    imageItem.x = first.x + spacing * index;
+                    this.updateImagePosition(imageItem);
+                });
+
+                this.setStatus(`已水平分布 ${this.selectedImages.length} 个图片`);
+            }
+
+            distributeVertical() {
+                if (this.selectedImages.length < 3) {
+                    this.setStatus('请选择至少3个图片进行分布');
+                    return;
+                }
+
+                // 按Y坐标排序
+                this.selectedImages.sort((a, b) => a.y - b.y);
+                
+                const first = this.selectedImages[0];
+                const last = this.selectedImages[this.selectedImages.length - 1];
+                const totalDistance = last.y - first.y;
+                const spacing = totalDistance / (this.selectedImages.length - 1);
+
+                this.selectedImages.forEach((imageItem, index) => {
+                    imageItem.y = first.y + spacing * index;
+                    this.updateImagePosition(imageItem);
+                });
+
+                this.setStatus(`已垂直分布 ${this.selectedImages.length} 个图片`);
+            }
+
+            distributeHorizontalCustom() {
+                if (this.selectedImages.length < 2) {
+                    this.setStatus('请选择至少2个图片进行分布');
+                    return;
+                }
+
+                const customSpacing = parseInt(document.getElementById('customSpacing').value);
+                if (isNaN(customSpacing) || customSpacing <= 0) {
+                    this.setStatus('请输入有效的中心点间距值');
+                    return;
+                }
+
+                // 按X坐标排序(按中心点排序)
+                this.selectedImages.sort((a, b) => (a.x + a.width / 2) - (b.x + b.width / 2));
+                
+                // 从第一个图片的中心点开始,按指定中心点间距排列
+                const firstCenterX = this.selectedImages[0].x + this.selectedImages[0].width / 2;
+                
+                this.selectedImages.forEach((imageItem, index) => {
+                    if (index === 0) {
+                        // 第一个图片保持原位置
+                        return;
+                    } else {
+                        // 后续图片按中心点间距排列
+                        const newCenterX = firstCenterX + customSpacing * index;
+                        imageItem.x = newCenterX - imageItem.width / 2;
+                    }
+                    this.updateImagePosition(imageItem);
+                });
+
+                this.setStatus(`已按 ${customSpacing}px 中心点间距水平分布 ${this.selectedImages.length} 个图片`);
+            }
+
+            resizeCanvas() {
+                const width = parseInt(document.getElementById('canvasWidth').value);
+                const height = parseInt(document.getElementById('canvasHeight').value);
+                
+                if (width > 0 && height > 0) {
+                    this.canvasWidth = width;
+                    this.canvasHeight = height;
+                    this.setupCanvas();
+                    this.setStatus(`画布已调整为 ${width} x ${height}`);
+                }
+            }
+
+            clearCanvas() {
+                if (confirm('确定要清空画布吗?')) {
+                    this.images.forEach(img => this.canvas.removeChild(img.element));
+                    this.images = [];
+                    this.selectedImages = [];
+                    this.updateInfo();
+                    this.setStatus('画布已清空');
+                }
+            }
+
+            exportCanvas() {
+                if (this.images.length === 0) {
+                    this.setStatus('画布为空,无法导出');
+                    return;
+                }
+
+                // 计算实际内容边界
+                let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
+                
+                this.images.forEach(imageItem => {
+                    minX = Math.min(minX, imageItem.x);
+                    minY = Math.min(minY, imageItem.y);
+                    maxX = Math.max(maxX, imageItem.x + imageItem.width);
+                    maxY = Math.max(maxY, imageItem.y + imageItem.height);
+                });
+
+                const contentWidth = maxX - minX;
+                const contentHeight = maxY - minY;
+
+                // 创建临时canvas
+                const tempCanvas = document.createElement('canvas');
+                const ctx = tempCanvas.getContext('2d');
+                tempCanvas.width = contentWidth;
+                tempCanvas.height = contentHeight;
+
+                // 绘制所有图片
+                let loadedCount = 0;
+                const totalImages = this.images.length;
+
+                this.images.forEach(imageItem => {
+                    const img = new Image();
+                    img.onload = () => {
+                        ctx.drawImage(img, imageItem.x - minX, imageItem.y - minY, imageItem.width, imageItem.height);
+                        loadedCount++;
+                        
+                        if (loadedCount === totalImages) {
+                            // 所有图片加载完成,导出
+                            tempCanvas.toBlob(blob => {
+                                const url = URL.createObjectURL(blob);
+                                const a = document.createElement('a');
+                                a.href = url;
+                                a.download = `composed_content_${Date.now()}.png`;
+                                a.click();
+                                URL.revokeObjectURL(url);
+                                this.setStatus('内容已导出');
+                            });
+                        }
+                    };
+                    img.src = imageItem.element.querySelector('img').src;
+                });
+            }
+
+            exportFullCanvas() {
+                 if (this.images.length === 0) {
+                     this.setStatus('画布为空,无法导出');
+                     return;
+                 }
+ 
+                 // 创建临时canvas,使用完整画布大小
+                 const tempCanvas = document.createElement('canvas');
+                 const ctx = tempCanvas.getContext('2d');
+                 tempCanvas.width = this.canvasWidth;
+                 tempCanvas.height = this.canvasHeight;
+ 
+                 // 背景保持透明,不填充任何颜色
+
+                // 绘制所有图片
+                let loadedCount = 0;
+                const totalImages = this.images.length;
+
+                this.images.forEach(imageItem => {
+                    const img = new Image();
+                    img.onload = () => {
+                        ctx.drawImage(img, imageItem.x, imageItem.y, imageItem.width, imageItem.height);
+                        loadedCount++;
+                        
+                        if (loadedCount === totalImages) {
+                            // 所有图片加载完成,导出
+                            tempCanvas.toBlob(blob => {
+                                const url = URL.createObjectURL(blob);
+                                const a = document.createElement('a');
+                                a.href = url;
+                                a.download = `full_canvas_${Date.now()}.png`;
+                                a.click();
+                                URL.revokeObjectURL(url);
+                                this.setStatus('完整画布已导出');
+                            });
+                        }
+                    };
+                    img.src = imageItem.element.querySelector('img').src;
+                });
+            }
+
+            fitCanvasToContent() {
+                if (this.images.length === 0) {
+                    this.setStatus('没有图片,无法调整画布大小');
+                    return;
+                }
+
+                // 计算所有图片的边界
+                let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
+                
+                this.images.forEach(imageItem => {
+                    minX = Math.min(minX, imageItem.x);
+                    minY = Math.min(minY, imageItem.y);
+                    maxX = Math.max(maxX, imageItem.x + imageItem.width);
+                    maxY = Math.max(maxY, imageItem.y + imageItem.height);
+                });
+
+                // 添加一些边距
+                const padding = 20;
+                const newWidth = Math.max(400, maxX - minX + padding * 2);
+                const newHeight = Math.max(300, maxY - minY + padding * 2);
+
+                // 调整所有图片位置
+                const offsetX = padding - minX;
+                const offsetY = padding - minY;
+
+                this.images.forEach(imageItem => {
+                    imageItem.x += offsetX;
+                    imageItem.y += offsetY;
+                    this.updateImagePosition(imageItem);
+                });
+
+                // 更新画布大小
+                this.canvasWidth = newWidth;
+                this.canvasHeight = newHeight;
+                this.canvas.style.width = newWidth + 'px';
+                this.canvas.style.height = newHeight + 'px';
+
+                // 更新画布大小输入框
+                document.getElementById('canvasWidth').value = newWidth;
+                document.getElementById('canvasHeight').value = newHeight;
+
+                this.updateInfo();
+                this.setStatus(`画布已调整为 ${newWidth}x${newHeight}`);
+            }
+
+            zoomIn() {
+                this.zoom = Math.min(this.zoom * 1.2, 3);
+                this.applyZoom();
+            }
+
+            zoomOut() {
+                this.zoom = Math.max(this.zoom / 1.2, 0.1);
+                this.applyZoom();
+            }
+
+            resetZoom() {
+                this.zoom = 1;
+                this.applyZoom();
+            }
+
+            applyZoom() {
+                this.canvas.style.transform = `translate(-50%, -50%) scale(${this.zoom})`;
+                this.updateInfo();
+            }
+
+            updateInfo() {
+                document.getElementById('canvasSize').textContent = `${this.canvasWidth} x ${this.canvasHeight}`;
+                document.getElementById('imageCount').textContent = this.images.length;
+                document.getElementById('selectedCount').textContent = this.selectedImages.length;
+                document.getElementById('zoomLevel').textContent = Math.round(this.zoom * 100) + '%';
+            }
+
+            setStatus(message) {
+                document.getElementById('statusText').textContent = message;
+                setTimeout(() => {
+                    document.getElementById('statusText').textContent = '就绪';
+                }, 3000);
+            }
+        }
+
+        // 全局函数
+        let composer;
+
+        function init() {
+            composer = new ImageComposer();
+        }
+
+        function importImages() {
+            document.getElementById('fileInput').click();
+        }
+
+        function exportCanvas() {
+            composer.exportCanvas();
+        }
+
+        function exportFullCanvas() {
+            composer.exportFullCanvas();
+        }
+
+        function fitCanvasToContent() {
+            composer.fitCanvasToContent();
+        }
+
+        function clearCanvas() {
+            composer.clearCanvas();
+        }
+
+        function selectAll() {
+            composer.selectAll();
+        }
+
+        function clearSelection() {
+            composer.clearSelection();
+        }
+
+        function deleteSelected() {
+            composer.deleteSelected();
+        }
+
+        function alignHorizontal() {
+            composer.alignHorizontal();
+        }
+
+        function alignVertical() {
+            composer.alignVertical();
+        }
+
+        function distributeHorizontal() {
+            composer.distributeHorizontal();
+        }
+
+        function distributeVertical() {
+            composer.distributeVertical();
+        }
+
+        function distributeHorizontalCustom() {
+            composer.distributeHorizontalCustom();
+        }
+
+        function resizeCanvas() {
+            composer.resizeCanvas();
+        }
+
+        function zoomIn() {
+            composer.zoomIn();
+        }
+
+        function zoomOut() {
+            composer.zoomOut();
+        }
+
+        function resetZoom() {
+            composer.resetZoom();
+        }
+
+        // 初始化
+        document.addEventListener('DOMContentLoaded', init);
+    </script>
+</body>
+</html>

Some files were not shown because too many files changed in this diff