MaxAdsRouter là abstract class thay thế MaxAdsHybridController, với kiến trúc tách biệt:
Router: Đăng ký callbacks MAX SDK, quản lý click tracking, chọn controller phù hợp
Controller: Xử lý load/show/retry ads (1 hoặc nhiều AdUnitId)
Tự động chọn controller:
Có ≥ 2 AdUnitId (Reward hoặc Interstitial) → MultipleAdUnitIdMaxAdsController
Có 1 AdUnitId → OneAdUnitIdMaxAdsController
3.2 Kiến trúc
3.3 Flow khởi tạo
Sơ đồ (Mermaid):
Sơ đồ luồng: OneAdUnitIdMaxAdsController:
Sơ đồ luồng: MultipleAdUnitIdMaxAdsController:
3.4 Các method Abstract cần implement
3.5 Thiết lập AdUnitId trong GSMSettings
Trong GSMSettings Inspector, cấu hình AdUnitIds:
Nếu có nhiều AdUnitId (server sẽ trả về danh sách):
3.6 Cách sử dụng trong game
Kế thừa từ GSMAdManager để quản lý ads tập trung:
Sử dụng trong game:
Cấu trúc trong Scene:
Flow hoạt động:
3.7 MaxConfig – Cấu hình từ GSM
Cấu hình load/retry và từng AdUnit được đọc từ GSM config qua object maxConfig.
Cấu trúc MaxConfig:
Thuộc tính
Kiểu
Mặc định
Mô tả
adUnitConfig
Dictionary<string, MaxAdUnitConfig>
{}
Cấu hình theo từng AdUnitId
delayLoadAfterHiddenSeconds
int
1
Số giây chờ trước khi gọi load lại sau khi quảng cáo bị đóng
delayTimeoutSecond
float
5f
Thời gian chờ phản hồi tối đa của request load ads trước khi timeout
timeoutLoadRewardSeconds
int
0
Max thời gian load Video rào (Giây), = 0 là tắt
timeoutLoadInterSeconds
int
0
Max thời gian load Interstitial (Giây), = 0 là tắt
retryDelay
int
2
Cơ số retry: delay = retryDelay^attempt (giây), tối đa 6 lần → max 64s
retryMaxReward
int
10
Số lần retry load Reward Ads tối đa (OneAdUnit dùng global retry, MultipleAdUnit dùng loadFailCount trên mỗi AdUnitId).
retryMaxInter
int
10
Số lần retry load Interstitial Ads tối đa (OneAdUnit dùng global retry, MultipleAdUnit dùng loadFailCount trên mỗi AdUnitId).
isAutoLoadRewardVideo
bool?
null
Override auto load Reward (null = giữ giá trị Inspector)
isAutoLoadInterstitial
bool?
null
Override auto load Interstitial (null = giữ giá trị Inspector)
Lưu ý: MaxConfig (bao gồm delayTimeoutSecond sẽ được cache lại sử dụng PlayerPrefs qua khoá Prefs_Max_Config để tối ưu cho những lần mở máy không có mạng hoặc mạng chậm).
Cấu trúc MaxAdUnitConfig:
Thuộc tính
Kiểu
Mặc định
Mô tả
loadFailMax
int
2
Số lần fail tối đa trước khi loại AdUnit khỏi rotation (trừ default)
Ví dụ GSM Config (JSON):
3.8 Cơ chế Fail và Retry
3.9 So sánh với MaxAdsHybridController
Tiêu chí
MaxAdsHybridController (cũ)
MaxAdsRouter (mới)
Kiến trúc
Monolithic (~900 dòng)
Router + Controller tách biệt
Callback registration
Trong class
Router đăng ký, delegate cho controller
Controller selection
Không
Tự động: One vs Multiple
Show exception protection
❌ Không
✅ try/catch + resume app
Null check adUnitId
❌ Thiếu
✅ Toàn bộ callbacks
State inconsistency check
Có
✅ Có (Multiple controller)
retryDelay default
4 (lên tới 4096s!)
2 (max 64s)
GSMMaxTracking safety
❌ Không try/catch
✅ try/catch tất cả callbacks
Version
Plugin Version: 0.0.2.0
Thay đổi 0.0.2.0:
MultipleAdUnitIdMaxAdsController: Sửa lỗi conflict luồng tải quảng cáo ngầm khi đang hiển thị. Quảng cáo chỉ tiếp tục tải mới khi load fail hoặc sau khi user đóng lại.
MultipleAdUnitIdMaxAdsController: Thêm cơ chế rotation theo thứ tự list AdUnitId. Lần load đầu luôn dùng Default. Từ lần thứ 2, ưu tiên AdUnitId đầu tiên khả dụng (High → Medium → Default). Giữ ad unit hiện tại cho đến khi fail thì tự chuyển sang tiếp theo.
Thay đổi 0.0.1.9:
Loại bỏ hoàn toàn các Config Load Delay và định kỳ (rewardSecondDelayForLoad, interSecondDelayForLoad, periodicReloadRewardSeconds, periodicReloadInterSeconds).
Loại bỏ hoàn toàn luồng hoạt động PeriodicReload và tính năng Delay Load sau khi tải xong (MultipleAdUnitIdMaxAdsController, OneAdUnitIdMaxAdsController, MaxAdsRouter).
Loại bỏ các thiết lập Block Time (rewardShowBlockSeconds, rewardDisplayedBlockSeconds, vân vân) do nó không còn được sử dụng ở tiến trình đếm periodic.
MaxConfig: Thêm delayLoadAfterHiddenSeconds (mặc định 1s) xử lý độ trễ load sau khi đóng Ads.
MaxConfig: Thêm delayTimeoutSecond (mặc định 5s) và logic huỷ tiến trình nếu quảng cáo bị timeout.
MaxConfig: Thêm cấu hình số lần retry tối đa retryMaxReward và retryMaxInter (mặc định 10 lần) áp dụng trên cả 2 phiên bản controller. Lưu ý MultipleAdUnitController đánh giá số lần fail trên từng AdUnitId độc lập bằng loadFailCount.
MaxAdsRouter: Sử dụng chức năng lưu MaxConfig trực tiếp dưới cấu hình json vào PlayerPrefs để thiết lập ưu tiên nếu game mở offline không gọi dữ liệu từ server. Đọc chuỗi JSON qua Prefs_Max_Config.
Bổ sung tracking Load_Reward_Timeout và Load_Inter_Timeout khi quá hiệu số chờ config.
MaxAdsHybridController: Bỏ OnSdkInitializedEvent, thay bằng method OnMaxSdkInitializedEventSuccess.
MaxAdsHybridController: Chuyển WaitMaxSDKInitSuccess và StartLoadAds từ Awake() xuống Start().
Thay đổi 0.0.1.6:
MaxConfig: Cấu hình từ GSM gồm adUnitConfig, rewardSecondDelayForLoad, interSecondDelayForLoad, retryDelay, isAutoLoadRewardVideo, isAutoLoadInterstitial.
Retry delay dùng công thức retryDelay^attempt.
StartLoadAds: Chờ đủ ba điều kiện mới bắt đầu load ad.
├── Server trả về:
│ ├── Rewarded: ["HIGH_ECPM_REWARD", "MED_REWARD", "DEFAULT_REWARD"]
│ └── Interstitial: ["HIGH_ECPM_INTER", "MED_INTER", "DEFAULT_INTER"]
│
│ → MaxAdsRouter tự động chọn MultipleAdUnitIdMaxAdsController
│ → Lần load đầu: Default. Từ lần 2: ưu tiên theo thứ tự list (eCPM cao → thấp)
│ → Giữ ad unit hiện tại cho đến khi fail, rồi chuyển sang ad tiếp theo
using GSM.Ads;
using UnityEngine;
public class AdsController : GSMAdManager
{
public static AdsController Instance { get; private set; }
private void Awake()
{
Instance = this;
}
#region Rewarded Video
public bool IsRewardVideoAdsReady()
{
return base.IsRewardVideoAdsReady();
}
public void ShowVideoReward(System.Action onUserEarnedReward, System.Action onAdClosed)
{
if (IsRewardVideoAdsReady())
{
HideMREC();
base.ShowVideoAds(onUserEarnedReward, onAdClosed);
}
else
{
Debug.Log("Reward ad đang tải...");
onAdClosed?.Invoke();
}
}
#endregion
#region Interstitial
public bool IsInterstitialAdsReady()
{
return base.IsInterstitialReady();
}
public void ShowInterstitial(System.Action onSuccessShow, System.Action onClose, System.Action onFailShow = null)
{
if (IsInterstitialAdsReady())
{
base.ShowInterstitial(
onSuccess: () => { onSuccessShow?.Invoke(); },
onAdClosed: () => { onClose?.Invoke(); }
);
}
else
{
Debug.Log("Interstitial chưa sẵn sàng");
onFailShow?.Invoke();
}
}
#endregion
}
public class GameManager : MonoBehaviour
{
private AdsController _adsController;
private void Start()
{
_adsController = AdsController.Instance;
}
public void ShowRewardForHint()
{
_adsController.ShowVideoReward(
onUserEarnedReward: () =>
{
PlayerData.Instance.AddHint(1);
},
onAdClosed: () =>
{
Debug.Log("Reward ad đã đóng");
}
);
}
public void ShowInterstitialBetweenLevels()
{
_adsController.ShowInterstitial(
onSuccessShow: () => { Debug.Log("Bắt đầu hiển thị Interstitial"); },
onClose: () => { LoadNextLevel(); },
onFailShow: () => { LoadNextLevel(); }
);
}
}
Hierarchy:
└── AdsController (GameObject)
└── MaxAdsController (Component) ← Kéo vào Inspector (kế thừa MaxAdsRouter)
└── [Tự động tạo] OneAdUnitIdMaxAdsController hoặc MultipleAdUnitIdMaxAdsController
1. GameManager gọi ShowVideoReward() hoặc ShowInterstitial()
↓
2. AdsController kiểm tra Is...Ready()
↓
3. Gọi base.Show...() từ GSMAdManager
↓
4. GSMAdManager gọi Show...() trên MaxAdsController (kế thừa MaxAdsRouter)
↓
5. MaxAdsRouter delegate cho controller (One hoặc Multiple) → hiển thị ad