CatGunner CookbookBardCat 1.1.59 · L1 ANALYSIS · tài liệu học
Trang chủCookbook › 12 — Kiến trúc & quy mô (một-scene-481MB và cái giá)
Nguồn: 12-kien-truc-scale.md · Cập nhật lần cuối: 2026-09-05 08:39 +07 · sha256 nguồn 9181bc762646

12 — Kiến trúc & quy mô (một-scene-481MB và cái giá)

Nguồn framework: docs/framework/08-architecture-and-scale.md toàn bộ; docs/framework/07-progression-managers.md §3.1 (35 singleton); [liveops catalog: SCREEN_CATALOG.md] §1 (đính chính số Canvas). Cấp bằng chứng: L1 ANALYSIS — dựng lại clean-room từ disasm + metadata + quan sát runtime; không phải source gốc. Mỗi dòng bên dưới mang nhãn bằng chứng riêng.

Mục tiêu

Muốn biết một game idle thương mại thật sự lớn cỡ nào, và quyết định kiến trúc nào sẽ bắt bạn trả giá về sau — trước khi bạn lặp lại nó. CatGunner chọn một scene duy nhất chứa toàn bộ game (481 MB): không loading, không bundle, nhưng import Editor > 30 phút, không merge được, và một loại bug mới (combat chạy trong menu). Trang này đưa hoá đơn đo được, ngân sách hình ảnh mobile đã ship, và bài học "đo, đừng đoán".

Game gốc làm gì

Danh tính build

Điều game làm Con số / hằng số Mức bằng chứng Nguồn
com.Chodun.CatGunner, versionName 1.1.59, versionCode 227 package-source [shared facts nội bộ]
Unity 6000.0.61f1, URP, IL2CPP, chỉ arm64-v8a package-source [shared facts nội bộ]; chuỗi build trong libil2cpp.so
XAPK: base 147 557 129 B + config.arm64_v8a.apk 40 028 322 B; không OBB, không asset pack package-source [report: asset_lane.md] §1
libil2cpp.so 101 516 264 B; global-metadata.dat 17 924 548 B package-source [shared facts nội bộ]
minSdk 27, targetSdk 36 package-source [report: project_lane.md] §7
136 assembly · 18 678 type · 141 908 method sau giải mã; Assembly-CSharp = 731 type / 5 194 method metadata-proven [shared facts nội bộ]

Một scene — con số

Điều game làm Con số / hằng số Mức bằng chứng Nguồn
đúng MỘT scene: Main (level0) 1 asset-census [report: asset_lane.md] §3
Kích thước sau export 481 217 168 B, 143 032 object asset-census như trên
38 375 GameObject · 24 452 Transform + 13 923 RectTransform · 12 561 CanvasRenderer · 7 718 ParticleSystem · 6 491 SpriteRenderer · 31 354 MonoBehaviour asset-census asset_lane.md §3, §4a
30 082 tham chiếu m_Script trỏ tới 190 class của Assembly-CSharp (trên 731) 190 / 731 scene-census [index: scene_census.json]
Doc 08 ghi 25 Canvas; lane liveops đếm lại trên export đã giải mã: 21 Canvas, và chỉ 1 là Canvas UI thật (Canvas, GO 779); 20 còn lại là Canvas lồng override sorting cho Fish_item (N), tất cả m_IsActive: 0 21 / 1 VERIFIED (đếm từ scene) SCREEN_CATALOG.md §1; G152 gap list (nội bộ)
Đơn vị màn hình thật = con của Canvas / Canvas/SafeArea / Canvas/Contents_UI: 18 nhánh gốc, ~90 panel, 221 class MonoBehaviour duy nhất 221 VERIFIED SCREEN_CATALOG.md §1, §3; G152
Không level1..N, không AssetBundle, không Addressables, không CDN asset asset-census (phủ định) asset_lane.md §3
Mọi asset trong một bundle data.unity3d (32 167 543 B) asset-census asset_lane.md §2

Hoá đơn đo được của quyết định đó

Chi phí Số đo Mức bằng chứng Nguồn
Import sạch scene trong Editor > 30 phút, CPU-bound 98 % build-measured G027
Một lần import THẤT BẠI: Editor SIGKILL (-9) sau 2 544 s — không OOM (128 GB, còn 93 %), không timeout 42 phút build-measured G028
Rác log ReadObjectThreaded ~24 GB build-measured G027 / G028
Scene đã giải mã cần ~75 GB RSS đỉnh (trước giải mã ~9 GB) vì 28k component mang dữ liệu thật 75 GB build-measured G124 (toolchain)
Build trong process group của caller bị SIGKILL đúng 60 phút ⇒ phải spawn_detached.py + os.setsid() 60 phút build-measured G120
APK dựng lại: scene chia thành level0.split0..145 146 mảnh build-measured [report: project_lane.md] §7
File YAML 481 MB — không merge được, thực tế là khoá độc quyền build-measured doc 08 §3.1
Combat chạy trong menu nếu thiếu guard Gamemanager.is_gaming; Enemy.Damaged/Enemy.Die từng thiếu guard khi dựng lại disasm-proven (bug đã sửa) G132b; doc 02 §3.5

