一、前言
新年就要到了,祝大家新的一年:?? 龍行龘龘,?? 前程?hào)T朤!
白澤花了點(diǎn)時(shí)間,用 800 行 Go 代碼寫了一個(gè)控制臺(tái)的小游戲:《模擬龍生》,在游戲中你將模擬一條新生的巨龍,開始無(wú)盡的冒險(xiǎn)!
3天前的《??模擬龍生|500行Go代碼寫一個(gè)隨機(jī)冒險(xiǎn)游戲|巨龍修為挑戰(zhàn)》文章中已經(jīng)對(duì)核心玩法和游戲核心架構(gòu)做了介紹,但是第一版實(shí)在是寫得匆忙,編碼不夠優(yōu)雅。
??幸得熱心同學(xué)提了 pr 優(yōu)化了部分代碼邏輯,甚至凌晨1:30給游戲加了 UI,在這個(gè)基礎(chǔ)上,白澤也為游戲增加了排行榜功能,這篇文章講解一下相比3天前,《模擬龍生》的一些架構(gòu)上的變化以及玩法的更新。
?? 游戲更新主要包含:
-
使用 termdash(基于終端窗口的跨平臺(tái)儀表盤)作為 UI。
-
架構(gòu)升級(jí),使用 channel 傳遞游戲內(nèi)所有 IO 內(nèi)容,面向協(xié)程編程。
-
增加排行榜玩法。
公眾號(hào) 「白澤talk」,我也開源了一個(gè) Go 學(xué)習(xí)倉(cāng)庫(kù):包含我寫作的 Go 各階段學(xué)習(xí)文章、讀書筆記、電子書、簡(jiǎn)歷模板等,歡迎 star。
白澤目前正在打造一個(gè)氛圍良好的行業(yè)交流群(游戲交流群),文章的更新也會(huì)提前預(yù)告,歡迎加入:622383022。
二、核心玩法
- 玩法流程:
具體參詳前一篇文章,后續(xù)也會(huì)盡快在倉(cāng)庫(kù)的 README 部分更新新增內(nèi)容玩法手冊(cè)。
游戲核心玩法:掛機(jī)、打怪、冒險(xiǎn)、修煉。
- 游玩體驗(yàn)(gif):
- 分配100點(diǎn)能力值,并進(jìn)行x輪冒險(xiǎn),這里我輸入100。
- 選擇2開始冒險(xiǎn),進(jìn)行50輪,但冒險(xiǎn)中第41輪意外死亡,丟失9輪冒險(xiǎn)次數(shù)。
- 選擇1返回修養(yǎng),進(jìn)行10輪,恢復(fù)生命值和提升修為。
- 選擇2開始冒險(xiǎn),進(jìn)行40輪,最后獲得修為2093進(jìn)入排行榜第三名。
三、更新內(nèi)容
3.1 termdash 構(gòu)建 UI
Termdash 是一款基于終端的跨平臺(tái)定制儀表盤。只要將需要展示的消息,發(fā)送給 termdash 庫(kù)負(fù)責(zé) UI 展示的結(jié)構(gòu)體,則可以將其以儀表盤的形式,動(dòng)態(tài)展示更新。
《模擬龍生》將游戲 UI 區(qū)域分成歷史記錄區(qū)、排行榜區(qū)、數(shù)值區(qū)、操作提示區(qū)、輸入?yún)^(qū)。
界面布局
termdash 的界面布局與 HTML 的 div 布局有些相似,通過(guò) container 將區(qū)域進(jìn)行分割,可以水平分割也可以垂直分割,下面這段代碼就是 dragon 游戲當(dāng)中,歷史記錄區(qū)域與排行榜區(qū)域布局。
container.SplitPercent(50)
這行代碼表示各占百分之五十空間。
// 歷史記錄區(qū)域布局 & 排行榜區(qū)域布局
container.Right(
container.SplitVertical(
container.Left(
container.PlaceWidget(historyPanel),
container.BorderTitle(HistoryAreaBorderTitle),
container.Border(HistoryAreaBorderStyle),
container.BorderColor(HistoryAreaBorderColor),
container.KeyFocusSkip(),
),
container.Right(
container.PlaceWidget(rankPanel),
container.BorderTitle(RankAreaBorderTitle),
container.Border(RankAreaBorderStyle),
container.BorderColor(RankAreaBorderColor),
container.KeyFocusSkip(),
),
container.SplitPercent(50),
),
),
3.2 使用 channel 傳遞消息
整個(gè)游戲的左下角是用戶唯一的輸入?yún)^(qū)域,通過(guò)捕獲用戶的輸入,觸發(fā)相遇的游戲邏輯之后,通過(guò) channel 將數(shù)據(jù)發(fā)送到對(duì)應(yīng)的 container 區(qū)域進(jìn)行展示。
每一個(gè)游戲區(qū)域,在 printer
結(jié)構(gòu)體中,都有對(duì)應(yīng)的屬性字段,比如 historyText
字段對(duì)應(yīng)著“龍生經(jīng)歷”區(qū)域,而每一個(gè)區(qū)域也都有對(duì)應(yīng)的一個(gè)channel 用于接收消息,如 history
就是用于接收龍生經(jīng)歷的 channel。
// 創(chuàng)建消息打印器結(jié)構(gòu)體
p := &printer{
terminal: terminal,
ctx: ctx,
container: c,
// 歷史記錄消息接收
history: make(chan historyInfo),
// 歷史記錄區(qū)域 UI
historyText: historyPanel,
rank: make(chan rankInfo),
rankText: rankPanel,
operateHintText: operationHint,
operateHint: make(chan string),
scanned: make(chan string),
flushChannel: make(chan struct{}),
values: values,
experienceBar: experience,
hpBar: hpBar,
keyBinding: func(k *terminalapi.Keyboard) {
// Ctrl + W 退出
if k.Key == keyboard.KeyCtrlW {
cancel()
os.Exit(0)
}
// Enter 完成輸入
if k.Key == keyboard.KeyEnter {
value := inputs.ReadAndClear()
p.scanned <- value
}
},
}
// 更新數(shù)值面板區(qū)域
go p.updateValuesPanel()
// 接收并打印龍的經(jīng)歷到歷史經(jīng)歷區(qū)域
go p.receiveHistory()
// 接收并打印操作提示語(yǔ)區(qū)域
go p.receiveOperateHint()
// 接收并打印信息到排行榜區(qū)域
go p.receiveRank()
只有先從 channel 中獲取到了消息,才能將消息在對(duì)應(yīng) UI 區(qū)域展示。以龍的冒險(xiǎn)為例,如果龍正在參與冒險(xiǎn),則每過(guò)0.5秒會(huì)在龍生經(jīng)歷(歷史記錄)區(qū)域打印一條記錄,如:剩余壽命 xxx 輪,你打敗了 xxx,修為增加 xxx
。
而UI 上的內(nèi)容展示與程序執(zhí)行關(guān)系如下:
- 提前啟動(dòng) go 協(xié)程監(jiān)聽 history 這個(gè) channel,獲取要打印到 UI 區(qū)域的龍的經(jīng)歷。(調(diào)用的是
p.receiveHistory()
)。 - 每隔0.5秒處理業(yè)務(wù),將需要打印的信息發(fā)送給
p.history
這個(gè) channel。
// 接收歷史數(shù)據(jù),并換行
func (p *printer) addHistoryLn(info historyInfo) {
info.info += "\n"
p.history <- info
}
// 接收歷史數(shù)據(jù)處理方法
func (p *printer) receiveHistory() {
go func() {
for {
select {
case info := <-p.history:
p.historyText.Write(info.info, info.options...)
}
}
}()
}
游戲中所有 UI 區(qū)域的內(nèi)容都是通過(guò)最終調(diào)用 p.xxx.Write()
方法輸出到 UI 儀表盤上的,而諸如 historyText
這個(gè)屬性對(duì)應(yīng)的數(shù)據(jù)類型,都是 termdash 庫(kù)所提供的。
3.3 排行榜玩法
在游戲開始之初會(huì)打印之前歷史記錄中,最終獲得經(jīng)驗(yàn)值最高的10條記錄,降序排列。并在游戲正常結(jié)束(非 CTRL + W 形式結(jié)束)后,如果進(jìn)入前十,則更新榜單。
排行榜的實(shí)現(xiàn):
- 以
sqlite3
作為數(shù)據(jù)庫(kù),對(duì)應(yīng)rank.db
文件,運(yùn)行程序時(shí)如果不存在則會(huì)自動(dòng)創(chuàng)建。 - 對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)和數(shù)據(jù)處理方法:
// 創(chuàng)建消息打印器結(jié)構(gòu)體
p := &printer{
// rank 數(shù)據(jù)接收 channel
rank: make(chan rankInfo),
// rank UI 區(qū)域
rankText: rankPanel,
}
// 接收并打印信息到排行榜區(qū)域
go p.receiveRank()
// 接收排行榜數(shù)據(jù),并換行
func (p *printer) addRankLn(info rankInfo) {
info.info += "\n"
p.rank <- info
}
// 展示排行榜
func showRank(ranks []*Rank, rank *Rank) {
p.rankText.Reset()
for i, r := range ranks {
s := fmt.Sprintf("第%v名,龍的ID:%v,名稱:%v,經(jīng)驗(yàn)值:%v,攻擊力:%v,防御力:%v,生命值:%v", i+1, r.DragonID, r.Name, r.Experience, r.Attack, r.Defense, r.Life)
if r.equal(rank) {
s = "??" + s
}
s = s + "\n"
p.addRankLn(newRankInfo(s))
}
}
四、小結(jié)
?? 下一階段的打算:
-
趣味性:優(yōu)化 NPC 和隨機(jī)事件的內(nèi)容。
-
功能性:待定文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-820846.html
歡迎評(píng)論對(duì)《模擬龍生》游玩的體驗(yàn),有好的想法也可以一起交流,當(dāng)然也歡迎多多 pr。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-820846.html
到了這里,關(guān)于他凌晨1:30給我開源的游戲加了UI|模擬龍生,掛機(jī)冒險(xiǎn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!