1. 程式人生 > >一個人獨立開發 3D 遊戲引擎可能嗎?

一個人獨立開發 3D 遊戲引擎可能嗎?

 

作者:孫志超
連結:https://www.zhihu.com/question/24733255/answer/42000966
來源:知乎
著作權歸作者所有,轉載請聯絡作者獲得授權。

當然可以,但難道有個引擎,就可以做出真正商業化的遊戲麼?而且國產遊戲大部分是網遊啊。

幾年前的老文——《一個人的伺服器端》(只是為了說明遊戲開發難度,不是針對題主問題。)


技術準備

能夠做這個MMO的觸發點是通過某些途徑得到了某個大公司使用的一款3D引擎,其他的都是白手起家。當時大家還不知道有“分散式伺服器端”一說,伺服器端框架參考了《劍3》:劍3內測的時候經常伺服器crash,但是每次只crash一個地圖,所以可以推知他們是一個地圖一個server;加上自己對伺服器端的認識,需要Gate當防火牆,需要GameServer來總管MapServer,需要DB來儲存,那麼最初的伺服器端框架就定下來了:Gate、GameServer、MapServer、DBServer。想讓伺服器之間的連線方式最簡化,所以確定GameServer是中心,其他Server都連線並且只連線GameServer。MapServer和GameServer上面準備加指令碼,指令碼直接選擇了python,因為python語法清晰一點。開發平臺選擇windows,因為當時公司內沒有一個人瞭解linux。

策劃定調

做一個3D版的征途,這個就是最終確定的整個專案的方向。為什麼這個方向?因為:1.征途火,大家都想學。2.征途的開發技術難度相對較低。3.當時很多運營遊戲的公司的產品線上都沒有類似產品。

專案啟動

啟動時team中只有5人。一共有3件事:1. 伺服器端先行開發,伺服器端3個月的計劃是構建所有伺服器並且封包走通。2.研究引擎並且改造引擎,確保自己會使用,同時沒有抄襲痕跡。3.開始策劃招聘,對這個策劃的定位很明確:要能夠扛得起旗幟,有領導力,有大局觀,最好是征途公司的。

伺服器端這邊的目標是達到每秒30萬個封包的效率,封包大小在30byte到50byte。30萬這個資料來自於估算:伺服器端300人互相可見的情況下,每人每秒動3次,那麼就是300*300*3,差不多30萬封包了。

伺服器端第一次重構

服務端這邊開始是2人開發,我做大局,另一個做網路層,一個月後封包效率仍然上不去,於是大家一起檢查程式碼,發現網路層的封包pool結構不是用list儲存,而是用map,還發現很多其他常識性問題,於是決定把他調離伺服器端,去做場景編輯器。於是從此我開始了一個人的伺服器端的旅程,花了1個星期的時間完成重寫他的程式碼,然後繼續前進。

優化經驗

這個專案的優化難度不太高,源於2點:1.整個專案設計都遵循一條指導原則:能不用第三方庫就不用,所以優化過程中不需要理解第三方庫的用法。2.封包定義的結構是struct,定義封包的方式是先寫虛擬碼,然後再用根據根據虛擬碼生成每個封包的struct和ReadBuffer、GetBuffer函式。一般封包都是用memcpy賦值,只有動態封包,比如封包中如果不得不使用sting,才會通過比較慢的方式讀取,這樣的話在封包賦值、轉移操作上面沒有碰到任何效能問題。

基於以上的定義,實際上只有2條優化經驗:1.讀取封包的時候,一次要儘可能多的讀取封包,不要先read一個封包長度,再根據長度read實際內容。這種方式會讓效能大打折扣。2.傳送封包的時候,需要稍微拼接一下再發送,可能會造成一定的封包延時,但是效率會上升至少10倍。

策劃啟動

理想的主策過來了,是以前征途的策劃,純市場導向。過來後沒有急著開始招策劃,而是先風格定調,這個過程就像RPG遊戲裡面給一個角色配點一樣,總共屬性點是有限的,我們必須強調哪些東西而捨棄哪些東西。在這個遊戲中,強調的是PK,捨棄的是娛樂性、PVE。我們堅決不做魔獸世界,這個是當時的口頭禪。事實證明這句話是對的,魔獸世界不是誰都可以做出來的。最終確定了遊戲背景是戰國時間,以秦始皇為藍本。

世界觀確定的時間大概有1個月,之後就開始招聘執行策劃了。

美術啟動

主美也過來了,是老牌工作室的人。主美過來的同時就開始招聘美術,這個時候,對於美術各個職位,待遇最高的是原畫。為了激勵大家的士氣,老闆公開定了3個月一次MileStone,每個MileStone拿一次獎金的制度,所有人都很開心。

專案危機

現在有幾個問題:

問題1:內部不協調。程式是公司自己培養起來的,是公司的主動力,而普通策劃和普通美術都是新來的,但是一來工資卻比程式高很多,因為公司開的工資在業內是偏低的,要招新人,必須按照業內標準來招才能找到人。也就是說新來的策劃美術都是業內標準工資,而程式拿的是很低的工資。

問題2:過來的人的工資超過了老闆的預期,資金準備不夠了。於是老闆不得不讓出部分股權尋找天使投資人。

建立基本開發流程

主策招聘策劃的標準很明晰:1.不做魔獸世界。2.要有血性。所以進來的策劃全是熱血的新手。主策對招人的點抓得很不錯,對一個前途未卜的公司來講,最需要的是熱情。

“策劃主導開發”,這個是征途的思想,也帶到我們這裡。功能策劃負責整體功能的實現,伺服器、客戶端溝通,程式加班策劃也要加班,功能失敗也是先找策劃。當時程式還在打基礎,策劃有大量的時間,但是也沒閒著,主策讓他們寫功能文件,越詳細越好,經常幾個策劃無聊在比誰的功能文件寫的全。

伺服器端第二次重構

重構源於分散式伺服器端,碰到問題:如果保證玩家在所有伺服器登陸的一致性?也就是說:如何不卡號。一開始的登陸流程是:1.玩家登陸Gate。2.登陸GameServer,GameServer讀取DB資料。3.GameServer通知MapServer玩家登陸並且把DB資料給MapServer。

