Chương 9: Chọn & Chuyển Provider (MGW ↔ SayKit)
Khám phá mô hình trừu tượng hóa Provider trong MGW SDK: quy tắc độc quyền chính xác một Provider cho mỗi bản build, bảng đối chiếu chuyên sâu giữa ngăn xếp MGW (Firebase + MAX) và SayKit, công cụ chuyển đổi tự động trong Editor, và kỹ thuật viết code gameplay an toàn chạy đa nền tảng.
1. Mục đích & Mô hình kiến trúc Provider
Trong quá trình phát triển game di động, studio có thể tự phát hành tựa game với hệ thống phân tích và quảng cáo riêng (ngăn xếp MGW gồm Firebase và AppLovin MAX), hoặc hợp tác phát hành với đơn vị bên ngoài yêu cầu bộ công cụ chuyên biệt của họ (tiêu biểu là SayKit từ SayGames).
Thay vì yêu cầu đội ngũ lập trình phải viết lại toàn bộ mã nguồn quản lý quảng cáo, mua hàng IAP hay luồng sự kiện phân tích, com.mgw.sdk thiết lập lớp trừu tượng thông qua interface ISdkProvider (kiểu internal của SDK: game không hiện thực hay gọi trực tiếp). Các dịch vụ cấp cao của game luôn gọi qua mặt nạ tĩnh App.Ads, App.Analytics, App.RemoteConfig, App.Iap và App.Review:
Mỗi bản build chỉ được phép có chính xác một Provider được biên dịch.
- Nếu có 2 Provider cùng tồn tại: Hệ thống sẽ in lỗi
[Sdk] Two SDK providers compiled in (...)(log lỗi lúc chạy, không phải lỗi biên dịch và không do Build Guard bắt) và Provider thứ hai sẽ bị bỏ qua hoàn toàn. Trên thực tế asmdef đã loại trừ nhau bằngTB_SDK_SAYKIT, nên tình huống này chỉ xảy ra khi cấu hình define bị phá hỏng. - Nếu không có Provider nào được biên dịch: Hệ thống sẽ in lỗi
[Boot] No SDK provider compiled in: ads disabled, Remote Config uses defaultsvà chốt chặn Build Guard sẽ hủy bỏ quá trình build ngay lập tức. - Hai module phân tích GameAnalytics và AppMetrica là module bổ trợ dành riêng cho MGW. Khi chạy trên SayKit, hai module này tuyệt đối không được kích hoạt vì SayKit yêu cầu gỡ bỏ triệt để các SDK phân tích và quảng cáo ngoài.
2. Bảng đối chiếu chi tiết MGW vs SayKit
Bảng dưới đây tổng hợp toàn diện sự khác biệt giữa hai ngăn xếp công nghệ để giúp bạn nắm rõ hành vi thực tế ở thời gian chạy và các yêu cầu tích hợp:
| Hạng mục dịch vụ | Provider MGW (Mặc định) | Provider SayKit (SayGames) |
|---|---|---|
| Dịch vụ Quảng cáo (App.Ads) |
Triển khai qua MaxAdsService (AppLovin MAX).- Kiểm soát tần suất hiển thị interstitial bằng AdsPolicy cục bộ đọc từ Remote Config (giới hạn thời gian inter_first_delay_sec và inter_interval_sec).- Mua Remove Ads: Tự động hủy banner, chặn interstitial và app open (Rewarded vẫn phục vụ). - Hỗ trợ format App Open ( TryShowAppOpen()): cần ad unit App Open trong CoreConfig và app_open_enabled = true; SDK cũng tự thử hiện khi người chơi quay lại từ nền.
|
Triển khai qua SayKitAdsService.- Không áp dụng AdsPolicy vì SayKit tự giới hạn tần suất trên máy chủ (server-side per place).- Mua Remove Ads: Gọi SayKit.enablePremium() (dừng banner, interstitial và cross-promo; Rewarded vẫn phục vụ).- Không hỗ trợ App Open ( TryShowAppOpen() luôn trả về false).- Trạng thái sẵn sàng được lưu đệm (cache) 1 giây để tránh gọi dồn API. |
| Hệ thống Phân tích (App.Analytics) |
Định tuyến qua FirebaseAnalyticsSink.- Khi bật tùy chọn: Tự động phân phối đồng thời sang GameAnalyticsSink và AppMetricaSink.- Toàn bộ sự kiện được chuẩn hóa qua TrackEventMapper.ToGeneric.
|
Định tuyến trực tiếp sang SayKitAnalyticsSink (triển khai ITrackingSink).- Nhận trực tiếp các hàm chuẩn native: trackLevel*, trackLevelStage*, trackItem, trackScreen, trackClick, trackSoftIncome, v.v.- SetUserProperty, LogAdRevenue, LogPurchase là no-op trong sink (SayKit tự bắt doanh thu quảng cáo qua callback adRevenuePaid).
|
| Cấu hình từ xa (App.RemoteConfig) |
Triển khai qua FirebaseRemoteConfigService.- Dữ liệu quản trị tập trung trên Firebase Console. - Giá trị mặc định offline thiết lập trong CoreConfig.asset.
|
Triển khai qua SayKitRemoteConfigService.- Đọc dữ liệu từ đối tượng SayKit.gameConfig sau khi nhận sự kiện remoteConfigUpdated.- Yêu cầu các field phải được khai báo trước trong file SayKitGameConfig.cs.
|
| Quyền riêng tư & Đồng thuận (Consent / ATT) |
HandlesConsent = false.- Bước khởi động ATT của Core sẽ hiển thị hộp thoại xin quyền theo dõi IDFA trên iOS.- MAX tự quản lý luồng GDPR Consent Flow thông qua cấu hình trong Integration Manager. |
HandlesConsent = true.- SayKit tự điều phối toàn bộ luồng xin quyền ATT và popup GDPR của họ. - Core tự động bỏ qua bước boot ATT để tránh hiện hai lần hộp thoại làm người dùng bối rối.
|
| Theo dõi mua IAP (IAP Tracking) |
Dịch vụ IapService quản lý mua hàng qua Unity IAP.- Ghi nhận mua hàng qua các sink đang hoạt động: Firebase Analytics, AppMetrica và GameAnalytics (khi module được biên dịch và bật). |
Cũng dùng IapService của SDK Core.- Khi có define SAYKIT_PURCHASING, hệ thống kích hoạt SayKitPurchaseTracker để tự động chuyển tiếp đơn hàng sang SayKit.trackOrderPurchase.
|
| Dịch vụ bổ trợ đặc thù |
Có sẵn: FirebaseService, RealtimeDbService, BIService, và NotificationService (thông báo cục bộ trên thiết bị).
|
Thay thế IReviewService bằng SayKitReviewService (gọi SayKit.showRateAppPopup(), vừa xin đánh giá vừa kết thúc chế độ quảng cáo nhẹ ban đầu của SayKit).
|
| Các gói SDK bắt buộc cài trong dự án |
- com.applovin.mediation.ads (UPM)- Các gói Firebase 12.x tarball ( .tgz)- com.google.external-dependency-manager- com.unity.mobile.notifications (assembly MGW tham chiếu Unity.Notifications.*)- Cả hai provider đều cần com.unity.purchasing (đã khai báo trong dependency của com.mgw.sdk).
|
- io.saygames.saykit (tải qua SayKit Version Manager).- Bắt buộc không còn các gói: com.applovin.mediation.ads, com.google.firebase.app, com.google.external-dependency-manager, com.gameanalytics.sdk, io.appmetrica.analytics (Build Guard chặn mọi bản build nếu còn). com.unity.mobile.notifications không nằm trong danh sách này và không bị gỡ.
|
| File & Key cần cung cấp |
- Assets/google-services.json (Android)- Assets/GoogleService-Info.plist (iOS)- MAX SDK Key và Ad Units trong CoreConfig.asset (MAX SDK Key còn phải khai báo trong AppLovin Integration Manager).- Key AppMetrica trong CoreConfig.appMetricaApiKey (nếu dùng AppMetrica); key GameAnalytics trong cửa sổ GameAnalytics Settings.
|
- Các hằng số trong Assets/SayKit/SayKitApp.cs: APP_KEY_ANDROID, APP_KEY_IOS, APP_NAME_IOS, APP_BUNDLE_IOS (Build Guard kiểm tra bốn hằng số này), cùng APP_SECRET_ANDROID, APP_SECRET_IOS và PROMO_KEY_IOS do SayGames cấp.- Khai báo biến Remote Config trong SayKitGameConfig.cs.
|
3. Các Scripting Define Symbols & Quy tắc quản lý
Kiến trúc của SDK chuyển đổi mã nguồn C# thông qua các cờ biên dịch (Scripting Defines). Mỗi nhóm define có nhiệm vụ và phạm vi hoạt động cụ thể:
| Ký hiệu Define | Thiết lập bởi | Mục đích & Hiệu lực biên dịch |
|---|---|---|
TB_SDK_SAYKIT |
SdkSettings.Apply khi chọn SayKit |
Loại trừ assembly TapBrain.Sdk.MGW, GameAnalytics, AppMetrica; kích hoạt assembly TapBrain.Sdk.SayKit. Khi vắng mặt, mặc định dự án dùng MGW. |
TB_USE_GA |
SdkSettings.Apply (Chỉ MGW) |
Kích hoạt biên dịch assembly TapBrain.Sdk.GameAnalytics khi đã có gói thư viện tương ứng. |
TB_USE_APPMETRICA |
SdkSettings.Apply (Chỉ MGW) |
Kích hoạt biên dịch assembly TapBrain.Sdk.AppMetrica và bật điều kiện kiểm tra API Key trong Build Guard. |
TB_HAS_GA_PACKAGETB_HAS_APPMETRICA_PACKAGETB_HAS_SAYKIT_PACKAGE |
Tự động qua versionDefines trong từng asmdef |
Đảm bảo assembly SDK bên thứ ba chỉ biên dịch khi package tương ứng thực sự tồn tại trong Packages/manifest.json. Cờ này chỉ nhìn thấy nội bộ trong asmdef đó. |
SAYKIT_PURCHASING |
SdkSettings.Apply (Chỉ SayKit) |
Bắt buộc kích hoạt theo tài liệu SayKit để theo dõi IAP; biên dịch lớp SayKitPurchaseTracker. |
SAYKIT_NOTIFICATIONS |
SdkSettings.Apply khi bật toggle SayKit push notifications trong SDK Settings (chỉ SayKit, tùy chọn) |
Kích hoạt tính năng thông báo đẩy từ xa của SayGames (SayKit Remote Push). Hoàn toàn độc lập với NotificationService cục bộ của MGW. |
gameanalytics_max_enabled |
SdkSettings.Apply (chỉ MGW, chỉ Android/iOS; được bật cùng provider MGW, không phụ thuộc toggle GameAnalytics) |
Kích hoạt tích hợp theo dõi doanh thu Impression-Level Ad Revenue (ILRD) giữa MAX và GameAnalytics. |
Lập trình viên tuyệt đối không thêm hoặc xóa các define trên bằng tay trong cửa sổ Project Settings > Player > Scripting Define Symbols, và không sửa tay Packages/manifest.json để đổi Provider.
Nguyên nhân: Việc đổi Provider đòi hỏi đồng bộ hóa đồng thời trên cả 3 nền tảng (Android, iOS, Standalone), cập nhật Packages/manifest.json, và di chuyển an toàn các file cấu hình nhạy cảm. Provider chỉ được đổi bằng menu TapBrain > SDK > Switch To ... (hoặc nút Switch To ... trong cửa sổ TapBrain > SDK Settings...). Bản thân cửa sổ SDK Settings chỉ bật/tắt module của Provider hiện tại (GameAnalytics/AppMetrica trên MGW, SayKit push trên SayKit) rồi nhấn Apply (recompiles).
4. Hướng dẫn chuyển đổi Provider trong Unity Editor
SDK cung cấp công cụ tự động hóa toàn diện cho việc chuyển đổi ngăn xếp SDK nằm trong menu Unity Editor:
A. Các lối vào chuyển đổi trong Unity Editor
- Chuyển sang SayKit: Menu
TapBrain > SDK > Switch To SayKit... - Chuyển về MGW: Menu
TapBrain > SDK > Switch To MGW... - Giao diện điều khiển: Menu
TapBrain > SDK Settings...→ Bấm nútSwitch To <Tên Provider đối ứng>....
- Hộp thoại xác nhận nhắc commit trước: việc chuyển sửa
Packages/manifest.json, scripting defines và nhiều file trongAssets/. - Chuyển sang SayKit lần đầu bị chặn (hộp thoại chỉ hiện lý do, không thực hiện) nếu: gói
io.saygames.saykitchưa có trong manifest, thiếuAssets/SayKitExtensions/GameConfig/SayKitGameConfig.cs(mởSayKit > Version Managerđể nó sinh ra), hoặc không tìm thấy file mẫuSayKitApp.cs.txtcủa SDK. - Chuyển về MGW chỉ khả dụng khi stack MGW đã được cất giữ từ lần chuyển sang SayKit trước đó. Dự án chưa từng chạy MGW sẽ bị chặn với thông báo "No MGW stack parked".
- Bấm chuyển sang Provider đang dùng sẽ báo "The project already uses ...". Nếu file của stack đích còn sót trong
Assets/và không thuộc stack đang rời đi, bộ chuyển cũng chặn và yêu cầu xóa hoặc di chuyển một trong hai bản.
B. Cơ chế hoạt động của bộ chuyển đổi ngăn xếp SDK
Khi bạn xác nhận chuyển đổi, công cụ sẽ thực hiện tuần tự các bước nghiêm ngặt sau:
-
Cất giữ ngăn xếp rời đi (Park):
- Sao chép toàn bộ các thư mục, file cấu hình và file
.metacủa Provider hiện tại ra một thư mục lưu trữ stash riêng bên ngoàiAssets/(tránh bị Unity import hay gây lỗi biên dịch). - Đối với MGW, các mục được cất giữ bao gồm:
Assets/MaxSdk,Assets/GeneratedLocalRepo, các file thư viện Android Firebase trongAssets/Plugins/Android/, fileAssets/google-services.json,Assets/GoogleService-Info.plist, cấu hình Crashlytics và thư mục GameAnalytics. - Đối với SayKit, các mục được cất giữ bao gồm:
Assets/SayKit,Assets/SayKitExtensions, vàAssets/Resources/SayKit. - Các file cấu hình build Android trong
Assets/Plugins/Android/(các file.gradlevàAndroidManifest.xml) cũng được cất theo stack đang active. - Trích xuất các dependency và scoped registry thuộc sở hữu của provider rời đi ra khỏi
Packages/manifest.jsonvà lưu vào file trạng tháistack.jsontrong thư mục stash. - Thực hiện xóa an toàn các thư mục đã được cất khỏi
Assets/quaAssetDatabase.DeleteAssets.
- Sao chép toàn bộ các thư mục, file cấu hình và file
-
Khôi phục ngăn xếp đích (Restore):
- Nếu Provider đích đã từng được cất trước đó: Khôi phục toàn bộ file, dependency trong manifest, scoped registry, cờ bật tắt module và xóa thư mục stash của Provider đích.
- Nếu là lần đầu chuyển sang SayKit: xóa
Assets/SayKitExtensions/SayKitApp.cs(bản của trình cài đặt SayKit, tự gọiSayKit.init) và tạoAssets/SayKit/SayKitApp.cstừ file mẫu của SDK (không có hàm init).
-
Cập nhật cấu hình & Biên dịch lại:
- Ghi đè file
Packages/manifest.json(giữ nguyên chuẩn kết thúc dòng CRLF). - Cập nhật toàn bộ Scripting Defines tương ứng trên Android, iOS và Standalone thông qua
SdkSettings.Apply. - Đặt
CoreConfig.enableAppUpdateCheck = falsekhi chuyển sang SayKit (vì thư viện native Google Play Core phụ thuộc EDM4U vốn đã được gỡ bỏ cùng MGW); khi chuyển về MGW, giá trị cũ được khôi phục từ trạng thái đã cất. - Kích hoạt
AssetDatabase.Refresh()vàClient.Resolve()để Unity giải quyết package UPM và biên dịch lại code một lần duy nhất.
- Ghi đè file
5. Quy trình thiết lập SayKit lần đầu cho dự án
Khi dự án bắt đầu tích hợp SayKit lần đầu tiên, hãy thực hiện theo đúng các bước sau:
Bước 1: Nhập gói SayKit Version Manager
Nhập file .unitypackage do SayGames cung cấp vào dự án. Gói này chỉ chứa file công cụ quản lý phiên bản SayKitVersionManager.dll.
Bước 2: Cài đặt SayKit qua Version Manager
Mở menu SayKit > Version Manager trên thanh công cụ Unity. Tiến hành cài đặt gói phiên bản SayKit (ví dụ 2026091100).
Tuyệt đối không cài đặt SayGames Services (tab riêng trong Version Manager). Chỉ cài đặt phần SayKit Core. Sau khi cài xong, Version Manager sẽ tự động thêm gói io.saygames.saykit vào Packages/manifest.json và sinh thư mục Assets/SayKitExtensions cùng file SayKitGameConfig.cs.
Bước 3: Thực hiện chuyển đổi Provider trên SDK
Chọn menu:
TapBrain > SDK > Switch To SayKit...
Hộp thoại xác nhận sẽ liệt kê toàn bộ các gói và thư mục MGW sẽ được cất giữ an toàn. Bấm Switch. Bộ chuyển đổi sẽ tự động dọn dẹp file cài đặt thừa và tạo file Assets/SayKit/SayKitApp.cs chuẩn hóa từ SDK.
Bước 4: Điền thông số Key và Remote Config
1. Mở file Assets/SayKit/SayKitApp.cs và thay các giá trị giữ chỗ dạng <...> bằng khóa do SayGames cấp. Chỉ sửa giá trị của các hằng số có sẵn: giữ nguyên tên class SayKitApp (namespace toàn cục) và tên các hằng số vì SayKit cùng SDK đọc chúng theo tên; không thêm hàm [RuntimeInitializeOnLoadMethod] (Build Guard sẽ chặn build).
// Assets/SayKit/SayKitApp.cs: only replace the placeholder values, keep every other generated member as it is.
public class SayKitApp
{
public const string APP_NAME_IOS = "MyGame";
public const string APP_BUNDLE_IOS = "com.mycompany.mygame";
public const string APP_KEY_IOS = "your_saygames_ios_key";
public const string APP_SECRET_IOS = "your_saygames_ios_secret";
public const string APP_KEY_ANDROID = "your_saygames_android_key";
public const string APP_SECRET_ANDROID = "your_saygames_android_secret";
// ... other generated members (notificationsEnabled, PROMO_KEY_IOS, AD_INTERSTITIAL, AD_REWARDED...)
}
2. Khai báo các khóa Remote Config của game vào file Assets/SayKitExtensions/GameConfig/SayKitGameConfig.cs (thêm field vào class đã được Version Manager sinh ra). Tên field trùng với tên key Remote Config và giá trị khởi tạo là giá trị mặc định. Key không có field tương ứng sẽ chỉ dùng giá trị mặc định cục bộ (CoreConfig hoặc AddDefault). Các key inter_interval_sec/inter_first_delay_sec của AdsPolicy không cần khai báo vì SayKit không áp dụng AdsPolicy:
// Add fields to the existing SayKitGameConfig class; field name = Remote Config key, initial value = default.
public float coin_reward_multiplier = 2f;
public int daily_bonus_coins = 50;
public bool shop_banner_enabled = false;
Đồng bộ SayKitAppSettings.asset
SayKit đọc file Assets/Resources/SayKit/SayKitAppSettings.asset khi chạy; thiếu file này, Play mode trong Editor sẽ lỗi bên trong SayKit.init. Lớp SayKitSettingsSync của SDK tự tạo lại file này (qua importer của SayKit) sau mỗi lần biên dịch khi file đang thiếu; nó không ghi đè file đã tồn tại. Sau khi sửa khóa trong SayKitApp.cs, hãy chủ động đồng bộ qua menu (chỉ có khi stack SayKit đang được biên dịch):
TapBrain > SDK > Sync SayKit App Settings
6. Các quy tắc Build Guard theo từng Provider
Lớp CoreConfigTools (triển khai IPreprocessBuildWithReport) tự động rà soát tính hợp lệ trước khi Unity xuất bản bản build:
| Điều kiện kiểm tra | Provider áp dụng | Mức độ nghiêm trọng & Hành vi |
|---|---|---|
| Chưa biên dịch Provider nào (!HasProviderCompiled) |
Cả MGW & SayKit | Chặn tất cả các bản build (kể cả Development Build) bằng BuildFailedException: "[SdkSettings] No SDK provider compiled for provider ... Fix it in TapBrain > SDK Settings." |
| Số lượng class SayKitApp ≠ 1 | SayKit | Chặn mọi bản build (kể cả Development). Thường do còn sót file Assets/SayKitExtensions/SayKitApp.cs (bản của trình cài đặt, khởi tạo SayKit lần thứ hai). Hãy xóa file trùng lặp này. |
| SayKitApp có hàm init tự động | SayKit | Chặn mọi bản build (kể cả Development). SayKitApp không được chứa hàm mang attribute [RuntimeInitializeOnLoadMethod] vì SDK đã tự gọi SayKit.init trong SayKitProvider. |
| Còn sót package của MGW | SayKit | Chặn mọi bản build (kể cả Development). Báo lỗi ngay nếu còn bất kỳ gói nào: com.applovin.mediation.ads, com.google.firebase.app, com.google.external-dependency-manager, com.gameanalytics.sdk, io.appmetrica.analytics. |
| Còn sót Placeholder trong SayKitApp | SayKit | Chặn Release Build (Development Build chỉ cảnh báo) nếu các hằng số APP_KEY_ANDROID, APP_KEY_IOS, APP_NAME_IOS, APP_BUNDLE_IOS bị để trống hoặc bắt đầu bằng ký tự < (ví dụ: <APP_KEY_ANDROID>). APP_SECRET_* và PROMO_KEY_IOS không được kiểm tra. |
| Còn sót Placeholder trong CoreConfig | MGW | Chặn Release Build (Development Build chỉ cảnh báo) nếu: bật enableFirebase mà thiếu google-services.json (build Android) / GoogleService-Info.plist (build iOS); bật enableAds mà MAX SDK Key hoặc ad unit banner/interstitial/rewarded (Android hoặc iOS) còn REPLACE_ME hoặc trống; hoặc, khi module AppMetrica được biên dịch và enableAppMetrica bật, API key AppMetrica còn giữ chỗ. |
| Kiểm tra chung | Cả MGW & SayKit | Chặn Release Build (Development Build chỉ cảnh báo) nếu iosAppStoreId chưa điền (kiểm tra cho mọi nền tảng build) hoặc Bundle Identifier trong Player Settings vẫn bắt đầu bằng com.tapbrain.template; cũng báo Missing Resources/CoreConfig.asset nếu không tìm thấy CoreConfig. |
7. Bật tắt Module GameAnalytics & AppMetrica (Chỉ MGW)
Trên ngăn xếp MGW, bạn có thể tùy chọn tích hợp thêm GameAnalytics hoặc AppMetrica để theo dõi nâng cao:
- Cài đặt package vào
Packages/manifest.json(GameAnalytics qua OpenUPM:com.gameanalytics.sdk; AppMetrica qua Git URL:io.appmetrica.analytics). - Mở
TapBrain > SDK Settings..., tích chọn Use GameAnalytics hoặc Use AppMetrica, sau đó nhấn nút Apply (recompiles) để cập nhật define symbols trên Android, iOS và Standalone. Nếu package chưa cài, cửa sổ cảnh báo module sẽ không biên dịch. - Mở
CoreConfig.asset, đảm bảo các cờ thời gian chạyenableGameAnalyticshoặcenableAppMetricađược đặt thànhtrue(mặc định đã bật). ĐiềnappMetricaApiKeycho AppMetrica; khóa game/secret của GameAnalytics nằm trong cửa sổ GameAnalytics Settings, không nằm trongCoreConfig. - Bỏ chọn một module trong SDK Settings chỉ loại code của module khỏi biên dịch; package vẫn nằm trong manifest cho đến khi bạn gỡ thủ công.
- Khi chuyển sang Provider SayKit, hai module này bị tắt, loại khỏi biên dịch và các package của chúng được cất khỏi
manifest.jsoncùng stack MGW.
8. Cách viết Code Game tương thích an toàn đa Provider
Để đảm bảo dự án game của bạn có thể chuyển đổi qua lại giữa MGW và SayKit chỉ bằng một cú click chuột mà không phát sinh bất kỳ lỗi biên dịch nào, hãy tuân thủ triệt để các nguyên tắc sau:
Các nguyên tắc cốt lõi
- Tuyệt đối không gọi trực tiếp API của bên thứ ba: Không bao giờ gọi trực tiếp
MaxSdk.*,Firebase.*, haySayKit.*trong mã gameplay. Luôn gọi quaApp.Ads,App.Iap,App.Analytics,App.RemoteConfig,App.Review. - Bọc mã dịch vụ đặc thù MGW trong cờ điều kiện: Các dịch vụ chỉ có trên MGW (như
FirebaseService,RealtimeDbService,BIService,NotificationService, namespaceTapBrain.Sdk.Mgw) phải luôn được bọc trong khối#if !TB_SDK_SAYKIT, kể cả dòngusingcủa namespace đó. Khi chuyển sang SayKit, assembly MGW không còn được biên dịch nên mã không bọc sẽ lỗi biên dịch (không phải lỗi lúc chạy). - Dùng ServiceLocator.TryGet để truy xuất dịch vụ tùy chọn: Luôn kiểm tra xem dịch vụ có được đăng ký hay không trước khi sử dụng, và kiểm tra cờ sẵn sàng của dịch vụ (ví dụ
IsReady).
So sánh Code Đúng vs Code Sai
using UnityEngine;
// WRONG: references a third-party SDK directly
using Firebase.Database;
namespace MyGame
{
public class RewardManager : MonoBehaviour
{
public void ShowAd()
{
// WRONG: SayKit builds have no MaxSdk, so this fails to compile
// as soon as the project is switched to SayKit.
if (MaxSdk.IsRewardedAdReady("ad_unit_id"))
{
MaxSdk.ShowRewardedAd("ad_unit_id");
}
}
public void SaveToCloud(string data)
{
// WRONG: not guarded by TB_SDK_SAYKIT and bypasses the SDK services
var db = FirebaseDatabase.DefaultInstance;
db.RootReference.Child("users").SetRawJsonValueAsync(data);
}
}
}
using UnityEngine;
using TapBrain.Core;
using TapBrain.Core.Services;
#if !TB_SDK_SAYKIT
using TapBrain.Sdk.Mgw; // MGW-only namespace: the using must be guarded too
#endif
namespace MyGame
{
public class RewardManager : MonoBehaviour
{
public void ShowAd()
{
// RIGHT: go through the App.Ads facade.
// It routes to MAX or SayKit depending on the compiled provider.
if (App.Ads.IsRewardedReady)
{
App.Ads.ShowRewarded("double_reward", result =>
{
// Grant only on Rewarded; Skipped / NotReady / Failed give nothing.
if (result == RewardedResult.Rewarded) GiveReward();
});
}
}
public void SaveToCloud(string path, string json)
{
// RIGHT: MGW-only service, guarded by #if !TB_SDK_SAYKIT
// and resolved with ServiceLocator.TryGet.
#if !TB_SDK_SAYKIT
if (ServiceLocator.TryGet<RealtimeDbService>(out var db) && db.IsReady)
{
db.SetJson(path, json, success =>
{
Debug.Log($"[MyGame] Cloud sync: {success}");
});
}
#endif
}
private void GiveReward() { }
}
}
Nếu game của bạn dùng asmdef riêng, asmdef đó phải tham chiếu TapBrain.Core (và thêm TapBrain.Sdk.MGW nếu dùng dịch vụ MGW, kèm điều kiện !TB_SDK_SAYKIT). Mã nằm trong Assembly-CSharp (không có asmdef) tự thấy các assembly SDK được biên dịch.
9. Hành vi thời gian chạy & Log Console kiểm chứng
Khi chạy game trong Unity Editor, Console hiển thị luồng khởi tạo tương ứng với Provider đang kích hoạt. Các dòng [Tag] message mức Info (như [Boot] ... done in ...) chỉ in khi CoreConfig.verboseLog bật hoặc khi chạy development build (trong Editor luôn in); cảnh báo và lỗi luôn in. Số liệu thời gian và số sản phẩm dưới đây chỉ là minh họa.
A. Dòng log chuẩn khi dùng Provider MGW
Trường hợp đã có đầy đủ key và cấu hình Firebase (các thông điệp chưa có key được liệt kê ở Chương 11):
[IAP] No IReceiptValidator registered: receipts accepted without local validation (only if the game registered no validator)
[Boot] ATT done in 0.00s
[Firebase] Ready (Analytics + Crashlytics)
[Boot] Firebase done in 0.12s
[Boot] Auth done in 0.05s
[RemoteConfig] Fetched, activated=True
[Boot] RemoteConfig done in 0.08s
[RTDB] Ready at /my_game/debug
[Boot] RealtimeDb+BI done in 0.01s
[Ads] MAX initialized
[Boot] Ads done in 0.15s
[Boot] AppMetrica done in 0.01s (order of the two optional modules is not fixed)
[Boot] GameAnalytics done in 0.02s
[IAP] Ready, 3 products
[Boot] IAP done in 0.20s
[Boot] Notifications done in 0.01s
[Boot] Background done in 0.00s
[Boot] Completed, user=17300000001234 new=False session=12
B. Dòng log chuẩn khi dùng Provider SayKit
[IAP] No IReceiptValidator registered: receipts accepted without local validation (only if the game registered no validator)
[Boot] SayKit done in 0.18s
[RemoteConfig] SayKit game config ready (3 fields)
[Boot] RemoteConfig done in 0.02s
[IAP] Ready, 3 products
[Boot] IAP done in 0.22s
[Boot] Background done in 0.01s
[Boot] Completed, user=17300000001234 new=False session=12
Lưu ý thứ tự: các dòng do bước tự in (như [Ads] MAX initialized, [IAP] Ready) xuất hiện trước dòng [Boot] <Bước> done của chính bước đó.
- Trên SayKit, bước
ATThoàn toàn không xuất hiện vì SayKit tự quản lý quyền riêng tư. - Các bước
Firebase,Auth,RealtimeDb+BI,Ads,Notificationschỉ chạy trên MGW;GameAnalyticsvàAppMetricachỉ xuất hiện trên MGW khi module tương ứng được biên dịch. - Nếu chưa điền key trong Editor, SayKit sẽ cảnh báo:
[SayKit] App key is still a placeholder in SayKitApp.cs; SayKit will not serve real ads. Mọi bước đều có timeout: nếu SayKit không khởi tạo xong trongCoreConfig.sayKitInitTimeout(mặc định 10 giây), console in[Boot] SayKit timed out after 10s, continuingvà game vẫn tiếp tục khởi động.
10. Checklist kiểm tra khi chuyển đổi Provider
TapBrain > SDK > Switch To... hoặc cửa sổ SDK Settings..., không tự chỉnh sửa Scripting Defines trong Player Settings.Assets/SayKit/SayKitApp.cs và file tạm tại Assets/SayKitExtensions/SayKitApp.cs đã được xóa.Packages/manifest.json.TapBrain > Validate Core Config (chỉ chạy phần kiểm tra placeholder, không chạy các kiểm tra Provider/SayKit/package ở trên) và đảm bảo không còn key giữ chỗ REPLACE_ME hoặc, với SayKit, ký tự < trong SayKitApp.cs.