心動女朋友框架分析

 文檔結構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請求服務器獲取相應視頻連接。

 

PC端播放視頻

請求加載視頻

 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 } }

 

App播放視頻

一種是進入遊戲時播放固定視頻

 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() }

 

五 Http通信協議

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 }
相關文章
相關標籤/搜索