Unity手游開發(fā)札記——我們是如何使用Lua來開發(fā)大型游戲的幅狮?(上)

粘貼過來的原因闯估,代碼比較亂,知乎原文傳送門:https://zhuanlan.zhihu.com/p/34660501

0. 照舊的碎碎念

轉(zhuǎn)眼間已經(jīng)三月了供炼,2月份的博客因為過年的懶惰和開年之后的忙碌而沒有寫……第二個月就打破了去年總結(jié)時對于2018年的愿望一屋,真是羞恥呢……

年后在準備新的測試版本,斷斷續(xù)續(xù)做了一些優(yōu)化袋哼,更多的精力放在團隊的績效評估冀墨、溝通這樣偏管理的事物上,說實話技術(shù)上可以聊的東西不多涛贯。近期看到UWA群里和問答上聊Lua的使用之類的話題比較多诽嘉,也在看ET這套完全基于C#進行游戲開發(fā)的框架中提到——

“在發(fā)布的時候,定義預(yù)編譯指令I(lǐng)LRuntime就可以無縫切換成使用ILRuntime加載熱更新動態(tài)庫。這樣開發(fā)起來及其方便虫腋,再也不用使用狗屎lua了骄酗。”

Lua是門小而精的語言悦冀,它的確很多地方像狗屎一樣……比如只提供table這樣一種數(shù)據(jù)結(jié)構(gòu)趋翻,而且基于數(shù)組域和哈希域的封裝讓#這樣的操作符號可以坑死不少新手甚至老司機,一個哈希表要取長度還要自己封裝一個遍歷函數(shù)等等諸多不便的地方盒蟆。

我們項目深度使用了Lua踏烙,原因其實在1年多前的一篇文章里已經(jīng)有聊過——《Unity手游開發(fā)札記——Lua語言集成》,有興趣的朋友可以再去看看历等。那篇文章也聊了最初對于一些框架上的改造讨惩,而今天這篇文章我想聊聊我們團隊是如何使用Lua來開發(fā)大型游戲的。一方面讓大家看看我們是如何把Lua這個“狗屎”募闲,捏成巧克力的形狀甚至做出一點點巧克力的味道步脓;另外一方面愿待,也想為糾結(jié)是否使用Lua來做Unity的代碼更新方案的朋友提供一些做決策的參考浩螺。

1. 我的觀點

在聊一些更加具體的經(jīng)驗之前,我想先把我自己的觀點拋出來仍侥,這也是我花時間寫這篇文章最想表達的兩點內(nèi)容:

使用Lua這樣的腳本語言要出,目的不僅僅在于讓代碼可以被Patch更新,而且讓游戲邏輯可以被Hotfix更新农渊。

使用Lua這樣的腳本語言患蹂,調(diào)試bug的效率并不低,甚至可能比C#這樣的靜態(tài)語言還要高砸紊。

先聊下第一點传于,我看很多朋友在聊的時候不斷提到客戶端的熱更新,可能每個人或者公司有自己不同的叫法醉顽,在我的觀點里沼溜,通過在游戲啟動的時候下載新的資源文件替換之前的文件,讓游戲不需要重新安裝就可以更新內(nèi)容的方式叫做“Patch更新”游添,而不是熱更新(Hotfix)系草。

在我的理解中,熱更新(Hotfix)的概念從服務(wù)端來講唆涝,是指不停止服務(wù)的情況下進行的更新找都,此時如果玩家正在進行游戲,玩家是無感知的廊酣,最多感覺到一點頓卡之類的能耻。而對于客戶端來說,玩家正在進行游戲,這時候如果需要玩家退出到登陸界面重新下載Patch內(nèi)容再進入游戲嚎京,打斷了玩家的游戲體驗嗡贺,根本就不能稱之為“熱”更新,雖不至于是冷更鞍帝,最多是“溫”更新……

腳本語言讓游戲邏輯和數(shù)據(jù)可以做到玩家無感知的情況下進行錯誤的修復(fù)诫睬,比如有一個trace導(dǎo)致了玩家某個系統(tǒng)的界面打開后內(nèi)容顯示錯誤,Hotfix應(yīng)用之后帕涌,玩家下次打開這個界面的時候摄凡,trace就已經(jīng)被修復(fù)了,內(nèi)容顯示正確蚓曼,而玩家完全沒有任何更新的感知亲澡,這種才能叫做真正的客戶端熱更新

