Solar2D 支持多种允许您展示应用内广告的插件。这些插件的基本工作方式相同;但是,存在一些您必须密切注意的细微差别。
所有广告提供商都有一些共同的概念。让我们从控制面板设置开始。
每个广告提供商都有一个控制面板,您必须创建一个帐户,设置您的应用程序,然后获取各种标识符……而且它们都不同。遵循他们的指南和文档将帮助您在他们的平台上进行设置。
您需要为要实现广告的每个应用/游戏创建一个“应用”。几乎在所有情况下,您都需要在其控制面板中为 iOS 版本和 Android 版本定义单独的应用。广告提供商会将 iOS 特定广告定位到 iOS 应用,将 Android 特定广告定位到 Android 应用,因此如果您同时为两个平台构建应用,即使您只有一个 Solar2D 应用,您也会有两个应用程序,这是有道理的。
每个供应商可能会为此使用不同的标识符,但在本教程中,我们将其称为“AppId”。它是一个字符串。该字符串可以是全数字、带十六进制数字的代码或带符号的长字符串。对于 Solar2D,这些都将是字符串。
接下来,您很可能必须定义要展示的广告类型,例如横幅广告、插屏广告或激励视频广告。根据您的应用设计,有时您可能需要具有单独跟踪的相同类型的多个广告。例如,您可能会在游戏中提供两种观看激励视频的奖励。每种奖励都有不同的金额,例如一个激励视频奖励 10 个金币,另一个奖励 5 个宝石。
您需要设置您的应用需要处理的不同值。因此,每个单独跟踪的广告位都有其标识符。这可以称为 PlacementId 或 adUnitId 或其他术语。在本教程中,我们将它们称为 `PlacementId`。与 `AppId` 一样,这些可能看起来像数字,但在 Solar2D 中它们都被视为字符串。注意:本教程中的代码使用 `adUnitId`,因为它使用 AdMob 作为实现示例。
这些广告位与应用一起使用。如果您同时拥有 iOS 应用和 Android 应用,并且每个应用都有三个广告位,那么您将拥有两个 `AppId` 和六个 `PlacementId`。
在您的代码中,您需要测试以查看您所在的平台,并使用每个广告位的相应 ID 初始化您的插件。
每个插件都必须包含在您的 `build.settings` 文件中。您应该能够从插件的文档页面复制并粘贴代码,并将其放入您的 `build.settings` 文件中。例如,要包含 AdMob,您需要将此块添加到您的 build.settings 中
settings = { plugins = { ["plugin.admob"] = { publisherId = "com.coronalabs" }, }, }
如果您还想包含 Chartboost,您可以这样做
settings = { plugins = { ["plugin.admob"] = { publisherId = "com.coronalabs" }, ["plugin.chartboost"] = { publisherId = "com.coronalabs" }, }, }
当然,这只是您的 `build.settings` 文件的一部分,您需要将此插件块与设置表中的其他子表一起添加。
在需要调用广告插件的每个模块中,您都需要引用该插件。在每个 `.lua` 文件的顶部附近,只需执行
local admob = require( "plugin.admob" )
这将使该插件可用于该模块。请记住,使用 Lua,插件模块仅在物理上需要并加载一次。在其他模块中需要时,您只需设置指向已需要模块的链接即可。
在初始化插件之前,您必须了解一些基本概念。
首先,初始化不会立即发生。由于初始化会进行网络调用,因此需要一些时间才能完成。您不希望您的应用在此过程中冻结,因此初始化实际上在后台进行,并且立即将控制权返回给您的应用。这意味着在初始化完成之前,您**不能**进行其他插件 API 调用。
初始化应该只发生一次。最好在 `main.lua` 中完成,但对于更高级的设置,您可以创建一个模块来保存您的广告代码,并在 `main.lua` 中引用该模块,但对于大多数人来说,您将在 `main.lua` 中完成所有这些操作。
每个插件都支持一个侦听器或回调函数,该函数在发生广告事件时执行。这些事件可能是诸如让您知道插件已完成初始化、广告已成功加载或激励视频观看已完成之类的事情。每个插件都会为这些**阶段**使用不同的名称,您可以在此侦听器函数中对其进行测试。
您将只有一个函数来处理所有这些事件。基本的侦听器函数如下所示(使用 AdMob 作为示例)
-- AdMob listener function local function adListener( event ) local json = require( "json" ) print("Ad event: ") print( json.prettify( event ) ) if ( event.phase == "init" ) then -- Successful initialization print( event.provider ) elseif ( event.phase == "loaded" ) then -- Ads were loaded elseif ( event.phase == "reward" ) then -- A rewarded video was completed end end
编写完核心侦听器函数后,您可以调用插件的 `.init()` 方法。这些方法通常需要您传递**侦听器函数的地址**、您的 `AppId` 以及其他其他参数,例如测试模式、GDPR 同意和其他选项。
由于广告提供商可能需要针对不同平台使用不同的 `AppId`,因此您的代码可能应该执行以下操作
local myAppId = "your_Android_AppId" local myInterstitialAdUnitId = "YOUR_ANDROID_ADMOB_INTERSTITAL_AD_UNIT_ID" local myBannerAdUnitId = "YOUR_ANDROID_ADMOB_BANNER_AD_UNIT_ID" local myRewardedAdUnitId = "YOUR_ANDROID_ADMOB_REWARDED_AD_UNIT_ID" if "ios" == system.getInfo("platform") then myAppId = "your_iOS_AppId" myInterstitialAdUnitId = "YOUR_IOS_ADMOB_INTERSTITAL_AD_UNIT_ID" myBannerAdUnitId = "YOUR_IOS_ADMOB_BANNER_AD_UNIT_ID" myRewardedAdUnitId = "YOUR_IOS_ADMOB_REWARDED_AD_UNIT_ID" end admob.init( adListener, { appId = myAppId, testMode = true } )
**注意:**在测试您的应用时,您应始终启用 `testMode`。如果您针对真实广告进行测试,一些广告提供商将取消您的帐户!
此时,使用正确的 `appId` 初始化插件的请求将完成。插件完成初始化后,将调用您的 `adListener` 函数。在您收到此事件之前,您无法对插件进行任何其他调用。
请注意,该代码还设置了变量来保存我们的广告位 ID(AdMob 的 `adUnitId`)
大型广告(例如插屏广告或基于视频的广告)需要在展示之前预加载。这是因为从提供商的服务器下载广告需要时间。
作为使用广告的应用开发者,您的部分责任是明智地使用广告。广告最佳实践超出了本教程的范围,但此时讨论预加载广告或缓存广告非常重要。如果您查看 Appodeal 的这篇博文,它讨论了预加载或缓存广告的问题之一。
如果您始终预加载广告,但直到用户完成例如游戏的五个关卡后才让用户有机会观看它,则许多用户可能在一段时间内都无法达到第五关。在这种情况下,在应用启动时预加载广告会浪费广告客户的广告。如果您计划使用户界面 (UI) 按钮让用户观看激励视频,例如,您可以在应用启动时预加载视频。
下面的代码使用了一个在应用启动时预加载的示例。由于您无法在插件初始化之前加载广告,因此您可以使用 `init` 事件作为可以加载广告的信号。查看此修改后的 `adListener` 函数
-- AdMob listener function local function adListener( event ) local json = require( "json" ) print("Ad event: ") print( json.prettify( event ) ) if ( event.phase == "init" ) then -- Successful initialization print( event.provider ) admob.load( "interstitial", { adUnitId = myInterstitialAdUnitId, hasUserConsent = true } ) admob.load( "banner", { adUnitId = myBannerAdUnitId, hasUserConsent = true } ) admob.load( "rewardedVideo", { adUnitId = myRewardedAdUnitId, hasUserConsent = true } ) elseif event.phase == "failed" then if event.type == "banner" then -- Put your ad loading failover code here -- The most common failure reason is lack of ads to show, so simply try to load another ad -- You don't want to do this over and over. Eventually, you should give up or try after a longer period of time elseif event.type == "interstitial" then -- Put your ad loading failover code here elseif event.type == "rewardedVideo" then -- Put your ad loading failover code here end elseif ( event.phase == "displayed" ) then -- Ads were loaded if event.type == "interstitial" then admob.load( "interstitial", { adUnitId = myInterstitialAdUnitId, hasUserConsent = true } ) elseif event.type == "rewardedVideo" then admob.load( "rewardedVideo", { adUnitId = myRewardedAdUnitId, hasUserConsent = true } ) end elseif ( event.phase == "reward" ) then -- A rewarded video was completed end end
这将只加载一次一个广告(每种类型一个)。某些插件可能会自动预加载第一个广告。展示此广告后,您必须先加载下一个广告,然后再展示它。如果您的插件支持,您可以查找指示广告已成功展示的 `event.phase`。这将因插件提供商而异。对于 AdMob,您可以使用 `displayed` 的 `event.phase` 来让您知道您可以加载下一个广告。看看 `adListener` 函数是如何为您完成大量工作的。
您会注意到,在展示横幅广告后,没有尝试加载横幅广告。大多数广告提供商会根据时间表轮换横幅广告,因此将为您加载下一个要展示的广告。如果您尝试在当前横幅广告展示时加载新广告,某些广告提供商将隐藏正在展示的横幅广告。
如果广告类型加载失败,您将获得“failed”的 `event.phase`。您可以利用此机会尝试从另一个插件加载广告(即设置您的广告瀑布)。您还可以使用计时器等待几秒钟,看看您的主要广告提供商是否有新的广告可用。
设置瀑布系统或定时广告重新加载系统超出了本教程的范围。
从代码的角度来看,展示广告毫不费力。但从应用设计的角度来看,需要更多思考。难题是“何时应该展示广告?”
插页式广告是全屏广告。您永远不希望在游戏过程中显示这些广告。这些广告适合在游戏过程和下一个关卡/游戏结束画面之间显示。但是,如果您的游戏每次游玩只持续几秒钟,那么您可能不希望每次游戏结束都显示广告。例如,您可能只想每五次显示一次广告。
关于在显示插页式广告之间应该等待多长时间,没有硬性规定。您最好不要惹恼用户以至于他们删除您的应用程序。这是一种平衡行为。
插页式广告的收益明显高于横幅广告。您应该考虑在游戏中尽可能使用这些广告。
当您进入游戏结束场景、下一级场景等时显示广告是有意义的。您可以在场景显示在屏幕上后,在 `scene:show()` 函数中执行此操作。
function scene:show( event ) if "did" == event.phase then local adTimer = nil local adShowAttempts = 10 local function showAd() if adTimer then timer.cancel( adTimer ) end adShowAttempts = adShowAttempts - 1 if adShowAttempts <= 0 then return end if not admob.isLoaded( "interstitial" ) then adTimer = timer.performWithDelay( 500, showAd ) else admob.show( "interstitial" ) end end adTimer = timer.performWithDelay( 100, showAd ) end end
用户必须采取操作关闭插页式广告才能返回应用程序,因此您无需执行任何操作来隐藏广告。您可以实施类似的测试,以在调用 show 函数之前验证广告是否已加载。
视频广告类似于插页式广告,它们是全屏的并且会完全打断用户体验。您应该考虑在显示插页式广告的相同位置显示这些广告。事实上,对于许多广告插件来说,插页式广告可能是视频广告。它们遵循与插页式广告相同的广告展示规则。
激励视频广告是一种特殊类型的视频广告,用户可以通过观看这些类型的广告来获得一些游戏内奖励。这些广告通常与 UI 中的按钮绑定,而不是像插页式广告那样打断游戏流程的广告。
也许您的按钮显示视频和大量硬币的图标。当用户与视频互动并完整观看后,他们将获得奖励。
**重要提示** 几乎所有支持激励视频广告的广告提供商都会以不同的方式处理这部分。在某些情况下,您可能只会收到一个表示奖励已完成的事件,并且由您的应用决定是否授予奖励。
另一个提供商可能允许您设置多个具有不同值的激励视频广告位置。也就是说,观看视频位置 A 并获得 50 个硬币,观看视频位置 B 并获得 10 个宝石。在这种情况下,您在 `adListener` 中获得的事件可能会告诉您要给予的奖励金额和类型。
其他提供商可能要求您设置一个 Web 服务器,并在其中放置一个 Web 脚本,以便广告提供商可以调用该脚本记录奖励,然后当您收到一个表示激励视频广告已完成的事件时,您可以与您的服务器核对以获取奖励的金额和类型。所有这些都应在广告提供商的网站上 documented。
在 AdMob 的情况下,没有服务器要求,但您将获得一个包含奖励信息的 “reward” 事件。您将在事件表中获得一个名为 `event.data` 的成员,其中包含一个可以转换为 Lua 表以供您使用的 JSON 字符串。考虑对 `adListener` 函数进行以下更改
-- AdMob listener function local function adListener( event ) local json = require( "json" ) print("Ad event: ") print( json.prettify( event ) ) if ( event.phase == "init" ) then -- Successful initialization print( event.provider ) admob.load( "interstitial", { adUnitId = myInterstitialAdUnitId, hasUserConsent = true } ) admob.load( "banner", { adUnitId = myBannerAdUnitId, hasUserConsent = true } ) admob.load( "rewardedVideo", { adUnitId = myRewardedAdUnitId, hasUserConsent = true } ) elseif event.phase == "failed" then if event.type == "banner" then -- Put your ad loading failover code here -- The most common failure reason is lack of ads to show, so simply try to load another ad -- You don't want to do this over and over. Eventually you should give up or try after a longer period of time elseif event.type == "interstitial" then -- Put your ad loading failover code here elseif event.type == "rewardedVideo" then -- Put your ad loading failover code here end elseif ( event.phase == "displayed" ) then -- Ads were loaded if event.type == "interstitial" then admob.load( "interstitial", { adUnitId = myInterstitialAdUnitId, hasUserConsent = true } ) elseif event.type == "rewardedVideo" then admob.load( "rewardedVideo", { adUnitId = myRewardedAdUnitId, hasUserConsent = true } ) end elseif ( event.phase == "reward" ) then -- A rewarded video was completed local data = json.decode( event.data ) local rewardAmount = data.rewardAmount local rewardItem = data.rewardItem -- code to give these to your user end end
讲解如何将这些奖励给予用户超出了本教程的范围。
要显示激励视频广告,请使用您喜欢的任何按钮方法(即,widget.newButton(),带有 `touch` 或 `tap` 侦听器的 display.newImageRect() 等)在用户界面中创建一个按钮,并在处理按钮交互的函数中调用 `.show()` 函数。
local function handleRewardedVideoButton( event ) if "began" == event.phase then -- touch type event admob.show( "rewardedVideo" ) end return true end
您还应该考虑在向用户呈现 UI 按钮之前测试广告是否可用。
要了解有关激励广告和实施策略的更多信息,请查看 AppsFlyer 的这篇文章。
由于广告插件仅适用于 Android 和 iOS 设备,因此您只能在真实设备上对其进行测试。Live Builds 可以帮助您进行构建和测试阶段,但除非您使用的是 Mac 并且模拟器的控制台日志仍然可以读取您设备的控制台日志,否则您无法从设备获取信息来了解发生了什么。
如果您使用的是 Mac,只要您不关闭“构建完成”对话框,就可以让 Solar2D 将应用程序安装到通过 USB 连接的设备上。来自您的设备和应用程序的消息将显示在 Solar2D 控制台日志中。如果您关闭该对话框,或者您没有 Mac,则必须使用其他工具来查看设备的控制台日志。
对于 iOS,您可以使用 Xcode 的“设备和模拟器”窗口并查看设备的控制台日志。对于 Android,您可以安装名为 `adb` 或 Android 调试桥的命令行工具。如何安装 `adb` 超出了本教程的范围,学习如何在您的计算机上运行它也是如此。互联网上有很多关于如何设置它的通用教程。
假设您现在可以看到设备的控制台日志,您在寻找什么?
所有广告插件提供商都会在生成的每个事件中返回信息:例如事件的 `phase`,它是否是错误,关于任何错误的文本响应以及更多信息。
了解您的应用程序发生了什么的 最简单方法是阅读这些信息。这可以通过打印事件表的内容来完成。Solar2D 提供了一个基本的 API 调用,可让您轻松转储任何表的内容。如果您查看 `adListener()` 函数的开头,您将找到以下代码
-- AdMob listener function local function adListener( event ) local json = require( "json" ) print("Ad event: ") print( json.prettify( event ) ) end
这包括 JSON 库。当然,您可以将需要该库的行移动到 `main.lua` 的顶部,或者如果您已经包含了它,则可以在此处跳过它。神奇之处在于第二个 print 语句。
使用 json.prettify 函数和 print 语句,Solar2D 将获取 Lua 表,将其转换为 JSON 字符串,然后以非常易读的格式打印 JSON 字符串。您应该得到如下所示的内容
Nexus 9: { Nexus 9: "data":"{"errorMsg":"No Ads Available","errorCode":3,"adUnitId":"ca-app-pub-xxxxxxxxxxxxxxxxx/xxxxxxxxxxx"}", Nexus 9: "name":"adsRequest", Nexus 9: "phase":"failed", Nexus 9: "provider":"admob", Nexus 9: "response":"loadFailed", Nexus 9: "type":"rewardedVideo", Nexus 9: "isError":false Nexus 9: }
您可以看到 `event.phase` 是什么。您是否收到了 `loaded` 事件?您是否收到了 `reward` 事件?您还会获得一个 `event.response` 事件。它是否告诉您没有可用的广告?它是否告诉您配置有问题?有了这些输出,如果您无法自行解决问题,您现在就可以将信息复制并粘贴到论坛消息中,向社区寻求帮助。
这是许多尝试实施广告的人未能注意到的关键步骤。当您在论坛上发帖询问“我正确地实施了 XYZ 广告插件,但我没有收到广告,出了什么问题?”时,如果没有您的代码副本和显示这些重要打印语句的控制台日志输出,则无法回答您的问题。
实施广告在广告提供商门户设置、最低限度的实施代码以及您的广告位置之间有很多活动部分。但是,通过一些耐心和了解您的应用程序流程,您可以为您的应用程序或游戏制定成功的获利计划。
请记住:不要惹恼您的用户。不要浪费您的广告客户资源。不要通过构建广告多于内容的应用或其他可能导致您被禁止的违规行为来违反商店的准则。要更详细地了解这些概念,请查看GameAnalytics 的这篇文章并向下滚动到“手机游戏中运行广告的 5 个要点”部分。
如有任何疑问,请随时寻求帮助。我们在论坛和Discord中有一个很棒的开发者社区,他们愿意为您提供帮助。