Posts made by changx
-
Tổng hợp Optimize NATVposted in Developer Discussion
🧩Unity Performance Optimization (NATV)
I. Giảm DrawCall
️ Dùng SRP Batcher của URPGom nhiều mesh có cùng shader vào 1 batch drawcall, giúp giảm đáng kể chi phí CPU khi render nhiều object như monster hoặc vật cản.
Bật trong Project Settings → Graphics → SRP Batcher

🧱 Dùng chung Shader / Material / Layer
- Chuyển các vật cản, nơi đứng của monster thành Spine Animation (shader:
Universal Render Pipeline/2D/Spine/Skeleton) - Dùng Layer giống nhau giữa monster và vật cản để SRP gom vào chung batch
- Việc biến
SpriteRendererthànhSpine Animationrất đơn giản – tham khảoCreateSpineAnimForTextureExtensions.cs

Vòng tròn đỏ: Skeleton Animation
🟢 Vòng tròn xanh: Sprite Renderer
🧩 Sprite Renderer – Dùng chung Material & Atlas
Khi vẫn dùng
SpriteRenderer, cần dùng chung material và chung atlas để Unity nhóm các sprite lại thành 1 drawcall.
GPU InstancingDùng cho các object giống nhau:
Ví dụ: cỏ, hiệu ứng vòng tròn tấn công, đá nhỏ, lá rơi, …
→ Giúp giảm drawcall mà vẫn giữ hiệu ứng động.
🧭 Sắp xếp Layer hợp lý
- Layer 1: Mặt đất, cây cỏ, rác, đá sỏi, các vật có thể bị đạp lên.
- Layer 2: Monster, hero, effect, dog, base, obstacle – nơi các vật thể có thể che nhau.

II. Optimize CPU
Giảm đối tượng cần vẽ / tính toánMonster nằm ngoài camera chỉ cần cập nhật logic, không cần render.
→ TrongSkeletonAnimation, bật:
Advanced → Update When Invisible → Only Event Timelines
Loại bỏ Collider / Rigidbody không cần thiếtDùng hệ thống va chạm logic tự tính thay cho vật lý thật:
- Tính khoảng cách giữa tâm hero và monster.
- Nếu nhỏ hơn bán kính kỹ năng → trong phạm vi tấn công.
→ Tham khảo:DetectCollideManager.cs.
III. Optimize Addressables
Tổ chức theo ChapterMỗi chapter là 1 group riêng, chọn Pack Together để Unity build thành 1 bundle duy nhất → gọn nhẹ, dễ load/unload.

Không bỏ Scene vào AddressableScene luôn bị build dạng Pack Separately, dễ bị duplicate asset → tăng size bundle và memory.
🧾 Config chỉ chứa data cơ bản
- Chỉ nên chứa text, number, config JSON.
- Không chứa sprite, texture, sound để tránh trùng atlas.
- Nếu buộc phải có sprite → bỏ tích Include in Build trong atlas và dùng
SpriteAtlasManagerđể load thủ công.