第二點纫版,有些朋友認為腳本語言只能通過打log進行調(diào)試床绪,是一件非常痛苦的事情。首先其弊,Python和Lua這樣的腳本語言都有各自的調(diào)試工具癞己,可能沒有那么便利,但基本功能是夠用的梭伐;其次痹雅,在移動網(wǎng)絡(luò)游戲的開發(fā)中,有網(wǎng)絡(luò)因素糊识、異步邏輯绩社、設(shè)備上運行等存在的情況下,有些bug是很難單步調(diào)試來進行重現(xiàn)和分析的赂苗,這種情況下log調(diào)試必不可少愉耙,而且我認為通過分析代碼邏輯精準地添加log快速定位問題并修復(fù)問題的能力,是每一個程序員應(yīng)該掌握的基本技巧拌滋;最后朴沿,結(jié)合動態(tài)語言的reload功能,即使是使用log調(diào)試鸠真,也有很高效的方法悯仙,在加上內(nèi)存查看工具,可以做到很高效的bug定位和修復(fù)吠卷。

這里只是先闡述一下我個人的觀點锡垄,下面我將根據(jù)實際的項目經(jīng)驗來聊聊我們使用Lua的一些方面。

2. 讓Lua代碼更好寫

Lua自身提供的功能很精簡祭隔,精簡也意味著它在很多方面會有些“殘疾”……這會導(dǎo)致團隊的開發(fā)效率比較低货岭,因此必須通過一些基礎(chǔ)內(nèi)容的構(gòu)建來讓團隊更好地使用Lua語言路操。需要注意的是,天下沒有免費的午餐千贯,更快的開發(fā)效率有很多時候意味著更慢的運行效率屯仗。

2.1 全局變量訪問控制

Lua的設(shè)計中有一個特點就是:

當(dāng)你不在變量前使用local關(guān)鍵字的時候,這個變量會被放在_G這個全局表中搔谴。

我在最初學(xué)習(xí)Lua的時候也很難理解這個設(shè)計魁袜,這和之前我使用的編程語言中作用域的概念是相違背的,但是當(dāng)你理解函數(shù)的env概念之后敦第,就很容易理解為什么在Lua語言中峰弹,這樣的設(shè)計反而是最為合理和自洽的。

對于Lua語言自身來說芜果,這種合理和自洽是美的鞠呈,但是它會給使用的人帶來困惑和難以排查的bug,因為你非秤壹兀可能因為遺漏的local聲明蚁吝,導(dǎo)致污染了_G,甚至修改到了了你不想修改的變量舀射,或者你的某個變量被別處的代碼不小心修改了窘茁。因此在我們的工程中,去掉了Lua的這一特性后控,當(dāng)期望使用一個局部變量但是沒有寫local變量的時候庙曙,使用error報出錯誤空镜,所有的全局變量必須顯示地進行聲明浩淘。

實現(xiàn)方法很簡單,重寫_G的__index方法和__newindex方法:

-- Global.lua-- 輔助記錄全局變量的名稱是否被使用過local_GlobalNames={}localfunction__innerDeclare(name,defaultValue)ifnotrawget(_G,name)thenrawset(_G,name,defaultValueorfalse)elseprint("[Warning] The global variable "..name.." is already declared!")end_GlobalNames[name]=truereturn_G[name]endlocalfunction__innerDeclareIndex(tbl,key)ifnot_GlobalNames[key]thenerror("Attempt to access an undeclared global variable : "..key,2)endreturnnilendlocalfunction__innerDeclareNewindex(tbl,key,value)ifnot_GlobalNames[key]thenerror("Attempt to write an undeclared global variable : "..key,2)elserawset(tbl,key,value)endendlocalfunction__GLDeclare(name,defaultValue)localok,ret=pcall(__innerDeclare,name,defaultValue)ifnotokthen--? ? ? ? LogError(debug.traceback(res, 2))returnnilelsereturnretendendlocalfunction__isGLDeclared(name)if_GlobalNames[name]orrawget(_G,name)thenreturntrueelsereturnfalseendend-- Set "GLDeclare" into global.if(not__isGLDeclared("GLDeclare"))or(notGLDeclare)then__GLDeclare("GLDeclare",__GLDeclare)end-- Set "IsGLDeclared" into global.if(not__isGLDeclared("IsGLDeclared"))or(notIsGLDeclared)then__GLDeclare("IsGLDeclared",__isGLDeclared)endsetmetatable(_G,{__index=function(tbl,key)localok,res=pcall(__innerDeclareIndex,tbl,key)ifnotokthenlogerror(debug.traceback(res,2))endreturnnilend,__newindex=function(tbl,key,value)localok,res=pcall(__innerDeclareNewindex,tbl,key,value)ifnotokthenlogerror(debug.traceback(res,2))endend})return__GLDeclare

