首页 > 应用开发 > R语言 > 小游戏入门到精通OR放弃?
需要使用Android手机
登录手Q开启厘米秀
侧滑点击人物形象或者选择任意一好友点击**「+」 滑拔一下找到 「厘米秀」**
| 搜索厘米秀 | 申请体验资格 | 开启厘米秀 |
|---|---|---|
|
|
|
| 侧滑 | 好友点「+」或者直接点击人物 | 游戏入口页 |
|
|
|
注册很简单,使用已有Q号登录「厘米游戏」开放平台按照流程提交资料审核即可 。 开发者接入官方说明文档
「厘米游戏」 开放平台注册提交资料的同时会注册一个相关联的**「QQ服务号」**。游戏中显示的用户信息是通过后台静默授权「QQ服务号」后再通过用户相关的接口获得,这点与微信公众号以及微信小游戏类似。
一句话概括: 目前暂未对个人开放,现阶段为邀请码模式。但如果你有好的IP资源或者优秀开发团队是比较好申请的。
与 「微信小游戏」 做比较目前来看最大的优势就是
现阶段游戏中集成广告所得广告费用平台不分成
游戏评级高官方可以让游戏上中心化首页推荐位
上线游戏都需要 「游戏自审自查报告」 、 「计算机软件著作权登记证书」 ,如需内购需要提供 「广电总局版号批文」 以及 「文化部备案信息」
如果使用了第三方引擎
Mac
电脑非必须。
第三方引擎的实现方式为基于 bricks 的 webGL 接口进行封装,具有较高的灵活性,但渲染性能会欠缺。 如开发者对性能要求更高,推荐使用bricks引擎的原生渲染 。
注意: iOS 在手 Q 770 版本禁用了 webGL ,会导致界面卡在 99% 加载界面,开发者忽略 iOS 端表现,关注安卓端表现。
关于使用什么引擎来开发「轻游戏」或者「H5游戏」都有各自的说法。就像大家讨论Java是世界最好的语言一样。
世界上没有不出bug的程序,引擎或者IDE都或多或少存在一定的Bug以及局限性。请根据项目需求以及当下的环境酌情选择。
本文示例使用的游戏引擎为Cocos Creator
游戏启动后,引擎会为开发者写入名为
GameStatusInfo
的有关游戏的全局参数(类似于H5中windows对象),从中可获取有关用户标识符(openId)、游戏标识(gameId)、机型等参数。
详细参数对照表请移步至 官方参考文档-登录与鉴权
示例参考-- 获取手Q版本跳转其他游戏
if (cc.sys.platform != cc.sys.QQ_PLAY) { self.setTipMsg("请在QQ玩一玩环境下测试"); return;
}if (BKTools.versionCompare(GameStatusInfo.QQVer, "7.7.0.0")) {
BKTools.skipGame("2731");
} else { self.setTipMsg("手Q版本过低,请更新");
}
如何跳转其他游戏完整的代码后面会提到
function getNick(callback) {
BK.MQQ.Account.getNick(GameStatusInfo.openId, callback);
}
BKTools.getNick(function(openId, nick) {
Global.nickName = nick;
});
getHead() { let self = this; let absolutePath = "GameSandBox://_head/" + GameStatusInfo.openId + ".jpg"; let isExit = BK.FileUtil.isFileExist(absolutePath);
cc.log(absolutePath + " is exit :" + isExit); //如果指定目录中存在此图像就直接显示否则从网络获取
if (isExit) {
cc.loader.load(absolutePath, function (err, texture) { if (err == null) {
self.head.getComponent(cc.Sprite).spriteFrame = new cc.SpriteFrame(texture);
}
});
} else {
BK.MQQ.Account.getHeadEx(GameStatusInfo.openId, function (oId, imgPath) {
cc.log("openId:" + oId + " imgPath:" + imgPath); var image = new Image();
image.onload = function () { var tex = new cc.Texture2D();
tex.initWithElement(image);
tex.handleLoadedTexture();
self.head.getComponent(cc.Sprite).spriteFrame = new cc.SpriteFrame(tex);
}
image.src = imgPath;
});
}
}, // onLoad () {},
btn(event, data) {
cc.log("点击了按钮"); if (cc.sys.platform == cc.sys.QQ_PLAY) { this.getHead();
} else {
cc.log("请在QQ玩一玩平台中测试");
}
},
QQ轻游戏分享方法比较多具体实现方式可以官方的 分享相关文档 。这里介绍常用的一种方式 多渠道分享 。
/**
* 获取分享信息
* @param {String} localPicPath
*/function getShareInfo(localPicPath) { if (!localPicPath) {
localPicPath = "GameRes://qrcode.png";//游戏资源包根目录存放图片qrcode.png
} let summarys = ["文案1", "游戏太好玩了,玩得停不下来!", "游戏太刺激了,邀请还能领抱枕!", "文案2"]; let shareInfo = { summary: summarys[getRandomInt(0, 3)], picUrl: "http://h.hiphotos.baidu.com/image/pic/item/18d8bc3eb13533fa4dd573ada3d3fd1f40345bd6.jpg", //支持HTTPS
extendInfo: Global.openId,//或者使用GameStatusInfo.openId
localPicPath: localPicPath, //分享至空间、微信、朋友圈时需要的图。(选填,若无该字段,系统使用游戏对应的二维码)
}; return shareInfo;
}/**
* 分享
* @param {*} shareInfo
* @param {*} callback
*/function toShare(shareInfo, callback) { if (cc.sys.platform == cc.sys.QQ_PLAY) {
BK.QQ.share(shareInfo, function (retCode, shareDest, isFirstShare) {
log("分享结果 retCode:" + retCode + " shareDest:" + shareDest + " isFirstShare:" + isFirstShare); if (retCode == 0) { if (callback) {
callback(0);
} if (shareDest == 0) { //聊天窗
log("成功分享至QQ");
} else if (shareDest == 1) { //空间
log("成功分享至空间");
} else if (shareDest == 2) { //微信
log("成功分享至微信");
} else if (shareDest == 3) { // 朋友圈
log("成功分享至朋友圈");
}
} else if (retCode == 1) { if (callback) {
callback(-1);
}
log("分享失败" + retCode);
} else if (retCode == 2) { if (callback) {
callback(-1);
}
log("分享失败,用户取消分享:" + retCode);
}
});
} else { if (callback) {
callback(0);
}
}
}
但这里有一个问题
点击右上角的「…」选择分享游戏,分享后图片不显示再次调用接口来实现分享时无任何影响
,要想解决此问题自需要实现生命周期监听并实现
onShare
方法
/**
* 游戏事件以及生命周期
*/function addGameEvent() { new BK.Game({ //游戏启动后
onLoad: function (app) {
log("BK.Game.onLoad");
}, //进入点击最大化后
onMaximize: function (app) {
log("BK.Game.onMaxmize");
}, //进入点击最小化后
onMinimize: function (app) {
log("BK.Game.onMinmize");
}, //进入后台后响应
onEnterBackground: function (app) {
log("BK.Game.onEnterbackground");
}, //回到前台后响应
onEnterForeground: function (app) {
log("BK.Game.onEnterforeground");
}, //点击“分享游戏”后响应。(可选)
onShare: function (app) {
log("BK.Game.onShare"); return getShareInfo();
}, //分享成功
onShareComplete: function (app, retCode, shareDest, isFirstShare) {
log("BK.Game.onShareComplete retCode:" + retCode + " shareDest:" + shareDest + " isFirstShare:" + isFirstShare);
}, //进入点击关闭响应
onClose: function (app) {
log("BK.Game.onClose");
}, //网络环境切换事件
onNetworkChange: function (app, state) {
log("BK.Game.onNetworkChange:STATE :" + state);
}, //全局异常监听
onException: function () {
log("BK.Game.onException msg:" + this.errorMessage() + " ,stack:" + this.errorStacktace());
}
});
}
支付接入比较简单,目前没有异步通知给开发者的接口,所有的逻辑都由玩一玩后台处理。支付接入步骤
平台上传道具资源(图片、描述、单价等)
道具申请上架
游戏内通过接口获取道具信息(道具ID、名称、图片等)
通过道具ID列表购买道具
具体流程实现参考官方文档- 支付
据内部消息 发送B2C红 在今年国庆假期间将有一批轻游戏试水,有什么样的创意和玩法可以期待一下。红包总金额最低5W最高20W。
在 原生引擎开发指引 中可以了解到。网络方案可以使用原生引擎、或者三方引擎进行界面以及逻辑的搭建。
下面我会介绍
BK.HttpUtil:用于短连接
XMLHttpRequest:用于短连接
WebSocket:用于长连接
BK.HttpUtil
function BKGet(url, callback, custom) { let httpUtil = new BK.HttpUtil(url);
httpUtil.setHttpMethod("get");
httpUtil.custom = custom; //绑定回调对象
httpUtil.requestAsync(callback.bind(httpUtil));
}
如果是POST请求,将
httpUtil.setHttpMethod("get");
设置为
httpUtil.setHttpMethod("post");
//下载图片并保存在手机中BKTools.BKGet("http://h.hiphotos.baidu.com/image/pic/item/18d8bc3eb13533fa4dd573ada3d3fd1f40345bd6.jpg", function (res, code) {
cc.log("结果:" + code + " 渗透参数:" + this.custom);
BK.FileUtil.writeBufferToFile("GameSandBox://test/test.jpg", res);
}, "custom");//普通的get请求BKTools.BKGet("http://www.wanandroid.com/tools/mockapi/3461/Javen", function (res, code) {
cc.log("结果:" + code + " 渗透参数:" + this.custom);if (code == 200) { let str = res.readAsString();
cc.log(str); let data = JSON.parse(str); if (data.code == 0) {
self.setTipMsg("网络请求结果 Gitee:" + data.data.name);
} else {
self.setTipMsg("网络请求异常:" + data.msg);
}
}
}, "请求返回字符串");
XMLHttpRequest
/**
* post 请求
* @param {*} url
* @param {*} data
* @param {*} callBack
*/function post(url, data, callBack) {
log("请求参数:" + data); var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () { let status = xhr.status; if (xhr.readyState == 4 && status == 200) { var responseBody = xhr.responseText;
log("响应的结果:" + responseBody);
callBack(status, JSON.parse(responseBody));
}
};
xhr.open("POST", url, true);
xhr.send(data);
}/**
* get请求
* @param {*} url
* @param {*} data
* @param {*} callBack
*/function get(url, data, callBack) {
log("请求参数:" + data); var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () { let status = xhr.status; if (xhr.readyState == 4 && status == 200) { var responseBody = xhr.responseText;
log("响应的结果:" + responseBody);
callBack(status, JSON.parse(responseBody));
}
};
xhr.open("GET", url + "?" + encodeURIComponent(data), true);
xhr.send();
}
如果是QQ玩一玩平台就是使用
BK.WebSocket,其他平台使用 标准的WebSocket
//Script/common/WebSocket.js /**
* @author Javen
* @copyright 2018-09-22 17:32:21 javendev@126.com
* @description webSocket工具组件
*/let Global = require("Global");let BKTools = require("BKTools");let WS_TYPE = cc.Enum({ BK_WS: 1, WEB_WS: 2,
});
cc.Class({ extends: cc.Component, // properties: {
// },
// onLoad () {},
start() { // this.schedule(function () {
// if (this.hasConnected) {
// }
// }, 5);
},
initWebSocket() { if (cc.sys.platform == cc.sys.QQ_PLAY) { this._ws = new BK.WebSocket("ws://" + Global.WEB_SOCKET.URL); this._wsType = WS_TYPE.BK_WS;
} else { this._ws = new WebSocket("ws://" + Global.WEB_SOCKET.URL); this._wsType = WS_TYPE.WEB_WS;
} this.addEventListener(this._ws);
},
addEventListener(ws) { let self = this;
ws.onopen = function (event) {
self._isConnected = true;
BKTools.log("onopen....");
};
ws.onerror = function (event) {
self._isConnected = false;
BKTools.log("onerror....");
};
ws.onclose = function (event) {
self._isConnected = false;
BKTools.log("onclose....");
}; if (self._wsType == WS_TYPE.BK_WS) {
ws.onMessage = function (ws, event) { if (event.isBinary) { let buf = event.data; //将游标pointer重置为0
buf.rewind(); let ab = new ArrayBuffer(buf.length); let dv = new DataView(ab); while (!buf.eof) {
dv.setUint8(buf.pointer, buf.readUint8Buffer());
}
self.toHander(ab);
} else {
BKTools.log("BK.WebSocket data type is not binary");
}
}
} else {
ws.onmessage = function (event) { if (event.data instanceof Blob) { let blob = event.data; var reader = new FileReader();
reader.readAsArrayBuffer(blob);
reader.onload = function (e) { if (e.target.readyState == FileReader.DONE) { let result = reader.result;
self.toHander(result);
}
}
} else {
BKTools.log("webSocket data type is not blob");
}
};
}
},
hasConnected() { return this._isConnected;
},
toHander(buffer) { let self = this; let cmd = proto.UserCmdOutComonProto.deserializeBinary(buffer); switch (cmd.getId()) { case proto.UserCmdOutType.RECONNECTION_RESULT:
BKTools.log("重连结果...."); break; case proto.UserCmdOutType.USER_CONNECT_SUCCESS:
BKTools.log("客户端连接成功...."); break; case proto.UserCmdOutType.USER_LOGIN_SUCCESS:
BKTools.log("反馈登录消息开始..."); break; case proto.UserCmdOutType.USER_LOGIN_SUCCESS_OVER:
BKTools.log("反馈登录消息结束...."); let loginOver = proto.PlayerLoginOverProtoOut.deserializeBinary(buffer); //回调给请求页
Global.loginResponse(loginOver); break; default: break;
}
},
send(bytes) { this._ws.send(bytes);
}, /**
* 登录
*/
toLogin() { if (!this.hasConnected()) { this.initWebSocket(); return;
} let login = new proto.UserLoginProto();
login.setId(proto.UserCmdInType.USER_LOGIN);
login.setToken(Global.WEB_SOCKET.TOKEN); this.send(login.serializeBinary());
}, // update (dt) {},});
如何使用?
将webSocket工具组件绑定到常驻节点,在通过
cc.find
查找常驻节点上的
WebSocket
组件
this._webSocket = cc.find("常驻节点名称").getComponent("WebSocket");//调用封装的接口this._webSocket.toLogin();
跳转到其他游戏手Q 7.7.0 及以上才支持
/**
* 判断手Q版本
* @param {String} ver1 7.1.1.1
* @param {String} ver2 6.3.3.3
*/function versionCompare(ver1, ver2) {
ver1 = parseInt(ver1.replace(/\./g, ""));
ver2 = parseInt(ver2.replace(/\./g, "")); if (ver1 >= ver2) { return true;
} else { return false;
}
}
/**
* 跳转到其他游戏
* @param {Number} gameId
*/function skipGame(gameId) {
BK.QQ.skipGame(gameId, "扩展参数");//游戏启动时可以通过GameStatusInfo.gameParam获取}
官方文档- 成绩上报与排行榜
最新版本接口示例 胜局积累-大到小
/**
* 成绩上报
* @param {*} isWin
* @param {*} callback
*/function uploadScore(isWin, callback) { if (cc.sys.platform != cc.sys.QQ_PLAY) { if (callback) {
callback(-1, "此接口只支持QQ玩一玩平台");
} return;
} if (!isWin) {
isWin = 0;
} else {
isWin = 1;
} var data = { userData: [{ openId: GameStatusInfo.openId, startMs: Global.startGameTime.toString(), endMs: ((new Date()).getTime()).toString(), scoreInfo: { score: isWin,
},
}, ], attr: { score: { type: 'rank', order: 3,
}
},
};
BK.QQ.uploadScoreWithoutRoom(1, data, function (errCode, cmd, data) {
log("uploadScoreWithoutRoom callback cmd" + cmd + " errCode:" + errCode + " data:" + JSON.stringify(data)); if (callback) {
callback(errCode, data);
}
});
} /**
* 拉取排行榜数据
* @param {*} callback
*/
function getRankList(callback) { if (cc.sys.platform != cc.sys.QQ_PLAY) { if (callback) {
callback(-1, "此接口只支持QQ玩一玩平台");
} return;
} let attr = "score"; let order = 3; let rankType = 0;
BK.QQ.getRankListWithoutRoom(attr, order, rankType, function (errCode, cmd, data) {
log("getRankListWithoutRoom callback cmd" + cmd + " errCode:" + errCode); if (errCode != 0) {
callback(errCode); return;
} if (data) { let rankList = data.data.ranking_list;
log("data not null " + rankList.length);
log(JSON.stringify(data)); // rankList.forEach(element => {
// log("....华丽的分割线....");
// log("score:" + element.score);
// log("nick:" + element.nick);
// log("....华丽的分割线....");
// });
if (callback) {
callback(errCode, rankList);
}
}
});
}
查询是否关注公众号
function checkPubAccountState(){
BK.QQ.checkPubAccountState(Global.PUIN ,function(errCode, cmd, data) {
BK.Script.log(0,0," callback errCode = "+errCode+ " cmd = "+ cmd + " data = "+ data); if(data.is_follow == 1){ return true;
}else{ return false;
}
});
}
进入公众号主页
/**
* 关注公众号
*/function follow() { if (cc.sys.platform == cc.sys.QQ_PLAY) {
BK.QQ.enterPubAccountCard(Global.PUIN);
}
}
如何获取 PUIN ?请移步至 官方-公众号
详细介绍请移步至官网- 广告接入流程
简单的封装与使用
/**
* 加载视频广告
*/function fetchVideoAd(videoType) { if (!videoType) {
videoType = 0;
}
log("开始加载视频广告..." + videoType);
BK.Advertisement.fetchVideoAd(videoType, function (retCode, msg, handle) {
log("retCode:" + retCode + " msg:" + msg); //返回码0表示成功
if (retCode == 0) {
Global.videoHandle = handle; //广告监听在业务逻辑中处理
} else {
log("拉取视频广告失败error:" + retCode + " msg:" + msg);
}
}.bind(this));
log("加载了视频广告...");
}/**
* 加载条幅广告
*/function fetchBannerAd() {
BK.Advertisement.fetchBannerAd(function (retCode, msg, bannerHandle) {
log("retCode:" + retCode + " msg:" + msg); if (retCode == 0) {
Global.bannerHandle = bannerHandle;
bannerHandle.onClickContent(function () {
log("用户点击了落地页");
});
bannerHandle.onClickClose(function () {
log("用户点击了X关闭广告");
});
} else {
log("fetchBannerAd failed. retCode:" + retCode);
}
}.bind(this));
}function closeBannerAd() {
log("关闭广告...."); if (Global.bannerHandle) {
Global.bannerHandle.close();
Global.bannerHandle = undefined;
}
}function loadBannerAd() { if (cc.sys.platform == cc.sys.QQ_PLAY) {
log("预加载Banner");
fetchBannerAd();
}
}function loadVideoAd() { if (cc.sys.platform == cc.sys.QQ_PLAY) {
log("预加载Video");
fetchVideoAd();
}
}
/**
* @author Javen
* @copyright 2018-09-26 15:53:52 javendev@126.com
* @description 广告测试
*/let BKTools = require("BKTools");var Global = require("Global");
cc.Class({ extends: cc.Component, properties: {
}, // onLoad () {},
btnClick(event, data) {
BKTools.log("点击了>" + data); if (data == 'loadVideo') { //如果需要判断是否加载成功可以在封装的函数中添加回调
BKTools.loadVideoAd();
} else if (data == 'showVideo') { if (Global.videoHandle) { this.jumpVideoAd();
} else {
BKTools.log("无视频广告句柄");
BKTools.loadVideoAd();
}
} else if (data == 'loadBanner') {
BKTools.loadBannerAd();
} else if (data == 'showBanner') { if (Global.bannerHandle) { this.showBannerAd();
} else {
BKTools.log("无条幅广告句柄");
BKTools.loadBannerAd();
}
} else if (data == 'closeBanner') {
BKTools.closeBannerAd();
} else if (data == 'back') {
cc.director.loadScene("welcome");
}
},
jumpVideoAd() { let self = this;
Global.videoHandle.jump();
Global.videoHandle.setEventCallack( function (code, msg) {}.bind(this), //关闭游戏(不再使用不需要监听)
function (code, msg) { if (code == 0) {
BKTools.log("达到看广告时长要求,可以下发奖励 endVide code:" + code + " msg:" + msg); //达到看广告时长要求,可以下发奖励
} else {
BKTools.log("其他异常,比如播放视频是程序返回到后台");
}
}.bind(this), function (code, msg) {
BKTools.log("关闭视频webview endVide code:" + code + " msg:" + msg); //关闭视频webview
}.bind(this), function (code, msg) {
BKTools.log("开始播放视频 startVide code:" + code + " msg:" + msg); //开始播放视频
}.bind(this));
},
showBannerAd() {
Global.bannerHandle.show(function (succCode, msg, handle) { if (succCode == 0) {
BKTools.log("banner展示成功 home");
} else {
BKTools.log("banner展示失败home msg:" + msg);
}
});
},
start() {
}, // update (dt) {},});
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/31557424/viewspace-2218282/ ,如需转载,请注明出处,否则将追究法律责任。