Tham khảo:
SpriteAtlasAddressableLoader.cs
Tài liệu: Unity SpriteAtlasManager
️ Giải phóng tài nguyên đúng cáchMọi handle async của Addressables (
LoadAssetAsync,InstantiateAsync, …)
→ Phải gọiAddressables.Release(handle)khi không dùng nữa.
IV. Optimize Memory
Không tạo object mới mỗi frameKhông
new string,new List,new Dictionary... trongUpdate→ gây GC Alloc liên tục.
→ Dùng biến cache hoặcList.Clear()để tái sử dụng.
🧹 Dọn dẹp bộ nhớ
Khi đổi scene, gọi:
Addressables.ReleaseUnusedAssets(); Resources.UnloadUnusedAssets();Có thể thêm
GC.Collect()trong màn hình loading để thu hồi managed memory.
🧱 Giảm phân mảnh bộ nhớ
- Dùng
List,Queue,Stack,Dictionarycó capacity cố định. - Tính trước kích thước hợp lý ngay từ đầu để tránh
reallocate. - Tham khảo code gốc của .NET
List<T>:
List.cs#L198 - Dùng Object Pooling cho object tạo–hủy thường xuyên (đạn, hiệu ứng, popup...).
V. Optimize Spine Animation
- Gom tất cả monster cùng chapter vào 1 atlas duy nhất
→ ví dụ:Chapter_01_Monsters.atlas,Chapter_02_Monsters.atlas. - Nếu vùng sprite là hình lồi, cắt lồi để giảm vertices/triangle.
- Bật Triangulate Low Detail hoặc giảm precision vùng mesh.
- Tránh sprite có alpha phức tạp hoặc chi tiết biên thừa.
- Giảm bone, attachment, slot không cần thiết.
- Gộp nhiều attachment tĩnh thành 1 sprite duy nhất.
- Xóa bone test, placeholder, slot không render.
- Animation đơn giản (vd: idle) chỉ cần 2 keyframe đầu/cuối (Spine tự nội suy).
- Xóa keyframe dư và animation không dùng runtime.
VI. Optimize Texture
- Dùng ASTC thay vì RGBA32 để giảm kích thước build và RAM.
- Dùng ASTC 10×10 hoặc 12×12 nếu chất lượng hình ảnh vẫn ổn.
- Gom sprite hợp lý vào atlas để giảm texture switch.
VII. Optimize UIToolkit
- Dùng
usageHints:
Giúp Unity xác định cách tối ưu layout và render cho UI element. - Giảm thay đổi transform:
Không thay đổiposition,rotation,scalethường xuyên — vì sẽ rebuild layout gây tốn CPU.
VIII. Optimize Âm Thanh
- Decompress On Load:
Cho âm thanh phát liên tục (bắn, hit, bước chân). - Streaming:
Cho âm thanh dài (nhạc nền, voice, cutscene). - Compress In Memory:
Cho âm thanh ngắn, ít khi phát (click UI, hiệu ứng nhỏ). - Load In Background:
Dùng cho các âm thanh không yêu cầu độ chính xác cao để không chặn luồng chính.
IX. Optimize Script
-
Cache các giá trị dùng nhiều:
Animator.StringToHash(trigger animation).LayerMask.GetMask,LayerMask.NameToLayer.Camera.main(vì gọi lại sẽFindGameObjectWithTag).
-
Tạo collection có capacity cụ thể:
new List<int>(64)hoặcnew Dictionary<string, int>(32)để tránh mở rộng mảng. -
Dùng HashSet thay List:
Nếu chỉ cần kiểm traContains,Add,Remove– tốc độ nhanh hơn nhiều. -
Local Function:
Đặt ở lớp ngoài cùng, truyền tham số rõ ràng để tránh GC capture closure.
- Xoá các hàm Update() hoặc FixedUpdate() nếu không dùng đến
- Chuyển các vật cản, nơi đứng của monster thành Spine Animation (shader:
-
[NATV] Spine Editorposted in Developer Discussion
mục tiêu chính: mỗi chapter dùng 1 atlas do Spine Editor xuất, các bước thực hiện:
1: tìm ra tên chung cho các con monster và các hiệu ứng của con monster đó, ví dụ như đạn, effect riêng liên quan đến con monster đó, bỏ vào thư mục chứa tên chung này, đưa các hình ảnh vào cho đúng
2: effect chung của cả chapter là Star_Stunned để vào 1 thư mục riêng
3: dùng Find And Replace trong SpineEditor để thực hiện đổi tên
4: loại bỏ những tấm hình không được dùng đến, thực hiện đô
5: export nhưng không cần pack atlas
6: copy toàn bộ thư mục images ra 1 thư mục atlas_images, texture packer atlas_images này với scale size là (1 và 0.5)có thể tham khảo từ đây: \192.168.0.200\Shared\Artist\GAME - NATV\Chapter-Raidboss REFACTOR
cho ví dụ:
trong chapter raidboss:- con Pertrified_Berserker có 1 hiệu ứng là Pertrified_Berserker FX, vì vậy tên chung của cả 2 file spine này là Pertrified_Berserker, tạo 1 thư mục images/Pertrified_Berserker trong cả Pertrified_Berserker và Pertrified_Berserker FX

- tạo 1 thư mục là images/Star_Stunned

Find: (.*)
Replace: Pertrified_Berserker/$1
các option còn lại chọn giống trong hình dưới

4. tìm xem các hình không được dùng đến thì thực hiện xoá
5. export với setting sau:

6: tạo 1 folder là Chapter_Raidboss_atlas, tạo thêm 1 images: copy tất cả thư mục images từ tất cả các con trên và bỏ vào trong này, sau đó dùng Spine Editor chọn chức năng TexturePacker: Input folder: là thư mục vừa rồi

Settings giống như sau:

- con Pertrified_Berserker có 1 hiệu ứng là Pertrified_Berserker FX, vì vậy tên chung của cả 2 file spine này là Pertrified_Berserker, tạo 1 thư mục images/Pertrified_Berserker trong cả Pertrified_Berserker và Pertrified_Berserker FX
-
RE: sử dụng ACTk unity cho hiệu quảposted in Developer Discussion
// cả 2 cái initValue1 và initValue2 có 5 và 3 đều dễ bị chỉnh sửa, thay thành dạng GlobalConstance hoặc static readonly ObscuredInt INIT_VALUE_1= 5; public class ConfigItem { public ObscuredInt initValue1 = 5; public ObscuredInt initValue2; public ConfigItem () { initValue2 = 3; } } -
sử dụng ACTk unity cho hiệu quảposted in Developer Discussion
đối với property
// không an toàn vì khi build ra nó sẽ thành public int get_coin() { // chỗ này quá dễ để cheat chỉnh sửa } public int coin => s_coin; private ObscuredInt s_coin; // chỗ này cũng như property coin bên trên, không sử dụng public int getCoin() { return s_coin; }đổi lại thành
// khó để return 1 ObscuredInt hơn trong memory public ObscuredInt coin => s_coin; private ObscuredInt s_coin;trong hàm
void ChangeGemToCoin() { // 2 cái gemCost và coinRecv không an toàn ObscuredInt gemCost = 10; ObscuredInt coinRecv = 100; // vd logic để thay đổi tiền applicationUser.gem -= gemCost; applicationUser.coin += coinRecv; }đổi lại thành
// cái này này đảm bảo việc GEM_COST và COIN_RECV phải được tạo ra khi vừa start chương trình public class GlobalConstance { public static readonly ObscuredInt GEM_COST = 10; public static readonly ObscuredInt COIN_RECV = 100; } void ChangeGemToCoin() { ObscuredInt gemCost = GlobalConstance.GEM_COST; ObscuredInt coinRecv = GlobalConstance.COIN_RECV; // vd logic để thay đổi tiền applicationUser.gem -= gemCost; applicationUser.coin += coinRecv; } -
Lỗ hỏng bảo mật của ACTk Unityposted in Developer Discussion
xem thử 1 biến như ObscuredLong, đoạn hàm Encrypt và Decrypt được viết như sau
public static long Encrypt(long value, long key) { unchecked { return (value ^ key) + key; } } public static long Decrypt(long value, long key) { unchecked { return (value - key) ^ key; } }value là giá trị gốc chưa bị che, nếu key = 0 thì (value ^ key) + key sẽ luôn = value; đoạn này được sử dụng trong ObscuredLong như sau:
currentCryptoKey = GeneratePrivateKey(); hiddenValue = Encrypt(value, currentCryptoKey);nếu vậy mục tiêu của mình chỉ cần dùng CheatEngine chỉnh sửa để GeneratePrivateKey() luôn return 0; vậy là hiddenValue sẽ lộ ra và lúc này chỉnh sửa bất kỳ giá trị nào dùng ObscuredLong dễ dàng
-
RE: Lưuposted in Adminstrator
@changx
Việc không localization khi thực hiện NEWS và MAIL BOX tính năng mới trong NATV có thể châm trước là vì game chỉ hỗ trợ tiếng anh nên không thực hiện localizationTuy nhiên việc scrollrect hoạt động không chính xác tại MAIL BOX đã được yêu cầu để fix thì lại bỏ qua scrollrect chạy trên NEWS là cần xem lại về thái độ không cẩn thận khi thực hiện task, rõ ràng 2 cái này là Lưu là người làm UI cho nó thì khi biết được cần fix scrollrect bên MAIL BOX thì tại sao không fix luôn bên NEWS, thái độ thiếu chủ động trong công việc, cần phải chủ động và linh hoạt hơn
-
RE: Lưuposted in Adminstrator
hệ thống trang bị vật phẩm có sáng tạo và giải quyết tốt việc có thể mở rộng sau này, đây là điểm cộng