638 lines
18 KiB
Markdown
638 lines
18 KiB
Markdown
# Godot 架构流程设计
|
||
|
||
## 1. 目标
|
||
|
||
当前 `godot/FarmGodot/scripts/ui/game_scene.gd` 已经承担了主场景渲染、土地玩法、背包、商店、仓库、好友、房屋升级、狗、弹窗、动画、资源裁剪、接口调用和数据修正等职责,文件长度已超过 3000 行。继续在这个文件里堆功能,会让性能优化、UI 对齐和后续功能开发都变慢。
|
||
|
||
重构目标不是立刻重写全部代码,而是建立一套可持续拆分的 Godot 客户端架构:
|
||
|
||
- 性能优先:减少节点创建、减少全量重绘、减少 `_process` 常驻逻辑、减少重复纹理加载。
|
||
- UI 一致:保留 Egret 运行截图作为验收基准,拆分不能改变视觉和动效表现。
|
||
- 业务清晰:UI 只负责显示和输入,后端协议、数据归一化、玩法规则放到独立模块。
|
||
- 渐进迁移:每次只拆一个垂直模块,拆完必须跑 Godot 检查、Web 导出和截图验证。
|
||
|
||
## 2. 当前问题
|
||
|
||
### 2.1 职责混杂
|
||
|
||
`GameScene` 当前包含这些职责:
|
||
|
||
- 顶部资源栏、经验条、头像、战斗力位图字体。
|
||
- 主菜单、左侧菜单、顶部入口、弹窗层。
|
||
- 商店、仓库、好友、房屋升级、土地扩展、狗面板。
|
||
- 土地点击、长按、圆形操作菜单、成熟倒计时、种植提示。
|
||
- 播种、收获、铲除、施肥、除虫、除草等后端请求。
|
||
- 背包数据、仓库数据、消耗扣除、本地土地数据更新。
|
||
- 图集区域读取、AtlasTexture 创建、通用按钮和标签工厂。
|
||
|
||
这些职责应该拆成服务层、状态层、玩法层、UI 层和资源层。
|
||
|
||
### 2.2 性能风险
|
||
|
||
- 多处运行时动态创建 `TextureRect`、`Button`、`Label`,面板反复打开会产生 GC 和节点树抖动。
|
||
- `_process` 负责倒计时刷新,后续页面增多后容易变成常驻开销。
|
||
- 图集区域和 `AtlasTexture` 在 UI 创建阶段按需拼装,缺少统一缓存。
|
||
- 土地渲染仍有全量清空再重建的路径,后续 12 块地之外的扩展会放大成本。
|
||
- UI 面板逻辑和接口逻辑绑定在同一个脚本里,不利于做请求节流、失败回滚和本地状态差异更新。
|
||
|
||
## 3. 目标分层
|
||
|
||
```text
|
||
Bootstrap / AppRoot
|
||
-> ServiceRegistry
|
||
-> SceneRouter
|
||
-> GameSession
|
||
-> Feature Scenes
|
||
|
||
services/
|
||
网络、接口、账号、配置、日志
|
||
|
||
state/
|
||
玩家、农场、背包、仓库、UI 临时状态
|
||
|
||
domain/
|
||
纯业务模型、规则、后端数据映射
|
||
|
||
features/
|
||
farm、topbar、main_menu、shop、warehouse、friend、building、dog、sign
|
||
|
||
ui/common/
|
||
通用面板壳、按钮、标签、Tab、虚拟列表、动画工具
|
||
|
||
assets/
|
||
Egret 原始资源、替换资源、生成资源、图集索引和缓存
|
||
```
|
||
|
||
核心规则:依赖只能从上往下,不能反向依赖。
|
||
|
||
```text
|
||
feature UI -> controllers -> domain use cases -> services/state
|
||
```
|
||
|
||
UI 不直接调用 `HTTPRequest`,也不直接解析后端原始字段。服务返回归一化数据,状态层负责保存,UI 订阅状态变化并做局部刷新。
|
||
|
||
## 4. 推荐目录
|
||
|
||
```text
|
||
godot/FarmGodot/
|
||
scenes/
|
||
bootstrap.tscn
|
||
app/
|
||
app_root.tscn
|
||
farm/
|
||
farm_scene.tscn
|
||
land_slot.tscn
|
||
crop_view.tscn
|
||
land_action_menu.tscn
|
||
plant_tip.tscn
|
||
seed_bag.tscn
|
||
ui/
|
||
top_bar.tscn
|
||
main_menu.tscn
|
||
modal_layer.tscn
|
||
common_panel_shell.tscn
|
||
tab_bar.tscn
|
||
panels/
|
||
shop_panel.tscn
|
||
warehouse_panel.tscn
|
||
friend_panel.tscn
|
||
building_panel.tscn
|
||
dog_panel.tscn
|
||
sign_panel.tscn
|
||
|
||
scripts/
|
||
app/
|
||
app_root.gd
|
||
scene_router.gd
|
||
service_registry.gd
|
||
game_session.gd
|
||
event_bus.gd
|
||
services/
|
||
api_client.gd
|
||
auth_service.gd
|
||
farm_api.gd
|
||
inventory_api.gd
|
||
shop_api.gd
|
||
friend_api.gd
|
||
building_api.gd
|
||
dog_api.gd
|
||
state/
|
||
player_state.gd
|
||
farm_state.gd
|
||
inventory_state.gd
|
||
warehouse_state.gd
|
||
ui_state.gd
|
||
domain/
|
||
models/
|
||
player_data.gd
|
||
land_data.gd
|
||
inventory_item.gd
|
||
cost.gd
|
||
rules/
|
||
land_action_rules.gd
|
||
crop_phase_rules.gd
|
||
cost_rules.gd
|
||
mappers/
|
||
login_mapper.gd
|
||
farm_mapper.gd
|
||
inventory_mapper.gd
|
||
resources/
|
||
atlas_texture_cache.gd
|
||
texture_region_catalog.gd
|
||
resource_paths.gd
|
||
node_pool.gd
|
||
ui/
|
||
common/
|
||
bitmap_font_label.gd
|
||
panel_shell.gd
|
||
texture_button_view.gd
|
||
virtual_item_list.gd
|
||
tween_runner.gd
|
||
top_bar/
|
||
top_bar_view.gd
|
||
main_menu/
|
||
main_menu_view.gd
|
||
farm/
|
||
farm_scene.gd
|
||
farm_controller.gd
|
||
land_grid_view.gd
|
||
land_slot_view.gd
|
||
crop_view.gd
|
||
land_action_menu.gd
|
||
plant_tip_view.gd
|
||
seed_bag_view.gd
|
||
panels/
|
||
shop_panel.gd
|
||
warehouse_panel.gd
|
||
friend_panel.gd
|
||
building_panel.gd
|
||
dog_panel.gd
|
||
sign_panel.gd
|
||
```
|
||
|
||
## 5. 启动流程
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A["Bootstrap"] --> B["ServiceRegistry.create"]
|
||
B --> C["AuthService.auto_login"]
|
||
C --> D["GameSession.set_player"]
|
||
D --> E["SceneRouter.open FarmScene"]
|
||
E --> F["FarmScene.setup(session)"]
|
||
F --> G["TopBarView.bind(PlayerState)"]
|
||
F --> H["FarmController.bind(FarmState, InventoryState)"]
|
||
H --> I["FarmState.init_from_login_land_data"]
|
||
I --> J["LandGridView.render_dirty_slots"]
|
||
```
|
||
|
||
启动阶段只做三件事:
|
||
|
||
- 创建全局服务。
|
||
- 登录并归一化玩家数据。
|
||
- 打开主农场场景。
|
||
|
||
`Bootstrap` 不再持有具体页面逻辑,后续切换账号、进入好友农场、回到家园都走 `SceneRouter`。
|
||
|
||
## 6. 状态流
|
||
|
||
### 6.1 状态对象
|
||
|
||
状态对象只保存数据和版本号,不创建 UI 节点。
|
||
|
||
```text
|
||
PlayerState
|
||
token
|
||
user_id
|
||
nickname
|
||
level
|
||
exp
|
||
gold
|
||
diamond
|
||
power
|
||
house_level
|
||
dog_level
|
||
version
|
||
|
||
FarmState
|
||
lands_by_position
|
||
changed_positions
|
||
version
|
||
|
||
InventoryState
|
||
seeds
|
||
fertilizers
|
||
warehouse_items
|
||
version
|
||
|
||
UIState
|
||
selected_land_position
|
||
active_panel
|
||
menu_open
|
||
```
|
||
|
||
### 6.2 更新原则
|
||
|
||
- 后端请求成功后,先更新状态,再通知 UI。
|
||
- 单块土地变化只标记 `changed_positions`,禁止整块农场全量重绘。
|
||
- 玩家资源变化只刷新 TopBar,不刷新主场景。
|
||
- 背包物品数量变化只刷新对应 item,不重建整个 ScrollContainer。
|
||
- 失败请求不改状态,只显示 toast 或错误态。
|
||
|
||
## 7. 土地玩法流程
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant LandSlot
|
||
participant FarmController
|
||
participant Rules as LandActionRules
|
||
participant Menu as LandActionMenu
|
||
participant Api as FarmApi
|
||
participant State as FarmState
|
||
|
||
LandSlot->>FarmController: pressed(position)
|
||
FarmController->>Rules: get_actions(land_data)
|
||
Rules-->>FarmController: actions
|
||
FarmController->>Menu: show(position, actions)
|
||
Menu->>FarmController: action_selected(action)
|
||
FarmController->>Api: request(action, payload)
|
||
Api-->>FarmController: normalized_result
|
||
FarmController->>State: apply_land_result(position, result)
|
||
State-->>LandSlot: dirty update
|
||
```
|
||
|
||
### 7.1 拆分模块
|
||
|
||
- `LandActionRules`:判断空地、成长中、成熟、虫草旱、可收获、可铲除。
|
||
- `CropPhaseRules`:根据 `seed_time`、`crop_id`、配置时间计算阶段和剩余时间。
|
||
- `FarmApi`:封装 `farm/sow-seeds`、`farm/gather-crop`、`farm/clear-land`、`farm/fertilize`、`farm/disease`。
|
||
- `FarmMapper`:把后端字段归一化为 `LandData`。
|
||
- `FarmController`:连接输入、规则、接口、状态。
|
||
- `LandGridView`:只管理 12 个 `LandSlotView`。
|
||
- `LandSlotView`:只管单块土地的底图、作物、状态图标、点击热区。
|
||
- `LandActionMenu`:只管圆形菜单的弹出、收回、按压动效。
|
||
- `PlantTipView`:只管倒计时面板显示。
|
||
|
||
### 7.2 性能规则
|
||
|
||
- `LandSlotView` 常驻 12 个节点,初始化后不销毁。
|
||
- 作物 `TextureRect`、虫草旱状态图标从 `NodePool` 复用。
|
||
- 土地刷新入口只接受 `position`,默认不允许全量刷新。
|
||
- 成熟倒计时使用一个 `Timer` 每秒驱动,只更新可见 `PlantTipView`,不扫描全部 UI。
|
||
- 圆形菜单打开时创建或复用固定数量按钮,不在每次点击时重新加载纹理。
|
||
|
||
## 8. 面板流程
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A["MainMenuView item pressed"] --> B["ModalLayer.open(panel_id)"]
|
||
B --> C["PanelFactory.get_or_create"]
|
||
C --> D["Panel.bind(state/services)"]
|
||
D --> E["Panel.show_enter_animation"]
|
||
E --> F["Panel requests data if stale"]
|
||
F --> G["State update"]
|
||
G --> H["Panel render dirty list"]
|
||
```
|
||
|
||
### 8.1 面板拆分
|
||
|
||
每个面板是独立 `.tscn + .gd`:
|
||
|
||
- `ShopPanel`:商店 tabs、商品列表、购买按钮。
|
||
- `WarehousePanel`:仓库 tabs、出售弹窗、数量步进器。
|
||
- `FriendPanel`:好友列表、排序、翻页、访问好友家园。
|
||
- `BuildingPanel`:房屋升级、土地扩展。
|
||
- `DogPanel`:狗状态、狗粮选择、喂养。
|
||
- `SignPanel`:签到面板,后续接入新签到资源。
|
||
|
||
公共能力放 `ui/common`:
|
||
|
||
- `PanelShell`:标题、关闭按钮、背景九宫格。
|
||
- `TabBar`:选中态和禁用态。
|
||
- `VirtualItemList`:复用可见 item,避免大量节点。
|
||
- `TextureButtonView`:统一按钮按压缩放、禁用态、音效钩子。
|
||
|
||
### 8.2 面板缓存
|
||
|
||
- 弹窗默认懒加载,第一次打开实例化,后续隐藏复用。
|
||
- 大列表只创建屏幕可见 item 加少量缓冲。
|
||
- 面板关闭只停止动画和输入,不释放资源;内存压力确认后再引入 LRU。
|
||
|
||
## 9. 资源和图集
|
||
|
||
### 9.1 资源原则
|
||
|
||
- `assets/egret` 保存原始迁移资源,不在业务代码里直接到处写路径。
|
||
- `assets/replacement` 保存替换素材。
|
||
- 后续生成的独立图标、裁剪图集和字体索引放 `assets/generated`。
|
||
- 所有图集区域通过 `TextureRegionCatalog` 查找。
|
||
- 所有 `AtlasTexture` 通过 `AtlasTextureCache` 获取。
|
||
|
||
### 9.2 AtlasTextureCache
|
||
|
||
目标接口:
|
||
|
||
```gdscript
|
||
func get_region(atlas_path: String, region_name: String) -> Texture2D
|
||
func get_rect(atlas_path: String, region_name: String) -> Rect2
|
||
func preload_group(group_name: String) -> void
|
||
func clear_unused() -> void
|
||
```
|
||
|
||
性能要求:
|
||
|
||
- 同一个 `atlas_path + region_name` 只创建一次 `AtlasTexture`。
|
||
- 高频 UI 的图集在场景进入时预加载。
|
||
- 不在 `_process`、列表 item 创建循环里解析 JSON。
|
||
|
||
## 10. 网络和用例
|
||
|
||
### 10.1 ApiClient
|
||
|
||
`ApiClient` 只负责:
|
||
|
||
- URL 拼接。
|
||
- Egret `msg=` base64 表单编码。
|
||
- HTTP 请求。
|
||
- JSON 解析。
|
||
- HTTP 状态和业务状态包装。
|
||
|
||
### 10.2 Feature Api
|
||
|
||
每个业务 API 包一层,避免 UI 直接传字符串接口名:
|
||
|
||
```text
|
||
AuthService.login(username, password)
|
||
InventoryApi.fetch_store_house(token)
|
||
FarmApi.sow_seed(token, position, item_id)
|
||
FarmApi.gather_crop(token, position)
|
||
FarmApi.clear_land(token, position)
|
||
FarmApi.fertilize(token, position, item_id)
|
||
FarmApi.kick_disease(token, position, type)
|
||
ShopApi.buy(token, store_id)
|
||
WarehouseApi.sell(token, item_id, count)
|
||
```
|
||
|
||
### 10.3 请求性能规则
|
||
|
||
- 同一按钮请求中必须禁用重复点击。
|
||
- 同一资源数据有缓存版本,未过期不重复拉取。
|
||
- 操作成功后用接口返回增量更新本地状态,不强制重新拉全部玩家数据。
|
||
- 失败时保留原状态,避免 UI 和服务端不一致。
|
||
|
||
## 11. 事件机制
|
||
|
||
使用轻量 `EventBus` 或状态信号,不能把所有节点互相 `get_node` 调用。
|
||
|
||
推荐事件:
|
||
|
||
```text
|
||
player_changed(player_state)
|
||
farm_land_changed(position)
|
||
inventory_changed(item_id)
|
||
warehouse_changed(item_id)
|
||
panel_opened(panel_id)
|
||
panel_closed(panel_id)
|
||
toast_requested(message)
|
||
```
|
||
|
||
高频事件禁止广播大对象,传 id 或 position,再由接收方按需读取状态。
|
||
|
||
## 12. 性能基线
|
||
|
||
当前目标是海外 H5,默认 Web 单线程导出,性能预算按中低端手机浏览器设计。
|
||
|
||
### 12.1 渲染预算
|
||
|
||
- 主农场常驻节点数目标小于 250。
|
||
- 打开一个普通面板后总节点数目标小于 450。
|
||
- 单次土地操作后新增临时节点小于 10,并在动画结束后复用或回收。
|
||
- UI idle 状态 `_process` 逻辑为 0 或只保留一个倒计时入口。
|
||
|
||
### 12.2 刷新预算
|
||
|
||
- TopBar:只有玩家资源变化时刷新。
|
||
- LandSlot:只有对应土地变化时刷新。
|
||
- Bag:只有打开背包或物品数量变化时刷新。
|
||
- Panel list:只刷新当前 tab 和可见 item。
|
||
- Texture:场景运行中禁止重复解析同一个图集 JSON。
|
||
|
||
### 12.3 动画预算
|
||
|
||
- 动效统一走 `TweenRunner`,避免散落创建不可控 Tween。
|
||
- 高频按钮按压只做 scale/modulate,不做复杂 shader。
|
||
- 循环动画必须在节点隐藏时停止。
|
||
- 面板关闭时要 kill 关联 tween,避免隐藏节点继续动画。
|
||
|
||
## 13. 迁移顺序
|
||
|
||
### 阶段 0:冻结基准
|
||
|
||
- 保存当前 Godot 主场景截图。
|
||
- 保存新版 HTML 主场景截图。
|
||
- 记录当前可用功能:自动登录、主场景、土地菜单、播种、收获、铲除、施肥、除虫、除草、倒计时、背包、主要面板。
|
||
|
||
验收:
|
||
|
||
- `godot --headless --path godot/FarmGodot --import --quit`
|
||
- `godot --headless --path godot/FarmGodot --check-only --script res://scripts/ui/game_scene.gd`
|
||
- Web 导出和浏览器截图通过。
|
||
|
||
### 阶段 1:抽资源层
|
||
|
||
先从 `GameScene` 抽出无业务风险的资源工具:
|
||
|
||
- `TextureRegionCatalog`
|
||
- `AtlasTextureCache`
|
||
- `ResourcePaths`
|
||
- `NodePool`
|
||
- 通用 `_add_texture`、`_add_nine_patch`、`_add_panel_label`、`_add_texture_button`
|
||
|
||
验收:
|
||
|
||
- 主场景截图不变。
|
||
- 打开商店、仓库、好友、土地菜单无缺图。
|
||
|
||
### 阶段 2:抽状态和规则
|
||
|
||
抽纯逻辑,不动 UI 节点布局:
|
||
|
||
- `PlayerState`
|
||
- `FarmState`
|
||
- `InventoryState`
|
||
- `LandActionRules`
|
||
- `CropPhaseRules`
|
||
- `FarmMapper`
|
||
- `InventoryMapper`
|
||
|
||
验收:
|
||
|
||
- 播种、收获、铲除、施肥、除虫、除草结果一致。
|
||
- 土地倒计时和成熟判断一致。
|
||
|
||
### 阶段 3:抽农场玩法
|
||
|
||
把土地相关 1000 行左右逻辑迁到 farm feature:
|
||
|
||
- `FarmController`
|
||
- `LandGridView`
|
||
- `LandSlotView`
|
||
- `LandActionMenu`
|
||
- `PlantTipView`
|
||
- `SeedBagView`
|
||
|
||
验收:
|
||
|
||
- 点击土地菜单位置、动效、空白关闭一致。
|
||
- 成熟点击行为一致。
|
||
- 单块土地操作后只刷新单块土地。
|
||
|
||
### 阶段 4:抽 TopBar 和主菜单
|
||
|
||
- `TopBarView`
|
||
- `MainMenuView`
|
||
- `LeftMenuView`
|
||
- `TopEntryMenuView`
|
||
|
||
验收:
|
||
|
||
- 顶部资源文字、战斗力位图、经验条、头像位置不变。
|
||
- 主菜单展开和收起动效不变。
|
||
|
||
### 阶段 5:抽面板
|
||
|
||
按风险从低到高拆:
|
||
|
||
1. `FriendPanel`
|
||
2. `WarehousePanel`
|
||
3. `ShopPanel`
|
||
4. `BuildingPanel`
|
||
5. `DogPanel`
|
||
6. `SignPanel`
|
||
|
||
验收:
|
||
|
||
- 每拆一个面板,单独截图对比。
|
||
- 面板关闭后节点数不持续增长。
|
||
|
||
### 阶段 6:删除旧 GameScene 负担
|
||
|
||
最终 `GameScene` 只保留:
|
||
|
||
- 场景组合。
|
||
- 绑定 session。
|
||
- 转发生命周期给 feature controller。
|
||
|
||
目标行数:
|
||
|
||
- `farm_scene.gd` 小于 300 行。
|
||
- 每个 panel 脚本小于 500 行。
|
||
- 每个 rules/mapper/state 文件小于 250 行。
|
||
- 单个脚本超过 800 行必须继续拆分。
|
||
|
||
## 14. 文件迁移映射
|
||
|
||
```text
|
||
game_scene.gd:_render_player
|
||
-> top_bar/top_bar_view.gd
|
||
-> state/player_state.gd
|
||
|
||
game_scene.gd:_setup_main_menu ... _on_main_menu_item_pressed
|
||
-> main_menu/main_menu_view.gd
|
||
|
||
game_scene.gd:_open_shop_panel ... _buy_shop_item
|
||
-> panels/shop_panel.gd
|
||
-> services/shop_api.gd
|
||
|
||
game_scene.gd:_open_warehouse_panel ... _sell_warehouse_item
|
||
-> panels/warehouse_panel.gd
|
||
-> state/warehouse_state.gd
|
||
|
||
game_scene.gd:_open_friend_panel ... _create_friend_row
|
||
-> panels/friend_panel.gd
|
||
-> services/friend_api.gd
|
||
|
||
game_scene.gd:_open_house_upgrade_panel ... _extend_land
|
||
-> panels/building_panel.gd
|
||
-> services/building_api.gd
|
||
|
||
game_scene.gd:_open_dog_panel ... _feed_selected_dog_food
|
||
-> panels/dog_panel.gd
|
||
-> services/dog_api.gd
|
||
|
||
game_scene.gd:_setup_farm_interaction ... _get_land_node
|
||
-> farm/farm_controller.gd
|
||
-> farm/land_grid_view.gd
|
||
-> farm/land_slot_view.gd
|
||
-> farm/land_action_menu.gd
|
||
-> farm/plant_tip_view.gd
|
||
|
||
game_scene.gd:_refresh_store_house ... _extract_warehouse_items
|
||
-> services/inventory_api.gd
|
||
-> domain/mappers/inventory_mapper.gd
|
||
-> state/inventory_state.gd
|
||
|
||
game_scene.gd:_sow_seed ... _kick_disease
|
||
-> services/farm_api.gd
|
||
-> farm/farm_controller.gd
|
||
```
|
||
|
||
## 15. 编码规则
|
||
|
||
- 每个新脚本必须有 `class_name`,方便类型标注。
|
||
- UI 脚本可以依赖 controller,controller 不依赖具体 UI 子节点。
|
||
- 业务规则必须能脱离场景运行。
|
||
- 后端原始字段只允许 mapper 读取。
|
||
- UI 节点路径只允许 view 脚本内部使用。
|
||
- 大量路径常量统一放 `ResourcePaths`。
|
||
- 不在 `_process` 里做数据解析、资源加载、节点创建。
|
||
- 不在列表 item 内部直接发请求,请求由 panel/controller 统一管理。
|
||
- 所有长动画、循环动画在 `_exit_tree` 或隐藏时停止。
|
||
|
||
## 16. 验证流程
|
||
|
||
每次拆分必须执行:
|
||
|
||
```bash
|
||
godot --headless --path godot/FarmGodot --import --quit
|
||
godot --headless --path godot/FarmGodot --check-only --script res://scripts/ui/game_scene.gd
|
||
godot --headless --path godot/FarmGodot --export-release Web /tmp/farm-godot-web/index.html
|
||
```
|
||
|
||
如果新增了脚本,对新增脚本逐个跑 `--check-only --script`。
|
||
|
||
UI 改动必须保存截图:
|
||
|
||
```text
|
||
docs/ui-baseline/godot-<feature>-before.png
|
||
docs/ui-baseline/godot-<feature>-after.png
|
||
docs/ui-baseline/godot-<feature>-meta.json
|
||
```
|
||
|
||
涉及后端的功能必须记录:
|
||
|
||
- 接口路径。
|
||
- HTTP 状态。
|
||
- 业务 status。
|
||
- 关键响应字段。
|
||
- 本地状态变化。
|
||
|
||
## 17. 禁止事项
|
||
|
||
- 禁止继续把新页面直接塞进 `game_scene.gd`。
|
||
- 禁止 UI 直接拼接口路径。
|
||
- 禁止功能成功后整页重载主场景。
|
||
- 禁止每次打开菜单都重新解析图集 JSON。
|
||
- 禁止隐藏面板后保留运行中的循环 tween。
|
||
- 禁止为了快速开发跳过截图验证。
|
||
- 禁止用“看起来差不多”替代新版 HTML 基准。
|
||
|
||
## 18. 下一步执行建议
|
||
|
||
最直接、风险最低的第一步是抽资源层和通用 UI 工厂,因为这部分不改变业务行为:
|
||
|
||
1. 新建 `scripts/resources/atlas_texture_cache.gd`。
|
||
2. 新建 `scripts/resources/texture_region_catalog.gd`。
|
||
3. 新建 `scripts/ui/common/ui_factory.gd` 或拆成更细的 view helpers。
|
||
4. 把 `GameScene` 中 `_common_texture`、`_friend_texture`、`_rank_texture`、`_building_texture`、`_landup_texture`、`_sign_texture`、`_region_for`、`_atlas_texture` 迁出。
|
||
5. 保持所有 UI 截图不变后,再进入状态和农场玩法拆分。
|