我相信這種強制報錯的設(shè)定可以幫助很多剛剛上手Lua的朋友避免一些錯誤吴攒。上述的代碼也是參考網(wǎng)上的開源工程张抄,需要用的朋友可以直接拿去。

2.2 Class的設(shè)計

雖然面向?qū)ο蟮脑O(shè)計在很多帖子的討論中已經(jīng)過時的洼怔,面向切面編程等等新概念不斷被提出署惯,但是對于一個需要團隊協(xié)作的游戲項目來說,面向?qū)ο蟮脑O(shè)計依然是目前最為常用的邏輯實現(xiàn)方式镣隶。Lua自身沒有Class的概念极谊,提供了metatable來做繼承,但很弱安岂。我們在項目最初的時候就構(gòu)建了Class的機制轻猖,來方便代碼的編寫。雖然和原生支持Class的Python和C#這樣的語言相比易用性和功能上還都有差距域那,但是基本夠用了咙边。

直接提供核心代碼如下:

-- Class.lua-- 類定義,不支持多重繼承l(wèi)ocalGLDeclare=require"Framework/Global"-- 所有定義過的類列表,key為類的類型名稱败许,value為對應(yīng)的虛表local__ClassTypeList={}-- 類的繼承關(guān)系數(shù)據(jù)王带,用于處理Hotfix等邏輯。-- 數(shù)據(jù)形式:key為ClassType市殷,value為繼承自它的子類列表愕撰。local__InheritRelationship={}localfunction__createSingletonClass(cls,...)ifcls._instance==nilthencls._instance=cls.new(...)endreturncls._instanceendlocalTypeNames={}-- 參數(shù)含義為:-- typeName: 字符串形式的類型名稱-- superType: 父類的類型,可以為nil-- isSingleton: 是否是單例模式的類localfunction__Class(typeName,superType,isSingleton)-- 該table為類定義對應(yīng)的表localclassType={__IsClass=true}-- 類型名稱classType.typeName=typeNameifTypeNames[typeName]~=nilthenlogerror("The class name is used already!!!"..typeName)elseTypeNames[typeName]=classTypeend-- 父類類型classType.superType=superType-- 在Class身上記錄繼承關(guān)系-- Todo:在修改了繼承關(guān)系的情況下醋寝,Reload和Hotfix可能會存在問題classType._inheritsCount=0ifsuperType~=nilthenlocalcache={}localcounter=1localcurClass=superTypewhilecurClassdocache[counter]=curClasscounter=counter+1curClass=curClass.superTypeendclassType._classInherits=cacheclassType._inheritsCount=counterendclassType._IsSingleton=isSingletonorfalse-- 記錄類的繼承關(guān)系ifsuperTypethenif__InheritRelationship[superType]==nilthen__InheritRelationship[superType]={}endtable.insert(__InheritRelationship[superType],classType)else__InheritRelationship[classType]={}endclassType.ctor=falseclassType.dtor=falselocalfunctionobjToString(self)ifnotself.__instanceNamethenlocalstr=tostring(self)local_,_,addr=string.find(str,"table%s*:%s*(0?[xX]?%x+)")self.__instanceName=string.format("Class %s : %s",classType.typeName,addr)endreturnself.__instanceNameendlocalfunctionobjGetClass(self)returnclassTypeendlocalfunctionobjGetType(self)returnclassType.typeNameend-- 創(chuàng)建對象的方法盟戏。classType.new=function(...)-- 該table為對象對應(yīng)的表localobj={}-- 對象的toString方法,輸出結(jié)果為類型名稱 內(nèi)存地址甥桂。obj.toString=objToString-- 獲取類obj.getClass=objGetClass-- 獲取類型名稱的方法柿究。obj.getType=objGetType-- 遞歸的構(gòu)造過程localcreateObj=function(class,object,...)-- 優(yōu)化遞歸過程中的函數(shù)調(diào)用ifclass.superType~=nilthenfori=class._inheritsCount-1,1,-1dolocalcurClass=class._classInherits[i]ifcurClass.ctorthencurClass.ctor(object,...)endendendifclass.ctorthenclass.ctor(object,...)endend-- 設(shè)置對象表的metatable為虛表的索引內(nèi)容setmetatable(obj,{__index=__ClassTypeList[classType]})-- 構(gòu)造對象createObj(classType,obj,...)returnobjend-- 類的toString方法。classType.toString=function(self)returnself.typeNameendifclassType._IsSingletonthenclassType.GetInstance=function(...)return__createSingletonClass(classType,...)endendifsuperTypethen-- 有父類存在時黄选,設(shè)置類身上的super屬性classType.super=setmetatable({},{__index=function(tbl,key)localfunc=__ClassTypeList[superType][key]if"function"==type(func)then-- 緩存查找結(jié)果-- Todo蝇摸,要考慮reload的影響tbl[key]=funcreturnfuncelseerror("Accessing super class field are not allowed!")endend})end-- 虛表對象。localvtbl={}__ClassTypeList[classType]=vtbl-- 類的metatable設(shè)置办陷,屬性寫入虛表貌夕,setmetatable(classType,{__index=function(tbl,key)returnvtbl[key]end,__newindex=function(tbl,key,value)vtbl[key]=valueend,-- 讓類可以通過調(diào)用的方式構(gòu)造。__call=function(self,...)-- 處理單例的模式ifclassType._IsSingleton==truethenreturn__createSingletonClass(classType,...)elsereturnclassType.new(...)endend})-- 如果有父類存在民镜,則設(shè)置虛表的metatable啡专,屬性從父類身上取-- 注意,此處實現(xiàn)了多層父類遞歸調(diào)用檢索的功能制圈,因為取到的父類也是一個修改過metatable的對象们童。ifsuperTypethensetmetatable(vtbl,{__index=function(tbl,key)localret=__ClassTypeList[superType][key]-- Todo 緩存提高了效率,但是要考慮reload時的處理鲸鹦。vtbl[key]=retreturnretend})endreturnclassTypeend-- 判斷一個類是否是另外一個類的子類localfunction__isSubClassOf(cls,otherCls)returntype(otherCls)=="table"andtype(cls.superType)=="table"and(cls.superType==otherClsor__isSubClassOf(cls.superType,otherCls))endif(notIsGLDeclared("isSubClassOf"))or(notisSubClassOf)thenGLDeclare("isSubClassOf",__isSubClassOf)end-- 判斷一個對象是否是一個類的實例(包含子類)localfunction__isInstanceOf(obj,cls)localobjClass=obj:getClass()returnobjClass~=nilandtype(cls)=='table'and(cls==objClassor__isSubClassOf(objClass,cls))endif(notIsGLDeclared("isInstanceOf"))or(notisInstanceOf)thenGLDeclare("isInstanceOf",__isInstanceOf)endif(notIsGLDeclared("Class"))or(notClass)thenGLDeclare("Class",__Class)endreturn__Class

