Golang 并不适合在前端工作,缺少强大的图形图像包和硬件加速包,更适合做成后台服务程序。本文的贪吃蛇小游戏运行与控制台上,其中调用了Window系统kernel32.dll中控制台相关的函数。
项目地址:
https://gitee.com/redfire0922/snakego
可以使用如下命令下载源码

go get gitee.com/redfire0922/snakego

最终效果

其中最关键的还是对系统函数的调用。因为golang读取用户输入都是按整行读取。也就是说用户在输入数据按下回车键之前,golang是一点点数据都读取不到。这对交互稍微丰富点的程序来说大大限制了想象力。其次是对输入位置的控制。普通情况下要录入一份类似表单的数据,golang只能一问一答录入。本文正是给出了另外一种方案。

// +bulid windowspackage mainimport ("syscall""unsafe"
)type (SHORT int16DWORD uint32WORD  uint16BOOL  int32WCHAR uint16COORD struct {X SHORTY SHORT}CONSOLE_SCREEN_BUFFER_INFO struct {Size              COORDCursorPosition    COORDAttributes        WORDWindow            SMALL_RECTMaximumWindowSize COORD}SMALL_RECT struct {Left   SHORTTop    SHORTRight  SHORTBottom SHORT}INPUT_RECORD struct {EventType WORD_         [2]byteEvent     [16]byte}KEY_EVENT_RECORD struct {KeyDown         BOOLRepeatCount     WORDVirtualKeyCode  WORDVirtualScanCode WORDUnicodeChar     WCHARControlKeyState DWORD}
)const (VK_ARROW_UP    = 0x26VK_ARROW_DOWN  = 0x28VK_ARROW_LEFT  = 0x25VK_ARROW_RIGHT = 0x27
)var (Stdout  syscall.HandleStdin   syscall.Handletmp_arg DWORD
)func (this SMALL_RECT) uintptr() uintptr {return uintptr(*(*int32)(unsafe.Pointer(&this)))
}func (this COORD) uintptr() uintptr {return uintptr(*(*int32)(unsafe.Pointer(&this)))
}
//获取输入,输出handle
func InitSyscall() {cout, err := syscall.Open("CONOUT$", syscall.O_RDWR, 0)if err != nil {panic(err)}Stdout = coutcin, err := syscall.Open("CONIN$", syscall.O_RDWR, 0)if err != nil {panic(err)}Stdin = cin}
//获取光标位置
func GetConsoleCursorPosition() COORD {var tmp_info CONSOLE_SCREEN_BUFFER_INFOerr := GetConsoleScreenBufferInfo(&tmp_info)if err != nil {panic(err)}return tmp_info.CursorPosition
}//获取控制台信息
func GetConsoleScreenBufferInfo(info *CONSOLE_SCREEN_BUFFER_INFO) (err error) {r0, _, e1 := proc_get_console_screen_buffer_info.Call(uintptr(Stdout), uintptr(unsafe.Pointer(info)))if int(r0) == 0 {if e1 != nil {err = e1} else {err = syscall.EINVAL}}return
}
//设置光标位置 新光标位置必须在已有数据的控制台Buffer范围内。
func SetConsoleCursorPosition(pos COORD) (err error) {r0, _, e1 := proc_set_console_cursor_position.Call(uintptr(Stdout), pos.uintptr())if int(r0) == 0 {if e1 != nil {err = e1} else {err = syscall.EINVAL}}return
}//读取用户输入
func ReadConsoleInput(record *INPUT_RECORD) (err error) {r0, _, e1 := proc_read_console_input.Call(uintptr(Stdin), uintptr(unsafe.Pointer(record)), 1, uintptr(unsafe.Pointer(&tmp_arg)))if int(r0) == 0 {if e1 != nil {err = e1} else {err = syscall.EINVAL}}return
}//等待事件 WaitForMultipleObjects等待Windows中的所有的内核对象(关于该函数的描述和例子见MSDN)
func WaitForMultipleObjects(objects []syscall.Handle) (err error) {r0, _, e1 := syscall.Syscall6(proc_wait_for_multiple_objects.Addr(), 4, uintptr(len(objects)), uintptr(unsafe.Pointer(&objects[0])), 0, 0xFFFFFFFF, 0, 0)if uint32(r0) == 0xFFFFFFFF {if e1 != 0 {err = error(e1)} else {err = syscall.EINVAL}}return
}var kernel32 = syscall.NewLazyDLL("kernel32.dll")
var (proc_get_console_screen_buffer_info = kernel32.NewProc("GetConsoleScreenBufferInfo")proc_set_console_cursor_position    = kernel32.NewProc("SetConsoleCursorPosition")proc_read_console_input             = kernel32.NewProc("ReadConsoleInputW")proc_wait_for_multiple_objects      = kernel32.NewProc("WaitForMultipleObjects")
)}

以下是贪吃蛇的实现,也可以说是上面系统函数的调用例子

