import { _decorator, Component } from 'cc'; import { AnalyticsManager } from '../Utils/AnalyticsManager'; const { ccclass } = _decorator; interface RewardedVideoAd { show(): Promise; load(): Promise; onLoad(callback: (res?: any) => void): void; onError(callback: (err: any) => void): void; onClose(callback: (res: { isEnded: boolean }) => void): void; offLoad(callback?: (res?: any) => void): void; offError(callback?: (err: any) => void): void; offClose(callback?: (res: { isEnded: boolean }) => void): void; } declare const wx: { createRewardedVideoAd(options: { adUnitId: string; multiton?: boolean }): RewardedVideoAd; }; @ccclass('AdManager') export class AdManager { private static instance: AdManager = null; private videoAd: RewardedVideoAd = null; private readonly AD_UNIT_ID = 'adunit-ab2f3b2d24b4f214'; // 替换为你的广告位ID private constructor() { // 延迟初始化广告,避免在微信环境未完全准备好时初始化 this.delayedInitialize(); } public static getInstance(): AdManager { if (!AdManager.instance) { AdManager.instance = new AdManager(); } return AdManager.instance; } /** * 静态方法:初始化广告管理器 * 建议在游戏启动或场景初始化时调用 */ public static initialize(): void { AdManager.getInstance(); console.log('[AdManager] 广告管理器已初始化'); } /** * 延迟初始化广告 * 解决微信小游戏环境下广告组件初始化时机问题 */ private delayedInitialize() { // 延迟500ms初始化,确保微信环境完全准备好 setTimeout(() => { this.initializeAd(); }, 500); } private initializeAd() { // 检查是否在微信小游戏环境 if (typeof wx !== 'undefined' && wx.createRewardedVideoAd) { try { // 创建激励视频广告实例,提前初始化 this.videoAd = wx.createRewardedVideoAd({ adUnitId: this.AD_UNIT_ID }); // 设置全局错误处理 this.setupGlobalErrorHandling(); // 预加载广告 this.preloadAd(); console.log('[AdManager] 激励视频广告初始化成功'); } catch (error) { console.error('[AdManager] 激励视频广告初始化失败:', error); // 初始化失败时,延迟重试 this.retryInitialize(); } } else { console.log('[AdManager] 非微信小游戏环境,跳过广告初始化'); } } /** * 重试初始化广告 * 当首次初始化失败时进行重试 */ private retryInitialize() { setTimeout(() => { console.log('[AdManager] 重试初始化广告'); this.initializeAd(); }, 2000); } /** * 设置全局广告错误处理 * 处理广告拉取异常情况 */ private setupGlobalErrorHandling() { if (!this.videoAd) return; // 设置全局错误监听 this.videoAd.onError((err: any) => { console.error('[AdManager] 广告全局错误:', err); // 根据错误码处理不同情况 if (err.errCode) { switch (err.errCode) { case 1000: console.log('[AdManager] 后端接口调用失败'); break; case 1001: console.log('[AdManager] 参数错误'); break; case 1002: console.log('[AdManager] 广告单元无效'); break; case 1003: console.log('[AdManager] 内部错误'); break; case 1004: console.log('[AdManager] 无合适的广告'); // 无广告返回时,建议隐藏激励视频入口 this.handleNoAdAvailable(); break; case 1005: console.log('[AdManager] 广告组件审核中'); break; case 1006: console.log('[AdManager] 广告组件被驳回'); break; case 1007: console.log('[AdManager] 广告组件被封禁'); break; case 1008: console.log('[AdManager] 广告单元已关闭'); break; case 1009: console.log('[AdManager] 广告单元为适配审核中,请勿调用show()'); break; default: console.log('[AdManager] 未知错误码:', err.errCode); break; } } }); } /** * 处理无广告可用的情况 * 建议隐藏激励视频入口 */ private handleNoAdAvailable() { console.log('[AdManager] 无合适广告,建议隐藏激励视频入口'); // 这里可以发送事件通知UI隐藏广告入口 // 或者设置一个标志位供外部查询 } /** * 检查是否有广告可用 * @returns 是否有广告可用 */ public isAdAvailable(): boolean { return this.videoAd !== null; } /** * 检查广告是否已经初始化完成 * @returns 广告是否已初始化 */ public isAdInitialized(): boolean { return this.videoAd !== null; } /** * 获取广告初始化状态 * @returns 广告状态信息 */ public getAdStatus(): { initialized: boolean; available: boolean } { const initialized = this.videoAd !== null; return { initialized, available: initialized }; } /** * 显示激励视频广告 * @param onReward 广告观看完成后的回调 * @param onError 广告显示失败的回调 */ public showRewardedVideoAd( onReward: () => void, onError?: (error: any) => void ): void { if (!this.videoAd) { console.log('[AdManager] 广告未初始化,等待初始化完成...'); // 等待广告初始化完成后再显示 this.waitForAdInitialization(() => { this.showRewardedVideoAd(onReward, onError); }, onReward); return; } // 设置广告事件监听 const onLoadHandler = (res?: any) => { console.log('[AdManager] 广告加载成功', res); if (res && res.useFallbackSharePage) { console.log('[AdManager] 使用兜底分享页'); } }; const onErrorHandler = (err: any) => { console.error('[AdManager] 广告加载失败:', err); // 根据错误码决定处理方式 let shouldGiveReward = false; if (err.errCode) { switch (err.errCode) { case 1004: // 无合适的广告 console.log('[AdManager] 无合适广告,不给予奖励'); shouldGiveReward = false; break; case 1000: // 后端接口调用失败 case 1003: // 内部错误 console.log('[AdManager] 系统错误,给予奖励'); shouldGiveReward = true; break; default: console.log('[AdManager] 其他错误,给予奖励'); shouldGiveReward = true; break; } } else { // 没有错误码的情况,默认给予奖励 shouldGiveReward = true; } if (onError) { onError(err); } else if (shouldGiveReward) { console.log('[AdManager] 广告失败但给予奖励'); onReward(); } else { console.log('[AdManager] 广告失败且不给予奖励'); } }; const onCloseHandler = (res: { isEnded: boolean }) => { console.log('[AdManager] 广告关闭:', res); // 清理事件监听 this.videoAd.offLoad(onLoadHandler); this.videoAd.offError(onErrorHandler); this.videoAd.offClose(onCloseHandler); // 根据广告是否完整观看来决定是否给予奖励 if (res.isEnded) { console.log('[AdManager] 用户完整观看广告,给予奖励'); onReward(); } else { console.log('[AdManager] 用户未完整观看广告,视为取消,不给予奖励'); // 用户主动关闭或未看完,通知上层进行状态重置 if (onError) { onError({ errCode: 'USER_CANCELLED', message: '用户取消观看激励视频广告', isEnded: false }); } } }; // 绑定事件监听 this.videoAd.onLoad(onLoadHandler); this.videoAd.onError(onErrorHandler); this.videoAd.onClose(onCloseHandler); // 显示广告 this.videoAd.show().then(() => { console.log('[AdManager] 广告显示成功'); // 追踪广告展示事件 AnalyticsManager.getInstance().trackAdShow({ ad_type: 'rewarded_video', ad_unit_id: this.AD_UNIT_ID, show_time: new Date().toISOString() }); }).catch((showError) => { console.log('[AdManager] 广告显示失败,尝试重新加载'); // 失败重试,增加延迟确保广告完全加载 this.videoAd.load() .then(() => { // 加载成功后稍等片刻再显示,避免渲染问题 setTimeout(() => { this.videoAd.show().catch(err => { console.error('[AdManager] 激励视频广告二次显示失败', err); onErrorHandler(err); }); }, 300); }) .catch(err => { console.error('[AdManager] 激励视频广告加载失败', err); onErrorHandler(err); }); }); } /** * 等待广告初始化完成 * @param callback 初始化完成后的回调 * @param fallback 超时后的回退处理 */ private waitForAdInitialization(callback: () => void, fallback: () => void): void { let attempts = 0; const maxAttempts = 10; // 最多等待5秒 const checkInitialization = () => { if (this.videoAd) { callback(); return; } attempts++; if (attempts >= maxAttempts) { console.log('[AdManager] 广告初始化超时,直接给予奖励'); fallback(); return; } setTimeout(checkInitialization, 500); }; checkInitialization(); } /** * 预加载广告 * 可以在游戏启动时调用,提前加载广告 */ public preloadAd(): void { if (this.videoAd) { console.log('[AdManager] 开始预加载广告'); this.videoAd.load().then(() => { console.log('[AdManager] 广告预加载成功'); }).catch(err => { console.error('[AdManager] 预加载广告失败:', err); // 预加载失败时,延迟重试 setTimeout(() => { console.log('[AdManager] 重试预加载广告'); this.preloadAd(); }, 3000); }); } } /** * 销毁广告管理器 */ public destroy(): void { if (this.videoAd) { // 清理所有事件监听,包括全局错误监听 this.videoAd.offLoad(); this.videoAd.offError(); this.videoAd.offClose(); this.videoAd = null; } AdManager.instance = null; } }