• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            戰(zhàn)魂小筑

            討論群:309800774 知乎關(guān)注:http://zhihu.com/people/sunicdavy 開源項目:https://github.com/davyxu

               :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
              257 隨筆 :: 0 文章 :: 506 評論 :: 0 Trackbacks

            2018年11月30日 #

            近期在游戲demo中試驗ECS, 深入研究Unity官方的ECS框架和第三方Entitas框架, 分享下使用ECS的心得。


            Unity在2018版中加入了ECS系統(tǒng), 但處于小白鼠階段。默認不是Unity的一部分, 需要手動下載代碼并導入Packages(新特性)。官方提供海豚例子, 但除此之外例子和資料非常少。所以完全無法,也不敢在demo中貿(mào)然引入這種系統(tǒng),所以放棄官方ECS系統(tǒng)。


            第三方的Entitas(https://github.com/sschmid/Entitas-CSharp)ECS框架從2015年就開始在各地演講中介紹。整體框架基于代碼生成, 能解決一部分的代碼爆炸問題, 而且性能也要好一些。例子,介紹非常豐富,例子雖然基于不同版本的Entitas,特性支持和最新版差不太多, 只是寫法有細微差異, 對于理解來說無礙。


            經(jīng)過1~2天的改造, 終于將demo從傳統(tǒng)Unity寫法改造為ECS標準寫法,新增了46個文件, 而傳統(tǒng)邏輯一共只有16個文件,大概對比下ECS的特點和差異。


            Entitas的ECS系統(tǒng)

            1. 本來在一個對象中添加一個類字段的過程,ECS需要添加一個類代表Component,并且代碼生成。

            這個字段一般用于描述對象的資源,處理顯示的GameObject, 表示對象的類型等。


            2. 本來一個對象的業(yè)務邏輯處理過程直接用方法解決的, ECS需要新加一個System,而操作對象需要使用Filter或Group查詢獲得。


            3. 一系列的操作, 需要拆分為多個System和Component拆分處理。如果System順序不對, 會造成一些詭異的bug。


            4. Component不僅僅是Model承載體, 也可以是參數(shù)的數(shù)據(jù)結(jié)構(gòu)。參數(shù)Component通過Entity傳遞到System處理。 例如: 通過ECS創(chuàng)建一個方塊的過程,使用CreateTileComponent,包含創(chuàng)建Tile的位置, 創(chuàng)建Entity并添加CreateTileComponent, 在CreateTileSystem中處理就創(chuàng)建了Tile,處理完成時, 需要將傳入的Entity.Destroy掉。


            6. Entity上修改Component的過程, 會觸發(fā)事件。修改的過程需要使用RelaceXXX,XXX表示組件名。組件可以頻繁修改, 不用擔心添加和刪除組件過程的性能, Entitas底層處理性能只相當于指針賦值的性能。


            ECS像什么?

            1. ECS中的System類似觸發(fā)器系統(tǒng)(Event-Condition-Action),其中,Event對應Entitas的GetTrigger+Collector,表示觸發(fā)事件。Condition對應Filter表示在事件來源對象中找到需要的對象。 Action對應Execute,表示實際的操作。

            2. ECS中的Component類似不用lua擴展的Redis或者不用存儲過程的MySQL, 純粹純數(shù)據(jù), 而不能對數(shù)據(jù)有任何封裝操作。沒有l(wèi)ua和存儲過程支持的db寫起來還是比較費勁的,但ECS就是那么的純。


            3. ECS中的Entity很尷尬,因為Component是按類別連續(xù)存儲的以保證性能。 邏輯又需要Entity組合成邏輯需要的復合對象。 兩邊都要照顧,所以這種設(shè)計就讓代碼量巨增,可讀性下降。



            ECS企圖用一套框架滅掉設(shè)計模式

            1. 單件(Singleton)在Entitas用Unique標簽標記Component, 在Context中就是唯一的, 其實也就是Singleton。


            2. ECS干掉了傳統(tǒng)的工廠模式,底層統(tǒng)一對對象(Entity)和屬性(Component)統(tǒng)一管理。需要按Component中的值找回Entity時, 可以使用EntityIndex。


            3. Entity攜帶不同的組件時,整個創(chuàng)建和銷毀過程被記錄并恢復,其實就是Command模式


            ECS適合做UI框架(類似MVVM,MVC,MVP)么?

            ECS不是專用的UI框架,但是可以對不同系統(tǒng)和數(shù)據(jù)間解耦。傳統(tǒng)代碼中數(shù)據(jù)修改后的Callback,ECS也可以用Listener做, 但Listener因為能保存數(shù)據(jù), 就需要用Component保存。 所以你需要面對的是,一個Button,響應創(chuàng)建一個參數(shù)用的Component和System,還要為數(shù)據(jù)改變寫一套ListenComponent和Listener處理的System,酸爽吧?


            Minecraft適合ECS來做么?

            可以,性能應該能提升不少,但是代碼會更繁瑣,特別像Java這種啰嗦語言配上ECS這種啰嗦框架,估計代碼量翻翻還是很輕松的。MC屬于特殊類型的游戲,適合特殊領(lǐng)域特別優(yōu)化,也就是專門為方塊做出特別的設(shè)計來做優(yōu)化。ECS屬于通用框架,即便性能OK,但是代碼未必能有良好的可讀性。


            體量小的游戲適合用ECS來做么?

            可以,但不建議。特別是只有幾個人維護的工程,貿(mào)然上ECS系統(tǒng),會讓系統(tǒng)變的極為復雜。當然你會說,如果開發(fā)到后期,傳統(tǒng)開發(fā)模式會導致代碼會亂,ECS會好些吧。掌握ECS也不是一天兩天的事情,不熟悉ECS的程序員設(shè)計出來的系統(tǒng)獲得的優(yōu)勢可能還不如用傳統(tǒng)設(shè)計方法好呢。

            架構(gòu)解決的是人的問題, 人都有問題,用什么框架都沒辦法。


            到底什么項目適合用ECS?

            1. 大量的小個體不斷的生成和銷毀以及顯示,例如: 攻城戰(zhàn)中,要體現(xiàn)每個角色的移動,戰(zhàn)斗。

            2. 多于5個人編寫核心戰(zhàn)斗邏輯。互相協(xié)作和模塊切分,需要一個大家都能信服的框架,ECS可以選擇。


            P.S.

            不要造ECS的輪子!

            很多同學看了ECS基本原理,在沒有深入使用過任何ECS系統(tǒng)時馬上操刀造輪子。ECS系統(tǒng)確實看起來簡單。實際造下來你會發(fā)現(xiàn),性能非常糟糕以及不知道一些邏輯如何用ECS來解決。


            總結(jié):

            1. ECS確實為性能而生,沒有并發(fā)加持性能的ECS都是耍流氓,要快就要快到極致。

            2. Unity中,ECS并發(fā)能擴展CPU的利用率,但是GPU的性能依然還是DrawCall優(yōu)化那一套,別期望ECS會顛覆Unity,性能也不會快到飛起,關(guān)鍵還是要看具體的項目和人。

            3. ECS是萬能框架,但不全能。傳統(tǒng)架構(gòu)和設(shè)計思想也不是一無是處,熟啥用啥,怎么快怎么來!

            無恥的廣告鏈接,請各位支持

            《Go語言從入門到進階實戰(zhàn)(視頻教學版)》(徐波)【摘要 書評 試讀】- 京東圖書

            posted @ 2018-11-30 18:01 戰(zhàn)魂小筑 閱讀(3290) | 評論 (0)編輯 收藏

            2018年8月29日 #

            為了編寫基于cellnet的新一代游戲服務器框架,最近深入研究微服務,ServiceMesh等概念。研究過程中對Web和游戲兩種服務器架構(gòu)設(shè)計有一些心得,編寫并記錄下來。(下文中,Game表示游戲服務器,Web表示W(wǎng)eb服務器) ``
            狀態(tài)緩存
            所謂狀態(tài)緩存,就是在內(nèi)存而非專業(yè)數(shù)據(jù)緩存服務器(如redis)中保存和處理邏輯數(shù)據(jù),手動編寫此過程較為繁瑣但是效率較高,但隨著狀態(tài)邏輯復雜性和并發(fā)、擴容問題提出,狀態(tài)同步會變得越來越復雜。
            Game:
            強交互性的服務器類型需要在服務器做緩存,邏輯編寫也較為容易,無需處理事務并發(fā)問題。例如:組隊,匹配,戰(zhàn)斗邏輯。服務器不能隨意重啟。
            弱交互性的服務器類型可配合redis做成無狀態(tài)服務器,例如:養(yǎng)成,技能升級,領(lǐng)取物品等。服務器隨時支持重啟。
            游戲服務器為了提高性能,早期所有服務器都是使用狀態(tài)緩存寫法編寫,特別是MMORPG這類強交互的游戲服務器尤為嚴重。
            Web:
            均為無狀態(tài)服務器,弱交互。使用事務方式處理并發(fā)邏輯,例如:交易,下單等。
            推送,單獨發(fā)送
            這里提到的所謂推送,單獨發(fā)送是與RPC區(qū)別的通訊方法。RPC要求請求必須有回應。而推送單獨發(fā)送則更像是通知和廣播,無需目的方返回任何消息。
            Game:
            找到服務器的Session,直接Send
            通過中轉(zhuǎn)服務器,或稱為中心服務器進行注冊/廣播
            客戶端的model數(shù)據(jù)需要更新時,服務器會主動推送消息。
            游戲服務器沒有嚴格的RPC設(shè)計需求,推送和單獨發(fā)送較Web服務器更多。而且游戲服務器多使用長連接,所以主動推送也比Web服務器來的方便一些。
            Web:
            將推送做成專有的服務,并做排隊和并發(fā)處理。
            可用性
            聽說過游戲停服更新,支付寶服務器在刷二維碼時停服了可一定被罵慘吧。Web對服務器高可用性要求很高,游戲雖然也注重服務器穩(wěn)定性和可用性,但是由于版本迭代更新頻繁,停服更新反而能獲得玩家接受。
            Game:
            游戲?qū)捎眯砸蟛桓摺?/span>
            游戲大版本更新時需要停服更新。支持熱更新技術(shù)的服務器(例如Erlang,Skynet)僅使用熱更新修復bug,很少直接更新新版本。
            不是所有的游戲服務器支持動態(tài)添加服務器。
            Web:
            極高的可用性,服務不允許停服更新,使用藍綠及灰度方式更新服務器。
            隨時可以橫向擴展服務器,提高服務器容量和承載。
            連接及傳輸
            均使用TCP傳輸協(xié)議,游戲服務器注重性能,自有協(xié)議及二進制協(xié)議使用較多。
            Web注重兼容和接口友好,使用JSON格式較多。
            Game:
            使用長連接,需要從邏輯層維護連接狀態(tài)及處理服務器不在線情況
            使用自有封包格式,大部分使用protobuf或二進制流格式。
            Web:
            微服務大部分使用短連接,grpc支持http2長連接
            使用json編碼方便調(diào)試和版本兼容。
            流量限制
            人數(shù)多了,任何服務器都扛不住,流量限制和登入限制能有效保護服務器穩(wěn)定。
            Game:
            單服有人數(shù)限制,可以通過GM后臺設(shè)置擋墻,超過無法進入
            Web:
            限流器中間件,可以精確到服務控制流量
            斷流,防止雪崩
            Game:
            游戲沒有,也不需要這種概念,游戲請求不會突然升高,即便有,也通過GM后臺人為控制
            Web:
            斷流器中間件
            服務發(fā)現(xiàn)
            如何找到服務器地址。
            服務有變化時,通過Watch系統(tǒng)通知訂閱者更新本地緩存
            服務器沒有變化時,使用本地緩存找到服務地址
            Game:
            游戲服務器互相依賴復用只在很小的范圍內(nèi),因此無需在不同語言不同進程服務間獲得地址,大部分在配置文件中填寫各服務的IP及地址即可互相訪問。
            早期游戲自己編寫服務器狀態(tài)及地址發(fā)現(xiàn)服務。
            有用redis做服務發(fā)現(xiàn)
            Web:
            使用服務發(fā)現(xiàn)系統(tǒng),分布式部署。無需依賴配置文件
            網(wǎng)關(guān)需求
            Game:
            網(wǎng)關(guān)處理客戶端上下線通知,心跳,維持連接,轉(zhuǎn)發(fā),廣播上下行封包
            Web:
            根據(jù)請求地址路由,無上下線概念,無心跳。廣播通過消息推送系統(tǒng)完成
            由于筆者從事游戲行業(yè),對Web服務器概念在逐漸熟悉中,若有錯誤和不足請各位大佬指出。
            本人新書《Go語言從入門到進階實戰(zhàn)》,生動的語言,例子帶有各種彩蛋,輕松了解Go語言特性,更有cellnet框架剖析解密
            https://search.jd.com/Search?keyword=go%E8%AF%AD%E8%A8%80%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E8%BF%9B%E9%98%B6%E5%AE%9E%E6%88%98&enc=utf-8&suggest=1.def.0.V02&wq=Go%E8%AF%AD%E8%A8%80%E4%BB%8E&pvid=145d55a92cab4b07b71326f8beb1700b
            posted @ 2018-08-29 11:16 戰(zhàn)魂小筑 閱讀(2704) | 評論 (0)編輯 收藏

            2017年12月19日 #

            嵌套git庫的管理
            使用git作為源代碼管理時,經(jīng)常需要在一個git代碼庫中從外網(wǎng)獲取新的git庫,也就是在git庫下嵌套另外一個git庫。而同時維護兩個git庫的過程就變的非常復雜。
            submodule的弊端
            常見的做法是使用git 提供的submodule功能。但submodule的管理嵌套git庫的結(jié)果往往不是我們期望的結(jié)果。假設(shè)有一個git庫叫project,在project的某個子目錄下還包含一個叫3rd的目錄,是另外一個git庫。
            D:.
            └─project
            └─3rd
            mytext.txt
            假設(shè)甲和乙都取了project的代碼,由于時間差異,甲通過submodule取到V1版,而乙取到了V2版本。甲乙同時在不同的3rd庫下進行開發(fā),勢必會造成不同的運行結(jié)果。也許你認為可以在獲取submodule時指定版本,但這個獲取過程很難控制。
            一般說來,第三方庫應由主程序進行更新及維護,一般情況下,項目沒有特殊需求時,不會隨便更新第三方庫到最新版本,因此submodule更新嵌套的git庫并不是理想的解決方案。
            嵌套git庫的修改可見性
            前面的例子中,project git庫下的3rd的git庫中如果有文件發(fā)生修改,此時在project目錄下,使用sourceTree等git管理工具無法識別3rd下的文件修改。
            也就是說,無法將3rd下的修改提交到project的git庫中。
            終極解決方案
            此時,將3rd目錄下的.git目錄暫時移出project和3rd目錄,此時,在project目錄用sourceTree查看時,將可以看到3rd下的文件修改,提交修改到project庫中。再將剛移出去的3rd的.git目錄移回3rd目錄下,在3rd目錄下使用git status,將可看到3rd目錄的修改,提交時,會將修改提交到3rd目錄。
            posted @ 2017-12-19 22:04 戰(zhàn)魂小筑 閱讀(3545) | 評論 (0)編輯 收藏

            2017年11月27日 #

            越來越多的人選擇用Mac或者Linux環(huán)境進行跨平臺項目開發(fā)。但是仍然有大部分人習慣于在Windows環(huán)境下進行開發(fā),畢竟Windows在各方面使用還是較為方便,特別像文件版本管理(Git,SVN等)

            在跨平臺下開發(fā)游戲或軟件,就需要有一套方便的自動化工具。Windows下需要使用批處理,雖然有PowerShell加持,但這東西學了也不靠譜,只有一個平臺能用。大家還是習慣Linux Shell。連Mac平臺都可以用Shell,Windows上要做自動化腳本就顯得非常尷尬。

            我曾經(jīng)在項目中使用go編寫了一套將配置轉(zhuǎn)為批處理和Linux Shell的工具。使用過程較為復雜,但是能跨平臺進行表格導出和協(xié)議編譯等工作。

            但是,這個工具還是需要對不同的平臺編寫多套模板進行代碼生成,非常繁瑣。如果有一套跨平臺的Shell,編寫一次就可以跨平臺運行那該多好。

            查閱資料后,一共有幾個方案:

            1. 使用Python作為自動化工具 這個方案其實就是使用python把批處理和Shell干的事情用代碼來解決。但前提是要重新學習Python,也需要一部分熟悉簡單的Python語法,人為學習成本較高,也比較費事。

            2. 自己編寫一套獨立的自動化工具 這個方案需要長時間的適配過程,差什么指令補什么指令,對項目進度有一定干擾。

            3. 自己編寫Linux Shell的解釋器 這個就更難了,要做到100%兼容,基本不可能。

            1. 使用Cygwin和Mingw 需要一個微型運行時進行Linux Shell的解釋,msys大概是18M左右,可行性較高。

            在研究Cygwin和Mingw如何整合的過程中,我誤操作點擊了一個sh后綴的Linux Shell,這是我希望讓Mingw運行的Shell。結(jié)果呢,sh后綴的文件居然能在Windows下運行。我馬上編寫了一系列的例子,發(fā)現(xiàn)幾乎完全兼容常用的Shell指令。 經(jīng)過研究,我發(fā)現(xiàn)Windows下能運行sh文件是由Git自帶的msys2提供的。MSYS2 (Minimal SYStem 2, http://www.msys2.org/) 是一個MSYS的獨立改寫版本,主要用于 shell 命令行開發(fā)環(huán)境。同時它也是一個在Cygwin (POSIX 兼容性層) 和 MinGW-w64(從"MinGW-生成")基礎(chǔ)上產(chǎn)生的,追求更好的互操作性的 Windows 軟件。

            那就是說,只要安裝了Git(https://git-scm.com/),那么就可以在Windows下直接運行Linux Shell,只需要將文件后綴命名為sh即可

            問過周邊友人是否知道這一功能,都說知道,只是沒有廣播而已,害我研究很久……

            posted @ 2017-11-27 15:15 戰(zhàn)魂小筑 閱讀(5425) | 評論 (3)編輯 收藏

            2017年7月6日 #

            本文主要研究游戲服務器帶狀態(tài)的熱更新需求 http的無狀態(tài)熱更新需求已經(jīng)有成熟方案, 故不在本文描述范圍

            基本概念

            • Golang的熱更新采用什么機制?

              使用go1.8提供的plugin包機制實現(xiàn)

            • plugin包本身設(shè)計的目的是熱更新么?

              plugin包其實只是支持將代碼分別編譯為多個動態(tài)庫,動態(tài)加載后運行 并不能完全支持類似C/C++的動態(tài)庫方式處理代碼

            • 帶狀態(tài)的進程熱更新的基本概念及范圍是什么?

              數(shù)據(jù)部分(model)不更新, 只更新邏輯部分(函數(shù))

            • 表格和配置更新算熱更新么?

              算, 但不是本文描述范圍

            • 熱更新能在windows上使用么?

              不支持

            代碼及結(jié)構(gòu)

            • 我能將原來一個exe的代碼編譯為so提供給plugin使用么?

              可以, 但是必須仍然保留main包作為插件入口, 并在main包內(nèi)添加提供給plugin調(diào)用入口.

            • 如果動態(tài)庫中沒有main包, 編譯出的so能用么?

              不能, 包必須包含main, 否則輸出的是.a的文件, plugin包加載會報錯

            • 動態(tài)庫中, 非main包的的代碼修改能做熱更新么?

              不能!(崩潰了吧, 我提了一個issue: https://github.com/golang/go/issues/20554)

              如果確實做了修改, 底層會報錯: plugin was built with a different version of package

              解決方法: 修改plugin包底層實現(xiàn)并重新編譯 打開runtime/plugin.go, 注釋以下代碼 for _, pkghash := range md.pkghashes { if pkghash.linktimehash != *pkghash.runtimehash { return "", nil, pkghash.modulename } } 執(zhí)行/usr/local/go/run.bash 重編譯+測試

            • 代碼中哪些可以被更新? 方法可以被更新么? 閉包呢?

              只能更新?lián)碛徐o態(tài)地址的結(jié)構(gòu).例如: 包級別函數(shù)(類似于靜態(tài)函數(shù))

              例如: svc_0.so里有一個Foo函數(shù), svc_1.so修改了Foo函數(shù)實現(xiàn), 熱更新可實現(xiàn)

              閉包=函數(shù)+捕獲變量, 實際上是一個動態(tài)結(jié)構(gòu), 沒有靜態(tài)地址, 無法被更新

              各種包級別變量, 結(jié)構(gòu)體變量, 結(jié)構(gòu)體方法, 局部變量均不能被熱更新, 但是變量值不會被影響

              新增結(jié)構(gòu)可以被運行

            • 使用結(jié)構(gòu)體方法調(diào)用了包級別函數(shù), 包級別函數(shù)能被更新么?

              可以, 雖然方法不能被更新, 但方法被調(diào)用的包級別函數(shù)的地址是固定的, 所以可以被熱更新

            • init包初始化函數(shù)能用么? 能被熱更新么?

              官方這樣描述:

              When a plugin is first opened, the init functions of all packages not already part of the program are called. The main function is not run. A plugin is only initialized once, and cannot be closed

              插件第一次被打開時, 其關(guān)聯(lián)的, 沒有成為程序的一部分的所有的包的init函數(shù)將被調(diào)用. 插件的main函數(shù)不會被調(diào)用. 插件只會被初始化一次, 不能被關(guān)閉

              因此, 需要手動將init函數(shù)改成自己的函數(shù), 統(tǒng)一在so的main包里調(diào)用

            編譯

            • 如何編譯獲得plugin包支持的動態(tài)庫

              SVCNAME=$1 SVCVER=$2 TIMESTAMP=`date '+%Y%m%d_%H%M%S'` go build -v -buildmode=plugin --ldflags="-pluginpath=${SVCNAME}_${TIMESTAMP}" -o ${SVCNAME}_${SVCVER}.so ${SVCNAME}

              -buildmode=plugin是重要參數(shù)

              --ldflags里的-pluginpath的作用是: 每次編譯的內(nèi)部識別路徑都是不同的, 避免重復加載的警告

              參考: https://github.com/golang/go/issues/19004

            posted @ 2017-07-06 12:47 戰(zhàn)魂小筑 閱讀(8554) | 評論 (0)編輯 收藏

            2017年4月20日 #

            使用Visual Studio Code調(diào)試Golang工程

            關(guān)鍵字

            • 最簡單的調(diào)試攻略
            • 多項目調(diào)試, 適用個人開發(fā)和項目開發(fā)
            • 無需修改系統(tǒng)環(huán)境變量

            準備VSCode

            在官網(wǎng)下載最新版的VSCode:

            https://code.visualstudio.com/

            安裝Golang插件

            • 打開擴展面板

              VSCode->查看->擴展

            • 找到Go插件 在搜索框里輸入Go, 找到第二行寫有 Rich Go language support for Visual Studio Code的插件, 點擊安裝

              注意不是排名最高的

            • 重啟編輯器

            配置啟動項

            • 打開調(diào)試面板

              VSCode->查看->調(diào)試

            • 添加調(diào)試目標

              在"沒有調(diào)試"的下拉框中點擊"添加配置.."

            • 添加目標調(diào)試配置

              例子:

              {
                  "version": "0.2.0",
                  "configurations": [
                      {
                          "name": "Launch",
                          "type": "go",
                          "request": "launch",
                          "mode": "debug",
                          "remotePath": "",
                          "port": 2345,
                          "host": "127.0.0.1",
                          "program": "${fileDirname}",
                          "env": {
                              "GOPATH":"D:/Develop/vscodegolang"
                          },
                          "args": [],
                          "showLog": true
                      }
                  ]
              }

            其中: "port", "host"都是go插件自動生成的

            "env"為設(shè)置環(huán)境變量, 設(shè)置為你的工程目錄就可以(包含bin, src的文件夾)

            準備調(diào)試插件

            此時找到main.go按F5, 會報錯提示:

            Failded to continue:"Cannot find Delve debugger. Install from https://github.com/derekparker/delve & ensure it is in your "GOPATH/bin" or "PATH"

            我們使用go命令行編譯調(diào)試器

            go get github.com/derekparker/delve/cmd/dlv

            將dlv調(diào)試器放在GOPATH(工程目錄)的bin目錄下

            開始調(diào)試

            選中要調(diào)試的main.go, 點擊F5, 既可以開始調(diào)試

            調(diào)試快捷鍵和Visual Studio系一致

            • F9 切換斷點
            • F10 Step over
            • F11 Step in
            • Shift+F11 Step out

            注意點

            • 某些結(jié)構(gòu)體成員無法直接顯示時, 可以直接選中變量名, 添加到監(jiān)視, 或者右鍵點擊: "調(diào)試:求值"

            多項目調(diào)試

            在launch.json中可以添加多組調(diào)試入口, 通過調(diào)試面板中選中對應的配置開啟不同目標的調(diào)試

            {
                "version": "0.2.0",
                "configurations": [
                    {
                        "name": "client",
                        "type": "go",
                        "request": "launch",
                        "mode": "debug",
                        "remotePath": "",
                        "port": 2345,
                        "host": "127.0.0.1",
                        "program": "${fileDirname}",
                        "env": {
                            "GOPATH":"D:/Develop/vscodegolang"
                        },
                        "args": [],
                        "showLog": true
                    },
            
                    {
                        "name": "server",
                        "type": "go",
                        "request": "launch",
                        "mode": "debug",
                        "remotePath": "",
                        "port": 2345,
                        "host": "127.0.0.1",
                        "program": "${workspaceRoot}/src/server",
                        "env": {
                            "GOPATH":"D:/Develop/vscodegolang"
                        },
                        "args": [],
                        "showLog": true
                    }
                ]
            }

            "program"中的"${fileDirname}"是以當前選中文件作為啟動點

            更建議使用"program"的"${workspaceRoot}", 以包名作為啟動點的方式進行配置

            參考鏈接

            https://code.visualstudio.com/Docs/editor/debugging

            posted @ 2017-04-20 12:52 戰(zhàn)魂小筑 閱讀(8297) | 評論 (0)編輯 收藏

            2017年4月8日 #

            相位技術(shù)

            相位技術(shù)大規(guī)模出現(xiàn)在魔獸世界WLK版本, 現(xiàn)在應用已經(jīng)廣泛應用在各種MMORPG游戲中. 下面對相位技術(shù)的做法進行簡單歸納匯總

            表現(xiàn)分類

            副本相位

            早期副本的出現(xiàn), 避免搶怪問題. 所以, 副本其實本身就是一種相位技術(shù). 只不過實現(xiàn)時, 我們一般會將小隊和怪物直接預分配在獨立的一個副本實例中(所以副本原文也是實例的意思)

            分線相位

            相位技術(shù)還沒有正式命名時, 同一個場景, 玩家進到不同的分線看到的玩家不一樣, 也是屬于相位的一種. 當然, 如果是組隊玩家, 服務器默認會分配所有隊伍玩家在同一個線(位面)

            真相位

            副本相位和分線相位其實都是靜態(tài)相位, 一旦進入, 中途不會有切換或者混合查看的過程. 真相位可以在一個場景中,動態(tài)切換相位, 相位內(nèi)和相位外所以不同

            我們常見的真相位表現(xiàn)為: 相位中的角色+玩家+隊伍成員

            在護送任務時, 還會在上面所見角色中疊加世界中的所有角色, 也就是說, 你和隊伍成員可以看到的角色, 其他人看不到, 其他人也看不到你和你的隊伍成員


            為了清晰的簡單的描述, 我為相位創(chuàng)建如下概念與名詞

            相位客體

            表現(xiàn)為除玩家外的角色(怪物,交互物體與相位可見場景)

            私有客體

            這是最常見的一種相位內(nèi)角色

            • 持有變量

              取PhasingID時,為PhasingTargetID

            • 生成規(guī)則

              當玩家開啟相位后, 在玩家相位內(nèi)生成的角色為私有客體.

              此時, 將 PhasingTargetID設(shè)置為相位生成者的實例ID

            • 刪除規(guī)則

              如果玩家退出相位, 私有客體會存在一段時間或按照需求刪除

            公共客體

            一般指提前放置在場景中, 世界內(nèi)不可見, 但是能被同相位玩家可見,且同相位玩家都可以互相可見 比如: 只要接了同一個任務的玩家, 都可以看到的NPC

            • 持有變量

              取PhasingID時,為PublicPhasingID

            • 生成規(guī)則

              通過場景編輯器, 放置角色時, 設(shè)置其可被觀察到的任務ID

              角色被加載后, 將任務ID設(shè)置到StaticPhasingID

            • 刪除規(guī)則

              場景刪除, 角色刪除

            相位主體

            包含玩家與同隊伍玩家

            • 開啟相位后, 可見私有客體+公有客體

            • 隊長視為相位主體, 單人時, 自己為隊長

            • 隊伍其他成員共享隊長的私有相位客體

            • 隊伍其他成員根據(jù)自己的PublicPhasingID匹配目標對象的PublicPhasingID時可互相可見

            • 持有變量

            相位開啟時, 取PhasingID時, 為角色實例ID

            相位關(guān)閉時, 取PhasingID時, 為0

            PublicPhasingID

            可見規(guī)則

            當兩個角色的PhasingID相等時, 主體與私有客體互相可見

            當兩個角色的PublicPhasingID相等時, 主體與公有客體互相可見

            可以通過開關(guān)設(shè)置, 是否在可見的相位客體基礎(chǔ)上, 疊加世界角色(護送任務)

            約束

            • 玩家同時只能看見1個相位
            posted @ 2017-04-08 14:41 戰(zhàn)魂小筑 閱讀(20386) | 評論 (0)編輯 收藏

            2016年12月1日 #

            Golang中沒有設(shè)計構(gòu)造函數(shù). 取而代之的, 設(shè)計Golang的大師希望你用普通函數(shù)去實現(xiàn)構(gòu)造的任務. 
            一直只是覺得這只是體現(xiàn)Golang這門新語言的精簡設(shè)計之道, 直到自己實現(xiàn)編譯器后才發(fā)現(xiàn)構(gòu)造函數(shù)的設(shè)計本身是值得商榷的

            我們先看下構(gòu)造函數(shù)的規(guī)則

            構(gòu)造函數(shù)調(diào)用規(guī)則

            構(gòu)造參數(shù)量: 0表示沒有構(gòu)造函數(shù), 1表示有構(gòu)造函數(shù)0個參數(shù)

            本類構(gòu)造父類構(gòu)造處理方法
            00不處理
            10調(diào)本類ctor
            01調(diào)父類ctor
            11調(diào)本類ctor, 本類ctor調(diào)父類ctor
            21調(diào)本類ctor, 本類ctor調(diào)父類ctor
            12報錯, 手動調(diào)父類ctor
            22報錯, 手動調(diào)父類ctor

            普通函數(shù)重載規(guī)則

            實際只用考慮最典型的一種行為: 實例化子類, 轉(zhuǎn)為父類調(diào)用方法, 這個時候

            如果方法是override, 調(diào)用的是子類

            如果方法是virutal或者不指明, 調(diào)用的是父類

            整個重載過程, 子類絕對不會隱式調(diào)用父類的行為

            需要構(gòu)造函數(shù)么?

            構(gòu)造函數(shù)的優(yōu)點

            • 本身屬于一種特殊的成員函數(shù)
            • 編譯器幫你自動傳導調(diào)用父級

            構(gòu)造函數(shù)的缺點

            • 隱式的調(diào)用規(guī)則
            • 雖然屬于成員函數(shù), 但是與其他成員函數(shù)調(diào)用規(guī)則完全不同, 需要特殊記憶
            • 帶參數(shù)的構(gòu)造函數(shù), 在父類參數(shù)多于子類時, 需要引用復雜語法來實現(xiàn)父級構(gòu)造調(diào)用

            其實我們對初始化函數(shù)的需求只有1條: 自定義

            所以, 可以理解Golang不加入構(gòu)造函數(shù)的設(shè)計是正確的 
            即: 簡單, 清晰, 有規(guī)律

            posted @ 2016-12-01 10:45 戰(zhàn)魂小筑 閱讀(3348) | 評論 (0)編輯 收藏

            2016年11月2日 #

            append, map, len不是關(guān)鍵字

            他們其實還是類庫功能, 都在buildin包里的, 系統(tǒng)默認給你做了個

            1. import(
            2. . "buildin"
            3. )

            將buildin的包內(nèi)容都映射到全局而已, 其實你也可以用自己的包這么做

            打印的另一種寫法

            想跟腳本一樣調(diào)試打印數(shù)據(jù)么?

            1. println("hello world")

            無需包含任何包, 因為它在buildin包里

            iota不是黑科技

            這是在buildin包里的定義

            1. // iota is a predeclared identifier representing the untyped integer ordinal
            2. // number of the current const specification in a (usually parenthesized)
            3. // const declaration. It is zero-indexed.
            4. const iota = 0 // Untyped int.

            其實go是有泛型概念的

            想想map和數(shù)組的定義 
            只是泛型沒有開放給用戶用而已(只許XX放火,不許XX點燈)

            map是支持多個key的, 而且很方便

            還在為多個key轉(zhuǎn)id的復雜算法而頭疼么?

            1. type myKey struct{
            2. number int
            3. str string
            4. }
            5. func main(){
            6. t := map[ myKey] int {
            7. myKey{ 2, "world"}: 1,
            8. }
            9. fmt.Println(t[myKey{2, "world"}])
            10. }
            11. 輸出: 1

            枚舉是可以轉(zhuǎn)成string的

            默認定義一個枚舉

            1. type MyConst int
            2. const (
            3. MyConst_A MyConst = iota
            4. MyConst_B MyConst = iota
            5. )
            6. func main(){
            7. fmt.Println(MyConst_A)
            8. }

            輸出: 0 
            如果我們想自動化輸出MyConst_A字符串時 
            就需要使用golang的一個工具鏈:golang.org/x/tools/cmd/stringer 
            將其下載, 編譯成可執(zhí)行工具后, 對代碼進行生成 
            生成的代碼會多出一個xx_string.go 
            里面就是枚舉的String()string 函數(shù)

            臨時轉(zhuǎn)換一個接口并調(diào)用的方法

            1. type love struct{
            2. }
            3. func (self*love)foo(){
            4. fmt.Println("love")
            5. }
            6. func main(){
            7. var chaos interface{} = new(love)
            8. chaos.(interface{
            9. foo()
            10. }).foo()
            11. }

            Golang的receiver實際上就是this的變種實現(xiàn)

            1. func( self*MyStruct) foo( p int ){
            2. }

            寫不慣receiver的寫法? 如果這樣改下呢?

            1. func foo( self *MyStruct, p int ){
            2. }

            所以為什么說Golang還是一個C語言嘛

            關(guān)于內(nèi)存分配…

            • new 傳入Type類型, 返回*Type類型
            • make 可以在分配數(shù)組時設(shè)置預分配大小, new不可以
            • make 能分配數(shù)組,map, 但不能分配結(jié)構(gòu)體和原始類型
            • new 能分配數(shù)組, map, 結(jié)構(gòu)體和原始類型等的所有類型
            • &Type等效于new
            • 切片不需要分配內(nèi)存(make,new), 直接聲明就可以了…

            Golang的反射無法通過一個類型名, 創(chuàng)建其實例

            C#有Assembly概念, 可以在一個Assembly里搜索, 創(chuàng)建實例

            Golang是靜態(tài)類型語言, 如果需要, 只能注冊你需要創(chuàng)建的結(jié)構(gòu)體, 然后將注冊好的map用于創(chuàng)建

            Golang可以替換Python來進行復雜的工具流程處理

            如果你需要跨平臺的工具流程處理, 對Python不熟悉, 可以使用

            1. go run yourcode.go 參數(shù)1 參數(shù)2

            方式來進行工具處理 
            覺得慢, 可以編譯成可執(zhí)行文件

            這樣做的好處: 如果有些類庫本身就是go寫的, Python想使用是很麻煩的, 而Golang來寫則輕而易舉

            例子: 通過go的protobuf庫, 對一些文件進行處理

            Golang可以自動持有方法的接收者實例

            1. type myType struct{
            2. }
            3. func (self*myType) foo( p int){
            4. fmt.Println("hello", p )
            5. }
            6. func main(){
            7. var callback func( int )
            8. ins := new(myType)
            9. callback = ins.foo
            10. callback( 100 )
            11. }

            做過lua的C++代碼綁定的童鞋都清楚: lua只支持外部靜態(tài)或者全局函數(shù)調(diào)用 
            如果要進行C++類成員函數(shù)調(diào)用時, 要自己處理this和成員函數(shù) 
            這種技巧因為早起編譯器的虛表不同平臺實現(xiàn)細節(jié)不統(tǒng)一需要專門處理 
            后面跨平臺虛表統(tǒng)一后, 類成員函數(shù)的調(diào)用寫法也是很惡心復雜的 
            但是Golang的小白式用法, 直接吊打C++, 甚至C#復雜的delegate

            LiteIDE篇: 多開秘籍

            • 找到 菜單->查看->選項->通用->存儲->存儲設(shè)置到本地ini文件

            • 關(guān)閉LiteIDE

            • 復制LiteIDE整個目錄, 命名文件夾為你的工程名

            • 每個工程所在的LiteIDE的配置將是獨立的, 不會互相干擾

            LiteIDE篇: 測試程序也是可以調(diào)試的

            別以為程序一定要是main開始的才可以調(diào)試

            Golang的測試程序雖然都是一個個Test開頭的函數(shù),但執(zhí)行g(shù)o test時, 還是有main入口

            在LiteIDE中, 可以在 調(diào)試->開始調(diào)試測試程序里進行測試程序調(diào)試

            LiteIDE篇: 在Windows上可以輸出linux可執(zhí)行文件

            go的工具鏈默認支持交叉編譯 
            在LiteIDE中, 可以通過切換輸出平臺, 輸出不同平臺的可執(zhí)行文件

            posted @ 2016-11-02 11:09 戰(zhàn)魂小筑 閱讀(22319) | 評論 (1)編輯 收藏

            2016年9月3日 #

            Google官方為Golang的調(diào)試例子默認使用了gdb

            然而, 使用gdb調(diào)試go程序會遇到goroutine的各類問題, 因為gdb不懂go

            因此, 這里使用delve黑科技來進行Golang的程序調(diào)試

            純命令行調(diào)試方法在網(wǎng)上很容易搜索到, 本文主要以LiteIDE來進行程序調(diào)試

            關(guān)閉編譯器優(yōu)化

            正常go build/install出的go程序是完全優(yōu)化過的, 強行使用調(diào)試器掛接調(diào)試時, 某些local變量/lamda表達式捕獲的變量會直接進入寄存器, 無法使用調(diào)試器查看

            刪掉所有的pkg, 為build或install參數(shù)加入關(guān)閉編譯器優(yōu)化的參數(shù) -gcflags "-N -l"

            例如:

            1. go install -gcflags "-N -l" svc\gamesvc

            delve調(diào)試器安裝方法

            LiteIDE自帶了gdb, 但是沒有delve調(diào)試器, 需要自行安裝, 命令如下

            1. go get github.com/derekparker/delve/cmd/dlv

            delve調(diào)試器會被放到你的GOPATH/bin下

            LiteIDE中的delve調(diào)試器配置

            選擇調(diào)試器

            在LiteIDE菜單中選擇 調(diào)試->debugger/delve

            delve環(huán)境變量設(shè)置

            這個時候, LiteIDE依然找不到delve, 因為它不在環(huán)境變量PATH中, 這里無需修改環(huán)境變量, 只需要LiteIDE的環(huán)境配置

            在LiteIDE菜單中選擇 查看->編輯當前環(huán)境, 在彈出的文檔中修改

            1. PATH=c:\mingw32\bin;%GOROOT%\bin;%PATH%;c:\your\path\to\delve

            去掉PATH前的注釋#, 在%PATH%添加分號, 然后和你到delve調(diào)試器的路徑

            開始調(diào)試

            選擇你的工程, 點擊F5, 進入調(diào)試模式

            調(diào)試器顯示變量值

            LiteIDE使用delve調(diào)試時, 無法在 變量 監(jiān)視等窗口中正確捕捉delve調(diào)試返回數(shù)據(jù)(因為格式太復雜了…)

            沒關(guān)系, 我們使用命令行配合顯示即可

            LiteIDE控制臺或調(diào)試輸出窗口在delve調(diào)試時, 實際上是一個標準命令行 
            命令如下

            • p 變量名可以查看變量值

            • locals查看局部變量

            • ls可查看當前文件

            • stack查看棧

            • help可以查看各種幫助

            調(diào)試外部程序

            如果你的程序是外部程序, 或者使用go install安裝到GOPATH/bin目錄的程序, 那么使用delve調(diào)試器啟動程序時, 可能會碰到啟動路徑錯誤的問題

            使用LiteIDE菜單 調(diào)試->調(diào)試其他應用程序… 填入你要調(diào)試程序的路徑以及工作目錄, 可以解決這個問題

            posted @ 2016-09-03 18:12 戰(zhàn)魂小筑 閱讀(5578) | 評論 (0)編輯 收藏

            僅列出標題  下一頁
            久久久久久亚洲精品无码| 精品国产热久久久福利| 久久久无码精品午夜| 2021国产精品午夜久久| 亚洲国产精品无码成人片久久| 亚洲中文字幕无码久久2020| 国产精品欧美久久久久无广告 | 久久久久99这里有精品10| 久久夜色精品国产噜噜噜亚洲AV| 99久久精品久久久久久清纯| 亚洲欧美日韩中文久久| 国内精品久久久久影院免费| 青青草原综合久久大伊人| 国产成人久久AV免费| 亚洲国产欧美国产综合久久| 国产成人精品综合久久久| 香蕉久久久久久狠狠色| 久久天堂电影网| 91久久精品无码一区二区毛片| 亚洲精品tv久久久久| 久久免费99精品国产自在现线| 漂亮人妻被黑人久久精品| 亚洲国产精品综合久久网络| 狠狠狠色丁香婷婷综合久久俺| 久久99热这里只有精品66| 97精品国产97久久久久久免费| 精品久久久久久亚洲精品 | 亚洲∧v久久久无码精品| 久久久久99精品成人片| 成人免费网站久久久| 久久精品男人影院| 少妇高潮惨叫久久久久久| 久久人人爽人人爽人人片AV东京热 | 一本大道加勒比久久综合| 久久夜色精品国产欧美乱| 中文字幕无码免费久久| 伊人热热久久原色播放www| 亚洲国产成人精品女人久久久 | 精品久久人人妻人人做精品| 国产精品久久久久影视不卡| 久久精品国产清高在天天线|