5什么是H5棋牌?H5棋牌
H5棋牌就是基于微信端、利用H5技術(shù)開發(fā)的在線棋牌游戲,不需要下載不需要注冊(cè),在微信上就可以進(jìn)入虛擬的棋牌室玩棋牌游戲,簡單方便易操作,是一種很好的空余時(shí)間線上棋牌娛樂方式!
普及常識(shí):H5的全稱是html5,叫做超文本標(biāo)記語言,2014年開始出現(xiàn),目前是第五次重大修改,上一代是H4SHI,1999年修訂的。H5從15年到17年也就發(fā)展了三年多,目前大多數(shù)做游戲的傳統(tǒng)游戲公司都做的是APP或者是PC端的頁游,真正H5的游戲還比較少。
H5HHHHHH5棋牌有什么優(yōu)勢?
(1)不需要下載不需要注冊(cè)也不需要充值,玩家在微信上通過房間鏈接進(jìn)入房間即可開始棋牌游戲,操作非常簡單方便;
(2)H5棋牌基于微信端分享,更容易傳播,更適合玩家在微信上操作。
(3)H5棋牌是基于H5技術(shù)開發(fā)的,不容易有外掛。
H5N什么是房卡模式?
房卡是是棋牌游戲中的一種虛擬道具,也是棋牌游戲平臺(tái)盈利的主要工具,玩家必需先購買房卡,才能開房進(jìn)入房間邀請(qǐng)好友玩游戲,開一次房就消耗一張房卡,房卡用完了可以找房卡代理商購買。
H5N房卡模式的優(yōu)勢?
房卡模式出現(xiàn)于2016年并發(fā)展起來,2017年房卡模式的棋牌游戲更加火爆,是棋牌游戲的重要?jiǎng)?chuàng)新,與傳統(tǒng)售賣游戲幣的棋牌游戲相比,房卡模式的盈利模式是售賣房卡,不存在實(shí)體貨幣與虛擬貨幣兌換,很大程度上規(guī)避了由銀商等角色帶來的賭博風(fēng)險(xiǎn)。
房卡棋牌游戲基于熟人關(guān)系,開房組局都是基于社交應(yīng)用的“好友”關(guān)系,玩家通過購買“房卡”創(chuàng)建游戲房間,然后通過微信或者QQ將房間號(hào)分享給好友,邀請(qǐng)好友進(jìn)入房間游戲。總的來說,房卡模式集合了線上棋牌的優(yōu)勢,隨時(shí)約局,利用空閑的碎片化時(shí)間就可來一局,同時(shí)又集合了線下棋牌室的優(yōu)點(diǎn),熟悉的地方玩法,熟人約戰(zhàn),自己規(guī)定輸贏值,不存在系統(tǒng)吃分。
H5H5“棋牌游戲”是否涉賭違法?
對(duì)于H5棋牌平臺(tái)而言,房卡游戲的贏利點(diǎn)是房卡的消耗,平臺(tái)上本身沒有游戲幣與資金的流動(dòng),沒有游戲幣的反向兌換,不存在刑事風(fēng)險(xiǎn)。對(duì)于下級(jí)代理商,若僅僅是銷售房卡,沒有參與游戲本身的抽水,也并不涉賭違法。
H5aNH5棋牌為什么能火?
第一、H5棋牌=微信棋牌,微信出個(gè)打飛機(jī)都能刷爆朋友圈,微信棋牌火的理所應(yīng)當(dāng),模式創(chuàng)新了。第二、微信是熟人社交,呼朋喚友賭博抽傭比賣貨賺錢多了,代理商們?yōu)榱隋X,自然奮不顧身。
H5N
H5棋牌有沒有外掛?
H5棋牌不存在外掛,因?yàn)橥鈷焱ǔ6际峭ㄟ^修改本地?cái)?shù)據(jù)來實(shí)現(xiàn)作弊,無論是透視,換牌等等都是一樣,而H5所有的數(shù)據(jù)都必須上傳到云端來進(jìn)行校對(duì),從而完美規(guī)避了外掛作弊的可能性。
H5aN做H5棋牌怎么賺錢?
很簡單,H5棋牌項(xiàng)目主要靠房卡代理賺取房卡零售利潤和代理差價(jià)收益!相當(dāng)于你幫助游戲平臺(tái)分享推廣了游戲,游戲平臺(tái)以房卡的形式給予你一定的利潤收益!
房卡代理商主要賺房卡拿貨差價(jià),群主主要賺房卡零售的利潤。
H5N做H5棋牌能賺多少錢?
(1)首先棋牌游戲這個(gè)市場是很大的,H5棋牌相當(dāng)于是微信棋牌室,越來越多的人參與并喜歡棋牌游戲娛樂,市場空間非常大。
(2)一般H5棋牌房卡代理商一個(gè)月收入1萬到10萬都是比較正常的,一個(gè)棋牌娛樂群主一天收益200到500也是比較正常的。
H5aN如何正確選擇靠譜的棋牌平臺(tái)合作?H5棋牌
1),首先棋牌游戲要是正規(guī)的游戲開發(fā)公司開發(fā)的!作為真正正規(guī)的棋牌游戲開發(fā)公司必需具備相關(guān)資質(zhì):文網(wǎng)文、軟件著作權(quán)證書、電信業(yè)務(wù)增值許可證、游戲版號(hào)等等,這些是作為正規(guī)的游戲開發(fā)必備的相關(guān)資質(zhì)。
2),棋牌游戲產(chǎn)品體驗(yàn)一定要好,有些小眾平臺(tái)不是正規(guī)游戲開發(fā)公司開發(fā)的,不是自己開發(fā)的源碼,也沒有后續(xù)的技術(shù)支持,游戲本身問題會(huì)很多,根本沒法正常玩下去。
3)不單要平臺(tái)靠譜,還要服務(wù)到位,如果只是買了房卡,沒有人及時(shí)服務(wù),有問題不能及時(shí)處理解決,非常影響自己發(fā)展,所以選擇對(duì)了平臺(tái),選擇跟誰合作也非常重要!
H5aN為什么有的平臺(tái)會(huì)經(jīng)常卡頓?
偶爾卡頓很正常,經(jīng)常卡頓無法進(jìn)入一般就是服務(wù)器被大流量攻擊,這種情況一般就是有人不想看到你發(fā)展就花錢請(qǐng)人(互聯(lián)網(wǎng)上有專門干這種事的人,也可以稱之為“黑客”)大量訪問攻擊服務(wù)器,導(dǎo)致服務(wù)器癱瘓無法訪問。
這就要看游戲平臺(tái)的防攻擊技術(shù)和能力夠不夠強(qiáng)大了!
H5aN為什么會(huì)被封鏈接?
這種情況一般也是有人操作大量惡意舉報(bào)投訴,導(dǎo)致微信官方接受投訴封掉游戲鏈接,而且被封次數(shù)多了后,微信官方本身也會(huì)進(jìn)行監(jiān)控。
H5N平臺(tái)如何才能真正穩(wěn)定不封?
(1)游戲平臺(tái)需要再加大資金和技術(shù)投入,增加技術(shù)防御等級(jí)、升級(jí)改進(jìn)游戲本身的防風(fēng)險(xiǎn)能力、嚴(yán)格規(guī)范市場運(yùn)營!
(2)另一個(gè)重要方面,那就是棋牌游戲開發(fā)公司相關(guān)資質(zhì)要具備,比如文網(wǎng)文、軟件著作證書等等,如此才能真正長遠(yuǎn)發(fā)展!!
H5N做好H5棋牌項(xiàng)目要注意什么?
做H5棋牌項(xiàng)目不能觸碰的四大紅線:
1),沒有經(jīng)驗(yàn)不學(xué)就做:
很多人看好H5棋牌項(xiàng)目,覺得能很快賺錢,但是沒有做過游戲代理沒有任何實(shí)操經(jīng)驗(yàn),急于賺錢就盲目操作不按正確的方法做,這樣往往適得其反一開始就很難做好導(dǎo)致失敗!
2),玩得過大、寧愿做賭徒:
有的群主為了更多得眼前利益,自己引導(dǎo)或者被部分玩家引導(dǎo)玩大的,群里玩家一天輸贏都是上千上萬,這樣就有涉嫌賭博的風(fēng)險(xiǎn)!
我們做H5棋牌項(xiàng)目,做的是游戲娛樂,為大眾用戶提供很好的空閑時(shí)間娛樂平臺(tái),而不是利用我們的H5棋牌做賭博,正常情況下就是正常的娛樂,如果為了更大的眼前利益玩得過大就不是單純得娛樂了,所以切記不要為了一時(shí)利益沒有了底線!
3),牽扯財(cái)務(wù)糾紛:
做H5棋牌項(xiàng)目也是做生意,牽扯到錢財(cái)利益的,該當(dāng)時(shí)結(jié)的就當(dāng)時(shí)結(jié)、該當(dāng)天結(jié)的就當(dāng)天結(jié),無論是代理團(tuán)隊(duì)之間還是親朋好友之間,不要互相借錢欠賬,也不要該收的錢不收,一旦牽涉財(cái)務(wù)糾紛就容易喪失彼此信任、影響自己的發(fā)展!
4),做甩手掌柜:
有些代理沒有團(tuán)隊(duì)意識(shí)、服務(wù)意識(shí),招募了代理把房卡給了代理就不管了,不用心教代理如何做,也不重視扶持新代理發(fā)展,這樣代理要么死掉不做要么流失,很難做大做強(qiáng)!
H5N如何防止挖坑被騙風(fēng)險(xiǎn)?
風(fēng)險(xiǎn)1,低價(jià)房卡被騙:
有些人投機(jī)取巧冒充各大平臺(tái)房卡代理商,低價(jià)大量出售各大平臺(tái)房卡,一開始發(fā)給你幾張真實(shí)的房卡體驗(yàn),等你轉(zhuǎn)賬后就拉黑跑人。
注:一般正規(guī)H5棋牌平臺(tái)都是有控價(jià)政策的,不會(huì)隨便低價(jià)賣卡,房卡代理商拿卡也是有成本的不會(huì)虧錢賣貨,所以不要輕易相信低價(jià),隨便賣低價(jià)的大都是騙子,只要是在合理的價(jià)格區(qū)間一般都是正常的!
風(fēng)險(xiǎn)2:選擇了仿冒的平臺(tái):
目前市面上主流的H5棋牌游戲平臺(tái),有的已經(jīng)出現(xiàn)了仿冒的平臺(tái),游戲功能、體驗(yàn)性、穩(wěn)定性都很差,很難正常操作,這樣的仿冒平臺(tái)一般都是超低價(jià)賣房卡,因?yàn)榉旅伴_發(fā)的成本非常低,但是穩(wěn)定性體驗(yàn)性非常差!
風(fēng)險(xiǎn)3:跟卡販子購買房卡
現(xiàn)在有些人不是專門做平臺(tái)運(yùn)營或者代理的,而是做各種平臺(tái)的房卡批發(fā),他們只是買房卡,基本沒有后續(xù)的服務(wù),跟他們購買房卡后續(xù)服務(wù)沒有保障,有問題不能及時(shí)解決。
要知道,棋牌游戲本身就是服務(wù)行業(yè),除了房卡本身,專業(yè)的服務(wù)更加重要,所以選擇靠譜平臺(tái)+跟專業(yè)的人合作最重要!
先H5棋牌是基于微信不需要下載的棋牌游戲,玩家只要進(jìn)入到游戲,一個(gè)鏈接就可以跟朋友一起玩,直接將線下的棋牌模式搬到了微信群里面,
房卡的模式就是熟人約局,用房卡來計(jì)算玩的局?jǐn)?shù),一般是10局一張房卡,平臺(tái)提供游戲給玩家,玩家自己來組局,無論大家玩不玩錢,玩多大跟平臺(tái),跟群主沒有關(guān)系
玩家開局需要房卡,平臺(tái)和群主賣房卡給玩家,這樣就規(guī)避了一般的棋牌的上分下分的踩線的法律風(fēng)險(xiǎn)。
比如炸金花0.5元一分,牛牛1元一分,然后進(jìn)入到公眾號(hào)里面開好房間,將游戲鏈接發(fā)到微信群里,游戲結(jié)束之后,根據(jù)游戲,同意的發(fā)送收款碼
這是游戲的一個(gè)大概的流程
2016以閑來麻將,皮皮麻將將房卡模式引爆,閑來麻將從4月份到11月份,總共賺了4個(gè)億,然后以20億的價(jià)格賣給了上市公司昆侖石油,隨后地方的棋牌麻將,異常的火爆,到今天之后,APP客戶端的麻將出現(xiàn)了各種各樣的問題,一個(gè)很嚴(yán)重的問題,就是外掛,如果一個(gè)游戲又了外掛來作弊,那么這個(gè)游戲玩家很容易的就流失掉
而H5房卡棋牌就能解決這一點(diǎn),H5的全稱是HTML5,14年開始了第五次的重大修改,這個(gè)產(chǎn)品從14年開始鋪開,到今年也就發(fā)展了三年多,大部分的游戲公司要不就是做APP,要不就是做PC端,微信端棋牌現(xiàn)在做的人非常少,屈指可數(shù),H5棋牌它有什么優(yōu)勢呢?
第一,客戶不需要下載,而且在PC端,用戶只要收藏,就能連接到服務(wù)器,帶來的好處是,轉(zhuǎn)化,教育的成本能夠降低很多,效率能夠提高很多倍
第二,不存在外掛,外掛一般是通過修改本地?cái)?shù)據(jù)實(shí)現(xiàn)作弊,無論是換牌,透視,H5的所有的數(shù)據(jù)全部都要上傳到云端校對(duì),從而完美的規(guī)避掉了外掛作弊的可能性
第三,做H5棋牌游戲,沒有風(fēng)險(xiǎn),首先沒有法律風(fēng)險(xiǎn),房卡模式?jīng)]有囤貨的風(fēng)險(xiǎn),隨取隨用
次分享總結(jié),起源于騰訊桌球項(xiàng)目,但是不僅僅限于項(xiàng)目本身。雖然基于Unity3D,很多東西同樣適用于Cocos。本文從以下10大點(diǎn)進(jìn)行闡述:
1.架構(gòu)設(shè)計(jì)
2.原生插件/平臺(tái)交互
3.版本與補(bǔ)丁
4.用腳本,還是不用?這是一個(gè)問題
5.資源管理
6.性能優(yōu)化
7.異常與Crash
8.適配與兼容
9.調(diào)試及開發(fā)工具
10.項(xiàng)目運(yùn)營
1、架構(gòu)設(shè)計(jì)
好的架構(gòu)利用大規(guī)模項(xiàng)目的多人團(tuán)隊(duì)開發(fā)和代碼管理,也利用查找錯(cuò)誤和后期維護(hù)。
框架的選擇:需要根據(jù)團(tuán)隊(duì)、項(xiàng)目來進(jìn)行選擇,沒有最好的框架,只有最合適的框架。
框架的使用:統(tǒng)一的框架能規(guī)范大家的行為,互相之間可以比較平滑切換,可維護(hù)性大大提升。除此之外,還能代碼解耦。例如StrangeIOC是一個(gè)超輕量級(jí)和高度可擴(kuò)展的控制反轉(zhuǎn)(IoC)框架,專門為C#和Unity編寫。已知公司內(nèi)部使用StrangeIOC框架的游戲有:騰訊桌球、歡樂麻將、植物大戰(zhàn)僵尸Online。
依賴注入(Dependency Injection,簡稱DI),是一個(gè)重要的面向?qū)ο缶幊痰姆▌t來削減計(jì)算機(jī)程序的耦合問題。依賴注入還有一個(gè)名字叫做控制反轉(zhuǎn)(Inversion of Control,英文縮寫為IoC)。依賴注入是這樣一個(gè)過程:由于某客戶類只依賴于服務(wù)類的一個(gè)接口,而不依賴于具體服務(wù)類,所以客戶類只定義一個(gè)注入點(diǎn)。在程序運(yùn)行過程中,客戶類不直接實(shí)例化具體服務(wù)類實(shí)例,而是客戶類的運(yùn)行上下文環(huán)境或?qū)iT組件負(fù)責(zé)實(shí)例化服務(wù)類,然后將其注入到客戶類中,保證客戶類的正常運(yùn)行。即對(duì)象在被創(chuàng)建的時(shí)候,由一個(gè)運(yùn)行上下文環(huán)境或?qū)iT組件將其所依賴的服務(wù)類對(duì)象的引用傳遞給它。也可以說,依賴被注入到對(duì)象中。所以,控制反轉(zhuǎn)是,關(guān)于一個(gè)對(duì)象如何獲取他所依賴的對(duì)象的引用,這個(gè)責(zé)任的反轉(zhuǎn)。
StrangeIOC采用MVCS(數(shù)據(jù)模型?Model,展示視圖?View,邏輯控制?Controller,服務(wù)Service)結(jié)構(gòu),通過消息/信號(hào)進(jìn)行交互和通信。整個(gè)MVCS框架跟flash的robotlegs基本一致。
數(shù)據(jù)模型 Model:主要負(fù)責(zé)數(shù)據(jù)的存儲(chǔ)和基本數(shù)據(jù)處理
展示視圖 View:主要負(fù)責(zé)UI界面展示和動(dòng)畫表現(xiàn)的處理
邏輯控制 Controller:主要負(fù)責(zé)業(yè)務(wù)邏輯處理,
服務(wù)Service:主要負(fù)責(zé)獨(dú)立的網(wǎng)絡(luò)收發(fā)請(qǐng)求等的一些功能。
消息/信號(hào):通過消息/信號(hào)去解耦Model、View、Controller、Service這四種模塊,他們之間通過消息/信號(hào)進(jìn)行交互。
綁定器Binder:負(fù)責(zé)綁定消息處理、接口與實(shí)例對(duì)象、View與Mediator的對(duì)應(yīng)關(guān)系。
MVCS Context:可以理解為MVC各個(gè)模塊存在的上下文,負(fù)責(zé)MVC綁定和實(shí)例的創(chuàng)建工作。
騰訊桌球客戶端項(xiàng)目框架
代碼目錄的組織:一般客戶端用得比較多的MVC框架,怎么劃分目錄?
先按業(yè)務(wù)功能劃分,再按照?MVC?來劃分。"蛋糕心語"就是使用的這種方式。
先按MVC劃分,再按照業(yè)務(wù)功能劃分。"D9"、"寶寶斗場"、"魔法花園"、"騰訊桌球"、"歡樂麻將"使用的這種方式。
根據(jù)使用習(xí)慣,可以自行選擇。個(gè)人推薦"先按業(yè)務(wù)功能劃分,再按照 MVC 來劃分",使得模塊更聚焦(高內(nèi)聚),第二種方式用多了發(fā)現(xiàn)隨著項(xiàng)目的運(yùn)營模塊增多,沒有第一種那么好維護(hù)。
Unity項(xiàng)目目錄的組織:結(jié)合Unity規(guī)定的一些特殊的用途的文件夾,我們建議Unity項(xiàng)目文件夾組織方式如下。
其中,Plugins支持Plugins/{Platform}這樣的命名規(guī)范:
Plugins/x86
Plugins/x86_64
Plugins/Android
Plugins/iOS
如果存在Plugins/{Platform},則加載Plugins/{Platform}目錄下的文件,否則加載Plugins目錄下的,也就是說,如果存在{Platform}目錄,Plugins根目錄下的DLL是不會(huì)加載的。
另外,資源組織采用分文件夾存儲(chǔ)"成品資源"及"原料資源"的方式處理:防止無關(guān)資源參與打包,RawResource即原始資源,Resource即成品資源。當(dāng)然并不限于RawResource這種形式,其他Unity規(guī)定的特殊文件夾都可以這樣,例如Raw Standard Assets。
公司組件
msdk(sns、支付midas、推送燈塔、監(jiān)控Bugly)
apollo
apollo voice
xlua
目前我們的騰訊桌球、四國軍棋都接入了apollo,但是如果服務(wù)器不采用apollo框架,不建議客戶端接apollo,而是直接接msdk減少二次封裝信息的丟失和帶來的錯(cuò)誤,方便以后升級(jí)維護(hù),并且減少導(dǎo)入無用的代碼。
第三方插件選型
NGUI
DoTween
GIF
GAF
VectrosityScripts
PoolManager
Mad Level Manger
2、原生插件/平臺(tái)交互
雖然大多時(shí)候使用Unity3D進(jìn)行游戲開發(fā)時(shí),只需要使用C#進(jìn)行邏輯編寫。但有時(shí)候不可避免的需要使用和編寫原生插件,例如一些第三方插件只提供C/C++原生插件、復(fù)用已有的C/C++模塊等。有一些功能是Unity3D實(shí)現(xiàn)不了,必須要調(diào)用Android/iOS原生接口,比如獲取手機(jī)的硬件信息(UnityEngine.SystemInfo沒有提供的部分)、調(diào)用系統(tǒng)的原生彈窗、手機(jī)震動(dòng)等等
2.1C/C++插件
編寫和使用原生插件的幾個(gè)關(guān)鍵點(diǎn):
創(chuàng)建C/C++原生插件
導(dǎo)出接口必須是C ABI-compatible函數(shù)
函數(shù)調(diào)用約定
在C#中標(biāo)識(shí)C/C++的函數(shù)并調(diào)用
標(biāo)識(shí) DLL 中的函數(shù)。至少指定函數(shù)的名稱和包含該函數(shù)的 DLL 的名稱。
創(chuàng)建用于容納 DLL 函數(shù)的類。可以使用現(xiàn)有類,為每一非托管函數(shù)創(chuàng)建單獨(dú)的類,或者創(chuàng)建包含一組相關(guān)的非托管函數(shù)的一個(gè)類。
在托管代碼中創(chuàng)建原型。使用?DllImportAttribute?標(biāo)識(shí) DLL 和函數(shù)。?用?static?和?extern?修飾符標(biāo)記方法。
調(diào)用 DLL 函數(shù)。像處理其他任何托管方法一樣調(diào)用托管類上的方法。
在C#中創(chuàng)建回調(diào)函數(shù),C/C++調(diào)用C#回調(diào)函數(shù)
創(chuàng)建托管回調(diào)函數(shù)。
創(chuàng)建一個(gè)委托,并將其作為參數(shù)傳遞給?C/C++函數(shù)。平臺(tái)調(diào)用會(huì)自動(dòng)將委托轉(zhuǎn)換為常見的回調(diào)格式。
確保在回調(diào)函數(shù)完成其工作之前,垃圾回收器不會(huì)回收委托。
那么C#與原生插件之間是如何實(shí)現(xiàn)互相調(diào)用的呢?
1.將源碼編譯為托管模塊;
2.將托管模塊組合為程序集;
3.加載公共語言運(yùn)行時(shí)CLR;
4.執(zhí)行程序集代碼。
注:CLR(公共語言運(yùn)行時(shí),Common Language Runtime)和Java虛擬機(jī)一樣也是一個(gè)運(yùn)行時(shí)環(huán)境,它負(fù)責(zé)資源管理(內(nèi)存分配和垃圾收集),并保證應(yīng)用和底層操作系統(tǒng)之間必要的分離。
為了提高平臺(tái)的可靠性,以及為了達(dá)到面向事務(wù)的電子商務(wù)應(yīng)用所要求的穩(wěn)定性級(jí)別,CLR還要負(fù)責(zé)其他一些任務(wù),比如監(jiān)視程序的運(yùn)行。按照.NET的說法,在CLR監(jiān)視之下運(yùn)行的程序?qū)儆?托管"(managed)代碼,而不在CLR之下、直接在裸機(jī)上運(yùn)行的應(yīng)用或者組件屬于"非托管"(unmanaged)的代碼。
這幾個(gè)過程我總結(jié)為下圖:
圖 .NET上的程序運(yùn)行
回調(diào)函數(shù)是托管代碼C#中的定義的函數(shù),對(duì)回調(diào)函數(shù)的調(diào)用,實(shí)現(xiàn)從非托管C/C++代碼中調(diào)用托管C#代碼。那么C/C++是如何調(diào)用C#的呢?大致分為2步,可以用下圖表示:
將回調(diào)函數(shù)指針注冊(cè)到非托管C/C++代碼中(C#中回調(diào)函數(shù)指委托delegate)
調(diào)用注冊(cè)過的托管C#函數(shù)指針
相比較托管調(diào)用非托管,回調(diào)函數(shù)方式稍微復(fù)雜一些。回調(diào)函數(shù)非常適合重復(fù)執(zhí)行的任務(wù)、異步調(diào)用等情況下使用。
由上面的介紹可以知道CLR提供了C#程序運(yùn)行的環(huán)境,與非托管代碼的C/C++交互調(diào)用也由它來完成。CLR提供兩種用于與非托管C/C++代碼進(jìn)行交互的機(jī)制:
平臺(tái)調(diào)用(Platform Invoke,簡稱PInvoke或者P/Invoke),它使托管代碼能夠調(diào)用從非托管DLL中導(dǎo)出的函數(shù)。
COM 互操作,它使托管代碼能夠通過接口與組件對(duì)象模型 (COM) 對(duì)象交互。考慮跨平臺(tái)性,Unity3D不使用這種方式。
平臺(tái)調(diào)用依賴于元數(shù)據(jù)在運(yùn)行時(shí)查找導(dǎo)出的函數(shù)并封送(Marshal)其參數(shù)。下圖顯示了這一過程。
注意:1.除涉及回調(diào)函數(shù)時(shí)以外,平臺(tái)調(diào)用方法調(diào)用從托管代碼流向非托管代碼,而絕不會(huì)以相反方向流動(dòng)。雖然平臺(tái)調(diào)用的調(diào)用只能從托管代碼流向非托管代碼,但是數(shù)據(jù)仍然可以作為輸入?yún)?shù)或輸出參數(shù)在兩個(gè)方向流動(dòng)。2.圖中DLL表示動(dòng)態(tài)庫,Windows平臺(tái)指.dll文件、Linux/Android指.so文件、Mac OS X指.dylib/framework文件、iOS中只能使用.a。后文都使用DLL代指,并且DLL使用C/C++編寫。
當(dāng)"平臺(tái)調(diào)用"調(diào)用非托管函數(shù)時(shí),它將依次執(zhí)行以下操作:
查找包含該函數(shù)的DLL。
將該DLL加載到內(nèi)存中。
查找函數(shù)在內(nèi)存中的地址并將其參數(shù)推到堆棧上,以封送所需的數(shù)據(jù)(參數(shù))。
將控制權(quán)轉(zhuǎn)移給非托管函數(shù)。
注意:只在第一次調(diào)用函數(shù)時(shí),才會(huì)查找和加載 DLL 并查找函數(shù)在內(nèi)存中的地址。iOS中使用的是.a已經(jīng)靜態(tài)打包到最終執(zhí)行文件中。
2.2Android插件
Java同樣提供了這樣一個(gè)擴(kuò)展機(jī)制JNI(Java Native Interface),能夠與C/C++互相通信。
注:
JNI wiki這里不深入介紹JNI,有興趣的可以自行去研究。如果你還不知道JNI也不用怕,就像Unity3D使用C/C++庫一樣,用起來還是比較簡單的,只需要知道這個(gè)東西即可。并且Unity3D對(duì)C/C++橋接器這塊做了封裝,提供AndroidJNI/AndroidJNIHelper/AndroidJavaObject/AndroidJavaClass/AndroidJavaProxy方便使用等,具體使用后面在介紹。JNI提供了若干的API實(shí)現(xiàn)了Java和其他語言的通信(主要是C&C++)。從Java1.1開始,JNI標(biāo)準(zhǔn)成為java平臺(tái)的一部分,它允許Java代碼和其他語言寫的代碼進(jìn)行交互,保證本地代碼能工作在任何Java?虛擬機(jī)環(huán)境下。"
作為知識(shí)擴(kuò)展,提一下Android Java虛擬機(jī)。Android的Java虛擬機(jī)有2個(gè),最開始是Dalvik,后面Google在Android 4.4系統(tǒng)新增一種應(yīng)用運(yùn)行模式ART。ART與Dalvik 之間的主要區(qū)別是其具有提前 (AOT) 編譯模式。 根據(jù) AOT 概念,設(shè)備安裝應(yīng)用時(shí),DEX 字節(jié)代碼轉(zhuǎn)換僅進(jìn)行一次。 相比于 Dalvik,這樣可實(shí)現(xiàn)真正的優(yōu)勢 ,因?yàn)?Dalvik 的即時(shí) (JIT) 編譯方法需要在每次運(yùn)行應(yīng)用時(shí)都進(jìn)行代碼轉(zhuǎn)換。下文中用Java虛擬機(jī)代指Dalvik/ART。
C#/Java都可以和C/C++通信,那么通過編寫一個(gè)C/C++模塊作為橋接,就使得C#與Java通信成為了可能,如下圖所示:
注:C/C++橋接器本身跟Unity3D沒有直接關(guān)系,不屬于Android和Unity3D,圖中放在Unity3D中是為了代指libunity.so中實(shí)現(xiàn)的橋接器以表示真實(shí)的情況。
通過JNI既可以用于Java代碼調(diào)用C/C++代碼,也可用于C/C++代碼與Java(Dalvik/ART虛擬機(jī))的交互。JNI定義了2個(gè)關(guān)鍵概念/結(jié)構(gòu):JavaVM、JNIENV。JavaVM提供虛擬機(jī)創(chuàng)建、銷毀等操作,Java中一個(gè)進(jìn)程可以創(chuàng)建多個(gè)虛擬機(jī),但是Android一個(gè)進(jìn)程只能有一個(gè)虛擬機(jī)。JNIENV是線程相關(guān)的,對(duì)應(yīng)的是JavaVM中的當(dāng)前線程的JNI環(huán)境,只有附加(attach)到JavaVM的線程才有JNIENV指針,通過JNIEVN指針可以獲取JNI功能,否則不能夠調(diào)用JNI函數(shù)。
C/C++要訪問的Java代碼,必須要能訪問到Java虛擬機(jī),獲取虛擬機(jī)有2中方法:
在加載動(dòng)態(tài)鏈接庫的時(shí)候,JVM會(huì)調(diào)用JNI_OnLoad(JavaVM* jvm, void* reserved),第一個(gè)參數(shù)會(huì)傳入JavaVM指針。
在C/C++中調(diào)用JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args)創(chuàng)建JavaVM指針
所以,我們只需要在編寫C/C++橋接器so的時(shí)候定義JNI_OnLoad(JavaVM* jvm, void* reserved)方法即可,然后把JavaVM指針保存起來作為上下文使用。
獲取到JavaVM之后,還不能直接拿到JNI函數(shù)去獲取Java代碼,必須通過線程關(guān)聯(lián)的JNIENV指針去獲取。所以,作為一個(gè)好的開發(fā)習(xí)慣在每次獲取一個(gè)線程的JNI相關(guān)功能時(shí),先調(diào)用AttachCurrentThread;又或者每次通過JavaVM指針獲取當(dāng)前的JNIENV:java_vm->GetEnv((void**)&jni_env,?version),一定是已經(jīng)附加到JavaVM的線程。通過JNIENV可以獲取到Java的代碼,例如你想在本地代碼中訪問一個(gè)對(duì)象的字段(field),你可以像下面這樣做:
1.對(duì)于類,使用jni_env->FindClass獲得類對(duì)象的引用
2.對(duì)于字段,使用jni_env->GetFieldId獲得字段ID
3.使用對(duì)應(yīng)的方法(例如jni_env->GetIntField)獲取字段的值
類似地,要調(diào)用一個(gè)方法,你step1.得獲得一個(gè)類對(duì)象的引用obj,step2.是方法methodID。這些ID通常是指向運(yùn)行時(shí)內(nèi)部數(shù)據(jù)結(jié)構(gòu)。查找到它們需要些字符串比較,但一旦你實(shí)際去執(zhí)行它們獲得字段或者做方法調(diào)用是非常快的。step3.調(diào)用jni_env->CallVoidMethodV(obj,methodID,args)。
從上面的示例代碼,我們可以看出使用原始的JNI方式去與Android(Java)插件交互是多的繁瑣,要自己做太多的事情,并且為了性能需要自己考慮緩存查詢到的方法ID,字段ID等等。幸運(yùn)的是,Unity3D已經(jīng)為我們封裝好了這些,并且考慮了性能優(yōu)化。Unity3D主要提供了一下2個(gè)級(jí)別的封裝來幫助高效編寫代碼:
注:Unity3D中對(duì)應(yīng)的C/C++橋接器包含在libunity.so中。
Level 1:AndroidJNI、AndroidJNIHelper,原始的封裝相當(dāng)于我們上面自己編寫的C# Wrapper。AndroidJNIHelper和AndroidJNI自動(dòng)完成了很多任務(wù)(指找到類定義,構(gòu)造方法等),并且使用緩存使調(diào)用java速度更快。AndroidJavaObject和AndroidJavaClass基于AndroidJNIHelper和AndroidJNI創(chuàng)建,但在處理自動(dòng)完成部分也有很多自己的邏輯,這些類也有靜態(tài)的版本,用來訪問java類的靜態(tài)成員。
Level 2:AndroidJavaObject、AndroidJavaClass、AndroidJavaProxy,這個(gè)3個(gè)類是基于Level1的封裝提供了更高層級(jí)的封裝使用起來更簡單,這個(gè)在第三部分詳細(xì)介紹。
2.3iOS插件
iOS編寫插件比Android要簡單很多,因?yàn)镺bjective-C也是 C-compatible的,完全兼容標(biāo)準(zhǔn)C語言。這些就可以非常簡單的包一層 extern "c"{},用C語言封裝調(diào)用iOS功能,暴露給Unity3D調(diào)用。并且可以跟原生C/C++庫一樣編成.a插件。C#與iOS(Objective-C)通信的原理跟C/C++完全一樣:
除此之外,Unity iOS支持插件自動(dòng)集成方式。所有位于Asset/Plugings/iOS文件夾中后綴名為.m , .mm , .c , .cpp的文件都將自動(dòng)并入到已生成的Xcode項(xiàng)目中。然而,最終編進(jìn)執(zhí)行文件中。后綴為.h的文件不能被包含在Xcode的項(xiàng)目樹中,但他們將出現(xiàn)在目標(biāo)文件系統(tǒng)中,從而使.m/.mm/.c/.cpp文件編譯。這樣編寫iOS插件,除了需要對(duì)iOS Objective-C有一定了解之外,與C/C++插件沒有差異,反而更簡單。
3、版本與補(bǔ)丁
任何游戲(端游、手游)都應(yīng)該提供游戲內(nèi)更新的途徑。一般游戲分為全量更新/整包更新、增量更新、資源更新。
全量
android游戲內(nèi)完整安裝包下載(ios跳轉(zhuǎn)到AppStore下載)
增量:主要指android省流量更新
可以使用bsdiff生成patch包
應(yīng)用寶也提供增量更新sdk可供接入
資源
Unity3D通過使用AssetBundle即可實(shí)現(xiàn)動(dòng)態(tài)更新資源的功能。
手游在實(shí)現(xiàn)這塊時(shí)需要注意的幾點(diǎn):
1.游戲發(fā)布出一定要提供游戲內(nèi)更新的途徑。即使是刪掉測試,保不準(zhǔn)這期間需要進(jìn)行資源或者BUG修復(fù)更新。很多玩家并不知道如何更新,而且Android手機(jī)應(yīng)用分發(fā)平臺(tái)多樣,分發(fā)平臺(tái)本身也不會(huì)跟官方同步更新(特別是小的分發(fā)平臺(tái))。
2.更新功能要提供強(qiáng)制更新、非強(qiáng)制更新配置化選項(xiàng),并指定哪些版本可以不強(qiáng)更,哪些版本必須強(qiáng)更。
3.當(dāng)游戲提供非強(qiáng)制更新功能之后,現(xiàn)網(wǎng)一定會(huì)存在多個(gè)版本。如果需要針對(duì)不同版本做不同的更新,例如配置文件A針對(duì)1.0.0.1修改了一項(xiàng),針對(duì)1.0.0.2修改了另一項(xiàng),2個(gè)版本需要分別更新對(duì)應(yīng)的修改,需要自己實(shí)現(xiàn)更新策略IIPS不提供這個(gè)功能。當(dāng)需要復(fù)雜的更新策略,推薦自己編寫更新服務(wù)器和客戶端邏輯,不使用iips組件(其實(shí)自己實(shí)現(xiàn)也很簡單)。
沒有運(yùn)營經(jīng)驗(yàn)的人會(huì)選擇二進(jìn)制,認(rèn)為二進(jìn)制安全、更小,這對(duì)端游/手游外網(wǎng)只存在一個(gè)版本的游戲適合,對(duì)一般不強(qiáng)升版本的手游并不適合,反而會(huì)對(duì)更新和維護(hù)帶來很大的麻煩。
4.配置使用XML或者JSON等文本格式,更利于多版本的兼容和更新。最開始騰訊桌球客戶端使用的二進(jìn)制格式(由excel轉(zhuǎn)換而來),但是隨著運(yùn)營配置格式需要增加字段,這樣老版本程序就解析不了新的二進(jìn)制數(shù)據(jù),給兼容和更新帶來了很大的麻煩。這樣就要求上面提到的針對(duì)不同步做不同更新,又或者配置一開始就預(yù)留好足夠的擴(kuò)展項(xiàng),其實(shí)不管怎么預(yù)留擴(kuò)展也很難跟上需求的變化,而且一開始會(huì)把配置表復(fù)雜化但是其實(shí)只有一張或者幾張才會(huì)變更結(jié)構(gòu)。
5.iOS版本的送審版本需要連接特定的包含新內(nèi)容的服務(wù)器,現(xiàn)網(wǎng)服務(wù)器還不包含新內(nèi)容。送審?fù)ㄟ^之后,上架游戲現(xiàn)網(wǎng)服務(wù)器會(huì)進(jìn)行更新,iOS版本需要連接現(xiàn)網(wǎng)服務(wù)器而非送審服務(wù)器,但是這期間又不能修改客戶度,這個(gè)切換需要通過服務(wù)器下發(fā)開關(guān)進(jìn)行控制。例如通過指定送審的iOS游戲版本號(hào),客戶端判斷本地版本號(hào)是否為送審版本,如果是連接送審服務(wù)器,否則連接現(xiàn)網(wǎng)服務(wù)器。
4、用腳本,還是不用?這是一個(gè)問題
方便更新,減少Crash(特別是使用C++的cocos引擎)
通過上面一節(jié)【版本與補(bǔ)丁】知道要實(shí)現(xiàn)代碼更新是非常困難的,正式這個(gè)原因客戶端開發(fā)的壓力是比較大的,如果出現(xiàn)了比較嚴(yán)重的BUG必須發(fā)強(qiáng)制更新版本,使用腳本可以解決這個(gè)問題。
由于Unity3D手游更新成本比較大,而且目前騰訊桌球要求不能強(qiáng)制更新,這導(dǎo)致新版本的活動(dòng)覆蓋率提升比較慢、出現(xiàn)問題之后難以修復(fù)。針對(duì)這個(gè)情況,考慮引入lua進(jìn)行活動(dòng)開發(fā),后續(xù)發(fā)布活動(dòng)及修復(fù)bug只需要發(fā)布lua資源,進(jìn)行資源更新即可,大大降低了發(fā)布和修復(fù)問題的成本。
可選方案還有使用Html5進(jìn)行活動(dòng)開發(fā),目前游戲中已經(jīng)預(yù)埋了Html5活動(dòng)入口,并且已經(jīng)用來發(fā)過"玩家調(diào)查"、"騰訊棋牌宣傳"等。但是與lua對(duì)比,不能做到與Unity3D的深度融合,體驗(yàn)不如使用lua,例如不能操作游戲中的ui、不能完成復(fù)雜界面的制作、不能復(fù)用已有的功能、玩家付費(fèi)充值跟已有的也會(huì)有差異
游戲腳本之王——Lua
在公司內(nèi)部魔方比較喜歡用lua,火隱忍者(手游)unity+ulua,全民水滸cocos2d-x+lua等等都有使用lua進(jìn)行開發(fā)。我們可以使用公司內(nèi)部的xlua組件,也可以使用ulua、UniLua等等。
5、資源管理
5.1資源管理器
業(yè)務(wù)不要直接使用引擎或者系統(tǒng)原生接口,而是封裝一個(gè)資源管理器負(fù)責(zé):資源加載、卸載
兼容Resource.Load與AssetBundle資源互相變更需求,開發(fā)期間使用Resource.Load方式而不必打AB包效率更高
加載資源時(shí),不管是同步加載還是異步加載,最好是使用異步編碼方式(回調(diào)函數(shù)或者消息通知機(jī)制)。如果哪一天資源由本地加載改為從服務(wù)器按需加載,而游戲中的邏輯都是同步方式編碼的,改起來將非常痛苦。其實(shí)異步編碼方式很簡單,不比同步方式復(fù)雜。
5.2資源類型
圖片/紋理(對(duì)性能、包體影響最大因素)
音頻
背景音樂,騰訊桌球使用.ogg/.mp3
音效,騰訊桌球使用.wav
數(shù)據(jù)
動(dòng)畫/特效
5.3圖片-文件格式與紋理格式
常用的圖像文件格式有BMP,TGA,JPG,GIF,PNG等;
常用的紋理格式有R5G6B5,A4R4G4B4,A1R5G5B5,R8G8B8, A8R8G8B8等。
文件格式是圖像為了存儲(chǔ)信息而使用的對(duì)信息的特殊編碼方式,它存儲(chǔ)在磁盤中,或者內(nèi)存中,但是并不能被GPU所識(shí)別,因?yàn)橐韵蛄坑?jì)算見長的GPU對(duì)于這些復(fù)雜的計(jì)算無能為力。這些文件格式當(dāng)被游戲讀入后,還是需要經(jīng)過CPU解壓成R5G6B5,A4R4G4B4,A1R5G5B5,R8G8B8, A8R8G8B8等像素格式,再傳送到GPU端進(jìn)行使用。
紋理格式是能被GPU所識(shí)別的像素格式,能被快速尋址并采樣。舉個(gè)例子,DDS文件是游戲開發(fā)中常用的文件格式,它內(nèi)部可以包含A4R4G4B4的紋理格式,也可以包含A8R8G8B8的紋理格式,甚至可以包含DXT1的紋理格式。在這里DDS文件有點(diǎn)容器的意味。OpenGL ES 2.0支持以上提到的R5G6B5,A4R4G4B4,A1R5G5B5,R8G8B8,A8R8G8B8等紋理格式,其中 R5G6B5,A4R4G4B4,A1R5G5B5每個(gè)像素占用2個(gè)字節(jié)(BYTE),R8G8B8每個(gè)像素占用3個(gè)字節(jié),A8R8G8B8每個(gè)像素占用 4個(gè)字節(jié)。
基于OpenGL ES的壓縮紋理有常見的如下幾種實(shí)現(xiàn):
1)ETC1(Ericsson texture compression),ETC1格式是OpenGL ES圖形標(biāo)準(zhǔn)的一部分,并且被所有的Android設(shè)備所支持。
2)PVRTC (PowerVR texture compression),支持的GPU為Imagination Technologies的PowerVR SGX系列。
3)ATITC (ATI texture compression),支持的GPU為Qualcomm的Adreno系列。
4)S3TC (S3 texture compression),也被稱為DXTC,在PC上廣泛被使用,但是在移動(dòng)設(shè)備上還是屬于新鮮事物。支持的GPU為NVIDIA Tegra系列。
5.4資源工具
有了規(guī)范就可以做工具檢查,從源頭到打包
6、性能優(yōu)化
掉幀主要針對(duì)GPU和CPU做分析;內(nèi)存占用大主要針對(duì)美術(shù)資源,音效,配置表,緩存等分析;卡頓也需要對(duì)GPU和CPU峰值分析,另外IO或者GC也易導(dǎo)致。
6.1工欲善其事,必先利其器
Unity Profiler
XCode?instruments
Qualcomm?Adreno Profiler
NVIDIA PerfHUD ES Tegra
6.2CPU:最佳原則減少計(jì)算
復(fù)用,UIScrollView Item復(fù)用,避免頻繁創(chuàng)建銷毀對(duì)象
緩存,例如Transform
運(yùn)算裁剪,例如碰撞檢測裁剪
粗略碰撞檢測(劃分空間——二分/四叉樹/八叉樹/網(wǎng)格等,降低碰撞檢測的數(shù)量)
精確碰撞檢測(檢查候選碰撞結(jié)果,進(jìn)而確定對(duì)象是否真實(shí)發(fā)生碰撞)
休眠機(jī)制:避免模擬靜止的球
邏輯幀與渲染幀分離
分幀處理
異步/多線程處理
6.3GPU:最佳原則減少渲染
紋理壓縮
批處理減少DrawCall(unity-Static Batching和Dynamic Batching,cocos SpriteBatchNode)
減少無效/不必要繪制:屏幕外的裁剪,F(xiàn)lash臟矩陣算法,
LOD/特效分檔
NGUI動(dòng)靜分離(UIPanel.LateUpdate的消耗)
控制角色骨骼數(shù)、模型面數(shù)/頂點(diǎn)數(shù)
降幀,并非所有場景都需要60幀(騰訊桌球游戲場景60幀,其他場景30幀;天天酷跑,在開始游戲前,F(xiàn)PS被限制為30,游戲開始之后FPS才為60。天天飛車的FPS為30,但是當(dāng)用戶一段時(shí)間不點(diǎn)擊界面后,F(xiàn)PS自動(dòng)降)
6.4內(nèi)存:最佳原則減少內(nèi)存分配/碎片、及時(shí)釋放
紋理壓縮-Android ETC1、iOS PVRTC 4bpp、windows DXT5
對(duì)象池-PoolManager
合并空閑圖集
UI九宮格
刪除不用的腳本(也會(huì)占用內(nèi)存)
6.5IO:最佳原則減少/異步io
資源異步/多線程加載
預(yù)加載
文件壓縮
合理規(guī)劃資源合并打包,并非texturepacker打包成大圖集一定好,會(huì)增加文件io時(shí)間
6.6網(wǎng)絡(luò):其實(shí)也是IO的一種
使用單線程——共用UI線程,通過事件/UI循環(huán)驅(qū)動(dòng);還是多線程——單獨(dú)的網(wǎng)絡(luò)線程?
單線程:由游戲循環(huán)(事件)驅(qū)動(dòng),單線程模式比使用多線程模式開發(fā)、維護(hù)簡單很多,但是性能比多線程要差一些,所以在網(wǎng)絡(luò)IO的時(shí)候,需要注意別阻塞到游戲循環(huán)。說明,如果網(wǎng)絡(luò)IO不復(fù)雜的情況下,推薦使用該模式。
在UI線程中,別調(diào)用可能阻塞的網(wǎng)絡(luò)函數(shù),優(yōu)先考慮非阻塞IO
這是網(wǎng)絡(luò)開發(fā)者經(jīng)常犯的錯(cuò)誤之一。比如:做一個(gè)簡單如 gethostbyname 的調(diào)用,這個(gè)操作在小范圍中不會(huì)存在任何問題,但是在有些情況中現(xiàn)實(shí)世界的玩家卻會(huì)因此阻塞數(shù)分鐘之久!如果你在 GUI 線程中調(diào)用這樣一個(gè)函數(shù),對(duì)于用戶來說,在函數(shù)阻塞時(shí),GUI 一直都處于 frozen 或者 hanged 狀態(tài),這從用戶體驗(yàn)的角度是絕對(duì)不允許的。
多線程:單獨(dú)的網(wǎng)絡(luò)線程,使用獨(dú)立的網(wǎng)絡(luò)線程有一個(gè)非常明顯的好處,主線程可以將臟活、累活交給網(wǎng)絡(luò)線程做使得UI更流暢,例如消息的編解碼、加解密工作,這些都是非常耗時(shí)的。但是使用多線程,給開發(fā)和維護(hù)帶來一定成本,并且如果沒有一定的經(jīng)驗(yàn)寫出來的網(wǎng)絡(luò)庫不那么穩(wěn)定,容易出錯(cuò),甚至導(dǎo)致游戲崩潰。下面是幾點(diǎn)注意事項(xiàng):
千萬千萬別在網(wǎng)絡(luò)線程中,回調(diào)主線程(UI線程)的回調(diào)函數(shù)。而是網(wǎng)絡(luò)線程將數(shù)據(jù)準(zhǔn)備好,讓主線程主動(dòng)去取,亦或者說網(wǎng)絡(luò)線程將網(wǎng)絡(luò)數(shù)據(jù)作為一個(gè)事件驅(qū)動(dòng)主線程去取。當(dāng)年我在用Cocos2d-x + Lua做魔法花園的手機(jī)demo時(shí),就采用的多線程模式,最初在網(wǎng)絡(luò)線程直接調(diào)用主線程回調(diào)函數(shù),經(jīng)常會(huì)導(dǎo)致莫名其妙的Crash。因?yàn)榫W(wǎng)絡(luò)線程中沒有渲染所必須的opengl上下文,會(huì)導(dǎo)致渲染出問題而Crash。
6.7包大小
使用壓縮格式的紋理/音頻
盡量不要使用System.Xml而使用較小的Mono.Xml
啟用Stripping來減小庫的大小
Unity strip level(strip by byte code)
Unity3D輸出APK,取消X86架構(gòu)
iOS?Xcode?strip開啟
6.8耗電
下面影響耗電的幾個(gè)因素和影響度摘自公司內(nèi)部的一篇文章。
7、異常與Crash
7.1防御式編程
非法的輸入中保護(hù)你的程序
斷言
防不勝防,不管如何防御總有失手的時(shí)候,這就需要異常捕獲和上報(bào)。
7.2異常捕獲
異常捕獲已經(jīng)有很多第三組件可供接入,這里不介紹組件的而接入,而是簡單談一下異常捕獲的原理。
由于很多錯(cuò)誤并不是發(fā)生在開發(fā)工作者調(diào)試階段,而是在用戶或測試工作者使用階段;這就需要相關(guān)代碼維護(hù)工作者對(duì)于程序異常捕獲收集現(xiàn)場信息。異常與Crash的監(jiān)控和上報(bào),這里不介紹Bugly的使用,按照apollo或者msdk的文檔接入即可,沒有太多可以說的。這里主要透過Bugly介紹手游的幾類異常的捕獲和分析:
Unity3D C#層異常捕獲:比較簡單使用Application.RegisterLogCallback/Application.RegisterLogCallbackThreaded(在一個(gè)新的線程中調(diào)用委托)注冊(cè)回調(diào)函數(shù)。特別注意:保證項(xiàng)目中只有一個(gè)Application.RegisterLogCallback注冊(cè)回調(diào),否則后面注冊(cè)的會(huì)覆蓋前面注冊(cè)的回調(diào)!回調(diào)函數(shù)中stackTrace參數(shù)包異常調(diào)用棧。
public void HandleLog(string logString, string stackTrace, LogType type)
{
if (logString == null || logString.StartsWith(cLogPrefix))
{
return;
}
ELogLevel level = ELogLevel.Verbose;
switch (type)
{
case LogType.Exception:
level = ELogLevel.Error;
break;
default:
return;
}
if (stackTrace != null)
{
Print(level, ELogTag.UnityLog, logString + "\n" + stackTrace);
}
Android Java層異常捕獲
try…catch顯式的捕獲異常一般是不引起游戲Crash的,它又稱為編譯時(shí)異常,即在編譯階段被處理的異常。編譯器會(huì)強(qiáng)制程序處理所有的Checked異常,因?yàn)镴ava認(rèn)為這類異常都是可以被處理(修復(fù))的。如果沒有try…catch這個(gè)異常,則編譯出錯(cuò),錯(cuò)誤提示類似于"Unhandled exception type?xxxxx"。
UnChecked異常又稱為運(yùn)行時(shí)異常,由于沒有相應(yīng)的try…catch處理該異常對(duì)象,所以Java運(yùn)行環(huán)境將會(huì)終止,程序?qū)⑼顺觯簿褪俏覀兯f的Crash。那為什么不會(huì)加在try…catch呢?
無法將所有的代碼都加上try…catch
UnChecked異常通常都是較為嚴(yán)重的異常,或者說已經(jīng)破壞了運(yùn)行環(huán)境的。比如內(nèi)存地址,即使我們try…catch住了,也不能明確知道如何處理該異常,才能保證程序接下來的運(yùn)行是正確的。
Uncaught異常會(huì)導(dǎo)致應(yīng)用程序崩潰。那么當(dāng)崩潰了,我們是否可以做些什么呢,就像Application.RegisterLogCallback注冊(cè)回調(diào)打印日志、上報(bào)服務(wù)器、彈窗提示用戶?Java提供了一個(gè)接口給我們,可以完成這些,這就是UncaughtExceptionHandler,該接口含有一個(gè)純虛函數(shù):
public abstract void uncaughtException (Thread thread, Throwableex)
Uncaught異常發(fā)生時(shí)會(huì)終止線程,此時(shí),系統(tǒng)便會(huì)通知UncaughtExceptionHandler,告訴它被終止的線程以及對(duì)應(yīng)的異常,然后便會(huì)調(diào)用uncaughtException函數(shù)。如果該handler沒有被顯式設(shè)置,則會(huì)調(diào)用對(duì)應(yīng)線程組的默認(rèn)handler。如果我們要捕獲該異常,必須實(shí)現(xiàn)我們自己的handler,并通過以下函數(shù)進(jìn)行設(shè)置:
public static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler handler)
特別注意:多次調(diào)用setDefaultUncaughtExceptionHandler設(shè)置handler,后面注冊(cè)的會(huì)覆蓋前面注冊(cè)的,以最后一次為準(zhǔn)。實(shí)現(xiàn)自定義的handler,只需要繼承UncaughtExceptionHandler該接口,并實(shí)現(xiàn)uncaughtException方法即可。
static class MyCrashHandler implements UncaughtExceptionHandler{
@Override
public void uncaughtException(Thread thread, final Throwable throwable) {
// Deal this exception
}
}
在任何線程中,都可以通過setDefaultUncaughtExceptionHandler來設(shè)置handler,但在Android應(yīng)用程序中,全局的Application和Activity、Service都同屬于UI主線程,線程名稱默認(rèn)為"main"。所以,在Application中應(yīng)該為UI主線程添加UncaughtExceptionHandler,這樣整個(gè)程序中的Activity、Service中出現(xiàn)的UncaughtException事件都可以被處理。
捕獲Exception之后,我們還需要知道崩潰堆棧的信息,這樣有助于我們分析崩潰的原因,查找代碼的Bug。異常對(duì)象的printStackTrace方法用于打印異常的堆棧信息,根據(jù)printStackTrace方法的輸出結(jié)果,我們可以找到異常的源頭,并跟蹤到異常一路觸發(fā)的過程。
public static String getStackTraceInfo(final Throwable throwable) {
String trace = ""; try {
Writer writer = new StringWriter;
PrintWriter pw = new PrintWriter(writer);
throwable.printStackTrace(pw);
trace = writer.toString;
pw.close;
} catch (Exception e) { return "";
} return trace;
}
Android Native Crash:前面我們知道可以編寫和使用C/C++原生插件,除非C++使用try...catch捕獲異常,否則一般會(huì)直接crash,通過捕獲信號(hào)進(jìn)行處理。
iOS 異常捕獲:
跟Android、Unity類似,iOS也提供NSSetUncaughtExceptionHandler?來做異常處理。
#import "CatchCrash.h"
@implementation CatchCrash
void uncaughtExceptionHandler(NSException *exception)
{
// 異常的堆棧信息
NSArray *stackArray = [exception callStackSymbols];
// 出現(xiàn)異常的原因
NSString *reason = [exception reason];
// 異常名稱
NSString *name = [exception name];
NSString *exceptionInfo = [NSString stringWithFormat:@"Exception reason:%@\nException name:%@\nException stack:%@",name, reason, stackArray];
NSLog(@"%@", exceptionInfo);
NSMutableArray *tmpArr = [NSMutableArray arrayWithArray:stackArray];
[tmpArr insertObject:reason atIndex:0];
[exceptionInfo writeToFile:[NSString stringWithFormat:@"%@/Documents/error.log",NSHomeDirectory()] atomically:YES encoding:NSUTF8StringEncoding error:nil];
}
@end
但是內(nèi)存訪問錯(cuò)誤、重復(fù)釋放等錯(cuò)誤引起崩潰就無能為力了,因?yàn)檫@種錯(cuò)誤它拋出的是信號(hào),所以還必須要專門做信號(hào)處理。
windows crash:同樣windows提供SetUnhandledExceptionFilter函數(shù),設(shè)置最高一級(jí)的異常處理函數(shù),當(dāng)程序出現(xiàn)任何未處理的異常,都會(huì)觸發(fā)你設(shè)置的函數(shù)里,然后在異常處理函數(shù)中獲取程序異常時(shí)的調(diào)用堆棧、內(nèi)存信息、線程信息等。
8、適配與兼容
8.1UI適配
錨點(diǎn)(UIAnchor、UIWidgetAnchor屬性)
NGUI UIRoot統(tǒng)一設(shè)置縮放比例
UIStretch
8.2兼容
shader兼容:例如if語句有的機(jī)型支持不好,Google nexus 6在shader中使用了if就會(huì)crash
字體兼容:android復(fù)雜的環(huán)境,有的手機(jī)廠商和rom會(huì)對(duì)字體進(jìn)行優(yōu)化,去掉android默認(rèn)字體,如果不打包字體會(huì)不現(xiàn)實(shí)中文字
9、調(diào)試及開發(fā)工具
9.1日志及跟蹤
事實(shí)證明,打印日志(printf調(diào)試法)是非常有效的方法。一個(gè)好用的日志調(diào)試,必備以下幾個(gè)功能:
日志面板/控制臺(tái),格式化輸出
冗長級(jí)別(verbosity level):ERROR、WARN、INFO、DEBUG
頻道(channel):按功能等進(jìn)行模塊劃分,如網(wǎng)絡(luò)頻道只接收/顯示網(wǎng)絡(luò)模塊的消息,頻道建議使用枚舉進(jìn)行命名。
日志同時(shí)會(huì)輸出到日志文件
日志上報(bào)
9.2調(diào)試用繪圖工具
調(diào)試?yán)L圖用工具指開發(fā)及調(diào)試期間為了可視化的繪圖用工具,如騰訊桌球開發(fā)調(diào)試時(shí)會(huì)使用VectrosityScripts可視化球桌的物理模型(實(shí)際碰撞線)幫助調(diào)試。這類工具可以節(jié)省大量時(shí)間及快速定位問題。通常調(diào)試用繪圖工具包含:
支持繪制基本圖形,如直線、球體、點(diǎn)、坐標(biāo)軸、包圍盒等
支持自定義配置,如顏色、粒度(線的粗細(xì)/球體半徑/點(diǎn)的大小)等
9.3游戲內(nèi)置菜單/作弊工具
在開發(fā)調(diào)試期間提供游戲進(jìn)行中的一些配置選項(xiàng)及作弊工具,以方便調(diào)試和提高效率。例如騰訊桌球游戲中提供:
游戲內(nèi)物理引擎參數(shù)調(diào)整菜單;
修改球桿瞄準(zhǔn)線長度/反射線數(shù)量、修改簽到獎(jiǎng)勵(lì)領(lǐng)取天數(shù)等作弊工具
注意游戲內(nèi)的所有開發(fā)調(diào)試用的工具,都需要通過編譯宏開關(guān),保證發(fā)布版本不會(huì)把工具代碼包含進(jìn)去。
9.4Unity擴(kuò)展
Untiy引擎提供了非常強(qiáng)大的編輯器擴(kuò)展功能,基于Unity Editor可以實(shí)現(xiàn)非常多的功能。公司內(nèi)部、外部都有非常的開源擴(kuò)展可用
公司外部,如GitHub上的:
UnityEditor-MiniExtension
Unity-Resource-Checker
UnityEditorHelper
MissingReferencesUnity
Unity3D-ExtendedEditor
…
公司內(nèi)部:
TUT、BeautyUnity、UnityDependencyBy
10、項(xiàng)目運(yùn)營
自動(dòng)構(gòu)建
版本號(hào)——主版本號(hào).特性版本號(hào).修正版本號(hào).構(gòu)建版本號(hào)
[構(gòu)建版本號(hào)]應(yīng)用分發(fā)平臺(tái)升級(jí)判斷基準(zhǔn)
自動(dòng)構(gòu)建
公司內(nèi)部接入SODA即可,建議搭建自己的構(gòu)建機(jī),開發(fā)期間每日N Build排隊(duì)會(huì)死人的,另外也可以搭建自己的搭建構(gòu)建平臺(tái)
統(tǒng)計(jì)上報(bào)
Tlog上報(bào)
玩家轉(zhuǎn)化關(guān)鍵步驟統(tǒng)計(jì)(重要)
Ping統(tǒng)計(jì)上報(bào)
游戲業(yè)務(wù)的統(tǒng)計(jì)上報(bào)(例如桌球球局相關(guān)的統(tǒng)計(jì)上報(bào))
燈塔自定義上報(bào)
運(yùn)營模板
上線前的checklist
項(xiàng)目 | 要點(diǎn) | 說明 | 指標(biāo) |
燈塔上報(bào) | 1. 燈塔自帶統(tǒng)計(jì)信息2. 自定義信息上報(bào) | 燈塔里面包含很多統(tǒng)計(jì)數(shù)據(jù),需要檢查是否ok | 1. 版本/渠道分布 2. 使用頻率統(tǒng)計(jì) 3. 留存統(tǒng)計(jì)(1天留存、3天留存、7天留存、14天留存) 4. 用戶結(jié)構(gòu)統(tǒng)計(jì)(有效用戶、沉默用戶、流失用戶、回流用戶、升級(jí)用戶、新增用戶) 5. 硬件統(tǒng)計(jì)(機(jī)型+版本、分辨率、操作系統(tǒng)、內(nèi)存、cpu、gpu) 6. Crash統(tǒng)計(jì)(Crash版本、Crash硬件、Crash次數(shù)等) 等等 |
信鴿推送 | 能夠針對(duì)單個(gè)玩家,所有玩家推送消息 | ||
米大師支付 | 正常支付 | ||
安全組件 | 1. TSS組件接入 2. 隱藏內(nèi)部符號(hào)表:C++開發(fā)的代碼使用strip編繹選項(xiàng),抹除程序的符號(hào) 3. 關(guān)鍵數(shù)據(jù)加密,如影子變量+異或加密算法 | 根據(jù)安全中心提供的文檔完成所有項(xiàng) | 接入安全組件,并通過安全中心的驗(yàn)收 |
穩(wěn)定性 | crash率 | 用戶crash率:發(fā)生CRASH的用戶數(shù)/使用用戶數(shù)啟動(dòng)crash率:啟動(dòng)5S內(nèi)發(fā)生crash用戶數(shù)/使用用戶數(shù) | 低于3% |
弱網(wǎng)絡(luò) | 斷線重連考慮,緩存消息,重發(fā)機(jī)制等等 | 客戶端的核心場景必須有斷線重連機(jī)制,并在有網(wǎng)絡(luò)抖動(dòng)、延時(shí)、丟包的網(wǎng)絡(luò)場景下,客戶端需達(dá)到以下要求: 一. 不能出現(xiàn)以下現(xiàn)象: 1、游戲中不能出現(xiàn)收支不等、客戶端卡死/崩潰等異常情況; 2、游戲核心功能(如登錄、單局、支付等)不能有導(dǎo)致游戲無法正常進(jìn)行的UI、交互問題; 3、不能有損害玩家利益或可被玩家額外獲利的問題; 4、需要有合理的重連機(jī)制,避免每次重連都返回到登錄界面。 二. 需要對(duì)延時(shí)的情況有相應(yīng)的提示 | |
兼容性 | 通過適配測試 | ||
游戲更新 | 1. 整包更新2. 增量更新 | 特別說明:iOS送審版本支持連特定環(huán)境,與正式環(huán)境區(qū)別開,需要通過服務(wù)器開關(guān)控制 | |
性能 | 內(nèi)存、CPU、幀率、流量、安裝包大小 | 【內(nèi)存占用要求】 Android平臺(tái):在對(duì)應(yīng)檔次客戶端最低配置以上,均需滿足以下內(nèi)存消耗指標(biāo)(PSS): 1檔機(jī)型指標(biāo):最高PSS<=300MB (PSS高于這個(gè)標(biāo)準(zhǔn)會(huì)影響28%用戶的體驗(yàn),約1800萬) 2檔機(jī)型指標(biāo):最高PSS<=200MB(PSS高于這個(gè)標(biāo)準(zhǔn)會(huì)影響45%用戶的體驗(yàn),約3000萬) 3檔機(jī)型指標(biāo):最高PSS<=150MB(PSS高于這個(gè)標(biāo)準(zhǔn)會(huì)影響27%用戶的體驗(yàn),約1800萬) iOS平臺(tái):在對(duì)應(yīng)檔次客戶端最低配置以上,均需滿足以下內(nèi)存消耗指標(biāo)(PSS): 1檔機(jī)型指標(biāo):消耗內(nèi)存(real mem)不大于250MB(高于這個(gè)標(biāo)準(zhǔn)會(huì)影響53%用戶的體驗(yàn),約1900萬) 2檔機(jī)型指標(biāo):消耗內(nèi)存(real mem)不大于200MB(高于這個(gè)標(biāo)準(zhǔn)會(huì)影響47%用戶的體驗(yàn),約1700萬) 【CPU占用要求】 Android平臺(tái):CPU占用(90%)小于60% iOS平臺(tái):CPU占用(90%)小于80% 【幀率要求】 1檔機(jī)型(CPU為四核1.4GHZ,RAM為2G)或以上機(jī)型:游戲核心玩法中,最小FPS應(yīng)不小于25幀/秒 2檔機(jī)型(CPU為兩核1.1GHZ,RAM為768M)或以上機(jī)型:游戲核心玩法中,最小FPS應(yīng)不小于25幀/秒 3檔機(jī)型(CPU為1GHZ,RAM為768M)或以上機(jī)型:游戲核心玩法中,最小FPS應(yīng)不小于18幀/秒 【流量消耗要求】 游戲核心玩法流量消耗情況(非一次性消耗)應(yīng)滿足以下條件: 1.對(duì)于分局的游戲場景,單局消耗流量不超過200KB 2.對(duì)于不分局游戲場景或流量與局時(shí)有關(guān)的場景,10分鐘消耗流量不超過500KB |
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。