
1. 项目概述如果你正在用Unity开发手游并且正在为如何快速、稳定地实现广告变现而头疼那么今天这个“快速接入Google AdMob广告SDK示例工程”的分享就是为你准备的。我见过太多独立开发者或者小团队在项目临近上线时才手忙脚乱地开始集成广告结果被各种SDK版本冲突、平台配置、代码逻辑折腾得焦头烂额白白浪费了宝贵的上线窗口期和初期流量。这个示例工程的核心目标就是帮你把Google AdMob这套全球最主流的移动广告变现方案以最清晰、最直接、最可复现的方式集成到你的Unity项目中。它不仅仅是一堆代码的堆砌而是我结合了多个项目实战经验提炼出的一套从零到一、避坑指南式的完整工作流。无论你是刚接触Unity广告的新手还是想优化现有集成流程的老手这个示例都能让你在半小时内看到一个可运行的、包含横幅、插页和激励视频广告的完整Demo并理解其背后的每一个关键配置和代码逻辑为你的产品商业化打下坚实基础。2. 核心思路与工程结构设计2.1 为什么选择官方Unity插件而非手动集成在动手之前我们必须先解决一个根本问题用什么方式把AdMob SDK引入Unity项目市面上常见的有三种1. 手动下载Android/iOS原生SDK然后通过Plugins目录引入2. 使用第三方Asset Store资源3. 使用Google官方提供的Unity插件包即com.google.ads.mobile。这个示例工程坚定地选择了第三种方案。理由很直接维护性、兼容性和开发效率。手动集成意味着你需要分别处理Android的.aar/.jar文件和iOS的.framework每当SDK更新你都要重复下载、替换、处理可能出现的依赖冲突过程繁琐且易错。第三方资源包虽然可能提供一些额外的UI工具但其更新往往滞后于官方在遇到SDK重大变更时可能无法及时适配存在潜在风险。而官方Unity插件通过Unity Package Manager (UPM) 进行管理本质上是一个封装好的、针对Unity环境优化过的SDK包。它内部已经妥善处理了Android Gradle依赖和iOS CocoaPods的集成你只需要在Unity中一键导入或通过清单文件声明剩下的平台特异性工作都由插件自动完成。这极大地降低了集成门槛和后续的维护成本。注意从AdMob SDK v8.0.0开始Google强烈推荐并主要维护这种UPM方式的集成。旧版的.unitypackage方式已逐渐不再提供因此顺应官方技术路线是明智的选择。2.2 示例工程的模块化设计思路一个健壮的示例工程不应该只是把所有代码扔在一个场景里。为了清晰展示不同广告类型的独立性与可复用性我将工程结构设计为以下几个核心模块AdMob管理器 (AdMobManager)这是一个单例类负责AdMob SDK的全局初始化、应用ID的配置以及提供获取各广告单元实例的接口。它是整个广告系统的中枢。广告单元基类 (AdUnitBase)抽象基类定义了所有广告类型的通用接口如LoadAd(),ShowAd(),IsLoaded()等并封装了广告事件加载成功、失败、展示、点击等的回调处理。这有利于代码复用和统一管理。具体广告单元类BannerAdUnit负责横幅广告的创建、定位如顶部、底部和样式设置。InterstitialAdUnit负责插页广告的加载和展示时机控制。RewardedAdUnit负责激励视频广告的加载、展示以及奖励发放的逻辑回调。UI演示场景 (DemoScene)一个简单的Unity场景包含几个UI按钮如“显示横幅”、“播放激励视频”用于触发对应广告的加载和展示直观验证集成效果。平台配置文件夹包含针对Android (AndroidManifest配置) 和 iOS (Info.plist配置) 的必要设置文件这些配置通常由插件自动处理或提供模板但我们需要理解其作用。这种模块化设计使得广告逻辑与游戏业务逻辑解耦。在你的实际项目中你可以直接复制AdMobManager和具体的AdUnit类然后在需要的地方调用即可UI部分则完全替换为你自己的游戏界面。3. 环境准备与SDK集成实操3.1 创建Unity项目与基础设置首先你需要一个Unity项目。建议使用Unity 2021 LTS或2022 LTS版本这些长期支持版本在稳定性和第三方插件兼容性上表现最好。创建项目时选择3D (URP)或2D模板均可这不会影响广告功能的集成。项目创建后有两项关键设置需要检查目标平台在File - Build Settings中确保已切换到你想要构建的平台比如Android或iOS。对于Android需要安装对应的SDK、NDK、JDK。Unity Hub通常可以帮你一键安装这些组件。Player SettingsAndroidOther Settings下的Minimum API Level建议设置为API Level 22 (Android 5.1)或更高以覆盖绝大多数设备。Target API Level设置为你测试设备所支持的较新版本如33。iOS需要拥有有效的Apple开发者账号并在Player Settings - Other Settings中配置正确的Bundle Identifier和Signing Team ID。3.2 通过Unity Package Manager集成AdMob插件这是最核心的一步。我们将不使用下载.unitypackage的方式而是采用更现代的UPM方式。打开Unity编辑器点击顶部菜单Window - Package Manager。在Package Manager窗口左上角点击“”按钮选择Add package from git URL...。在弹出的输入框中粘贴Google官方插件的Git仓库地址。注意这里不能直接使用GitHub的网页地址。根据官方文档正确的地址格式是https://github.com/googleads/googleads-mobile-unity.git#v11.2.0。其中#v11.2.0指定了版本号你可以将其替换为 GitHub Releases页面 上最新的稳定版本例如#v11.3.0。使用特定版本号可以避免因自动更新到可能不兼容的新版而带来的意外问题。点击“Add”。Unity会开始从Git仓库下载并解析该插件包。这个过程可能会花费几分钟取决于你的网络状况。集成成功后你可以在Package Manager的列表里看到Google Mobile Ads这个包。这意味着SDK的核心库已经添加到你的项目依赖中。3.3 配置AdMob应用与广告单元IDSDK集成好了但广告从哪里来你需要去Google AdMob后台创建你的“资产”。访问AdMob网站使用你的Google账号登录 AdMob官网 。创建应用在后台点击“应用”-“添加应用”。如果你已上架应用选择“是”并关联Google Play或App Store的现有应用如果还在测试选择“否”然后输入你的应用名称和平台如Android。获取应用ID应用创建后AdMob会为你分配一个唯一的应用ID格式类似ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy。这个ID是SDK初始化的必备参数用于标识你的应用。创建广告单元在应用详情页点击“创建广告单元”。你需要为每种广告类型分别创建横幅广告选择“横幅”设置名称如“MainMenu_Banner”保存后会得到一个广告单元ID格式如ca-app-pub-xxxxxxxxxxxxxxxx/zzzzzzzzzz。插页广告选择“插页式广告”。激励广告选择“激励广告”。记录ID将应用ID和各个广告单元ID妥善保存例如写在一个ScriptableObject配置资产里或者直接硬编码在初始化脚本中用于测试。切记在开发测试阶段务必使用AdMob提供的测试广告单元ID以避免无效流量导致账号被封禁。测试ID通常是固定的如Android横幅测试ID为ca-app-pub-3940256099942544/6300978111。4. 核心代码实现与分步解析4.1 AdMob管理器的实现与初始化创建一个名为AdMobManager.cs的C#脚本。这个类将采用单例模式确保全局只有一个实例来管理广告。using UnityEngine; using GoogleMobileAds.Api; public class AdMobManager : MonoBehaviour { public static AdMobManager Instance { get; private set; } // 在Inspector中配置你的AdMob应用ID [SerializeField] private string _androidAppId YOUR_ANDROID_APP_ID; [SerializeField] private string _iosAppId YOUR_IOS_APP_ID; // 对外暴露的广告单元实例 public BannerAdUnit BannerAd { get; private set; } public InterstitialAdUnit InterstitialAd { get; private set; } public RewardedAdUnit RewardedAd { get; private set; } private void Awake() { // 单例模式实现 if (Instance ! null Instance ! this) { Destroy(gameObject); return; } Instance this; DontDestroyOnLoad(gameObject); // 跨场景不销毁 InitializeAdMob(); } private void InitializeAdMob() { // 1. 配置请求用于儿童定向的功能COPPA合规性。如果你的应用面向儿童必须设置。 // RequestConfiguration requestConfiguration new RequestConfiguration.Builder() // .SetTagForChildDirectedTreatment(TagForChildDirectedTreatment.True) // .build(); // MobileAds.SetRequestConfiguration(requestConfiguration); // 2. 根据平台选择应用ID string appId; #if UNITY_ANDROID appId _androidAppId; #elif UNITY_IOS appId _iosAppId; #else appId unexpected_platform; #endif // 3. 初始化Google Mobile Ads SDK MobileAds.Initialize(initStatus { Debug.Log($AdMob SDK 初始化完成。初始化状态: {initStatus}); // 初始化完成后再创建具体的广告单元实例 CreateAdUnits(); }); } private void CreateAdUnits() { // 创建各广告单元实例并传入对应的广告单元ID BannerAd new BannerAdUnit(YOUR_BANNER_AD_UNIT_ID); InterstitialAd new InterstitialAdUnit(YOUR_INTERSTITIAL_AD_UNIT_ID); RewardedAd new RewardedAdUnit(YOUR_REWARDED_AD_UNIT_ID); // 可以在这里预加载一些广告如插页广告 InterstitialAd.LoadAd(); } }关键点解析MobileAds.Initialize是异步的。传入的回调函数会在SDK初始化完成后被调用这是安全的创建广告单元的时机。应用ID的配置使用了预处理指令 (#if UNITY_ANDROID)确保为不同平台传递正确的ID。将广告单元实例化为属性方便其他脚本通过AdMobManager.Instance.BannerAd等方式访问。4.2 广告单元基类的抽象与事件封装为了统一处理我们先创建一个抽象基类AdUnitBase.cs。它处理所有广告类型共有的逻辑加载、展示、事件监听。using System; using UnityEngine; using GoogleMobileAds.Api; public abstract class AdUnitBase { protected string _adUnitId; protected AdRequest _adRequest; protected bool _isLoaded false; // 定义一些通用的事件方便外部订阅 public event Action OnAdLoaded; public event ActionLoadAdError OnAdFailedToLoad; public event Action OnAdShowed; public event ActionAdError OnAdFailedToShow; public event Action OnAdClicked; public event Action OnAdClosed; protected AdUnitBase(string adUnitId) { _adUnitId adUnitId; // 创建一个通用的广告请求可以在这里添加测试设备ID、关键词等 _adRequest new AdRequest.Builder() .AddKeyword(unity) // 示例关键词 .Build(); } // 抽象方法由子类实现具体的加载逻辑 public abstract void LoadAd(); // 抽象方法由子类实现具体的展示逻辑 public abstract void ShowAd(); public bool IsLoaded() _isLoaded; // 通用的日志和事件触发方法 protected void LogAndInvokeLoaded() { _isLoaded true; Debug.Log(${GetType().Name} 加载成功。); OnAdLoaded?.Invoke(); } protected void LogAndInvokeLoadFailed(LoadAdError error) { _isLoaded false; Debug.LogError(${GetType().Name} 加载失败: {error.GetMessage()}); OnAdFailedToLoad?.Invoke(error); } }4.3 横幅广告的实现与布局控制横幅广告是最简单的广告形式。创建BannerAdUnit.cs。using UnityEngine; using GoogleMobileAds.Api; public class BannerAdUnit : AdUnitBase { private BannerView _bannerView; private AdSize _adSize AdSize.Banner; // 默认标准横幅 private AdPosition _adPosition AdPosition.Bottom; // 默认底部 public BannerAdUnit(string adUnitId, AdSize adSize null, AdPosition position AdPosition.Bottom) : base(adUnitId) { if (adSize ! null) _adSize adSize; _adPosition position; } public override void LoadAd() { // 如果已经存在BannerView实例先销毁它 if (_bannerView ! null) { _bannerView.Destroy(); } // 创建BannerView实例 _bannerView new BannerView(_adUnitId, _adSize, _adPosition); // 订阅广告事件 _bannerView.OnBannerAdLoaded () { LogAndInvokeLoaded(); }; _bannerView.OnBannerAdLoadFailed (LoadAdError error) { LogAndInvokeLoadFailed(error); }; _bannerView.OnAdImpressionRecorded () { OnAdShowed?.Invoke(); }; _bannerView.OnAdClicked () { OnAdClicked?.Invoke(); }; // 发起广告请求 _bannerView.LoadAd(_adRequest); } public override void ShowAd() { // 横幅广告加载后自动展示此方法可用于在隐藏后重新显示 if (_bannerView ! null _isLoaded) { _bannerView.Show(); } else { Debug.LogWarning(横幅广告未加载无法展示。); } } public void HideAd() { if (_bannerView ! null) { _bannerView.Hide(); } } public void DestroyAd() { if (_bannerView ! null) { _bannerView.Destroy(); _bannerView null; _isLoaded false; } } // 提供方法在运行时改变广告位置例如切换全屏游戏时隐藏横幅 public void ChangePosition(AdPosition newPosition) { if (_bannerView ! null) { _bannerView.SetPosition(newPosition); _adPosition newPosition; } } }实操心得AdSize不仅可以是AdSize.Banner(320x50)还可以是AdSize.MediumRectangle(300x250)、AdSize.IABBanner(468x60) 或AdSize.Leaderboard(728x90)。甚至可以使用AdSize.GetCurrentOrientationAnchoredAdaptiveBannerAdSizeWithWidth(width)来获取自适应横幅尺寸这在应对不同屏幕尺寸时非常有用。横幅广告一旦加载成功就会自动显示。ShowAd()方法更多用于在你调用HideAd()隐藏后再次将其显示出来。记得在游戏场景切换或不需要广告时如进入付费去广告模式调用DestroyAd()来释放资源。4.4 插页广告的实现与展示时机插页广告是全屏广告通常在游戏关卡之间、菜单切换时展示。创建InterstitialAdUnit.cs。using System; using UnityEngine; using GoogleMobileAds.Api; public class InterstitialAdUnit : AdUnitBase { private InterstitialAd _interstitialAd; public InterstitialAdUnit(string adUnitId) : base(adUnitId) { } public override void LoadAd() { // 如果已有广告实例先清理 if (_interstitialAd ! null) { _interstitialAd.Destroy(); _interstitialAd null; } // 创建插页广告实例 InterstitialAd.Load(_adUnitId, _adRequest, (InterstitialAd ad, LoadAdError error) { if (error ! null || ad null) { LogAndInvokeLoadFailed(error); return; } _interstitialAd ad; _isLoaded true; Debug.Log(${GetType().Name} 加载成功。); // 订阅广告事件 _interstitialAd.OnAdPaid (AdValue adValue) { /* 处理广告收益事件 */ }; _interstitialAd.OnAdImpressionRecorded () { OnAdShowed?.Invoke(); }; _interstitialAd.OnAdClicked () { OnAdClicked?.Invoke(); }; _interstitialAd.OnAdFullScreenContentClosed () { OnAdClosed?.Invoke(); // 广告关闭后立即重新加载下一个以备下次使用 LoadAd(); }; _interstitialAd.OnAdFullScreenContentFailed (AdError error) { Debug.LogError(${GetType().Name} 展示失败: {error.GetMessage()}); OnAdFailedToShow?.Invoke(error); // 展示失败也重新加载 LoadAd(); }; OnAdLoaded?.Invoke(); }); } public override void ShowAd() { if (_interstitialAd ! null _isLoaded) { _interstitialAd.Show(); _isLoaded false; // 展示后立即标记为未加载因为当前实例即将被处理 } else { Debug.LogWarning(插页广告未就绪无法展示。); // 可以在这里选择直接跳过广告或者尝试重新加载 LoadAd(); } } }注意事项插页广告是“一次性”的。每次展示后无论成功关闭还是展示失败当前的InterstitialAd对象就失效了。因此在OnAdFullScreenContentClosed和OnAdFullScreenContentFailed事件中我们都需要立即调用LoadAd()重新加载下一个广告。这是保证广告能连续展示的关键否则用户下次触发广告时可能看到“广告未就绪”的提示。展示时机非常重要。不要在用户进行关键操作如点击跳跃按钮时突然弹出插页广告这会导致极差的用户体验。合理的时机包括游戏角色死亡后、通关一个关卡后、从主菜单进入游戏时等自然停顿点。4.5 激励视频广告的实现与奖励回调激励视频广告需要用户选择观看以获取奖励如游戏货币、复活机会。创建RewardedAdUnit.cs。using System; using UnityEngine; using GoogleMobileAds.Api; public class RewardedAdUnit : AdUnitBase { private RewardedAd _rewardedAd; // 奖励回调参数可以是奖励类型和数量 public event Actionstring, int OnUserEarnedReward; public RewardedAdUnit(string adUnitId) : base(adUnitId) { } public override void LoadAd() { if (_rewardedAd ! null) { _rewardedAd.Destroy(); _rewardedAd null; } RewardedAd.Load(_adUnitId, _adRequest, (RewardedAd ad, LoadAdError error) { if (error ! null || ad null) { LogAndInvokeLoadFailed(error); return; } _rewardedAd ad; _isLoaded true; Debug.Log(${GetType().Name} 加载成功。); // 订阅事件 _rewardedAd.OnAdPaid (AdValue adValue) { /* 处理收益 */ }; _rewardedAd.OnAdImpressionRecorded () { OnAdShowed?.Invoke(); }; _rewardedAd.OnAdClicked () { OnAdClicked?.Invoke(); }; _rewardedAd.OnAdFullScreenContentClosed () { OnAdClosed?.Invoke(); LoadAd(); // 关闭后重新加载 }; _rewardedAd.OnAdFullScreenContentFailed (AdError error) { Debug.LogError(${GetType().Name} 展示失败: {error.GetMessage()}); OnAdFailedToShow?.Invoke(error); LoadAd(); // 失败后重新加载 }; // **核心用户获得奖励的事件** _rewardedAd.OnUserEarnedReward (Reward reward) { string type reward.Type; int amount (int)reward.Amount; Debug.Log($用户获得奖励: {type} x {amount}); OnUserEarnedReward?.Invoke(type, amount); }; OnAdLoaded?.Invoke(); }); } public override void ShowAd() { if (_rewardedAd ! null _isLoaded) { _rewardedAd.Show((Reward reward) { // 这个回调在广告展示后立即触发但奖励是否发放取决于用户是否看完广告 // 实际的奖励发放逻辑应在 OnUserEarnedReward 中处理 }); _isLoaded false; } else { Debug.LogWarning(激励视频广告未就绪无法展示。); // 可以给用户一个提示如“广告正在加载请稍后重试” } } }关键逻辑与避坑指南奖励发放的时机OnUserEarnedReward事件是唯一应该发放游戏内奖励的时机。绝对不要在Show方法的回调里发放奖励因为那个回调只表示广告开始播放了不代表用户看完了广告并应得奖励。只有OnUserEarnedReward被触发才意味着用户完成了观看条件通常是看完整个视频或达到最小观看时长。网络与加载状态激励视频文件较大加载成功率受网络影响明显。务必在UI按钮上根据IsLoaded()状态来改变交互如加载中显示转圈加载成功按钮亮起加载失败显示重试。在OnAdFailedToLoad事件中可以实现自动重试逻辑但需设置最大重试次数和延迟避免频繁请求。防止重复奖励一个常见的漏洞是用户快速连续点击“观看广告”按钮可能触发多次奖励。解决方案是在奖励发放后立即禁用相关按钮并设置一个冷却时间例如2秒或者使用一个全局的“奖励发放中”标志位确保同一时间只处理一次奖励逻辑。5. 平台特定配置与构建部署5.1 Android平台配置详解Unity AdMob插件大部分Android配置已自动化但仍有几个关键点需要手动确认或处理Gradle设置在Player Settings - Publishing Settings下确保Build System为Gradle。勾选Custom Base Gradle Template和Custom Main Gradle Template。这会在你的项目Assets/Plugins/Android目录下生成baseProjectTemplate.gradle和mainTemplate.gradle文件。插件通常会自动修改这些文件添加依赖但检查一下总是好的。打开mainTemplate.gradle在dependencies块中应该能看到类似implementation com.google.android.gms:play-services-ads:23.0.0的依赖。AndroidManifest权限广告SDK需要网络权限。插件一般会自动在合并后的Manifest中添加。你可以检查构建出的APK包中的AndroidManifest.xml确认包含uses-permission android:nameandroid.permission.INTERNET /和uses-permission android:nameandroid.permission.ACCESS_NETWORK_STATE /。Proguard混淆如果启用如果你在Player Settings - Minify中启用了Proguard需要在Assets/Plugins/Android/proguard-user.txt文件中添加AdMob的混淆保留规则防止SDK的类和方法被错误移除。规则通常可以在AdMob官方文档或GitHub仓库的README中找到。5.2 iOS平台配置详解iOS的配置相对更复杂一些主要集中在Xcode项目设置上。基本信息确保Player Settings中的Bundle Identifier是唯一的且与你在AdMob和Apple开发者后台配置的一致。Info.plist配置AdMob SDK需要获取广告标识符IDFA。你需要在Info.plist文件中添加NSUserTrackingUsageDescription键并提供一个描述字符串向用户解释为什么需要跟踪数据用于展示个性化广告。这个描述会在App首次请求跟踪权限时弹窗显示。你可以在Unity中通过创建一个PostProcessBuild脚本来自动添加或者手动修改Xcode工程中的Info.plist。构建与Xcode设置使用Unity构建出Xcode工程。打开Xcode确保项目的Signing Capabilities中选择了正确的Team和Bundle ID。重要在Build Settings中找到Other Linker Flags确保包含了-ObjC标志。这个标志对于包含类别(Category)的Objective-C库AdMob SDK就是是必须的否则可能在运行时遇到unrecognized selector崩溃。Unity插件通常会尝试自动添加但手动确认一下更保险。SKAdNetworkiOS 14为了支持苹果的SKAdNetwork归因框架你需要在Info.plist的SKAdNetworkItems数组中添加Google的SKAdNetwork标识符。具体标识符列表在Google官方文档中通常插件也会尝试自动配置。5.3 真机测试与调试技巧在将应用发布到商店前必须在真机上进行全面测试。使用测试广告单元ID如前所述开发阶段务必使用AdMob提供的测试ID。这不仅能避免无效流量测试广告的填充率是100%且格式固定便于调试UI布局。添加测试设备在AdMob后台你可以将你的真机设备添加为测试设备。获取设备的广告IDAndroid或IDFAiOS并在AdMob后台的“应用设置”-“测试设备”中添加。这样即使你使用了正式的广告单元ID在这些设备上看到的也将是测试广告。查看Logcat/Xcode日志AdMob SDK会输出详细的日志。在Android上使用adb logcat -s Unity和adb logcat -s Ads来过滤查看相关日志。在iOS上通过Xcode的Console查看设备日志。这些日志是排查“广告加载失败”、“无填充”等问题的最重要依据。模拟网络环境测试弱网2G/3G和断网情况下广告的加载、超时和失败回调处理是否正常你的游戏逻辑是否健壮例如激励视频加载失败时是否给了用户其他选择。6. 常见问题排查与性能优化6.1 广告加载失败问题速查表问题现象可能原因排查步骤与解决方案控制台报错Invalid ad unit ID广告单元ID格式错误或未创建。1. 检查代码中的广告单元ID字符串是否与AdMob后台完全一致包括ca-app-pub-前缀。2. 登录AdMob后台确认该广告单元已成功创建且状态为“有效”。广告一直显示“正在加载”或回调OnAdFailedToLoad1. 网络连接问题。2. 应用ID未正确初始化。3. 广告单元未启用或受限。4. 地区限制新应用/新单元可能无填充。1. 检查设备网络尝试切换Wi-Fi/4G。2. 确认MobileAds.Initialize已成功回调且使用了正确的应用ID非广告单元ID。3. 在AdMob后台检查广告单元状态确保没有因政策问题被禁用。4.使用测试广告单元ID进行验证。如果测试ID能加载说明集成代码正确问题是正式单元无广告填充。新广告单元需要几小时甚至更长时间才能开始填充。Android构建后崩溃1. Android Support库或Play Services版本冲突。2. Proguard混淆规则缺失。3.minSdkVersion设置过高。1. 检查mainTemplate.gradle中的依赖版本尝试统一或使用插件推荐的版本。2. 确认已添加正确的Proguard保留规则。3. 确保minSdkVersion不低于22。iOS构建后崩溃启动时或展示广告时1. 缺少-ObjC链接器标志。2. 未添加NSUserTrackingUsageDescription。3. 证书或描述文件问题。1. 在Xcode的Build Settings - Other Linker Flags中添加-ObjC。2. 检查Info.plist是否有跟踪权限描述。3. 检查Xcode中的代码签名设置是否正确。激励视频看完未发放奖励奖励发放逻辑写在了错误的事件回调中。绝对确保奖励发放逻辑只在OnUserEarnedReward事件中执行。检查RewardedAdUnit类中的事件订阅。横幅广告位置或尺寸不对AdSize或AdPosition设置错误或Canvas缩放模式影响。1. 确认使用的AdSize常量正确。2. 对于自适应横幅使用AdSize.GetCurrentOrientationAnchoredAdaptiveBannerAdSizeWithWidth()并传入合适的宽度dp。3. 如果使用Unity UI注意广告视图是原生控件其坐标系与Unity UI可能不同位置是相对于屏幕的。6.2 性能与用户体验优化建议预加载策略不要在用户点击按钮的瞬间才去加载广告尤其是插页和激励视频那肯定来不及。应在合适的时机提前加载。激励视频在游戏主界面、关卡选择界面等空闲场景就调用LoadAd()进行预加载。加载成功后再将“观看广告”按钮设置为可点击状态。插页广告在游戏启动后、或一个关卡结束后立即加载下一个插页广告使其在下一个自然间歇点如下一关开始前就绪。内存管理广告对象特别是视频广告会占用内存。在不需要的时候如玩家购买了去广告功能或进入一个绝对不会有广告的场景主动调用DestroyAd()对于横幅或让插页/激励视频对象被GC回收不再持有引用可以释放内存。频率控制不要过度展示插页广告这会引起用户反感。可以设计一个简单的计数器或时间冷却机制例如“每通过3个关卡才展示一次插页广告”或者“两次插页广告之间至少间隔90秒”。网络超时与降级处理为广告加载设置一个超时例如10秒。如果超时仍未加载成功则跳过广告展示直接继续游戏流程并尝试在后台静默重试加载。对于激励视频加载失败时可以向用户提供替代方案如消耗少量游戏货币直接获得奖励。关注AdMob后台数据上线后定期查看AdMob后台的“概览”和“诊断”页面。关注展示次数/请求次数匹配率、每千次展示收入RPM、点击率CTR等关键指标。如果某个广告单元的匹配率持续很低可能需要检查广告格式设置或调整广告展示的时机和频率。集成广告变现是一个需要持续调优的过程。这个示例工程为你提供了一个坚实、可扩展的起点。当你熟悉了基本流程后可以进一步探索AdMob的中介功能通过接入Facebook Audience Network、Unity Ads、AppLovin等其他广告源来竞争填充率最大化收益。记住良好的广告体验与优秀的游戏内容同样重要平衡好两者才能实现商业化的长期成功。