雷】-、头条、抖。。。
专栏概述及⽬录:
笑苍天Smile:专栏概述及⽬录z huanlan.zhihu
游戏截图:
游戏地址:
扫⼀扫
头条/抖⾳扫⼀扫
游戏源码[1]。
白百合的老公游戏技术:前端引擎-Cocos creator,语⾔-Ts,后端-Netty,语⾔-Java,数据库-Redis。
制作⽬的:做这款游戏的起因是去年⼩游戏⽕了,想做⼀款游戏看看的市场反馈,之前在taptap上线了接近10款经典⼩游戏,扫雷的数据[2]最好,关注的玩家⽐较多,就选择将扫雷移植到上,还能通过TapTap进⾏导量(效果很惨)。其实taptap上最早的版本是⽤unity做的,但是unity不⽀持⼩游戏,就改成了cocos creator,然后今年ts⽕了,就⽤ts重写了⼀遍,代码看起来确实⽐js要规范舒服多了,不过总体写起来和js差别不⼤。
写作⽬的:⾃我总结,游戏引流,经验分享。
游戏分成Cocos creator引擎、netty框架、Redis数据库的通⽤技术,和头条的第三⽅接⼊,wind扫雷共3个部分。扫雷玩法相关可参考3D扫雷[3],这⾥记录⼀些在3D扫雷中没有的。
通⽤技术(Cocos creator引擎、netty框架、Redis数据库)
1. 屏幕适配
2. websocket⽹络框架+⼼跳包+⽹络消息分发
3. 全服排⾏榜
4. Redis数据库[4]
5. 阿⾥云 nginx websocket wss踩坑[5]
6. ⾃动图集功能[6]:对游戏包体的影响很⼩。
屏幕适配:在每个场景中调⽤这个接⼝就⾏,其他细节可参考官⽅⽂档[7]。
initCanvas(){
var canvas = Component(cc.Canvas);
新车保险谢霆锋与周迅var size = canvas.designResolution;
var cSize = FrameSize();
if (cc.sys.os == cc.sys.OS_IOS){ //刘海屏判断
GLB.bSpView = (cSize.width == 414 && cSize.height == 896)||(cSize.width == 375 && cSize.height == 812);
}
// else{
// if (cSize.height/cSize.width > 16/9) GLB.bSpView = true;
// }
if (GLB.bSpView){
canvas.fitWidth = true;
canvas.fitHeight = true;
}else if (cSize.width/cSize.height >= size.width/size.height){
canvas.fitWidth = false;
canvas.fitHeight = true;
}else{
canvas.fitWidth = true;
canvas.fitHeight = false;
}
}
⽹络消息分发::
websocket⽹络框架+⼼跳包+⽹络消息分发
前端:可参考COCOS官⽅⽂档[8]。
//Socket.ts⽂件主要内容。
//⽹络消息分发:在其他需要进⾏⽹络通信的⽂件中调⽤WS.sendMsg(cmd, msg, this),再⽤onResponse⽅法监听回调。//⼼跳包heartCheck
var ws = null;
var WS = {
ws: ws,
obj: null,
sendMsg: null,
close: null,
reconnect: null,
tt: null,
getStrPBMineNum: null,
getTPBMineNum: null,
getStrPBStepInfo: null,
};
var bError = false;
var lockReconnect = false;
var heartCheck = {
timeout: 30000,
timeoutObj: null,
serverTimeoutObj: null,
start: function(){
var self = this;
this.timeoutObj && clearTimeout(this.timeoutObj);
this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj);
this.timeoutObj = setTimeout(function(){
//这⾥发送⼀个⼼跳,后端收到后,返回⼀个⼼跳消息,
//onmessage拿到返回的⼼跳就说明连接正常
ws.send("0");
self.serverTimeoutObj = setTimeout(function() {
console.Time()+"heart-timeout");
ws.close();
}, self.timeout);
}, this.timeout)
}
}
var creatWS = function () {
ws = null;
if (CC_WECHATGAME || cc.sys.os === cc.sys.OS_IOS)
ws = new WebSocket("wss://websocket.windgzs/websocket"); //wx/ios
else if (cc.sys.os === cc.sys.OS_ANDROID)
ws = new WebSocket("ws://47.107.178.120:8080/websocket"); //anroid其中安卓ssl连不上
else{
// ws = new WebSocket("ws://127.0.0.1:8080/websocket"); //本地测试
ws = new WebSocket("wss://websocket.windgzs/websocket"); //本地
}
WS.ws = ws;
console.Time()+"Send Text WS was opened.");
if (GLB.msgBox) GLB.msgBox.active = false;
heartCheck.start();
};
heartCheck.start();
var data = event.data;
if (data == "0") return;
// console.Time()+"response text msg = " + data);
var i1 = data.indexOf(":");
if (i1 == -1 || WS.obj == null) return;
韩寒郭敬明什么关系var cmd = data.substring(0, i1);
var sResponse = data.substring(i1+1);
Response(cmd, sResponse);
};
};
console.Time()+"Send Text fired an error.", event);
bError = true;
};
if (e.code && String() != "1001" && GLB.msgBox) GLB.msgBox.active = true;
console.Time()+"WebSocket instance closed.", e);
if (bError == false) WS.reconnect();
};
}
WS.sendMsg = function (cmd: string, msg: string, obj) {
if (cmd == null) return;
if (ws.readyState === WebSocket.OPEN) {
if (cmd == "0"){
ws.send(cmd);
return;
}
msg = msg || "";
var str = cmd + ":" + String();
/
/ console.Time()+"sendMsg = ", str);
ws.send(str);
if (obj != null){
WS.obj = obj;
}
return true;
}else {
console.Time()+"WebSocket instance wasn'");
if (GLB.msgBox) GLB.msgBox.active = true;
return false;
火影忍者四代火影}
};
WS.close = function () {
ws.close();
};
if (lockReconnect) return;
lockReconnect = true;
< && );
< = setTimeout(()=>{
bError = false;
creatWS();
lockReconnect = false;
}, 1000)
};
后端:官⽅有开源的DEMO[9],稍微改改就能⽤。
全服排⾏榜:前端挑战成功后将玩家ID以及分数发给后端,后端存⼊Redis数据库,需要的时候再向后端请求排⾏榜数据,主要是Redis[10]的两个⽅法,存分数和取分数,这⾥选了有序集合。
//去年刚开始接触Java时候的代码,代码质量可能有点差,因为没啥问题就不改了
public void addScore(String sIdx, String sName, float iScore){
Jedis jedis = null;
try {
jedis = getPool().getResource();
/// ... do stuff here ... for example
Double iScorePre = jedis.zscore(sKeyScore + sIdx, sName);
if (iScorePre == null || iScore < iScorePre)
jedis.zadd(sKeyScore + sIdx, iScore, sName);
} finally {
// You have to close jedis object. If you don't close then
// it doesn't release back to pool and you can't get a new
// resource from pool.
if (jedis != null) {
jedis.close();
}
}
}
public String getStrRank(String sIdx){
Jedis jedis = null;
try {
jedis = getPool().getResource();
Set<Tuple> stItems = angeWithScores(sKeyScore + sIdx, 0, 9);
String str = String();
if (str.length() == 2)
return "";
str = place("], [", "|");
str = place("[", "");
str = place("]", "");
return str;
} finally {
if (jedis != null) {
jedis.close();
}
}
}
第三⽅接⼊(、头条、抖⾳)
1. 登陆/授权获取⽤户信息
2. 分享
感叹号的意思
3. 视频⼴告
4. 横屏⼴告
5. 插屏⼴告
6. 录屏[11]
7. 游戏间跳转
我将基本所有接⼊相关都放进了这个⽅法,其中头条API兼容API,所以⼤部分可以直接⽤的。头条系的接⼊是我接过的所有平台⾥坑最多的(可能是因为头条第⼀次做游戏),被打回来两次了,第三次才过审。
onWxEvent(s){
if (!CC_WECHATGAME) return;
let self = this;
switch(s){
case "initBanner": //横屏⼴告
if (this._bannerAd == null){
if (){
const {
windowWidth,
windowHeight,
发布评论