我們的客戶端測試工具是我自己寫的模擬機器人,模擬器沒有讀條過程,所有很容易暴露問題,我們發現了以下問題:

問題1.因為讀DB需要時間,MapServer會在沒有收到GameServer玩家登陸訊息的時候,收到玩家發來的移動訊息。當玩家頻繁重登的時候,這個玩家甚至有可能收到他上次登入發來的封包。

問題2:切換地圖邏輯異常麻煩。切換地圖邏輯需要做的事情:1.詢問對方地圖是否接受。2.傳送到對方地圖。3.完成傳送,改變玩家在GameServer上面註冊的MapID。這個邏輯是非同步的,任何環節都要處理異常,所有封包都走GameServer。

因為所有封包都會通過GameServer,GameServer負責了太多的狀態變數導致邏輯很不清晰,不把邏輯梳理清晰,肯定會引發重大問題。

於是有了第二次重構,重構的技術改動是:

改動1. GameServer拆分為RouterServer和GameServer,所有伺服器都會連線RouterServer,GameServer只負責世界邏輯。

改動2. RouterServer增加協層的概念,目的是處理像登陸或者切換地圖這種需要多臺伺服器互動的關係,把切換地圖做成“事物”,其他伺服器再也不用關係切換地圖的異常處理問題。

因為增加了一臺Server,伺服器改動比較大,前後大概有2個星期的時間成型。

美術部關係

主美不是一個領導力很強的人,在安逸的環境中不會暴露他的問題,但是在公司現在這種生死未卜的環境中,他不能給他的美術以信心和力量,導致美術部集體抗議專案獎金髮得過慢的問題,最終導致集體“請假”。對此,公司老闆非常火大也非常無耐,主策和老闆泡了一會茶,大概意思是說:要忍耐,要有信仰。對於主美,這是能力問題,不是他的錯,必須幫他扛起這塊地,而現在來講,只要美術都沒辭職,就將就用吧。所以在專案開發日夜顛倒的灰暗歲月中,美術是從來不加班的。程式每每都是在美術部正常下班的時候,雙眼發綠光地盯著離去的人發呆,因為程式還將繼續戰鬥8個小時左右。

這一段開發是一段黑暗的日子,對程式的整體認識都被翻了一遍,公司的很多庫函式也因為無法滿足效能需求而優化重寫,作為技術長進來講,的確是一段脫胎換骨的日子,作為回憶來講,只會感受到痛苦。

現在程式基礎基本OK,可以開始大規模開發。此時客戶端還停留在引擎階段,沒有真正的做遊戲,伺服器端一直用自己做的模擬器測試。

第二MileStone開始

程式的情況漸漸明晰,策劃,美術都已到位。預計第一MileStone3個月搞定,實際上用了5個月。第二MileStone的任務很簡單:做到10級。其中包括:客戶端啟動開發,製作一個地圖,主線任務,怪物AI,AOI,尋路,物品,商人,玩家和玩家交易。

當時開發流程中沒有專案管理軟體,開發流程很簡單:貼條。策劃自己給自己貼條,然後給程式貼條。客戶端程式由於剛剛啟動,前途未卜,還無法真正開發,所以開發程式也就只是指我一個人。後來大家覺得因為只有一個程式,貼條好像沒啥意義,所以統一直接找我口頭溝通了。

客戶端開始,狀態機的悲劇

客戶端程式也是一個人,老闆對客戶端程式的評價是:不撞南牆不回頭。所以當老闆做了錯誤的決定的時候,客戶端程式也拼命衝上去做了。這個錯誤是:使用狀態機來管理主角狀態。當時的狀態有接近30個,比如:待機、走路、跳躍準備、跳躍中、落下準備、攻擊待機、攻擊中、攻擊關鍵幀等等……,用了一個自己做的工具來生成狀態機程式碼,客戶端程式在不斷地考慮狀態轉換關係,而且每新增一個動作,就需要考慮這個動作和所有其他動作的轉換關係。客戶端程式本身就不是一個邏輯很清晰的人,於是發生了一次又一次的悲劇,在黑白顛倒的工作壓力下,客戶端程式已經完全崩潰,會出一些非常低階的問題。有一次客戶端程式隨意的給enum定義變數賦值而且賦值重複了,結果導致查了一通宵的問題。還有一些關聯性問題:比如我們跳躍的格子數是2格,為什麼每次玩家都會跳4格?一個通宵後找到問題:因為美術做跳躍動畫的時候,向前移了2格。

最後,終於客戶端程式主動辭職。由製作場景編輯器的人來繼續客戶端的開發,事實上專案沒有因為客戶端程式的離職導致太大問題。

策劃數值定調

這個遊戲的面向使用者不會很高階,所以一定不能做得太麻煩,於是定死了幾條規則:1.做攻減防體系。2.不做任何強控制技。攻減防體系是說計算傷害的時候,直接用攻擊方的攻擊力減去防禦方的防禦力得到總傷害,而不是像魔獸體系一樣,防禦做成百分比。後來的經驗證明攻減防體系是不對的,因為數值會飄,比如一個100點攻擊力的人攻擊防禦50點的人,傷害是50點;但是加了雙倍傷害之後,攻擊方變成200點攻擊力,對防禦50的人造成的傷害是150點,總傷害翻了3倍,所以一般pk的時候,誰先造成致命誰贏。強控制技是說擊暈等讓對方完全無法控制的技能,不做強控制技是為了防止若干個免費玩家通過操作能成功玩死一個終極裝備玩家。

python框架定調

採用的方法是資料在c++中,用swig做轉換介面轉換給python用,每一個封包到達會呼叫相應的python函式,python從c++的臨時資料中取出當前封包並且處理。整個專案都是基於這個在做下去,但是很後來的時候效能測試才發現,最消耗的地方不是在pyhton執行層,而是在python和c++介面層,因為資料全部在c++中,python大量使用c++介面,導致整體效能低下,至今為止,這個問題還無法完美解決。

任務框架制定

制定任務框架的時候參考了2個原始碼:一個是天堂II伺服器端原始碼,一個是征途洩露出來的原始碼,它們都有一個特點:任務系統使用xml體系來描述。天堂II的任務是html的,可以直接用瀏覽器開啟並且能夠看到對話和連結;征途的簡化一點,是xml描述的。這個發現給了人很大的啟發:任務系統和做網頁本質上是一樣的。於是我們的任務系統也決定這麼做,最終寫起來的感覺讓人覺得在做asp頁面。

