对战游戏
1 业务背景
H5游戏对战平台(app)具备基本的账号、玩家基本信息、匹配撮合能力,实时语音聊天能力,并通过提供给cp的js lib库,能够将这些能力开放给内嵌在该平台上运行的对战游戏。游戏cp只关注游戏的核心玩法,从js 接口获取匹配好的玩家信息,进行游戏,将游戏结果通知给对战平台即可。
业务场场景流程:
玩家登陆对战平台app。
选择需要对战的游戏,点击随机对手匹配。
对战平台按匹配条件规则匹配好对手,打开cp游戏界面。
cp游戏启动,通过接口获取双方玩家信息,开始游戏。
游戏结束条件达成,上报游戏平台结果,一局游戏完成。
对战游戏平台app关闭cp游戏界面,显示结算界面。
玩家重新选择匹配新一局游戏。
2 基本概念
H5游戏对战平台是由安卓客户端App+H5游戏,服务端接入和匹配系统构成。
服务端匹配系统根据匹配规则将玩家撮合分配到一起进行一局游戏,客户端拉起相应的H5游戏。
App 通过JS引擎消息管道,与H5游戏的JS 运行时进行异步交互。
通过App提供给H5游戏相应的能力完成对战游戏逻辑的实现。(如:获取当前对局基本信息,提供游戏业务消息管道,提供游戏过程控制和事件侦听)
平台提供JS lib给CP方进行H5游戏接入。
对接入的H5游戏提供两种方式供选择:
cp无游戏服务端的游戏:由对战平台提供对战双方游戏业务消息管道,通过该管道,使得单机游戏快速的具备了联机游戏的能力。
cp自有游戏服务端的游戏: 游戏连接自有服务端进行联网对战,需在单局游戏开始和结束调用对战平台服务端API进行状态通知。
3 接入步骤
cp整个接入分为三个大的阶段步骤:
1.技术接入阶段
2.资源包提审阶段
3.测试与发布阶段
3.1 技术接入阶段
该阶段主要的目的是进行技术对接。由我方提供开发版对战大厅apk,用于cp技术层面接入和连调,需要向我方平台申请appKey,appSecret,cp提供产品包名(包名要求以.nearme.gamecenter结尾),通过配置开发版apk的配置文件(具体配置方式参见:4.2开发版配置说明),进行游戏功能连调,可采用url方式方便调试。
3.2 资源包提审阶段
开发版apk和游戏功能联调好后,进入提审阶段,将游戏按照文档中要求的方式(具体参见:8游戏资源包打包说明),放入manifest.json ,将游戏资源打包并加入签名,按审核流程要求填写游戏相关信息,并提交加签名后的游戏资源包,由官方人员进行审核通过。
审核附带美术资源,须按规范(详见:icon图片规范.png
) 示意,提供三种规格icon图标。
示例包见 示例h5demo
文件夹下的gpk和zip
3.3 测试与发布阶段
审核通过后,进入游戏测试阶段,有bug,修改后重新通过提审进行提交。测试通过后,技术层面即可具备上线发布的条件,具体发布方式则由业务产品进行制定和操作。
3.4 其他接入要求
游戏逻辑需提供与机器人对战的能力。
选择两种接入方式之一进行接入。
4 开发调试
4.1 下载调试工具
注意:真机调试时须要使用OPPO手机调试
4.2 配置说明
配置文件文件名: /sdcard/Android/data/com.nearme.play/files/debug_config.json
注意: /sdcard/Android/data/com.nearme.play/files在初次运行apk才会创建,拷贝进去配置后,需要杀进程重启才生效(启动时加载该配置)
{
"pkgName": "com.game.nearme.gamecenter",
"appKey": "fasdfxdfe",
"appSecret": "sdfesdfc",
"resourceType": "1",
"gameRes": "http://www.xxxxxx.com/gameres/index.html",
"gpkMd5": "65f7b302e59f6cc9ee02c52cee0104b0",
"gpkVerify": "0"
}
// 注意:此处如果 resourceType:2,则"gameRes":"com.game.nearme.gamecenter.gpk"
属性 | 类型 | 描述 |
---|---|---|
pkgName | String | 游戏包名,为游戏大厅给游戏分配的指定参数,用来区分校验游戏 |
appKey | String | 为游戏大厅给游戏分配的指定参数,用来区分校验游戏 |
appSecret | String | 为游戏大厅给游戏分配的指定参数,用来区分校验游戏 |
resourceType | String | 资源类型,"1"或"2",填"1"时,表示使用链接的形式加载资源,填"2"时, gameRes 表示使用gpk本地包的形式加载资源,放入相同路径/sdcard/Android/data/com.nearme.play/files/ ,配置对应的文件名到gameRes ,如"gameRes":"game1.gpk" |
gameRes | String | 取决于"resourceType" ,当resourceType ="1"时,这里填游戏的url;当resourceType ="2"时,这里填入的是gpk本地包的文件名,gpk的打包方法请参考游戏资源包打包说明。 |
gpkMd5 | String | " "或游戏包的md5,取决于gpkVerfiy |
gpkVerify | String | "0"不开启本地游戏资源包验证,"1"开启本地资源包验证,为正式环境下验证方式,需要游戏包打签名,并在gpkMd5 下填写游戏包的md5 |
配置文件的以及gpk的存放路径:
/sdcard/Android/data/com.nearme.play/files/debug_config.json
/sdcard/Android/data/com.nearme.play/files/xxxxx.gpk
5 JS SDK接入
目前 js lib 最新版本号为version 2,历史版本见jslib
文件夹下的 各版本的BattlePlatform.js。
app与H5游戏之间通过 BattlePlatform.msgChannel
进行异步交互,参数为cmd和param,param2,发送和侦听接收事件均采用这2个参数。
接入方式:
1.在index.html中,加入下列一行
<script type = "text/javascript" src="https://cdofs.oppomobile.com/cdo-activity/201809/06/1809062158/v2/BattlePlatform.js"></script>
2.在游戏中引用BattlePlatform
对象进行使用。
属性 | 类型 | 说明 |
---|---|---|
objToJson | Function | json对象转为字符串,等同于JSON.stringify |
jsonToObj | Function | 字符串转为json对象,等同于JSON.parse |
msgChannel | Object | 用于控制命令发送和监听 msgChannel.send(cmd, param) cmd: String 命令 data: String 参数 msgChannel.setRecvCallback(cmd, param1, param2) cmd: String 命令 param1: String 参数1 param2: String 参数2 |
使用引导可以参考demo 。
6 SDK通用命令和事件
命令
- lib初始化
cmd: "init"
param :
{
libVer, //int lib版本号,设置为 BattlePlatform.version
mode, //int 预留,填0
}
- 查询本局信息
cmd: "infoReq"
param :
{
appKey, //string 分配给cp的鉴权key
appSecret, //string 分配给cp的鉴权secret
}
异步应答(1v1双人对战)
cmd : "infoRsp"
param :
{
code, //int 返回码,0为成功,其他为错误
versionCode,//int 游戏大厅版本号(1002以下版本该字段为空,使用versionCode时需要判空)
message, //string 返回消息(预留字段)
pkgName, //string 游戏包名
selfUid, //自己的id,该id不是真是玩家uid
tableId, //string 分配的桌子id
tableToken, //string 分配的桌子token
public boolean isFirstEnterGame, // 是否第一次进入游戏(1200以下版本该字段为空)
players : //玩家列表
[
//第一个玩家是自己
{
uid, //string 玩家uid, 该id不是真是玩家uid
name, //string 玩家昵称
headIcon, //string 玩家头像url
sex, //string 玩家性别 ,M为男性,F为女性
micStatus, //int 预留字段
speakerStatus, //int 预留字段
tag, //int 0:正常玩家, 1: ai机器人
},
{
uid,
name,
headIcon,
sex,
micStatus,
speakerStatus,
tag,
},
],
}
异步应答(多人对战随机组队模式)
cmd : "infoRsp"
param :
{
code, //int 返回码,0为成功,其他为错误
message, //string 返回消息(预留字段)
pkgName, //string 游戏包名
selfUid, //自己的id,该id不是真是玩家uid
tableId, //string 分配的桌子id
tableToken, //string 分配的桌子token
versionCode, //int 游戏大厅版本号
isFirstEnterGame, // 是否第一次进入游戏
camps : [ // 队伍列表
{
campId, 队伍Id
players: // 玩家列表
[
{
uid, //string 玩家uid, 该id不是真是玩家uid
name, //string 玩家昵称
headIcon, //string 玩家头像url
sex, //string 玩家性别 ,M为男性,F为女性
micStatus, //int 预留字段
speakerStatus, //int 预留字段
tag, //int 0:正常玩家, 1: ai机器人
},
{
} ...
],
} ,
{
} ...
]
}
异步应答(多人对战随各自为战模式)
cmd : "infoRsp"
param :
{
code, //int 返回码,0为成功,其他为错误
message, //string 返回消息(预留字段)
pkgName, //string 游戏包名
selfUid, //自己的id,该id不是真是玩家uid
tableId, //string 分配的桌子id
tableToken, //string 分配的桌子token
versionCode, //int 游戏大厅版本号
isFirstEnterGame, // 是否第一次进入游戏
players : //玩家列表
[
//第一个玩家是自己
{
uid, //string 玩家uid, 该id不是真是玩家uid
name, //string 玩家昵称
headIcon, //string 玩家头像url
sex, //string 玩家性别 ,M为男性,F为女性
micStatus, //int 预留字段
speakerStatus, //int 预留字段
tag, //int 0:正常玩家, 1: ai机器人
} ...
],
}
- 强制退出游戏
cmd: "forceQuit"
param :
{
reason, //退出原因,0:正常退出, 1:异常退出,其他待定义
message, //退出信息
}
- 数据白板,游戏写给app一些额外数据
cmd: "writeBlackboard"
param :
{
key, //string
value, //string
}
游戏结束时需要将双方玩家的得分传回给小游戏大厅显示:
其中key为PLAYER_ONE_SCORE
表示自己,key为PLAYER_TWO_SCORE
表示对方;value表示分数,必须为大于0的整数。
- 设置加载挡板加载进度
支持的版本:versionCode >= 1200
cmd: "setLoadingProgress"
param: "50" // 0~100的整数
1200版本后的小游戏大厅增加了统一的载入面板,CP在准备自身资源的过程中(调用game.ready之前)需要调用该方法上传进度。
注:游戏收到onGameStart后会自动关闭挡板
- 设置屏幕旋转方向
cmd: "setWebViewOrientation"
param : "landscape" // landscape 横屏; portrait 竖屏
- 设置Appbar可见性(包括返回按钮,声音,麦克风按钮)
cmd: "setAppBarVisible"
param: "visible" // visible 可见;invisible 不可见
- 设置麦克风是否启用
cmd: "setVoiceMicrophoneEnable"
param: "enable" // enable 启用; disable 禁用
- 退出游戏
cmd: "activityQuitGame"
param: 无
事件
- 麦克风状态变化
cmd: "onMicStatusChanged" param : { uid, //string 玩家uid status, //int 0:关闭, 1: 打开 }
7 自有游戏服务端接入方式
前端命令和事件使用lib通用命令和事件。
游戏服务器需要调用平台后端2个API上报开始和结束。
7.1 游戏开始API
7.1.1 接口
http://play.open.oppomobile.com/instant-battle/gameapi/gameStart
7.1.2 请求方式
POST
7.1.3 入参列表
参数名称 | 参数类型 | 必填与否 | 样例取值 | 参数说明 |
---|---|---|---|---|
pkgName | string | 必须 | com.test.www | 游戏包名 |
tableId | string | 必须 | 256 | 桌子ID |
tableToken | string | 必须 | cc1128ac63a6e808bdeb8da3ff96d39c | 桌子token |
playerList | string | 必须 | {"playerlist":["1","2"]} | 玩家列表,json字符串, json字串说明: playerlist(string数组):所有玩家id |
timeStamp | string | 必须 | 1526304757000 | 时间戳,毫秒 |
sign | string | 必须 | 5eca48e0326e8e98a7800c537ae636ed | 签名,生成方式参考文末 |
extension | string | 可选 | 拓展字段 |
7.1.4.出参列表
注意: 出参为json字符串(如:{ "data": "", "errorcode": "0", "errormsg": "ok" })
出参名称 | 出参类型 | 样例取值 | 参数说明 |
---|---|---|---|
errorcode | string | 0 | 错误码 |
errormsg | string | ok | 错误信息 |
data | string | 返回信息 |
7.2 1v1对战游戏结束API
V1.3版本战斗新增多人对战方式,其中多人对战中分为各自为战、随机组队两种模式,加上之前1v1游戏,目前对战游戏有三种类型。
其中游戏开始接口都是同一个接口,战斗结束,因为各个模式关注得点不一致,所以结束接口有所不同。其中多人对战结束关注是个人,随机组队结束得时候关注得是一个阵营。
7.2.1 1v1对战接口
http://play.open.oppomobile.com/instant-battle/gameapi/gameEnd
7.2.2 1v1对战请求方式
POST
7.2.3 1v1对战入参列表
参数名称 | 参数类型 | 必填与否 | 样例取值 | 参数说明 |
---|---|---|---|---|
pkgName | string | 必须 | com.test.www | 游戏包名 |
tableId | string | 必须 | 256 | 桌子ID |
tableToken | string | 必须 | cc1128ac63a6e808bdeb8da3ff96d39c | 桌子token |
result | string | 必须 | {"resultlist":[{"uid":"1","result":1},{"uid":"2","result":2}]} | 对局结果列表,json字符串, 所有玩家在这局游戏的对应结果,一个json对象代表一个玩家的结果. uid(string):玩家id result(int):玩家输赢(1获胜,2失败,3平局) |
timeStamp | string | 必须 | 1526304757000 | 时间戳,毫秒 |
sign | string | 必须 | 5eca48e0326e8e98a7800c537ae636ed | 签名,生成方式参考文末 |
extension | string | 可选 | 拓展字段 |
7.2.4 1v1对战出参列表
注意: 出参为json字符串(如:{ "data": "", "errorcode": "0", "errormsg": "ok" })
出参名称 | 出参类型 | 样例取值 | 参数说明 |
---|---|---|---|
errorcode | string | 0 | 错误码 |
errormsg | string | ok | 错误信息 |
data | string | 返回信息 |
7.3多人对战 各自为战游戏结束API
7.3.1 接口
instant-battle/gameapi/settlement/manytomany/solo
7.3.2 请求方式
POST
7.3.3 入参列表
参数名称 | 参数类型 | 必填与否 | 样例取值 | 参数说明 |
---|---|---|---|---|
pkgName | string | 必须 | com.test.www | 游戏包名 |
tableId | string | 必须 | 256 | 桌子ID |
tableToken | string | 必须 | cc1128ac63a6e808bdeb8da3ff96d39c | 桌子token |
result | string | 必须 | {"resultlist":[{"playerId":"10000","score":111,"battleRet":1},{"playerId":"10001","score":222,"battleRet":2},{"playerId":"10002","score":333,"battleRet":3}]} | 对战结果列表 |
timeStamp | string | 必须 | 1526304757000 | 时间戳,毫秒 |
sign | string | 必须 | 5eca48e0326e8e98a7800c537ae636ed | 签名,生成方式参考文末 |
extension | string | 可选 | 拓展字段 |
7.3.4 对战结果列表参数
结果以一个玩家为主,需要传所有玩家在这局游戏的对应结果, 一个json对象代表一个玩家的结果。游戏如果以积分结算,则必须传入积分,如果以胜负结算,必须传入胜负结果。如若传入结果与结算方式不对应,则会调用失败。
参数 | 类型 | 参数说明 | 是否必须 |
---|---|---|---|
playerId | String | 玩家id | 必须 |
battleRet | Integer | 玩家输赢(1获胜,2失败,3平局) | 可选 |
score | Integer | 游戏业务积分。积分将会再客户端显示 | 可选 |
7.3.5 出参列表
注意: 出参为json字符串(如:{ "data": "", "errorcode": "0", "errormsg": "ok" })
出参名称 | 出参类型 | 样例取值 | 参数说明 |
---|---|---|---|
errorcode | string | 0 | 错误码 |
errormsg | string | ok | 错误信息 |
data | string | 返回信息 |
7.4多人对战 随机组队游戏结束API
7.4.1 接口
/instant-battle/gameapi//settlement/manytomany/teamrandom
7.4.2 请求方式
POST
7.4.3 入参列表
参数名称 | 参数类型 | 样例取值 | 必填与否 | 参数说明 |
---|---|---|---|---|
pkgName | string | com.test.www | 必须 | 游戏包名 |
tableId | string | 256 | 必须 | 桌子ID |
tableToken | string | cc1128ac63a6e808bdeb8da3ff96d39c | 必须 | 桌子token |
result | string | {"resultlist":[{"campId":"1","battleRet":1, "score": 100},{"campId":"2","battleRet":2, "score": 133},{"campId":"3","battleRet":3, "score":220}]}} | 必须 | 对局结果列表 |
timeStamp | string | 1526304757000 | 必须 | 时间戳,毫秒 |
sign | string | 5eca48e0326e8e98a7800c537ae636ed | 必须 | 签名,生成方式参考文末 |
extension | string | 可选 | 拓展字段 |
7.4.4 对战结果列表
结果以一个阵营为主,需要传所有玩家在这局游戏的对应结果, 一个json对象代表一个玩家的结果。游戏如果以积分结算,则必须传入积分,如果以胜负结算,必须传入胜负结果。如若传入结果与结算方式不对应,则会调用失败。
参数 | 类型 | 参数说明 | 是否必须 |
---|---|---|---|
campId | String | 阵营id | 必须 |
battleRet | Integet | 玩家输赢(1获胜,2失败,3平局) | 可选 |
score | Integer | 游戏业务积分。积分将会再客户端显示 | 可选 |
7.4.5 出参列表
注意: 出参为json字符串(如:{ "data": "", "errorcode": "0", "errormsg": "ok" })
出参名称 | 出参类型 | 样例取值 | 参数说明 |
---|---|---|---|
errorcode | string | 0 | 错误码 |
errormsg | string | ok | 错误信息 |
data | string | 返回信息 |
7.5 错误码列表
errorcode | errormsg | 描述 |
---|---|---|
0 | ok | 请求成功 |
10001 | parameter is error | 参数错误 |
10002 | sign is error | 签名错误 |
20001 | system error | 系统错误 |
30001 | table not exist | 桌子不存在 |
30002 | Inconsistent player list | 玩家列表不一致 |
30003 | table info error | 桌子信息错误 |
30004 | tableToken is error | 桌子token错误 |
30005 | tableId is error | 桌子ID错误 |
40001 | game error | 游戏错误 |
40002 | game pkgName is error | 游戏包错误 |
7.6 签名生成
Step 1 构造源串
- 构造源串所需参数
- pkgName(游戏包名)
- appKey(游戏上架时分配的Key)
- appSecret(游戏上架时分配的密钥)
- tableToken(桌子token)
- tableId(桌子ID)
- timeStamp(时间戳)
- 将参数按照字典序排序,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串。
- 注意:
- 参数名区分大小写
- 示例
str = "appKey=11&appSecret=22&pkgName=com.oppo.testgame&tableId=969&tableToken=33&timeStamp=15281"
Step 2 生成sign值
- 使用 md5 算法对构造的源串进行加密,将得到的字符串所有字符转换为大写
- 示例
7224B81F66699C774B4AF6A8E4837E62
7.7 游戏流程
1.游戏调用
init
初始化lib。2.游戏调用
infoReq
查询本局信息。3.游戏连接自己的服务器,开始游戏 ,游戏服调用
游戏开始API
通知平台。4.游戏结束,客户端调用
writeBlackboard
写入结算信息字符串(key见前文api描述),游戏服调用游戏结束API
通知平台。5.其他游戏异常,调用
forceQuit
命令强制退出。
8 无服务端接入方式
无游戏服务端方式下,通过大厅对战服来打通双方客户端的消息管道,前端需要使用到更多的命令和事件
8.1 命令API
8.1.1 对战接口
- 游戏初始化完毕,准备
cmd: "game.ready"
param :
{
param, //string 游戏业务附带数据
}
发送游戏消息请求
V1版本
cmd: "game.broadcast"
param :
{
msg, //string 游戏消息 ,注: 游戏协议需cp自定义协议需自行进行序列化为string类型
excptSelf, //int 是否排除自己,0:不排除,1:排除
}
param2 : msg
V2版本
cmd: "game.broadcast"
param :
{
excptSelf, //int 是否排除自己,0:不排除,1:排除
}
param2 : msg //string 游戏消息 ,注: 游戏协议需cp自定义协议需自行进行序列化为string类型
- 游戏结束请求(有胜负)
cmd: "game.finish"
param :
{
winner, //string 胜利者uid
}
- 游戏结束请求(平局)
版本需求:versionCode >= 1002
cmd: "game.finishDraw"
param : {}
8.1.2 多人对战接口(1.3.0)
以下接口为1.3.0后新增的多人对战接口
- 组内广播消息
向同一小组内的所有玩家广播消息。(仅限于多人对战的随机组队类型的游戏)
版本需求:versionCode >= 1300
cmd: "game.teamBroadcast"
param:
{
excptSelf, //int 是否排除自己,0:不排除,1:排除
}
param2 : msg //string 游戏消息 ,注: 游戏协议需cp自定义协议需自行进行序列化为string类型
- 指定玩家发送消息
向指定的玩家发送消息。
版本需求:versionCode >= 1300
cmd: "game.sendMsgToPlayers"
param:
{
playerIds, // string array 需要发送的玩家id数组
}
param2 : msg //string 游戏消息 ,注: 游戏协议需cp自定义协议需自行进行序列化为string类型
8.2 事件
8.2.1 对战接口
- 游戏正式开始事件
cmd: "game.onStart"
param :
{
param, //string 游戏业务附带数据
}
游戏结束事件
cmd: "game.onEnd" param : {}
收到游戏消息事件
V1版本
cmd: "game.onRecvMsg" param : { uid, //string 发送者uidf msg, //string 游戏消息 }
V2版本
cmd: "game.onRecvMsg"
param :
{
uid, //string 发送者uid
}
param2 : msg //string 游戏消息
8.1.2 多人对战接口(1.3.0)
- 收到组内广播的消息(仅限于多人对战的随机组队类型的游戏)
版本需求:versionCode >= 1300
cmd: "game.onRecvTeamMsg"
param :
{
campId, // String
uid //string 发送者uid
}
param2 : msg //string 游戏消息
- 收到其他玩家向指定玩家发送的消息
版本需求:versionCode >= 1300
cmd: "game.onRecvPlayerMsg"
param :
{
uid, //string 发送者uid
}
param2 : msg //string 游戏消息
- 收到玩家离开时的消息
版本需求:versionCode >= 1300
cmd: "game.onUserEscape"
param :
{
uIdList : // array 离开的玩家的id数组
}
8.3 游戏流程
1.游戏调用
init
初始化lib。2.游戏调用
infoReq
查询本局信息。3.游戏初始化,调用
game.ready
4.游戏收到
game.onStart
,开始执行对局玩法。5.游戏调用
game.broadcast
发送业务消息给其他玩家。6.游戏调用
game.finish
上报游戏结束请求。7.游戏收到
game.onEnd
, 本局对局结束,写入游戏客户端调用writeBlackboard
写入结算信息字符串。(key见前文api描述)
9 游戏资源包打包说明
将游戏打包为签名包,采用jdk自带的keytool 和 jarsigner工具,为标准的jar打签名包方式。
用keytool 生成keystore,如有keystore则跳过该步骤。
在h5资源包中需增加 manifest.json文件(建议utf-8 无bom格式),与index.html同级目录,内容如下
package: 为包唯一标示,
name:为外显名称,
icon:暂时预留
versionName : 外显版本
versionCode : 游戏版本号,必须为整数
minPlatformVersion : 对战平台最低能力版本号
{
"package": "com.company.unit",
"name": "游戏1",
"icon": "",
"versionName": "1.0",
"versionCode": 1,
"minPlatformVersion": 1
}
用zip工具打包,保证manifest.json等资源为根一级。
使用jarsigner 将zip包和keystore打为签名包,扩展名由zip改为gpk,提交审核。
目录结构参考 可参考示例: 示例h5demo.
生成keystore示例
keytool -genkey -keystore testkeypair.keystore -alias testkeypair -keyalg RSA -keysize 2048 -sigalg MD5withRSA -validity 99999
jarsigner 打签名示例
jarsigner -verbose -keystore testkeypair.keystore -signedjar signed.zip unsigned.zip -digestalg SHA1 -sigalg MD5withRSA testkeypair
10 挡板的使用
(游戏必须接入挡板,不接入挡板,将导致游戏加载时,进度条丢失,无法通过测试审核)
对战游戏的加载挡板功能怎么兼容?
通过命令infoRsp获取到的大厅版本号进行判断,从而进一步做兼容性处理。
若大厅版本号小于1200,则使用自己开发的挡板能力。
若大厅版本号大于等于1200,则使用大厅提供的挡板能力。通过setLoadingProgress命令设置进度,在游戏收到onGameStart后会自动关闭挡板。
var t = 0;
var interval = setInterval(function() {
// 模拟加载
console.log("setLoadingProgress " + t)
// gameInfo 对象的获取请参考对战游戏的Demo
if (gameInfo.versionCode && gameInfo.versionCode > 1200) {
msgChannel.send("setLoadingProgress", t)
} else {
// 请添加兼容老版本大厅的展示自定义挡板逻辑
}
t += 20;
if (t > 100) {
clearInterval(interval)
interval = null
// 请添加兼容老版本大厅的关闭自定义挡板逻辑代码
//5. game ready
msgChannel.send("game.ready","{}");
}
}, 1000)
11 Demo
自有服务端接入方式
"use strict"
var gameInfo = {} //查询到的对局信息
var appKey = "xyz"; //cp app key
var appSecret = "xyz"; //cp secret key
var msgChannel = BattlePlatform.msgChannel;
//-----------game logic-----------------
//初始化lib库
function init(mode){
var data = {"libVer":BattlePlatform.version,"mode":mode};
msgChannel.send("init",BattlePlatform.objToJson(data));
}
//获取对局信息请求
function infoReq(appKey, appSecret){
msgChannel.send("infoReq",BattlePlatform.objToJson({"appKey":appKey, "appSecret":appSecret}));
}
//强制退出
function forceQuit(reason,message){
var data = {};
data.reason = reason;
data.message = message;
var json = BattlePlatform.objToJson(data);
msgChannel.send("forceQuit",json);
}
//写数据白板
function writeBlackboard(key,value){
var data = {};
data.key = key;
data.value = value;
var json = BattlePlatform.objToJson(data);
msgChannel.send("writeBlackboard",json);
}
//---------------game init -----------------------------
function gameInit(){
console.log("game init!");
}
console.log("game js loaded!");
//1. 设置管道回调
msgChannel.setRecvCallback(function(cmd,param){
console.log("java cmd:"+cmd);
if(cmd == "infoRsp"){
gameInfo = BattlePlatform.jsonToObj(param);
//4.init game
gameInit();
//cp 后续游戏逻辑
}
})
//2. 初始化lib库
init(0);
//3.获取对局数据
infoReq(appKey, appSecret);
非自有服务端接入方式
"use strict"
var gameInfo = {} //查询到的对局信息
var appKey = "xyz"; //cp app key
var appSecret = "xyz"; //cp secret key
var msgChannel = BattlePlatform.msgChannel;
//-----------game logic--------------
function init(mode){
var data = {"libVer":BattlePlatform.version,"mode":mode};
msgChannel.send("init",BattlePlatform.objToJson(data));
}
function infoReq(appKey, appSecret){
msgChannel.send("infoReq",BattlePlatform.objToJson({"appKey":appKey, "appSecret":appSecret}));
}
function forceQuit(reason,message){
var data = {};
data.reason = reason;
data.message = message;
var json = BattlePlatform.objToJson(data);
msgChannel.send("forceQuit",json);
}
function writeBlackboard(key,value){
var data = {};
data.key = key;
data.value = value;
var json = BattlePlatform.objToJson(data);
msgChannel.send("writeBlackboard",json);
}
//游戏准备请求
function setReady(param){
var data = {};
data.param = param;
var json = BattlePlatform.objToJson(data);
msgChannel.send("game.ready",json);
}
//游戏结束请求
function setFinish(winner){
var data = {};
data.winner = winner;
var json = BattlePlatform.objToJson(data);
msgChannel.send("game.finish",json);
}
//发送游戏协议广播
function broadcast(msg,excptSelf){
var data = {};
data.excptSelf = 1;
if(excptSelf){
data.excptSelf = excptSelf;
}
var json = BattlePlatform.objToJson(data);
msgChannel.send2("game.broadcast",json,msg);
}
//收到游戏协议
function onRecvGameMsg(uid,param,param2){
var data = BattlePlatform.jsonToObj(param);
var uid = data.uid;
var msg = param2;
}
//游戏开始通知
function onGameStart(param){
console.log("game start!");
var data = BattlePlatform.jsonToObj(param);
//cp 游戏开始
}
//游戏结束通知
function onGameEnd(param){
console.log("game end!");
var data = BattlePlatform.jsonToObj(param);
//cp 游戏结束
}
//---------------game init ------------------------------
function gameInit(){
console.log("game init!");
//cp todo...
}
console.log("game js loaded!!!");
//1. 设置回调
msgChannel.setRecvCallback(function(cmd,param,param2){
console.log("java cmd:"+cmd);
if(cmd == "infoRsp"){
gameInfo = BattlePlatform.jsonToObj(param);
//4.初始化工作
gameInit();
//5. 游戏开始准备请求
setReady("{}");
}
if(cmd == "game.onStart"){
//6. 游戏正式开始
onGameStart(param);
}
if(cmd == "game.onEnd"){
//7. 游戏结束
onGameEnd(param);
}
if(cmd == "game.onRecvMsg") {
onRecvGameMsg(uid,param,param2);
}
})
//2. 初始化lib库
init(0);
//3.获取游戏对局数据
infoReq(appKey, appSecret);
12 注意事项
- 若对战游戏上过线,后续在本地调试时,需要更新下 versionCode 版本号,才能访问到最新的游戏代码
- 对战游戏需要把 rpk 包和配置文件 debug_config.json 一起放到 /sdcard/Android/data/com.nearme.play/files/ 目录下,若期间清除过调试器缓存,则 /sdcard/Android/data/com.nearme.play/files/ 目录也会一起被清除
- appKey 和 appSecret 需要联系商务申请获取