package mainimport ("container/list""errors""fmt""strings""syscall""time""unsafe"
)var (height = 30 //整体高 30行width  = 30 //整体宽 30*2字符宽 origPos  COORDworkArea SMALL_RECT //活动区间food     SHORT //食物占用块empty    = make(map[SHORT]bool) //空白块集合snake    = list.New() //蛇占用块
)func main() {InitSyscall()StartAnimation()snakeHead := popCell()snake.PushFront(snakeHead)delete(empty, snakeHead)food = popCell()drawCell(snakeHead, "■")drawCell(food, "$")err := snakeRun()SetConsoleCursorPosition(COORD{origPos.X, origPos.Y + 2})if err != nil {fmt.Println(err)}fmt.Printf("得分:%v\n", (snake.Len()-1)*100)
}//游戏核心实现
func snakeRun() error {keypress := make(chan rune)go hanldeInput(keypress)direction := 's'for {select {case <-time.Tick(time.Millisecond * 200):break}newhead := snake.Front().Value.(SHORT)var kp runeselect {case kp = <-keypress:default:}switch kp {case 'a':if direction != 'd' {direction = kp}case 'w':if direction != 's' {direction = kp}case 's':if direction != 'w' {direction = kp}case 'd':if direction != 'a' {direction = kp}case 'q':return nil}switch direction {case 'a':newhead = (newhead - (1 << 8))case 'w':newhead = newhead + 1case 's':newhead = newhead - 1case 'd':newhead = (newhead + (1 << 8))}_, exists := empty[newhead]if exists {snake.PushFront(newhead)delete(empty, newhead)drawCell(newhead, "■")if food != newhead {back := snake.Back()snake.Remove(back)tail := back.Value.(SHORT)empty[tail] = truedrawCell(tail, " ")} else {food = popCell()if food == -1 {return errors.New("游戏结束")} else {drawCell(food, "$")}}} else {//游戏失败return errors.New("游戏失败")}}return nil
}//开场动画
func StartAnimation() {for _, ch := range []string{"■", "  "} {for i := 0; i < height; i++ {for n := 0; n < width; n++ {if i == 0 {if n == 0 {fmt.Print("┏")} else if n == width-1 {fmt.Print("┓")} else {fmt.Print("━")}} else if i == height-1 {if n == 0 {fmt.Print("┗")} else if n == width-1 {fmt.Print("┛")} else {fmt.Print("━")}} else {if n == 0 || n == width-1 {fmt.Print("┃")} else {fmt.Print(ch)}}}fmt.Println("")}if ch != "  " {time.Sleep(time.Second * 1)origPos = GetConsoleCursorPosition()origPos.X += 2origPos.Y -= 2SetConsoleCursorPosition(COORD{0, origPos.Y + 2 - SHORT(height)})/*  SetConsoleCursorPosition(COORD{origPos.X, origPos.Y + 1})fmt.Print("A")return */}}workArea.Left = 0workArea.Bottom = 0workArea.Right = SHORT((width - 3))workArea.Top = SHORT(height - 3)for i := workArea.Left; i <= workArea.Right; i++ {for n := workArea.Bottom; n <= workArea.Top; n++ {empty[((i << 8) | (n & 127))] = true}}
}//随机空白块
func popCell() SHORT {for k := range empty {return k}return -1
}
//绘制块
func drawCell(pos SHORT, ch string) {/* if err = checkArea(pos); err != nil {return err} */x := pos >> 8y := pos & 127err := SetConsoleCursorPosition(COORD{(x * 2) + origPos.X, origPos.Y - y})if err != nil {panic(err)}fmt.Print(ch)
}
//处理用户按键
func hanldeInput(keypress chan rune) {for {err := WaitForMultipleObjects([]syscall.Handle{Stdin})if err != nil {panic(err)}var r INPUT_RECORDReadConsoleInput(&r)kr := (*KEY_EVENT_RECORD)(unsafe.Pointer(&r.Event))if kr.KeyDown == 0 {continue}switch kr.VirtualKeyCode {case VK_ARROW_UP:keypress <- 'w'case VK_ARROW_DOWN:keypress <- 's'case VK_ARROW_LEFT:keypress <- 'a'case VK_ARROW_RIGHT:keypress <- 'd'}if kr.UnicodeChar != 0 {keypress <- rune(strings.ToLower(string(kr.UnicodeChar))[0])}}
}

Golang 控制台百行代码贪吃蛇小游戏相关推荐

  1. 【java】百行代码 实现2048小游戏

    2048的代码不是我写的,是从牛客网的项目平台找到的项目 项目代码链接(代码放在了文章最后) https://git.nowcoder.com/11000160/2048-java/blob/mast ...

  2. 100行代码,使用 Pygame 制作一个贪吃蛇小游戏!

    作者 | 周萝卜 来源 | 萝卜大杂烩 相信我们大家都玩过贪吃蛇游戏,今天我们就从头一起来写一个贪吃蛇小游戏,只需要100多行的代码就完成了. 用到的 Pygame 函数 贪吃蛇小游戏用到的函数 功能 ...

  3. python100行代码程序-100行python代码,轻松完成贪吃蛇小游戏

    大家小时候都玩过贪吃蛇吧?小编小时候可喜欢拿爸妈的手机玩了,厉害着呢!今天,小编就来用100行代码实现一个简易版的贪吃蛇.在网上,贪吃蛇教程蛮多的,但要安装蛮多库的,而且也不够清晰,今天的代码比较短, ...

  4. python100行代码-怎样写贪吃蛇小游戏?用100行python代码轻松解决!

    大家小时候都玩过贪吃蛇吧?小编小时候可喜欢拿爸妈的手机玩了,厉害着呢!今天,小编就来用100行代码实现一个简易版的贪吃蛇.在网上,贪吃蛇教程蛮多的,但要安装蛮多库的,而且也不够清晰,今天的代码比较短, ...

  5. bat贪吃蛇游戏代码_100行python代码,轻松完成贪吃蛇小游戏

    来源:DataGo数据狗   链接: https://mp.weixin.qq.com/s/fDcr1SfcbynLzHye7Q7VOg 大家小时候都玩过贪吃蛇吧?小编小时候可喜欢拿爸妈的手机玩了,厉 ...

  6. 如何用不到200行代码实现经典小游戏贪吃蛇,附源代码及详细实现思路

    不多废话,直接上链接 链接:https://pan.baidu.com/s/1ZKtVNhzR4fIzNZSGWgFKQw?pwd=zglt  提取码:zglt 有需要的好兄弟们可以直接取用,想要了解 ...

  7. python50行小游戏_50行python代码实现的贪吃蛇小游戏

    50行python代码实现的贪吃蛇小游戏 发布于 2014-09-01 21:26:24 | 1337 次阅读 | 评论: 1 | 来源: 网友投递 Python编程语言Python 是一种面向对象. ...

  8. python 贪吃蛇小游戏代码_10分钟再用Python编写贪吃蛇小游戏

    Python编写贪吃蛇 前不久我们公众号发布了一篇C++编写贪吃蛇小游戏的推文,反响空前.看来大家对这类简单易上手小游戏还是很喜爱的. 恰逢2018年IEEE Spectrum编程语言排行榜新鲜出炉, ...

  9. python代码,轻松完成贪吃蛇小游戏

    大家小时候都玩过贪吃蛇吧?小编小时候可喜欢拿爸妈的手机玩了,厉害着呢!今天,小编就来用100行代码实现一个简易版的贪吃蛇.在网上,贪吃蛇教程蛮多的,但要安装蛮多库的,而且也不够清晰,今天的代码比较短, ...

最新文章

  1. 纯干货!文字识别在高德地图数据生产中的演进
  2. 基于消息队列 RocketMQ 的大型分布式应用上云较佳实践
  3. 高精度算法(加减乘除取模(均可以处理负数))
  4. 01-Javascript简介
  5. git fork clone 区别_Working with Git | Git 与 GitHub
  6. 【12】Python函数学习(中)
  7. (137)FPGA面试题-Verilog HDL中function和task的区别(一)
  8. C++基于STL的演讲比赛流程管理系统
  9. a5松下驱动器参数设置表_松下伺服驱动器参数设置MSD043A1X
  10. 中兴iptv机顶盒破解教程图文:亲测中兴B760EV3、B860A、B860AV1.1完美安装应用!非ttl破解![转]
  11. 十分钟速成DevOps实践
  12. 经纬度转化为xy坐标系_Arcgis添加经纬度矢量点
  13. [Vulkan教程]绘制一个三角形/呈现/交换链(Swip chain)
  14. 防屏蔽浏览器_国外lead/emu广告联盟平台/扫盲篇–UA,指纹浏览器与流量来路-VMLogin指纹浏览器介绍..
  15. 新手程序员成长之路的五本必读书籍(附资源下载)
  16. 由index为timestamp的数据获取股票集合竞价数据
  17. React hooks - Ref 使用实例
  18. 2012年01月硕士学位毕业生论文目录
  19. 【SAP消息号CK322】
  20. 思考:智能化运维的发展

热门文章

  1. pc(电脑)用java通过adb操作android手机发短信及中文乱码解决
  2. 使用51单片机以及数码管来显示十字路口红绿灯
  3. [转] Python --- 爬虫框架Scrapy at a glance
  4. python中update是啥意思_python中update的基本使用
  5. 用计算机专利检索化工,(完整版)《计算机在化学化工中的应用》教学大纲.pdf
  6. 计算机网口通讯协议,接口通讯协议定义.doc
  7. ios 调用后台数据三级城市选择器
  8. Cocos2Dx之调度器-欧阳左至
  9. html语言有什么优点,什么是HTML?HTML的优点和缺点
  10. android 日历源码解析,Android 4.0日历(calendar)源码分析之概览