Ngân sách hình ảnh mobile đã ship

Cài đặt Giá trị Mức bằng chứng Nguồn
m_RenderScale 0.7 (render 49 % số pixel rồi upscale) scene-YAML [restore project: Mobile_RPAsset.asset:29]
m_UpscalingFilter: 1, m_FsrSharpness: 0.92 FSR scene-YAML như trên
m_MSAA: 1 (tắt), m_SupportsHDR: 0, m_MainLightShadowsSupported: 0, m_AdditionalLightShadowsSupported: 0 tắt scene-YAML như trên
m_RequireDepthTexture: 1, m_RequireOpaqueTexture: 0, m_EnableLODCrossFade: 0 scene-YAML như trên
Texture Android ASTC: ASTC_6x6 ×78 (gồm cả hai atlas 4096²), ASTC_8x8 ×9, RGBA32 ×50, Alpha8 ×20, RGB24 ×1 asset-census asset_lane.md §0
2 sprite atlas: Cat (125 sprite), _UI (374 sprite), cả hai 4096×4096 ASTC 6x6 499 sprite asset-census asset_lane.md §5
boot.config: androidStartInFullscreen=1, androidRenderOutsideSafeArea=1, adaptive-performance boost-launch package-source project_lane.md §7

Bộ khung code

Điều game làm Con số / hằng số Mức bằng chứng Nguồn
35 singleton *_manager / Gamemanager / UpdateManager / OfflineProgressManager, mỗi cái một instance; field static trần public static X instance; 35 scene-census + metadata-proven [report: codegen_lane.md] §1.2, §1.3
Không DI container, không event bus tổng, không state machine tổng không quan sát thấy dump.cs toàn bộ manager
Tập làm việc = {managers} ∪ {class ≥ 20 instance} = 66 class / 1 284 method trên 5 194 66 scene-census [index: priority_static.json]
Trace runtime: 505 method thực sự chạy trong boot + 40 s chơi (hook 1 279 method tại native offset) 505 runtime-observed [runtime trace: runtime_ranking.json]
BulletShooter (nghe như trái tim game): 0 lời gọi; SlicedFilledImage (thanh máu UI): 169 246 lời gọi, gấp 10 lần Bullet (16 321) 0 / 169 246 runtime-observed runtime_ranking.json

Package bên thứ ba

Assembly đã ship Package Mức bằng chứng Nguồn
DOTween, DOTweenPro, DemiLib DOTween Pro — Asset Store 32416 package-source quy tắc dự án (nội bộ) R2
AllIn1SpriteShaderAssembly All In 1 Sprite Shader — 156513 package-source như trên
ParticleImage UI Particle Image — 235001; version phải khớp bề mặt API của binary (game gọi EmitAtWorldPosition; bản cũ trong project không có ⇒ compile đỏ, G404) package-source như trên
ACTk.Runtime Anti-Cheat Toolkit — 202695 package-source như trên
MK.Toon, MK.Toon.URP MK Toon — 178415 package-source như trên
ShapesRuntime Shapes 4.6.0 — 173167 (lệch version, G117) package-source project_lane.md §4
OccaSoftware.OutlineObjects.Runtime Outline Objects 3.2.0 — 215454 (lệch version, G118) package-source như trên
InfiniteValueAsm Infinite Value (BigDecimal) 2.2.0 — 194963 (SOT ship bản cũ hơn, G122) package-source như trên
spine-csharp, spine-unity spine-unity 4.1 (miễn phí) package-source như trên
Whinarn.IndMeshSimplifier.Runtime UnityMeshSimplifier v3.1.1 (MIT) package-source như trên
SlicedFilledImage MIT (hiryma / Unity UI Extensions), vendor dưới dạng source package-source G128

Bảo vệ khi ship (mục nội bộ, đã lược bỏ)

Công thức / pseudocode

Ngân sách URP mobile đã ship — scene-YAML, Mobile_RPAsset.asset:

