using System; using Guru; using UnityEngine; namespace Guru { public abstract class ADServiceBase : IADService where T : new() { // 单利定义 private static T _instance; public static T Instance { get { if (null == _instance) _instance = new T(); return _instance; } } protected static readonly string Tag = "[Ads]"; public bool IsInitialized => MaxSdk.IsInitialized() || _isServiceStarted; protected bool IsNetworkEnabled => Application.internetReachability != NetworkReachability.NotReachable; private bool _isServiceStarted; protected bool _isDebugMode; protected bool _isAutoLoadAds; private Action _onSdkInitReady; public static Action OnBannerLoaded; public static Action OnInterstitialLoaded; public static Action OnInterstitialFailed; public static Action OnRewardLoaded; public static Action OnRewardFailed; private AdsModel _model; /// /// 启动广告服务 /// /// 广告初始化回调 /// 自动启动广告加载 /// debug模式 public virtual void StartService(Action callback = null, bool autoLoadAds = true, bool isDebugMode = false) { if (IsInitialized) return; // 已经初始化后, 无需再次初始化 _isServiceStarted = true; _isAutoLoadAds = autoLoadAds; _isDebugMode = isDebugMode; _onSdkInitReady = callback; _model = AdsModel.Create(); this.Log("AD SDK Start Init"); //-------------- 初始化回调 ------------------ MaxSdkCallbacks.OnSdkInitializedEvent += OnMaxSdkInitializedCallBack; MaxSdkCallbacks.Interstitial.OnAdRevenuePaidEvent += OnAdRevenuePaidEvent; MaxSdkCallbacks.Rewarded.OnAdRevenuePaidEvent += OnAdRevenuePaidEvent; MaxSdkCallbacks.Banner.OnAdRevenuePaidEvent += OnAdRevenuePaidEvent; MaxSdkCallbacks.MRec.OnAdRevenuePaidEvent += OnAdRevenuePaidEvent; //--------------- Banner 回调 ----------------- MaxSdkCallbacks.Banner.OnAdLoadedEvent += OnBannerLoadedEvent; MaxSdkCallbacks.Banner.OnAdLoadFailedEvent += OnBannerFailedEvent; MaxSdkCallbacks.Banner.OnAdClickedEvent += OnBannerClickedEvent; //--------------- IV 回调 ----------------- MaxSdkCallbacks.Interstitial.OnAdLoadedEvent += OnInterstitialLoadedEvent; MaxSdkCallbacks.Interstitial.OnAdLoadFailedEvent += OnInterstitialFailedEvent; MaxSdkCallbacks.Interstitial.OnAdDisplayFailedEvent += InterstitialFailedToDisplayEvent; MaxSdkCallbacks.Interstitial.OnAdClickedEvent += OnInterstitialClickEvent; MaxSdkCallbacks.Interstitial.OnAdDisplayedEvent += OnInterstitialDisplayEvent; MaxSdkCallbacks.Interstitial.OnAdHiddenEvent += OnInterstitialDismissedEvent; //--------------- RV 回调 ----------------- MaxSdkCallbacks.Rewarded.OnAdLoadedEvent += OnRewardedAdLoadedEvent; MaxSdkCallbacks.Rewarded.OnAdLoadFailedEvent += OnRewardedAdFailedEvent; MaxSdkCallbacks.Rewarded.OnAdDisplayFailedEvent += OnRewardedAdFailedToDisplayEvent; MaxSdkCallbacks.Rewarded.OnAdDisplayedEvent += OnRewardedAdDisplayedEvent; MaxSdkCallbacks.Rewarded.OnAdClickedEvent += OnRewardedAdClickedEvent; MaxSdkCallbacks.Rewarded.OnAdHiddenEvent += OnRewardedAdDismissedEvent; MaxSdkCallbacks.Rewarded.OnAdReceivedRewardEvent += OnRewardedAdReceivedRewardEvent; //-------------- SDK 初始化 ------------------- MaxSdk.SetVerboseLogging(_isDebugMode); InitService(); } protected virtual void InitService() { } private void OnMaxSdkInitializedCallBack(MaxSdkBase.SdkConfiguration sdkConfiguration) { this.Log("AD SDK Init Success"); if (_isAutoLoadAds) OnMaxSdkReady(); _onSdkInitReady?.Invoke(); } protected virtual void OnMaxSdkReady() { //TODO:各个项目根据自己情况进行修改,模版默认做法是SDK初始化完成后就进行广告请求 RequestBannerAD(); RequestInterstitialAD(); RequestRewardedAD(); } /// /// 可加载广告 /// /// public virtual bool CanLoadAds() { return IsInitialized && IsNetworkEnabled; } #region ILRD private double TchAD001RevValue { get => _model.TchAD001RevValue; set => _model.TchAD001RevValue = value; } private double TchAD02RevValue { get => _model.TchAD02RevValue; set => _model.TchAD02RevValue = value; } public void OnAdRevenuePaidEvent(string adUnitId, MaxSdkBase.AdInfo adInfo) { if (adInfo == null) return; // #1. ad_impression OnAdImpression(adInfo); // #2. tch_ad_rev_roas calculation double revenue = adInfo.Revenue; CalcTaichi001Value(revenue); CalcTaichi02Value(revenue); // #3. Adjust ad_revenue AdjustService.TrackADRevenue(adInfo); } /// /// 广告ARO收益打点 /// /// private void OnAdImpression(MaxSdkBase.AdInfo adInfo) { Analytics.ADImpression(adInfo); } /// /// 计算太极001收益 /// /// private void CalcTaichi001Value(double revenue) { TchAD001RevValue += revenue; double revenueValue = TchAD001RevValue; Debug.Log($"[TaichConfig] get totally: {revenueValue}"); if (revenueValue >= Analytics.Tch001TargetValue) { Debug.Log($"[TaichConfig] call with value: {revenueValue}"); Analytics.Tch001ADRev(revenueValue); TchAD001RevValue = 0.0; } } /// /// 计算太极02收益 /// /// private void CalcTaichi02Value(double revenue) { if (!Analytics.EnableTch02Event) return; TchAD02RevValue += revenue; double revenueValue = TchAD02RevValue; Debug.Log($"[Ads] get totally: {revenueValue}"); if (revenueValue >= Analytics.Tch02TargetValue) { Debug.Log($"[Ads] call with value: {revenueValue}"); Analytics.Tch02ADRev(revenueValue); TchAD02RevValue = 0.0; } } #endregion #region Banner Ads private string _backColorStr = "#50A436"; private Color _backColor = new Color(0, 0, 0, 0); private string _badsCategory; protected float _badsloadStartTime = 0; private int GetAdsLoadDuration(ref float startTime) { int duration = (int)((Time.realtimeSinceStartup - startTime) * 1000); startTime = 0; return duration; } public virtual void RequestBannerAD() { LoadMaxBannerAd(); } /// /// Banner MAX 加载方式 /// protected void LoadMaxBannerAd() { OnLoadBads(); // Banners are automatically sized to 320x50 on phones and 728x90 on tablets // You may use the utility method `MaxSdkUtils.isTablet()` to help with view sizing adjustments var id = GetBannerID(); MaxSdk.CreateBanner(id, MaxSdkBase.BannerPosition.BottomCenter); MaxSdk.SetBannerExtraParameter(id, "adaptive_banner", "false"); // Set background or background color for banners to be fully functional MaxSdk.SetBannerBackgroundColor(id, _backColor); // Analytics.ADBadsLoad(GetBannerID()); Analytics.ADBadsLoad(AdParams.Build(id)); } public void OnLoadBads() { _badsloadStartTime = Time.realtimeSinceStartup; } public virtual void ShowBanner(string category = "") { _badsCategory = category; string adUnitId = GetBannerID(); MaxSdk.ShowBanner(adUnitId); MaxSdk.SetBannerBackgroundColor(adUnitId, _backColor); OnBannerImpEvent(adUnitId); } public virtual void HideBanner() { MaxSdk.HideBanner(GetBannerID()); } private void OnBannerLoadedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo) { // Analytics.ADBadsLoaded(adUnitId, GetAdsLoadDuration(ref _badsloadStartTime), _badsCategory); Analytics.ADBadsLoaded(AdParams.Build(adUnitId, adInfo, duration: GetAdsLoadDuration(ref _badsloadStartTime), category: _badsCategory)); OnBannerLoaded?.Invoke(); } private void OnBannerFailedEvent(string adUnitId, MaxSdkBase.ErrorInfo errorInfo) { // Analytics.ADBadsFailed(adUnitId, (int)errorInfo.Code, GetAdsLoadDuration(ref _badsloadStartTime), _badsCategory); Analytics.ADBadsFailed(AdParams.Build(adUnitId, duration: GetAdsLoadDuration(ref _badsloadStartTime), category: _badsCategory, errorCode: (int)errorInfo.Code, waterfallName: errorInfo?.WaterfallInfo?.Name ?? "")); } private void OnBannerClickedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo) { // Analytics.ADBadsClick(adUnitId, _badsCategory); Analytics.ADBadsClick(AdParams.Build(adUnitId, adInfo, _badsCategory)); } private void OnBannerImpEvent(string adUnitId) { // Analytics.ADBadsClick(adUnitId, _badsCategory); Analytics.ADBadsImp(AdParams.Build(adUnitId, category: _badsCategory)); } #endregion #region Interstitial Ads private string _iadsCategory = "main"; private int _interstitialRetryAttempt; protected float _iadsLoadStartTime; private Action _interstitialDismissAction; public virtual void RequestInterstitialAD() { if (!CanLoadAds()) return; LoadMaxInterstitial(); } protected void LoadMaxInterstitial() { OnLoadIads(); var id = GetInterstitialID(); Analytics.ADIadsLoad(AdParams.Build(id)); MaxSdk.LoadInterstitial(id); } public void OnLoadIads() { _iadsLoadStartTime = Time.realtimeSinceStartup; } public bool IsInterstitialADReady() { if (!IsInitialized) return false; return MaxSdk.IsInterstitialReady(GetInterstitialID()); } /// /// 显示插屏广告 /// /// 广告奖励回调 /// 广告失败回调 /// 广告界面关闭回调 public virtual void ShowInterstitialAD(string category, Action dismissAction = null) { if (!IsInitialized) { this.LogWarning("广告未初始化完成,无法显示插屏广告"); return; } if (!IsInterstitialADReady()) { this.LogWarning("插屏没有加载准备好,无法显示插屏广告"); return; } _iadsCategory = category; _interstitialDismissAction = dismissAction; MaxSdk.ShowInterstitial(GetInterstitialID()); RequestInterstitialAD(); // 直接加载下一个广告 } protected virtual void OnInterstitialLoadedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo) { // Interstitial ad is ready to be shown. MaxSdk.IsInterstitialReady(interstitialAdUnitId) will now return 'true' // Reset retry attempt // Analytics.ADIadsLoaded(adUnitId, GetAdsLoadDuration(ref _iadsLoadStartTime), _iadsCategory); Analytics.ADIadsLoaded(AdParams.Build(adUnitId, duration: GetAdsLoadDuration(ref _iadsLoadStartTime), category: _iadsCategory)); _interstitialRetryAttempt = 0; OnInterstitialLoaded?.Invoke(); } protected virtual void OnInterstitialFailedEvent(string adUnitId, MaxSdkBase.ErrorInfo errorInfo) { // Interstitial ad failed to load // We recommend retrying with exponentially higher delays up to a maximum delay (in this case 64 seconds) this.LogError( $"OnInterstitialFailedEvent AdLoadFailureInfo:{errorInfo.AdLoadFailureInfo}, Message: {errorInfo.Message}"); _interstitialRetryAttempt++; double retryDelay = Math.Pow(2, Math.Min(3, _interstitialRetryAttempt)); DelayCall((float)retryDelay, RequestInterstitialAD); // Analytics.ADIadsFailed(adUnitId, (int)errorInfo.Code, GetAdsLoadDuration(ref _iadsLoadStartTime), _iadsCategory); Analytics.ADIadsFailed(AdParams.Build(adUnitId, duration: GetAdsLoadDuration(ref _iadsLoadStartTime), category: _iadsCategory, errorCode: (int)errorInfo.Code, waterfallName: errorInfo?.WaterfallInfo?.Name ?? "")); OnInterstitialFailed?.Invoke(); } protected virtual void InterstitialFailedToDisplayEvent(string adUnitId, MaxSdkBase.ErrorInfo errorInfo, MaxSdkBase.AdInfo adInfo) { // Interstitial ad failed to display. We recommend loading the next ad this.LogError( $"InterstitialFailedToDisplayEvent AdLoadFailureInfo:{errorInfo.AdLoadFailureInfo}, Message: {errorInfo.Message}"); // Analytics.ADIadsFailed(adUnitId, (int)errorInfo.Code, GetAdsLoadDuration(ref _iadsLoadStartTime), _iadsCategory); Analytics.ADIadsFailed(AdParams.Build(adUnitId, duration: GetAdsLoadDuration(ref _iadsLoadStartTime), category: _iadsCategory, errorCode: (int)errorInfo.Code, waterfallName: errorInfo?.WaterfallInfo?.Name ?? "")); DelayCall(2.0f, RequestInterstitialAD); } protected virtual void OnInterstitialDisplayEvent(string adUnitId, MaxSdkBase.AdInfo adInfo) { // Analytics.ADIadsImp(adUnitId, _iadsCategory); Analytics.ADIadsImp(AdParams.Build(adUnitId, category: _iadsCategory)); } protected virtual void OnInterstitialClickEvent(string adUnitId, MaxSdkBase.AdInfo adInfo) { // Analytics.ADIadsClick(adUnitId, _iadsCategory); Analytics.ADIadsClick(AdParams.Build(adUnitId, category: _iadsCategory)); } protected virtual void OnInterstitialDismissedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo) { // Interstitial ad is hidden. Pre-load the next ad _interstitialDismissAction?.Invoke(); // Analytics.ADIadsClose(adUnitId, _iadsCategory); Analytics.ADIadsClose(AdParams.Build(adUnitId, category: _iadsCategory)); //延时加载下一个广告 DelayCall(2.0f, RequestInterstitialAD); } #endregion #region Rewarded Ads private string _rewardCategory = "main"; private int _rewardRetryAttempt; protected float _radsLoadStartTime; private Action _rewardAction; private Action _failAction; private Action _dismissAction; public virtual void RequestRewardedAD() { if (!IsInitialized) return; LoadMaxRewardAd(); } /// /// 默认加载 MAX 广告逻辑 /// protected void LoadMaxRewardAd(string unitId = "") { OnLoadRads(); var id = GetRewardedVideoID(); Analytics.ADRadsLoad(AdParams.Build(id)); // 上报打点 MaxSdk.LoadRewardedAd(id); } public void OnLoadRads() { _radsLoadStartTime = Time.realtimeSinceStartup; } public virtual bool IsRewardedADReady() { if (!IsInitialized) return false; return MaxSdk.IsRewardedAdReady(GetRewardedVideoID()); } /// /// 显示激励视频广告 /// /// 广告奖励回调 /// 广告失败回调 /// 广告界面关闭回调 public virtual void ShowRewardedAD(string category, Action rewardAction = null, Action failAction = null, Action dismissAction = null) { if (!IsInitialized) { this.LogWarning("广告未初始化完成,无法显示视频广告"); return; } if (!IsRewardedADReady()) { this.LogWarning("广告没有准备好,无法显示视频广告"); return; } _rewardCategory = category; _rewardAction = rewardAction; _failAction = failAction; _dismissAction = dismissAction; MaxSdk.ShowRewardedAd(GetRewardedVideoID()); RequestRewardedAD(); } protected virtual void OnRewardedAdLoadedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo) { // Rewarded ad is ready to be shown. MaxSdk.IsRewardedAdReady(rewardedAdUnitId) will now return 'true' // Reset retry attempt // this.Log("OnRewardedAdLoadedEvent"); // Analytics.ADRadsLoaded(adUnitId, GetAdsLoadDuration(ref _radsLoadStartTime), _rewardCategory); Analytics.ADRadsLoaded(AdParams.Build(adUnitId, duration: GetAdsLoadDuration(ref _iadsLoadStartTime), category: _iadsCategory)); _rewardRetryAttempt = 0; OnRewardLoaded?.Invoke(); } protected virtual void OnRewardedAdFailedEvent(string adUnitId, MaxSdkBase.ErrorInfo errorInfo) { // Rewarded ad failed to load // We recommend retrying with exponentially higher delays up to a maximum delay (in this case 64 seconds) this.LogError( $"OnRewardedAdFailedEvent AdLoadFailureInfo:{errorInfo.AdLoadFailureInfo}, Message: {errorInfo.Message}"); // Analytics.ADRadsFailed(adUnitId, (int)errorInfo.Code, GetAdsLoadDuration(ref _radsLoadStartTime), _rewardCategory); Analytics.ADRadsFailed(AdParams.Build(adUnitId, duration: GetAdsLoadDuration(ref _radsLoadStartTime), category: _rewardCategory, errorCode: (int)errorInfo.Code, waterfallName: errorInfo?.WaterfallInfo?.Name ?? "")); _rewardRetryAttempt++; double retryDelay = Math.Pow(2, Math.Min(3, _rewardRetryAttempt)); DelayCall((float)retryDelay, RequestRewardedAD); OnRewardFailed?.Invoke(); } protected virtual void OnRewardedAdFailedToDisplayEvent(string adUnitId, MaxSdkBase.ErrorInfo errorInfo, MaxSdkBase.AdInfo arg3) { // Rewarded ad failed to display. We recommend loading the next ad this.LogError( $"OnRewardedAdFailedToDisplayEvent AdLoadFailureInfo:{errorInfo.AdLoadFailureInfo}, Message: {errorInfo.Message}"); // Analytics.ADRadsFailed(adUnitId, (int)errorInfo.Code, GetAdsLoadDuration(ref _radsLoadStartTime), _rewardCategory); Analytics.ADRadsFailed(AdParams.Build(adUnitId, duration: GetAdsLoadDuration(ref _radsLoadStartTime), category: _rewardCategory, errorCode: (int)errorInfo.Code, waterfallName: errorInfo?.WaterfallInfo?.Name ?? "")); _failAction?.Invoke("OnRewardedAdFailedToDisplayEvent"); DelayCall(2.0f, RequestRewardedAD); OnRewardFailed?.Invoke(); } protected virtual void OnRewardedAdDisplayedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo) { this.Log("OnRewardedAdDisplayedEvent"); // Analytics.ADRadsImp(adUnitId, _rewardCategory); Analytics.ADRadsImp(AdParams.Build(adUnitId, category: _rewardCategory)); } protected virtual void OnRewardedAdClickedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo) { this.Log("OnRewardedAdClickedEvent"); // Analytics.ADRadsClick(adUnitId, _rewardCategory); Analytics.ADRadsClick(AdParams.Build(adUnitId, category: _rewardCategory)); } protected virtual void OnRewardedAdDismissedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo) { this.Log("OnRewardedAdDismissedEvent"); // Analytics.ADRadsClose(adUnitId, _rewardCategory); Analytics.ADRadsClose(AdParams.Build(adUnitId, category: _rewardCategory)); _dismissAction?.Invoke(); //延时加载下一个广告 DelayCall(2.0f, RequestRewardedAD); } protected virtual void OnRewardedAdReceivedRewardEvent(string adUnitId, MaxSdk.Reward reward, MaxSdkBase.AdInfo arg3) { this.Log("OnRewardedAdReceivedRewardEvent"); // Analytics.ADRadsRewarded(adUnitId, _rewardCategory); Analytics.ADRadsRewarded(AdParams.Build(adUnitId, category: _rewardCategory)); // Rewarded ad was displayed and user should receive the reward _rewardAction?.Invoke(); } #endregion #region Ad Settings protected virtual string GetRewardedVideoID() { return GuruSettings.Instance.ADSetting.GetRewardedVideoID(); } protected virtual string GetInterstitialID() { return GuruSettings.Instance.ADSetting.GetInterstitialID(); } protected virtual string GetBannerID() { return GuruSettings.Instance.ADSetting.GetBannerID(); } #endregion #region MaxDebugView public void ShowMaxDebugPanel() { if (!IsInitialized) return; #if !UNITY_EDITOR MaxSdk.ShowMediationDebugger(); #endif } #endregion #region DelayCall private void DelayCall(float time, Action callback) { CoroutineHelper.Instance.StartDelayed(time, callback); } #endregion } }