任務體系最終的形態是:一個任務一個目錄,目錄下面是事件目錄,事件目錄裡面是以NPCID為檔名的xml處理,比如如果玩家有ID為1的任務,需要和NPCID為100的NPC對話才能觸發事件,那麼目錄結構是:目錄(名稱1)下面有子目錄(名稱on_npc),下面有檔案(100.xml),這麼做的好處是:如果以後有某個任務壞了導致會有死鏈或者刷任務,我們可以直接刪除任務這個目錄,不會影響其他的任務的開發。

python程式招聘和整個系統的重新設計

我們從遊戲開發培訓學校招了2個學員來試用,雖然這2個人當時沒有對開發產生多大的推動作用,但是引入了多人開發這個概念,整個框架定調都和以前完全不同,這對整個專案有很積極的作用。比如以前我們寫一個技能,考慮的實現方法是儘量重用,儘量使程式碼簡化並且對編碼者易維護,但是這樣的副作用是過於抽象會讓程式碼變得難懂,只用開發者自己才能夠維護,而且很難找到某個功能的負責人,只能由我全權負責。後來的技能寫法轉變為一個技能一個python檔案,如果某個技能出問題了,也可以很輕易的找到功能負責人,更好的是,如果一個技能有bug,直接刪除這個技能指令碼即可,不會對整個系統有任何負面影響。上面說的任務框架,技能系統,物品系統全部是根據一個原則來設計:出了錯誤可以馬上被替換。這個思想的轉變給人的感覺是打war3對戰和打星際對戰的區別,一個是英雄戰,講究個人風範,一個是群戰,講究分兵佈陣。

客戶端python化

伺服器python化的經驗證明,開發速度上面,python速度比c++速度快4倍,並且寫出來的東西清晰易懂,所以這個經驗也用到了客戶端上面,一開始是用在做介面上,客戶端的介面框架是自己實現的,要增加一個介面,首先需要用c++ builder畫好介面,然後把介面程式碼dfm檔案放到客戶端中,客戶端解析這個介面檔案,然後找到相應的python程式碼來處理邏輯,客戶端大的思想是一個介面一個py,並且各個介面儲存各個介面自己的資料,相互獨立。這麼做的好處是功能劃分清晰,壞處是對於同一個資料重新整理,不同的介面如果想取得同樣的資料的話,需要2份一樣的讀取程式碼,比如如果有2個組隊資訊介面,每個介面都需要單獨讀取組隊資訊封包,管理自己的組隊資料。但是,總的說來,當時的這個選擇是好處大於壞處

MileStone3

目標是做到40級,並且做副本系統。就是說一個MapServer可能會跑多個副本,每個函式的處理都需要加上GameWorld引數,標識自己在哪個副本中,當時的做法很簡單:多個副本在同一個執行緒中跑,每跑一個副本的時候,都會改變current_game_world變數,在邏輯處理的時候,通過current_game_world可以得到當前的遊戲世界。這麼做的好處是所有邏輯都不用改,壞處是跨服邏輯的處理:其他伺服器無法知道這個玩家在哪個副本中,所以只好統一在0號副本處理跨服邏輯,這個變成了一個潛規則,而且每個開發者都會因為這個潛規則而糊塗一陣。

python程式繼續招聘

python程式已經開始大量招聘,都是像那一家遊戲培訓學校要人,工資開得都很低,但是這些人都發揮了很重要的作用,他們特點總結說來:1.聽話。2.熱情。筆者始終認為這2個特點是一個合格新手的必備條件。後來在這些python程式中成長起來一批人,至今筆者還非常認同,不比任何一個大學的畢業生差。

至此,專案各項流程都已經初步確定,工作可以有序展開,下一步是最重要的:推向市場。

尋找買家

為了找到好的買家,公司上了一次CJ展會,果真發現了不少商機,有很多大公司都提出意向,但是開出的條件非常苛刻,不過老闆還是去周邊走了一趟,順便見了見國內的各個大小開發商。比較有印象的是3家公司:盛大,約18號去面談,最終談失敗了,因為還不想加入18計劃;騰訊,因為有個巨大的平臺,所以幾乎沒有開出什麼利潤條件;巨人,這個公司對我們很有幫助,產品總監過來看了一下,直說我們的產品還很次,不夠檔次。於是我們鬱悶了很久,最終下決定美術工作重翻。

在美術工作重翻的時候,我們也放棄了在大陸能夠找到好買家的期望,因為畢竟是一個很新的公司,沒人瞭解。於是找到了臺灣的10年“合作”夥伴:臺灣網龍,網龍那邊很爽快就答應了,但是要求任務系統重翻一次。於是生意一拍即合,我們開始繁體化介面了。

大陸買家

在確認了臺灣網龍會代理後,在大陸這邊找代理商就容易多了,看來各個代理商都是在等待對方先吃螃蟹。最終大陸確認由光宇代理,對公司來講,終於,拖了1年多的獎金總算髮下來了一點點。

大陸封測

光宇的地推很給力,封測總共5組伺服器,他們保證每組伺服器都滿載6000人在跑,但是進來玩的人似乎不都是玩家,整個封測過程大家都是掛機自動打怪,沒有任何社會活動在裡面,也沒出任何問題,封測非常順利。

大陸內測

封測的順利讓光宇決定:內測直接開商城。大家都知道,現在的遊戲實際上已經沒有內測封測的說法了,開商城就意味著遊戲正式運營。我們公司對這件事很沒底,伺服器沒有故障過更覺得沒底,但是來不及思考,內測開始了,當時的感受只能用一句很流行的話來說:Hell, It's about time。

內測總共出的問題列舉如下:

串號:一個玩家錯誤的登陸到另外一個玩家的號上面去了,是因為我們沒有用sessionID作為封包識別符號。

卡號:通常是因為MapServer報錯,導致玩家在Router上面一直是登陸狀態,後來在Router上面加了ping包踢人機制,但是仍然還有這個問題,因為當router判定需要踢人的時候,玩家在MapServer上出錯就無法下線,否則回檔。

