Compare commits
23 Commits
fe823df01e
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3d246de24c | ||
|
|
f8198e0463 | ||
|
|
dd883f14b0 | ||
|
|
16efb1bb25 | ||
|
|
fbea31b9ea | ||
|
|
950572ad74 | ||
|
|
9d60fa601d | ||
|
|
1e5017e28e | ||
|
|
5074706115 | ||
|
|
f5732b46a5 | ||
|
|
ecc82ae9a7 | ||
|
|
4f93725779 | ||
|
|
8d54ffdbf8 | ||
|
|
4d699c127f | ||
|
|
457dd07d80 | ||
|
|
5eef9d8528 | ||
|
|
34e06480ce | ||
|
|
5d472e1c30 | ||
|
|
ddf51919b0 | ||
|
|
2f74f260b7 | ||
|
|
9cf499a5e1 | ||
|
|
69c0986996 | ||
|
|
447e7a944a |
102
AGENTS.md
Normal file
@@ -0,0 +1,102 @@
|
||||
# Repository Guidelines
|
||||
|
||||
## 项目结构与模块组织
|
||||
本仓库是 `Cocos Creator 3.8.8` 小游戏项目。主入口在 `assets/main.ts`,主场景是 `assets/main.scene`。页面与弹窗组件集中在 `assets/prefabs/`,命名通常为 `PageXxx.ts`、`PassModal.ts`。公共逻辑位于 `assets/scripts/`:`core/` 放页面基类与视图管理,`utils/` 放 SDK、鉴权、存储、网络等工具,`config/` 放接口配置,`types/` 放类型定义。静态资源在 `assets/resources/`,编辑器配置在 `settings/v2/packages/`,设计说明在 `docs/` 与根目录分析文档中。
|
||||
|
||||
## 构建、调试与开发命令
|
||||
先运行 `npm install`,同步 `minigame-api-typings` 依赖。日常开发主要通过 Cocos Creator 编辑器完成:使用 3.8.8 打开仓库,点击 `Play` 预览,使用 `Project > Build` 或 `Cmd+B` 构建小游戏包。若编辑器已生成 `temp/tsconfig.cocos.json`,可执行 `npx tsc --noEmit` 做一次 TypeScript 静态检查。
|
||||
|
||||
## 代码风格与命名约定
|
||||
项目使用 TypeScript,当前代码统一为 4 空格缩进。组件类、页面类、管理器类使用 `PascalCase`,实例属性与私有方法使用 `camelCase` / `_camelCase`,管理器统一使用 `XxxManager` 后缀。新增页面、预制体、脚本请保持同名,例如 `PageLevel.prefab` 对应 `PageLevel.ts`。优先把复用逻辑放入 `assets/scripts/utils/` 或 `assets/scripts/core/`,不要把业务代码散落到场景脚本中。Cocos 资源的 `.meta` 文件必须一并提交。
|
||||
|
||||
## 测试与验证
|
||||
仓库当前未配置 Jest、Vitest 一类自动化测试。提交前至少完成三项验证:1. 编辑器预览主流程可进入页面;2. 目标平台构建成功;3. 涉及微信能力时,在真机或开发者工具验证登录、分享、隐私授权等流程。若修改接口或体力/关卡逻辑,补充手动验证步骤到 PR 描述。
|
||||
|
||||
## 提交与 Pull Request 规范
|
||||
Git 历史采用 Conventional Commits,且摘要多为中文,例如 `feat: 支持分享关卡通关上报`、`fix: 修复关卡排序`、`docs: 添加设计文档`。请继续使用 `feat:`、`fix:`、`perf:`、`docs:` 前缀,首行聚焦单一变更。PR 需说明改动范围、影响页面或模块、验证方式;涉及 UI 请附截图或录屏,涉及微信环境差异请写明复现条件与平台。
|
||||
|
||||
|
||||
<claude-mem-context>
|
||||
# Memory Context
|
||||
|
||||
# [mp-xieyingeng] recent context, 2026-04-29 6:32pm GMT+8
|
||||
|
||||
Legend: 🎯session 🔴bugfix 🟣feature 🔄refactor ✅change 🔵discovery ⚖️decision 🚨security_alert 🔐security_note
|
||||
Format: ID TIME TYPE TITLE
|
||||
Fetch details: get_observations([IDs]) | Search: mem-search skill
|
||||
|
||||
Stats: 50 obs (9,755t read) | 1,857,754t work | 99% savings
|
||||
|
||||
### Apr 26, 2026
|
||||
1330 4:45p 🔄 API Protocol Upgrade - NextLevel-Driven Architecture
|
||||
1331 " ✅ ApiTypes.ts type definitions updated
|
||||
1332 " ✅ Task 4 completed: ApiTypes.ts type definitions updated
|
||||
1333 4:46p ✅ Tasks 4 and 5 completed: type definitions migrated
|
||||
1334 5:10p 🔵 进度条小百分比时圆角畸形问题
|
||||
1335 " 🔵 进度条圆角畸形问题调查中
|
||||
1336 " 🔵 通关弹窗进度条使用 Cocos ProgressBar 组件
|
||||
1337 5:11p 🔵 进度条圆角畸形调查中 - Prefab 结构检查
|
||||
1338 " 🔵 ProgressBar 系统架构已定位
|
||||
1340 " 🔵 找到进度条圆角畸形根本原因
|
||||
1341 5:12p 🔵 进度条小百分比时圆角变形问题定位
|
||||
1339 " 🔵 发现 RoundedRectMask 工具组件
|
||||
S1309 移除 PageLevel.ts 进入关卡时弹出体力扣减 toast (Apr 26 at 5:16 PM)
|
||||
1342 5:16p 🔵 PassModal 进度值规范化逻辑
|
||||
1343 5:17p 🔴 进度条圆角畸形问题已修复
|
||||
1344 " ⚖️ 探索使用 Mask 裁剪替代最小进度值方案
|
||||
1345 " ⚖️ 否决 Mask 方案,确认最小进度值方案最优
|
||||
### Apr 27, 2026
|
||||
1346 9:21a 🔴 移除进入游戏后的体力扣减 toast 提示
|
||||
1347 " 🔴 移除进入游戏后的体力扣减 toast 提示
|
||||
1348 " 🟣 首页添加体力值显示功能
|
||||
S1308 移除进入游戏关卡时弹出体力扣减的 toast 提示 (Apr 27 at 9:21 AM)
|
||||
S1310 移除进入游戏体力扣减 toast 并在首页添加体力显示 (Apr 27 at 9:22 AM)
|
||||
S1311 移除体力扣减 toast 并完善首页体验 (Apr 27 at 9:23 AM)
|
||||
1349 9:25a ✅ PageHome.ts 添加 ToastManager 导入
|
||||
1350 " ✅ PK按钮点击改为提示"功能开发中"
|
||||
S1313 Implement object-fit: cover image scaling in Cocos Creator to prevent downloaded images from being distorted (Apr 27 at 9:26 AM)
|
||||
1351 9:32a 🟣 Stamina animation system for level entry
|
||||
1352 " 🔵 Cocos Creator game project structure discovered
|
||||
1354 " 🟣 StarGame 按钮点击动画需求
|
||||
1356 " 🔵 体力系统架构发现
|
||||
1353 9:33a 🔵 StaminaManager API confirmed for stamina operations
|
||||
1355 9:37a 🔵 项目结构和现有代码发现
|
||||
1357 9:38a 🔵 EnterLevelData 结构分析
|
||||
1359 9:39a 🟣 Stamina animation system implemented for game entry flow
|
||||
1358 9:41a ⚖️ 体力值飞行动画实现计划制定完成
|
||||
1362 9:45a 🟣 Image Cover Mode Scaling in Cocos Creator
|
||||
S1312 Fix image distortion in PageLevel.ts by implementing object-fit: cover behavior for downloaded images (Apr 27 at 9:45 AM)
|
||||
1360 " ✅ Cocos Creator animation imports added to PageHome.ts
|
||||
1361 " ✅ Animation constants added to PageHome class
|
||||
1363 " ✅ Start game button integrated with stamina check and animation flow
|
||||
1364 " 🟣 Stamina consumption animation fully implemented in PageHome
|
||||
S1314 Implement stamina consumption animation in Cocos Creator game - when clicking StarGame button, IconLive node flies to button with floating "-1" text, then navigates to level (Apr 27 at 9:45 AM)
|
||||
1365 9:46a 🔴 Animation completes but navigation to PageLevel fails
|
||||
S1315 为 PageLevel.ts 的 mainImage 和 mainImage2 节点实现 CSS cover 模式图片缩放 (Apr 27 at 9:46 AM)
|
||||
1366 9:50a 🔴 Cocos Creator tween chain broken by premature node destroy
|
||||
1367 9:55a 🔴 Navigation still failing after first tween chain fix - further debugging needed
|
||||
1368 " 🔴 Decoupled fly animation from float text timing using setTimeout
|
||||
1369 9:56a 🔴 Cocos Creator tween chain broken by any node state change mid-animation
|
||||
1370 9:59a 🟣 CSS cover-style image scaling for PageLevel images
|
||||
1371 10:01a ✅ PageLevel.ts imports extended for cover-style image scaling
|
||||
1372 " 🟣 CSS cover-mode image scaling implemented for PageLevel images
|
||||
S1316 Debug cover mode image overflow in PageLevel.ts - image exceeds container bounds despite Mask applied (Apr 27 at 10:02 AM)
|
||||
1373 10:02a 🔴 Cover mode image overflows container bounds
|
||||
1374 10:06a 🔵 GRAPHICS_RECT vs GRAPHICS_STENCIL for Mask in Cocos Creator
|
||||
1375 10:08a 🔴 Mask.Type.RECT does not exist in Cocos Creator API
|
||||
1376 " 🔵 MaskType enum found in Cocos Creator 3.8 engine declarations
|
||||
1378 " 🔵 MaskType enum values confirmed - GRAPHICS_RECT exists
|
||||
1377 10:09a 🔵 MaskType enum definition found at line 46015
|
||||
1379 10:10a 🔴 Reverted Mask.Type to GRAPHICS_RECT
|
||||
S1317 Implement CSS cover-mode image scaling for PageLevel.ts mainImage nodes (Apr 27 at 10:10 AM)
|
||||
**Investigated**: Cover mode implementation in _applyCoverSprite() using Mask + scaled child node approach; Cocos Creator 3.8 MaskType enum investigation; root cause analysis of image overflow
|
||||
|
||||
**Learned**: Cocos Creator Mask only clips child nodes, NOT the container's own Sprite component; GRAPHICS_RECT = 0 is valid enum value in Cocos 3.8; container Sprite renders outside Mask bounds; solution: disable containerSprite and use child node for image display
|
||||
|
||||
**Completed**: _applyCoverSprite() method implemented with: Mask(GRAPHICS_RECT) on container, child node '_coverImg' with Sprite.SizeMode.RAW, cover scale calculation, containerSprite.enabled = false to prevent overflow
|
||||
|
||||
**Next Steps**: User to test in Cocos Creator editor to verify image clipping now works correctly
|
||||
|
||||
|
||||
Access 1858k tokens of past work via get_observations([IDs]) or mem-search skill.
|
||||
</claude-mem-context>
|
||||
496
ARCHITECTURE.md
@@ -1,496 +0,0 @@
|
||||
# Architecture Overview
|
||||
|
||||
## System Architecture Diagram
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ COCOS CREATOR GAME │
|
||||
│ WeChat Mini-Game Version 3.8.8 │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ PRESENTATION LAYER (UI) │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ PageLoading.ts ──────┐ │
|
||||
│ (Loading Screen) │ │
|
||||
│ │ │
|
||||
│ ┌──────────┐ │ ┌──────────┐ ┌──────────┐ │
|
||||
│ │PageHome │─────────┴──────│PageLevel │──────│PassModal │ │
|
||||
│ │(Menu) │ │ (Play) │ │(Victory) │ │
|
||||
│ └──────────┘ └──────────┘ └──────────┘ │
|
||||
│ ▲ ▲ │ │
|
||||
│ │ │ │ │
|
||||
│ └────────────────┬───────────┘ │ │
|
||||
│ │ (Back button) │ │
|
||||
│ └─────────────────────────────┘ │
|
||||
│ │
|
||||
│ Toast.ts ────────────────────────────────────────────────────────────│
|
||||
│ (Notifications) │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ CORE LOGIC LAYER │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌──────────────────────┐ ┌──────────────────────┐ │
|
||||
│ │ PageLevel.ts │ │ PassModal.ts │ │
|
||||
│ ├──────────────────────┤ ├──────────────────────┤ │
|
||||
│ │ • Answer validation │ │ • Next level │ │
|
||||
│ │ • Hint unlock (-life)│ │ • Share button │ │
|
||||
│ │ • Countdown (60s) │ │ • Callbacks │ │
|
||||
│ │ • Sound/Vibration │ │ │ │
|
||||
│ │ • Life display │ │ │ │
|
||||
│ └──────────────────────┘ └──────────────────────┘ │
|
||||
│ ▲ ▲ │
|
||||
│ │ │ │
|
||||
│ └────────────────┬───────────────┘ │
|
||||
│ │ │
|
||||
│ ┌──────────────────────────┴──────────────────────────────────┐ │
|
||||
│ │ ViewManager.ts (View Navigation) │ │
|
||||
│ ├──────────────────────────────────────────────────────────────┤ │
|
||||
│ │ • Page registration & caching │ │
|
||||
│ │ • Page stack management (push/pop/replace) │ │
|
||||
│ │ • Lifecycle: onViewLoad → onViewShow → onViewHide → destroy │ │
|
||||
│ │ • Parameter passing between pages │ │
|
||||
│ └──────────────────────────────────────────────────────────────┘ │
|
||||
│ ▲ │
|
||||
│ │ │
|
||||
│ BaseView.ts (Abstract base class for all pages) │
|
||||
│ • Lifecycle hooks │
|
||||
│ • Page state management │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ DATA & STATE MANAGEMENT LAYER │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌──────────────────────────────────────────────────────────────┐ │
|
||||
│ │ StorageManager.ts (Single Source of Truth) │ │
|
||||
│ ├──────────────────────────────────────────────────────────────┤ │
|
||||
│ │ Lives Management: │ │
|
||||
│ │ • getLives() / setLives() / consumeLife() / addLife() │ │
|
||||
│ │ │ │
|
||||
│ │ Progress Management: │ │
|
||||
│ │ • getCurrentLevelIndex() / getMaxUnlockedLevelIndex() │ │
|
||||
│ │ • onLevelCompleted(levelIndex) │ │
|
||||
│ │ • isLevelUnlocked(levelIndex) │ │
|
||||
│ │ │ │
|
||||
│ │ Storage Backend: sys.localStorage │ │
|
||||
│ │ └─ game_lives: string number (default: "10") │ │
|
||||
│ │ └─ game_progress: JSON with currentIndex & maxUnlockedIdx │ │
|
||||
│ └──────────────────────────────────────────────────────────────┘ │
|
||||
│ ▲ │
|
||||
│ │ │
|
||||
│ ┌──────────────────────────────────────────────────────────────┐ │
|
||||
│ │ LevelDataManager.ts (Level Data & Assets) │ │
|
||||
│ ├──────────────────────────────────────────────────────────────┤ │
|
||||
│ │ • initialize(onProgress) - Fetch API & load first level │ │
|
||||
│ │ • getLevelConfig(index) - Get level data + image │ │
|
||||
│ │ • ensureLevelReady(index) - On-demand level loading │ │
|
||||
│ │ • preloadNextLevel(currentIndex) - Async preload │ │
|
||||
│ │ │ │
|
||||
│ │ Memory Caches: │ │
|
||||
│ │ • _apiData: All levels from server │ │
|
||||
│ │ • _levelConfigs: Map of loaded level configs │ │
|
||||
│ │ • _imageCache: Map of loaded images (SpriteFrames) │ │
|
||||
│ │ • _loadingLevels: Set of levels being loaded │ │
|
||||
│ └──────────────────────────────────────────────────────────────┘ │
|
||||
│ ▲ │
|
||||
│ │ │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ UTILITY & SDK LAYER │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌────────────────────┐ ┌────────────────────┐ ┌──────────────────┐ │
|
||||
│ │ HttpUtil.ts │ │ WxSDK.ts │ │ToastManager.ts │ │
|
||||
│ ├────────────────────┤ ├────────────────────┤ ├──────────────────┤ │
|
||||
│ │ • get<T>() │ │ • isWechat() │ │ • init() │ │
|
||||
│ │ • post<T>() │ │ • initShare() │ │ • show() │ │
|
||||
│ │ │ │ • shareAppMessage()│ │ │ │
|
||||
│ │ Timeout: 10s │ │ • onShareAppMsg() │ │ Display duration │ │
|
||||
│ │ Error handling │ │ • vibrateShort() │ │ Fade out anim │ │
|
||||
│ │ │ │ • vibrateLong() │ │ │ │
|
||||
│ └────────────────────┘ └────────────────────┘ └──────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ EXTERNAL SYSTEMS │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌──────────────────────────────────────┐ │
|
||||
│ │ WeChat Mini-Game SDK (wx global) │ │
|
||||
│ ├──────────────────────────────────────┤ │
|
||||
│ │ • wx.shareAppMessage() │ │
|
||||
│ │ • wx.onShareAppMessage() │ │
|
||||
│ │ • wx.showShareMenu() │ │
|
||||
│ │ • wx.vibrateShort() │ │
|
||||
│ │ • wx.vibrateLong() │ │
|
||||
│ └──────────────────────────────────────┘ │
|
||||
│ ▲ │
|
||||
│ │ │
|
||||
│ │ │
|
||||
│ ┌──────────────────────────────────────┐ │
|
||||
│ │ Backend API Server │ │
|
||||
│ │ https://ilookai.cn │ │
|
||||
│ ├──────────────────────────────────────┤ │
|
||||
│ │ GET /api/v1/wechat-game/levels │ │
|
||||
│ │ • Returns: {success, data, message} │ │
|
||||
│ │ • Retry: 2x with 1s delay │ │
|
||||
│ │ • Timeout: 8s │ │
|
||||
│ └──────────────────────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Data Flow Diagram: Complete User Session
|
||||
|
||||
```
|
||||
START
|
||||
│
|
||||
├─ [main.ts] onLoad()
|
||||
│ └─ ViewManager.init(canvas)
|
||||
│ └─ ToastManager.init(toastPrefab)
|
||||
│ └─ Register PageHome & PageLevel
|
||||
│
|
||||
├─ [PageLoading.ts] start()
|
||||
│ ├─ LevelDataManager.initialize()
|
||||
│ │ ├─ HttpUtil.get() → API call
|
||||
│ │ │ └─ https://ilookai.cn/api/v1/wechat-game/levels
|
||||
│ │ ├─ Cache response → _apiData
|
||||
│ │ ├─ Load first level image
|
||||
│ │ │ └─ assetManager.loadRemote() → SpriteFrame
|
||||
│ │ │ └─ Cache in _imageCache
|
||||
│ │ └─ Return success
|
||||
│ │
|
||||
│ └─ ViewManager.preload('PageHome')
|
||||
│ └─ ViewManager.open('PageHome')
|
||||
│ └─ PageHome.onViewLoad()
|
||||
│ └─ WxSDK.initShare()
|
||||
│
|
||||
├─ [USER CLICKS "START GAME"]
|
||||
│
|
||||
├─ ViewManager.open('PageLevel', {params: {levelIndex: 0}})
|
||||
│ └─ PageLevel.onViewLoad()
|
||||
│ ├─ StorageManager.getCurrentLevelIndex()
|
||||
│ ├─ PageLevel.initLevel()
|
||||
│ │ ├─ LevelDataManager.ensureLevelReady(levelIndex)
|
||||
│ │ │ ├─ Check cache (_levelConfigs)
|
||||
│ │ │ ├─ If not cached: load image + create config
|
||||
│ │ │ └─ Store in cache
|
||||
│ │ │
|
||||
│ │ └─ PageLevel._applyLevelConfig()
|
||||
│ │ ├─ Display main image (sprite)
|
||||
│ │ ├─ Show Hint 1 (free)
|
||||
│ │ ├─ Hide Hints 2, 3 (show unlock buttons)
|
||||
│ │ ├─ Create input field based on answer.length
|
||||
│ │ ├─ Display lives: StorageManager.getLives()
|
||||
│ │ ├─ Start countdown (60s)
|
||||
│ │ └─ LevelDataManager.preloadNextLevel() [async]
|
||||
│
|
||||
├─ [USER INTERACTS]
|
||||
│
|
||||
├─ Option A: UNLOCK HINT
|
||||
│ ├─ PageLevel.onUnlockClue(2 or 3)
|
||||
│ ├─ Check: StorageManager.hasLives()?
|
||||
│ ├─ Yes → StorageManager.consumeLife() → display hint
|
||||
│ └─ No → Show "生命值不足" message
|
||||
│
|
||||
├─ Option B: SUBMIT ANSWER
|
||||
│ ├─ PageLevel.onSubmitAnswer()
|
||||
│ ├─ Validate: userAnswer === correctAnswer?
|
||||
│ │
|
||||
│ ├─ YES (Correct)
|
||||
│ │ ├─ PageLevel.showSuccess()
|
||||
│ │ ├─ Stop countdown
|
||||
│ │ ├─ Play successAudio
|
||||
│ │ ├─ StorageManager.addLife() [+1]
|
||||
│ │ ├─ PageLevel._showPassModal()
|
||||
│ │ │ ├─ Instantiate PassModal prefab
|
||||
│ │ │ ├─ PassModal.onViewLoad()
|
||||
│ │ │ ├─ PassModal.onViewShow()
|
||||
│ │ │ │ └─ Play success sound
|
||||
│ │ │ │ └─ Adjust widget to full screen
|
||||
│ │ │ │
|
||||
│ │ │ └─ User clicks button:
|
||||
│ │ │ ├─ "Next Level" → PageLevel.nextLevel()
|
||||
│ │ │ │ ├─ StorageManager.onLevelCompleted(currentIndex)
|
||||
│ │ │ │ │ ├─ currentLevelIndex++
|
||||
│ │ │ │ │ ├─ maxUnlockedLevelIndex = max(prev, current)
|
||||
│ │ │ │ │ └─ Save to localStorage
|
||||
│ │ │ │ ├─ Reload PageLevel with new index
|
||||
│ │ │ │ │ └─ Repeat from Level Setup
|
||||
│ │ │ │ │
|
||||
│ │ │ │ └─ If last level:
|
||||
│ │ │ │ └─ ViewManager.back() → return to PageHome
|
||||
│ │ │ │
|
||||
│ │ │ └─ "Share" → WxSDK.shareAppMessage()
|
||||
│ │ │ └─ query: `level=${currentIndex+1}`
|
||||
│ │
|
||||
│ ├─ NO (Wrong)
|
||||
│ │ ├─ PageLevel.showError()
|
||||
│ │ ├─ Play failAudio
|
||||
│ │ ├─ WxSDK.vibrateLong() [device vibration]
|
||||
│ │ ├─ ToastManager.show("答案错误,再试试吧!")
|
||||
│ │ │ ├─ Create Toast node
|
||||
│ │ │ ├─ Show for 2000ms
|
||||
│ │ │ ├─ Fade out animation (300ms)
|
||||
│ │ │ └─ Destroy node
|
||||
│ │ │
|
||||
│ │ └─ Continue playing (allow retry)
|
||||
│
|
||||
├─ Option C: TIMEOUT
|
||||
│ └─ Countdown reaches 0
|
||||
│ ├─ PageLevel.onTimeUp()
|
||||
│ ├─ Play failAudio
|
||||
│ └─ [Incomplete: can still submit after timeout]
|
||||
│
|
||||
├─ [USER CLICKS BACK BUTTON]
|
||||
│ ├─ ViewManager.back()
|
||||
│ │ ├─ PageLevel._doHide()
|
||||
│ │ └─ PageHome._doShow()
|
||||
│ │
|
||||
│ └─ Return to home (progress saved in localStorage)
|
||||
│
|
||||
└─ END
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## State Management Flow
|
||||
|
||||
```
|
||||
USER PROGRESS STATE
|
||||
│
|
||||
├─ localStorage: game_progress
|
||||
│ └─ {
|
||||
│ "currentLevelIndex": 5,
|
||||
│ "maxUnlockedLevelIndex": 5
|
||||
│ }
|
||||
│
|
||||
├─ Memory Cache (LevelDataManager)
|
||||
│ ├─ _apiData: ApiLevelData[] (all levels)
|
||||
│ ├─ _levelConfigs: Map<levelIndex, RuntimeLevelConfig>
|
||||
│ ├─ _imageCache: Map<url, SpriteFrame>
|
||||
│ └─ _loadingLevels: Set<levelIndex> (currently loading)
|
||||
│
|
||||
└─ Session State (PageLevel component)
|
||||
├─ currentLevelIndex: number
|
||||
├─ _currentConfig: RuntimeLevelConfig
|
||||
├─ _countdown: number (60 → 0)
|
||||
├─ _isTimeUp: boolean
|
||||
├─ _isTransitioning: boolean
|
||||
└─ _passModalNode: Node | null
|
||||
|
||||
|
||||
LIVES STATE
|
||||
│
|
||||
├─ localStorage: game_lives
|
||||
│ └─ "10" (string representation of number)
|
||||
│
|
||||
├─ Cached value (StorageManager._progressCache)
|
||||
│ └─ Read on first access, cached for performance
|
||||
│
|
||||
└─ Operations (immediate storage update)
|
||||
├─ getLives() → read from storage
|
||||
├─ setLives(n) → validate & write to storage
|
||||
├─ consumeLife() → getLives() - 1, setLives()
|
||||
├─ addLife() → getLives() + 1, setLives()
|
||||
└─ hasLives() → getLives() > 0
|
||||
|
||||
|
||||
API CACHE
|
||||
│
|
||||
├─ First Load (Startup)
|
||||
│ ├─ HttpUtil.get(apiUrl, 8000ms timeout)
|
||||
│ ├─ Retry: 2 times (1s delay between)
|
||||
│ └─ Cache in LevelDataManager._apiData
|
||||
│
|
||||
└─ Per-Level Resources (On-Demand)
|
||||
├─ Check _levelConfigs cache
|
||||
├─ If missing: assetManager.loadRemote(imageUrl)
|
||||
├─ Create SpriteFrame
|
||||
└─ Cache in _imageCache
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## View Stack & Navigation
|
||||
|
||||
```
|
||||
VIEW STACK (LIFO - Last In First Out)
|
||||
│
|
||||
├─ Level 0 (Bottom)
|
||||
│ └─ PageHome
|
||||
│
|
||||
├─ Level 1 (Middle)
|
||||
│ └─ PageLevel
|
||||
│
|
||||
├─ Level 2 (Top - Current)
|
||||
│ └─ PassModal (temporary, on PageLevel)
|
||||
│
|
||||
└─ Operations
|
||||
├─ open(viewId) → push to stack + show
|
||||
├─ back() → pop from stack + hide + show prev
|
||||
├─ replace(viewId) → pop + push new view
|
||||
└─ close() → pop + show previous
|
||||
|
||||
|
||||
CACHING BEHAVIOR
|
||||
│
|
||||
├─ PageHome
|
||||
│ ├─ cache: true
|
||||
│ └─ Cached after first open, reused on back
|
||||
│
|
||||
├─ PageLevel
|
||||
│ ├─ cache: true
|
||||
│ └─ Cached per instance (but reinitializes on each open)
|
||||
│
|
||||
└─ PassModal
|
||||
├─ Dynamically instantiated
|
||||
├─ Not cached
|
||||
└─ Destroyed after close
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Class Hierarchy
|
||||
|
||||
```
|
||||
Component (Cocos)
|
||||
│
|
||||
├─ BaseView (Abstract base)
|
||||
│ ├─ PageHome
|
||||
│ ├─ PageLevel
|
||||
│ └─ PassModal
|
||||
│
|
||||
└─ Toast
|
||||
|
||||
|
||||
Managers (Singleton)
|
||||
│
|
||||
├─ ViewManager
|
||||
│ └─ Manages page lifecycle & navigation
|
||||
│
|
||||
├─ LevelDataManager
|
||||
│ └─ Manages API data & asset loading
|
||||
│
|
||||
├─ StorageManager
|
||||
│ └─ Manages user data persistence
|
||||
│
|
||||
├─ ToastManager
|
||||
│ └─ Manages toast notifications
|
||||
│
|
||||
└─ WxSDK
|
||||
└─ Manages WeChat integration
|
||||
|
||||
|
||||
Utilities
|
||||
│
|
||||
├─ HttpUtil
|
||||
│ └─ Static HTTP methods (GET/POST)
|
||||
│
|
||||
└─ LevelTypes
|
||||
└─ TypeScript interfaces for API data
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Dependency Graph
|
||||
|
||||
```
|
||||
main.ts
|
||||
├─ ViewManager
|
||||
├─ ToastManager
|
||||
└─ [Page Prefabs]
|
||||
├─ PageHome
|
||||
│ └─ WxSDK
|
||||
│
|
||||
├─ PageLevel
|
||||
│ ├─ StorageManager
|
||||
│ ├─ WxSDK
|
||||
│ ├─ LevelDataManager
|
||||
│ ├─ ToastManager
|
||||
│ └─ PassModal
|
||||
│ ├─ WxSDK
|
||||
│ └─ BaseView
|
||||
│
|
||||
└─ PageLoading
|
||||
├─ ViewManager
|
||||
├─ LevelDataManager
|
||||
│ ├─ HttpUtil
|
||||
│ │ └─ XMLHttpRequest
|
||||
│ ├─ LevelTypes
|
||||
│ └─ Cocos assetManager
|
||||
└─ ToastManager
|
||||
|
||||
|
||||
EXTERNAL APIS
|
||||
│
|
||||
├─ https://ilookai.cn/api/v1/wechat-game/levels
|
||||
│ └─ Called by LevelDataManager._fetchApiData()
|
||||
│
|
||||
└─ WeChat SDK (wx global)
|
||||
├─ wx.shareAppMessage()
|
||||
├─ wx.onShareAppMessage()
|
||||
├─ wx.showShareMenu()
|
||||
├─ wx.vibrateShort()
|
||||
└─ wx.vibrateLong()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
### Memory Management
|
||||
- **Image Loading**: Remote images loaded on-demand with local caching
|
||||
- **Level Configs**: Loaded incrementally, current + next level only
|
||||
- **View Caching**: Pages cached after first load, reused on navigation back
|
||||
- **Message Queuing**: No event queue; direct method calls
|
||||
|
||||
### Network
|
||||
- **Single API Call**: Startup only (all levels fetched at once)
|
||||
- **Timeout**: 8 seconds for API requests
|
||||
- **Retry Logic**: 2 attempts with 1-second delay
|
||||
- **Cache Strategy**: In-memory cache, no expiration
|
||||
|
||||
### Storage
|
||||
- **localStorage**: 2 keys (lives + progress)
|
||||
- **No Batching**: Each update writes immediately
|
||||
- **Synchronous**: No async operations needed
|
||||
|
||||
### UI/Graphics
|
||||
- **Single Image**: Main puzzle image per level
|
||||
- **Dynamic Input**: Input field size adjusts based on answer length
|
||||
- **Audio**: One-shot playback, no loops
|
||||
- **Animations**: Toast fade-out only
|
||||
|
||||
---
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Current Implementation
|
||||
- ⚠️ All data stored locally (no encryption)
|
||||
- ⚠️ No user authentication
|
||||
- ⚠️ No server-side validation
|
||||
- ⚠️ Progress can be manually edited via localStorage
|
||||
|
||||
### Vulnerabilities
|
||||
1. localStorage can be inspected/modified via browser console
|
||||
2. No server-side checks on progress/lives
|
||||
3. Can modify localStorage to skip levels
|
||||
4. Can modify lives directly
|
||||
|
||||
### Improvements Needed
|
||||
- Server-side progress validation
|
||||
- User authentication
|
||||
- Encrypted storage
|
||||
- Server-side truth for lives/progress
|
||||
|
||||
288
DOCS_INDEX.md
@@ -1,288 +0,0 @@
|
||||
# Documentation Index
|
||||
|
||||
This directory contains comprehensive analysis of the mp-xieyingeng (写英语) Cocos Creator game point/score system.
|
||||
|
||||
## 📚 Documentation Files
|
||||
|
||||
### 1. **GAME_ANALYSIS.md** (19 KB) - COMPREHENSIVE ANALYSIS
|
||||
The complete technical analysis of the game's point and score system. **Start here for complete understanding.**
|
||||
|
||||
**Contents:**
|
||||
- PART 1: Points/Asset System (Lives currency)
|
||||
- PART 2: Level Completion & Rewards (+1 life per pass)
|
||||
- PART 3: Hint/Clue System (Costs 1 life each)
|
||||
- PART 4: Game Play Logic (Answer validation, timing)
|
||||
- PART 5: Loading Page Logic (Initialization flow)
|
||||
- PART 6: API & Network Requests (Backend endpoint)
|
||||
- PART 7: User Data Storage (localStorage schema)
|
||||
- PART 8: WeChat Mini-Game SDK Usage
|
||||
- PART 9: Complete Game Flow Diagram
|
||||
- PART 10: Complete Resource Flow
|
||||
- PART 11: Key Observations & Implementation Gaps
|
||||
- PART 12: Network Architecture
|
||||
- Summary table of all features
|
||||
|
||||
### 2. **QUICK_REFERENCE.md** (8.6 KB) - QUICK LOOKUP GUIDE
|
||||
Fast reference guide for developers. Use this for quick lookups.
|
||||
|
||||
**Contents:**
|
||||
- Core Currency Overview (Lives system)
|
||||
- Flow Charts (Life economics, answer submission, app startup)
|
||||
- Data Structures (localStorage, API response)
|
||||
- Key Functions & Methods
|
||||
- Missing Features & Implementation Gaps
|
||||
- Network Calls Summary
|
||||
- Game Loop Per Level
|
||||
- Data Integrity Notes
|
||||
- WeChat Features Status
|
||||
- Extension Ideas (Easy/Medium/Complex)
|
||||
|
||||
### 3. **ARCHITECTURE.md** (26 KB) - SYSTEM DESIGN
|
||||
Detailed architecture diagrams and system design documentation.
|
||||
|
||||
**Contents:**
|
||||
- System Architecture Diagram (layered view)
|
||||
- Data Flow Diagram (complete user session)
|
||||
- State Management Flow
|
||||
- View Stack & Navigation
|
||||
- Class Hierarchy
|
||||
- Dependency Graph
|
||||
- Performance Considerations
|
||||
- Security Considerations & Vulnerabilities
|
||||
|
||||
## 🎯 Quick Navigation
|
||||
|
||||
### "I want to understand..."
|
||||
|
||||
**...how the point system works**
|
||||
→ Read: QUICK_REFERENCE.md "THE COMPLETE PICTURE"
|
||||
|
||||
**...the complete game flow**
|
||||
→ Read: GAME_ANALYSIS.md "PART 9: Complete Game Flow Diagram"
|
||||
|
||||
**...how lives are stored and managed**
|
||||
→ Read: GAME_ANALYSIS.md "PART 1: Points/Asset System" + PART 7: User Data Storage"
|
||||
|
||||
**...what happens when a user completes a level**
|
||||
→ Read: GAME_ANALYSIS.md "PART 2: Level Completion & Rewards"
|
||||
|
||||
**...how hints work**
|
||||
→ Read: GAME_ANALYSIS.md "PART 3: Hint/Clue System"
|
||||
|
||||
**...the API integration**
|
||||
→ Read: GAME_ANALYSIS.md "PART 6: API & Network Requests"
|
||||
|
||||
**...the code organization**
|
||||
→ Read: ARCHITECTURE.md "System Architecture Diagram" + "Dependency Graph"
|
||||
|
||||
**...what's missing in the implementation**
|
||||
→ Read: GAME_ANALYSIS.md "PART 11: Key Observations & Gaps"
|
||||
|
||||
**...where to add new features**
|
||||
→ Read: QUICK_REFERENCE.md "EXTENSION IDEAS"
|
||||
|
||||
---
|
||||
|
||||
## 🔑 Key Findings Summary
|
||||
|
||||
### THE POINT SYSTEM
|
||||
- **Currency**: Lives (生命值)
|
||||
- **Default**: 10 lives
|
||||
- **Earn**: +1 per level pass
|
||||
- **Spend**: -1 per hint unlock (Hint 2 or 3 only)
|
||||
- **Storage**: localStorage under key `game_lives`
|
||||
- **No other currency**: No points, coins, or score counter
|
||||
|
||||
### LEVEL COMPLETION
|
||||
- **Reward**: +1 life (only reward)
|
||||
- **No time bonus**: Same reward regardless of speed
|
||||
- **No partial credit**: Exact case-sensitive match required
|
||||
- **Unlimited retries**: Can retry wrong answers indefinitely
|
||||
- **Timeout incomplete**: 60s countdown exists but doesn't prevent submission
|
||||
|
||||
### HINT SYSTEM
|
||||
- **Hint 1**: Free (always shown)
|
||||
- **Hint 2**: Costs 1 life (unlock button)
|
||||
- **Hint 3**: Costs 1 life (unlock button)
|
||||
- **Max loss per level**: 2 lives (both hints)
|
||||
- **Net per level**: -1 to +1 depending on hints used
|
||||
|
||||
### DATA STORAGE
|
||||
- **Lives**: localStorage["game_lives"]
|
||||
- **Progress**: localStorage["game_progress"] with currentLevelIndex & maxUnlockedLevelIndex
|
||||
- **All local**: No server-side sync
|
||||
- **No encryption**: Direct access via console
|
||||
- **Immediate writes**: Each update written to storage immediately
|
||||
|
||||
### API INTEGRATION
|
||||
- **Single endpoint**: GET https://ilookai.cn/api/v1/wechat-game/levels
|
||||
- **Startup only**: Called once during initialization
|
||||
- **Retry**: 2 attempts with 1s delay
|
||||
- **Timeout**: 8 seconds
|
||||
- **No other backend calls**: No score submission, no analytics, no leaderboard
|
||||
|
||||
### WECHAT FEATURES
|
||||
- ✅ Sharing (with level parameter)
|
||||
- ✅ Haptic feedback (vibration on errors)
|
||||
- ❌ No authentication
|
||||
- ❌ No cloud save
|
||||
- ❌ No leaderboard
|
||||
|
||||
---
|
||||
|
||||
## 📁 Source Files Referenced
|
||||
|
||||
All 16 TypeScript files in the project are analyzed:
|
||||
|
||||
**Core Pages:**
|
||||
- `PageLoading.ts` - Loading screen & initialization
|
||||
- `PageHome.ts` - Home menu page
|
||||
- `PageLevel.ts` - Main game level (where all game logic happens)
|
||||
- `PassModal.ts` - Level completion modal
|
||||
|
||||
**Management Systems:**
|
||||
- `ViewManager.ts` - Page navigation & lifecycle
|
||||
- `StorageManager.ts` - Lives & progress persistence
|
||||
- `LevelDataManager.ts` - API integration & asset loading
|
||||
|
||||
**Utilities:**
|
||||
- `BaseView.ts` - Base class for pages
|
||||
- `HttpUtil.ts` - HTTP request wrapper
|
||||
- `WxSDK.ts` - WeChat SDK integration
|
||||
- `ToastManager.ts` - Toast notifications
|
||||
- `Toast.ts` - Toast component
|
||||
- `LevelTypes.ts` - TypeScript interfaces
|
||||
- `RoundedRectMask.ts` - UI utility
|
||||
- `BackgroundScaler.ts` - UI utility
|
||||
- `main.ts` - App entry point
|
||||
|
||||
---
|
||||
|
||||
## 🔬 Analysis Methodology
|
||||
|
||||
This documentation was created by:
|
||||
1. Finding all 16 TypeScript files in the assets/ directory
|
||||
2. Reading and analyzing each file for:
|
||||
- Score/points logic
|
||||
- Currency/asset management
|
||||
- Level completion mechanics
|
||||
- Hint/cost systems
|
||||
- API calls
|
||||
- Storage mechanisms
|
||||
- WeChat SDK usage
|
||||
- Data flows
|
||||
3. Mapping dependencies between files
|
||||
4. Creating flowcharts and diagrams
|
||||
5. Documenting observations and gaps
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Using This Documentation
|
||||
|
||||
### For Understanding the System
|
||||
1. Start with QUICK_REFERENCE.md for overview
|
||||
2. Read GAME_ANALYSIS.md for detailed understanding
|
||||
3. Refer to ARCHITECTURE.md for system design
|
||||
|
||||
### For Making Changes
|
||||
1. Check ARCHITECTURE.md "Dependency Graph"
|
||||
2. Review relevant code sections in GAME_ANALYSIS.md
|
||||
3. Use QUICK_REFERENCE.md to find specific methods
|
||||
|
||||
### For Adding Features
|
||||
1. Review QUICK_REFERENCE.md "EXTENSION IDEAS"
|
||||
2. Check ARCHITECTURE.md "Security Considerations"
|
||||
3. Plan changes against current dependencies
|
||||
|
||||
### For Debugging
|
||||
1. Review GAME_ANALYSIS.md "PART 9: Game Flow Diagram"
|
||||
2. Check ARCHITECTURE.md "Data Flow Diagram"
|
||||
3. Trace through StorageManager and LevelDataManager
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Important Notes
|
||||
|
||||
### Security Issues
|
||||
- ⚠️ All data stored locally without encryption
|
||||
- ⚠️ No server-side validation of progress
|
||||
- ⚠️ Users can modify localStorage directly
|
||||
- ⚠️ Can skip levels by editing progress
|
||||
|
||||
### Implementation Gaps
|
||||
- ❌ No points/coins display
|
||||
- ❌ No time-based bonuses
|
||||
- ❌ Timeout doesn't prevent submission
|
||||
- ❌ No server-side progress sync
|
||||
- ❌ No analytics tracking
|
||||
|
||||
### To Improve
|
||||
- Add server-side progress validation
|
||||
- Implement user authentication
|
||||
- Add score API endpoint
|
||||
- Track time-to-completion
|
||||
- Consider leaderboard system
|
||||
|
||||
---
|
||||
|
||||
## 📝 Document Versions
|
||||
|
||||
- Created: April 5, 2026
|
||||
- Cocos Creator Version: 3.8.8
|
||||
- Project: mp-xieyingeng (写英语)
|
||||
- Platform: WeChat Mini-Game
|
||||
- Analysis Coverage: 100% of TypeScript codebase (16 files)
|
||||
|
||||
---
|
||||
|
||||
## 📞 Questions Answered
|
||||
|
||||
This documentation answers:
|
||||
- ✅ What is the points/score system?
|
||||
- ✅ How do users earn points?
|
||||
- ✅ How are points spent?
|
||||
- ✅ What happens on level completion?
|
||||
- ✅ How do hints work and cost lives?
|
||||
- ✅ Where is data stored?
|
||||
- ✅ What API calls are made?
|
||||
- ✅ How does WeChat integration work?
|
||||
- ✅ What's the complete game flow?
|
||||
- ✅ What features are missing?
|
||||
- ✅ What's the system architecture?
|
||||
- ✅ Where are the security issues?
|
||||
|
||||
---
|
||||
|
||||
## 🔗 Cross-References
|
||||
|
||||
| Topic | Main Document | Quick Ref | Architecture |
|
||||
|-------|---------------|-----------|--------------|
|
||||
| Lives System | PART 1 | Overview | State Mgmt |
|
||||
| Level Rewards | PART 2 | Economics | Data Flow |
|
||||
| Hints & Costs | PART 3 | Game Loop | Dependencies |
|
||||
| API | PART 6 | Network | External |
|
||||
| Storage | PART 7 | Data Structures | State Mgmt |
|
||||
| WeChat | PART 8 | Features | Dependencies |
|
||||
| Game Flow | PART 9 | Game Loop | Data Flow |
|
||||
| Features | PART 11 | Missing | Performance |
|
||||
|
||||
---
|
||||
|
||||
## 📊 Statistics
|
||||
|
||||
- **TypeScript Files Analyzed**: 16
|
||||
- **Lines of Code Reviewed**: ~1,800
|
||||
- **API Endpoints**: 1
|
||||
- **Storage Keys**: 2
|
||||
- **External SDKs**: 1 (WeChat)
|
||||
- **Currency Types**: 1 (Lives)
|
||||
- **Hint Levels**: 3
|
||||
- **Levels Supported**: 100+ (from API)
|
||||
- **Player Lives**: 10 (default)
|
||||
- **Time Per Level**: 60 seconds
|
||||
- **Max Hint Cost Per Level**: 2 lives
|
||||
- **Max Life Gain Per Level**: 1 life
|
||||
|
||||
---
|
||||
|
||||
**For questions or clarifications, refer to the specific document sections listed above.**
|
||||
504
GAME_ANALYSIS.md
@@ -1,504 +0,0 @@
|
||||
# Cocos Creator Game - Complete Point/Score System Analysis
|
||||
|
||||
## Project Overview
|
||||
This is a WeChat Mini-Game built with Cocos Creator. It's a word puzzle game where players guess answers to images within a 60-second time limit. The game uses a "lives" system instead of traditional points/coins.
|
||||
|
||||
---
|
||||
|
||||
## 1. LIVES/RESOURCE SYSTEM (The Currency/Points Equivalent)
|
||||
|
||||
### Storage & Persistence
|
||||
**File:** `StorageManager.ts`
|
||||
- **Storage Key:** `game_lives`
|
||||
- **Default Lives:** 10 (for new users)
|
||||
- **Minimum Lives:** 0
|
||||
- **Storage Method:** `sys.localStorage` (Cocos local storage)
|
||||
|
||||
### Lives Management Methods:
|
||||
```typescript
|
||||
// Core Methods:
|
||||
- getLives(): number // Get current lives (default 10 if not set)
|
||||
- setLives(lives: number) // Set lives value (min 0)
|
||||
- consumeLife(): boolean // Deduct 1 life, returns success status
|
||||
- addLife(): void // Add 1 life
|
||||
- hasLives(): boolean // Check if lives > 0
|
||||
- resetLives(): void // Reset to default 10
|
||||
```
|
||||
|
||||
### Lives Usage:
|
||||
1. **Unlocking Hints (Clues):** Each unlock costs 1 life
|
||||
- Clue 1: Free (always unlocked)
|
||||
- Clue 2: Costs 1 life to unlock
|
||||
- Clue 3: Costs 1 life to unlock
|
||||
|
||||
---
|
||||
|
||||
## 2. LEVEL PROGRESSION SYSTEM
|
||||
|
||||
**File:** `StorageManager.ts` (User Progress section)
|
||||
|
||||
### Progress Data Structure:
|
||||
```typescript
|
||||
interface UserProgress {
|
||||
currentLevelIndex: number; // Current level (0-based)
|
||||
maxUnlockedLevelIndex: number; // Highest level player reached
|
||||
}
|
||||
```
|
||||
|
||||
### Progress Storage:
|
||||
- **Storage Key:** `game_progress`
|
||||
- **Default:** `{ currentLevelIndex: 0, maxUnlockedLevelIndex: 0 }`
|
||||
- **Caching:** Cached in memory (`_progressCache`) to avoid repeated localStorage reads
|
||||
|
||||
### Progression Methods:
|
||||
```typescript
|
||||
- getCurrentLevelIndex(): number // Get current level
|
||||
- setCurrentLevelIndex(index): void // Set current level
|
||||
- getMaxUnlockedLevelIndex(): number // Get highest unlocked level
|
||||
- isLevelUnlocked(levelIndex): boolean // Check if level is playable
|
||||
- onLevelCompleted(completedLevelIndex) // Called when level is beaten
|
||||
- resetProgress(): void // Reset to level 1
|
||||
```
|
||||
|
||||
### Level Unlock Logic:
|
||||
- **Level 1** is always unlocked
|
||||
- When player completes level N:
|
||||
- Current level → N+1
|
||||
- Max unlocked → max(maxUnlocked, N)
|
||||
- This allows replaying lower levels while progressing forward
|
||||
|
||||
---
|
||||
|
||||
## 3. GAME LEVEL DATA & API
|
||||
|
||||
**File:** `LevelDataManager.ts`
|
||||
|
||||
### API Configuration:
|
||||
```typescript
|
||||
API_URL = 'https://ilookai.cn/api/v1/wechat-game/levels'
|
||||
REQUEST_TIMEOUT = 8000ms
|
||||
API_RETRY_COUNT = 2 (retries on failure)
|
||||
```
|
||||
|
||||
### Level Data Structure (from API):
|
||||
```typescript
|
||||
interface ApiLevelData {
|
||||
id: string; // UUID
|
||||
level: number; // Level number
|
||||
imageUrl: string; // Main image URL
|
||||
hint1: string; // Free clue
|
||||
hint2: string; // Paid clue (costs 1 life)
|
||||
hint3: string; // Paid clue (costs 1 life)
|
||||
answer: string; // The correct answer
|
||||
sortOrder: number; // Sorting order
|
||||
}
|
||||
```
|
||||
|
||||
### API Response:
|
||||
```typescript
|
||||
interface ApiResponse {
|
||||
success: boolean;
|
||||
message: string | null;
|
||||
data: {
|
||||
levels: ApiLevelData[];
|
||||
total: number;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Image Loading:
|
||||
- Remote images loaded via `assetManager.loadRemote()`
|
||||
- Cached in memory (`_imageCache: Map<URL, SpriteFrame>`)
|
||||
- First level image preloaded during app initialization
|
||||
- Next level image preloaded silently after entering current level
|
||||
|
||||
### Loading Strategy:
|
||||
1. **On App Start:** Load all level metadata + first level image (80% of loading bar)
|
||||
2. **On Level Enter:** Load current level image if needed
|
||||
3. **After Level Completion:** Preload next level asynchronously (doesn't block gameplay)
|
||||
|
||||
---
|
||||
|
||||
## 4. GAMEPLAY LOOP
|
||||
|
||||
**File:** `PageLevel.ts`
|
||||
|
||||
### Game Sequence:
|
||||
1. Player enters level
|
||||
2. Main image displays with hint 1 visible
|
||||
3. 60-second countdown starts
|
||||
4. Player enters answer in single EditBox
|
||||
5. Player can unlock hints 2 & 3 by spending lives
|
||||
6. Player submits answer
|
||||
|
||||
### Time Limit:
|
||||
- **Duration:** 60 seconds per level
|
||||
- **Implementation:** `schedule(this.onCountdownTick, 1)` (1-second interval)
|
||||
- **On Time Up:** Plays fail sound, but doesn't force level exit
|
||||
|
||||
### Input System:
|
||||
- **Type:** Single EditBox (not multi-input per character)
|
||||
- **Width:** Dynamic (based on answer length)
|
||||
- Formula: `Math.min(600, Math.max(200, answerLength * 60 + 40))` pixels
|
||||
- **Max Length:** Match answer length
|
||||
- **Placeholder:** Shows answer length as hint
|
||||
|
||||
### Answer Processing:
|
||||
```typescript
|
||||
getAnswer(): string {
|
||||
const editBox = this._inputNodes[0].getComponent(EditBox);
|
||||
return (editBox?.string ?? '').trim(); // Trimmed
|
||||
}
|
||||
|
||||
// Comparison is case-sensitive:
|
||||
if (userAnswer === this._currentConfig.answer) {
|
||||
// WIN
|
||||
} else {
|
||||
// LOSE
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. WINNING & REWARDS
|
||||
|
||||
**File:** `PageLevel.ts`
|
||||
|
||||
### On Correct Answer:
|
||||
1. **Stop Timer:** Countdown stops
|
||||
2. **Play Sound:** Success audio plays
|
||||
3. **Award 1 Life:** `addLife()` called
|
||||
4. **Show Modal:** PassModal displays with buttons
|
||||
|
||||
### Pass Modal (Victory Screen):
|
||||
- Shows "Next Level" button
|
||||
- Shows "Share with Friends" button
|
||||
- On "Next Level": Progress to next level (calls `onLevelCompleted()`)
|
||||
- On "Share": Triggers WeChat share with query param `?level=<levelIndex>`
|
||||
|
||||
### Progression on Pass:
|
||||
```typescript
|
||||
StorageManager.onLevelCompleted(currentLevelIndex);
|
||||
// Sets:
|
||||
// - currentLevelIndex → currentLevelIndex + 1
|
||||
// - maxUnlockedLevelIndex → max(maxUnlocked, currentLevelIndex)
|
||||
```
|
||||
|
||||
### Game End Condition:
|
||||
- When `currentLevelIndex >= totalLevels`, player has beaten all levels
|
||||
- Game returns to home page
|
||||
|
||||
---
|
||||
|
||||
## 6. LOSING & CONSEQUENCES
|
||||
|
||||
**File:** `PageLevel.ts`
|
||||
|
||||
### On Wrong Answer:
|
||||
1. **Play Sound:** Fail audio plays
|
||||
2. **Vibration:** `WxSDK.vibrateLong()` (400ms vibration on WeChat)
|
||||
3. **Toast Message:** "答案错误,再试试吧!" (Answer wrong, try again!)
|
||||
4. **No Penalty:** No life deducted, level doesn't change
|
||||
|
||||
### On Time Up:
|
||||
1. **Play Sound:** Fail audio plays
|
||||
2. **Countdown Stops:** `_isTimeUp = true`
|
||||
3. **No Forced Exit:** Player can continue typing and submitting
|
||||
4. **No Life Penalty:** Still can retry
|
||||
|
||||
---
|
||||
|
||||
## 7. HINT/CLUE SYSTEM
|
||||
|
||||
**File:** `PageLevel.ts`
|
||||
|
||||
### Clue Mechanics:
|
||||
1. **Clue 1:** Always visible, FREE
|
||||
2. **Clue 2:** Hidden by default, costs 1 life to unlock
|
||||
3. **Clue 3:** Hidden by default, costs 1 life to unlock
|
||||
|
||||
### Unlocking Process:
|
||||
```typescript
|
||||
onUnlockClue(index: number) {
|
||||
// 1. Check if lives available
|
||||
if (!this.hasLives()) return;
|
||||
|
||||
// 2. Consume 1 life
|
||||
if (!this.consumeLife()) return;
|
||||
|
||||
// 3. Play click sound
|
||||
this.playClickSound();
|
||||
|
||||
// 4. Hide unlock button
|
||||
this.hideUnlockButton(index);
|
||||
|
||||
// 5. Show clue content
|
||||
this.showClue(index);
|
||||
this.setClue(index, clueContent);
|
||||
}
|
||||
```
|
||||
|
||||
### Clue Cost Implications:
|
||||
- Player starts with 10 lives
|
||||
- Can unlock both clues 2 & 3 = 2 lives spent minimum
|
||||
- But only 8 lives remain per level if both used
|
||||
- Player can conserve lives by solving without clues
|
||||
|
||||
---
|
||||
|
||||
## 8. NETWORK & API COMMUNICATION
|
||||
|
||||
**File:** `HttpUtil.ts`
|
||||
|
||||
### HTTP Methods:
|
||||
```typescript
|
||||
// GET request
|
||||
HttpUtil.get<T>(url: string, timeout: number = 10000): Promise<T>
|
||||
|
||||
// POST request
|
||||
HttpUtil.post<T>(url: string, data: object, timeout: number = 10000): Promise<T>
|
||||
```
|
||||
|
||||
### Implementation:
|
||||
- Uses `XMLHttpRequest`
|
||||
- Supports JSON responses
|
||||
- Default timeout: 10 seconds
|
||||
- Error handling: Rejects on HTTP errors, timeouts, or network failures
|
||||
|
||||
### Used By:
|
||||
- `LevelDataManager` uses `HttpUtil.get()` to fetch level data from API
|
||||
- No POST requests currently used
|
||||
|
||||
---
|
||||
|
||||
## 9. WECHAT SDK INTEGRATION
|
||||
|
||||
**File:** `WxSDK.ts`
|
||||
|
||||
### WeChat Features Used:
|
||||
|
||||
#### 1. Platform Detection:
|
||||
```typescript
|
||||
isWechat(): boolean
|
||||
// Returns: sys.platform === sys.Platform.WECHAT_GAME
|
||||
```
|
||||
|
||||
#### 2. Sharing:
|
||||
- **Share Menu:** `showShareMenu()` - Enables share button in header
|
||||
- **Friend Share:** `onShareAppMessage(config)` - Right-click "Share" message
|
||||
- **Timeline Share:** `onShareTimeline(config)` - Moments sharing
|
||||
- **Active Share:** `shareAppMessage(config)` - Trigger share dialog
|
||||
|
||||
#### 3. Vibration:
|
||||
- **Short Vibrate:** `vibrateShort()` - 15ms, for button clicks
|
||||
- **Long Vibrate:** `vibrateLong()` - 400ms, for errors
|
||||
|
||||
#### 4. Share Configuration:
|
||||
```typescript
|
||||
interface WxShareConfig {
|
||||
title: string; // Share title: "写英语"
|
||||
imageUrl?: string; // Share image (optional)
|
||||
query?: string; // Query params (e.g., "level=5")
|
||||
}
|
||||
```
|
||||
|
||||
#### 5. Initialization:
|
||||
```typescript
|
||||
WxSDK.initShare(config) {
|
||||
// Calls in sequence:
|
||||
// 1. showShareMenu()
|
||||
// 2. onShareAppMessage(config)
|
||||
// 3. onShareTimeline(config)
|
||||
}
|
||||
```
|
||||
|
||||
**Called in:** PageHome on game start
|
||||
|
||||
---
|
||||
|
||||
## 10. GAME STATE MANAGEMENT
|
||||
|
||||
**File:** `ViewManager.ts` (Page Stack) + `StorageManager.ts` (Data)
|
||||
|
||||
### View Stack (Navigation):
|
||||
- Maintains page stack for navigation
|
||||
- `PageHome` (z-index 0) - Main menu
|
||||
- `PageLevel` (z-index 1) - Game level
|
||||
- `PassModal` (z-index 999) - Victory overlay
|
||||
|
||||
### Persistent State:
|
||||
- Lives stored in localStorage with key `game_lives`
|
||||
- Progress stored in localStorage with key `game_progress`
|
||||
- Both persist across app sessions
|
||||
- Data survives app closure and reopening
|
||||
|
||||
### Runtime State:
|
||||
- Current countdown timer
|
||||
- Current input box content
|
||||
- Unlocked clues state (reset each level)
|
||||
- Current level config (API data)
|
||||
|
||||
---
|
||||
|
||||
## 11. LOADING PAGE FLOW
|
||||
|
||||
**File:** `PageLoading.ts`
|
||||
|
||||
### Initialization Sequence:
|
||||
1. **Stage 1 (0-30%):** Fetch all levels from API via `LevelDataManager.initialize()`
|
||||
- API call with retry logic
|
||||
- Parse level metadata
|
||||
- NOT loading all images yet
|
||||
|
||||
2. **Stage 2 (30-80%):** Preload first level image
|
||||
- Uses `LevelDataManager.ensureLevelReady(0)`
|
||||
- Shows "正在加载游戏必备资源..." message
|
||||
|
||||
3. **Stage 3 (80-100%):** Preload PageHome view
|
||||
- `ViewManager.preload('PageHome')`
|
||||
- Shows "正在加载界面资源..." message
|
||||
|
||||
4. **Completion (100%):** Open PageHome and destroy loading page
|
||||
|
||||
---
|
||||
|
||||
## 12. COMPLETE POINTS FLOW DIAGRAM
|
||||
|
||||
```
|
||||
START GAME
|
||||
↓
|
||||
[10 Lives] (default)
|
||||
↓
|
||||
LEVEL 1 STARTS
|
||||
├─ View Clue 1 (FREE)
|
||||
├─ Option: Unlock Clue 2 (-1 Life) → [9 Lives]
|
||||
├─ Option: Unlock Clue 3 (-1 Life) → [8 Lives]
|
||||
├─ Player submits answer
|
||||
│
|
||||
├─ IF CORRECT:
|
||||
│ ├─ Add 1 Life → [9 or 10+ Lives]
|
||||
│ ├─ Show PassModal
|
||||
│ └─ Move to LEVEL 2
|
||||
│
|
||||
└─ IF WRONG:
|
||||
├─ Play fail sound & vibrate
|
||||
├─ Show toast message
|
||||
├─ Lives unchanged
|
||||
└─ Can retry (no level exit)
|
||||
|
||||
IF ALL LEVELS COMPLETE:
|
||||
└─ Return to home
|
||||
|
||||
IF TIME UP:
|
||||
├─ Play fail sound
|
||||
├─ Can still submit (lives unchanged)
|
||||
└─ No forced exit
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 13. KEY FILES SUMMARY
|
||||
|
||||
| File | Purpose | Key Components |
|
||||
|------|---------|-----------------|
|
||||
| StorageManager.ts | Data persistence | Lives + Progress storage |
|
||||
| LevelDataManager.ts | Level data loading | API calls + Image caching |
|
||||
| PageLevel.ts | Main game logic | Countdown, input, hints, validation |
|
||||
| PageLoading.ts | App initialization | Loading bar + progress |
|
||||
| PageHome.ts | Home screen | Start game button |
|
||||
| PassModal.ts | Victory screen | Next/Share buttons |
|
||||
| ViewManager.ts | Page navigation | View stack + caching |
|
||||
| WxSDK.ts | WeChat API | Share + vibration |
|
||||
| HttpUtil.ts | Network requests | GET/POST + error handling |
|
||||
| ToastManager.ts | Notifications | Brief toast messages |
|
||||
|
||||
---
|
||||
|
||||
## 14. IMPORTANT CONSTANTS
|
||||
|
||||
### Game Constants:
|
||||
- **Level Time Limit:** 60 seconds
|
||||
- **Default Lives:** 10
|
||||
- **Life Cost per Hint:** 1 life per hint (hints 2 & 3)
|
||||
- **Reward per Level:** +1 life
|
||||
|
||||
### API Constants:
|
||||
- **Endpoint:** `https://ilookai.cn/api/v1/wechat-game/levels`
|
||||
- **Timeout:** 8000ms
|
||||
- **Retry Count:** 2
|
||||
|
||||
### UI Constants:
|
||||
- **PageHome z-index:** 0
|
||||
- **PageLevel z-index:** 1
|
||||
- **PassModal z-index:** 999
|
||||
|
||||
---
|
||||
|
||||
## 15. MISSING FEATURES (Observations)
|
||||
|
||||
1. **No User Authentication:** No wx.login call visible
|
||||
2. **No Backend Sync:** No calls to save progress to server
|
||||
3. **No Ads/IAP:** No monetization system
|
||||
4. **No Leaderboards:** No score submission to WeChat
|
||||
5. **No Analytics:** No tracking events beyond console logs
|
||||
6. **No Life Refill:** No premium way to get more lives
|
||||
7. **No Difficulty Levels:** All players see same levels
|
||||
8. **No Sound Toggle:** Sound plays automatically
|
||||
|
||||
---
|
||||
|
||||
## 16. DATA FLOW SUMMARY
|
||||
|
||||
```
|
||||
WeChat API: https://ilookai.cn/api/v1/wechat-game/levels
|
||||
↓
|
||||
LevelDataManager (fetch + cache)
|
||||
↓
|
||||
PageLevel (display + gameplay)
|
||||
├─ InputBox (player answer)
|
||||
├─ Clues (cost lives to unlock)
|
||||
└─ Timer (60 second countdown)
|
||||
↓
|
||||
StorageManager (save lives + progress)
|
||||
↓
|
||||
localStorage
|
||||
├─ game_lives: number
|
||||
└─ game_progress: UserProgress (JSON)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 17. CRITICAL BUSINESS LOGIC
|
||||
|
||||
### Win Condition:
|
||||
```
|
||||
userAnswer (trimmed) === correctAnswer (from API)
|
||||
→ Award +1 life
|
||||
→ Save progress
|
||||
→ Move to next level
|
||||
```
|
||||
|
||||
### Lose Condition:
|
||||
```
|
||||
userAnswer !== correctAnswer
|
||||
→ No penalty
|
||||
→ Can retry immediately
|
||||
→ Timer continues (even after time up)
|
||||
```
|
||||
|
||||
### Progression:
|
||||
```
|
||||
Beat Level N
|
||||
→ currentLevel = N + 1
|
||||
→ maxUnlocked = max(maxUnlocked, N)
|
||||
→ Reward: +1 life (so levels can chain profitably)
|
||||
```
|
||||
|
||||
### Economy Balance:
|
||||
- Start: 10 lives
|
||||
- Per level: Can spend 0-2 lives (hints) or 0 lives (no hints)
|
||||
- Per level: Earn +1 life (net: -1 or +1 lives)
|
||||
- Average player with no hints: +1 life/level → infinite scaling
|
||||
- Average player with 1 hint: 0 lives/level → stable
|
||||
- Hardcore with 2 hints: -1 life/level → finite runway
|
||||
|
||||
@@ -1,494 +0,0 @@
|
||||
# Complete Points/Lives Flow Diagram
|
||||
|
||||
## 🔴 INITIALIZATION PHASE
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ GAME STARTS │
|
||||
│ PageLoading initializes │
|
||||
└──────────────┬──────────────────────┘
|
||||
│
|
||||
↓
|
||||
┌──────────────────────────┐
|
||||
│ Check localStorage │
|
||||
│ "game_lives" key │
|
||||
└────┬──────────────┬──────┘
|
||||
│ │
|
||||
NOT SET EXISTS
|
||||
(New User) (Returning)
|
||||
│ │
|
||||
↓ ↓
|
||||
┌────────────┐ ┌────────────┐
|
||||
│ 10 Lives │ │Parse Value │
|
||||
│(DEFAULT) │ │from Storage│
|
||||
└────┬───────┘ └────┬───────┘
|
||||
│ │
|
||||
└────────┬───────┘
|
||||
↓
|
||||
┌────────────────────────┐
|
||||
│ LOAD LEVEL DATA │
|
||||
│ (API fetch) │
|
||||
│ https://ilookai.cn ... │
|
||||
└────────┬───────────────┘
|
||||
│
|
||||
↓
|
||||
┌────────────────────────┐
|
||||
│ Display PageHome │
|
||||
│ (with shared config) │
|
||||
└────────┬───────────────┘
|
||||
│
|
||||
↓
|
||||
┌────────────────────────┐
|
||||
│ READY TO PLAY │
|
||||
│ Lives: X | Level: Y │
|
||||
└────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎮 GAMEPLAY PHASE (Single Level)
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────┐
|
||||
│ LEVEL STARTS │
|
||||
│ Load level data from cache │
|
||||
│ Display image + Clue 1 (FREE) │
|
||||
│ Start 60-second countdown │
|
||||
└──────────────┬─────────────────────────┘
|
||||
│
|
||||
├─────────────────────────────────────┐
|
||||
│ │
|
||||
↓ ↓
|
||||
┌──────────────────┐ ┌──────────────────────┐
|
||||
│ UNLOCK CLUE 2 │ │ SUBMIT ANSWER │
|
||||
│ onUnlockClue(2) │ │ onSubmitAnswer() │
|
||||
│ │ │ │
|
||||
│ Check: hasLives()├──NO──→ │ Compare: │
|
||||
│ │ │ │ input === │
|
||||
│ YES │ │ correctAnswer │
|
||||
│ │ │ │ │
|
||||
│ ↓ │ │ ├──YES──→┐ │
|
||||
│ consumeLife() │ │ │ │ │
|
||||
│ Lives: -1 │ │ │ │ │
|
||||
│ │ │ │ │ │ │
|
||||
│ ↓ │ │ │ ┌┴─────────┐
|
||||
│ Display Clue 2 │ │ │ │ │
|
||||
│ Refresh Label │ │ │ ↓ │
|
||||
│ │ │ │ │ SUCCESS! │
|
||||
│ ↓ │ │ │ showSuccess() │
|
||||
│ Continue Game │ │ │ │ │
|
||||
└──────────────────┘ │ │ ↓ │
|
||||
│ │ │ Play success │
|
||||
│ │ │ sound │
|
||||
↓ │ │ Stop timer │
|
||||
┌──────────────────┐ │ │ addLife() │
|
||||
│ UNLOCK CLUE 3 │ │ │ Lives: +1 │
|
||||
│ onUnlockClue(3) │ │ │ │ │
|
||||
│ │ │ │ ↓ │
|
||||
│ Check: hasLives()├──NO──→ │ │ Show PassModal │
|
||||
│ │ │ │ │ │ │
|
||||
│ YES │ │ │ ├─►[NEXT] │
|
||||
│ │ │ │ │ │ │
|
||||
│ ↓ │ │ │ └─►[SHARE] │
|
||||
│ consumeLife() │ │ │ │
|
||||
│ Lives: -1 │ │ └──NO──────────────┘
|
||||
│ │ │ │ │
|
||||
│ ↓ │ │ ↓
|
||||
│ Display Clue 3 │ │ ERROR!
|
||||
│ Refresh Label │ │ showError()
|
||||
│ │ │ │ │
|
||||
│ ↓ │ │ ↓
|
||||
│ Continue Game │ │ Play fail sound
|
||||
│ │ │ Vibrate long
|
||||
│ │ │ Show toast:
|
||||
│ │ │ "答案错误,
|
||||
└──────────────────┘ │ 再试试吧!"
|
||||
│ │ Lives unchanged
|
||||
│ │ │
|
||||
│ │ ↓
|
||||
│ │ Can retry
|
||||
│ │ (same level)
|
||||
│ │
|
||||
└──────────────────────┘
|
||||
│
|
||||
↓
|
||||
┌──────────────────────┐
|
||||
│ TIME UP? │
|
||||
└──────────────────────┘
|
||||
│
|
||||
↓
|
||||
┌─────────────────────┐
|
||||
│ Play fail sound │
|
||||
│ Stop countdown │
|
||||
│ Can still retry │
|
||||
│ Lives unchanged │
|
||||
└─────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 LIVES TRANSACTION FLOW
|
||||
|
||||
```
|
||||
LEVEL START
|
||||
│
|
||||
┌────────┴─────────┐
|
||||
│ │
|
||||
OPTIONAL MANDATORY
|
||||
(Hints) (Level)
|
||||
│ │
|
||||
┌───────┴────────┐ │
|
||||
│ │ │
|
||||
HINT 2 HINT 3 SUBMISSION
|
||||
-1 Life -1 Life │
|
||||
│ │ │
|
||||
└────────┬───────┴─┬───────┤
|
||||
│ │ │
|
||||
(Spent) (Spent) ANSWER
|
||||
│ │ │
|
||||
└────┬────┴───┬───┘
|
||||
│ │
|
||||
0-2 LIVES SPENT
|
||||
(Depends on choices)
|
||||
│ │
|
||||
┌─────────┼────────┼─────────┐
|
||||
│ │ │ │
|
||||
CORRECT WRONG TIMEOUT ...
|
||||
│ │ │
|
||||
│ │ │
|
||||
+1 LIFE 0 LIVES 0 LIVES
|
||||
│ │ │
|
||||
↓ ↓ ↓
|
||||
┌───────────────────────────────┐
|
||||
│ NEW LIVES BALANCE │
|
||||
│ │
|
||||
│ Formula: │
|
||||
│ newLives = │
|
||||
│ oldLives │
|
||||
│ - (hintsUnlocked × 1) │
|
||||
│ + (if levelWon ? 1 : 0) │
|
||||
│ │
|
||||
│ Examples: │
|
||||
│ 10 - 0 + 1 = 11 (no hints) │
|
||||
│ 10 - 1 + 1 = 10 (1 hint) │
|
||||
│ 10 - 2 + 1 = 9 (2 hints) │
|
||||
│ 10 - 0 + 0 = 10 (retry) │
|
||||
└───────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🏆 LEVEL PROGRESSION & PERSISTENCE
|
||||
|
||||
```
|
||||
LEVEL WON
|
||||
│
|
||||
↓
|
||||
┌────────────────────────────┐
|
||||
│ StorageManager. │
|
||||
│ onLevelCompleted() │
|
||||
│ │
|
||||
│ Takes: levelIndex (0-based) │
|
||||
└────────────┬────────────────┘
|
||||
│
|
||||
┌────────┴────────┐
|
||||
│ │
|
||||
↓ ↓
|
||||
┌────────────┐ ┌──────────────────┐
|
||||
│ currentLevel │ maxUnlocked │
|
||||
│ = N + 1 │ = max(N, current)│
|
||||
└────────────┘ └──────────────────┘
|
||||
│ │
|
||||
└────────┬────────┘
|
||||
│
|
||||
↓
|
||||
┌──────────────────────────────┐
|
||||
│ Save to localStorage: │
|
||||
│ "game_progress" = │
|
||||
│ { │
|
||||
│ currentLevelIndex: N+1, │
|
||||
│ maxUnlockedLevelIndex: N │
|
||||
│ } │
|
||||
└────────┬─────────────────────┘
|
||||
│
|
||||
↓
|
||||
┌──────────────────────────────┐
|
||||
│ NEXT GAME SESSION: │
|
||||
│ Restore from localStorage │
|
||||
│ Resume at Level N+1 │
|
||||
│ Can replay Levels 0 to N │
|
||||
└──────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💾 DATA PERSISTENCE
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ localStorage (Browser) │
|
||||
├─────────────────────────────────────────┤
|
||||
│ │
|
||||
│ KEY: "game_lives" │
|
||||
│ VALUE: "10" (string number) │
|
||||
│ Type: String │
|
||||
│ Managed by: StorageManager │
|
||||
│ Accessed by: PageLevel, PassModal │
|
||||
│ │
|
||||
├─────────────────────────────────────────┤
|
||||
│ │
|
||||
│ KEY: "game_progress" │
|
||||
│ VALUE: JSON string │
|
||||
│ { │
|
||||
│ "currentLevelIndex": 2, │
|
||||
│ "maxUnlockedLevelIndex": 4 │
|
||||
│ } │
|
||||
│ Type: String (JSON) │
|
||||
│ Managed by: StorageManager │
|
||||
│ Accessed by: PageLevel, ViewManager │
|
||||
│ │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🌐 API & LEVEL DATA FLOW
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────┐
|
||||
│ Remote API Server │
|
||||
│ https://ilookai.cn/api/v1/... │
|
||||
│ │
|
||||
│ Returns: Array<ApiLevelData> │
|
||||
│ Fields: id, level, imageUrl, hint1-3, │
|
||||
│ answer, sortOrder │
|
||||
└────────────────┬──────────────────────────┘
|
||||
│
|
||||
↓ (1st app load only)
|
||||
HttpUtil.get()
|
||||
│
|
||||
↓
|
||||
┌──────────────────────────┐
|
||||
│ LevelDataManager │
|
||||
│ _fetchApiData() │
|
||||
│ │
|
||||
│ Retry: 2 attempts │
|
||||
│ Timeout: 8000ms │
|
||||
└────────┬─────────────────┘
|
||||
│
|
||||
┌────┴────┐
|
||||
│ │
|
||||
FAIL SUCCESS
|
||||
│ │
|
||||
│ ↓
|
||||
│ ┌──────────────────┐
|
||||
│ │ Cache all level │
|
||||
│ │ metadata in │
|
||||
│ │ memory │
|
||||
│ │ (_apiData) │
|
||||
│ └────┬─────────────┘
|
||||
│ │
|
||||
│ ↓
|
||||
│ ┌──────────────────┐
|
||||
│ │ Preload Level 0 │
|
||||
│ │ image │
|
||||
│ │ (_levelConfigs) │
|
||||
│ └────┬─────────────┘
|
||||
│ │
|
||||
└────┬────┘
|
||||
│
|
||||
↓
|
||||
┌──────────────────────────┐
|
||||
│ PageLevel ready │
|
||||
│ Display first level │
|
||||
└──────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⏰ COUNTDOWN TIMER FLOW
|
||||
|
||||
```
|
||||
┌──────────────────┐
|
||||
│ startCountdown() │
|
||||
│ _countdown = 60 │
|
||||
│ _isTimeUp = false│
|
||||
└────────┬─────────┘
|
||||
│
|
||||
↓
|
||||
┌─────────────────────────┐
|
||||
│ schedule( │
|
||||
│ onCountdownTick, 1 │ ← Every 1 second
|
||||
│ ) │
|
||||
└────────┬────────────────┘
|
||||
│
|
||||
├─────────────────────────────┐
|
||||
│ EVERY SECOND │
|
||||
│ (if _isTimeUp = false) │
|
||||
│ │
|
||||
├─→ _countdown-- │
|
||||
├─→ updateClockLabel() │
|
||||
│ (display "59s", "58s"...) │
|
||||
│ │
|
||||
└────────┬────────────────────┘
|
||||
│
|
||||
├─────────────────────────┐
|
||||
│ │
|
||||
_countdown > 0 _countdown <= 0
|
||||
│ │
|
||||
│ ↓
|
||||
│ _isTimeUp = true
|
||||
│ stopCountdown()
|
||||
│ onTimeUp()
|
||||
│ playFailSound()
|
||||
│ │
|
||||
│ ↓
|
||||
│ Can still submit!
|
||||
│ Lives unchanged
|
||||
│ │
|
||||
└──────────┬──────────────┘
|
||||
│
|
||||
↓
|
||||
┌────────────────┐
|
||||
│ PLAYER ACTION: │
|
||||
│ Submit Answer │
|
||||
└────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📱 WECHAT INTEGRATION POINTS
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────┐
|
||||
│ GAME START (PageHome) │
|
||||
├──────────────────────────────────────┤
|
||||
│ WxSDK.initShare({ │
|
||||
│ title: "写英语", │
|
||||
│ query: "" │
|
||||
│ }) │
|
||||
│ └─► Enable share menu in header │
|
||||
└──────────────────────────────────────┘
|
||||
|
||||
┌──────────────────────────────────────┐
|
||||
│ LEVEL WON (PassModal) │
|
||||
├──────────────────────────────────────┤
|
||||
│ User clicks "Share" │
|
||||
│ WxSDK.shareAppMessage({ │
|
||||
│ title: "快来一起玩...", │
|
||||
│ query: "level=<levelIndex>" │
|
||||
│ }) │
|
||||
│ └─► Opens share dialog │
|
||||
│ with level param │
|
||||
└──────────────────────────────────────┘
|
||||
|
||||
┌──────────────────────────────────────┐
|
||||
│ WRONG ANSWER (PageLevel) │
|
||||
├──────────────────────────────────────┤
|
||||
│ showError() │
|
||||
│ WxSDK.vibrateLong() │
|
||||
│ └─► 400ms vibration feedback │
|
||||
└──────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Complete Player Journey
|
||||
|
||||
```
|
||||
START
|
||||
↓
|
||||
┌─────────────────────┐
|
||||
│ Check localStorage │
|
||||
│ game_lives: 10 │
|
||||
│ game_progress: {0,0}│
|
||||
└──────────┬──────────┘
|
||||
↓
|
||||
┌─────────────────┐
|
||||
│ PageHome │
|
||||
│ Start Button │
|
||||
└──────────┬──────┘
|
||||
↓
|
||||
┌──────────────────────────┐
|
||||
│ PageLevel: LEVEL 1 │
|
||||
│ Lives: 10 │ Answer: __ │
|
||||
│ Clue 1: ✓ (FREE) │
|
||||
│ Clue 2: Unlock? (-1 life)│
|
||||
│ Clue 3: Unlock? (-1 life)│
|
||||
│ ⏱️ 60s countdown... │
|
||||
└──────────┬───────────────┘
|
||||
│
|
||||
┌──────┴──────┬────────────────┐
|
||||
│ │ │
|
||||
UNLOCK SUBMIT TIMEOUT
|
||||
CLUE 2 ANSWER (still ok)
|
||||
-1 Life │
|
||||
│ ├─ CORRECT: +1 Life
|
||||
│ │ ↓
|
||||
│ │ PassModal
|
||||
│ │ ├─ NEXT → Level 2
|
||||
│ │ └─ SHARE → wx.share
|
||||
│ │
|
||||
│ └─ WRONG: Lives stay
|
||||
│ ↓
|
||||
│ Retry same level
|
||||
│
|
||||
└─ UNLOCK
|
||||
CLUE 3
|
||||
-1 Life
|
||||
│
|
||||
↓
|
||||
(Try answer)
|
||||
│
|
||||
├─ CORRECT: +1 Life
|
||||
│ ↓
|
||||
│ PassModal
|
||||
│ └─ NEXT → Level 2
|
||||
│
|
||||
└─ WRONG: Lives stay
|
||||
↓
|
||||
Retry
|
||||
|
||||
PROGRESSION CONTINUES...
|
||||
|
||||
BEAT ALL LEVELS
|
||||
↓
|
||||
Return to PageHome
|
||||
(Save progress in localStorage)
|
||||
↓
|
||||
COMPLETE
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📈 Lives Over Time (Example Scenario)
|
||||
|
||||
```
|
||||
Session 1: No hints used
|
||||
━━━━━━━━━━━━━━━━━━━━━
|
||||
Level 1: 10 Lives → Correct → 11 Lives
|
||||
Level 2: 11 Lives → Correct → 12 Lives
|
||||
Level 3: 12 Lives → Correct → 13 Lives
|
||||
Session 1 ends: 13 Lives saved
|
||||
|
||||
Session 2: Some hints used
|
||||
━━━━━━━━━━━━━━━━━━━━━
|
||||
Start: 13 Lives
|
||||
Level 4: 13 - 1 (hint) + 1 (win) = 13 Lives
|
||||
Level 5: 13 - 2 (hints) + 1 (win) = 12 Lives
|
||||
Level 6: 12 - 0 (no hints) + 1 (win) = 13 Lives
|
||||
Level 7: 13 - 0 (no hints) + 1 (win) = 14 Lives
|
||||
Session 2 ends: 14 Lives saved
|
||||
|
||||
Session 3: Struggling with hints
|
||||
━━━━━━━━━━━━━━━━━━━━━
|
||||
Start: 14 Lives
|
||||
Level 8: 14 - 2 (hints) + 1 (win) = 13 Lives
|
||||
Level 9: 13 - 2 (hints) + 1 (win) = 12 Lives
|
||||
Level 10: 12 - 0 (retry, no hints) + 1 (eventually win) = 13 Lives
|
||||
Session 3 ends: 13 Lives saved
|
||||
|
||||
PATTERN: Lives stay stable or grow over time
|
||||
depending on hint usage
|
||||
```
|
||||
|
||||
@@ -1,336 +0,0 @@
|
||||
# 📚 Game Points/Lives System - Documentation Index
|
||||
|
||||
> Complete analysis of the Cocos Creator WeChat Mini-Game points/score/lives system
|
||||
|
||||
**Generated:** April 5, 2026
|
||||
**Scope:** All 21 TypeScript source files analyzed
|
||||
**Total Documentation:** ~45KB
|
||||
|
||||
---
|
||||
|
||||
## 📄 Documentation Files
|
||||
|
||||
### 1. **SUMMARY.md** (11 KB) ⭐ **START HERE**
|
||||
**Best for:** High-level overview and executive understanding
|
||||
|
||||
**Contains:**
|
||||
- Executive summary of the lives-based economy
|
||||
- 21 files analyzed (categorized)
|
||||
- Complete points/lives flow table
|
||||
- Data persistence details
|
||||
- Level progression mechanics
|
||||
- Hint system mechanics
|
||||
- Gameplay loop description
|
||||
- API integration overview
|
||||
- WeChat integration features
|
||||
- User journey example
|
||||
- Testing scenarios
|
||||
- Business logic highlights
|
||||
|
||||
**Read this if:** You want a quick understanding of how the entire system works
|
||||
|
||||
---
|
||||
|
||||
### 2. **GAME_ANALYSIS.md** (13 KB)
|
||||
**Best for:** Deep technical analysis of every system component
|
||||
|
||||
**Contains 17 Sections:**
|
||||
1. Project Overview
|
||||
2. Lives/Resource System (complete)
|
||||
3. Level Progression System
|
||||
4. Game Level Data & API
|
||||
5. Gameplay Loop
|
||||
6. Winning & Rewards
|
||||
7. Losing & Consequences
|
||||
8. Hint/Clue System
|
||||
9. Network & API Communication
|
||||
10. WeChat SDK Integration
|
||||
11. Game State Management
|
||||
12. Loading Page Logic
|
||||
13. Complete Points Flow Diagram
|
||||
14. Key Files Summary (table)
|
||||
15. Important Constants
|
||||
16. Missing Features
|
||||
17. Data Flow Summary & Business Logic
|
||||
|
||||
**Read this if:** You need comprehensive technical details on every aspect
|
||||
|
||||
---
|
||||
|
||||
### 3. **QUICK_REFERENCE.md** (5.6 KB)
|
||||
**Best for:** Quick lookup while coding
|
||||
|
||||
**Contains:**
|
||||
- What is the currency? (Lives)
|
||||
- Lives management methods
|
||||
- How lives are spent (table)
|
||||
- How lives are earned (table)
|
||||
- Level progression mechanics
|
||||
- Level data structure (from API)
|
||||
- Gameplay mechanics (time limit, input, conditions)
|
||||
- Rewards & penalties (table)
|
||||
- Economy balance formulas
|
||||
- API integration details
|
||||
- Storage schema
|
||||
- WeChat integration points
|
||||
- Key files list
|
||||
- Important constants
|
||||
- What's NOT implemented
|
||||
|
||||
**Read this if:** You need quick facts/numbers without reading full docs
|
||||
|
||||
---
|
||||
|
||||
### 4. **POINTS_FLOW_DIAGRAM.md** (22 KB)
|
||||
**Best for:** Visual understanding of system flows
|
||||
|
||||
**Contains ASCII Diagrams For:**
|
||||
1. Initialization Phase
|
||||
2. Gameplay Phase (single level)
|
||||
3. Lives Transaction Flow
|
||||
4. Level Progression & Persistence
|
||||
5. Data Persistence (localStorage)
|
||||
6. API & Level Data Flow
|
||||
7. Countdown Timer Flow
|
||||
8. WeChat Integration Points
|
||||
9. Complete Player Journey
|
||||
10. Lives Over Time (example scenario)
|
||||
|
||||
**Read this if:** You prefer visual diagrams over text explanations
|
||||
|
||||
---
|
||||
|
||||
### 5. **POINTS_SYSTEM_INDEX.md** (This File!)
|
||||
**Best for:** Navigation and understanding what exists where
|
||||
|
||||
**Contains:**
|
||||
- Overview of all documentation
|
||||
- Quick navigation by topic
|
||||
- Search guide
|
||||
- Related code file references
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Quick Navigation by Topic
|
||||
|
||||
### I want to understand...
|
||||
|
||||
**The Lives Economy**
|
||||
- → SUMMARY.md → "The Complete Points/Lives Flow"
|
||||
- → QUICK_REFERENCE.md → "Economy Balance"
|
||||
- → GAME_ANALYSIS.md → Section 1 & 2
|
||||
|
||||
**How to Earn Lives**
|
||||
- → SUMMARY.md → "EARNING Lives table"
|
||||
- → QUICK_REFERENCE.md → "How Lives Are Earned"
|
||||
- → POINTS_FLOW_DIAGRAM.md → "Complete Player Journey"
|
||||
|
||||
**How to Spend Lives**
|
||||
- → SUMMARY.md → "SPENDING Lives table"
|
||||
- → QUICK_REFERENCE.md → "How Lives Are Spent"
|
||||
- → GAME_ANALYSIS.md → Section 7 "Hint/Clue System"
|
||||
|
||||
**Level Progression**
|
||||
- → SUMMARY.md → "Level Progression System"
|
||||
- → QUICK_REFERENCE.md → "Level Progression"
|
||||
- → GAME_ANALYSIS.md → Section 2
|
||||
- → POINTS_FLOW_DIAGRAM.md → "Level Progression & Persistence"
|
||||
|
||||
**Data Storage**
|
||||
- → SUMMARY.md → "Data Persistence"
|
||||
- → QUICK_REFERENCE.md → "Storage Schema"
|
||||
- → GAME_ANALYSIS.md → Section 2 "Storage & Persistence"
|
||||
- → POINTS_FLOW_DIAGRAM.md → "Data Persistence"
|
||||
|
||||
**API Integration**
|
||||
- → SUMMARY.md → "API Integration"
|
||||
- → GAME_ANALYSIS.md → Section 3 & 4
|
||||
- → POINTS_FLOW_DIAGRAM.md → "API & Level Data Flow"
|
||||
|
||||
**WeChat Integration**
|
||||
- → SUMMARY.md → "WeChat Integration"
|
||||
- → GAME_ANALYSIS.md → Section 9
|
||||
- → POINTS_FLOW_DIAGRAM.md → "WeChat Integration Points"
|
||||
|
||||
**Complete Gameplay Flow**
|
||||
- → SUMMARY.md → "Gameplay Loop"
|
||||
- → GAME_ANALYSIS.md → Section 4 & 5 & 6
|
||||
- → POINTS_FLOW_DIAGRAM.md → "Gameplay Phase"
|
||||
|
||||
**Testing/Validation**
|
||||
- → SUMMARY.md → "Testing Scenarios"
|
||||
- → QUICK_REFERENCE.md → Check "What's NOT implemented" section
|
||||
|
||||
---
|
||||
|
||||
## 📖 Code References
|
||||
|
||||
### Core Files & What They Do
|
||||
|
||||
**StorageManager.ts** (Lives + Progress)
|
||||
- `getLives()` / `setLives()` / `consumeLife()` / `addLife()`
|
||||
- `getCurrentLevelIndex()` / `onLevelCompleted()`
|
||||
- Referenced in: GAME_ANALYSIS §2, SUMMARY "Data Persistence"
|
||||
|
||||
**PageLevel.ts** (Main Gameplay)
|
||||
- `onUnlockClue()` / `onSubmitAnswer()` / `showSuccess()` / `showError()`
|
||||
- `startCountdown()` / `onCountdownTick()`
|
||||
- Referenced in: GAME_ANALYSIS §4-7, SUMMARY "Gameplay Loop"
|
||||
|
||||
**LevelDataManager.ts** (API + Caching)
|
||||
- `initialize()` / `ensureLevelReady()` / `preloadNextLevel()`
|
||||
- Referenced in: GAME_ANALYSIS §3, SUMMARY "API Integration"
|
||||
|
||||
**PassModal.ts** (Victory Screen)
|
||||
- Victory UI and rewards display
|
||||
- Referenced in: GAME_ANALYSIS §5
|
||||
|
||||
**WxSDK.ts** (WeChat Integration)
|
||||
- `shareAppMessage()` / `vibrateLong()` / etc.
|
||||
- Referenced in: GAME_ANALYSIS §9
|
||||
|
||||
**ViewManager.ts** (Page Navigation)
|
||||
- Page stack management
|
||||
- Referenced in: GAME_ANALYSIS §10
|
||||
|
||||
---
|
||||
|
||||
## 🔑 Key Numbers & Values
|
||||
|
||||
| Item | Value | Reference |
|
||||
|------|-------|-----------|
|
||||
| Default Lives | 10 | QUICK_REFERENCE, SUMMARY |
|
||||
| Min Lives | 0 | QUICK_REFERENCE, SUMMARY |
|
||||
| Hint Cost | 1 life each | QUICK_REFERENCE, SUMMARY |
|
||||
| Win Reward | 1 life | QUICK_REFERENCE, SUMMARY |
|
||||
| Level Time | 60 seconds | QUICK_REFERENCE, SUMMARY |
|
||||
| API Timeout | 8000ms | QUICK_REFERENCE, GAME_ANALYSIS §3 |
|
||||
| API Retries | 2 attempts | QUICK_REFERENCE, GAME_ANALYSIS §3 |
|
||||
|
||||
---
|
||||
|
||||
## 🧩 How Systems Interact
|
||||
|
||||
```
|
||||
StorageManager (Lives + Progress)
|
||||
↓
|
||||
PageLevel (Gameplay)
|
||||
├─ Uses Lives for: Hint Unlocks
|
||||
├─ Updates Lives on: Level Complete
|
||||
├─ Uses Progress for: Level Selection
|
||||
└─ Updates Progress on: Level Complete
|
||||
↓
|
||||
LevelDataManager (API + Cache)
|
||||
├─ Fetches: Level Data + Images
|
||||
└─ Serves: Hints + Answers + Images to PageLevel
|
||||
↓
|
||||
WxSDK (WeChat)
|
||||
├─ Shares: Victory with Level Param
|
||||
└─ Vibrates: On Error
|
||||
↓
|
||||
ViewManager (Navigation)
|
||||
└─ Manages: Page Stack + State
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ Document Completeness Checklist
|
||||
|
||||
- [x] Lives earning mechanics
|
||||
- [x] Lives spending mechanics
|
||||
- [x] Level progression system
|
||||
- [x] Hint/clue system
|
||||
- [x] API integration
|
||||
- [x] Data persistence
|
||||
- [x] WeChat features
|
||||
- [x] Gameplay loop
|
||||
- [x] Win/lose conditions
|
||||
- [x] User journey examples
|
||||
- [x] Flow diagrams
|
||||
- [x] Code references
|
||||
- [x] Constants & values
|
||||
- [x] Error handling
|
||||
- [x] Performance optimizations
|
||||
- [x] Missing features list
|
||||
- [x] Testing scenarios
|
||||
|
||||
**Coverage:** 100% of points/lives/score system
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Using This Documentation
|
||||
|
||||
### For Understanding
|
||||
1. Start with SUMMARY.md (5 min read)
|
||||
2. Read QUICK_REFERENCE.md (3 min read)
|
||||
3. Review POINTS_FLOW_DIAGRAM.md (5 min read)
|
||||
4. Deep dive into GAME_ANALYSIS.md (15 min read)
|
||||
|
||||
### For Maintenance
|
||||
- QUICK_REFERENCE.md is your quick lookup
|
||||
- GAME_ANALYSIS.md has implementation details
|
||||
- Code references show where logic lives
|
||||
|
||||
### For Modifications
|
||||
- "Data Persistence" section for storage changes
|
||||
- "Lives Transaction Flow" for economy tweaks
|
||||
- "Constants" section for balance adjustments
|
||||
- Individual sections in GAME_ANALYSIS for specific systems
|
||||
|
||||
---
|
||||
|
||||
## ❓ FAQ
|
||||
|
||||
**Q: Where is the lives starting value defined?**
|
||||
A: `StorageManager.ts` line 25, `DEFAULT_LIVES = 10`
|
||||
|
||||
**Q: How do players earn lives?**
|
||||
A: By completing levels correctly. See SUMMARY.md "Earning Lives"
|
||||
|
||||
**Q: Can players lose lives?**
|
||||
A: Only by unlocking hints (-1 each). Wrong answers don't penalize. See SUMMARY.md "Spending Lives"
|
||||
|
||||
**Q: Where is progress stored?**
|
||||
A: Browser localStorage under keys "game_lives" and "game_progress". See POINTS_FLOW_DIAGRAM.md "Data Persistence"
|
||||
|
||||
**Q: What happens when time runs out?**
|
||||
A: Game plays fail sound but doesn't end. Players can still submit. See GAME_ANALYSIS.md Section 6
|
||||
|
||||
**Q: How does the hint system work?**
|
||||
A: Hint 1 is free, Hints 2 & 3 cost 1 life each. See GAME_ANALYSIS.md Section 7
|
||||
|
||||
**Q: Is there backend sync?**
|
||||
A: No. Progress is local-only. See SUMMARY.md "Missing Features"
|
||||
|
||||
---
|
||||
|
||||
## 📞 Document Metadata
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Total Files Analyzed | 21 TypeScript files |
|
||||
| Total Lines of Code | ~2,500 lines |
|
||||
| Documentation Generated | 4 files, ~45KB |
|
||||
| Coverage | Complete (100%) |
|
||||
| Last Updated | April 5, 2026 |
|
||||
| Scope | Points/Lives/Score System |
|
||||
|
||||
---
|
||||
|
||||
## 🎓 Summary of Key Takeaways
|
||||
|
||||
1. **Currency:** LIVES (renewable resource), not points
|
||||
2. **Starting:** 10 lives for new players
|
||||
3. **Economy:** +1 life per win, -1 per hint unlock
|
||||
4. **Storage:** localStorage (persistent, survives app close)
|
||||
5. **API:** Fetches level data on app start, cached in memory
|
||||
6. **Progression:** Sequential level unlocking with level 1 always available
|
||||
7. **Strategy:** No penalties for wrong answers, encourages replayability
|
||||
8. **WeChat:** Sharing with level referral parameter
|
||||
9. **Scope:** No backend sync, no authentication, local-only progress
|
||||
10. **Design:** Simple, effective, encourages skill-based progression
|
||||
|
||||
---
|
||||
|
||||
**Questions?** All answers are in one of these 4 documentation files!
|
||||
|
||||
@@ -1,196 +0,0 @@
|
||||
# QUICK REFERENCE - Game Points/Score System
|
||||
|
||||
## 🎮 What is the "Currency"?
|
||||
**LIVES** - Not traditional points/coins, but a renewable "health" resource.
|
||||
|
||||
## 📊 Lives Management
|
||||
```
|
||||
Storage Key: "game_lives" (localStorage)
|
||||
Default: 10
|
||||
Min Value: 0
|
||||
Max Value: ∞ (no limit)
|
||||
|
||||
Methods:
|
||||
├─ getLives() → Returns current lives
|
||||
├─ setLives(n) → Set specific value
|
||||
├─ consumeLife() → Deduct 1 life
|
||||
├─ addLife() → Add 1 life
|
||||
├─ hasLives() → Check if > 0
|
||||
└─ resetLives() → Reset to 10
|
||||
```
|
||||
|
||||
## 🎯 How Lives Are Spent
|
||||
| Action | Cost | Where |
|
||||
|--------|------|-------|
|
||||
| Unlock Clue 2 | 1 Life | PageLevel → onUnlockClue(2) |
|
||||
| Unlock Clue 3 | 1 Life | PageLevel → onUnlockClue(3) |
|
||||
| **TOTAL PER LEVEL** | **0-2 Lives** | Depends on player choice |
|
||||
|
||||
## 🏆 How Lives Are Earned
|
||||
| Action | Reward | Where |
|
||||
|--------|--------|-------|
|
||||
| Complete a Level | +1 Life | PageLevel → showSuccess() |
|
||||
| Wrong Answer | 0 | No penalty |
|
||||
| Time Up | 0 | No penalty |
|
||||
|
||||
## 📈 Level Progression
|
||||
```
|
||||
Storage Key: "game_progress" (localStorage)
|
||||
Structure:
|
||||
{
|
||||
currentLevelIndex: number, // 0-based, current level
|
||||
maxUnlockedLevelIndex: number // 0-based, highest reached
|
||||
}
|
||||
|
||||
Default: { currentLevelIndex: 0, maxUnlockedLevelIndex: 0 }
|
||||
```
|
||||
|
||||
### Progression Rules:
|
||||
1. **Level 1 always unlocked** - Start here
|
||||
2. **Beat Level N** → currentLevel becomes N+1
|
||||
3. **Beat Level N** → maxUnlocked becomes max(maxUnlocked, N)
|
||||
4. **Can replay earlier levels** - But always progress forward
|
||||
|
||||
### Methods:
|
||||
```
|
||||
getCurrentLevelIndex() → Get current (0-based)
|
||||
setCurrentLevelIndex(n) → Jump to level
|
||||
getMaxUnlockedLevelIndex() → Get highest reached
|
||||
isLevelUnlocked(n) → Check if playable
|
||||
onLevelCompleted(n) → Save win + progress
|
||||
resetProgress() → Reset to level 1
|
||||
```
|
||||
|
||||
## 🎨 Level Data (from API)
|
||||
**Endpoint:** `https://ilookai.cn/api/v1/wechat-game/levels`
|
||||
|
||||
```typescript
|
||||
ApiLevelData {
|
||||
id: string, // UUID
|
||||
level: number, // Level number (1-based display)
|
||||
imageUrl: string, // Main puzzle image
|
||||
hint1: string, // Free clue
|
||||
hint2: string, // Costs 1 life
|
||||
hint3: string, // Costs 1 life
|
||||
answer: string, // The answer (case-sensitive, trimmed)
|
||||
sortOrder: number // Sort order
|
||||
}
|
||||
```
|
||||
|
||||
## ⏱️ Gameplay Mechanics
|
||||
|
||||
### Time Limit
|
||||
- **Duration:** 60 seconds per level
|
||||
- **On Timeout:** Play fail sound, game doesn't end
|
||||
- **After Timeout:** Can still submit answers
|
||||
|
||||
### Input System
|
||||
- **Type:** Single text box (not per-character)
|
||||
- **Processing:** Trimmed, case-sensitive comparison
|
||||
- **Max Length:** Based on answer length
|
||||
|
||||
### Win Condition
|
||||
```
|
||||
input.trim() === answer
|
||||
↓
|
||||
Play success sound → Stop timer → Award +1 life
|
||||
→ Show PassModal → Save progress
|
||||
```
|
||||
|
||||
### Lose Condition
|
||||
```
|
||||
input.trim() !== answer
|
||||
↓
|
||||
Play fail sound → Vibrate → Show toast
|
||||
→ Lives unchanged → Can retry
|
||||
```
|
||||
|
||||
## 🎁 Rewards & Penalties
|
||||
| Event | Lives Change | Other Effects |
|
||||
|-------|--------------|---------------|
|
||||
| Correct Answer | +1 | Play success sound, show modal |
|
||||
| Wrong Answer | 0 | Play fail sound, vibrate, toast |
|
||||
| Unlock Clue | -1 | Show clue content |
|
||||
| Time Up | 0 | Play fail sound, countdown stops |
|
||||
| Level Complete | Already +1ed | Save progress, move to next |
|
||||
|
||||
## 🔄 Economy Balance
|
||||
```
|
||||
Starting Inventory: 10 lives
|
||||
|
||||
Without Hints: +1 life/level → Infinite
|
||||
With 1 Hint/Level: 0 lives/level → Stable
|
||||
With 2 Hints/Level: -1 life/level → Finite (10-20 levels)
|
||||
|
||||
Net Formula: newLives = oldLives - hintsUsed + 1 (on win)
|
||||
```
|
||||
|
||||
## 📡 API Integration
|
||||
```
|
||||
LevelDataManager {
|
||||
API_URL: "https://ilookai.cn/api/v1/wechat-game/levels"
|
||||
TIMEOUT: 8000ms
|
||||
RETRY_COUNT: 2
|
||||
|
||||
Calls:
|
||||
├─ initialize() → Load all level metadata + image for level 1
|
||||
├─ ensureLevelReady(n) → Load specific level image
|
||||
├─ preloadNextLevel(n) → Silently preload level n+1
|
||||
└─ getLevelConfig(n) → Get cached level data
|
||||
}
|
||||
```
|
||||
|
||||
## 📁 Storage Schema
|
||||
```
|
||||
localStorage: {
|
||||
"game_lives": "10",
|
||||
"game_progress": "{\"currentLevelIndex\":0,\"maxUnlockedLevelIndex\":0}"
|
||||
}
|
||||
```
|
||||
|
||||
## 🌐 WeChat Integration
|
||||
```
|
||||
Features Used:
|
||||
├─ WxSDK.initShare() → Enable sharing
|
||||
├─ WxSDK.shareAppMessage() → Share to friend with level query param
|
||||
├─ WxSDK.vibrateLong() → 400ms vibration on error
|
||||
└─ WxSDK.vibrateShort() → 15ms vibration on click
|
||||
```
|
||||
|
||||
## 🔑 Key Files
|
||||
```
|
||||
StorageManager.ts → Lives & progress persistence
|
||||
LevelDataManager.ts → API & image loading
|
||||
PageLevel.ts → Main game logic
|
||||
PageLoading.ts → App initialization
|
||||
PassModal.ts → Victory screen
|
||||
ViewManager.ts → Page navigation
|
||||
WxSDK.ts → WeChat APIs
|
||||
```
|
||||
|
||||
## ⚙️ Constants
|
||||
```
|
||||
DEFAULT_LIVES 10
|
||||
MIN_LIVES 0
|
||||
LEVEL_TIME_LIMIT 60 seconds
|
||||
LIFE_PER_HINT 1
|
||||
LIFE_PER_WIN 1
|
||||
API_TIMEOUT 8000ms
|
||||
API_RETRY_COUNT 2
|
||||
|
||||
Game Title "写英语" (Write English)
|
||||
Share Query Format "level=<levelIndex>"
|
||||
```
|
||||
|
||||
## 🚨 No Implementation For:
|
||||
- User Authentication (wx.login)
|
||||
- Backend Progress Save
|
||||
- Ads/Monetization
|
||||
- Leaderboards
|
||||
- Analytics
|
||||
- Premium Life Refills
|
||||
- Difficulty Levels
|
||||
|
||||
---
|
||||
|
||||
**In Summary:** Players earn/spend LIVES by unlocking clues (-1 each) or winning levels (+1 each). Progress is saved locally with streak tracking. The economy encourages players to solve without hints to maximize lives.
|
||||
416
SUMMARY.md
@@ -1,416 +0,0 @@
|
||||
# Game System Summary - Points/Score/Lives Analysis
|
||||
|
||||
**Project:** Cocos Creator WeChat Mini-Game ("写英语" - Write English)
|
||||
**Date Analyzed:** April 5, 2026
|
||||
**Analysis Scope:** Complete TypeScript codebase (21 source files)
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
This game uses a **LIVES-based economy** rather than traditional points/coins:
|
||||
|
||||
- **Currency:** Lives (renewable resource)
|
||||
- **Starting Value:** 10 lives per new player
|
||||
- **Earning Mechanic:** +1 life per completed level
|
||||
- **Spending Mechanic:** -1 life per hint unlock (optional, 2 hints available)
|
||||
- **Net Effect:** Players naturally gain lives if they solve without hints, stay stable with 1 hint/level, or lose lives if using both hints
|
||||
|
||||
The system is **fully persistent** (saved to localStorage) and **progression-aware** (can replay earlier levels while advancing).
|
||||
|
||||
---
|
||||
|
||||
## Files Analyzed (21 total)
|
||||
|
||||
### Core Game Logic (4 files)
|
||||
1. **PageLevel.ts** (786 lines) - Main gameplay, hint system, answer validation
|
||||
2. **LevelDataManager.ts** (312 lines) - API calls, level caching, image loading
|
||||
3. **StorageManager.ts** (240 lines) - Lives & progress persistence
|
||||
4. **PageLoading.ts** (88 lines) - App initialization, preloading
|
||||
|
||||
### UI/Navigation (3 files)
|
||||
5. **ViewManager.ts** (320 lines) - Page stack management
|
||||
6. **BaseView.ts** (132 lines) - View lifecycle
|
||||
7. **PassModal.ts** (155 lines) - Victory screen with rewards
|
||||
|
||||
### Utilities (5 files)
|
||||
8. **ToastManager.ts** (59 lines) - Toast notifications
|
||||
9. **WxSDK.ts** (188 lines) - WeChat API wrapper
|
||||
10. **HttpUtil.ts** (76 lines) - HTTP requests
|
||||
11. **Toast.ts** (50 lines) - Toast display component
|
||||
12. **LevelTypes.ts** (59 lines) - TypeScript interfaces
|
||||
|
||||
### Prefabs & Entry Points (3 files)
|
||||
13. **PageHome.ts** (78 lines) - Home page
|
||||
14. **main.ts** (59 lines) - App entry point
|
||||
15. Plus UI utilities (BackgroundScaler, RoundedRectMask)
|
||||
|
||||
---
|
||||
|
||||
## The Complete Points/Lives Flow
|
||||
|
||||
### 🟢 EARNING Lives
|
||||
| Event | Amount | Condition |
|
||||
|-------|--------|-----------|
|
||||
| Correct Answer | +1 | Submitted answer matches API answer exactly (case-sensitive, trimmed) |
|
||||
| New Game | +10 | First time players only (default) |
|
||||
| **Cannot Earn** | 0 | Wrong answers, timeouts, hint unlocks, returning players don't reset |
|
||||
|
||||
### 🔴 SPENDING Lives
|
||||
| Event | Amount | Condition |
|
||||
|--------|--------|-----------|
|
||||
| Unlock Hint 2 | -1 | Player clicks unlock, must have lives > 0 |
|
||||
| Unlock Hint 3 | -1 | Player clicks unlock, must have lives > 0 |
|
||||
| No Other Costs | - | No penalties for wrong answers or timeouts |
|
||||
|
||||
### 📊 Net Economy Per Level
|
||||
```
|
||||
Best Case (No Hints): +1 life/level → Infinite scaling
|
||||
Average Case (1 Hint): 0 lives/level → Stable indefinitely
|
||||
Hard Case (2 Hints): -1 life/level → Runs out after 10-20 levels
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Data Persistence
|
||||
|
||||
### localStorage Keys
|
||||
```javascript
|
||||
{
|
||||
"game_lives": "10", // Current lives count
|
||||
"game_progress": "{ // Player progression
|
||||
\"currentLevelIndex\": 0,
|
||||
\"maxUnlockedLevelIndex\": 0
|
||||
}"
|
||||
}
|
||||
```
|
||||
|
||||
### Lifespan
|
||||
- **Initial:** Set when first loading game
|
||||
- **Updates:** Every level completion or hint unlock
|
||||
- **Persistence:** Survives app close/reopen (native browser storage)
|
||||
- **Manual Reset:** Available via `StorageManager.resetAll()`
|
||||
|
||||
---
|
||||
|
||||
## Level Progression System
|
||||
|
||||
### Mechanics
|
||||
1. **Level 1 Always Unlocked** - New players start here
|
||||
2. **Sequential Unlocking** - Beat level N to unlock level N+1
|
||||
3. **Non-Linear Progress** - Can replay earlier levels anytime
|
||||
4. **Max Tracking** - Tracks highest level reached for achievements
|
||||
|
||||
### Data Structure
|
||||
```typescript
|
||||
{
|
||||
currentLevelIndex: number, // Which level to play next (0-based)
|
||||
maxUnlockedLevelIndex: number // Highest level player has beaten
|
||||
}
|
||||
```
|
||||
|
||||
### Example Progression
|
||||
```
|
||||
User beats Level 3 (index 2)
|
||||
↓
|
||||
currentLevelIndex → 2+1 = 3 (level 4)
|
||||
maxUnlockedLevelIndex → max(2, previous) = 2
|
||||
↓
|
||||
Can now play levels 0-3
|
||||
Must complete level 3 to unlock level 4
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Hint System
|
||||
|
||||
### Mechanics
|
||||
- **Hint 1:** Always visible, completely free
|
||||
- **Hint 2:** Hidden by default, costs 1 life to unlock
|
||||
- **Hint 3:** Hidden by default, costs 1 life to unlock
|
||||
|
||||
### Implementation
|
||||
```typescript
|
||||
onUnlockClue(index: 2|3) {
|
||||
if (!hasLives()) return; // Must have >= 1 life
|
||||
consumeLife(); // Deduct 1 life
|
||||
showClue(index); // Reveal the clue content
|
||||
}
|
||||
```
|
||||
|
||||
### Strategic Element
|
||||
Players must decide if they want to spend lives for hints or solve blindly to accumulate more lives for future levels.
|
||||
|
||||
---
|
||||
|
||||
## Gameplay Loop
|
||||
|
||||
### Per-Level Sequence
|
||||
1. Load level image from API cache
|
||||
2. Display Hint 1 (free) + two "Unlock" buttons
|
||||
3. Start 60-second countdown
|
||||
4. Player can:
|
||||
- Unlock hints by spending lives (optional, repeatable)
|
||||
- Type their answer in single text box
|
||||
- Submit answer
|
||||
5. Outcome:
|
||||
- **Correct:** Reward +1 life, show victory modal
|
||||
- **Wrong:** No penalty, show error toast, can retry
|
||||
- **Timeout:** Play fail sound, can still submit
|
||||
|
||||
### Victory Rewards
|
||||
```
|
||||
onSubmitAnswer(userAnswer) {
|
||||
if (userAnswer.trim() === correctAnswer) {
|
||||
playSuccessSound();
|
||||
addLife(); // +1 life
|
||||
StorageManager.onLevelCompleted(currentLevel);
|
||||
showPassModal(); // Next/Share buttons
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API Integration
|
||||
|
||||
### Endpoint
|
||||
```
|
||||
GET https://ilookai.cn/api/v1/wechat-game/levels
|
||||
```
|
||||
|
||||
### Response Format
|
||||
```typescript
|
||||
{
|
||||
success: boolean,
|
||||
message: string | null,
|
||||
data: {
|
||||
levels: [
|
||||
{
|
||||
id: "uuid",
|
||||
level: 1,
|
||||
imageUrl: "https://...",
|
||||
hint1: "Free clue",
|
||||
hint2: "Paid clue",
|
||||
hint3: "Paid clue",
|
||||
answer: "CORRECT_ANSWER",
|
||||
sortOrder: 1
|
||||
},
|
||||
// ... more levels
|
||||
],
|
||||
total: number
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Reliability Features
|
||||
- **Retry Logic:** 2 attempts on failure
|
||||
- **Timeout:** 8 seconds per request
|
||||
- **Fallback:** Shows error message if all retries fail
|
||||
- **Caching:** All levels cached in memory after first load
|
||||
|
||||
---
|
||||
|
||||
## WeChat Integration
|
||||
|
||||
### Features Implemented
|
||||
1. **Sharing** - Share game to friends with level parameter
|
||||
2. **Vibration** - Haptic feedback on errors
|
||||
3. **Platform Detection** - Gracefully degrade on non-WeChat platforms
|
||||
|
||||
### Share Implementation
|
||||
```typescript
|
||||
// On victory, user can click "Share"
|
||||
WxSDK.shareAppMessage({
|
||||
title: "快来一起玩这款游戏吧",
|
||||
query: `level=${victoryLevelIndex}`
|
||||
});
|
||||
// Opens WeChat share dialog with referral link
|
||||
```
|
||||
|
||||
### Not Implemented
|
||||
- ❌ User authentication (wx.login)
|
||||
- ❌ Backend progress sync
|
||||
- ❌ Analytics
|
||||
- ❌ Ads/Monetization
|
||||
- ❌ Leaderboards
|
||||
|
||||
---
|
||||
|
||||
## User Journey Example
|
||||
|
||||
```
|
||||
New User Opens Game
|
||||
↓
|
||||
[Initialize: 10 lives, Level 1]
|
||||
↓
|
||||
Attempt Level 1
|
||||
├─ Unlocks Hint 2 (-1 life) → 9 lives
|
||||
├─ Unlocks Hint 3 (-1 life) → 8 lives
|
||||
├─ Submits Answer
|
||||
│
|
||||
├─ CORRECT
|
||||
│ ├─ Awards +1 life → 9 lives
|
||||
│ ├─ Saves progress (level → 2)
|
||||
│ └─ Shows PassModal
|
||||
│ ├─ Can click "Next Level" → Level 2
|
||||
│ └─ Can click "Share" → wx.share
|
||||
│
|
||||
└─ WRONG
|
||||
├─ No penalty → 9 lives unchanged
|
||||
├─ Shows error toast
|
||||
└─ Can retry immediately
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Loading & Initialization
|
||||
|
||||
### On App Start
|
||||
1. **Detect New vs. Returning User**
|
||||
- Check localStorage "game_lives" key
|
||||
- New: Initialize to 10
|
||||
- Returning: Restore value
|
||||
|
||||
2. **Load Level Metadata**
|
||||
- API call to fetch all levels
|
||||
- Retry up to 2 times on failure
|
||||
- Cache all level data in memory
|
||||
|
||||
3. **Preload First Level**
|
||||
- Download image for level 1
|
||||
- Cache in memory for instant display
|
||||
|
||||
4. **Show Home Screen**
|
||||
- Display "Start Game" button
|
||||
- Initialize WeChat sharing
|
||||
|
||||
### On Subsequent Game Sessions
|
||||
1. Restore lives from localStorage
|
||||
2. Restore progress from localStorage
|
||||
3. Resume at saved level
|
||||
4. API already cached (no refetch unless app restarted)
|
||||
|
||||
---
|
||||
|
||||
## Key Constants
|
||||
|
||||
| Constant | Value | Usage |
|
||||
|----------|-------|-------|
|
||||
| DEFAULT_LIVES | 10 | New player starting amount |
|
||||
| MIN_LIVES | 0 | Cannot go negative |
|
||||
| LEVEL_TIME_LIMIT | 60 seconds | Countdown timer |
|
||||
| API_TIMEOUT | 8000ms | HTTP request timeout |
|
||||
| API_RETRY_COUNT | 2 | Attempts on failure |
|
||||
| HINT_COST | 1 life each | Unlock clue 2 or 3 |
|
||||
| WIN_REWARD | 1 life | Completion bonus |
|
||||
| MODAL_Z_INDEX | 999 | Victory modal layer |
|
||||
|
||||
---
|
||||
|
||||
## Business Logic Highlights
|
||||
|
||||
### Win Condition
|
||||
```typescript
|
||||
userAnswer.trim() === correctAnswer
|
||||
```
|
||||
- Case-sensitive
|
||||
- Whitespace trimmed
|
||||
- Must match exactly
|
||||
|
||||
### No Lose Condition
|
||||
- Wrong answers: No penalty
|
||||
- Time up: No penalty
|
||||
- Can retry infinitely on same level
|
||||
|
||||
### Safety Checks
|
||||
- Can't unlock hints if lives ≤ 0
|
||||
- Can't go below 0 lives
|
||||
- Can't exceed total levels
|
||||
- Invalid data resets to defaults
|
||||
|
||||
---
|
||||
|
||||
## Performance Optimizations
|
||||
|
||||
1. **Image Caching** - Downloaded images cached in memory
|
||||
2. **Level Metadata Caching** - API data cached to avoid re-fetching
|
||||
3. **Progress Caching** - localStorage data cached to reduce reads
|
||||
4. **Async Preloading** - Next level preloaded silently
|
||||
5. **Efficient UI** - Single EditBox (not per-character)
|
||||
6. **Lazy Loading** - Pages cached after first load
|
||||
|
||||
---
|
||||
|
||||
## Missing Features / Gaps
|
||||
|
||||
| Feature | Status | Impact |
|
||||
|---------|--------|--------|
|
||||
| User Authentication | ❌ | No way to sync across devices |
|
||||
| Backend Progress Save | ❌ | Progress lost if localStorage clears |
|
||||
| Monetization | ❌ | No revenue stream |
|
||||
| Ads | ❌ | No ad integration |
|
||||
| Analytics | ❌ | Can't track player behavior |
|
||||
| Leaderboards | ❌ | No competition mechanics |
|
||||
| Sound Toggle | ❌ | Always plays sounds |
|
||||
| Difficulty Levels | ❌ | All players see same levels |
|
||||
|
||||
---
|
||||
|
||||
## Testing Scenarios
|
||||
|
||||
### Test Case 1: Lives Economy
|
||||
```
|
||||
1. New game → 10 lives
|
||||
2. Beat level without hints → 11 lives
|
||||
3. Beat level with 1 hint → 11 lives (net 0)
|
||||
4. Beat level with 2 hints → 10 lives (net -1)
|
||||
→ Verify localStorage updated after each
|
||||
```
|
||||
|
||||
### Test Case 2: Progression
|
||||
```
|
||||
1. Complete level 1
|
||||
2. Verify currentLevel → 2
|
||||
3. Verify maxUnlocked → 1
|
||||
4. Restart app
|
||||
5. Verify levels still saved
|
||||
6. Replay level 1
|
||||
7. Verify no duplicate progress
|
||||
```
|
||||
|
||||
### Test Case 3: Hint Cost
|
||||
```
|
||||
1. Start with 10 lives
|
||||
2. Unlock hint 2 → 9 lives displayed
|
||||
3. Unlock hint 3 → 8 lives displayed
|
||||
4. Can't unlock again (button inactive)
|
||||
5. Wrong answer → 8 lives still
|
||||
6. Correct answer → 9 lives (net -1)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Conclusion
|
||||
|
||||
This is a **well-architected mini-game** with:
|
||||
- ✅ Clear lives-based economy
|
||||
- ✅ Persistent progress tracking
|
||||
- ✅ Strategic hint system
|
||||
- ✅ Reliable API integration
|
||||
- ✅ Good error handling
|
||||
- ✅ WeChat platform integration
|
||||
|
||||
The point system is **intentionally forgiving** - players who solve puzzles smartly gain lives indefinitely, while those using hints maintain stability. This encourages skill development without hard progress walls.
|
||||
|
||||
---
|
||||
|
||||
## Documentation Files Generated
|
||||
|
||||
1. **GAME_ANALYSIS.md** - Comprehensive 17-section analysis (13KB)
|
||||
2. **QUICK_REFERENCE.md** - Quick lookup guide (5.6KB)
|
||||
3. **POINTS_FLOW_DIAGRAM.md** - Visual flow diagrams (15KB)
|
||||
4. **SUMMARY.md** - This executive summary (8KB)
|
||||
|
||||
**Total:** 42KB of detailed documentation covering every aspect of the points/score/lives system.
|
||||
|
||||
BIN
assets/.DS_Store
vendored
Normal file
@@ -176,7 +176,7 @@
|
||||
"a": 255
|
||||
},
|
||||
"_spriteFrame": {
|
||||
"__uuid__": "d532045e-55f8-47c2-9493-b918e18364b0@f9941",
|
||||
"__uuid__": "10047f2e-bf2c-46f0-ba98-6fc23cf7ea87@f9941",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
"_type": 0,
|
||||
@@ -597,8 +597,8 @@
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 80,
|
||||
"height": 50.4
|
||||
"width": 88,
|
||||
"height": 58.4
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
@@ -646,19 +646,19 @@
|
||||
"_isSystemFontUsed": true,
|
||||
"_spacingX": 0,
|
||||
"_isItalic": false,
|
||||
"_isBold": false,
|
||||
"_isBold": true,
|
||||
"_isUnderline": false,
|
||||
"_underlineHeight": 2,
|
||||
"_cacheMode": 0,
|
||||
"_enableOutline": false,
|
||||
"_enableOutline": true,
|
||||
"_outlineColor": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 0,
|
||||
"g": 0,
|
||||
"b": 0,
|
||||
"r": 72,
|
||||
"g": 158,
|
||||
"b": 35,
|
||||
"a": 255
|
||||
},
|
||||
"_outlineWidth": 2,
|
||||
"_outlineWidth": 4,
|
||||
"_enableShadow": false,
|
||||
"_shadowColor": {
|
||||
"__type__": "cc.Color",
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { _decorator, Component, ProgressBar, Label } from 'cc';
|
||||
import { _decorator, Component, ProgressBar, Label, assetManager } from 'cc';
|
||||
import type { AssetManager } from 'cc';
|
||||
import { ViewManager } from './scripts/core/ViewManager';
|
||||
import { LevelDataManager } from './scripts/utils/LevelDataManager';
|
||||
import { AuthManager } from './scripts/utils/AuthManager';
|
||||
import { StorageManager } from './scripts/utils/StorageManager';
|
||||
import { ShareManager } from './scripts/utils/ShareManager';
|
||||
import { WxSDK } from './scripts/utils/WxSDK';
|
||||
const { ccclass, property } = _decorator;
|
||||
@@ -10,10 +10,12 @@ const { ccclass, property } = _decorator;
|
||||
/**
|
||||
* 页面加载组件
|
||||
* 负责用户登录、预加载资源并显示加载进度
|
||||
* 登录与关卡数据加载并行执行以减少等待时间
|
||||
* 流程:登录 + game-data → 拿到 nextLevel → 加载首关图片 → 进入首页
|
||||
*/
|
||||
@ccclass('PageLoading')
|
||||
export class PageLoading extends Component {
|
||||
private static readonly FONT_BUNDLE_NAME = 'fonts';
|
||||
|
||||
@property(ProgressBar)
|
||||
progressBar: ProgressBar | null = null;
|
||||
|
||||
@@ -31,15 +33,11 @@ export class PageLoading extends Component {
|
||||
|
||||
this._updateStatusLabel('正在加载...');
|
||||
|
||||
// 登录和关卡数据并行加载
|
||||
const [loginSuccess, levelSuccess] = await Promise.all([
|
||||
AuthManager.instance.initialize(),
|
||||
LevelDataManager.instance.initialize((progress, message) => {
|
||||
// 关卡加载占 0-80% 进度
|
||||
this._updateProgress(progress);
|
||||
this._updateStatusLabel(message);
|
||||
}),
|
||||
]);
|
||||
// 阶段1: 登录 + 获取 game-data(含 nextLevel)
|
||||
this._updateProgress(0);
|
||||
this._updateStatusLabel('正在连接服务器...');
|
||||
|
||||
const loginSuccess = await AuthManager.instance.initialize();
|
||||
|
||||
if (loginSuccess) {
|
||||
console.log('[PageLoading] 用户登录成功');
|
||||
@@ -47,14 +45,38 @@ export class PageLoading extends Component {
|
||||
console.warn('[PageLoading] 登录失败,继续离线模式');
|
||||
}
|
||||
|
||||
this._updateProgress(0.2);
|
||||
|
||||
// 阶段2: 加载首关图片(如果有 nextLevel)
|
||||
const nextLevel = AuthManager.instance.nextLevel;
|
||||
let levelSuccess = false;
|
||||
|
||||
if (nextLevel) {
|
||||
levelSuccess = await LevelDataManager.instance.initialize(nextLevel, (progress, message) => {
|
||||
// 关卡图片加载占 20%-80% 进度
|
||||
this._updateProgress(0.2 + progress * 0.6);
|
||||
this._updateStatusLabel(message);
|
||||
});
|
||||
|
||||
if (!levelSuccess) {
|
||||
this._updateStatusLabel('资源加载失败,请重新打开游戏');
|
||||
return;
|
||||
}
|
||||
} else if (loginSuccess) {
|
||||
// nextLevel 为 null → 全部通关(或服务端无关卡)
|
||||
console.log('[PageLoading] 全部通关或无可用关卡');
|
||||
this._updateProgress(0.8);
|
||||
} else {
|
||||
// 登录失败且没有 nextLevel
|
||||
this._updateStatusLabel('加载失败,请重新打开游戏');
|
||||
return;
|
||||
}
|
||||
|
||||
// 登录 + 关卡数据都就绪后,用服务端进度覆盖本地进度
|
||||
if (loginSuccess) {
|
||||
this._syncProgressFromServer();
|
||||
// 阶段3: 加载字体分包
|
||||
const fontSuccess = await this._loadFontBundle();
|
||||
if (!fontSuccess) {
|
||||
this._updateStatusLabel('字体资源加载失败,请重新打开游戏');
|
||||
return;
|
||||
}
|
||||
|
||||
// 检测分享码:从微信启动参数中获取
|
||||
@@ -77,10 +99,10 @@ export class PageLoading extends Component {
|
||||
console.warn('[PageLoading] 加入分享失败,进入正常模式');
|
||||
}
|
||||
|
||||
// 正常流程:预加载 PageHome (80-100%)
|
||||
// 正常流程:预加载 PageHome (82-100%)
|
||||
ViewManager.instance.preload('PageHome',
|
||||
(progress) => {
|
||||
this._updateProgress(0.8 + progress * 0.2);
|
||||
this._updateProgress(0.82 + progress * 0.18);
|
||||
this._updateStatusLabel('正在加载界面资源...');
|
||||
},
|
||||
() => {
|
||||
@@ -113,28 +135,32 @@ export class PageLoading extends Component {
|
||||
}
|
||||
|
||||
/**
|
||||
* 用服务端通关进度覆盖本地进度
|
||||
* 将 completedLevelIds 转换为本地的 currentLevelIndex / maxUnlockedLevelIndex
|
||||
* 加载字体分包,避免字体资源进入小游戏主包
|
||||
*/
|
||||
private _syncProgressFromServer(): void {
|
||||
const completedIds = AuthManager.instance.completedLevelIds;
|
||||
if (completedIds.length === 0) {
|
||||
console.log('[PageLoading] 服务端无通关记录,使用本地进度');
|
||||
private _loadFontBundle(): Promise<boolean> {
|
||||
const bundleName = PageLoading.FONT_BUNDLE_NAME;
|
||||
const cachedBundle = assetManager.getBundle(bundleName);
|
||||
if (cachedBundle) {
|
||||
console.log(`[PageLoading] 字体分包已加载: ${bundleName}`);
|
||||
this._updateProgress(0.82);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
this._updateStatusLabel('正在加载字体资源...');
|
||||
this._updateProgress(0.8);
|
||||
|
||||
return new Promise((resolve) => {
|
||||
assetManager.loadBundle(bundleName, (err: Error | null, bundle: AssetManager.Bundle | null) => {
|
||||
if (err || !bundle) {
|
||||
console.error(`[PageLoading] 字体分包加载失败: ${bundleName}`, err);
|
||||
resolve(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const maxCompletedIndex = LevelDataManager.instance.getMaxCompletedIndex(completedIds);
|
||||
if (maxCompletedIndex < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const localMax = StorageManager.getMaxUnlockedLevelIndex();
|
||||
|
||||
// 取服务端和本地的较大值,防止进度回退
|
||||
if (maxCompletedIndex > localMax) {
|
||||
// onLevelCompleted 会同时设置 currentLevelIndex = maxCompletedIndex + 1 和 maxUnlockedLevelIndex
|
||||
StorageManager.onLevelCompleted(maxCompletedIndex);
|
||||
console.log(`[PageLoading] 服务端进度同步:已通关到第 ${maxCompletedIndex + 1} 关`);
|
||||
}
|
||||
console.log(`[PageLoading] 字体分包加载完成: ${bundleName}`);
|
||||
this._updateProgress(0.82);
|
||||
resolve(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
11
assets/fonts.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"ver": "1.2.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "fc1f44af-699f-467c-ba2d-f54994551c4d",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"isBundle": true
|
||||
}
|
||||
}
|
||||
BIN
assets/fonts/Game011_3.ttf
Normal file
12
assets/fonts/Game011_3.ttf.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.0.1",
|
||||
"importer": "ttf-font",
|
||||
"imported": true,
|
||||
"uuid": "fb4acba6-6bc7-4eb3-be34-8f2ac9823a80",
|
||||
"files": [
|
||||
".json",
|
||||
"Game011_3.ttf"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
9
assets/levels.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.2.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "0b928321-a809-4339-8af8-5a053aeda2d5",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
import { _decorator, Node, Button } from 'cc';
|
||||
import { _decorator, Node, Button, Label, tween, Vec3, UIOpacity, UITransform, Color, instantiate } from 'cc';
|
||||
import { BaseView } from 'db://assets/scripts/core/BaseView';
|
||||
import { ViewManager } from 'db://assets/scripts/core/ViewManager';
|
||||
import { WxSDK, checkPrivacySetting, requirePrivacyAuthorize } from 'db://assets/scripts/utils/WxSDK';
|
||||
import { StaminaManager } from 'db://assets/scripts/utils/StaminaManager';
|
||||
import { ToastManager } from 'db://assets/scripts/utils/ToastManager';
|
||||
import { StaminaInfo } from 'db://assets/scripts/types/ApiTypes';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
/**
|
||||
@@ -10,12 +13,34 @@ const { ccclass, property } = _decorator;
|
||||
*/
|
||||
@ccclass('PageHome')
|
||||
export class PageHome extends BaseView {
|
||||
/** 默认体力上限 */
|
||||
private static readonly DEFAULT_STAMINA_MAX = 50;
|
||||
|
||||
@property({ type: Node, tooltip: '开始游戏按钮' })
|
||||
startGameBtn: Node | null = null;
|
||||
|
||||
@property({ type: Node, tooltip: 'PK按钮' })
|
||||
pkBtn: Node | null = null;
|
||||
|
||||
/** 体力值显示标签 */
|
||||
@property(Label)
|
||||
liveLabel: Label | null = null;
|
||||
|
||||
/** 飞行动画持续时间(秒) */
|
||||
private static readonly FLY_DURATION = 0.5;
|
||||
|
||||
/** 到达后弹跳持续时间(秒) */
|
||||
private static readonly BOUNCE_DURATION = 0.15;
|
||||
|
||||
/** 浮动文本动画持续时间(秒) */
|
||||
private static readonly FLOAT_DURATION = 0.8;
|
||||
|
||||
/** 浮动文本上移距离 */
|
||||
private static readonly FLOAT_OFFSET_Y = 120;
|
||||
|
||||
/** 是否正在播放体力消耗动画 */
|
||||
private _isAnimating: boolean = false;
|
||||
|
||||
/**
|
||||
* 页面首次加载时调用
|
||||
*/
|
||||
@@ -76,8 +101,29 @@ export class PageHome extends BaseView {
|
||||
* 开始游戏按钮点击回调
|
||||
*/
|
||||
private _onStartGameClick(): void {
|
||||
if (this._isAnimating) return;
|
||||
|
||||
console.log('[PageHome] 开始游戏按钮点击');
|
||||
|
||||
// 体力检查
|
||||
if (!StaminaManager.instance.hasStamina()) {
|
||||
ToastManager.show('体力不足,请等待恢复');
|
||||
return;
|
||||
}
|
||||
|
||||
this._isAnimating = true;
|
||||
this._playStaminaCostAnimation()
|
||||
.then(() => {
|
||||
ViewManager.instance.open('PageLevel');
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('[PageHome] 体力消耗动画异常:', err);
|
||||
// 异常兜底:直接进入关卡
|
||||
ViewManager.instance.open('PageLevel');
|
||||
})
|
||||
.finally(() => {
|
||||
this._isAnimating = false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,11 +134,198 @@ export class PageHome extends BaseView {
|
||||
ViewManager.instance.open('PageWriteLevels');
|
||||
}
|
||||
|
||||
// ========== 体力消耗动画 ==========
|
||||
|
||||
/**
|
||||
* 通过节点路径查找 IconLive
|
||||
*/
|
||||
private _findIconLive(): Node | null {
|
||||
return this.node
|
||||
.getChildByName('TopLayout')
|
||||
?.getChildByName('Live')
|
||||
?.getChildByName('IconLive') ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将节点的世界坐标转换为目标父节点的本地坐标
|
||||
*/
|
||||
private _worldToLocal(worldPos: Vec3, parent: Node): Vec3 {
|
||||
const parentTransform = parent.getComponent(UITransform);
|
||||
if (!parentTransform) return worldPos;
|
||||
return parentTransform.convertToNodeSpaceAR(worldPos);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取节点的世界坐标
|
||||
*/
|
||||
private _getWorldPos(node: Node): Vec3 {
|
||||
const transform = node.getComponent(UITransform);
|
||||
if (!transform) return node.worldPosition.clone();
|
||||
return transform.convertToWorldSpaceAR(Vec3.ZERO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 播放体力消耗动画
|
||||
* 1. 克隆 IconLive 飞向 StarGame 按钮
|
||||
* 2. 到达后弹跳
|
||||
* 3. "体力值-1" 浮动文本上移渐隐
|
||||
* 4. 同步更新体力数字
|
||||
*/
|
||||
private _playStaminaCostAnimation(): Promise<void> {
|
||||
return new Promise<void>((resolve) => {
|
||||
const iconLive = this._findIconLive();
|
||||
const targetBtn = this.startGameBtn;
|
||||
|
||||
if (!iconLive || !targetBtn) {
|
||||
console.warn('[PageHome] 动画节点未找到,跳过动画');
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
// --- 坐标计算 ---
|
||||
const iconWorldPos = this._getWorldPos(iconLive);
|
||||
const targetWorldPos = this._getWorldPos(targetBtn);
|
||||
const rootNode = this.node;
|
||||
|
||||
// 起始和终点在 root 本地空间的坐标
|
||||
const startLocal = this._worldToLocal(iconWorldPos, rootNode);
|
||||
const endLocal = this._worldToLocal(targetWorldPos, rootNode);
|
||||
|
||||
// --- 克隆飞行节点 ---
|
||||
const flyNode = instantiate(iconLive);
|
||||
flyNode.name = '_flyIcon';
|
||||
flyNode.setPosition(startLocal);
|
||||
// 保持与原始 IconLive 相同的缩放
|
||||
flyNode.setScale(iconLive.worldScale.clone());
|
||||
rootNode.addChild(flyNode);
|
||||
|
||||
// 隐藏原始 IconLive
|
||||
iconLive.active = false;
|
||||
|
||||
// --- 飞行路径(带弧度的抛物线效果) ---
|
||||
// 中间控制点:x 取中点,y 取较高值 + 偏移形成弧线
|
||||
const midX = (startLocal.x + endLocal.x) / 2;
|
||||
const midY = Math.max(startLocal.y, endLocal.y) + 150;
|
||||
const midPoint = new Vec3(midX, midY, 0);
|
||||
|
||||
// 阶段1:飞到弧线顶点
|
||||
const halfDuration = PageHome.FLY_DURATION / 2;
|
||||
|
||||
tween(flyNode)
|
||||
.to(halfDuration, { position: midPoint }, { easing: 'quadOut' })
|
||||
.to(halfDuration, { position: endLocal }, { easing: 'quadIn' })
|
||||
// 阶段2:到达弹跳
|
||||
.to(PageHome.BOUNCE_DURATION / 2, { scale: new Vec3(0.4, 0.4, 1) }, { easing: 'quadOut' })
|
||||
.to(PageHome.BOUNCE_DURATION / 2, { scale: new Vec3(0.3, 0.3, 1) }, { easing: 'quadIn' })
|
||||
.call(() => {
|
||||
// 飞行完成 — flyNode 使命结束,立即清理
|
||||
flyNode.destroy();
|
||||
iconLive.active = true;
|
||||
|
||||
// 创建浮动文本
|
||||
this._showFloatText(targetBtn, rootNode);
|
||||
|
||||
// 乐观更新体力数字
|
||||
this._optimisticUpdateStamina();
|
||||
|
||||
// 等浮动文本播完再 resolve(用 setTimeout,不依赖已销毁节点的 tween)
|
||||
setTimeout(() => resolve(), PageHome.FLOAT_DURATION * 1000);
|
||||
})
|
||||
.start();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示浮动提示文本 "体力值-1"
|
||||
* 从按钮位置向上漂移并渐隐
|
||||
*/
|
||||
private _showFloatText(anchorNode: Node, parentNode: Node): void {
|
||||
// 创建文本节点
|
||||
const textNode = new Node('_floatText');
|
||||
textNode.addComponent(UITransform);
|
||||
|
||||
const label = textNode.addComponent(Label);
|
||||
label.string = '体力值-1';
|
||||
label.fontSize = 36;
|
||||
label.lineHeight = 40;
|
||||
label.color = new Color(255, 80, 80, 255);
|
||||
label.isBold = true;
|
||||
|
||||
// 复用 liveLabel 的字体(如果有)
|
||||
if (this.liveLabel?.font) {
|
||||
label.font = this.liveLabel.font;
|
||||
}
|
||||
|
||||
// 添加 UIOpacity 用于渐隐
|
||||
const opacity = textNode.addComponent(UIOpacity);
|
||||
opacity.opacity = 255;
|
||||
|
||||
// 定位到按钮上方
|
||||
const anchorWorldPos = this._getWorldPos(anchorNode);
|
||||
const localPos = this._worldToLocal(anchorWorldPos, parentNode);
|
||||
// 起始位置在按钮上方偏移
|
||||
localPos.y += 120;
|
||||
textNode.setPosition(localPos);
|
||||
|
||||
parentNode.addChild(textNode);
|
||||
|
||||
// 向上漂移 + 渐隐
|
||||
const floatTarget = new Vec3(localPos.x, localPos.y + PageHome.FLOAT_OFFSET_Y, 0);
|
||||
tween(textNode)
|
||||
.to(PageHome.FLOAT_DURATION, { position: floatTarget }, { easing: 'quadOut' })
|
||||
.call(() => {
|
||||
textNode.destroy();
|
||||
})
|
||||
.start();
|
||||
|
||||
tween(opacity)
|
||||
.delay(PageHome.FLOAT_DURATION * 0.3)
|
||||
.to(PageHome.FLOAT_DURATION * 0.7, { opacity: 0 }, { easing: 'quadIn' })
|
||||
.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 乐观更新体力标签(本地 -1 预扣显示)
|
||||
*/
|
||||
private _optimisticUpdateStamina(): void {
|
||||
if (!this.liveLabel) return;
|
||||
|
||||
const stamina = StaminaManager.instance.getStamina();
|
||||
const maxStamina = this._getStaminaMax(stamina);
|
||||
const displayCurrent = Math.max(0, stamina.current - 1);
|
||||
this.liveLabel.string = `${displayCurrent}/${maxStamina}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 页面每次显示时调用
|
||||
*/
|
||||
onViewShow(): void {
|
||||
console.log('[PageHome] onViewShow');
|
||||
this.updateStaminaLabel();
|
||||
|
||||
// 保险恢复:防止动画中途被打断导致 IconLive 隐藏残留
|
||||
const iconLive = this._findIconLive();
|
||||
if (iconLive && !iconLive.active) {
|
||||
iconLive.active = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取体力上限
|
||||
*/
|
||||
private _getStaminaMax(stamina: StaminaInfo): number {
|
||||
return typeof stamina.max === 'number' ? stamina.max : PageHome.DEFAULT_STAMINA_MAX;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新体力值显示
|
||||
*/
|
||||
private updateStaminaLabel(): void {
|
||||
if (this.liveLabel) {
|
||||
const stamina = StaminaManager.instance.getStamina();
|
||||
const maxStamina = this._getStaminaMax(stamina);
|
||||
this.liveLabel.string = `${stamina.current}/${maxStamina}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { _decorator, Node, Button } from 'cc';
|
||||
import { BaseView } from 'db://assets/scripts/core/BaseView';
|
||||
import { ViewManager } from 'db://assets/scripts/core/ViewManager';
|
||||
import { CreatedShareItem } from 'db://assets/scripts/types/ApiTypes';
|
||||
import { ShareManager } from 'db://assets/scripts/utils/ShareManager';
|
||||
import { ToastManager } from 'db://assets/scripts/utils/ToastManager';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass('PagePKData')
|
||||
@@ -8,6 +11,9 @@ export class PagePKData extends BaseView {
|
||||
@property({ type: Node, tooltip: '返回按钮' })
|
||||
backBtn: Node | null = null;
|
||||
|
||||
private _createdShares: CreatedShareItem[] = [];
|
||||
private _isLoading: boolean = false;
|
||||
|
||||
onViewLoad(): void {
|
||||
if (this.backBtn) {
|
||||
this.backBtn.on(Button.EventType.CLICK, this._onBackClick, this);
|
||||
@@ -15,6 +21,7 @@ export class PagePKData extends BaseView {
|
||||
}
|
||||
|
||||
onViewShow(): void {
|
||||
void this._loadCreatedShares();
|
||||
}
|
||||
|
||||
onViewHide(): void {
|
||||
@@ -24,6 +31,26 @@ export class PagePKData extends BaseView {
|
||||
ViewManager.instance.back();
|
||||
}
|
||||
|
||||
private async _loadCreatedShares(): Promise<void> {
|
||||
if (this._isLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._isLoading = true;
|
||||
try {
|
||||
const items = await ShareManager.instance.fetchCreatedShares();
|
||||
if (!items) {
|
||||
ToastManager.instance.show('获取挑战列表失败,请稍后重试');
|
||||
return;
|
||||
}
|
||||
|
||||
this._createdShares = items;
|
||||
console.log('[PagePKData] 我创建的挑战列表:', this._createdShares);
|
||||
} finally {
|
||||
this._isLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
onViewDestroy(): void {
|
||||
if (this.backBtn) {
|
||||
this.backBtn.off(Button.EventType.CLICK, this._onBackClick, this);
|
||||
|
||||
@@ -1,29 +1,22 @@
|
||||
import { _decorator, Node, Button, Sprite, Label, ScrollView, instantiate, UITransform, Vec2 } from 'cc';
|
||||
import { _decorator, Node, Button, Label, ScrollView, instantiate, UITransform } from 'cc';
|
||||
import { BaseView } from 'db://assets/scripts/core/BaseView';
|
||||
import { ViewManager } from 'db://assets/scripts/core/ViewManager';
|
||||
import { LevelDataManager } from 'db://assets/scripts/utils/LevelDataManager';
|
||||
import { CompletedLevelsManager } from 'db://assets/scripts/utils/CompletedLevelsManager';
|
||||
import { PreviewLevelItem } from './PreviewLevelItem';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
/**
|
||||
* 预览试卷页面
|
||||
* 垂直滚动展示用户在 PageWriteLevels 中选中的 6 个关卡
|
||||
* 每个关卡展示:封面图、提示1、提示2、答案
|
||||
* 每个关卡展示:封面图、线索1、线索2、线索3、答案
|
||||
*
|
||||
* prefab 节点结构(已在编辑器中搭建):
|
||||
* 节点结构(仅 ScrollView 侧需要固定):
|
||||
* PagePreviewLevels
|
||||
* ├── Bg
|
||||
* ├── IconBack ← backBtn (返回按钮)
|
||||
* ├── PKTitle ← 标题 "挑战"
|
||||
* ├── ScrollView ← scrollView
|
||||
* │ ├── scrollBar
|
||||
* │ └── view
|
||||
* │ └── content ← listContent
|
||||
* ├── ListTpl ← listTemplate (关卡模板)
|
||||
* │ ├── LevelCover ← Sprite 封面图
|
||||
* │ ├── Tips1 ← Label 提示1
|
||||
* │ ├── Tips2 ← Label 提示2
|
||||
* │ └── Answer ← Label 答案
|
||||
* └── BackButton ← backButton (底部返回按钮)
|
||||
* ├── ScrollView / view / content ← listContent 容器
|
||||
* └── ListTpl ← listTemplate 模板根节点
|
||||
* (挂 PreviewLevelItem 组件,字段由编辑器拖拽绑定)
|
||||
*
|
||||
* item 内部节点层级/命名对本文件透明:所有引用都来自 PreviewLevelItem 的 @property。
|
||||
*/
|
||||
|
||||
/** 布局配置 — 垂直列表 */
|
||||
@@ -197,44 +190,26 @@ export class PagePreviewLevels extends BaseView {
|
||||
* 异步加载关卡数据并填充到 item 节点
|
||||
*/
|
||||
private async _loadLevelData(item: Node, levelIndex: number, displayIndex: number): Promise<void> {
|
||||
const config = await LevelDataManager.instance.ensureLevelReady(levelIndex);
|
||||
if (!config || !item.isValid) return;
|
||||
const level = CompletedLevelsManager.instance.getByIndex(levelIndex);
|
||||
if (!level || !item.isValid) return;
|
||||
|
||||
// 填充封面图
|
||||
const levelCover = item.getChildByName('LevelCover');
|
||||
if (levelCover) {
|
||||
const sprite = levelCover.getComponent(Sprite);
|
||||
if (sprite && config.spriteFrame) {
|
||||
sprite.spriteFrame = config.spriteFrame;
|
||||
}
|
||||
const view = item.getComponent(PreviewLevelItem);
|
||||
if (!view) {
|
||||
console.warn('[PagePreviewLevels] listTemplate 缺少 PreviewLevelItem 组件');
|
||||
return;
|
||||
}
|
||||
|
||||
// 填充提示1
|
||||
const tips1 = item.getChildByName('Tips1');
|
||||
if (tips1) {
|
||||
const label = tips1.getComponent(Label);
|
||||
if (label) {
|
||||
label.string = `线索一:${config.clue1 || ''}`;
|
||||
}
|
||||
}
|
||||
view.setTexts({
|
||||
answer: level.answer || '',
|
||||
hint1: level.hint1 || '',
|
||||
hint2: level.hint2 || '',
|
||||
hint3: level.hint3 || '',
|
||||
});
|
||||
|
||||
// 填充提示2
|
||||
const tips2 = item.getChildByName('Tips2');
|
||||
if (tips2) {
|
||||
const label = tips2.getComponent(Label);
|
||||
if (label) {
|
||||
label.string = `线索二:${config.clue2 || ''}`;
|
||||
}
|
||||
}
|
||||
|
||||
// 填充答案
|
||||
const answer = item.getChildByName('Answer');
|
||||
if (answer) {
|
||||
const label = answer.getComponent(Label);
|
||||
if (label) {
|
||||
label.string = `答案:${config.answer || ''}`;
|
||||
}
|
||||
}
|
||||
// 异步加载封面图(通常已由 WriteLevels 预热到缓存)
|
||||
const spriteFrame = await CompletedLevelsManager.instance.loadImage(level.image1Url);
|
||||
if (!spriteFrame || !item.isValid) return;
|
||||
view.setCover(spriteFrame);
|
||||
}
|
||||
|
||||
// ─── 事件处理 ───────────────────────────────────────
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { _decorator, Node, Button, Sprite, Label, Toggle, ScrollView, EditBox, instantiate, UITransform, Vec2, EventTouch } from 'cc';
|
||||
import { BaseView } from 'db://assets/scripts/core/BaseView';
|
||||
import { ViewManager } from 'db://assets/scripts/core/ViewManager';
|
||||
import { LevelDataManager } from 'db://assets/scripts/utils/LevelDataManager';
|
||||
import { CompletedLevelsManager } from 'db://assets/scripts/utils/CompletedLevelsManager';
|
||||
import { ToastManager } from 'db://assets/scripts/utils/ToastManager';
|
||||
import { ShareManager } from 'db://assets/scripts/utils/ShareManager';
|
||||
import { StorageManager } from 'db://assets/scripts/utils/StorageManager';
|
||||
@@ -9,28 +9,27 @@ import { WxSDK, getUserProfile } from 'db://assets/scripts/utils/WxSDK';
|
||||
import { AuthManager } from 'db://assets/scripts/utils/AuthManager';
|
||||
import { API_ENDPOINTS, API_TIMEOUT } from 'db://assets/scripts/config/ApiConfig';
|
||||
import { HttpUtil } from 'db://assets/scripts/utils/HttpUtil';
|
||||
import { ApiEnvelope } from 'db://assets/scripts/types/ApiTypes';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
/**
|
||||
* 布局配置
|
||||
* 基于实际 prefab 尺寸计算:
|
||||
* ScrollView / view 宽 900,高 1300
|
||||
* view (ScrollView 的可视窗口) 宽 900,高 1000,anchor (0.5, 1) 顶部中点
|
||||
* ListTpl (item) 宽 300,高 400
|
||||
*
|
||||
* 水平居中:2 × 300 + 1 × 40 = 640, padding_left = (900 - 640) / 2 = 130
|
||||
* 垂直居中:3 × 400 + 2 × 25 = 1250, padding_top = (1300 - 1250) / 2 = 25
|
||||
* 每页 2 行 × 2 列 = 4 个关卡
|
||||
* PADDING 不再手写,由 VIEW / ITEM / SPACING 自动派生(见 _getHorizontalPadding / _getVerticalPadding),
|
||||
* 保证 item 网格在 view 内始终居中。改 SPACING 时不用再算 PADDING。
|
||||
*/
|
||||
const LAYOUT_CONFIG = {
|
||||
COLS: 2,
|
||||
ROWS: 3,
|
||||
ROWS: 2,
|
||||
ITEM_WIDTH: 300,
|
||||
ITEM_HEIGHT: 400,
|
||||
SPACING_X: 40,
|
||||
SPACING_Y: 25,
|
||||
PADDING_LEFT: 130,
|
||||
PADDING_TOP: 25,
|
||||
SPACING_X: 160,
|
||||
SPACING_Y: 180,
|
||||
VIEW_WIDTH: 900,
|
||||
VIEW_HEIGHT: 1300,
|
||||
VIEW_HEIGHT: 1000,
|
||||
};
|
||||
|
||||
/** 必须选择的关卡数量 */
|
||||
@@ -140,7 +139,7 @@ export class PageWriteLevels extends BaseView {
|
||||
}
|
||||
}
|
||||
|
||||
private _onTouchStart(event: EventTouch): void {
|
||||
private _onTouchStart(_event: EventTouch): void {
|
||||
if (!this._scrollViewComp) return;
|
||||
this._touchStartOffsetX = this._scrollViewComp.getScrollOffset().x;
|
||||
this._touchStartTime = Date.now();
|
||||
@@ -149,7 +148,7 @@ export class PageWriteLevels extends BaseView {
|
||||
/**
|
||||
* 触摸结束:根据滑动距离和速度决定翻页方向
|
||||
*/
|
||||
private _onTouchEnd(event: EventTouch): void {
|
||||
private _onTouchEnd(_event: EventTouch): void {
|
||||
if (!this._scrollViewComp || this._totalPages <= 1) return;
|
||||
|
||||
const currentOffsetX = this._scrollViewComp.getScrollOffset().x;
|
||||
@@ -176,18 +175,27 @@ export class PageWriteLevels extends BaseView {
|
||||
console.log('[PageWriteLevels] onViewShow');
|
||||
// 仅首次初始化列表,从预览页返回时保留选中状态
|
||||
if (this._itemNodes.length === 0) {
|
||||
this._initLevelList();
|
||||
void this._initLevelList();
|
||||
}
|
||||
}
|
||||
|
||||
private _initLevelList(): void {
|
||||
private async _initLevelList(): Promise<void> {
|
||||
this._clearList();
|
||||
|
||||
this._levelCount = LevelDataManager.instance.getLevelCount();
|
||||
console.log('[PageWriteLevels] 关卡总数:', this._levelCount);
|
||||
// 拉取当前用户所有已通关关卡
|
||||
const levels = await CompletedLevelsManager.instance.fetch();
|
||||
if (levels === null) {
|
||||
console.warn('[PageWriteLevels] 获取已通关关卡失败');
|
||||
ToastManager.instance.show('获取关卡列表失败,请稍后重试');
|
||||
return;
|
||||
}
|
||||
|
||||
this._levelCount = levels.length;
|
||||
console.log('[PageWriteLevels] 已通关关卡总数:', this._levelCount);
|
||||
|
||||
if (this._levelCount === 0) {
|
||||
console.warn('[PageWriteLevels] 没有关卡数据');
|
||||
console.warn('[PageWriteLevels] 用户尚未通关任何关卡');
|
||||
ToastManager.instance.show('还没有已通关的关卡,快去玩几关吧');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -212,6 +220,25 @@ export class PageWriteLevels extends BaseView {
|
||||
this._selectedIndices.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 水平 padding:让整行 item 在 view 宽度内居中
|
||||
* padding_left = (VIEW_WIDTH - cols*ITEM_WIDTH - (cols-1)*SPACING_X) / 2
|
||||
*/
|
||||
private _getHorizontalPadding(): number {
|
||||
const { VIEW_WIDTH, COLS, ITEM_WIDTH, SPACING_X } = LAYOUT_CONFIG;
|
||||
const gridWidth = COLS * ITEM_WIDTH + (COLS - 1) * SPACING_X;
|
||||
return (VIEW_WIDTH - gridWidth) / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* 垂直 padding:让整列 item 在 view 高度内居中
|
||||
*/
|
||||
private _getVerticalPadding(): number {
|
||||
const { VIEW_HEIGHT, ROWS, ITEM_HEIGHT, SPACING_Y } = LAYOUT_CONFIG;
|
||||
const gridHeight = ROWS * ITEM_HEIGHT + (ROWS - 1) * SPACING_Y;
|
||||
return (VIEW_HEIGHT - gridHeight) / 2;
|
||||
}
|
||||
|
||||
private _updateContentSize(): void {
|
||||
if (!this.listContent) return;
|
||||
|
||||
@@ -221,11 +248,12 @@ export class PageWriteLevels extends BaseView {
|
||||
const totalWidth = this._totalPages * LAYOUT_CONFIG.VIEW_WIDTH;
|
||||
contentTransform.setContentSize(totalWidth, LAYOUT_CONFIG.VIEW_HEIGHT);
|
||||
|
||||
// anchor=(0,1),将 content 左上角对齐到 view 左上角
|
||||
// content anchor=(0,1),view anchor=(0.5,1)。
|
||||
// view 本地坐标系下,view 的左上角 = (-viewWidth/2, 0)。
|
||||
// content 的 anchor 点(左上角)需要贴到 view 的左上角。
|
||||
if (this._viewTransform) {
|
||||
const viewWidth = this._viewTransform.contentSize.width;
|
||||
const viewHeight = this._viewTransform.contentSize.height;
|
||||
this.listContent.setPosition(-viewWidth / 2, viewHeight / 2, 0);
|
||||
this.listContent.setPosition(-viewWidth / 2, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,12 +303,15 @@ export class PageWriteLevels extends BaseView {
|
||||
const col = localIndex % LAYOUT_CONFIG.COLS;
|
||||
const row = Math.floor(localIndex / LAYOUT_CONFIG.COLS);
|
||||
|
||||
const paddingLeft = this._getHorizontalPadding();
|
||||
const paddingTop = this._getVerticalPadding();
|
||||
|
||||
const x = pageIndex * LAYOUT_CONFIG.VIEW_WIDTH
|
||||
+ LAYOUT_CONFIG.PADDING_LEFT
|
||||
+ paddingLeft
|
||||
+ col * (LAYOUT_CONFIG.ITEM_WIDTH + LAYOUT_CONFIG.SPACING_X)
|
||||
+ LAYOUT_CONFIG.ITEM_WIDTH / 2;
|
||||
|
||||
const y = -(LAYOUT_CONFIG.PADDING_TOP
|
||||
const y = -(paddingTop
|
||||
+ row * (LAYOUT_CONFIG.ITEM_HEIGHT + LAYOUT_CONFIG.SPACING_Y)
|
||||
+ LAYOUT_CONFIG.ITEM_HEIGHT / 2);
|
||||
|
||||
@@ -291,11 +322,12 @@ export class PageWriteLevels extends BaseView {
|
||||
* 初始化 item 的默认名称和选中状态(不设置封面,由异步加载负责)
|
||||
*/
|
||||
private _initItemState(item: Node, index: number): void {
|
||||
const level = CompletedLevelsManager.instance.getByIndex(index);
|
||||
const levelName = item.getChildByName('LevelName');
|
||||
if (levelName) {
|
||||
const label = levelName.getComponent(Label);
|
||||
if (label) {
|
||||
label.string = `第${index + 1}关`;
|
||||
label.string = level ? `第${level.level}关` : `第${index + 1}关`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,27 +347,20 @@ export class PageWriteLevels extends BaseView {
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步加载关卡资源并刷新封面图和名称。
|
||||
* LevelDataManager 采用懒加载,初始化时只加载了第一关图片,
|
||||
* 其余关卡通过 ensureLevelReady 按需加载。
|
||||
* 异步加载关卡封面图并填充到 item
|
||||
*/
|
||||
private async _loadAndRefreshCover(item: Node, index: number): Promise<void> {
|
||||
const config = await LevelDataManager.instance.ensureLevelReady(index);
|
||||
if (!config || !item.isValid) return;
|
||||
const level = CompletedLevelsManager.instance.getByIndex(index);
|
||||
if (!level || !item.isValid) return;
|
||||
|
||||
const spriteFrame = await CompletedLevelsManager.instance.loadImage(level.image1Url);
|
||||
if (!spriteFrame || !item.isValid) return;
|
||||
|
||||
const levelCover = item.getChildByName('LevelCover');
|
||||
if (levelCover) {
|
||||
const sprite = levelCover.getComponent(Sprite);
|
||||
if (sprite && config.spriteFrame) {
|
||||
sprite.spriteFrame = config.spriteFrame;
|
||||
}
|
||||
}
|
||||
|
||||
const levelName = item.getChildByName('LevelName');
|
||||
if (levelName) {
|
||||
const label = levelName.getComponent(Label);
|
||||
if (label) {
|
||||
label.string = config.name;
|
||||
if (sprite) {
|
||||
sprite.spriteFrame = spriteFrame;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -551,9 +576,9 @@ export class PageWriteLevels extends BaseView {
|
||||
const ids: string[] = [];
|
||||
const sortedIndices = Array.from(this._selectedIndices).sort((a, b) => a - b);
|
||||
for (const index of sortedIndices) {
|
||||
const config = LevelDataManager.instance.getLevelConfig(index);
|
||||
if (config) {
|
||||
ids.push(config.id);
|
||||
const level = CompletedLevelsManager.instance.getByIndex(index);
|
||||
if (level) {
|
||||
ids.push(level.id);
|
||||
}
|
||||
}
|
||||
return ids;
|
||||
@@ -589,7 +614,7 @@ export class PageWriteLevels extends BaseView {
|
||||
StorageManager.setUserInfo(userInfo);
|
||||
|
||||
// 上传到服务端
|
||||
const response = await HttpUtil.post(
|
||||
const response = await HttpUtil.post<ApiEnvelope<unknown>>(
|
||||
API_ENDPOINTS.USER_INFO,
|
||||
{
|
||||
userId: userId,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { _decorator, Node, Label, AudioClip, AudioSource, view, UITransform, Size } from 'cc';
|
||||
import { BaseView } from 'db://assets/scripts/core/BaseView';
|
||||
import { _decorator, Node, Label, AudioClip, AudioSource, view, UITransform, Size, ProgressBar } from 'cc';
|
||||
import { BaseModal } from 'db://assets/scripts/core/BaseModal';
|
||||
import { WxSDK } from 'db://assets/scripts/utils/WxSDK';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@@ -13,12 +13,23 @@ export interface PassModalCallbacks {
|
||||
onShare?: () => void;
|
||||
}
|
||||
|
||||
export interface PassModalTitleInfo {
|
||||
titleText?: string;
|
||||
nextTitleProgress?: number;
|
||||
progressText?: string;
|
||||
}
|
||||
|
||||
interface PassModalParams {
|
||||
levelIndex?: number;
|
||||
titleInfo?: PassModalTitleInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通关弹窗组件
|
||||
* 继承 BaseView,显示通关成功弹窗,提供"下一关"和"分享给好友"两个按钮
|
||||
* 继承 BaseModal,显示通关成功弹窗,提供"下一关"和"分享给好友"两个按钮
|
||||
*/
|
||||
@ccclass('PassModal')
|
||||
export class PassModal extends BaseView {
|
||||
export class PassModal extends BaseModal {
|
||||
/** 静态常量:弹窗层级 */
|
||||
public static readonly MODAL_Z_INDEX = 999;
|
||||
|
||||
@@ -30,9 +41,17 @@ export class PassModal extends BaseView {
|
||||
@property(Node)
|
||||
shareButton: Node | null = null;
|
||||
|
||||
/** 提示Label(如 +1 生命) */
|
||||
/** 称号文字 */
|
||||
@property(Label)
|
||||
tipLabel: Label | null = null;
|
||||
titleLevelLabel: Label | null = null;
|
||||
|
||||
/** 距离下一个称号的进度 */
|
||||
@property(ProgressBar)
|
||||
titleProgressBar: ProgressBar | null = null;
|
||||
|
||||
/** 进度提示文案 */
|
||||
@property(Label)
|
||||
progressLabel: Label | null = null;
|
||||
|
||||
/** 通关音效 */
|
||||
@property(AudioClip)
|
||||
@@ -44,6 +63,21 @@ export class PassModal extends BaseView {
|
||||
/** 缓存的屏幕尺寸 */
|
||||
private _screenSize: Size | null = null;
|
||||
|
||||
/** 称号展示数据 */
|
||||
private _titleInfo: PassModalTitleInfo = {
|
||||
titleText: '冷场小白1级',
|
||||
nextTitleProgress: 0,
|
||||
progressText: '还差3题获得冷场小白2级'
|
||||
};
|
||||
|
||||
setParams(params: PassModalParams): void {
|
||||
super.setParams(params);
|
||||
|
||||
if (params?.titleInfo) {
|
||||
this.setTitleInfo(params.titleInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置回调函数
|
||||
*/
|
||||
@@ -51,6 +85,17 @@ export class PassModal extends BaseView {
|
||||
this._callbacks = callbacks;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置称号体系展示数据
|
||||
*/
|
||||
setTitleInfo(titleInfo: PassModalTitleInfo): void {
|
||||
this._titleInfo = {
|
||||
...this._titleInfo,
|
||||
...titleInfo
|
||||
};
|
||||
this._updateTitleInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* 页面首次加载时调用
|
||||
*/
|
||||
@@ -63,7 +108,9 @@ export class PassModal extends BaseView {
|
||||
* 页面每次显示时调用
|
||||
*/
|
||||
onViewShow(): void {
|
||||
super.onViewShow();
|
||||
this._updateWidget();
|
||||
this._updateTitleInfo();
|
||||
this._playSuccessSound();
|
||||
}
|
||||
|
||||
@@ -129,6 +176,37 @@ export class PassModal extends BaseView {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新称号体系核心变量
|
||||
*/
|
||||
private _updateTitleInfo(): void {
|
||||
if (this.titleLevelLabel && this._titleInfo.titleText !== undefined) {
|
||||
this.titleLevelLabel.string = this._titleInfo.titleText;
|
||||
}
|
||||
|
||||
if (this.titleProgressBar && this._titleInfo.nextTitleProgress !== undefined) {
|
||||
this.titleProgressBar.progress = this._normalizeProgress(this._titleInfo.nextTitleProgress);
|
||||
}
|
||||
|
||||
if (this.progressLabel && this._titleInfo.progressText !== undefined) {
|
||||
this.progressLabel.string = this._titleInfo.progressText;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 规范化进度值
|
||||
* 九宫格 Bar 的 Left+Right border = 240px,totalLength = 925px
|
||||
* 当 width < 240px 时圆角会畸变,因此 progress > 0 时强制最小值
|
||||
*/
|
||||
private _normalizeProgress(progress: number): number {
|
||||
if (!Number.isFinite(progress) || progress <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const MIN_PROGRESS = 240 / 925;
|
||||
return Math.max(MIN_PROGRESS, Math.min(1, progress));
|
||||
}
|
||||
|
||||
/**
|
||||
* 下一关按钮点击
|
||||
*/
|
||||
|
||||
47
assets/prefabs/PreviewLevelItem.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { _decorator, Component, Node, Sprite, Label, SpriteFrame } from 'cc';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
/**
|
||||
* 预览页单个关卡 item 的视图组件
|
||||
* 挂在 PagePreviewLevels 的 listTemplate 根节点上,由编辑器拖拽绑定子节点。
|
||||
* instantiate 克隆 item 时,Cocos 会把这些 Node 引用自动重映射到克隆后的子节点,
|
||||
* 所以每个 item 拿到的都是自己的封面/标签引用,与节点层级/命名解耦。
|
||||
*/
|
||||
@ccclass('PreviewLevelItem')
|
||||
export class PreviewLevelItem extends Component {
|
||||
@property({ type: Sprite, tooltip: '封面图 Sprite' })
|
||||
levelCover: Sprite | null = null;
|
||||
|
||||
@property({ type: Label, tooltip: '答案 Label' })
|
||||
answerLabel: Label | null = null;
|
||||
|
||||
@property({ type: Label, tooltip: '线索1 Label' })
|
||||
tips1Label: Label | null = null;
|
||||
|
||||
@property({ type: Label, tooltip: '线索2 Label' })
|
||||
tips2Label: Label | null = null;
|
||||
|
||||
@property({ type: Label, tooltip: '线索3 Label' })
|
||||
tips3Label: Label | null = null;
|
||||
|
||||
/**
|
||||
* 一次性设置所有文本字段
|
||||
*/
|
||||
setTexts(opts: {
|
||||
answer: string;
|
||||
hint1: string;
|
||||
hint2: string;
|
||||
hint3: string;
|
||||
}): void {
|
||||
if (this.answerLabel) this.answerLabel.string = `答案:${opts.answer}`;
|
||||
if (this.tips1Label) this.tips1Label.string = `线索一:${opts.hint1}`;
|
||||
if (this.tips2Label) this.tips2Label.string = `线索二:${opts.hint2}`;
|
||||
if (this.tips3Label) this.tips3Label.string = `线索三:${opts.hint3}`;
|
||||
}
|
||||
|
||||
setCover(spriteFrame: SpriteFrame | null): void {
|
||||
if (this.levelCover && spriteFrame) {
|
||||
this.levelCover.spriteFrame = spriteFrame;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "ddba71db-75ed-468d-ac99-b4632c0b2ae4",
|
||||
"uuid": "5fb357ec-a670-4fff-a5cd-f47f1a2d8ea3",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
2845
assets/prefabs/TimeoutModal.prefab
Normal file
13
assets/prefabs/TimeoutModal.prefab.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"ver": "1.1.50",
|
||||
"importer": "prefab",
|
||||
"imported": true,
|
||||
"uuid": "e41c722f-f605-47f7-9ce4-abff0ed2020f",
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"syncNodeName": "TimeoutModal"
|
||||
}
|
||||
}
|
||||
162
assets/prefabs/TimeoutModal.ts
Normal file
@@ -0,0 +1,162 @@
|
||||
import { _decorator, Node, view, UITransform, Size } from 'cc';
|
||||
import { BaseModal } from 'db://assets/scripts/core/BaseModal';
|
||||
import { WxSDK } from 'db://assets/scripts/utils/WxSDK';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
/**
|
||||
* TimeoutModal 回调接口
|
||||
*/
|
||||
export interface TimeoutModalCallbacks {
|
||||
/** 点击求助好友回调 */
|
||||
onShare?: () => void;
|
||||
/** 点击再次挑战回调 */
|
||||
onRestart?: () => void;
|
||||
/** 点击返回主页 / 关闭按钮回调 */
|
||||
onHome?: () => void;
|
||||
}
|
||||
|
||||
interface TimeoutModalParams {
|
||||
levelIndex?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 时间耗尽弹窗组件
|
||||
* 继承 BaseModal,显示倒计时结束提示,提供"求助好友"、"再次挑战"和"返回主页"三个按钮
|
||||
*/
|
||||
@ccclass('TimeoutModal')
|
||||
export class TimeoutModal extends BaseModal {
|
||||
/** 静态常量:弹窗层级 */
|
||||
public static readonly MODAL_Z_INDEX = 999;
|
||||
|
||||
/** 关闭按钮 */
|
||||
@property(Node)
|
||||
closeBtn: Node | null = null;
|
||||
|
||||
/** 求助好友按钮 */
|
||||
@property(Node)
|
||||
buttonShare: Node | null = null;
|
||||
|
||||
/** 再次挑战按钮 */
|
||||
@property(Node)
|
||||
buttonRestart: Node | null = null;
|
||||
|
||||
/** 返回主页按钮 */
|
||||
@property(Node)
|
||||
buttonHome: Node | null = null;
|
||||
|
||||
/** 回调函数 */
|
||||
private _callbacks: TimeoutModalCallbacks = {};
|
||||
|
||||
/** 缓存的屏幕尺寸 */
|
||||
private _screenSize: Size | null = null;
|
||||
|
||||
/**
|
||||
* 设置回调函数
|
||||
*/
|
||||
setCallbacks(callbacks: TimeoutModalCallbacks): void {
|
||||
this._callbacks = callbacks;
|
||||
}
|
||||
|
||||
/**
|
||||
* 页面首次加载时调用
|
||||
*/
|
||||
onViewLoad(): void {
|
||||
console.log('[TimeoutModal] onViewLoad');
|
||||
this._bindButtonEvents();
|
||||
}
|
||||
|
||||
/**
|
||||
* 页面每次显示时调用
|
||||
*/
|
||||
onViewShow(): void {
|
||||
super.onViewShow();
|
||||
this._updateWidget();
|
||||
}
|
||||
|
||||
/**
|
||||
* 页面销毁时调用
|
||||
*/
|
||||
onViewDestroy(): void {
|
||||
this._unbindButtonEvents();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置弹窗尺寸为全屏
|
||||
*/
|
||||
private _updateWidget(): void {
|
||||
if (!this._screenSize) {
|
||||
this._screenSize = view.getVisibleSize();
|
||||
}
|
||||
|
||||
const uiTransform = this.node.getComponent(UITransform);
|
||||
if (uiTransform) {
|
||||
uiTransform.setContentSize(this._screenSize.width, this._screenSize.height);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定按钮事件
|
||||
*/
|
||||
private _bindButtonEvents(): void {
|
||||
if (this.closeBtn) {
|
||||
this.closeBtn.on(Node.EventType.TOUCH_END, this._onHomeClick, this);
|
||||
}
|
||||
if (this.buttonShare) {
|
||||
this.buttonShare.on(Node.EventType.TOUCH_END, this._onShareClick, this);
|
||||
}
|
||||
if (this.buttonRestart) {
|
||||
this.buttonRestart.on(Node.EventType.TOUCH_END, this._onRestartClick, this);
|
||||
}
|
||||
if (this.buttonHome) {
|
||||
this.buttonHome.on(Node.EventType.TOUCH_END, this._onHomeClick, this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解除按钮事件绑定
|
||||
*/
|
||||
private _unbindButtonEvents(): void {
|
||||
if (this.closeBtn && this.closeBtn.isValid) {
|
||||
this.closeBtn.off(Node.EventType.TOUCH_END, this._onHomeClick, this);
|
||||
}
|
||||
if (this.buttonShare && this.buttonShare.isValid) {
|
||||
this.buttonShare.off(Node.EventType.TOUCH_END, this._onShareClick, this);
|
||||
}
|
||||
if (this.buttonRestart && this.buttonRestart.isValid) {
|
||||
this.buttonRestart.off(Node.EventType.TOUCH_END, this._onRestartClick, this);
|
||||
}
|
||||
if (this.buttonHome && this.buttonHome.isValid) {
|
||||
this.buttonHome.off(Node.EventType.TOUCH_END, this._onHomeClick, this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 求助好友按钮点击
|
||||
*/
|
||||
private _onShareClick(): void {
|
||||
console.log('[TimeoutModal] 点击求助好友');
|
||||
|
||||
WxSDK.shareAppMessage({
|
||||
title: '这道题太难了,快来帮帮我!',
|
||||
query: `level=${this._params?.levelIndex ?? 1}`
|
||||
});
|
||||
|
||||
this._callbacks.onShare?.();
|
||||
}
|
||||
|
||||
/**
|
||||
* 再次挑战按钮点击
|
||||
*/
|
||||
private _onRestartClick(): void {
|
||||
console.log('[TimeoutModal] 点击再次挑战');
|
||||
this._callbacks.onRestart?.();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回主页 / 关闭按钮点击
|
||||
*/
|
||||
private _onHomeClick(): void {
|
||||
console.log('[TimeoutModal] 点击返回主页');
|
||||
this._callbacks.onHome?.();
|
||||
}
|
||||
}
|
||||
9
assets/prefabs/TimeoutModal.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "bdb18473-6efb-4592-bf67-48555845eec5",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
1776
assets/prefabs/WrongModal.prefab
Normal file
13
assets/prefabs/WrongModal.prefab.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"ver": "1.1.50",
|
||||
"importer": "prefab",
|
||||
"imported": true,
|
||||
"uuid": "455c7845-d090-4cd9-aeb4-1f5cad616bb5",
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"syncNodeName": "WrongModal"
|
||||
}
|
||||
}
|
||||
111
assets/prefabs/WrongModal.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import { _decorator, Node, view, UITransform, Size } from 'cc';
|
||||
import { BaseModal } from 'db://assets/scripts/core/BaseModal';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
/**
|
||||
* WrongModal 回调接口
|
||||
*/
|
||||
export interface WrongModalCallbacks {
|
||||
/** 点击继续挑战 / 关闭按钮回调 */
|
||||
onContinue?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 答案错误弹窗组件
|
||||
* 继承 BaseModal,显示答案错误提示,提供"继续挑战"和关闭按钮
|
||||
*/
|
||||
@ccclass('WrongModal')
|
||||
export class WrongModal extends BaseModal {
|
||||
/** 静态常量:弹窗层级 */
|
||||
public static readonly MODAL_Z_INDEX = 999;
|
||||
|
||||
/** 关闭按钮 */
|
||||
@property(Node)
|
||||
closeBtn: Node | null = null;
|
||||
|
||||
/** 继续挑战按钮 */
|
||||
@property(Node)
|
||||
buttonHint: Node | null = null;
|
||||
|
||||
/** 回调函数 */
|
||||
private _callbacks: WrongModalCallbacks = {};
|
||||
|
||||
/** 缓存的屏幕尺寸 */
|
||||
private _screenSize: Size | null = null;
|
||||
|
||||
/**
|
||||
* 设置回调函数
|
||||
*/
|
||||
setCallbacks(callbacks: WrongModalCallbacks): void {
|
||||
this._callbacks = callbacks;
|
||||
}
|
||||
|
||||
/**
|
||||
* 页面首次加载时调用
|
||||
*/
|
||||
onViewLoad(): void {
|
||||
console.log('[WrongModal] onViewLoad');
|
||||
this._bindButtonEvents();
|
||||
}
|
||||
|
||||
/**
|
||||
* 页面每次显示时调用
|
||||
*/
|
||||
onViewShow(): void {
|
||||
super.onViewShow();
|
||||
this._updateWidget();
|
||||
}
|
||||
|
||||
/**
|
||||
* 页面销毁时调用
|
||||
*/
|
||||
onViewDestroy(): void {
|
||||
this._unbindButtonEvents();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置弹窗尺寸为全屏
|
||||
*/
|
||||
private _updateWidget(): void {
|
||||
if (!this._screenSize) {
|
||||
this._screenSize = view.getVisibleSize();
|
||||
}
|
||||
|
||||
const uiTransform = this.node.getComponent(UITransform);
|
||||
if (uiTransform) {
|
||||
uiTransform.setContentSize(this._screenSize.width, this._screenSize.height);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定按钮事件
|
||||
*/
|
||||
private _bindButtonEvents(): void {
|
||||
if (this.closeBtn) {
|
||||
this.closeBtn.on(Node.EventType.TOUCH_END, this._onContinueClick, this);
|
||||
}
|
||||
if (this.buttonHint) {
|
||||
this.buttonHint.on(Node.EventType.TOUCH_END, this._onContinueClick, this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解除按钮事件绑定
|
||||
*/
|
||||
private _unbindButtonEvents(): void {
|
||||
if (this.closeBtn && this.closeBtn.isValid) {
|
||||
this.closeBtn.off(Node.EventType.TOUCH_END, this._onContinueClick, this);
|
||||
}
|
||||
if (this.buttonHint && this.buttonHint.isValid) {
|
||||
this.buttonHint.off(Node.EventType.TOUCH_END, this._onContinueClick, this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 继续挑战 / 关闭按钮点击
|
||||
*/
|
||||
private _onContinueClick(): void {
|
||||
console.log('[WrongModal] 点击继续挑战');
|
||||
this._callbacks.onContinue?.();
|
||||
}
|
||||
}
|
||||
9
assets/prefabs/WrongModal.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "972c57fc-10e7-493e-a1da-4849f3c1e555",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
BIN
assets/resources/images/BarStam.png
Normal file
|
After Width: | Height: | Size: 5.9 KiB |
134
assets/resources/images/BarStam.png.meta
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"ver": "1.0.27",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "85f4694c-9bb3-4a86-b961-6dbe12154989",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
],
|
||||
"subMetas": {
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "85f4694c-9bb3-4a86-b961-6dbe12154989@6c48a",
|
||||
"displayName": "BarStam",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "85f4694c-9bb3-4a86-b961-6dbe12154989",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
"magfilter": "linear",
|
||||
"mipfilter": "none",
|
||||
"anisotropy": 0
|
||||
},
|
||||
"ver": "1.0.22",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "85f4694c-9bb3-4a86-b961-6dbe12154989@f9941",
|
||||
"displayName": "BarStam",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0.5,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 208,
|
||||
"height": 81,
|
||||
"rawWidth": 208,
|
||||
"rawHeight": 82,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
"pivotY": 0.5,
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-104,
|
||||
-40.5,
|
||||
0,
|
||||
104,
|
||||
-40.5,
|
||||
0,
|
||||
-104,
|
||||
40.5,
|
||||
0,
|
||||
104,
|
||||
40.5,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
3
|
||||
],
|
||||
"uv": [
|
||||
0,
|
||||
82,
|
||||
208,
|
||||
82,
|
||||
0,
|
||||
1,
|
||||
208,
|
||||
1
|
||||
],
|
||||
"nuv": [
|
||||
0,
|
||||
0.012195121951219513,
|
||||
1,
|
||||
0.012195121951219513,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"minPos": [
|
||||
-104,
|
||||
-40.5,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
104,
|
||||
40.5,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "85f4694c-9bb3-4a86-b961-6dbe12154989@6c48a",
|
||||
"atlasUuid": "",
|
||||
"trimType": "auto"
|
||||
},
|
||||
"ver": "1.0.12",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
}
|
||||
},
|
||||
"userData": {
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": false,
|
||||
"hasAlpha": true,
|
||||
"redirect": "85f4694c-9bb3-4a86-b961-6dbe12154989@6c48a"
|
||||
}
|
||||
}
|
||||
BIN
assets/resources/images/Bg.jpg
Normal file
|
After Width: | Height: | Size: 212 KiB |
134
assets/resources/images/Bg.jpg.meta
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"ver": "1.0.27",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "10047f2e-bf2c-46f0-ba98-6fc23cf7ea87",
|
||||
"files": [
|
||||
".jpg",
|
||||
".json"
|
||||
],
|
||||
"subMetas": {
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "10047f2e-bf2c-46f0-ba98-6fc23cf7ea87@6c48a",
|
||||
"displayName": "Bg",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "10047f2e-bf2c-46f0-ba98-6fc23cf7ea87",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
"magfilter": "linear",
|
||||
"mipfilter": "none",
|
||||
"anisotropy": 0
|
||||
},
|
||||
"ver": "1.0.22",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "10047f2e-bf2c-46f0-ba98-6fc23cf7ea87@f9941",
|
||||
"displayName": "Bg",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 1376,
|
||||
"height": 3064,
|
||||
"rawWidth": 1376,
|
||||
"rawHeight": 3064,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
"pivotY": 0.5,
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-688,
|
||||
-1532,
|
||||
0,
|
||||
688,
|
||||
-1532,
|
||||
0,
|
||||
-688,
|
||||
1532,
|
||||
0,
|
||||
688,
|
||||
1532,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
3
|
||||
],
|
||||
"uv": [
|
||||
0,
|
||||
3064,
|
||||
1376,
|
||||
3064,
|
||||
0,
|
||||
0,
|
||||
1376,
|
||||
0
|
||||
],
|
||||
"nuv": [
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"minPos": [
|
||||
-688,
|
||||
-1532,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
688,
|
||||
1532,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "10047f2e-bf2c-46f0-ba98-6fc23cf7ea87@6c48a",
|
||||
"atlasUuid": "",
|
||||
"trimType": "auto"
|
||||
},
|
||||
"ver": "1.0.12",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
}
|
||||
},
|
||||
"userData": {
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": false,
|
||||
"hasAlpha": false,
|
||||
"redirect": "10047f2e-bf2c-46f0-ba98-6fc23cf7ea87@6c48a"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 26 KiB |
BIN
assets/resources/images/ButtonSetting.png
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
134
assets/resources/images/ButtonSetting.png.meta
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"ver": "1.0.27",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "7a8edfce-b0ad-41b3-8dc9-ec1c1ee22153",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
],
|
||||
"subMetas": {
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "7a8edfce-b0ad-41b3-8dc9-ec1c1ee22153@6c48a",
|
||||
"displayName": "ButtonSetting",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "7a8edfce-b0ad-41b3-8dc9-ec1c1ee22153",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
"magfilter": "linear",
|
||||
"mipfilter": "none",
|
||||
"anisotropy": 0
|
||||
},
|
||||
"ver": "1.0.22",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "7a8edfce-b0ad-41b3-8dc9-ec1c1ee22153@f9941",
|
||||
"displayName": "ButtonSetting",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 115,
|
||||
"height": 123,
|
||||
"rawWidth": 115,
|
||||
"rawHeight": 123,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
"pivotY": 0.5,
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-57.5,
|
||||
-61.5,
|
||||
0,
|
||||
57.5,
|
||||
-61.5,
|
||||
0,
|
||||
-57.5,
|
||||
61.5,
|
||||
0,
|
||||
57.5,
|
||||
61.5,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
3
|
||||
],
|
||||
"uv": [
|
||||
0,
|
||||
123,
|
||||
115,
|
||||
123,
|
||||
0,
|
||||
0,
|
||||
115,
|
||||
0
|
||||
],
|
||||
"nuv": [
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"minPos": [
|
||||
-57.5,
|
||||
-61.5,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
57.5,
|
||||
61.5,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "7a8edfce-b0ad-41b3-8dc9-ec1c1ee22153@6c48a",
|
||||
"atlasUuid": "",
|
||||
"trimType": "auto"
|
||||
},
|
||||
"ver": "1.0.12",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
}
|
||||
},
|
||||
"userData": {
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": false,
|
||||
"hasAlpha": true,
|
||||
"redirect": "7a8edfce-b0ad-41b3-8dc9-ec1c1ee22153@6c48a"
|
||||
}
|
||||
}
|
||||
9
assets/resources/images/FlatIcon.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.2.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "a6e360cb-d0cd-4f57-9e43-10b13f6f0d11",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
BIN
assets/resources/images/FlatIcon/flatIconHelp.png
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
@@ -2,7 +2,7 @@
|
||||
"ver": "1.0.27",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "010e6809-3636-4c23-8403-1424c771f0f8",
|
||||
"uuid": "69f85753-6740-4686-ba40-5d2383292f39",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
@@ -10,14 +10,14 @@
|
||||
"subMetas": {
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "010e6809-3636-4c23-8403-1424c771f0f8@6c48a",
|
||||
"displayName": "startGame",
|
||||
"uuid": "69f85753-6740-4686-ba40-5d2383292f39@6c48a",
|
||||
"displayName": "flatIconHelp",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "010e6809-3636-4c23-8403-1424c771f0f8",
|
||||
"imageUuidOrDatabaseUri": "69f85753-6740-4686-ba40-5d2383292f39",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
@@ -34,8 +34,8 @@
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "010e6809-3636-4c23-8403-1424c771f0f8@f9941",
|
||||
"displayName": "startGame",
|
||||
"uuid": "69f85753-6740-4686-ba40-5d2383292f39@f9941",
|
||||
"displayName": "flatIconHelp",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
@@ -45,10 +45,10 @@
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 216,
|
||||
"height": 107,
|
||||
"rawWidth": 216,
|
||||
"rawHeight": 107,
|
||||
"width": 468,
|
||||
"height": 468,
|
||||
"rawWidth": 468,
|
||||
"rawHeight": 468,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
@@ -60,17 +60,17 @@
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-108,
|
||||
-53.5,
|
||||
-234,
|
||||
-234,
|
||||
0,
|
||||
108,
|
||||
-53.5,
|
||||
234,
|
||||
-234,
|
||||
0,
|
||||
-108,
|
||||
53.5,
|
||||
-234,
|
||||
234,
|
||||
0,
|
||||
108,
|
||||
53.5,
|
||||
234,
|
||||
234,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
@@ -83,12 +83,12 @@
|
||||
],
|
||||
"uv": [
|
||||
0,
|
||||
107,
|
||||
216,
|
||||
107,
|
||||
468,
|
||||
468,
|
||||
468,
|
||||
0,
|
||||
0,
|
||||
216,
|
||||
468,
|
||||
0
|
||||
],
|
||||
"nuv": [
|
||||
@@ -102,18 +102,18 @@
|
||||
1
|
||||
],
|
||||
"minPos": [
|
||||
-108,
|
||||
-53.5,
|
||||
-234,
|
||||
-234,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
108,
|
||||
53.5,
|
||||
234,
|
||||
234,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "010e6809-3636-4c23-8403-1424c771f0f8@6c48a",
|
||||
"imageUuidOrDatabaseUri": "69f85753-6740-4686-ba40-5d2383292f39@6c48a",
|
||||
"atlasUuid": "",
|
||||
"trimType": "auto"
|
||||
},
|
||||
@@ -129,6 +129,6 @@
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": false,
|
||||
"hasAlpha": true,
|
||||
"redirect": "010e6809-3636-4c23-8403-1424c771f0f8@6c48a"
|
||||
"redirect": "69f85753-6740-4686-ba40-5d2383292f39@6c48a"
|
||||
}
|
||||
}
|
||||
BIN
assets/resources/images/FlatIcon/flatIconHome.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
134
assets/resources/images/FlatIcon/flatIconHome.png.meta
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"ver": "1.0.27",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "fb73de29-d734-4eb4-853d-8cbdbb18a9c9",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
],
|
||||
"subMetas": {
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "fb73de29-d734-4eb4-853d-8cbdbb18a9c9@6c48a",
|
||||
"displayName": "flatIconHome",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "fb73de29-d734-4eb4-853d-8cbdbb18a9c9",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
"magfilter": "linear",
|
||||
"mipfilter": "none",
|
||||
"anisotropy": 0
|
||||
},
|
||||
"ver": "1.0.22",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "fb73de29-d734-4eb4-853d-8cbdbb18a9c9@f9941",
|
||||
"displayName": "flatIconHome",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 427,
|
||||
"height": 427,
|
||||
"rawWidth": 427,
|
||||
"rawHeight": 427,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
"pivotY": 0.5,
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-213.5,
|
||||
-213.5,
|
||||
0,
|
||||
213.5,
|
||||
-213.5,
|
||||
0,
|
||||
-213.5,
|
||||
213.5,
|
||||
0,
|
||||
213.5,
|
||||
213.5,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
3
|
||||
],
|
||||
"uv": [
|
||||
0,
|
||||
427,
|
||||
427,
|
||||
427,
|
||||
0,
|
||||
0,
|
||||
427,
|
||||
0
|
||||
],
|
||||
"nuv": [
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"minPos": [
|
||||
-213.5,
|
||||
-213.5,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
213.5,
|
||||
213.5,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "fb73de29-d734-4eb4-853d-8cbdbb18a9c9@6c48a",
|
||||
"atlasUuid": "",
|
||||
"trimType": "auto"
|
||||
},
|
||||
"ver": "1.0.12",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
}
|
||||
},
|
||||
"userData": {
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": false,
|
||||
"hasAlpha": true,
|
||||
"redirect": "fb73de29-d734-4eb4-853d-8cbdbb18a9c9@6c48a"
|
||||
}
|
||||
}
|
||||
BIN
assets/resources/images/FlatIcon/flatIconNext.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
134
assets/resources/images/FlatIcon/flatIconNext.png.meta
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"ver": "1.0.27",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "1c8a9e95-7b00-4e59-a0a2-72a4aa97c03c",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
],
|
||||
"subMetas": {
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "1c8a9e95-7b00-4e59-a0a2-72a4aa97c03c@6c48a",
|
||||
"displayName": "flatIconNext",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "1c8a9e95-7b00-4e59-a0a2-72a4aa97c03c",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
"magfilter": "linear",
|
||||
"mipfilter": "none",
|
||||
"anisotropy": 0
|
||||
},
|
||||
"ver": "1.0.22",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "1c8a9e95-7b00-4e59-a0a2-72a4aa97c03c@f9941",
|
||||
"displayName": "flatIconNext",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 13.5,
|
||||
"offsetY": -0.5,
|
||||
"trimX": 82,
|
||||
"trimY": 29,
|
||||
"width": 375,
|
||||
"height": 455,
|
||||
"rawWidth": 512,
|
||||
"rawHeight": 512,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
"pivotY": 0.5,
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-187.5,
|
||||
-227.5,
|
||||
0,
|
||||
187.5,
|
||||
-227.5,
|
||||
0,
|
||||
-187.5,
|
||||
227.5,
|
||||
0,
|
||||
187.5,
|
||||
227.5,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
3
|
||||
],
|
||||
"uv": [
|
||||
82,
|
||||
483,
|
||||
457,
|
||||
483,
|
||||
82,
|
||||
28,
|
||||
457,
|
||||
28
|
||||
],
|
||||
"nuv": [
|
||||
0.16015625,
|
||||
0.0546875,
|
||||
0.892578125,
|
||||
0.0546875,
|
||||
0.16015625,
|
||||
0.943359375,
|
||||
0.892578125,
|
||||
0.943359375
|
||||
],
|
||||
"minPos": [
|
||||
-187.5,
|
||||
-227.5,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
187.5,
|
||||
227.5,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "1c8a9e95-7b00-4e59-a0a2-72a4aa97c03c@6c48a",
|
||||
"atlasUuid": "",
|
||||
"trimType": "auto"
|
||||
},
|
||||
"ver": "1.0.12",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
}
|
||||
},
|
||||
"userData": {
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": false,
|
||||
"hasAlpha": true,
|
||||
"redirect": "1c8a9e95-7b00-4e59-a0a2-72a4aa97c03c@6c48a"
|
||||
}
|
||||
}
|
||||
BIN
assets/resources/images/FlatIcon/flatIconRestart.png
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
134
assets/resources/images/FlatIcon/flatIconRestart.png.meta
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"ver": "1.0.27",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "c24760c9-312a-4638-ae56-6422010d93d0",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
],
|
||||
"subMetas": {
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "c24760c9-312a-4638-ae56-6422010d93d0@6c48a",
|
||||
"displayName": "flatIconRestart",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "c24760c9-312a-4638-ae56-6422010d93d0",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
"magfilter": "linear",
|
||||
"mipfilter": "none",
|
||||
"anisotropy": 0
|
||||
},
|
||||
"ver": "1.0.22",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "c24760c9-312a-4638-ae56-6422010d93d0@f9941",
|
||||
"displayName": "flatIconRestart",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 490,
|
||||
"height": 492,
|
||||
"rawWidth": 490,
|
||||
"rawHeight": 492,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
"pivotY": 0.5,
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-245,
|
||||
-246,
|
||||
0,
|
||||
245,
|
||||
-246,
|
||||
0,
|
||||
-245,
|
||||
246,
|
||||
0,
|
||||
245,
|
||||
246,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
3
|
||||
],
|
||||
"uv": [
|
||||
0,
|
||||
492,
|
||||
490,
|
||||
492,
|
||||
0,
|
||||
0,
|
||||
490,
|
||||
0
|
||||
],
|
||||
"nuv": [
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"minPos": [
|
||||
-245,
|
||||
-246,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
245,
|
||||
246,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "c24760c9-312a-4638-ae56-6422010d93d0@6c48a",
|
||||
"atlasUuid": "",
|
||||
"trimType": "auto"
|
||||
},
|
||||
"ver": "1.0.12",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
}
|
||||
},
|
||||
"userData": {
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": false,
|
||||
"hasAlpha": true,
|
||||
"redirect": "c24760c9-312a-4638-ae56-6422010d93d0@6c48a"
|
||||
}
|
||||
}
|
||||
BIN
assets/resources/images/FlatIcon/flatIconShare.png
Normal file
|
After Width: | Height: | Size: 6.5 KiB |
134
assets/resources/images/FlatIcon/flatIconShare.png.meta
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"ver": "1.0.27",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "b9bec232-3d37-48a0-9ca8-62aab6c4083b",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
],
|
||||
"subMetas": {
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "b9bec232-3d37-48a0-9ca8-62aab6c4083b@6c48a",
|
||||
"displayName": "flatIconShare",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "b9bec232-3d37-48a0-9ca8-62aab6c4083b",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
"magfilter": "linear",
|
||||
"mipfilter": "none",
|
||||
"anisotropy": 0
|
||||
},
|
||||
"ver": "1.0.22",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "b9bec232-3d37-48a0-9ca8-62aab6c4083b@f9941",
|
||||
"displayName": "flatIconShare",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"trimX": 22,
|
||||
"trimY": 11,
|
||||
"width": 468,
|
||||
"height": 490,
|
||||
"rawWidth": 512,
|
||||
"rawHeight": 512,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
"pivotY": 0.5,
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-234,
|
||||
-245,
|
||||
0,
|
||||
234,
|
||||
-245,
|
||||
0,
|
||||
-234,
|
||||
245,
|
||||
0,
|
||||
234,
|
||||
245,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
3
|
||||
],
|
||||
"uv": [
|
||||
22,
|
||||
501,
|
||||
490,
|
||||
501,
|
||||
22,
|
||||
11,
|
||||
490,
|
||||
11
|
||||
],
|
||||
"nuv": [
|
||||
0.04296875,
|
||||
0.021484375,
|
||||
0.95703125,
|
||||
0.021484375,
|
||||
0.04296875,
|
||||
0.978515625,
|
||||
0.95703125,
|
||||
0.978515625
|
||||
],
|
||||
"minPos": [
|
||||
-234,
|
||||
-245,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
234,
|
||||
245,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "b9bec232-3d37-48a0-9ca8-62aab6c4083b@6c48a",
|
||||
"atlasUuid": "",
|
||||
"trimType": "auto"
|
||||
},
|
||||
"ver": "1.0.12",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
}
|
||||
},
|
||||
"userData": {
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": false,
|
||||
"hasAlpha": true,
|
||||
"redirect": "b9bec232-3d37-48a0-9ca8-62aab6c4083b@6c48a"
|
||||
}
|
||||
}
|
||||
BIN
assets/resources/images/FlatIcon/flatIconView.png
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
134
assets/resources/images/FlatIcon/flatIconView.png.meta
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"ver": "1.0.27",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "cf6809c4-f69b-4107-9b50-bd692766213a",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
],
|
||||
"subMetas": {
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "cf6809c4-f69b-4107-9b50-bd692766213a@6c48a",
|
||||
"displayName": "flatIconView",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "cf6809c4-f69b-4107-9b50-bd692766213a",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
"magfilter": "linear",
|
||||
"mipfilter": "none",
|
||||
"anisotropy": 0
|
||||
},
|
||||
"ver": "1.0.22",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "cf6809c4-f69b-4107-9b50-bd692766213a@f9941",
|
||||
"displayName": "flatIconView",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"trimX": 27,
|
||||
"trimY": 80,
|
||||
"width": 458,
|
||||
"height": 352,
|
||||
"rawWidth": 512,
|
||||
"rawHeight": 512,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
"pivotY": 0.5,
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-229,
|
||||
-176,
|
||||
0,
|
||||
229,
|
||||
-176,
|
||||
0,
|
||||
-229,
|
||||
176,
|
||||
0,
|
||||
229,
|
||||
176,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
3
|
||||
],
|
||||
"uv": [
|
||||
27,
|
||||
432,
|
||||
485,
|
||||
432,
|
||||
27,
|
||||
80,
|
||||
485,
|
||||
80
|
||||
],
|
||||
"nuv": [
|
||||
0.052734375,
|
||||
0.15625,
|
||||
0.947265625,
|
||||
0.15625,
|
||||
0.052734375,
|
||||
0.84375,
|
||||
0.947265625,
|
||||
0.84375
|
||||
],
|
||||
"minPos": [
|
||||
-229,
|
||||
-176,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
229,
|
||||
176,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "cf6809c4-f69b-4107-9b50-bd692766213a@6c48a",
|
||||
"atlasUuid": "",
|
||||
"trimType": "auto"
|
||||
},
|
||||
"ver": "1.0.12",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
}
|
||||
},
|
||||
"userData": {
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": false,
|
||||
"hasAlpha": true,
|
||||
"redirect": "cf6809c4-f69b-4107-9b50-bd692766213a@6c48a"
|
||||
}
|
||||
}
|
||||
BIN
assets/resources/images/IconAd.png
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
@@ -2,7 +2,7 @@
|
||||
"ver": "1.0.27",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "a34c93d2-cae0-42d4-a2eb-c3155052ad20",
|
||||
"uuid": "5a832eca-5c6c-4fbc-9218-bf5aae721934",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
@@ -10,14 +10,14 @@
|
||||
"subMetas": {
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "a34c93d2-cae0-42d4-a2eb-c3155052ad20@6c48a",
|
||||
"displayName": "ContentBg",
|
||||
"uuid": "5a832eca-5c6c-4fbc-9218-bf5aae721934@6c48a",
|
||||
"displayName": "IconAd",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "a34c93d2-cae0-42d4-a2eb-c3155052ad20",
|
||||
"imageUuidOrDatabaseUri": "5a832eca-5c6c-4fbc-9218-bf5aae721934",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
@@ -34,8 +34,8 @@
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "a34c93d2-cae0-42d4-a2eb-c3155052ad20@f9941",
|
||||
"displayName": "ContentBg",
|
||||
"uuid": "5a832eca-5c6c-4fbc-9218-bf5aae721934@f9941",
|
||||
"displayName": "IconAd",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
@@ -45,10 +45,10 @@
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 304,
|
||||
"height": 404,
|
||||
"rawWidth": 304,
|
||||
"rawHeight": 404,
|
||||
"width": 63,
|
||||
"height": 49,
|
||||
"rawWidth": 63,
|
||||
"rawHeight": 49,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
@@ -60,17 +60,17 @@
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-152,
|
||||
-202,
|
||||
-31.5,
|
||||
-24.5,
|
||||
0,
|
||||
152,
|
||||
-202,
|
||||
31.5,
|
||||
-24.5,
|
||||
0,
|
||||
-152,
|
||||
202,
|
||||
-31.5,
|
||||
24.5,
|
||||
0,
|
||||
152,
|
||||
202,
|
||||
31.5,
|
||||
24.5,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
@@ -83,12 +83,12 @@
|
||||
],
|
||||
"uv": [
|
||||
0,
|
||||
404,
|
||||
304,
|
||||
404,
|
||||
49,
|
||||
63,
|
||||
49,
|
||||
0,
|
||||
0,
|
||||
304,
|
||||
63,
|
||||
0
|
||||
],
|
||||
"nuv": [
|
||||
@@ -102,18 +102,18 @@
|
||||
1
|
||||
],
|
||||
"minPos": [
|
||||
-152,
|
||||
-202,
|
||||
-31.5,
|
||||
-24.5,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
152,
|
||||
202,
|
||||
31.5,
|
||||
24.5,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "a34c93d2-cae0-42d4-a2eb-c3155052ad20@6c48a",
|
||||
"imageUuidOrDatabaseUri": "5a832eca-5c6c-4fbc-9218-bf5aae721934@6c48a",
|
||||
"atlasUuid": "",
|
||||
"trimType": "auto"
|
||||
},
|
||||
@@ -127,8 +127,8 @@
|
||||
},
|
||||
"userData": {
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": false,
|
||||
"hasAlpha": true,
|
||||
"redirect": "a34c93d2-cae0-42d4-a2eb-c3155052ad20@6c48a"
|
||||
"fixAlphaTransparencyArtifacts": false,
|
||||
"redirect": "5a832eca-5c6c-4fbc-9218-bf5aae721934@6c48a"
|
||||
}
|
||||
}
|
||||
BIN
assets/resources/images/IconStam2.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
134
assets/resources/images/IconStam2.png.meta
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"ver": "1.0.27",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "d18c3099-87cb-4ee0-9778-b3b27468f7b7",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
],
|
||||
"subMetas": {
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "d18c3099-87cb-4ee0-9778-b3b27468f7b7@6c48a",
|
||||
"displayName": "IconStam2",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "d18c3099-87cb-4ee0-9778-b3b27468f7b7",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
"magfilter": "linear",
|
||||
"mipfilter": "none",
|
||||
"anisotropy": 0
|
||||
},
|
||||
"ver": "1.0.22",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "d18c3099-87cb-4ee0-9778-b3b27468f7b7@f9941",
|
||||
"displayName": "IconStam2",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0.5,
|
||||
"trimX": 86,
|
||||
"trimY": 45,
|
||||
"width": 228,
|
||||
"height": 309,
|
||||
"rawWidth": 400,
|
||||
"rawHeight": 400,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
"pivotY": 0.5,
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-114,
|
||||
-154.5,
|
||||
0,
|
||||
114,
|
||||
-154.5,
|
||||
0,
|
||||
-114,
|
||||
154.5,
|
||||
0,
|
||||
114,
|
||||
154.5,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
3
|
||||
],
|
||||
"uv": [
|
||||
86,
|
||||
355,
|
||||
314,
|
||||
355,
|
||||
86,
|
||||
46,
|
||||
314,
|
||||
46
|
||||
],
|
||||
"nuv": [
|
||||
0.215,
|
||||
0.115,
|
||||
0.785,
|
||||
0.115,
|
||||
0.215,
|
||||
0.8875,
|
||||
0.785,
|
||||
0.8875
|
||||
],
|
||||
"minPos": [
|
||||
-114,
|
||||
-154.5,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
114,
|
||||
154.5,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "d18c3099-87cb-4ee0-9778-b3b27468f7b7@6c48a",
|
||||
"atlasUuid": "",
|
||||
"trimType": "auto"
|
||||
},
|
||||
"ver": "1.0.12",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
}
|
||||
},
|
||||
"userData": {
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": false,
|
||||
"hasAlpha": true,
|
||||
"redirect": "d18c3099-87cb-4ee0-9778-b3b27468f7b7@6c48a"
|
||||
}
|
||||
}
|
||||
BIN
assets/resources/images/Plus.png
Normal file
|
After Width: | Height: | Size: 341 B |
134
assets/resources/images/Plus.png.meta
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"ver": "1.0.27",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "a2cba4c9-ed0a-46c4-9184-362cfd0d878d",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
],
|
||||
"subMetas": {
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "a2cba4c9-ed0a-46c4-9184-362cfd0d878d@6c48a",
|
||||
"displayName": "Plus",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "a2cba4c9-ed0a-46c4-9184-362cfd0d878d",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
"magfilter": "linear",
|
||||
"mipfilter": "none",
|
||||
"anisotropy": 0
|
||||
},
|
||||
"ver": "1.0.22",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "a2cba4c9-ed0a-46c4-9184-362cfd0d878d@f9941",
|
||||
"displayName": "Plus",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 26,
|
||||
"height": 26,
|
||||
"rawWidth": 26,
|
||||
"rawHeight": 26,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
"pivotY": 0.5,
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-13,
|
||||
-13,
|
||||
0,
|
||||
13,
|
||||
-13,
|
||||
0,
|
||||
-13,
|
||||
13,
|
||||
0,
|
||||
13,
|
||||
13,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
3
|
||||
],
|
||||
"uv": [
|
||||
0,
|
||||
26,
|
||||
26,
|
||||
26,
|
||||
0,
|
||||
0,
|
||||
26,
|
||||
0
|
||||
],
|
||||
"nuv": [
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"minPos": [
|
||||
-13,
|
||||
-13,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
13,
|
||||
13,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "a2cba4c9-ed0a-46c4-9184-362cfd0d878d@6c48a",
|
||||
"atlasUuid": "",
|
||||
"trimType": "auto"
|
||||
},
|
||||
"ver": "1.0.12",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
}
|
||||
},
|
||||
"userData": {
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": false,
|
||||
"hasAlpha": true,
|
||||
"redirect": "a2cba4c9-ed0a-46c4-9184-362cfd0d878d@6c48a"
|
||||
}
|
||||
}
|
||||
BIN
assets/resources/images/bg_green.jpg
Normal file
|
After Width: | Height: | Size: 117 KiB |
134
assets/resources/images/bg_green.jpg.meta
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"ver": "1.0.27",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "e9f94fb0-2acf-4004-8c6e-023e9deeb9cb",
|
||||
"files": [
|
||||
".jpg",
|
||||
".json"
|
||||
],
|
||||
"subMetas": {
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "e9f94fb0-2acf-4004-8c6e-023e9deeb9cb@6c48a",
|
||||
"displayName": "bg_green",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "e9f94fb0-2acf-4004-8c6e-023e9deeb9cb",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
"magfilter": "linear",
|
||||
"mipfilter": "none",
|
||||
"anisotropy": 0
|
||||
},
|
||||
"ver": "1.0.22",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "e9f94fb0-2acf-4004-8c6e-023e9deeb9cb@f9941",
|
||||
"displayName": "bg_green",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 1376,
|
||||
"height": 3064,
|
||||
"rawWidth": 1376,
|
||||
"rawHeight": 3064,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
"pivotY": 0.5,
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-688,
|
||||
-1532,
|
||||
0,
|
||||
688,
|
||||
-1532,
|
||||
0,
|
||||
-688,
|
||||
1532,
|
||||
0,
|
||||
688,
|
||||
1532,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
3
|
||||
],
|
||||
"uv": [
|
||||
0,
|
||||
3064,
|
||||
1376,
|
||||
3064,
|
||||
0,
|
||||
0,
|
||||
1376,
|
||||
0
|
||||
],
|
||||
"nuv": [
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"minPos": [
|
||||
-688,
|
||||
-1532,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
688,
|
||||
1532,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "e9f94fb0-2acf-4004-8c6e-023e9deeb9cb@6c48a",
|
||||
"atlasUuid": "",
|
||||
"trimType": "auto"
|
||||
},
|
||||
"ver": "1.0.12",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
}
|
||||
},
|
||||
"userData": {
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": false,
|
||||
"hasAlpha": false,
|
||||
"redirect": "e9f94fb0-2acf-4004-8c6e-023e9deeb9cb@6c48a"
|
||||
}
|
||||
}
|
||||
BIN
assets/resources/images/level/BuyButtonBg.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
134
assets/resources/images/level/BuyButtonBg.png.meta
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"ver": "1.0.27",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "39c1c6c3-43dd-4fa1-bbc2-fb394d8cf6d6",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
],
|
||||
"subMetas": {
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "39c1c6c3-43dd-4fa1-bbc2-fb394d8cf6d6@6c48a",
|
||||
"displayName": "BuyButtonBg",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "39c1c6c3-43dd-4fa1-bbc2-fb394d8cf6d6",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
"magfilter": "linear",
|
||||
"mipfilter": "none",
|
||||
"anisotropy": 0
|
||||
},
|
||||
"ver": "1.0.22",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "39c1c6c3-43dd-4fa1-bbc2-fb394d8cf6d6@f9941",
|
||||
"displayName": "BuyButtonBg",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 326,
|
||||
"height": 143,
|
||||
"rawWidth": 326,
|
||||
"rawHeight": 143,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
"pivotY": 0.5,
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-163,
|
||||
-71.5,
|
||||
0,
|
||||
163,
|
||||
-71.5,
|
||||
0,
|
||||
-163,
|
||||
71.5,
|
||||
0,
|
||||
163,
|
||||
71.5,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
3
|
||||
],
|
||||
"uv": [
|
||||
0,
|
||||
143,
|
||||
326,
|
||||
143,
|
||||
0,
|
||||
0,
|
||||
326,
|
||||
0
|
||||
],
|
||||
"nuv": [
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"minPos": [
|
||||
-163,
|
||||
-71.5,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
163,
|
||||
71.5,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "39c1c6c3-43dd-4fa1-bbc2-fb394d8cf6d6@6c48a",
|
||||
"atlasUuid": "",
|
||||
"trimType": "auto"
|
||||
},
|
||||
"ver": "1.0.12",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
}
|
||||
},
|
||||
"userData": {
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": false,
|
||||
"hasAlpha": true,
|
||||
"redirect": "39c1c6c3-43dd-4fa1-bbc2-fb394d8cf6d6@6c48a"
|
||||
}
|
||||
}
|
||||
BIN
assets/resources/images/pageHome/HomeButtonGreen.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
134
assets/resources/images/pageHome/HomeButtonGreen.png.meta
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"ver": "1.0.27",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "02047ea3-2a13-4f17-b4dd-5aefa0e182dc",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
],
|
||||
"subMetas": {
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "02047ea3-2a13-4f17-b4dd-5aefa0e182dc@6c48a",
|
||||
"displayName": "HomeButtonGreen",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "02047ea3-2a13-4f17-b4dd-5aefa0e182dc",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
"magfilter": "linear",
|
||||
"mipfilter": "none",
|
||||
"anisotropy": 0
|
||||
},
|
||||
"ver": "1.0.22",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "02047ea3-2a13-4f17-b4dd-5aefa0e182dc@f9941",
|
||||
"displayName": "HomeButtonGreen",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 488,
|
||||
"height": 197,
|
||||
"rawWidth": 488,
|
||||
"rawHeight": 197,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
"pivotY": 0.5,
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-244,
|
||||
-98.5,
|
||||
0,
|
||||
244,
|
||||
-98.5,
|
||||
0,
|
||||
-244,
|
||||
98.5,
|
||||
0,
|
||||
244,
|
||||
98.5,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
3
|
||||
],
|
||||
"uv": [
|
||||
0,
|
||||
197,
|
||||
488,
|
||||
197,
|
||||
0,
|
||||
0,
|
||||
488,
|
||||
0
|
||||
],
|
||||
"nuv": [
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"minPos": [
|
||||
-244,
|
||||
-98.5,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
244,
|
||||
98.5,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "02047ea3-2a13-4f17-b4dd-5aefa0e182dc@6c48a",
|
||||
"atlasUuid": "",
|
||||
"trimType": "auto"
|
||||
},
|
||||
"ver": "1.0.12",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
}
|
||||
},
|
||||
"userData": {
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": false,
|
||||
"hasAlpha": true,
|
||||
"redirect": "02047ea3-2a13-4f17-b4dd-5aefa0e182dc@6c48a"
|
||||
}
|
||||
}
|
||||
BIN
assets/resources/images/pageHome/HomeButtonOrange.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
134
assets/resources/images/pageHome/HomeButtonOrange.png.meta
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"ver": "1.0.27",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "2270362e-66d7-4d0f-ab51-b5cbca25a768",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
],
|
||||
"subMetas": {
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "2270362e-66d7-4d0f-ab51-b5cbca25a768@6c48a",
|
||||
"displayName": "HomeButtonOrange",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "2270362e-66d7-4d0f-ab51-b5cbca25a768",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
"magfilter": "linear",
|
||||
"mipfilter": "none",
|
||||
"anisotropy": 0
|
||||
},
|
||||
"ver": "1.0.22",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "2270362e-66d7-4d0f-ab51-b5cbca25a768@f9941",
|
||||
"displayName": "HomeButtonOrange",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 488,
|
||||
"height": 197,
|
||||
"rawWidth": 488,
|
||||
"rawHeight": 197,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
"pivotY": 0.5,
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-244,
|
||||
-98.5,
|
||||
0,
|
||||
244,
|
||||
-98.5,
|
||||
0,
|
||||
-244,
|
||||
98.5,
|
||||
0,
|
||||
244,
|
||||
98.5,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
3
|
||||
],
|
||||
"uv": [
|
||||
0,
|
||||
197,
|
||||
488,
|
||||
197,
|
||||
0,
|
||||
0,
|
||||
488,
|
||||
0
|
||||
],
|
||||
"nuv": [
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"minPos": [
|
||||
-244,
|
||||
-98.5,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
244,
|
||||
98.5,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "2270362e-66d7-4d0f-ab51-b5cbca25a768@6c48a",
|
||||
"atlasUuid": "",
|
||||
"trimType": "auto"
|
||||
},
|
||||
"ver": "1.0.12",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
}
|
||||
},
|
||||
"userData": {
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": false,
|
||||
"hasAlpha": true,
|
||||
"redirect": "2270362e-66d7-4d0f-ab51-b5cbca25a768@6c48a"
|
||||
}
|
||||
}
|
||||
BIN
assets/resources/images/pageHome/friendChallenge.png
Normal file
|
After Width: | Height: | Size: 153 KiB |
@@ -2,7 +2,7 @@
|
||||
"ver": "1.0.27",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "729b014d-f0e9-4b67-b99d-177756102d0e",
|
||||
"uuid": "12eb7a16-3f31-40d8-a9cd-30e18900a3a3",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
@@ -10,14 +10,14 @@
|
||||
"subMetas": {
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "729b014d-f0e9-4b67-b99d-177756102d0e@6c48a",
|
||||
"displayName": "IconTips",
|
||||
"uuid": "12eb7a16-3f31-40d8-a9cd-30e18900a3a3@6c48a",
|
||||
"displayName": "friendChallenge",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "729b014d-f0e9-4b67-b99d-177756102d0e",
|
||||
"imageUuidOrDatabaseUri": "12eb7a16-3f31-40d8-a9cd-30e18900a3a3",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
@@ -34,21 +34,21 @@
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "729b014d-f0e9-4b67-b99d-177756102d0e@f9941",
|
||||
"displayName": "IconTips",
|
||||
"uuid": "12eb7a16-3f31-40d8-a9cd-30e18900a3a3@f9941",
|
||||
"displayName": "friendChallenge",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0.5,
|
||||
"offsetY": 0,
|
||||
"trimX": 31,
|
||||
"trimY": 12,
|
||||
"width": 139,
|
||||
"height": 176,
|
||||
"rawWidth": 200,
|
||||
"rawHeight": 200,
|
||||
"offsetX": 0,
|
||||
"offsetY": -8.5,
|
||||
"trimX": 170,
|
||||
"trimY": 243,
|
||||
"width": 684,
|
||||
"height": 555,
|
||||
"rawWidth": 1024,
|
||||
"rawHeight": 1024,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
@@ -60,17 +60,17 @@
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-69.5,
|
||||
-88,
|
||||
-342,
|
||||
-277.5,
|
||||
0,
|
||||
69.5,
|
||||
-88,
|
||||
342,
|
||||
-277.5,
|
||||
0,
|
||||
-69.5,
|
||||
88,
|
||||
-342,
|
||||
277.5,
|
||||
0,
|
||||
69.5,
|
||||
88,
|
||||
342,
|
||||
277.5,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
@@ -82,38 +82,38 @@
|
||||
3
|
||||
],
|
||||
"uv": [
|
||||
31,
|
||||
188,
|
||||
170,
|
||||
188,
|
||||
31,
|
||||
12,
|
||||
781,
|
||||
854,
|
||||
781,
|
||||
170,
|
||||
12
|
||||
226,
|
||||
854,
|
||||
226
|
||||
],
|
||||
"nuv": [
|
||||
0.155,
|
||||
0.06,
|
||||
0.85,
|
||||
0.06,
|
||||
0.155,
|
||||
0.94,
|
||||
0.85,
|
||||
0.94
|
||||
0.166015625,
|
||||
0.220703125,
|
||||
0.833984375,
|
||||
0.220703125,
|
||||
0.166015625,
|
||||
0.7626953125,
|
||||
0.833984375,
|
||||
0.7626953125
|
||||
],
|
||||
"minPos": [
|
||||
-69.5,
|
||||
-88,
|
||||
-342,
|
||||
-277.5,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
69.5,
|
||||
88,
|
||||
342,
|
||||
277.5,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "729b014d-f0e9-4b67-b99d-177756102d0e@6c48a",
|
||||
"imageUuidOrDatabaseUri": "12eb7a16-3f31-40d8-a9cd-30e18900a3a3@6c48a",
|
||||
"atlasUuid": "",
|
||||
"trimType": "auto"
|
||||
},
|
||||
@@ -129,6 +129,6 @@
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": false,
|
||||
"hasAlpha": true,
|
||||
"redirect": "729b014d-f0e9-4b67-b99d-177756102d0e@6c48a"
|
||||
"redirect": "12eb7a16-3f31-40d8-a9cd-30e18900a3a3@6c48a"
|
||||
}
|
||||
}
|
||||
BIN
assets/resources/images/pageHome/homeIconChallenge.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
@@ -2,7 +2,7 @@
|
||||
"ver": "1.0.27",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "d532045e-55f8-47c2-9493-b918e18364b0",
|
||||
"uuid": "894c2169-ec35-4aeb-b656-3caa09604b09",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
@@ -10,14 +10,14 @@
|
||||
"subMetas": {
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "d532045e-55f8-47c2-9493-b918e18364b0@6c48a",
|
||||
"displayName": "Bg",
|
||||
"uuid": "894c2169-ec35-4aeb-b656-3caa09604b09@6c48a",
|
||||
"displayName": "homeIconChallenge",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "d532045e-55f8-47c2-9493-b918e18364b0",
|
||||
"imageUuidOrDatabaseUri": "894c2169-ec35-4aeb-b656-3caa09604b09",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
@@ -34,8 +34,8 @@
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "d532045e-55f8-47c2-9493-b918e18364b0@f9941",
|
||||
"displayName": "Bg",
|
||||
"uuid": "894c2169-ec35-4aeb-b656-3caa09604b09@f9941",
|
||||
"displayName": "homeIconChallenge",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
@@ -45,10 +45,10 @@
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 750,
|
||||
"height": 1626,
|
||||
"rawWidth": 750,
|
||||
"rawHeight": 1626,
|
||||
"width": 375,
|
||||
"height": 183,
|
||||
"rawWidth": 375,
|
||||
"rawHeight": 183,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
@@ -60,17 +60,17 @@
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-375,
|
||||
-813,
|
||||
-187.5,
|
||||
-91.5,
|
||||
0,
|
||||
375,
|
||||
-813,
|
||||
187.5,
|
||||
-91.5,
|
||||
0,
|
||||
-375,
|
||||
813,
|
||||
-187.5,
|
||||
91.5,
|
||||
0,
|
||||
375,
|
||||
813,
|
||||
187.5,
|
||||
91.5,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
@@ -83,12 +83,12 @@
|
||||
],
|
||||
"uv": [
|
||||
0,
|
||||
1626,
|
||||
750,
|
||||
1626,
|
||||
183,
|
||||
375,
|
||||
183,
|
||||
0,
|
||||
0,
|
||||
750,
|
||||
375,
|
||||
0
|
||||
],
|
||||
"nuv": [
|
||||
@@ -102,18 +102,18 @@
|
||||
1
|
||||
],
|
||||
"minPos": [
|
||||
-375,
|
||||
-813,
|
||||
-187.5,
|
||||
-91.5,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
375,
|
||||
813,
|
||||
187.5,
|
||||
91.5,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "d532045e-55f8-47c2-9493-b918e18364b0@6c48a",
|
||||
"imageUuidOrDatabaseUri": "894c2169-ec35-4aeb-b656-3caa09604b09@6c48a",
|
||||
"atlasUuid": "",
|
||||
"trimType": "auto"
|
||||
},
|
||||
@@ -129,6 +129,6 @@
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": false,
|
||||
"hasAlpha": true,
|
||||
"redirect": "d532045e-55f8-47c2-9493-b918e18364b0@6c48a"
|
||||
"redirect": "894c2169-ec35-4aeb-b656-3caa09604b09@6c48a"
|
||||
}
|
||||
}
|
||||
BIN
assets/resources/images/pageHome/homeIconCreate.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
134
assets/resources/images/pageHome/homeIconCreate.png.meta
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"ver": "1.0.27",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "0253c893-2588-492f-9d96-911878b8a673",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
],
|
||||
"subMetas": {
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "0253c893-2588-492f-9d96-911878b8a673@6c48a",
|
||||
"displayName": "homeIconCreate",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "0253c893-2588-492f-9d96-911878b8a673",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
"magfilter": "linear",
|
||||
"mipfilter": "none",
|
||||
"anisotropy": 0
|
||||
},
|
||||
"ver": "1.0.22",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "0253c893-2588-492f-9d96-911878b8a673@f9941",
|
||||
"displayName": "homeIconCreate",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": -0.5,
|
||||
"trimX": 0,
|
||||
"trimY": 1,
|
||||
"width": 373,
|
||||
"height": 232,
|
||||
"rawWidth": 373,
|
||||
"rawHeight": 233,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
"pivotY": 0.5,
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-186.5,
|
||||
-116,
|
||||
0,
|
||||
186.5,
|
||||
-116,
|
||||
0,
|
||||
-186.5,
|
||||
116,
|
||||
0,
|
||||
186.5,
|
||||
116,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
3
|
||||
],
|
||||
"uv": [
|
||||
0,
|
||||
232,
|
||||
373,
|
||||
232,
|
||||
0,
|
||||
0,
|
||||
373,
|
||||
0
|
||||
],
|
||||
"nuv": [
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0.9957081545064378,
|
||||
1,
|
||||
0.9957081545064378
|
||||
],
|
||||
"minPos": [
|
||||
-186.5,
|
||||
-116,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
186.5,
|
||||
116,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "0253c893-2588-492f-9d96-911878b8a673@6c48a",
|
||||
"atlasUuid": "",
|
||||
"trimType": "auto"
|
||||
},
|
||||
"ver": "1.0.12",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
}
|
||||
},
|
||||
"userData": {
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": false,
|
||||
"hasAlpha": true,
|
||||
"redirect": "0253c893-2588-492f-9d96-911878b8a673@6c48a"
|
||||
}
|
||||
}
|
||||
BIN
assets/resources/images/pageHome/homeIconFriend.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
134
assets/resources/images/pageHome/homeIconFriend.png.meta
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"ver": "1.0.27",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "a85c793d-50ce-4db1-ac92-2af76b8aa505",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
],
|
||||
"subMetas": {
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "a85c793d-50ce-4db1-ac92-2af76b8aa505@6c48a",
|
||||
"displayName": "homeIconFriend",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "a85c793d-50ce-4db1-ac92-2af76b8aa505",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
"magfilter": "linear",
|
||||
"mipfilter": "none",
|
||||
"anisotropy": 0
|
||||
},
|
||||
"ver": "1.0.22",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "a85c793d-50ce-4db1-ac92-2af76b8aa505@f9941",
|
||||
"displayName": "homeIconFriend",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 345,
|
||||
"height": 260,
|
||||
"rawWidth": 345,
|
||||
"rawHeight": 260,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
"pivotY": 0.5,
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-172.5,
|
||||
-130,
|
||||
0,
|
||||
172.5,
|
||||
-130,
|
||||
0,
|
||||
-172.5,
|
||||
130,
|
||||
0,
|
||||
172.5,
|
||||
130,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
3
|
||||
],
|
||||
"uv": [
|
||||
0,
|
||||
260,
|
||||
345,
|
||||
260,
|
||||
0,
|
||||
0,
|
||||
345,
|
||||
0
|
||||
],
|
||||
"nuv": [
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"minPos": [
|
||||
-172.5,
|
||||
-130,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
172.5,
|
||||
130,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "a85c793d-50ce-4db1-ac92-2af76b8aa505@6c48a",
|
||||
"atlasUuid": "",
|
||||
"trimType": "auto"
|
||||
},
|
||||
"ver": "1.0.12",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
}
|
||||
},
|
||||
"userData": {
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": false,
|
||||
"hasAlpha": true,
|
||||
"redirect": "a85c793d-50ce-4db1-ac92-2af76b8aa505@6c48a"
|
||||
}
|
||||
}
|
||||
BIN
assets/resources/images/pageHome/homeIconGift.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
134
assets/resources/images/pageHome/homeIconGift.png.meta
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"ver": "1.0.27",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "496f93ae-424d-427d-b3eb-54e47e336fc8",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
],
|
||||
"subMetas": {
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "496f93ae-424d-427d-b3eb-54e47e336fc8@6c48a",
|
||||
"displayName": "homeIconGift",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "496f93ae-424d-427d-b3eb-54e47e336fc8",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
"magfilter": "linear",
|
||||
"mipfilter": "none",
|
||||
"anisotropy": 0
|
||||
},
|
||||
"ver": "1.0.22",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "496f93ae-424d-427d-b3eb-54e47e336fc8@f9941",
|
||||
"displayName": "homeIconGift",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 283,
|
||||
"height": 285,
|
||||
"rawWidth": 283,
|
||||
"rawHeight": 285,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
"pivotY": 0.5,
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-141.5,
|
||||
-142.5,
|
||||
0,
|
||||
141.5,
|
||||
-142.5,
|
||||
0,
|
||||
-141.5,
|
||||
142.5,
|
||||
0,
|
||||
141.5,
|
||||
142.5,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
3
|
||||
],
|
||||
"uv": [
|
||||
0,
|
||||
285,
|
||||
283,
|
||||
285,
|
||||
0,
|
||||
0,
|
||||
283,
|
||||
0
|
||||
],
|
||||
"nuv": [
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"minPos": [
|
||||
-141.5,
|
||||
-142.5,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
141.5,
|
||||
142.5,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "496f93ae-424d-427d-b3eb-54e47e336fc8@6c48a",
|
||||
"atlasUuid": "",
|
||||
"trimType": "auto"
|
||||
},
|
||||
"ver": "1.0.12",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
}
|
||||
},
|
||||
"userData": {
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": false,
|
||||
"hasAlpha": true,
|
||||
"redirect": "496f93ae-424d-427d-b3eb-54e47e336fc8@6c48a"
|
||||
}
|
||||
}
|
||||
BIN
assets/resources/images/pageHome/homeIconRank.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
134
assets/resources/images/pageHome/homeIconRank.png.meta
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"ver": "1.0.27",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "6cc6bad3-00c6-4416-b48f-0434317d96c2",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
],
|
||||
"subMetas": {
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "6cc6bad3-00c6-4416-b48f-0434317d96c2@6c48a",
|
||||
"displayName": "homeIconRank",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "6cc6bad3-00c6-4416-b48f-0434317d96c2",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
"magfilter": "linear",
|
||||
"mipfilter": "none",
|
||||
"anisotropy": 0
|
||||
},
|
||||
"ver": "1.0.22",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "6cc6bad3-00c6-4416-b48f-0434317d96c2@f9941",
|
||||
"displayName": "homeIconRank",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": -0.5,
|
||||
"trimX": 0,
|
||||
"trimY": 1,
|
||||
"width": 346,
|
||||
"height": 307,
|
||||
"rawWidth": 346,
|
||||
"rawHeight": 308,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
"pivotY": 0.5,
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-173,
|
||||
-153.5,
|
||||
0,
|
||||
173,
|
||||
-153.5,
|
||||
0,
|
||||
-173,
|
||||
153.5,
|
||||
0,
|
||||
173,
|
||||
153.5,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
3
|
||||
],
|
||||
"uv": [
|
||||
0,
|
||||
307,
|
||||
346,
|
||||
307,
|
||||
0,
|
||||
0,
|
||||
346,
|
||||
0
|
||||
],
|
||||
"nuv": [
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0.9967532467532467,
|
||||
1,
|
||||
0.9967532467532467
|
||||
],
|
||||
"minPos": [
|
||||
-173,
|
||||
-153.5,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
173,
|
||||
153.5,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "6cc6bad3-00c6-4416-b48f-0434317d96c2@6c48a",
|
||||
"atlasUuid": "",
|
||||
"trimType": "auto"
|
||||
},
|
||||
"ver": "1.0.12",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
}
|
||||
},
|
||||
"userData": {
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": false,
|
||||
"hasAlpha": true,
|
||||
"redirect": "6cc6bad3-00c6-4416-b48f-0434317d96c2@6c48a"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 38 KiB |
BIN
assets/resources/images/pageLevel/1_0022_Layer-19.png
Normal file
|
After Width: | Height: | Size: 178 KiB |
134
assets/resources/images/pageLevel/1_0022_Layer-19.png.meta
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"ver": "1.0.27",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "3ef3f86f-bc1f-49b7-a50f-0e8683cd2400",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
],
|
||||
"subMetas": {
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "3ef3f86f-bc1f-49b7-a50f-0e8683cd2400@6c48a",
|
||||
"displayName": "1_0022_Layer-19",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "3ef3f86f-bc1f-49b7-a50f-0e8683cd2400",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
"magfilter": "linear",
|
||||
"mipfilter": "none",
|
||||
"anisotropy": 0
|
||||
},
|
||||
"ver": "1.0.22",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "3ef3f86f-bc1f-49b7-a50f-0e8683cd2400@f9941",
|
||||
"displayName": "1_0022_Layer-19",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 1297,
|
||||
"height": 1004,
|
||||
"rawWidth": 1297,
|
||||
"rawHeight": 1004,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
"pivotY": 0.5,
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-648.5,
|
||||
-502,
|
||||
0,
|
||||
648.5,
|
||||
-502,
|
||||
0,
|
||||
-648.5,
|
||||
502,
|
||||
0,
|
||||
648.5,
|
||||
502,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
3
|
||||
],
|
||||
"uv": [
|
||||
0,
|
||||
1004,
|
||||
1297,
|
||||
1004,
|
||||
0,
|
||||
0,
|
||||
1297,
|
||||
0
|
||||
],
|
||||
"nuv": [
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"minPos": [
|
||||
-648.5,
|
||||
-502,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
648.5,
|
||||
502,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "3ef3f86f-bc1f-49b7-a50f-0e8683cd2400@6c48a",
|
||||
"atlasUuid": "",
|
||||
"trimType": "auto"
|
||||
},
|
||||
"ver": "1.0.12",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
}
|
||||
},
|
||||
"userData": {
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": false,
|
||||
"hasAlpha": true,
|
||||
"redirect": "3ef3f86f-bc1f-49b7-a50f-0e8683cd2400@6c48a"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 1.3 KiB |
BIN
assets/resources/images/pageLevel/ButtonGreen.png
Normal file
|
After Width: | Height: | Size: 53 KiB |
134
assets/resources/images/pageLevel/ButtonGreen.png.meta
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"ver": "1.0.27",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "f87d228a-c520-499a-bf3a-e66cbb6def64",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
],
|
||||
"subMetas": {
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "f87d228a-c520-499a-bf3a-e66cbb6def64@6c48a",
|
||||
"displayName": "ButtonGreen",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "f87d228a-c520-499a-bf3a-e66cbb6def64",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
"magfilter": "linear",
|
||||
"mipfilter": "none",
|
||||
"anisotropy": 0
|
||||
},
|
||||
"ver": "1.0.22",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "f87d228a-c520-499a-bf3a-e66cbb6def64@f9941",
|
||||
"displayName": "ButtonGreen",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 666,
|
||||
"height": 235,
|
||||
"rawWidth": 666,
|
||||
"rawHeight": 235,
|
||||
"borderTop": 120,
|
||||
"borderBottom": 115,
|
||||
"borderLeft": 120,
|
||||
"borderRight": 120,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
"pivotY": 0.5,
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-333,
|
||||
-117.5,
|
||||
0,
|
||||
333,
|
||||
-117.5,
|
||||
0,
|
||||
-333,
|
||||
117.5,
|
||||
0,
|
||||
333,
|
||||
117.5,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
3
|
||||
],
|
||||
"uv": [
|
||||
0,
|
||||
235,
|
||||
666,
|
||||
235,
|
||||
0,
|
||||
0,
|
||||
666,
|
||||
0
|
||||
],
|
||||
"nuv": [
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"minPos": [
|
||||
-333,
|
||||
-117.5,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
333,
|
||||
117.5,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "f87d228a-c520-499a-bf3a-e66cbb6def64@6c48a",
|
||||
"atlasUuid": "",
|
||||
"trimType": "none"
|
||||
},
|
||||
"ver": "1.0.12",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
}
|
||||
},
|
||||
"userData": {
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": false,
|
||||
"hasAlpha": true,
|
||||
"redirect": "f87d228a-c520-499a-bf3a-e66cbb6def64@6c48a"
|
||||
}
|
||||
}
|
||||
BIN
assets/resources/images/pageLevel/ButtonOrange.png
Normal file
|
After Width: | Height: | Size: 116 KiB |
134
assets/resources/images/pageLevel/ButtonOrange.png.meta
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"ver": "1.0.27",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "0366f161-d78c-43b1-b2cb-8d0666dd2fbd",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
],
|
||||
"subMetas": {
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "0366f161-d78c-43b1-b2cb-8d0666dd2fbd@6c48a",
|
||||
"displayName": "ButtonOrange",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "0366f161-d78c-43b1-b2cb-8d0666dd2fbd",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
"magfilter": "linear",
|
||||
"mipfilter": "none",
|
||||
"anisotropy": 0
|
||||
},
|
||||
"ver": "1.0.22",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "0366f161-d78c-43b1-b2cb-8d0666dd2fbd@f9941",
|
||||
"displayName": "ButtonOrange",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 666,
|
||||
"height": 235,
|
||||
"rawWidth": 666,
|
||||
"rawHeight": 235,
|
||||
"borderTop": 112,
|
||||
"borderBottom": 112,
|
||||
"borderLeft": 112,
|
||||
"borderRight": 112,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
"pivotY": 0.5,
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-333,
|
||||
-117.5,
|
||||
0,
|
||||
333,
|
||||
-117.5,
|
||||
0,
|
||||
-333,
|
||||
117.5,
|
||||
0,
|
||||
333,
|
||||
117.5,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
3
|
||||
],
|
||||
"uv": [
|
||||
0,
|
||||
235,
|
||||
666,
|
||||
235,
|
||||
0,
|
||||
0,
|
||||
666,
|
||||
0
|
||||
],
|
||||
"nuv": [
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"minPos": [
|
||||
-333,
|
||||
-117.5,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
333,
|
||||
117.5,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "0366f161-d78c-43b1-b2cb-8d0666dd2fbd@6c48a",
|
||||
"atlasUuid": "",
|
||||
"trimType": "auto"
|
||||
},
|
||||
"ver": "1.0.12",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
}
|
||||
},
|
||||
"userData": {
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": false,
|
||||
"hasAlpha": true,
|
||||
"redirect": "0366f161-d78c-43b1-b2cb-8d0666dd2fbd@6c48a"
|
||||
}
|
||||
}
|
||||
BIN
assets/resources/images/pageLevel/ButtonYellow.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
134
assets/resources/images/pageLevel/ButtonYellow.png.meta
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"ver": "1.0.27",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "ba58112d-788c-465d-a2f3-46b468026d03",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
],
|
||||
"subMetas": {
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "ba58112d-788c-465d-a2f3-46b468026d03@6c48a",
|
||||
"displayName": "ButtonYellow",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "ba58112d-788c-465d-a2f3-46b468026d03",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
"magfilter": "linear",
|
||||
"mipfilter": "none",
|
||||
"anisotropy": 0
|
||||
},
|
||||
"ver": "1.0.22",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "ba58112d-788c-465d-a2f3-46b468026d03@f9941",
|
||||
"displayName": "ButtonYellow",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": -0.5,
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 665,
|
||||
"height": 235,
|
||||
"rawWidth": 666,
|
||||
"rawHeight": 235,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
"pivotY": 0.5,
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-332.5,
|
||||
-117.5,
|
||||
0,
|
||||
332.5,
|
||||
-117.5,
|
||||
0,
|
||||
-332.5,
|
||||
117.5,
|
||||
0,
|
||||
332.5,
|
||||
117.5,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
3
|
||||
],
|
||||
"uv": [
|
||||
0,
|
||||
235,
|
||||
665,
|
||||
235,
|
||||
0,
|
||||
0,
|
||||
665,
|
||||
0
|
||||
],
|
||||
"nuv": [
|
||||
0,
|
||||
0,
|
||||
0.9984984984984985,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0.9984984984984985,
|
||||
1
|
||||
],
|
||||
"minPos": [
|
||||
-332.5,
|
||||
-117.5,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
332.5,
|
||||
117.5,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "ba58112d-788c-465d-a2f3-46b468026d03@6c48a",
|
||||
"atlasUuid": "",
|
||||
"trimType": "auto"
|
||||
},
|
||||
"ver": "1.0.12",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
}
|
||||
},
|
||||
"userData": {
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": false,
|
||||
"hasAlpha": true,
|
||||
"redirect": "ba58112d-788c-465d-a2f3-46b468026d03@6c48a"
|
||||
}
|
||||
}
|
||||
BIN
assets/resources/images/pageLevel/CharBlock.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
@@ -2,7 +2,7 @@
|
||||
"ver": "1.0.27",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "e1267c1b-ceb0-4483-b36d-bc9cb4d2fd26",
|
||||
"uuid": "31d2998e-4261-4284-bbcd-9a34ad9c73a6",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
@@ -10,14 +10,14 @@
|
||||
"subMetas": {
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "e1267c1b-ceb0-4483-b36d-bc9cb4d2fd26@6c48a",
|
||||
"displayName": "ButtonBg",
|
||||
"uuid": "31d2998e-4261-4284-bbcd-9a34ad9c73a6@6c48a",
|
||||
"displayName": "CharBlock",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "e1267c1b-ceb0-4483-b36d-bc9cb4d2fd26",
|
||||
"imageUuidOrDatabaseUri": "31d2998e-4261-4284-bbcd-9a34ad9c73a6",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
@@ -34,8 +34,8 @@
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "e1267c1b-ceb0-4483-b36d-bc9cb4d2fd26@f9941",
|
||||
"displayName": "ButtonBg",
|
||||
"uuid": "31d2998e-4261-4284-bbcd-9a34ad9c73a6@f9941",
|
||||
"displayName": "CharBlock",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
@@ -44,11 +44,11 @@
|
||||
"offsetX": 0,
|
||||
"offsetY": 0.5,
|
||||
"trimX": 1,
|
||||
"trimY": 0,
|
||||
"width": 306,
|
||||
"height": 77,
|
||||
"rawWidth": 308,
|
||||
"rawHeight": 78,
|
||||
"trimY": 1,
|
||||
"width": 177,
|
||||
"height": 209,
|
||||
"rawWidth": 179,
|
||||
"rawHeight": 212,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
@@ -60,17 +60,17 @@
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-153,
|
||||
-38.5,
|
||||
-88.5,
|
||||
-104.5,
|
||||
0,
|
||||
153,
|
||||
-38.5,
|
||||
88.5,
|
||||
-104.5,
|
||||
0,
|
||||
-153,
|
||||
38.5,
|
||||
-88.5,
|
||||
104.5,
|
||||
0,
|
||||
153,
|
||||
38.5,
|
||||
88.5,
|
||||
104.5,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
@@ -83,37 +83,37 @@
|
||||
],
|
||||
"uv": [
|
||||
1,
|
||||
78,
|
||||
307,
|
||||
78,
|
||||
211,
|
||||
178,
|
||||
211,
|
||||
1,
|
||||
1,
|
||||
307,
|
||||
1
|
||||
2,
|
||||
178,
|
||||
2
|
||||
],
|
||||
"nuv": [
|
||||
0.003246753246753247,
|
||||
0.01282051282051282,
|
||||
0.9967532467532467,
|
||||
0.01282051282051282,
|
||||
0.003246753246753247,
|
||||
1,
|
||||
0.9967532467532467,
|
||||
1
|
||||
0.00558659217877095,
|
||||
0.009433962264150943,
|
||||
0.994413407821229,
|
||||
0.009433962264150943,
|
||||
0.00558659217877095,
|
||||
0.9952830188679245,
|
||||
0.994413407821229,
|
||||
0.9952830188679245
|
||||
],
|
||||
"minPos": [
|
||||
-153,
|
||||
-38.5,
|
||||
-88.5,
|
||||
-104.5,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
153,
|
||||
38.5,
|
||||
88.5,
|
||||
104.5,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "e1267c1b-ceb0-4483-b36d-bc9cb4d2fd26@6c48a",
|
||||
"imageUuidOrDatabaseUri": "31d2998e-4261-4284-bbcd-9a34ad9c73a6@6c48a",
|
||||
"atlasUuid": "",
|
||||
"trimType": "auto"
|
||||
},
|
||||
@@ -129,6 +129,6 @@
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": false,
|
||||
"hasAlpha": true,
|
||||
"redirect": "e1267c1b-ceb0-4483-b36d-bc9cb4d2fd26@6c48a"
|
||||
"redirect": "31d2998e-4261-4284-bbcd-9a34ad9c73a6@6c48a"
|
||||
}
|
||||
}
|
||||
BIN
assets/resources/images/pageLevel/CharBlock2.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
134
assets/resources/images/pageLevel/CharBlock2.png.meta
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"ver": "1.0.27",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "88cc67ba-1a68-4e04-99de-db6fd60fe3d6",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
],
|
||||
"subMetas": {
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "88cc67ba-1a68-4e04-99de-db6fd60fe3d6@6c48a",
|
||||
"displayName": "CharBlock2",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "88cc67ba-1a68-4e04-99de-db6fd60fe3d6",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
"magfilter": "linear",
|
||||
"mipfilter": "none",
|
||||
"anisotropy": 0
|
||||
},
|
||||
"ver": "1.0.22",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "88cc67ba-1a68-4e04-99de-db6fd60fe3d6@f9941",
|
||||
"displayName": "CharBlock2",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 178,
|
||||
"height": 186,
|
||||
"rawWidth": 178,
|
||||
"rawHeight": 186,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
"pivotY": 0.5,
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-89,
|
||||
-93,
|
||||
0,
|
||||
89,
|
||||
-93,
|
||||
0,
|
||||
-89,
|
||||
93,
|
||||
0,
|
||||
89,
|
||||
93,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
3
|
||||
],
|
||||
"uv": [
|
||||
0,
|
||||
186,
|
||||
178,
|
||||
186,
|
||||
0,
|
||||
0,
|
||||
178,
|
||||
0
|
||||
],
|
||||
"nuv": [
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"minPos": [
|
||||
-89,
|
||||
-93,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
89,
|
||||
93,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "88cc67ba-1a68-4e04-99de-db6fd60fe3d6@6c48a",
|
||||
"atlasUuid": "",
|
||||
"trimType": "auto"
|
||||
},
|
||||
"ver": "1.0.12",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
}
|
||||
},
|
||||
"userData": {
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": false,
|
||||
"hasAlpha": true,
|
||||
"redirect": "88cc67ba-1a68-4e04-99de-db6fd60fe3d6@6c48a"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 1.9 KiB |
BIN
assets/resources/images/pageLevel/IconBulb.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
134
assets/resources/images/pageLevel/IconBulb.png.meta
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"ver": "1.0.27",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "a428ffd4-ce8c-4682-bf0b-d127509c8f23",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
],
|
||||
"subMetas": {
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "a428ffd4-ce8c-4682-bf0b-d127509c8f23@6c48a",
|
||||
"displayName": "IconBulb",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "a428ffd4-ce8c-4682-bf0b-d127509c8f23",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
"magfilter": "linear",
|
||||
"mipfilter": "none",
|
||||
"anisotropy": 0
|
||||
},
|
||||
"ver": "1.0.22",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "a428ffd4-ce8c-4682-bf0b-d127509c8f23@f9941",
|
||||
"displayName": "IconBulb",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"trimX": 59,
|
||||
"trimY": 7,
|
||||
"width": 382,
|
||||
"height": 486,
|
||||
"rawWidth": 500,
|
||||
"rawHeight": 500,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
"pivotY": 0.5,
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-191,
|
||||
-243,
|
||||
0,
|
||||
191,
|
||||
-243,
|
||||
0,
|
||||
-191,
|
||||
243,
|
||||
0,
|
||||
191,
|
||||
243,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
3
|
||||
],
|
||||
"uv": [
|
||||
59,
|
||||
493,
|
||||
441,
|
||||
493,
|
||||
59,
|
||||
7,
|
||||
441,
|
||||
7
|
||||
],
|
||||
"nuv": [
|
||||
0.118,
|
||||
0.014,
|
||||
0.882,
|
||||
0.014,
|
||||
0.118,
|
||||
0.986,
|
||||
0.882,
|
||||
0.986
|
||||
],
|
||||
"minPos": [
|
||||
-191,
|
||||
-243,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
191,
|
||||
243,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "a428ffd4-ce8c-4682-bf0b-d127509c8f23@6c48a",
|
||||
"atlasUuid": "",
|
||||
"trimType": "auto"
|
||||
},
|
||||
"ver": "1.0.12",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
}
|
||||
},
|
||||
"userData": {
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": false,
|
||||
"hasAlpha": true,
|
||||
"redirect": "a428ffd4-ce8c-4682-bf0b-d127509c8f23@6c48a"
|
||||
}
|
||||
}
|
||||