這個Lua的Class實現(xiàn)也有參考網(wǎng)上的開源代碼慧库,做了一些自己的改進,主要功能有:

只支持單繼承馋嗜;

原生支持單例齐板,但注意,對于不需要繼承的單例葛菇,比如一些常用的Manager甘磨,其實不推薦使用Class的方式,而是直接使用Lua的Table的形式來做效率更高眯停;

支持super來調(diào)用父類的方法济舆,但是調(diào)用的時候必須使用ClassName.super(self, ...)這樣的方式來顯示地把self傳遞給父類,否則父類拿到的self會是錯誤的對象庵朝;

支持構(gòu)造函數(shù)ctor吗冤,但是這在某些想自動控制構(gòu)造的情況下也是一把雙刃劍……

對于多重集成沒有提供原生支持又厉,本來是可以的,但是多重集成有自身的問題椎瘟,我們提供了一種基于Mixin 的思路來處理覆致,類似于Interface,核心目標功能是合并一些函數(shù)到一個Class中肺蔚,提供一些大類的模塊拆分煌妈,避免出現(xiàn)一個幾千甚至上萬行代碼的類文件。(之前端游項目中宣羊,幾萬行的py文件都有遇到……當(dāng)時eclipse這樣的IDE打開這樣的py文件都要好久……)