回檔:都是因為伺服器出錯而導致的,解決方法是每個MapServer每2分鐘會把本地圖的所有玩家資料寫到本地儲存檔案。每次開發的時候,如果發現本地儲存檔案有玩家資料資訊,則把玩家資料寫回資料庫。

覆檔:這個是上面的解決方案導致的,如果2個地圖都有同一個玩家的本地快取資料,那麼可能發生舊的玩家資料把新的玩家資料覆蓋的情況。解決方法是每個玩家進入地圖的時候,都必須觸發一次本地快取,這樣玩家資料可以保證唯一。

清檔:運營商那邊要求:為了防止運營商內部人員修改遊戲資料,資料庫中的資料都必須做一個雜湊標識,玩家登陸的時候,如果根據資料中的資料計算的雜湊標識和已經儲存的雜湊識別符號不匹配,則出錯處理。在出錯處理中,如果玩家身上的物品雜湊標識不對,那麼就直接刪除此物品。這個是一個非常嚴重的失誤,某次遊戲更新,我們給物品表新增了新欄位,但是忘記了雜湊錯誤處理這回事,結果放到公網上,每個登陸的玩家的物品都被清空,伺服器開了半個小時立即全部關服,但是已經有4000多個玩家的資料遭到損害。更悲慘的是,上週運營商那邊忘記了做資料備份,所以我們的解決方式是:1.根據上上週的資料備份恢復丟掉裝備的玩家資料。2.根據2周的系統日誌來補償玩家物品。3.給出相應補償。整個過程持續了28個小時,提供了8個版本的恢復工具給運營商,最後回到家的時候,我連開門的鑰匙扣都找不到在哪裡。

刷錢:當時有個bug:比如一個玩家的賬號名是account,他用賬號名Account和賬號名account能夠登入同時到router,也就是說,router判定重登的時候沒有區分賬號的大小寫,導致的問題是玩家可以同時登陸2個同樣的角色到選人介面,Account賬號進入遊戲,把錢全部給小號,下線,然後account賬號再下線,這樣後下線的賬號儲存資料會蓋掉前面登陸賬號的儲存資料,達到刷錢的目的。這個bug的發現過程非常複雜,很多情況下只能靠聯想去思考,運營商那邊一直懷疑自己有內鬼,當發現最後的真相的時候,我們都很汗顏。

很難登陸:我們物品表的組織結構是一個物品一條記錄,導致一組伺服器運營半年後,物品表有一千多萬條記錄,玩家登陸一次,讀取30個物品需要的時間竟然需要30秒。經過排查發現,物品表的索引欄位定得過多導致讀取寫入物品都非常慢,去掉了索引欄位後,問題緩解。不久之後問題繼續,因為資料量實在太大了,所以我們對物品表做了一個系統性的改動:用二進位制形式儲存玩家的所有物品,一個玩家一行記錄,這樣才根本的解決了讀取過慢的問題,但是副作用是我們不能隨意的查詢玩家物品資料。

每日任務重複觸發:因為當時每日任務觸發機制做在MapServer上,而各個MapServer硬體的時間有些微妙的不一樣,所以會發生這樣的情況:一個玩家在Map1上面走道00:00分觸發每日任務,然後切換地圖去Map2,Map2的時間是11:59分,所以Map2走到00:00分的時候,又會再次觸發每日任務。這個問題的解決方法是:1.玩家資料中記錄下來當前觸發每日任務的日期,用作防止重複觸發判定。2.伺服器整體時間由GameServer來同步,MapServer禁止取得本地時間,防止再次發生類似悲劇。

客戶端本地檔案損壞:這個bug也隱藏得非常深,因為客戶端用記憶體檔案對映開啟本地檔案的模組沒有用只讀方式開啟,所以客戶端某一個野指標可能會寫到本地檔案然後儲存,結果是客戶端本地檔案莫名其妙不能用了,只能完全重新下載重灌。排查這種bug和推理小說中的偵探做事方法差不多,每次查到問題,總會有一種自虐的成就感。程式設計師最大的敵人是自己。

整個測試過程中,因為我們沒有提供很好的遊戲查詢方式給運營商,所以比如說要找到一個伺服器花錢最多的人,都是由我在公網資料庫中執行select操作實現的,我可以直接訪問運營極,並且好幾次是由我來公網開服,客服很多問題也是由我來解答,整個運營體系極其混亂。

臺灣內測

臺灣在內測前提供以下幾個文件:log介面和log格式,充值介面。log介面總共定了200多種日誌型別,規則很清晰,並且導致我們對所有程式碼都翻了一遍才加上完備的log。對接成功之後,整個遊戲上線了。我們這邊沒有伺服器的訪問許可權,但是整個運營過程幾乎沒出什麼問題,臺灣那邊很少打電話過來,只是看到他們的電視廣告一直打,整個遊戲很火,據臺灣說同時線上最高有3萬人。唯一記得的一個bug是:一次我們改動了物品拖動邏輯,導致在某些情況下會複製物品。臺灣因這個bug損失很大,他們重整了自己的很多QC流程,後來才知道,臺灣網龍的代理流程是把遊戲版本拿過來,先QC測試,QC保證這個產品的質量,如果出問題,第一責任人是QC。

臺灣的成功來源於2點:1.大陸先上線,很多雷被踩過了。2.制度規範,介面規範,運營商很清晰,不會有惡性迴圈。

臺灣計費

雖然臺灣產品很火,老闆卻不太高興,因為充值計費全部是走的運營商介面,並且玩家在不登陸游戲的時候也可以給遊戲充值,所以我們這邊完全無法知道玩家確切的充值資料,也就是說,運營分成上面完全沒有主動權。

大陸線上人數跳水

這個是預料之中的,因為程式這邊的crash已經讓所有人都崩潰。

人數穩步下滑

在這個時候,玩家對這款遊戲的新意已經過去,沒有玩點,粘稠度再強、再強調社交都沒有用,我自己來講,也是每日任務做完就下線了,pvp只是有錢人的玩意,而pve的boss又必須花錢去買才能殺,對於不喜歡用錢玩遊戲的人,幾乎無法生存,沒有了羊的世界,狼也慢慢消亡了。這是我個人總結的經驗。

小結

至此,一款網遊完整的生死已經結束,通過這次開發得到了不少經驗也成熟了不少,同時身體也垮了不少。總的說來,“我做成了一件事情”這句話還是挺有成就感的。

