他凌晨1:30给我开源的游戏加了UI|模拟龙生,挂机冒险
一、前言
新年就要到了,祝大家新的一年: 龙行龘龘, 前程朤朤!
白泽花了点时间,用 800 行 Go 代码写了一个控制台的小游戏:《模拟龙生》,在游戏中你将模拟一条新生的巨龙,开始无尽的冒险!
3天前的《模拟龙生|500行Go代码写一个随机冒险游戏|巨龙修为挑战》文章中已经对核心玩法和游戏核心架构做了介绍,但是第一版实在是写得匆忙,编码不够优雅。
幸得热心同学提了 pr 优化了部分代码逻辑,甚至凌晨1:30给游戏加了 UI,在这个基础上,白泽也为游戏增加了排行榜功能,这篇文章讲解一下相比3天前,《模拟龙生》的一些架构上的变化以及玩法的更新。
游戏更新主要包含:
使用 termdash(基于终端窗口的跨平台仪表盘)作为 UI。
架构升级,使用 channel 传递游戏内所有 IO 内容,面向协程编程。
增加排行榜玩法。
公众号 「白泽talk」,我也开源了一个 Go 学习仓库:包含我写作的 Go 各阶段学习文章、读书笔记、电子书、简历模板等,欢迎 star。
白泽目前正在打造一个氛围良好的行业交流群(游戏交流群),文章的更新也会提前预告,欢迎加入:622383022。
二、核心玩法
- 玩法流程:
具体参详前一篇文章,后续也会尽快在仓库的 README 部分更新新增内容玩法手册。
游戏核心玩法:挂机、打怪、冒险、修炼。

- 游玩体验(gif):
- 分配100点能力值,并进行x轮冒险,这里我输入100。
- 选择2开始冒险,进行50轮,但冒险中第41轮意外死亡,丢失9轮冒险次数。
- 选择1返回修养,进行10轮,恢复生命值和提升修为。
- 选择2开始冒险,进行40轮,最后获得修为2093进入排行榜第三名。

三、更新内容
3.1 termdash 构建 UI
Termdash 是一款基于终端的跨平台定制仪表盘。只要将需要展示的消息,发送给 termdash 库负责 UI 展示的结构体,则可以将其以仪表盘的形式,动态展示更新。

《模拟龙生》将游戏 UI 区域分成历史记录区、排行榜区、数值区、操作提示区、输入区。
界面布局
termdash 的界面布局与 HTML 的 div 布局有些相似,通过 container 将区域进行分割,可以水平分割也可以垂直分割,下面这段代码就是 dragon 游戏当中,历史记录区域与排行榜区域布局。
container.SplitPercent(50) 这行代码表示各占百分之五十空间。
// 历史记录区域布局 & 排行榜区域布局
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 传递消息
整个游戏的左下角是用户唯一的输入区域,通过捕获用户的输入,触发相遇的游戏逻辑之后,通过 channel 将数据发送到对应的 container 区域进行展示。

每一个游戏区域,在 printer 结构体中,都有对应的属性字段,比如 historyText 字段对应着“龙生经历”区域,而每一个区域也都有对应的一个channel 用于接收消息,如 history 就是用于接收龙生经历的 channel。
// 创建消息打印器结构体
p := &printer{
terminal: terminal,
ctx: ctx,
container: c,
// 历史记录消息接收
history: make(chan historyInfo),
// 历史记录区域 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
}
},
}
// 更新数值面板区域
go p.updateValuesPanel()
// 接收并打印龙的经历到历史经历区域
go p.receiveHistory()
// 接收并打印操作提示语区域
go p.receiveOperateHint()
// 接收并打印信息到排行榜区域
go p.receiveRank()
只有先从 channel 中获取到了消息,才能将消息在对应 UI 区域展示。以龙的冒险为例,如果龙正在参与冒险,则每过0.5秒会在龙生经历(历史记录)区域打印一条记录,如:剩余寿命 xxx 轮,你打败了 xxx,修为增加 xxx。
而UI 上的内容展示与程序执行关系如下:
- 提前启动 go 协程监听 history 这个 channel,获取要打印到 UI 区域的龙的经历。(调用的是
p.receiveHistory())。 - 每隔0.5秒处理业务,将需要打印的信息发送给
p.history这个 channel。
// 接收历史数据,并换行
func (p *printer) addHistoryLn(info historyInfo) {
info.info += "\n"
p.history <- info
}
// 接收历史数据处理方法
func (p *printer) receiveHistory() {
go func() {
for {
select {
case info := <-p.history:
p.historyText.Write(info.info, info.options...)
}
}
}()
}
游戏中所有 UI 区域的内容都是通过最终调用 p.xxx.Write() 方法输出到 UI 仪表盘上的,而诸如 historyText 这个属性对应的数据类型,都是 termdash 库所提供的。
3.3 排行榜玩法
在游戏开始之初会打印之前历史记录中,最终获得经验值最高的10条记录,降序排列。并在游戏正常结束(非 CTRL + W 形式结束)后,如果进入前十,则更新榜单。

