Chương 11: Từ Điển Thuật Ngữ & 20 Câu Hỏi Thường Gặp (FAQ)
Khi mới bước vào phát triển game mobile chuyên nghiệp, bạn sẽ bắt gặp hàng loạt khái niệm từ lập trình nâng cao (Composition Root, Facade, Service Locator) tới kỹ thuật kiếm tiền (Mediation, Waterfall, eCPM, ILRD) và nền tảng hệ thống (IL2CPP, ATT, EDM4U). Trang tài liệu này cung cấp định nghĩa súc tích cho 40 thuật ngữ quan trọng kèm vị trí áp dụng trong template, cùng giải đáp chi tiết cho 20 câu hỏi thực tế thường gặp nhất.
1. Từ điển 40 thuật ngữ chuyên ngành Game Mobile
A. Lập trình C# & Kiến trúc phần mềm Unity
| Thuật ngữ | Giải thích súc tích | Áp dụng trong Template |
|---|---|---|
| Interface | Hợp đồng trừu tượng định nghĩa danh sách các phương thức và thuộc tính mà các lớp kế thừa bắt buộc phải cài đặt, giúp giảm phụ thuộc trực tiếp. | IAdsService, IIapService, IAnalyticsSink |
| Callback | Hàm được truyền dưới dạng tham số vào một phương thức khác để được kích hoạt thực thi khi một tác vụ bất đồng bộ hoàn tất. | Action<PurchaseResult>, Action<RewardedResult> |
| Delegate | Kiểu dữ liệu an toàn trong C# đại diện cho tham chiếu tới các phương thức có cùng chữ ký (signature), nền tảng cho event và callback. | PurchaseGrantHandler trong IIapService.cs |
| Event | Cơ chế thông báo sự kiện theo mô hình publish-subscribe của C#, bảo vệ delegate khỏi bị gán đè ngoài ý muốn từ bên ngoài. | NotificationService.ScheduleRequested, SceneLoader.SceneLoaded |
| Service Locator | Design pattern cung cấp một điểm truy cập tập trung toàn cục để đăng ký và lấy các service trong ứng dụng mà không cần truyền phụ thuộc thủ công. | ServiceLocator.Get<T>() trong ServiceLocator.cs |
| Composition Root | Điểm khởi đầu duy nhất và sớm nhất của ứng dụng, nơi toàn bộ hệ thống dịch vụ, dependency injection và cấu hình được khởi tạo và kết nối với nhau. | Bootstrapper.cs (Core) và GameEntry.cs (Game) |
| Facade | Lớp đại diện cung cấp giao diện đơn giản hóa cho một hệ thống phức tạp bên dưới, giúp lập trình viên thao tác nhanh mà không bị rò rỉ logic. | Lớp tĩnh App (Core) và GameApp (Game) |
| Asmdef | Assembly Definition trong Unity cho phép chia nhỏ mã nguồn thành các thư viện C# riêng biệt để tăng tốc độ compile và kiểm soát biên giới kiến trúc. | TapBrain.Core.asmdef, TapBrain.Core.Editor.asmdef |
| ScriptableObject | Lớp đối tượng dữ liệu của Unity cho phép lưu trữ cấu hình độc lập với GameObject và Scene, không bị reset giá trị khi chuyển đổi scene. | CoreConfig.cs (file CoreConfig.asset) |
| Coroutine | Hàm đặc biệt trong Unity có khả năng tạm dừng thực thi tại các lệnh yield return và tự động tiếp tục ở khung hình tiếp theo. |
CoreHost.Run(), InitPipeline.cs |
| Domain Reload | Quá trình Unity reset lại AppDomain C# khi nhấn nút Play hoặc biên dịch lại mã nguồn, đưa toàn bộ biến static về giá trị ban đầu. | Quản lý trong Bootstrapper.cs để làm sạch service locator |
B. Quảng cáo Mediation & Kiếm tiền (Monetization)
| Thuật ngữ | Giải thích súc tích | Áp dụng trong Template |
|---|---|---|
| Mediation | Nền tảng trung gian quản lý và phân phối lượt hiển thị quảng cáo từ nhiều mạng quảng cáo khác nhau để tối đa hóa doanh thu cho nhà phát hành. | MaxAdsService.cs tích hợp AppLovin MAX |
| Waterfall | Mô hình phân phối quảng cáo truyền thống nơi các ad network được gọi tuần tự từ mạng có giá eCPM cao xuống thấp cho đến khi có mạng nhận fill. | Được cấu hình tự động trong AppLovin MAX Dashboard |
| In-App Bidding | Cơ chế đấu giá theo thời gian thực (real-time header bidding) giữa các ad network để chọn ra mạng trả giá cao nhất cho từng lượt hiển thị. | Tích hợp qua MAX SDK với AdMob, Mintegral... |
| eCPM | effective Cost Per Mille — Doanh thu ước tính mà ứng dụng kiếm được trên mỗi 1.000 lượt hiển thị quảng cáo thành công. | Chỉ số đo lường hiệu quả trên dashboard MAX và BI |
| ILRD | Impression-Level Revenue Data — Dữ liệu doanh thu quảng cáo chính xác đến từng lượt hiển thị, phục vụ tính toán LTV và attribution. | AnalyticsService.LogAdRevenue() |
| Fill Rate | Tỷ lệ phần trăm số lượt yêu cầu quảng cáo (ad request) được các mạng quảng cáo đáp ứng và trả về nội dung quảng cáo thành công. | Được kiểm tra qua IsInterstitialReady, IsRewardedReady |
| Interstitial | Định dạng quảng cáo xen kẽ hiển thị toàn màn hình, thường xuất hiện giữa các khoảng nghỉ tự nhiên như khi hoàn thành một level. | App.Ads.TryShowInterstitial() |
| Rewarded Ad | Quảng cáo video có tặng thưởng mà người chơi chủ động chọn xem để nhận vật phẩm hoặc lượt chơi tiếp tục trong game. | App.Ads.ShowRewarded() |
| App Open Ad | Định dạng quảng cáo toàn màn hình xuất hiện ngay khi người dùng mở ứng dụng hoặc quay lại game sau khi ứng dụng chạy nền. | App.Ads.TryShowAppOpen() |
| Placement | Tên định danh vị trí hoặc ngữ cảnh cụ thể mà quảng cáo được kích hoạt trong game (ví dụ: level_end, double_reward). |
Khai báo tập trung trong GameKeys.cs |
| DAU & Retention | DAU là số người dùng hoạt động trong ngày; Retention (D1, D7, D30) là tỷ lệ phần trăm người chơi tiếp tục quay lại game sau các mốc ngày. | Được tính toán qua UserIdentity và GameAnalytics |
C. In-App Purchase (IAP) & Kinh tế Game
| Thuật ngữ | Giải thích súc tích | Áp dụng trong Template |
|---|---|---|
| Consumable | Gói vật phẩm IAP tiêu dùng được (như Coins, Gems) có thể mua nhiều lần và bị trừ dần trong quá trình chơi game. | GameWallet.GrantProduct() (gói coins_small) |
| Non-Consumable | Gói IAP vĩnh viễn (như Remove Ads) chỉ cần mua một lần duy nhất trong đời và có thể khôi phục lại khi cài lại game trên máy mới. | CoreConfig.removeAdsProductId |
| Receipt Validation | Quy trình xác minh tính hợp lệ và chữ ký mật mã của hóa đơn thanh toán từ Google Play hoặc Apple App Store để chống gian lận IAP. | Xử lý tự động trong IapService.cs |
| Tangle / Obfuscator | Kỹ thuật làm rối và mã hóa public license key của store thành các mảng byte trong code C# để ngăn hacker can thiệp vào file APK/IPA. | Thư mục Assets/Scripts/UnityPurchasing/generated/ |
| Pending Purchase | Giao dịch mua hàng đang ở trạng thái chờ thanh toán từ phía ngân hàng/store, hoặc đã trừ tiền nhưng game chưa xác nhận cấp phát. | PendingOrderStore.cs lưu lại để cấp phát ở phiên sau |
D. Nền tảng Backend, SDKs & Công cụ Build
| Thuật ngữ | Giải thích súc tích | Áp dụng trong Template |
|---|---|---|
| Remote Config | Dịch vụ đám mây cho phép thay đổi các tham số cấu hình game từ xa theo thời gian thực mà không cần người chơi phải tải bản cập nhật mới. | RemoteConfigService.cs (Firebase Remote Config) |
| A/B Testing | Phương pháp thử nghiệm so sánh 2 hoặc nhiều phiên bản tham số trên các nhóm người chơi khác nhau để tìm ra phương án tối ưu nhất. | Thực hiện thông qua Firebase Remote Config & Analytics |
| RTDB | Firebase Realtime Database — Cơ sở dữ liệu đám mây NoSQL đồng bộ dữ liệu JSON theo thời gian thực giữa máy khách và máy chủ. | RealtimeDbService.cs, BIService.cs |
| Crashlytics | Hệ thống giám sát và báo cáo lỗi crash ứng dụng thời gian thực của Firebase, giúp đội ngũ dev theo dõi callstack lỗi chi tiết trên máy người dùng. | Khởi tạo trong FirebaseService.cs |
| Anonymous Auth | Cơ chế xác thực ẩn danh của Firebase, tự động cấp một mã người dùng duy nhất (UID) cho thiết bị mà không bắt người chơi nhập email/mật khẩu. | FirebaseService.AuthUid, cần thiết cho RTDB |
| ATT | App Tracking Transparency — Khung bảo mật bắt buộc của Apple trên iOS 14.5+ yêu cầu ứng dụng phải xin phép trước khi lấy mã IDFA. | AttService.cs (bước boot đầu tiên trên iOS) |
| IDFA | Identifier for Advertisers — Mã định danh quảng cáo ngẫu nhiên trên thiết bị iOS giúp các mạng ad tối ưu hóa phân phối quảng cáo chính xác. | Được cấp phép qua ATT và gửi cho AppLovin MAX |
| SKAdNetwork | Hệ sinh thái đo lường chuyển đổi và hiệu quả quảng cáo bảo vệ quyền riêng tư do Apple cung cấp độc quyền trên hệ điều hành iOS. | Cấu hình tự động vào Info.plist bởi MAX SDK |
| EDM4U | External Dependency Manager for Unity — Công cụ tự động giải quyết và tải các thư viện Android (AAR qua Maven) và iOS (CocoaPods). | Menu Assets > External Dependency Manager |
| Gradle | Hệ thống tự động hóa build tiêu chuẩn của Google cho Android, được Unity sử dụng để biên dịch và đóng gói file APK hoặc Android App Bundle (AAB). | Các file template trong Assets/Plugins/Android/ |
| CocoaPods | Trình quản lý thư viện mã nguồn mở tiêu chuẩn cho iOS và macOS, được EDM4U gọi để cài đặt SDK Firebase và MAX vào dự án Xcode. | Tự động kích hoạt khi build iOS từ Unity Editor |
| IL2CPP | Trình biên dịch chuyển đổi mã bytecode C# IL thành mã nguồn C++ native trước khi build, giúp tối ưu hóa hiệu năng và tăng tính bảo mật cho game. | Thiết lập bắt buộc trong Player Settings cho Android & iOS |
| Bundle Identifier | Chuỗi định danh duy nhất của ứng dụng trên chợ ứng dụng Google Play và App Store (ví dụ: com.tapbrain.blockpuzzle). |
PlayerSettings và CoreConfig.androidPackageName |
2. 20 Câu Hỏi Thực Tế Thường Gặp (FAQ)
Q1: Tại sao trong Scene không có GameObject nào chứa hàm Start() hay Awake() để khởi động game?
Trả lời: Template sử dụng tính năng thuộc tính C# mạnh mẽ của Unity: [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] trên phương thức Bootstrapper.Boot() (đặt tại Assets/_Project/Core/Bootstrap/Bootstrapper.cs). Nhờ đó, Unity tự động kích hoạt hàm khởi động này ngay khi người dùng mở ứng dụng, trước khi bất kỳ Scene nào kịp nạp vào bộ nhớ. Điều này giúp loại bỏ hoàn toàn sự phụ thuộc vào các prefab "GameManager" rải rác trong scene.
Q2: Tôi viết mã nguồn gameplay thật ở đâu và kết nối vào hệ thống như thế nào?
Trả lời: Toàn bộ mã nguồn gameplay đặt trong thư mục Assets/_Project/Game/Gameplay/. Điểm vào chính của bạn là phương thức GameplayController.BeginLevel() (được đánh dấu comment TODO(game)). Khi lượt chơi kết thúc (người chơi ghép xong khối, giải đố xong...), bạn chỉ cần gọi phương thức GameplayController.EndLevel(bool won). Toàn bộ logic cộng thưởng, hiện popup kết quả, hiển thị quảng cáo xen kẽ và video x2 thưởng đều đã được template lắp ráp sẵn.
Q3: Tại sao quảng cáo không hiển thị trong Unity Editor khi tôi nhấn test?
Trả lời: Có 2 trường hợp. (1) maxSdkKey trong CoreConfig còn là REPLACE_ME: MaxAdsService bỏ qua việc khởi tạo MAX (log [Ads] MAX SDK key not set in CoreConfig, ads disabled), nên ShowRewarded trả về RewardedResult.NotReady và TryShowInterstitial bỏ qua nhưng vẫn gọi callback. Đây là trạng thái của template gốc và không phải lỗi. (2) Đã điền SDK key và ad unit thật: trong Editor, AppLovin MAX hiển thị quảng cáo giả (editor mock) để bạn test luồng đóng/nhận thưởng. Quảng cáo thật chỉ xuất hiện trên máy thật. Dùng DebugServicePanel (phím F1) để bấm Show Interstitial/Show Rewarded và xem trạng thái sẵn sàng.
Q4: Tôi có thể cài đặt thêm các plugin từ Unity Asset Store (như Spine, Odin, Feel...) không?
Trả lời: Hoàn toàn được và rất dễ dàng! Thư mục Assets/_Project/Game/ có chủ đích không sử dụng asmdef, nghĩa là nó được biên dịch trực tiếp vào Assembly-CSharp mặc định của Unity. Nhờ vậy, mọi script gameplay của bạn có thể trực tiếp gọi đến bất kỳ plugin nào từ Asset Store mà không bao giờ gặp lỗi thiếu tham chiếu Assembly.
Q5: Làm thế nào để thêm một Scene mới vào dự án một cách an toàn và đúng chuẩn?
Trả lời: Bạn chỉ cần thực hiện đúng 3 bước:
- Mở file
Assets/_Project/Core/Infra/SceneLoader.cs, thêm tên scene mới vào enumSceneId. - Mở
Assets/_Project/Editor/TemplateSetup.cs, thêm scene đó vào danh sách trong phương thứcApplyBuildSettings(). - Trên thanh menu Unity, nhấn TapBrain → Setup → Apply Build Settings. Khi cần chuyển sang scene mới từ code, hãy gọi
App.Scenes.Load(SceneId.YourNewScene).
Q6: Điều gì xảy ra nếu tôi mở trực tiếp scene Main hoặc Home rồi nhấn Play trong Unity Editor?
Trả lời: Game vẫn khởi động và chạy hoàn toàn bình thường! Nhờ cơ chế BeforeSceneLoad của Bootstrapper, toàn bộ các service cốt lõi (ServiceLocator, Config, Storage) đều được khởi tạo trước khi scene Main hay Home xuất hiện. Bạn không bao giờ bị văng lỗi NullReferenceException do quên chạy từ scene Loading khi đang debug dở một màn chơi.
Q7: Làm sao để thay đổi thời gian giãn cách giữa các lần hiện quảng cáo Interstitial mà không cần build lại game?
Trả lời: Mở Firebase Console → vào mục Remote Config → tìm tham số inter_interval_sec và sửa thành số giây bạn mong muốn (ví dụ 45) → nhấn Publish changes. Remote Config chỉ được fetch một lần ở bước boot (InitPipeline), nên giá trị mới có hiệu lực từ lần mở app tiếp theo của mỗi người chơi có mạng. Sau đó AdsPolicy đọc giá trị này mỗi lần quyết định có hiện interstitial hay không. Người chơi offline vẫn dùng giá trị mặc định trong CoreConfig.remoteConfigDefaults (30 giây).
Q8: Tại sao tôi bấm mua gói IAP trong Unity Editor thì lại hiện popup Buy/Cancel mà không bị trừ tiền thật?
Trả lời: Unity IAP tích hợp chế độ Fake Store riêng cho môi trường Editor để lập trình viên có thể kiểm tra toàn bộ luồng mua hàng và kiểm thử hàm cấp phát quà mà không cần phải thực hiện thanh toán thẻ tín dụng thật. Bạn bấm Buy để giả lập mua thành công hoặc Cancel để giả lập hủy.
Q9: Tại sao tôi đã mua IAP thành công nhưng tài khoản trong game không được cộng coin?
Trả lời: Có 2 nguyên nhân phổ biến nhất: 1) Bạn chưa đăng ký hàm cấp phát (App.Iap.SetGrantHandler) trong GameEntry.cs; hoặc 2) Hàm GrantProduct của bạn chưa có nhánh case khớp với mã Product ID của gói đó và đang trả về false. Hãy mở Assets/_Project/Game/Meta/GameWallet.cs và bổ sung mã Product ID vào lệnh switch-case.
Q10: Game của tôi có chơi offline được hoàn toàn khi mất mạng hoặc bật chế độ máy bay không?
Trả lời: Có, 100% chơi được mượt mà! Hệ thống khởi động InitPipeline quy định mỗi bước boot đều có một giới hạn timeout riêng (Firebase 8s, Remote Config 4s, Ads 6s, IAP 8s...; ATT và Notifications chờ người chơi bấm popup lâu hơn). Khi không có mạng, các bước kết nối server sẽ tự động timeout, ghi nhận log cảnh báo và bỏ qua để vào thẳng scene Home. Game tự nạp bộ tham số offline từ CoreConfig.remoteConfigDefaults và dữ liệu lưu từ LocalStorage.
Q11: Có được phép tạo class Singleton với biến static Instance trong thư mục Game/ không?
Trả lời: Tuyệt đối KHÔNG! Quy tắc R4 của template nghiêm cấm mẫu thiết kế Singleton static Instance. Nếu bạn cần một dịch vụ sống suốt game (như Economy, Quest, Inventory), hãy tạo một class C# thuần thông thường, đăng ký nó vào ServiceLocator trong GameEntry.Init() và thêm một thuộc tính truy cập nhanh trong GameApp.cs.
Q12: Sự khác biệt về vai trò và phạm vi giữa App.X và GameApp.X là gì?
Trả lời:
App.X(nằm trong namespaceTapBrain.Core): Là Facade đại diện cho các dịch vụ hạ tầng nền tảng của template dùng chung cho mọi dự án (nhưApp.Ads,App.Iap,App.Scenes,App.Dialogs).GameApp.X(nằm trong namespaceTapBrain.Game): Là Facade đại diện cho các dịch vụ nghiệp vụ thuộc riêng logic của game bạn đang phát triển (nhưGameApp.Wallet,GameApp.Progress).
Q13: Tại sao tôi không thể gọi code của thư mục Game/ từ bên trong thư mục Core/?
Trả lời: Thư mục Core/ được gói trong assembly riêng biệt là TapBrain.Core.asmdef, còn thư mục Game/ nằm ở Assembly-CSharp. Theo quy tắc kiến trúc chuẩn R2, Core là nền tảng dùng chung không được phụ thuộc vào bất kỳ logic đặc thù nào của Game. Nếu Core tham chiếu sang Game, compiler sẽ báo lỗi biên dịch ngay lập tức.
Q14: Khi nào tôi nên lưu dữ liệu bằng LocalStorage và khi nào nên dùng RealtimeDbService?
Trả lời: Sử dụng LocalStorage cho toàn bộ dữ liệu cần truy xuất tức thì ngay trên thiết bị của người chơi (số coin, màn chơi hiện tại, tùy chọn bật/tắt âm thanh). Chỉ sử dụng RealtimeDbService khi bạn cần sao lưu hồ sơ người dùng lên đám mây Firebase hoặc đẩy dữ liệu phân tích BI (Business Intelligence) phục vụ thống kê người dùng.
Q15: Làm sao để tạo một popup (dialog) mới hiển thị trên màn hình với hiệu ứng mở/đóng chuẩn?
Trả lời: 1) Tạo một script mới kế thừa từ DialogBase (ví dụ: public class ShopDialog : DialogBase). 2) Tạo một prefab có cùng tên ShopDialog.prefab đặt trong thư mục Assets/_Project/Resources/Dialogs/, gốc prefab kéo component ShopDialog và gán panel vào biến content. 3) Trong code, mở popup bằng lệnh: App.Dialogs.Show<ShopDialog>().
Q16: Tại sao các menu TapBrain > Setup > Rebuild Scenes lại cực kỳ nguy hiểm nếu đã chỉnh sửa scene?
Trả lời: Các menu này được thiết kế để tự động sinh lại mã nguồn các scene ban đầu của template từ code C#. Khi bạn nhấn chạy, Unity sẽ ghi đè hoàn toàn và xóa sạch mọi đối tượng, hình ảnh, animation bạn đã tự tay kéo thả vào 3 scene Loading, Home và Main mà không có nút Undo phục hồi! Vì vậy mỗi menu đều hiện hộp xác nhận trước khi chạy, còn Apply Template Player Settings cảnh báo thêm một lần nếu bundle id đã khác com.tapbrain.template. Đừng bấm "Overwrite" trên game đã clone.
Q17: Làm sao để tắt một dịch vụ không dùng đến (ví dụ game không có IAP hoặc không dùng AppMetrica)?
Trả lời: Mở file Assets/_Project/Resources/CoreConfig.asset (hoặc vào menu TapBrain → Select Core Config). Tại nhóm Modules, bạn chỉ cần bỏ tích ô tương ứng (ví dụ: bỏ tích enableIap hoặc enableAppMetrica). Bước boot tương ứng trong pipeline sẽ tự động biến thành no-op và bị bỏ qua trong nháy mắt.
Q18: Tại sao Console báo lỗi thiếu DOTween.Modules hoặc GameAnalyticsILRD sau khi cập nhật SDK?
Trả lời: Khi bạn tải bản cập nhật mới của các SDK này từ Package Manager hoặc Asset Store, quá trình import thường xóa mất file .asmdef phụ trợ mà template đã tạo sẵn. Bạn chỉ cần đọc mục 2 trong docs/AGENT_GUIDE.md và tạo lại các file asmdef tương ứng ở đúng đường dẫn thư mục là lỗi sẽ biến mất.
Q19: Làm sao để xử lý nút Back trên thiết bị Android một cách thống nhất trong toàn bộ game?
Trả lời: Template đã xử lý tập trung nút Back thông qua CoreHost (bắt sự kiện Input.GetKeyDown(KeyCode.Escape)). Khi có popup đang mở, DialogService tự động lắng nghe sự kiện này và đóng popup trên cùng của ngăn xếp (Stack). Nếu bạn muốn xử lý riêng cho một màn hình, chỉ cần đăng ký lắng nghe sự kiện ServiceLocator.Get<CoreHost>().BackPressed += OnBack;.
Q20: Tôi muốn thêm một sự kiện phân tích (Analytics Event) mới khi người chơi tương tác thì làm thế nào?
Trả lời: Chỉ cần gọi một dòng lệnh thông qua facade Analytics:
App.Analytics.LogEvent("level_boss_defeated", new Dictionary<string, object>
{
{ "boss_id", "dragon_fire" },
{ "duration_sec", 45 },
{ "hp_remaining", 120 }
});
Hệ thống AnalyticsService sẽ tự động định dạng và chuyển tiếp sự kiện này đồng thời tới tất cả các nhà cung cấp phân tích đang bật (Firebase Analytics, GameAnalytics, AppMetrica).