作者:孫志超
連結:https://www.zhihu.com/question/24733255/answer/42000966
來源:知乎
著作權歸作者所有,轉載請聯絡作者獲得授權。

當然可以,但難道有個引擎,就可以做出真正商業化的遊戲麼?而且國產遊戲大部分是網遊啊。

幾年前的老文——《一個人的伺服器端》(只是為了說明遊戲開發難度,不是針對題主問題。)


技術準備

能夠做這個MMO的觸發點是通過某些途徑得到了某個大公司使用的一款3D引擎,其他的都是白手起家。當時大家還不知道有“分散式伺服器端”一說,伺服器端框架參考了《劍3》:劍3內測的時候經常伺服器crash,但是每次只crash一個地圖,所以可以推知他們是一個地圖一個server;加上自己對伺服器端的認識,需要Gate當防火牆,需要GameServer來總管MapServer,需要DB來儲存,那麼最初的伺服器端框架就定下來了:Gate、GameServer、MapServer、DBServer。想讓伺服器之間的連線方式最簡化,所以確定GameServer是中心,其他Server都連線並且只連線GameServer。MapServer和GameServer上面準備加指令碼,指令碼直接選擇了python,因為python語法清晰一點。開發平臺選擇windows,因為當時公司內沒有一個人瞭解linux。

策劃定調

做一個3D版的征途,這個就是最終確定的整個專案的方向。為什麼這個方向?因為:1.征途火,大家都想學。2.征途的開發技術難度相對較低。3.當時很多運營遊戲的公司的產品線上都沒有類似產品。

專案啟動

啟動時team中只有5人。一共有3件事:1. 伺服器端先行開發,伺服器端3個月的計劃是構建所有伺服器並且封包走通。2.研究引擎並且改造引擎,確保自己會使用,同時沒有抄襲痕跡。3.開始策劃招聘,對這個策劃的定位很明確:要能夠扛得起旗幟,有領導力,有大局觀,最好是征途公司的。

伺服器端這邊的目標是達到每秒30萬個封包的效率,封包大小在30byte到50byte。30萬這個資料來自於估算:伺服器端300人互相可見的情況下,每人每秒動3次,那麼就是300*300*3,差不多30萬封包了。

伺服器端第一次重構

服務端這邊開始是2人開發,我做大局,另一個做網路層,一個月後封包效率仍然上不去,於是大家一起檢查程式碼,發現網路層的封包pool結構不是用list儲存,而是用map,還發現很多其他常識性問題,於是決定把他調離伺服器端,去做場景編輯器。於是從此我開始了一個人的伺服器端的旅程,花了1個星期的時間完成重寫他的程式碼,然後繼續前進。

優化經驗

這個專案的優化難度不太高,源於2點:1.整個專案設計都遵循一條指導原則:能不用第三方庫就不用,所以優化過程中不需要理解第三方庫的用法。2.封包定義的結構是struct,定義封包的方式是先寫虛擬碼,然後再用根據根據虛擬碼生成每個封包的struct和ReadBuffer、GetBuffer函式。一般封包都是用memcpy賦值,只有動態封包,比如封包中如果不得不使用sting,才會通過比較慢的方式讀取,這樣的話在封包賦值、轉移操作上面沒有碰到任何效能問題。

基於以上的定義,實際上只有2條優化經驗:1.讀取封包的時候,一次要儘可能多的讀取封包,不要先read一個封包長度,再根據長度read實際內容。這種方式會讓效能大打折扣。2.傳送封包的時候,需要稍微拼接一下再發送,可能會造成一定的封包延時,但是效率會上升至少10倍。

策劃啟動

理想的主策過來了,是以前征途的策劃,純市場導向。過來後沒有急著開始招策劃,而是先風格定調,這個過程就像RPG遊戲裡面給一個角色配點一樣,總共屬性點是有限的,我們必須強調哪些東西而捨棄哪些東西。在這個遊戲中,強調的是PK,捨棄的是娛樂性、PVE。我們堅決不做魔獸世界,這個是當時的口頭禪。事實證明這句話是對的,魔獸世界不是誰都可以做出來的。最終確定了遊戲背景是戰國時間,以秦始皇為藍本。

世界觀確定的時間大概有1個月,之後就開始招聘執行策劃了。

美術啟動

主美也過來了,是老牌工作室的人。主美過來的同時就開始招聘美術,這個時候,對於美術各個職位,待遇最高的是原畫。為了激勵大家的士氣,老闆公開定了3個月一次MileStone,每個MileStone拿一次獎金的制度,所有人都很開心。

專案危機

現在有幾個問題:

問題1:內部不協調。程式是公司自己培養起來的,是公司的主動力,而普通策劃和普通美術都是新來的,但是一來工資卻比程式高很多,因為公司開的工資在業內是偏低的,要招新人,必須按照業內標準來招才能找到人。也就是說新來的策劃美術都是業內標準工資,而程式拿的是很低的工資。

問題2:過來的人的工資超過了老闆的預期,資金準備不夠了。於是老闆不得不讓出部分股權尋找天使投資人。

建立基本開發流程

主策招聘策劃的標準很明晰:1.不做魔獸世界。2.要有血性。所以進來的策劃全是熱血的新手。主策對招人的點抓得很不錯,對一個前途未卜的公司來講,最需要的是熱情。

“策劃主導開發”,這個是征途的思想,也帶到我們這裡。功能策劃負責整體功能的實現,伺服器、客戶端溝通,程式加班策劃也要加班,功能失敗也是先找策劃。當時程式還在打基礎,策劃有大量的時間,但是也沒閒著,主策讓他們寫功能文件,越詳細越好,經常幾個策劃無聊在比誰的功能文件寫的全。

伺服器端第二次重構

重構源於分散式伺服器端,碰到問題:如果保證玩家在所有伺服器登陸的一致性?也就是說:如何不卡號。一開始的登陸流程是:1.玩家登陸Gate。2.登陸GameServer,GameServer讀取DB資料。3.GameServer通知MapServer玩家登陸並且把DB資料給MapServer。

我們的客戶端測試工具是我自己寫的模擬機器人,模擬器沒有讀條過程,所有很容易暴露問題,我們發現了以下問題:

