修改历史
时间 | 版本 |
2024/2/26 | 1.0.0 |
2024/3/22 | 1.0.1 |
2024/5/11 | 1.0.2 |
2024/10/23 | 1.0.3 |
接入流程(基于Unity 2020)
1. 添加minigame小游戏发布模版
Unity提供了2个默认模板(Default和Minimal),都在Unity的安装目录下


SDK使用的是Default模板,因为这个模板的功能更完整。把Default目录复制到项目的Assets/WebGLTemplates/<自己的模板> ,假设模板目录为minigameDefault,那么工程的Assets目录大概是这样子:

这样打包的时候,WebGL Template中会多出一个minigameDefault的选择,打包时选择minigameDefault。

2. 移除在移动设备上运行的警告
在minigameDefault模版工程的入口文件index.html中,把下面代码注释掉,不然Unity在手机上运行会弹出一个警告。

并将其改成下面这样:
container.className = "unity-mobile";
config.devicePixelRatio = 1;
也可以直接去修改unity-container的class:
<div id="unity-container" class="unity-mobile">
...
</div>
unity打出来的包通常会在右边出现滚动条,如下:

需要给body添加css样式以隐藏滚动条
<body style="overflow: hidden;">
3. 添加SDK链接
在index.html的head标签中添加SDK链接:
<script src="https://sdk.minigame.vip/js/1.1/minigame.js"></script>
4.添加SDK启动代码
在index.html中封装unity启动方法startUnity
,在进度条位置调用进度显示接口minigame.setLoadingProgress(100 * progress);
,并将unityInstance赋值给全局对象。
window.unityInstance = unityInstance;
,用于后续和SDK进行交互,最后再启动SDK: minigame.startGameAsync()
function startUnity(){
var script = document.createElement("script");
script.src = loaderUrl;
script.onload = () => {
createUnityInstance(canvas, config, (progress) => {
progressBarFull.style.width = 100 * progress + "%";
minigame.setLoadingProgress(100 * progress);
}).then((unityInstance) => {
loadingBar.style.display = "none";
fullscreenButton.onclick = () => {
unityInstance.SetFullscreen(1);
};
window.unityInstance = unityInstance;
setTimeout(() => {
minigame.startGameAsync();
}, 2000);
}).catch((message) => {
alert(message);
});
};
document.body.appendChild(script);
}
调用初始化minigame.initializeAsync
,启动成功后调用startUnity
。
minigame.initializeAsync()
.then(function () {
console.info("minigame initializeAsync..");
startUnity();
})
.catch(function (e) {
cc.error("minigame-sdk init error: ", e);
})
5. Unity和SDK的交互
Unity提供了.jslib扩展名文件来进行Unity和JavaScript的交互。不熟悉的同学可以看下官方链接:https://docs.unity3d.com/cn/2018.4/Manual/webgl-interactingwithbrowserscripting.html
js调用Unity:
这里假设 .jslib 文件名为JS.jslib,
将JS.jslib
文件放置在 Assets 文件夹中的“Plugins”子文件夹下。将广告接口调用添加到此文件里面,并通过unityInstance.SendMessage
来从javaScript层发消息给Unity层,假设场景中挂载广告代码的对象名称为ScriptControl
。
mergeInto(LibraryManager.library, {
showInterstitial: function() {
if (typeof MiniGameAds === "undefined") {
console.error("MiniGameAds is undefined");
return;
}
MiniGameAds.showInterstitial()
.then(function() {
unityInstance.SendMessage("ScriptControl", "showInterstitialCallback", 1);
}).catch(function(e) {
unityInstance.SendMessage("ScriptControl", "showInterstitialCallback", 0);
});
},
showRewardedVideo: function() {
if (typeof MiniGameAds === "undefined") {
console.error("MiniGameAds is undefined");
return;
}
MiniGameAds.showRewardedVideo()
.then(function() {
unityInstance.SendMessage("ScriptControl", "showReWardedCallback", 1);
}).catch(function(e) {
unityInstance.SendMessage("ScriptControl", "showReWardedCallback", 0);
});
},
showBanner: function() {
if (typeof MiniGameAds === "undefined") {
console.error("MiniGameAds is undefined");
return;
}
MiniGameAds.showBanner().then(function() {
unityInstance.SendMessage("ScriptControl", "showBannerCallback", 1);
}).catch(function(e){
unityInstance.SendMessage("ScriptControl", "showBannerCallback", 0);
});
},
hideBanner: function() {
if (typeof MiniGameAds === "undefined") {
console.error("MiniGameAds is undefined");
return;
}
MiniGameAds.hideBanner().then(function() {
unityInstance.SendMessage("ScriptControl", "hideBannerCallback", 1);
}).catch(function() {
unityInstance.SendMessage("ScriptControl", "hideBannerCallback", 0);
});
}
});
Unity调用js
public class AdsControl : MonoBehaviour {
[DllImport("__Internal")]
private static extern void showInterstitial();
[DllImport("__Internal")]
private static extern void showRewardedVideo();
[DllImport("__Internal")]
private static extern void showBanner();
[DllImport("__Internal")]
private static extern void hideBanner();
public void showReWardedCallback(int isSuccess) {
Debug.Log("show reward: " + (isSuccess == 1 ? "success" : "failed"));
}
public void showInterstitialCallback(int isSuccess) {
Debug.Log("show Interstitial: " + (isSuccess == 1 ? "success" : "failed"));
}
public void showBannerCallback(int isSuccess) {
Debug.Log("show banner: " + (isSuccess == 1 ? "success" : "failed"));
}
public void hideBannerCallback(int isSuccess) {
Debug.Log("hide banner: " + (isSuccess == 1 ? "success" : "failed"));
}
public void showInterstitialAd()
{
showInterstitial();
}
public void ShowRewardedVideoAd()
{
showRewardedVideo();
}
public void showBannerAd()
{
showBanner();
}
public void hideBannerAd()
{
hideBanner();
}
}
6. WebGL打包
WebGL打包时,默认会开启资源压缩,由于很多平台的服务器没有启用压缩格式的文件头,因此这里有两个选择:
1.关闭资源压缩
: 这会导致导出的游戏包体过大。