排行榜的实现:
- 以
sqlite3作为数据库,对应rank.db文件,运行程序时如果不存在则会自动创建。 - 对应的数据结构和数据处理方法:
// 创建消息打印器结构体
p := &printer{
// rank 数据接收 channel
rank: make(chan rankInfo),
// rank UI 区域
rankText: rankPanel,
}
// 接收并打印信息到排行榜区域
go p.receiveRank()
// 接收排行榜数据,并换行
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,经验值:%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))
}
}
四、小结
下一阶段的打算:
趣味性:优化 NPC 和随机事件的内容。
功能性:待定
欢迎评论对《模拟龙生》游玩的体验,有好的想法也可以一起交流,当然也欢迎多多 pr。
他凌晨1:30给我开源的游戏加了UI|模拟龙生,挂机冒险的更多相关文章
- 国内开源html5游戏引擎全收录
本文引自<国内开源html5游戏引擎全收录> 游戏开发这潭水太深,英文水平太差,不敢看国外的, 而且这几年国内技术水平也挺高了不少,特别是JS方面.(我个人感觉) 最近看了几个国产的js游 ...
- 记录一下八款开源 Android 游戏引擎
记录一下八款开源 Android 游戏引擎 虽然android学了点点,然后现在又没学了(我为啥这么没有恒心呢大哭).以后有时间还是要继续学android的,一定要啊!虽然现在没学android游戏编 ...
- 30款基于 jQuery & CSS3 的加载动画和进度条插件
我们所生活每一天看到的新技术或新设计潮流的兴起,Web 开发正处在上升的时代.HTML5 & CSS3 技术的发展让 Web 端可以实现的功能越来越强大. 加载动画和进度条使网站更具吸引力.该 ...
- [转]八款开源Android游戏引擎
八款开源Android游戏引擎 1.Angle Angle是一款专为Android平台设计的,敏捷且适合快速开发的2D游戏引擎,基于OpenGL ES技术开发.该引擎全部用Java代码编写,并且可以根 ...
- 2018年最值得关注的30个Vue开源项目
译者按: 学习优秀的开源项目是提高代码水平最有效的方式. 原文: 30 Amazing Vue.js Open Source Projects for the Past Year (v.2018) 译 ...
- 开源3D游戏引擎Irrlicht简介
Irrlicht简介 Irrlicht在国内也被叫做"鬼火"引擎,是一款用C++编写的开放源代码的高性能游戏引擎.而且是跨平台的,具有很好的移植性,Irrlicht支持OpenGl ...
- Android Fresco (Facebook开源的图片加载管理库)
Fresco是Facebook开源的一个图片加载和管理库. 这里是Fresco的GitHub网址. 同类型的开源库市面有非常多,比如Picasso, Universal Image Loader, G ...
- 【开源java游戏框架libgdx专题】-08-中文显示与绘制
libgdx虽然是由美国人Mario Zechner(即BadlogicGames)写的开源引擎,由于Libgdx底层是用OpenGL实现的,所以Libgdx是可以支持中文的,在libgdx中的汉字都 ...
- 【开源java游戏框架libgdx专题】-01-libgdx介绍
libgdx是一款开源的java游戏框架,而且还实现了Desktop/Android/BlackBerry/iOS/HTML5这些些平台的跨平台开发.官方网址:https://libgdx.badlo ...
- Android开源框架之SwipeListView导入及模拟QQ侧滑
SwipeListView是Github上的一个开源框架,地址:https://github.com/47deg/android-swipelistview SwipeListView was bor ...
随机推荐
- Python——Html(HEAD头部)
HTML中HEAD头部设置(了解) 在HTML中,<head> 元素是文档的头部部分,通常包含了一些关于文档的元信息和链接到外部资源的标签.以下是一些常见的 <head> 元素 ...
- Python——第五章:json模块
什么是json: json 模块是用于处理 JSON(JavaScript Object Notation)数据的模块,翻译过来叫js对象简谱.JSON是一种轻量级的数据交换格式,常用于将数据在不同语 ...
- Ubuntu图形界面root登录“sorry, that didn't work please
https://blog.51cto.com/u_14757092/2484490 ssh登录主机执行下vim /etc/pam.d/gdm-autologin 注释行 "auth requ ...
- 斯坦福 UE4 C++ ActionRoguelike游戏实例教程 07.在C++中使用UMG
斯坦福 UE4 C++ ActionRoguelike游戏实例教程 07.在C++中使用UMG 斯坦福课程 UE4 C++ ActionRoguelike游戏实例教程 0.绪论 概述 本篇文章的目标是 ...
- curl使用小记(三)——获取远端数据到内存缓冲区
目录 1. 概述 2. 实现 3. 参考 1. 概述 我在博文<curl使用小记(二)--远程下载一张图片>中介绍了如何通过Curl获取远端的文件.不过在那个例子中,将获取远端数据与写入数 ...
- 华为云应用服务网格最佳实践之从Spring Cloud 到 Istio
摘要:在全球首届社区峰会IstioCon 2021中,华为云应用服务网格首席架构师张超盟发表了<Best practice:from Spring Cloud to Istio>主题演讲, ...
- 鸿蒙轻内核M核源码分析:中断Hwi
摘要:本文带领大家一起剖析了鸿蒙轻内核的中断模块的源代码,掌握中断相关的概念,中断初始化操作,中断创建.删除,开关中断操作等. 本文分享自华为云社区<鸿蒙轻内核M核源码分析系列五 中断Hwi&g ...
- 华为云UGO:醒醒!你的异构数据库迁移难题有救了
摘要:华为云推出的数据库和应用迁移 UGO,正是一款专注于异构数据库结构迁移和应用SQL转换的专业云服务. 数字化时代下,上云已成为企业管理者的基本共识,随着技术日新月异,上云也变得轻松简单起来,但异 ...
- hadoop fs,hadoop dfs以及hdfs dfs区别
1.hadoop dfs 专门针对hdfs系统 2.hdfs dfs 和hadoop dfs,当使用hadoop dfs时会被转为hdfs dfs命令 3.hadoop fs 范围更广
- 火山引擎DataLeap背后的支持者 - 工作流编排调度系统FlowX
更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 背景介绍 业务场景 在日常工作中,我们时不时需要对某些逻辑进行重复调度,这时我们就需要一个调度系统.根据不同的调度 ...