問題1.因為讀DB需要時間,MapServer會在沒有收到GameServer玩家登陸訊息的時候,收到玩家發來的移動訊息。當玩家頻繁重登的時候,這個玩家甚至有可能收到他上次登入發來的封包。

問題2:切換地圖邏輯異常麻煩。切換地圖邏輯需要做的事情:1.詢問對方地圖是否接受。2.傳送到對方地圖。3.完成傳送,改變玩家在GameServer上面註冊的MapID。這個邏輯是非同步的,任何環節都要處理異常,所有封包都走GameServer。

因為所有封包都會通過GameServer,GameServer負責了太多的狀態變數導致邏輯很不清晰,不把邏輯梳理清晰,肯定會引發重大問題。

於是有了第二次重構,重構的技術改動是:

改動1. GameServer拆分為RouterServer和GameServer,所有伺服器都會連線RouterServer,GameServer只負責世界邏輯。

改動2. RouterServer增加協層的概念,目的是處理像登陸或者切換地圖這種需要多臺伺服器互動的關係,把切換地圖做成“事物”,其他伺服器再也不用關係切換地圖的異常處理問題。

因為增加了一臺Server,伺服器改動比較大,前後大概有2個星期的時間成型。

美術部關係

主美不是一個領導力很強的人,在安逸的環境中不會暴露他的問題,但是在公司現在這種生死未卜的環境中,他不能給他的美術以信心和力量,導致美術部集體抗議專案獎金髮得過慢的問題,最終導致集體“請假”。對此,公司老闆非常火大也非常無耐,主策和老闆泡了一會茶,大概意思是說:要忍耐,要有信仰。對於主美,這是能力問題,不是他的錯,必須幫他扛起這塊地,而現在來講,只要美術都沒辭職,就將就用吧。所以在專案開發日夜顛倒的灰暗歲月中,美術是從來不加班的。程式每每都是在美術部正常下班的時候,雙眼發綠光地盯著離去的人發呆,因為程式還將繼續戰鬥8個小時左右。

這一段開發是一段黑暗的日子,對程式的整體認識都被翻了一遍,公司的很多庫函式也因為無法滿足效能需求而優化重寫,作為技術長進來講,的確是一段脫胎換骨的日子,作為回憶來講,只會感受到痛苦。

現在程式基礎基本OK,可以開始大規模開發。此時客戶端還停留在引擎階段,沒有真正的做遊戲,伺服器端一直用自己做的模擬器測試。

第二MileStone開始

程式的情況漸漸明晰,策劃,美術都已到位。預計第一MileStone3個月搞定,實際上用了5個月。第二MileStone的任務很簡單:做到10級。其中包括:客戶端啟動開發,製作一個地圖,主線任務,怪物AI,AOI,尋路,物品,商人,玩家和玩家交易。

當時開發流程中沒有專案管理軟體,開發流程很簡單:貼條。策劃自己給自己貼條,然後給程式貼條。客戶端程式由於剛剛啟動,前途未卜,還無法真正開發,所以開發程式也就只是指我一個人。後來大家覺得因為只有一個程式,貼條好像沒啥意義,所以統一直接找我口頭溝通了。

客戶端開始,狀態機的悲劇

客戶端程式也是一個人,老闆對客戶端程式的評價是:不撞南牆不回頭。所以當老闆做了錯誤的決定的時候,客戶端程式也拼命衝上去做了。這個錯誤是:使用狀態機來管理主角狀態。當時的狀態有接近30個,比如:待機、走路、跳躍準備、跳躍中、落下準備、攻擊待機、攻擊中、攻擊關鍵幀等等……,用了一個自己做的工具來生成狀態機程式碼,客戶端程式在不斷地考慮狀態轉換關係,而且每新增一個動作,就需要考慮這個動作和所有其他動作的轉換關係。客戶端程式本身就不是一個邏輯很清晰的人,於是發生了一次又一次的悲劇,在黑白顛倒的工作壓力下,客戶端程式已經完全崩潰,會出一些非常低階的問題。有一次客戶端程式隨意的給enum定義變數賦值而且賦值重複了,結果導致查了一通宵的問題。還有一些關聯性問題:比如我們跳躍的格子數是2格,為什麼每次玩家都會跳4格?一個通宵後找到問題:因為美術做跳躍動畫的時候,向前移了2格。

最後,終於客戶端程式主動辭職。由製作場景編輯器的人來繼續客戶端的開發,事實上專案沒有因為客戶端程式的離職導致太大問題。

策劃數值定調

這個遊戲的面向使用者不會很高階,所以一定不能做得太麻煩,於是定死了幾條規則:1.做攻減防體系。2.不做任何強控制技。攻減防體系是說計算傷害的時候,直接用攻擊方的攻擊力減去防禦方的防禦力得到總傷害,而不是像魔獸體系一樣,防禦做成百分比。後來的經驗證明攻減防體系是不對的,因為數值會飄,比如一個100點攻擊力的人攻擊防禦50點的人,傷害是50點;但是加了雙倍傷害之後,攻擊方變成200點攻擊力,對防禦50的人造成的傷害是150點,總傷害翻了3倍,所以一般pk的時候,誰先造成致命誰贏。強控制技是說擊暈等讓對方完全無法控制的技能,不做強控制技是為了防止若干個免費玩家通過操作能成功玩死一個終極裝備玩家。

python框架定調

採用的方法是資料在c++中,用swig做轉換介面轉換給python用,每一個封包到達會呼叫相應的python函式,python從c++的臨時資料中取出當前封包並且處理。整個專案都是基於這個在做下去,但是很後來的時候效能測試才發現,最消耗的地方不是在pyhton執行層,而是在python和c++介面層,因為資料全部在c++中,python大量使用c++介面,導致整體效能低下,至今為止,這個問題還無法完美解決。

任務框架制定

制定任務框架的時候參考了2個原始碼:一個是天堂II伺服器端原始碼,一個是征途洩露出來的原始碼,它們都有一個特點:任務系統使用xml體系來描述。天堂II的任務是html的,可以直接用瀏覽器開啟並且能夠看到對話和連結;征途的簡化一點,是xml描述的。這個發現給了人很大的啟發:任務系統和做網頁本質上是一樣的。於是我們的任務系統也決定這麼做,最終寫起來的感覺讓人覺得在做asp頁面。