m_RenderScale: 0.7               # -51 % pixel phải tô; game 2.5D nhìn từ trên gần như không nhận ra
m_UpscalingFilter: 1             # FSR, FsrSharpness 0.92
m_MSAA: 1                        # TẮT
m_SupportsHDR: 0                 # TẮT
m_MainLightShadowsSupported: 0   # TẮT
m_AdditionalLightShadowsSupported: 0
m_ShadowDistance: 50
m_ShadowCascadeCount: 1
m_RequireDepthTexture: 1         # BẬT (có hiệu ứng cần depth — outline là ứng viên, chưa xác nhận)
m_RequireOpaqueTexture: 0        # TẮT
m_EnableLODCrossFade: 0          # TẮT

Bộ khung singleton — mẫu lặp ở ~30 manager (doc 07 §3.1):

public class X_manager : MonoBehaviour
{
    public static X_manager instance;          // field static trần
    private void Awake() { instance = this; }  // đăng ký, không kiểm trùng
    public int[] SaveData_return() { … }       // xuất state
    public void LoadData(SaveData data) { … }  // nạp state
    public void UI_Reload() { … }              // vẽ lại UI từ state
}
// Hoạt động vì game có ĐÚNG MỘT scene: mọi manager sống từ load đến thoát.
// Nguy hiểm vì khớp nối chéo toàn phần: Tree.Die_Reload -> Tree_manager.Tree_Destroyed chạm 5 singleton cho một cây đổ (G346).

Guard trạng thái toàn cục — bài học từ G132b (L1 ANALYSIS, Enemy.Damaged / Enemy.Die):

if (!Gamemanager.is_gaming) return;   // BẮT BUỘC ở từng entry-point combat trong kiến trúc một-scene
// Thiếu dòng này: quái vẫn ăn damage và RƠI TIỀN khi người chơi đang mở shop — không crash, kinh tế sai âm thầm

Xếp hạng công việc bằng dữ liệu — phương pháp (doc 08 §3.5):

(khối mã nội bộ đã lược bỏ)

Gợi ý framework (không phải bằng chứng SOT): mô hình lai — một scene lõi bền + các màn hình phụ tải thêm; is_gaming thành cổng ở tầng framework (base class hoặc IDamageable gate); đo chi phí import ngay từ đầu, không đợi tới 481 MB.

Bẫy đã gặp

  • Một scene cho tất cả rồi mới đo. Hoá đơn đến muộn và không đảo được: > 30 phút import (G027), SIGKILL sau 2 544 s (G028), 75 GB RSS đỉnh (G124 toolchain), file 481 MB không merge được.
  • Trạng thái rò rỉ xuyên "màn hình". Vì quái/đạn/collider luôn tồn tại, thứ duy nhất chặn combat trong menu là một cờ boolean kiểm ở từng entry-point. Bản dựng lại từng thiếu guard ở Enemy.Damaged/Enemy.Die (G132b).
  • Đếm Canvas để kiểm kê màn hình. Doc 08 và RULES R7 ghi "25 Canvas"; đếm lại trên export đã giải mã ra 21, và chỉ 1 là Canvas UI thật (SCREEN_CATALOG.md §1, G152). Đơn vị màn hình đúng là con của Canvas/SafeAreaCanvas/Contents_UI.
  • Chọn target theo tên class. BulletShooter nghe như trái tim game và có 0 lần gọi; SlicedFilledImage là class nóng nhất. Không có oracle runtime, lane khôi phục sẽ đổ công vào chỗ vô giá trị (doc 08 §3.5).
  • Trỏ AssetRipper vào assets/bin/Data đơn lẻ. Không thấy lib/arm64-v8a/libil2cpp.so ⇒ không chạy Cpp2IL ⇒ 100 % dummy class. Cây staging phải là layout Android đầy đủ (G123).
  • Tưởng chữ ký + field là "khôi phục". Cpp2IL dựng chữ ký và field, không bao giờ dựng logic — Update(), ShootBullet() đều { }. APK từ export đó là vỏ cấu trúc, không phải game (G129, G115).
  • Đổ lỗi bảo vệ cho thư viện sai. libpglarmor.so bị nghi là bộ mã hoá metadata; thực ra là Pangle AD-SDK (G001a).
  • Nâng version package serialize vào scene mà không kiểm layout. InfVal serialize inline (s_digits, s_exponent); đổi tên field sẽ âm thầm xoá sạch dữ liệu kinh tế (G122).
  • Chạy job Unity dài trong process group của caller. Bị SIGKILL đúng 60 phút (G120).