-- 將一個table中所有的屬性和方法合并到一個class中璧诵,用于處理一個類比較大的設(shè)計-- 注意,合并的方法的reload需要單獨處理localfunction__MixinClass(cls,mixin)assert(type(mixin)=='table',"mixin must be a table")forname,attrinpairs(mixin)doifcls[name]==nilthencls[name]=attrelse-- 屬性名稱相同不覆蓋而是給出警告仇冯。print(string.format("[WARNING] The attribute name %s is already in the Class %s!",name,cls.toString()))endendendif(notIsGLDeclared("MixinClass"))or(notMixinClass)thenGLDeclare("MixinClass",__MixinClass)end

2.3 常用函數(shù)庫的補充

這一部分是自己來彌補Lua語言函數(shù)庫不豐富的問題之宿,當(dāng)然也要看項目需求,我們引入的主要有:

table相關(guān)的一些操作函數(shù)苛坚,包括長度獲取比被、dump為字符串、深淺拷貝泼舱、深度對比等缀、根據(jù)值獲得索引等等;

json庫娇昙;

int64庫(用的是Lua 5.1)尺迂;

bit操作庫;

Lua socket庫冒掌;

……

這部分跟項目具體需求相關(guān)噪裕,就不一一列舉和給出代碼了。

2.4 IDE

IDE的部分也只說幾句宋渔,我們團隊目前用的比較多的是Sublime Text 3和VS Code州疾,最初我個人還在使用VS+插件的形式辜限,后來也轉(zhuǎn)向了VS Code陣營皇拣。

個人體驗VS Code還是比較不錯的,加上一些自動補全和基于LuaChecker的語法檢查插件薄嫡,基本能夠保證避免開發(fā)中一些很蠢的bug氧急。

如果需要,可以自己導(dǎo)出一下Unity的接口為一個Lua的文件毫深,提升自動補全的體驗吩坝,比如我們最初導(dǎo)出的一份U3DAPI.lua的部分內(nèi)容截取示例如下:

--- --- 全名:UnityEngine.Camera.depthTextureMode [讀寫] --- 返回值 : DepthTextureMode--- --- Camera.depthTextureMode=function()end--- --- 全名:UnityEngine.Camera.clearStencilAfterLightingPass [讀寫] --- 返回值 : Boolean--- --- Camera.clearStencilAfterLightingPass=function()end--- --- 全名:UnityEngine.Camera.commandBufferCount [讀寫] --- 返回值 : Int32--- --- Camera.commandBufferCount=function()end

2.5 培訓(xùn)和分享

我們團隊的同學(xué)大都有多年使用Python的經(jīng)驗,但是對于Lua還是需要上手時間哑蔫,所以在最初的時候就組織了程序內(nèi)部的Lua培訓(xùn)和分享钉寝,把比如對于table和string使用的坑弧呐、元表、Lua的GC基本原理嵌纲、錯誤處理等等方面在團隊內(nèi)部進行了統(tǒng)一的學(xué)習(xí)和討論俘枫,整體的收獲還是比較大的。在開發(fā)過程中發(fā)現(xiàn)的代碼上的問題逮走,也及時在群內(nèi)進行討論鸠蚪,這些都逐步提高了整個團隊使用Lua進行游戲開發(fā)的能力和效率。

2.6 小結(jié)

