文檔結構php
一 遊戲入口html
二 遊戲統計node
三 視頻播放、音頻播放web
四 支付json
五 http通信api
六 遊戲配置文件數組
七 關鍵字屏蔽服務器
八 新手指引app
cocos2d-js-min.js cocos引擎ide
egreth5sdk.js 白鷺SDK
exif.js ???
gameApi.js 遊戲API(充值、分享統計等)
gameCenterSA.js ???
main.js 充值、統計、發送桌面
project.js 遊戲主文件
settings.js ???
zm_engine_v2.js ???
代碼:
Global.js 全局支付、請求指引
data.js 對話配置、關鍵詞屏蔽
gconst.js 全局常量 (字體名、商品類型等遊戲變量)
gevent.js 事件名
glang.js 全局語言(全部動態文本的固定文字)
gplayer.js 遊戲主類
grpc.js http通信
gsdk.js 原生交互、cordova視頻、支付
gutils.js 數組、字符串等數據處理
guideUI.js 新手指引
createRole.js 建立角色(暱稱)
gwnd.js 升級會話框
homeUI.js 主頁
loginUI.js 登陸
main.js 將遊戲內函數存入window,供外部調用
paymentUI.js 支付UI
videoPlayerUI.js 視頻播放
1 從白鷺平臺獲取egretID、用戶信息等,而後進入遊戲正式連接
<div id="gameIframeBox" style="display:block;margin:0 auto;width:100%;height:0px;background-color:#000">
<iframe id="gameIframe" name="gameIframe" src="http://api.egret-labs.org/v2/game/20409/91822?chanId=20409&channelId=20409&showLoginPanel=no&time=1509974135&egretstartfrom=gamece
nter&egretSdkDomain=https://api.egret-labs.org/v3&egretServerDomain=https://api.egret-labs.org/v3&egretOauthUser=1&egretId=3d66bcad0342d9c2b838810445fc166a&userId=D02F30FBF9C4E6C3F2F7B9738D11772F__qq
&userName=危險的畫本&userImg=http://q.qlogo.cn/qqapp/101237728/D02F30FBF9C4E6C3F2F7B9738D11772F/100&userSex=1&sign=118620d8e41c0ccfc78406b0c24c84e4&egretwt=mobile&isEgretLogin=1"
frameborder="no" border="0" marginwidth="0" marginheight="0" scrolling="no" width="100%" height="100%"></iframe>
</div>
2 正式遊戲網頁,加載cocos-js代碼,運行遊戲
<script src="src/settings.js" charset="utf-8"></script>
<script src="main.js" charset="utf-8"></script>
百度統計
TalkingData
新用戶進入遊戲的視頻單獨申請播放,和遊戲中升級後播放的視頻申請步驟不同。
遊戲中levelup後的視頻,在升級後根據等級level請求服務器獲取相應視頻連接。
請求加載視頻
login_preload: function() { gaudio.loadBgSound(), gevent.emit(gevent.EVENT_LOAD_VIDEO, 1101) }
gevent.on(gevent.EVENT_LOAD_VIDEO, function(e) { var t = e.detail; 1 != gsdk.videoType() && this.loadVideo(this.getVideoURL(t)) }
cocos自帶的video播放html視頻
loadVideo: function(e) { if (1 != gsdk.videoType()) { var t = new cc.Node("VideoPlayer"); t.setAnchorPoint(cc.p(0, 0)); var n = t.addComponent(cc.Widget); n.isAlignTop = !0, n.isAlignBottom = !0, n.isAlignLeft = !0, n.isAlignRight = !0, n.top = 0, n.bottom = 0, n.left = 0, n.right = 0, this.videoPlayer = t.addComponent(cc.VideoPlayer), this.videoPlayer.keepAspectRatio = !0, this.node.addChild(t), this.videoPlayer.remoteURL = e; var i = document.getElementsByClassName("cocosVideo")[0]; i.setAttribute("x5-video-player-type", "h5"), i.setAttribute("webkit-playsinline", "true"); var o = function() { this._onVideoEnded() }; this.videoPlayer.node.on("completed", o, this), this.videoPlayer.node.on("stopped", o, this), this.videoPlayer.play(), this.videoPlayer.pause(), this.videoPlayer.node.active = !1 } }
一種是進入遊戲時播放固定視頻
enterGmae: function() { var e = this.editBox.string; if (e.length <= 0) gwnd.showPrompt(glang.NameEmpty); else if (this._checkName(e)) gwnd.showPrompt(glang.ContainsIllegalCharacters); else { gevent.emit(gevent.EVENT_LOAD_VIDEO, 1102); var t = this; grpc.CreateRole(function(n) { 0 === n && (gsdk.analytics(gconst.TD_EVENT.CREATE_ROLE, { roleName: e }), gsdk.reportUserProgress(1), t.playVideo()) }, e) } }
第二種是角色升級時,播放視頻
_tryShowLevelUpAction: function() { this._tryShowComingTelphone() || this.tryShowStoryVideo() }, tryShowStoryVideo: function() { var e = gplayer.level; if (gplayer.max_story_video_level >= e) return ! 1; var t = gsheet.Level.find({ Level: e }); if (t.StoryVideoId <= 0) return ! 1; grpc.WatchStoryVideo(function(t) { 0 == t && (gplayer.max_story_video_level = e) }), gaudio.pauseBgSound(); var n = cc.instantiate(this.levelupAnim); this.node.addChild(n); return n.once("anim-complete", function(e) { n.removeFromParent(), gwnd.openVideo(t.StoryVideoId, gconst.VIDEO_TYPE.STORY) }, this), !0 }
須要播放視頻時,根據視頻ID和視頻類型,請求播放視頻。
gwnd.openVideo(1101, gconst.VIDEO_TYPE.STORY);
VIDEO_TYPE: { STORY: 1, FACE_TIME: 2, LIBRARY: 3, SEVENLOGIN: 4 }, VIDEO_PLAY_STATE: { NONE: 0, PLAYING: 1, PAUSED: 2, COMPLETED: 3 }
發送請求視頻事件
openVideo: function(e, t, n) {
gevent.emit(gevent.EVENT_OPEN_VIDEO, {
videoId: e,
callBack: n,
videoType: t
})
}
處理播放視頻事件,關閉背景音樂,並嘗試播放視頻
gevent.on(gevent.EVENT_OPEN_VIDEO, function(e) { this._isOpenVideo = !0, this.bgSoundState("video", !0), this.windowLayer.getChildren().length <= 0 && this.anim.play("Exit") }
gevent.on(gevent.EVENT_OPEN_VIDEO, function(e) { var t = e.detail; 1103 == t.videoId && (this.food = !0), this._quondamVideoId = t.videoId, this._videoId = gutils.getmapVideoID(t.videoId), this._videoType = t.videoType, this._callBack = t.callBack, Global.home.active = !1, 1 == gsdk.videoType() && (document.body.style.backgroundColor = "transparent", cc.director.setClearColor(cc.color(0, 0, 0, 0)), this.touchLayer.active = !0), this._tryPlay() }, this)
判斷是否須要加載小標題再播放視頻,或是直接播放視頻
_tryPlay: function() { this._playState = gconst.VIDEO_PLAY_STATE.NONE, this._lastSubtitle = null, this._subtitles = new Array; var e = !1; gconst.VIDEO_TYPE.STORY != this._videoType && gconst.VIDEO_TYPE.INTERIM != this._videoType || (e = !0), e && 1 == gsdk.videoType() ? this._loadSubtitle() : this._play() }
獲取並拼接視頻正式Url,並使用cordova播放視頻,監聽播放進度和播放完成事件
_play: function() { var e = this; if (1 == gsdk.videoType()) { this.maskLayer.active = !0, gsdk.videoStop(); var t = 0, n = 0; gplayer.hasOwnProperty("account_id") && (t = gplayer.account_id), gplayer.hasOwnProperty("level") && (n = gplayer.level), this.setloadingView(!0), gsdk.getVideoURL(this._videoId, t, n, function(t) { gsdk.videoPlay(t), e._onVideoPlaying(), gsdk.watchProgress(function(t) { e._onVideoProgress(t) }, function() { e._onVideoEnded() }) }, function(e, t) {}) } else this.onClicked(); gaudio.pauseBgSound() }
使用插件cordova播放視頻
videoPlay: function(e) { cordova.plugins.videobackground.play(e, !1) }
播放視頻結束,多是多段視頻組成的場景。第一段播放,中間有兩個選項,根據用戶選擇,再播放第二段視頻。
_onVideoEnded: function() { var e = !0; if (this.removeVideo(), this.food) this.food = !1, e = !1, this.nodeSelectFood.getChildByName("txtSelect").getComponent(cc.Label).string = glang.SelectFood, this.nodeSelectFood.active = !0; else { var t = gsheet.Video.find({ ID: this._quondamVideoId }); if (t) { this.btnPlay.active = !1, e = !1, this.options.active = !0; var n = this.options.getChildByName("op1"), i = this.options.getChildByName("op2"); n.getComponent(cc.Label).string = t.NextOption1, i.getComponent(cc.Label).string = t.NextOption2, n.once(cc.Node.EventType.TOUCH_END, function() { this.options.active = !1, this._quondamVideoId = t.NextVideoId1, this._videoId = gutils.getmapVideoID(t.NextVideoId1), this.loadVideo(this.getVideoURL(this._videoId)), this._tryPlay() }, this), i.once(cc.Node.EventType.TOUCH_END, function() { this.options.active = !1, this._quondamVideoId = t.NextVideoId2, this._videoId = gutils.getmapVideoID(t.NextVideoId2), this.loadVideo(this.getVideoURL(this._videoId)), this._tryPlay() }, this) } } e && this.closeVideoPlayer() }
點擊商品,提示購買
clickEvent: function() { var e = gsheet.ShopGoods.find({ ID: this._sheet.GoodsId }); if (gplayer.isHaveShopGoods(e)) gwnd.showPrompt(e.HaveTip); else if (e.OpenFuncId > 0 && !gplayer.isFuncOpend(e.OpenFuncId)) { var t = gsheet.FuncOpening.find({ ID: e.OpenFuncId }), n = gsheet.Level.find({ Level: t.OpenLevel }), i = gutils.substitute(glang.BuyUnopenedGoodsTip, e.Name, n.Day); gwnd.showPrompt(i) } else Global.sdk_payment_pay(e.ID) }
彈出購買商品的提示框
payment_pay: function(e) { var t = this; lobby.purchaseConfirm ? gwnd.confirmPrompt(function() { t.payment_resume() }, "商品購買", "請爲您選購的商品進行支付", gconst.PROMPT_WND_TYPE.ONE, "關閉") : gwnd.loading(!0); var n = { ID: e.ID, Price: 100 * e.Price, Label: e.Name, level: gplayer.level, Name: gplayer.name, accountId: gplayer.account_id }; lobby.purchase(n, e.ID, function(e) { e ? t.payment_OnRecipt(e) : (gwnd.closeConfirmPrompt(), t.payment_resume()), gwnd.loading(!1) }, function(e, t) { if (gwnd.closeConfirmPrompt(), gwnd.loading(!1), e || t) { var n = ""; t && t.length > 0 && (n += t), e && e.length > 0 && (n += "錯誤碼:" + e), gwnd.showPrompt(n) } else gwnd.showPrompt("充值失敗!") }, function(e, t, n, i, o) { gwnd.loading(!1), e >= n.Price ? gwnd.open("common_wnd", [n, i, o]) : gwnd.open("common_nomoney_wnd", [e, t, n, i, o]) }) }
確認購買。獲取商品ID、價格、名字等信息,發起購買請求。
project.js
payment_resume: function() { window.lobby && lobby.processPendingPurchases(this.payment_OnRecipt) }
"商品購買", "請爲您選購的商品進行支付", gconst.PROMPT_WND_TYPE.ONE, "關閉") : gwnd.loading(!0); var n = { ID: e.ID, Price: 100 * e.Price, Label: e.Name, level: gplayer.level, Name: gplayer.name, accountId: gplayer.account_id }; lobby.purchase(n, e.ID, function(e) { e ? t.payment_OnRecipt(e) : (gwnd.closeConfirmPrompt(), t.payment_resume()), gwnd.loading(!1) },
生成商品訂單
gameApi.js
//充值補單
this.processPendingPurchases = function(payment_OnRecipt_callback){
self.rpc.request(function(code, errmsg, rcptArr){
if(200 === code && rcptArr){
for(var i=0;i<rcptArr.length;i++){
payment_OnRecipt_callback(rcptArr[i]);
}
}
},self.sdkHash.loveportalurl + "pendingpurchase.php",{gameid:self.appId,account:self.lovechaccount,channel:self.CHANNEL_PLATFORM});
}
//充值 cData : {ID: 道具ID,Price: 價格 (分) ,Label: (商品名or描述) }
this.purchase = function(cData, dialysis, succeedCallback, failedCallback){ if(!self.sdkJs){ failedCallback(0, "登陸失敗請重試"); return; } var _attachs = { itemname:cData.Label, playerlevel:cData.level, playername:cData.Name, accountid:cData.accountId, channelattachs:self.sdkHash.attachs } var createOrderData = { gameid:self.appId, account:self.lovechaccount, sdkname:self.CHANNEL_SDKNAME, channel:self.CHANNEL_PLATFORM, itemid:cData.ID, attachs:_attachs } self.sdkJs.purchase(cData, createOrderData, succeedCallback, failedCallback); }
發起http請求購買。ZmSDK是啥...
main.js
//充值
this.purchase = function (cData, createOrderData, succeedCallback, failedCallback) { //建立訂單
self.rpc.request(function(code, errmsg, sData){ if(200 === code){ self.purchaseStart(cData, sData, succeedCallback, failedCallback); }else{ failedCallback(code, errmsg); } },self.sdkHash.loveportalurl + "requestpay.php",createOrderData); } this.purchaseStart = function (cData, sData, succeedCallback, failedCallback) { var payinfojson = { check: sData.attachs.check, feeid: cData.ID+"", fee: sData.price+"", feename: cData.Label, extradata: sData.gameorderid, serverid: 1+"", rolename: cData.Name, roleid: cData.accountId+"", servername: "" }; ZmSdk.getInstance().pay(payinfojson, function(data){ //跳轉到了支付頁面
if(data.retcode == 3) return; if(data.retcode == 1){ failedCallback(0,data.msg); return; } if(data.retcode == 2){ failedCallback(0,"您取消了支付"); return; } if(data.retcode == 0) succeedCallback(); }); }
支付成功回調
project.js
payment_OnRecipt: function(e) { grpc.SdkPaymentPay(function(t, n, i, o, s, a, c, l, r) { 1 !== t ? 0 === t && (lobby.finishPurchase(e), gplayer.buyShopGoodsSuccess(n, i, o, s, a, c, l, r), gwnd.showPrompt(glang.PaySuccess), gevent.emit(gevent.PAY_SUCCESS)) : lobby.finishPurchase(e) }, e) }
發起http請求,告知服務端支付完成
gameApi.js
//完成一筆訂單
this.finishPurchase = function(rcpt){ self.rpc.request(function(){},self.sdkHash.loveportalurl + "finishpurchase.php",{gameid:self.appId,receipt:rcpt}); }
遊戲內更新界面
gevent.on(gevent.PAY_SUCCESS, this._paySuccessHandler, this)
_paySuccessHandler: function() { this.updateView() }
post模式。
http包含token,且全部請求都寫在Http類裏面,留出回調接口
grpc: [function(e, t, n) { "use strict"; cc._RF.push(t, "e1dd3ZUONZBW4caM364h+q8", "grpc"); var i = { token: "", request: function(e, t, n) { gwnd.loading(!0); var i = new XMLHttpRequest; i.onreadystatechange = function() { if (cc.log("xhr.readyState:" + i.readyState + "--xhr.status:" + i.status + "--xhr.responseText:" + i.responseText), i.readyState == XMLHttpRequest.DONE) if (200 == i.status) { var e = i.responseText; cc.log("rpc:|" + e); var n = JSON.parse(e); if ("err10001" == n[0]) return gwnd.confirmPrompt(function() { gsdk.isExitLobby() ? gsdk.exitToLobby() : cc.director.loadScene("logoCanvas") }, " 重複登陸提醒", "檢測到您的帳號在其餘設備登陸游戲,請從新登陸驗證。", gconst.PROMPT_WND_TYPE.ONE), void gwnd.loading(!1); t.apply(this, n.rpc), "mail" in n && gplayer.addNewMails(n.mail), "activity" in n && gplayer.addNewActivitys(n.activity), "activity_target" in n && gplayer.updateActivityTargets(n.activity_target), gwnd.loading(!1) } else gwnd.loading(!1) }, i.open("POST", gsdk.serverURL(), !0); var o = "A=" + encodeURIComponent(e); if ("" !== this.token && (o = o + "&T=" + encodeURIComponent(this.token)), void 0 !== n) { o += "&G="; var s = JSON.stringify(n); o += encodeURIComponent(s) } cc.log("rpcName:" + e + "--send:" + o), i.setRequestHeader("Content-type", "application/x-www-form-urlencoded"), i.send(o) }, getSDKGiftReward: function(e, t) { this.request("getSDKGiftReward", e, [t]) }, getSendToDesktopReward: function(e) { this.request("getSendToDesktopReward", e, []) }, BindAccount: function(e, t, n, i, o) { this.request("BindAccount", e, [t, n, i, o]) },
全部標題、隨機姓名、對話所有都寫在代碼裏
取名時,從服務器下載name_forbit.txt,裏面有被屏蔽的關鍵字。
cc.loader.loadRes("forbid/name_forbid.txt", function(t, n) { t || (e._forbidNames = n.split("\r\n")) })
每次進入某界面(有新手指引的界面)時,檢查該界面是否須要顯示新手指引,若是有,則顯示新手指引。
這個檢查會每次進入某界面都會檢查一次,新手指引完成後,會向服務器發送保存當前指引步驟。
點擊新手指引按鈕時,會執行bind的處理函數handler,來繼續指引操做。
下一步新的指引
newbieGuide: function(e, t, n, i) {
gevent.emit(gevent.EVENT_GUIDE_START_STEP, {
step: e,
btnView: t,
callback: n,
isEnable: i
})
}
處理下一步新手指引
onLoad: function() { gevent.on(gevent.EVENT_GUIDE_START_STEP, this.guideStartHander, this) }, guideStartHander: function(e) { this.step = e.detail.step, this.btnView = e.detail.btnView, this.callback = e.detail.callback, this.isEnable = e.detail.isEnable, this._data = gsheet.GuideStep.find({ ID: this.step }), gsdk.analytics(gconst.TD_EVENT.BEGIN_MISSION, { guideStep: this.step }), this.newbieGuide() }
顯示Dog新手指引
newbieGuide: function() { if (this.node.on(cc.Node.EventType.TOUCH_END, this.touchNodeEnd, this), this.createDog(), void 0 != this.btnView) { var e = this.btnView.getComponent(cc.Button); void 0 != e && void 0 == this.isEnable && (e.interactable = !1), this.perch = cc.instantiate(this.perchPf), this.node.addChild(this.perch), this.schedule(this.updateDisplay, .2), this.perch.on(cc.Node.EventType.TOUCH_END, this.touchBtnEnd, this), this.arrow = cc.instantiate(this.arrows), this.arrow.rotation = this._data.Rotation, this.node.addChild(this.arrow), this.perch.active = !1, this.arrow.active = !1, this.updateDisplay() } }, updateDisplay: function() { var e = this.btnView.getNodeToWorldTransform(); if (void 0 != this.worldTransform) if (this.worldTransform.tx == e.tx && this.worldTransform.ty == e.ty) { this.perch.active = !0, this.arrow.active = !0, this.perch.x = e.tx, this.perch.y = e.ty; var t = this.btnView.getContentSize(); this.perch.setContentSize(t), this.arrow.x = e.tx + t.width / 2, this.arrow.y = e.ty + t.height / 2 } else this.worldTransform = e; else this.worldTransform = e }