Checklist khi tự làm

  • [ ] Quyết định cấu trúc scene sớm và đo chi phí import ngay khi scene còn nhỏ; ưu tiên mô hình lai (scene lõi + màn hình phụ).
  • [ ] Nếu chọn một-scene: guard trạng thái game là hạ tầng bắt buộc (base class / gate), không phải dòng if phải nhớ chép.
  • [ ] Kiểm kê màn hình theo GameObject con của Canvas gốc, không đếm component Canvas.
  • [ ] Ngân sách hình ảnh 2D/2.5D mobile: RenderScale 0.7 + FSR, tắt MSAA/HDR/bóng, ASTC 6x6, ít atlas lớn (2 × 4096²).
  • [ ] Bốn quy ước method cho mọi manager (Balance_Reload / SaveData_return+LoadData / First_Reload+Value_Reload / X_return) — xem 10-tien-trinh-mo-khoa.md.
  • [ ] Tách máy móc toàn vẹn (Time_manager, nửa Gamemanager) ra module riêng sau ITrustedTime.
  • [ ] Xếp hạng công việc bằng scene census + topology + trace runtime; đừng tin tên.
  • [ ] Mua package thật; ghi lệch version thành gap; kiểm layout serialize trước khi tin dùng.
  • [ ] Hiểu bảo vệ là quản trị rủi ro: chống dịch ngược (metadata) và chống phân phối lại (license) là hai lớp khác nhau; key phải nằm trong binary nên không ngăn được người quyết tâm.
  • [ ] Nói đúng về "khôi phục": chữ ký + field ≠ hành vi.

Điều chưa biết (UNKNOWN)

  • Managed stripping level, API compatibility level, cấu hình compiler IL2CPP, scripting defines, thứ tự graphics API: KHÔNG khôi phục được từ evidence (G107; G131 đóng một phần nhờ ScriptableObject URP).
  • Phân bố 7 718 ParticleSystem theo gameplay vs UI: UNKNOWN — chỉ biết tổng.
  • Hai GUID script đông nhất trong scene (7 9914 957 instance): UNKNOWN — không ánh xạ được sang class Assembly-CSharp; nằm trong DLL plugin. Không đoán.
  • Gate build Android trên base đã giải mã: CHƯA CHẠY — import vẫn bị SIGKILL (G028, G123 toolchain).
  • Đo hiệu năng trên thiết bị (fps, thermal, memory): CHƯA ĐO.
  • 32 shader stub DummyShaderTextExporter: ĐÓNG MỘT PHẦN (G366) — render state của 3 shader sprite đã đọc từ bundle: Sprites/Fill Amount Batched (1 931 material) và Custom/MonsterBounce (240) blend premultiplied (One OneMinusSrcAlpha), Custom/DiamondClipSprite (6) blend SrcAlpha OneMinusSrcAlpha; cả ba ZWrite Off / Cull Off / Queue Transparent, không LOD. Thân hiệu ứng (fill clip, bounce đỉnh, diamond clip) còn là placeholder; dòng premultiply c.rgb *= c.acủa bản dựng lại, không phải SOT. GLSL đã giải nén, đang port sang HLSL (sẽ dán nhãn reconstruction).
  • .spriteatlas cho Cat / _UI: gap chỉ về định dạng (G013) — bảng đóng gói có trong JSON export.
  • Sprite Hand: G014 — vắng trong project export, có trong corpus UnityPy.
  • Hiệu ứng nào cần RequireDepthTexture: chưa xác nhận (outline là ứng viên xét package Outline Objects — suy luận, không phải kết luận).
  • Giá trị 25 vs 21 Canvas trong doc 08 §2.2 chưa được doc 08 sửa lại; trang này lấy số của SCREEN_CATALOG.md (đếm trực tiếp trên export đã giải mã).
  • Hệ text (đang làm — G397): ScriptLocalization (317 term, đầu ra công cụ I2) sẽ được dựng lại từ binary như class thường (mỗi getter = một literal GetTranslation("Category/Term")); glyph lỗi ở capsule tiền/dia nghi do font asset TMP (atlas/material/fallback) chưa khớp export; cỡ text/margin là scene-serialized. Chưa có claim nào — ghi để biết đây là một hạng mục riêng của việc khôi phục. Cập nhật (G407): font đã kiểm giống hệt game gốc (18 font asset, 5 sprite asset, 27 material — GUID, atlas, glyph, metrics, fallback); lỗi tìm ra là tài nguyên mẫu của package (TMP Essential Resources, I2 Examples, Shapes) che asset gốc ở 23 đường Resources.Load — "TMP Settings" resolve không tất định ⇒ font/sprite/style mặc định có thể lệch âm thầm. Đang xoá phần che (giữ shader + LiberationSans). Bài học: sau mỗi lần import package, kiểm collision Resources.Load với asset của game.

Đọc thêm