任務體系最終的形態是:一個任務一個目錄,目錄下面是事件目錄,事件目錄裡面是以NPCID為檔名的xml處理,比如如果玩家有ID為1的任務,需要和NPCID為100的NPC對話才能觸發事件,那麼目錄結構是:目錄(名稱1)下面有子目錄(名稱on_npc),下面有檔案(100.xml),這麼做的好處是:如果以後有某個任務壞了導致會有死鏈或者刷任務,我們可以直接刪除任務這個目錄,不會影響其他的任務的開發。

python程式招聘和整個系統的重新設計

我們從遊戲開發培訓學校招了2個學員來試用,雖然這2個人當時沒有對開發產生多大的推動作用,但是引入了多人開發這個概念,整個框架定調都和以前完全不同,這對整個專案有很積極的作用。比如以前我們寫一個技能,考慮的實現方法是儘量重用,儘量使程式碼簡化並且對編碼者易維護,但是這樣的副作用是過於抽象會讓程式碼變得難懂,只用開發者自己才能夠維護,而且很難找到某個功能的負責人,只能由我全權負責。後來的技能寫法轉變為一個技能一個python檔案,如果某個技能出問題了,也可以很輕易的找到功能負責人,更好的是,如果一個技能有bug,直接刪除這個技能指令碼即可,不會對整個系統有任何負面影響。上面說的任務框架,技能系統,物品系統全部是根據一個原則來設計:出了錯誤可以馬上被替換。這個思想的轉變給人的感覺是打war3對戰和打星際對戰的區別,一個是英雄戰,講究個人風範,一個是群戰,講究分兵佈陣。

客戶端python化

伺服器python化的經驗證明,開發速度上面,python速度比c++速度快4倍,並且寫出來的東西清晰易懂,所以這個經驗也用到了客戶端上面,一開始是用在做介面上,客戶端的介面框架是自己實現的,要增加一個介面,首先需要用c++ builder畫好介面,然後把介面程式碼dfm檔案放到客戶端中,客戶端解析這個介面檔案,然後找到相應的python程式碼來處理邏輯,客戶端大的思想是一個介面一個py,並且各個介面儲存各個介面自己的資料,相互獨立。這麼做的好處是功能劃分清晰,壞處是對於同一個資料重新整理,不同的介面如果想取得同樣的資料的話,需要2份一樣的讀取程式碼,比如如果有2個組隊資訊介面,每個介面都需要單獨讀取組隊資訊封包,管理自己的組隊資料。但是,總的說來,當時的這個選擇是好處大於壞處

MileStone3

目標是做到40級,並且做副本系統。就是說一個MapServer可能會跑多個副本,每個函式的處理都需要加上GameWorld引數,標識自己在哪個副本中,當時的做法很簡單:多個副本在同一個執行緒中跑,每跑一個副本的時候,都會改變current_game_world變數,在邏輯處理的時候,通過current_game_world可以得到當前的遊戲世界。這麼做的好處是所有邏輯都不用改,壞處是跨服邏輯的處理:其他伺服器無法知道這個玩家在哪個副本中,所以只好統一在0號副本處理跨服邏輯,這個變成了一個潛規則,而且每個開發者都會因為這個潛規則而糊塗一陣。

python程式繼續招聘

python程式已經開始大量招聘,都是像那一家遊戲培訓學校要人,工資開得都很低,但是這些人都發揮了很重要的作用,他們特點總結說來:1.聽話。2.熱情。筆者始終認為這2個特點是一個合格新手的必備條件。後來在這些python程式中成長起來一批人,至今筆者還非常認同,不比任何一個大學的畢業生差。

至此,專案各項流程都已經初步確定,工作可以有序展開,下一步是最重要的:推向市場。

尋找買家

為了找到好的買家,公司上了一次CJ展會,果真發現了不少商機,有很多大公司都提出意向,但是開出的條件非常苛刻,不過老闆還是去周邊走了一趟,順便見了見國內的各個大小開發商。比較有印象的是3家公司:盛大,約18號去面談,最終談失敗了,因為還不想加入18計劃;騰訊,因為有個巨大的平臺,所以幾乎沒有開出什麼利潤條件;巨人,這個公司對我們很有幫助,產品總監過來看了一下,直說我們的產品還很次,不夠檔次。於是我們鬱悶了很久,最終下決定美術工作重翻。

在美術工作重翻的時候,我們也放棄了在大陸能夠找到好買家的期望,因為畢竟是一個很新的公司,沒人瞭解。於是找到了臺灣的10年“合作”夥伴:臺灣網龍,網龍那邊很爽快就答應了,但是要求任務系統重翻一次。於是生意一拍即合,我們開始繁體化介面了。

大陸買家

在確認了臺灣網龍會代理後,在大陸這邊找代理商就容易多了,看來各個代理商都是在等待對方先吃螃蟹。最終大陸確認由光宇代理,對公司來講,終於,拖了1年多的獎金總算髮下來了一點點。

大陸封測

光宇的地推很給力,封測總共5組伺服器,他們保證每組伺服器都滿載6000人在跑,但是進來玩的人似乎不都是玩家,整個封測過程大家都是掛機自動打怪,沒有任何社會活動在裡面,也沒出任何問題,封測非常順利。

大陸內測

封測的順利讓光宇決定:內測直接開商城。大家都知道,現在的遊戲實際上已經沒有內測封測的說法了,開商城就意味著遊戲正式運營。我們公司對這件事很沒底,伺服器沒有故障過更覺得沒底,但是來不及思考,內測開始了,當時的感受只能用一句很流行的話來說:Hell, It's about time。

內測總共出的問題列舉如下:

串號:一個玩家錯誤的登陸到另外一個玩家的號上面去了,是因為我們沒有用sessionID作為封包識別符號。

卡號:通常是因為MapServer報錯,導致玩家在Router上面一直是登陸狀態,後來在Router上面加了ping包踢人機制,但是仍然還有這個問題,因為當router判定需要踢人的時候,玩家在MapServer上出錯就無法下線,否則回檔。

回檔:都是因為伺服器出錯而導致的,解決方法是每個MapServer每2分鐘會把本地圖的所有玩家資料寫到本地儲存檔案。每次開發的時候,如果發現本地儲存檔案有玩家資料資訊,則把玩家資料寫回資料庫。

