离散事件模拟在游戏中的应用
離散事件模擬(discrete event simulation),這個(gè)東西可能在游戲領(lǐng)域用得并不是很多,它是模擬仿真領(lǐng)域的一個(gè)仿真模型,用來模擬在時(shí)間軸上一系列離散事件后,整個(gè)系統(tǒng)的變化情況,這么說,可能還是有點(diǎn)抽象,給大家舉一個(gè)使用離散事件模擬的一個(gè)經(jīng)典的例子,如何計(jì)算銀行柜臺(tái)排隊(duì)的平均等待時(shí)間。
先簡要描述一下這個(gè)場景,假設(shè)一個(gè)銀行有兩個(gè)柜臺(tái),每個(gè)柜臺(tái)都可以辦理業(yè)務(wù),每隔3分鐘都會(huì)有一位客戶進(jìn)來辦理業(yè)務(wù),如果有空的柜臺(tái),那他就直接辦理,如果沒有,就排隊(duì),每個(gè)人辦理的業(yè)務(wù)時(shí)間都不同,可能是1~10分鐘的任意一個(gè)時(shí)間,整個(gè)銀行的營業(yè)時(shí)間是8小時(shí),那請(qǐng)計(jì)算,在8小時(shí)所接待的所有客戶中,客戶的平均等待時(shí)間是多少?
這個(gè)問題,如果靠數(shù)學(xué)方法來計(jì)算,并不是很容易,那換一個(gè)思路就是,我可不可以把整個(gè)這個(gè)8小時(shí)的過程模擬一遍,把每一個(gè)客戶的等待時(shí)間記錄下來,然后求平均,這樣就可以得到這個(gè)平均等待時(shí)間了。當(dāng)然,真的把模擬的過程跑8個(gè)小時(shí),太沒有效率了,所以這個(gè)時(shí)候,就可以用離散事件模擬的方法,對(duì)這個(gè)問題去快速的進(jìn)行仿真。
離散事件模擬有幾個(gè)基本的概念,我們可以結(jié)合上面的這個(gè)例子來一個(gè)個(gè)看:
時(shí)鐘(Clock):整個(gè)模擬是按照時(shí)間去前進(jìn)的,也就是有一個(gè)虛擬的時(shí)間去跟蹤當(dāng)前的時(shí)間,在這里,這個(gè)時(shí)鐘就是從銀行開門,一直到銀行關(guān)門,總長8小時(shí)
結(jié)束條件(Ending Condition):模擬結(jié)束所要滿足的條件,因?yàn)椴豢赡軣o限模擬下去,在這里,結(jié)束條件,就是時(shí)間到了8小時(shí)
事件(Event):在時(shí)間軸上需要處理的事件,每個(gè)事件都有發(fā)生的時(shí)間,并且事件是按照時(shí)間的先后排列在時(shí)間軸上的,在這里,有幾個(gè)事件,客戶到達(dá)銀行,客戶結(jié)束辦理業(yè)務(wù)。
整個(gè)模擬過程是怎么樣的呢?簡單的一句話,就是不斷的從時(shí)間軸上處理一個(gè)個(gè)的事件,直至滿足結(jié)束條件或者所有的事件都處理完畢。由于在相鄰的兩個(gè)事件與事件之間,是沒有任何事情發(fā)生的,所以時(shí)鐘是可以從當(dāng)前的事件觸發(fā)的時(shí)間直接跳到下一個(gè)事件觸發(fā)的時(shí)間,這也就是“離散事件”的含義,因?yàn)檫@些“事件”在整個(gè)時(shí)間軸上是離散分布的,從時(shí)間的角度來看,整個(gè)模擬過程是“跳躍”前進(jìn)的。
?
新的事件可以通過在處理老的事件的時(shí)候來不斷的產(chǎn)生,并且添加到時(shí)間軸上,繼續(xù)用上面的例子,我們把“客戶n在時(shí)間t到達(dá)銀行”這個(gè)事件記為E(Arrive,n,t),把“客戶n在柜臺(tái)m用了t分鐘結(jié)束了業(yè)務(wù)辦理”這個(gè)事件記為E(Finish,n,m,t),第一個(gè)事件,就是E(Arrive,Tom,0),也就是銀行一開門,來了一個(gè)用戶叫Tom,這樣在時(shí)間軸上,現(xiàn)在是這樣的。
?
這樣我們初始化就好了,現(xiàn)在銀行開門,我們開始進(jìn)行8小時(shí)的模擬
我們先處理第一個(gè)事件E(Arrive,Tom,0),當(dāng)前時(shí)鐘設(shè)為事件的時(shí)間,第0分鐘,這個(gè)事件處理的時(shí)候,因?yàn)楝F(xiàn)在柜臺(tái)是空的,所以Tom選擇了1號(hào)柜臺(tái),并且需要用了8分鐘辦理業(yè)務(wù),那我們就在時(shí)間軸上再添加一個(gè)時(shí)間E(Finish,Tom,1,8),并且,我們還需要往時(shí)間軸再添加下一個(gè)“客戶到達(dá)事件”,假設(shè)3分鐘后會(huì)來一個(gè)客戶叫Jerry,這樣時(shí)間軸上又加了一個(gè)事件E(Arrive,Jerry,3),這個(gè)事件會(huì)加在E(Finish,Tom,1,8)前面,因?yàn)樗鼤?huì)先發(fā)生。
?
然后我們開始處理第二個(gè)事件,因?yàn)槭录前凑諘r(shí)間軸排序的,所以我們一定能拿到一個(gè)最近要發(fā)生的事件,現(xiàn)在的例子里,這個(gè)事件是E(Arrive,Jerry,3),又來了一個(gè)客戶Jerry,當(dāng)前時(shí)鐘設(shè)為事件的時(shí)間,第3分鐘,處理的過程和上面一樣,QQ賣號(hào)不過Jerry選擇2號(hào)柜臺(tái),我們會(huì)壓兩個(gè)事件,E(Finish,Jerry,2,3+6),E(Arrive,Mary,3+3),值得注意的是,事件發(fā)生的時(shí)刻,我用了3+6這樣的形式,也就是表示,當(dāng)前時(shí)鐘是第3分鐘,這個(gè)事件需要在當(dāng)前時(shí)鐘的往后的6分鐘發(fā)生。
按照我們的時(shí)間軸,第三個(gè)事件,是E(Arrive,Mary,3+3),當(dāng)前時(shí)鐘設(shè)為事件的時(shí)間,第6分鐘,這個(gè)時(shí)候,我們不能直接為Mary添加E(Finish)事件,因?yàn)闆]有空的柜臺(tái),所以Mary只能選擇排隊(duì),但下一個(gè)E(Arrive)事件還是需要添加。
第四個(gè)事件,總算輪到很早就添加的E(Finish,Tom,1,8),所以添加的早,不一定執(zhí)行的早,因?yàn)檫@個(gè)模擬是按照時(shí)間來推進(jìn)的,當(dāng)前時(shí)鐘到了第8分鐘,這個(gè)時(shí)候Tom的業(yè)務(wù)辦完了,在處理E(Finish)事件的時(shí)候,我們就要去看,是不是有人在排隊(duì),如果有人在排隊(duì),那就要為排在第一個(gè)的用戶,添加E(Finish)事件,現(xiàn)在這個(gè)人是Mary,那我們就添加E(Finish,Mary,1,8+6),在1號(hào)柜臺(tái),預(yù)計(jì)花6分鐘的時(shí)間結(jié)束。
?
這樣基本上所有可能的情況我們都簡單描述了,剩下的,就是讓這個(gè)模擬過程一直跑,整個(gè)過程,就是不斷的有事件被處理,然后又不斷的有事件產(chǎn)生,到最后8小時(shí)的時(shí)候,銀行關(guān)門,模擬結(jié)束。基于這個(gè)模擬過程,平均等待時(shí)間就很容易算了,客戶來的時(shí)間記t1(也就是處理E(Arrive)的時(shí)間點(diǎn)),客戶開始辦業(yè)務(wù)的時(shí)間記t2(也就是添加E(Finish)事件的時(shí)間點(diǎn)),然后t2-t1,就是單個(gè)客戶的等待時(shí)間,然后把所有等待時(shí)間求和,再除以所有的客戶數(shù),就是平均等待時(shí)間了。
這個(gè)東西說起來好累,其實(shí)實(shí)現(xiàn)起來并不復(fù)雜,在我維護(hù)的那個(gè)TsiU的庫中,已經(jīng)實(shí)現(xiàn)了一個(gè)離散事件模擬的工具庫,也就100多行代碼,非常簡單。在項(xiàng)目中,我用這個(gè)方法,做過一個(gè)工具,用來模擬在特定的同時(shí)在線數(shù)量的情況下玩家匹配游戲的平均等待時(shí)間,這樣可以幫助運(yùn)營人員去確定需要導(dǎo)入多少的用戶量,來盡可能的減少匹配的等待。離散事件模擬,對(duì)于這種情景可以說是一個(gè)非常好用的利器,概念簡單,實(shí)現(xiàn)快捷。不過在實(shí)際的游戲?qū)用?#xff0c;它可不可以發(fā)揮作用呢?或者說,有沒有什么游戲情景可以用這個(gè)方法去架構(gòu)呢?答案是,可以有!
說了一大圈,總算到了這次分享的正題了。離散事件模擬是可以用在某些特定的游戲情境中,比如有一些游戲是戰(zhàn)報(bào)類的,也就是說你點(diǎn)擊了戰(zhàn)斗之后,后臺(tái)系統(tǒng)就已經(jīng)把整個(gè)戰(zhàn)斗的過程算好了,然后客戶端就是把整個(gè)戰(zhàn)斗結(jié)果來回放一遍,這種類型的戰(zhàn)斗就很適合用離散事件模擬來架構(gòu)(不知道類似的游戲有沒有采用這樣的方法),假設(shè)我們采用類似于早期最終幻想的ATB戰(zhàn)斗系統(tǒng),也就是每個(gè)參加角色都有個(gè)自己的時(shí)間條,誰先長滿了就誰先行動(dòng),速度高的角色時(shí)間條長得快一點(diǎn),速度低的角色時(shí)間條就長得慢一點(diǎn)。
?
要模擬這樣一場戰(zhàn)斗就可以用離散事件模擬的結(jié)構(gòu),假設(shè)我們先簡化為只能進(jìn)行攻擊的行動(dòng),我們就會(huì)有一個(gè)事件就是攻擊,E(Attack,t),在戰(zhàn)斗的一開始,我們把速度最快的那個(gè)人的E(Attack,t)添加到時(shí)間軸上(有點(diǎn)類似于上面銀行的例子里,排在最前面等處理業(yè)務(wù)的那個(gè)人),然后開始進(jìn)行模擬,在處理E(Attack,t)時(shí),需要進(jìn)行以下幾步
?
- 處理傷害
- 添加下一個(gè)行動(dòng)的人的E(Attack,t)
- 把自己加入行動(dòng)的隊(duì)列
一直進(jìn)行模擬,直到己方勝出或者團(tuán)滅。再復(fù)雜一點(diǎn),如果再攻擊中改變了時(shí)間條的增長速度,那么就需要對(duì)行動(dòng)隊(duì)列進(jìn)行調(diào)整,也就是重新按照速度排序。這樣當(dāng)這場戰(zhàn)斗模擬完畢的時(shí)候,就可以把所有的模擬過程的數(shù)據(jù)發(fā)送給客戶端,由客戶端進(jìn)行戰(zhàn)斗表現(xiàn)。
不過,在這種模擬過程中,游戲內(nèi)是不能進(jìn)行操作的,也就說,整個(gè)戰(zhàn)斗是自動(dòng)進(jìn)行的,那如果需要支持操作,那個(gè)應(yīng)該怎么處理呢?對(duì)于使用離散模擬的游戲來說,是沒有辦法做到“實(shí)時(shí)操作,實(shí)時(shí)生效”的,不過可以做到“實(shí)時(shí)操作,延時(shí)生效”,這在某些游戲中是很常見的,比如像類似于足球經(jīng)理類的游戲,在你看比賽模擬的時(shí)候,你可以進(jìn)行戰(zhàn)術(shù)改變,換人等操作,這種操作對(duì)于玩家來說并不需要很高的實(shí)時(shí)性,只要在一段時(shí)間后能生效就可以了,再比如上面說的ATB的戰(zhàn)斗系統(tǒng),假設(shè)玩家可以在觀看戰(zhàn)斗的時(shí)候,能夠使用一些全局魔法,這個(gè)時(shí)候,就可以用一些“施法時(shí)間”的概念來告訴玩家,你的施法是需要時(shí)間的,來掩蓋無法實(shí)時(shí)生效的問題,但是又保證了服務(wù)器快速的模擬計(jì)算,節(jié)省服務(wù)器資源。
用離散事件模擬做“實(shí)時(shí)操作,延時(shí)生效”的關(guān)鍵,就是采用“分段模擬”,也就是不是一下子模擬整個(gè)過程,而是模擬一段時(shí)間,等待一段時(shí)間,再模擬一段時(shí)間,這樣,如果玩家的操作發(fā)生在上一個(gè)時(shí)間段,那我就可以在下一個(gè)時(shí)間段模擬的時(shí)候,加入玩家操作的影響。
?
采用“分段模擬”的時(shí)候,和客戶端通信會(huì)采用一段一段數(shù)據(jù)發(fā)送的方式,服務(wù)器先模擬一段時(shí)間的數(shù)據(jù),比如模擬10秒鐘邏輯,然后推送給客戶端,之后等待一段時(shí)間,這段時(shí)間不能超過每段的模擬時(shí)間,一般可以等待50%~80%的模擬時(shí)間,比如等待5~8秒鐘,如果等待超過10秒鐘(甚至接近10秒鐘),就會(huì)導(dǎo)致客戶端那邊播放完了,新的數(shù)據(jù)還沒到,從而客戶端的表現(xiàn)產(chǎn)生卡頓的情況,后續(xù)就一直循環(huán)這個(gè)過程,直到整個(gè)模擬結(jié)束。
離散事件模擬在游戲中雖然不是很常用,不過也是可以作為一個(gè)知識(shí)點(diǎn),儲(chǔ)備在那里,當(dāng)遇到適合的場合,就可以作為選擇之一加以應(yīng)用。具體的例子我就不寫了,大家有興趣可以用TsiU里的那個(gè)庫,自己做一個(gè)模擬銀行平均等待時(shí)間的程序,來體會(huì)一下,有任何問題,和指教,歡迎討論。
總結(jié)
以上是生活随笔為你收集整理的离散事件模拟在游戏中的应用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 技术人员是如何分析游戏环境的? 《影之诗
- 下一篇: 游戏编程中的数学——随机数字生成(RNG