2.开启解压回退
: 默认情况下是关闭的,开启后会导致加载器大小增大和构建文件加载方案的效率降低,会增加游戏加载的时间。

具体选择哪一种,需要根据游戏项目的实际情况:
游戏上微游平台: 使用方案2。
游戏上Facebook平台: 使用方案1。
接口
广告相关接口
广告功能推荐使用微游SDK提供的广告接口(MiniGameAds)进行接入,对于熟悉FBIG接口的游戏开发者,也可以直接使用FBIG兼容的广告接口进行接入,推荐使用MiniGameAds。
MiniGameAds主要有5个接口:
作用:展示插屏广告
示例:
MiniGameAds.showInterstitial().then(()=>{
console.info("====> show interstitial success");
}).catch(e => {
console.error("====> show interstitial error: " + e.message);
});
注意:由于插屏广告默认第一次播放需要30秒后,才能播放,后面播放每次时间间隔40秒,如果cp测试的时候觉得时间太长,可以把minigame.json里fb_ad_delay_for_first_interstitial和fb_interstitial_refresh_interval字段改小。
作用:展示激励广告
示例:
MiniGameAds.showRewardedVideo().then(()=>{
console.info("====> show RewardedVideo success");
}).catch(e => {
console.error("====> show RewardedVideo error: " + e.message);
});
作用:激进版广告开关
返回:true为激进版激励广告,需要展示激进版UI,false为普通版激励广告,需要展示普通版UI。
激进版:包含误点击、诱导点击的广告策略,类型比如:
普通版:没有激进版的误点击和诱导点击的广告策略。
注意:倘若游戏中存在激进型激励广告,需要做两套处理:一套按照原本的激进版处理,还需做一套普通版的,
通过在minigame.json中添加字段"isAdRadical“字段可以控制isAdRadical()的返回值,
”isAdRadical“:true则isAdRadical() === true,”isAdRadical“:false则isAdRadical() === false
示例:
const isAdRadical = MiniGameAds.isAdRadical();
minigame.json具体配置如下:
{
"platform": "minigame",
"sdk": "https://sdk.minigame.vip/js/1.0/minigame-sdk.debug.js",
"instance": "FBInstant",
"gameName": "minigame",
"features": {
"ads": {
"enabled": false,
"isBannerEnabled": true,
"isTest": true,
"isAdRadical": true,
"config": {
"interstitial": "4864743603539728_5070034729677280",
"banner": "4864743603539728_5082905605056859",
"rewarded_video": "4864743603539728_5070034119677341",
"rewarded_interstitial": "4864743603539728_5070034119677341"
},
"options": {
"fb_max_ad_instance": 3,
"fb_init_ad_count": 3,
"fb_banner_refresh_interval": 40,
"fb_interstitial_refresh_interval": 40,
"fb_rewarded_video_refresh_interval": 0,
"fb_max_banner_error": 1,
"fb_max_interstitial_error": 3,
"fb_max_rewarded_video_error": 3,
"fb_auto_load_on_play": true,
"fb_auto_reload_delay": 1,
"fb_ad_delay_for_first_banner": 0,
"fb_ad_delay_for_first_interstitial": 30,
"fb_ad_delay_for_first_rewarded_video": 0
}
},
"leaderboard": {
"enabled": true
},
"ga": {
"enabled": true,
"isDefault": true,
"config": {
"gid": "UA-226110216-21"
}
}
}
}
作用:展示横幅广告
示例:
MiniGameAds.showBanner().then(()=>{
console.info("====> show banner success");
}).catch(e => {
console.error("====> show banner error: " + e.message);
});
作用:隐藏横幅广告
示例:
MiniGameAds.hideBanner().then(()=>{
console.info("====> hide banner success");
}).catch(e => {
console.error("====> hide banner error: " + e.message);
});
游戏统计(有需要才接入)
onGameEvent
public onGameEvent (eventName: string, label: string)
调用实例:
MiniGameAnalytics.onGameEvent("login", "自定义标签");
内购支付(有需要才接入)
内购接口
Examples:
minigame.payments.onReady(function () {
minigame.payments.getPurchasesAsync().then(function(purchases) {
console.log(purchases);
purchases.forEach((purchase) => {
minigame.payments.consumePurchaseAsync(purchase.purchaseToken)
.then(function () {
});
})
});
});
getCatalogAsync(): Promise<Product[]>
获取游戏的产品目录
Examples:
minigame.payments.getCatalogAsync().then(function (catalog) {
console.log(catalog);
});
purchaseAsync(purchaseConfig: PurchaseConfig): Promise<Purchase>
开始特定产品的购买流程, productID在接入的时候,我们会进行提供。
Examples:
minigame.payments.purchaseAsync(
{ productID: '12345',
developerPayload: 'foobar'
}).then(function (purchase) {
console.log(purchase);
});
getPurchasesAsync(): Promise<Purchase[]>
获取玩家未消费的所有购买商品
Examples:
minigame.payments.getPurchasesAsync().then(function (purchases) {
console.log(purchase);
});
consumePurchaseAsync(purchaseToken: string): Promise<void>
消费当前玩家拥有的特定购买商品
Examples:
minigame.payments.consumePurchaseAsync('54321').then(function () {
});
注意purchaseAsync支付成功后,需要马上调用消耗接口才能发放奖励,例如:
minigame.payments.purchaseAsync({
productID: '12345',
developerPayload: 'foobar',
}).then(function (purchase) {
minigame.payments.consumePurchaseAsync(purchase.purchaseToken)
.then(function () {
});
});
2.类型定义
支付参数:
export interface PurchaseConfig {
productID: string;
developerPayload?: string;
}
商品信息:
export interface Product {
title: string;
productID: string;
description?: string;
imageURI?: string;
price: string;
priceCurrencyCode: string;
priceAmount: number;
}
支付返回:
export interface Purchase {
developerPayload?: string;
paymentActionType: string;
paymentID: string;
productID: string;
purchasePrice: string;
purchaseTime: string;
purchaseToken: string;
signedRequest: string;
}
添加桌面快捷方式(置顶)(有需要才接入)
Examples:
minigame.canCreateShortcutAsync()
.then(function(canCreateShortcut) {
if (canCreateShortcut) {
minigame.createShortcutAsync()
.then(function() {
console.info("call createShortcutAsync done");
})
.catch(function(error) {
console.info("call createShortcutAsync fail: ", error);
});
}
})
.catch(function(error) {
console.info("canCreateShortcut fail: ", error);
});
注意: 必须先判断是否准备好,可以创建的情况才创建快捷方式。
分享(有需要才接入)
Examples:
minigame.shareAsync({
image: base64Picture,
text: 'X is asking for your help!',
data: { myReplayData: '...' },
switchContext: false,
}).then(function() {
})
.catch(function(error) {
});
参数定义:
interface SharePayload {
intent?: string;
image: string;
media?: MediaParams;
text: string;
data?: Object;
shareDestination?: string[],
switchContext?: boolean,
}
语言
const lang = minigame.getLocale();
if (lang.includes("en")){
console.info("current language is en");
}
if (lang.substr(0, 2) === "en"){
console.info("current language is en");
}
邀请
minigame.inviteAsync({
image: base64Picture,
text: {
default: 'X just invaded Y\'s village!',
localizations: {
ar_AR: 'X \u0641\u0642\u0637 \u063A\u0632\u062A ' +
'\u0642\u0631\u064A\u0629 Y!',
en_US: 'X just invaded Y\'s village!',
es_LA: '\u00A1X acaba de invadir el pueblo de Y!'
}
},
data: { myReplayData: '...' }
}).then(function() {
}).catch(function(error){
console.error("invite error: ", error);
})
参数定义:
interface InvitePayload {
image: string;
text: string | LocalizableContent;
cta?: string | LocalizableContent;
data?: Object;
filters?: Array<InviteSection>;
sections?: Array<InviteSection>;
dialogTitle?: string | LocalizableContent;
}
interface InviteSectionType {
GROUPS: string;
USERS: string;
}
interface LocalizationsDict {
[key: string]: string;
}
interface LocalizableContent {
default: string;
localizations: LocalizationsDict;
}
interface InviteSection {
sectionType: InviteSectionType;
maxResults: number;
}
本地测试环境
需要在游戏根目录启动一个web服务器,创建web服务器的方式有很多,比如node.js的http-server,Serve等模块,python的SimpleHTTPServer等等,具体使用什么库,由开发者自行决定。
这里以Node.js的http-server为例:
1.将minigame.json的isTest设置为true。
2.在游戏根目录启动web服务器,执行指令: http-server -o -c-l,这样会自动打开浏览器,并打开 http://127.0.0.1:8080/ ,接下来就可以直接测试广告了。
模拟线上环境
步骤:
1.打开https://minigamecloud.com/testgame 。
2.将minigame.json中的isTest设置为false
。
3.将接入SDK后的本地游戏链接,放进页面的地址栏中。

4.点击开始测试后,就可以模拟线上环境测试。
------------------------------------------------------ END ---------------------------------------------------------------------------