覆檔:這個是上面的解決方案導致的,如果2個地圖都有同一個玩家的本地快取資料,那麼可能發生舊的玩家資料把新的玩家資料覆蓋的情況。解決方法是每個玩家進入地圖的時候,都必須觸發一次本地快取,這樣玩家資料可以保證唯一。

清檔:運營商那邊要求:為了防止運營商內部人員修改遊戲資料,資料庫中的資料都必須做一個雜湊標識,玩家登陸的時候,如果根據資料中的資料計算的雜湊標識和已經儲存的雜湊識別符號不匹配,則出錯處理。在出錯處理中,如果玩家身上的物品雜湊標識不對,那麼就直接刪除此物品。這個是一個非常嚴重的失誤,某次遊戲更新,我們給物品表新增了新欄位,但是忘記了雜湊錯誤處理這回事,結果放到公網上,每個登陸的玩家的物品都被清空,伺服器開了半個小時立即全部關服,但是已經有4000多個玩家的資料遭到損害。更悲慘的是,上週運營商那邊忘記了做資料備份,所以我們的解決方式是:1.根據上上週的資料備份恢復丟掉裝備的玩家資料。2.根據2周的系統日誌來補償玩家物品。3.給出相應補償。整個過程持續了28個小時,提供了8個版本的恢復工具給運營商,最後回到家的時候,我連開門的鑰匙扣都找不到在哪裡。

刷錢:當時有個bug:比如一個玩家的賬號名是account,他用賬號名Account和賬號名account能夠登入同時到router,也就是說,router判定重登的時候沒有區分賬號的大小寫,導致的問題是玩家可以同時登陸2個同樣的角色到選人介面,Account賬號進入遊戲,把錢全部給小號,下線,然後account賬號再下線,這樣後下線的賬號儲存資料會蓋掉前面登陸賬號的儲存資料,達到刷錢的目的。這個bug的發現過程非常複雜,很多情況下只能靠聯想去思考,運營商那邊一直懷疑自己有內鬼,當發現最後的真相的時候,我們都很汗顏。

很難登陸:我們物品表的組織結構是一個物品一條記錄,導致一組伺服器運營半年後,物品表有一千多萬條記錄,玩家登陸一次,讀取30個物品需要的時間竟然需要30秒。經過排查發現,物品表的索引欄位定得過多導致讀取寫入物品都非常慢,去掉了索引欄位後,問題緩解。不久之後問題繼續,因為資料量實在太大了,所以我們對物品表做了一個系統性的改動:用二進位制形式儲存玩家的所有物品,一個玩家一行記錄,這樣才根本的解決了讀取過慢的問題,但是副作用是我們不能隨意的查詢玩家物品資料。

每日任務重複觸發:因為當時每日任務觸發機制做在MapServer上,而各個MapServer硬體的時間有些微妙的不一樣,所以會發生這樣的情況:一個玩家在Map1上面走道00:00分觸發每日任務,然後切換地圖去Map2,Map2的時間是11:59分,所以Map2走到00:00分的時候,又會再次觸發每日任務。這個問題的解決方法是:1.玩家資料中記錄下來當前觸發每日任務的日期,用作防止重複觸發判定。2.伺服器整體時間由GameServer來同步,MapServer禁止取得本地時間,防止再次發生類似悲劇。

客戶端本地檔案損壞:這個bug也隱藏得非常深,因為客戶端用記憶體檔案對映開啟本地檔案的模組沒有用只讀方式開啟,所以客戶端某一個野指標可能會寫到本地檔案然後儲存,結果是客戶端本地檔案莫名其妙不能用了,只能完全重新下載重灌。排查這種bug和推理小說中的偵探做事方法差不多,每次查到問題,總會有一種自虐的成就感。程式設計師最大的敵人是自己。

整個測試過程中,因為我們沒有提供很好的遊戲查詢方式給運營商,所以比如說要找到一個伺服器花錢最多的人,都是由我在公網資料庫中執行select操作實現的,我可以直接訪問運營極,並且好幾次是由我來公網開服,客服很多問題也是由我來解答,整個運營體系極其混亂。

臺灣內測

臺灣在內測前提供以下幾個文件:log介面和log格式,充值介面。log介面總共定了200多種日誌型別,規則很清晰,並且導致我們對所有程式碼都翻了一遍才加上完備的log。對接成功之後,整個遊戲上線了。我們這邊沒有伺服器的訪問許可權,但是整個運營過程幾乎沒出什麼問題,臺灣那邊很少打電話過來,只是看到他們的電視廣告一直打,整個遊戲很火,據臺灣說同時線上最高有3萬人。唯一記得的一個bug是:一次我們改動了物品拖動邏輯,導致在某些情況下會複製物品。臺灣因這個bug損失很大,他們重整了自己的很多QC流程,後來才知道,臺灣網龍的代理流程是把遊戲版本拿過來,先QC測試,QC保證這個產品的質量,如果出問題,第一責任人是QC。

臺灣的成功來源於2點:1.大陸先上線,很多雷被踩過了。2.制度規範,介面規範,運營商很清晰,不會有惡性迴圈。

臺灣計費

雖然臺灣產品很火,老闆卻不太高興,因為充值計費全部是走的運營商介面,並且玩家在不登陸游戲的時候也可以給遊戲充值,所以我們這邊完全無法知道玩家確切的充值資料,也就是說,運營分成上面完全沒有主動權。

大陸線上人數跳水

這個是預料之中的,因為程式這邊的crash已經讓所有人都崩潰。

人數穩步下滑

在這個時候,玩家對這款遊戲的新意已經過去,沒有玩點,粘稠度再強、再強調社交都沒有用,我自己來講,也是每日任務做完就下線了,pvp只是有錢人的玩意,而pve的boss又必須花錢去買才能殺,對於不喜歡用錢玩遊戲的人,幾乎無法生存,沒有了羊的世界,狼也慢慢消亡了。這是我個人總結的經驗。

小結

至此,一款網遊完整的生死已經結束,通過這次開發得到了不少經驗也成熟了不少,同時身體也垮了不少。總的說來,“我做成了一件事情”這句話還是挺有成就感的。