Lua語言自身的確是有很多易用性上的問題师溅,前文提到的庫不夠豐富之類的茅信,通過在項目初期添加一些基礎(chǔ)的結(jié)構(gòu)和庫,再加上一些提前規(guī)避錯誤的強制手段墓臭,可以一定程度上改善易用性的問題蘸鲸。然而,即使到現(xiàn)在窿锉,使用Lua有一年多的時候棚贾,我們團隊中還是偶爾有同學(xué)出現(xiàn).和:用錯導(dǎo)致bug的現(xiàn)象。用好一門語言總是需要一個不斷踩坑不斷成長的過程榆综,C#也好妙痹,Python也好,Lua也好鼻疮,都需要不斷地學(xué)習(xí)和改進怯伊,希望我們的一些經(jīng)驗和教訓(xùn)可以幫助剛剛上手Lua的團隊提前規(guī)避一些坑,也期望更多已經(jīng)熟練使用Lua的團隊可以分享你們經(jīng)驗和方法~

總是判沟,Lua這門小而精的語言耿芹,在提供了腳本語言中幾乎最快的運行效率的同時,也有著開發(fā)效率方面的各種問題挪哄,這些問題需要整個團隊的力量去彌補和改進吧秕。 我相信,經(jīng)過積淀的團隊迹炼,在使用Lua進行大型游戲的開發(fā)時砸彬,可以達到不差于任何其他語言的開發(fā)速度。

[未完待續(xù)]

2018年3月18日凌晨于杭州家中

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末斯入,一起剝皮案震驚了整個濱河市砂碉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌刻两,老刑警劉巖增蹭,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異磅摹,居然都是意外死亡滋迈,警方通過查閱死者的電腦和手機霎奢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來饼灿,“玉大人椰憋,你說我怎么就攤上這事∨馔耍” “怎么了橙依?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長硕旗。 經(jīng)常有香客問我窗骑,道長,這世上最難降的妖魔是什么漆枚? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任创译,我火速辦了婚禮,結(jié)果婚禮上墙基,老公的妹妹穿的比我還像新娘软族。我一直安慰自己,他們只是感情好残制,可當(dāng)我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布立砸。 她就那樣靜靜地躺著,像睡著了一般初茶。 火紅的嫁衣襯著肌膚如雪颗祝。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天恼布,我揣著相機與錄音螺戳,去河邊找鬼。 笑死折汞,一個胖子當(dāng)著我的面吹牛倔幼,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播爽待,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼损同,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了堕伪?” 一聲冷哼從身側(cè)響起揖庄,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎欠雌,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體疙筹,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡富俄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年禁炒,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片霍比。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡幕袱,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出悠瞬,到底是詐尸還是另有隱情们豌,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布浅妆,位于F島的核電站望迎,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏凌外。R本人自食惡果不足惜辩尊,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望康辑。 院中可真熱鬧摄欲,春花似錦、人聲如沸疮薇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽按咒。三九已至劳秋,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間胖齐,已是汗流浹背玻淑。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留呀伙,地道東北人补履。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像剿另,于是被迫代替她去往敵國和親箫锤。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Lua 5.1 參考手冊 by Roberto Ierusalimschy, Luiz Henrique de F...
    蘇黎九歌閱讀 13,810評論 0 38
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉雨女,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,720評論 0 9
  • 一夜驚雷谚攒,一夜雨, 似寐非寐氛堕。 曉來霽靄似霏馏臭, 一池濁水。 涼風(fēng)拂新葉讼稚, 淡香惹人醉括儒。 花妍草色翠绕沈, 春濃慵倦退。
    近者悅遠者來閱讀 290評論 0 3
  • 人的一生帮寻,能找到自己喜歡的事情是幸運的乍狐。有自己感興趣的人,才會生活得有趣固逗,才可能成為一個有意思的人浅蚪。當(dāng)你不計功利地...
    King木子木木亦大閱讀 173評論 0 0
  • 在網(wǎng)上看了電影《驢得水》惜傲。 顯而易見的荒誕、反諷嗡髓、黑色幽默操漠,還有人性被催谷、放大到以致撕裂饿这。 玩深度幽默的片子浊伙,調(diào)...
    悟恩說事閱讀 1,183評論 2 12