基于Golang编写贪吃蛇游戏

 更新时间:2023年07月28日 16:59:49   作者:LeoForBest  
这篇文章主要为大家学习介绍了Golang如何基于终端库termbox-go做个功能较简单的贪吃蛇游戏,文中的示例代码讲解详细,具有一定的学习价值

基于终端库termbox-go做了个贪吃蛇游戏, 功能较简单,代码约160行左右

一:原理介绍

1. 绘制原理

存储好蛇身和食物坐标都存储在Snake结构中

定时300毫秒执行移动蛇身/生成食物,然后清空终端再重新根据坐标绘制点●

达到模拟动画效果

type Location struct {
	X int
	Y int
}
type Snake struct {
	Body      []Location
	Food      Location
	......
}
func Draw(s *Snake) {
	termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
	for _, location := range s.Body {
		termbox.SetCell(location.X, location.Y, '●', termbox.ColorGreen, termbox.ColorDefault)
	}
	termbox.SetCell(s.Food.X, s.Food.Y, '●', termbox.ColorRed, termbox.ColorDefault)
	termbox.Flush()
}

2.贪吃蛇移动过程

原理很简单,根据当前行走方向,追加一个点到[]Localtion如果蛇头位置不是食物位置,删除[]Localtion第一个点, 添加一个点(最后一个点位置+1)相当于行走了

如果恰好是食物位置,添加一个点(最后一个点位置+1),再随机生成食物

// 移动一步, 如果碰壁返回false, 否则返回true
func (s *Snake) Move() bool {
	head := s.GetHead()
	switch s.Direction {
	case DIRECTION_UP:
		s.Body = append(s.Body, Location{head.X, head.Y - 1})
	case DIRECTION_DOWN:
		s.Body = append(s.Body, Location{head.X, head.Y + 1})
	case DIRECTION_LEFT:
		s.Body = append(s.Body, Location{head.X - 1, head.Y})
	case DIRECTION_RIGHT:
		s.Body = append(s.Body, Location{head.X + 1, head.Y})
	}
	head = s.GetHead()
	// 蛇头到达食物位置时标记食物已吃,并且追加到蛇尾(s.Body[0]不用剔除, 否则剔除)
	if head == s.Food {
		s.FoodEated = true
		s.RandomFood()
		s.Score += 10
	} else {
		s.Body = s.Body[1:]
	}
	return 0 <= head.X && head.X <= s.MaxX && 0 <= head.Y && head.Y <= s.MaxY
}

3.生成食物过程

仅需要注意是否生成在蛇身本身,是的话再生成

// 判断生成的食物坐标是否在蛇身上
func (s *Snake) isFoodInSnake(location Location) bool {
	for _, l := range s.Body {
		if l == location {
			return true
		}
	}
	return false
}
// 生成食物
func (s *Snake) RandomFood() {
	w, h := termbox.Size()
	// 上下两边留点空隙
	location := Location{rand.Intn(w-10) + 5, rand.Intn(h-10) + 5}
	for s.isFoodInSnake(location) {
		location = Location{rand.Intn(w), rand.Intn(h)}
	}
	s.Food = location
}

4.效果

二:完整代码

package main
import (
	"fmt"
	"math/rand"
	"time"
	"github.com/nsf/termbox-go"
)
const (
	DIRECTION_LEFT int = iota
	DIRECTION_RIGHT
	DIRECTION_UP
	DIRECTION_DOWN
)
type Location struct {
	X int
	Y int
}
type Snake struct {
	Body      []Location
	Food      Location
	FoodEated bool
	Direction int
	MaxX      int
	MaxY      int
	Score     int
}
// 获取蛇头位置
func (s *Snake) GetHead() Location {
	return s.Body[len(s.Body)-1]
}
// 移动一步, 如果碰壁返回false, 否则返回true
func (s *Snake) Move() bool {
	head := s.GetHead()
	switch s.Direction {
	case DIRECTION_UP:
		s.Body = append(s.Body, Location{head.X, head.Y - 1})
	case DIRECTION_DOWN:
		s.Body = append(s.Body, Location{head.X, head.Y + 1})
	case DIRECTION_LEFT:
		s.Body = append(s.Body, Location{head.X - 1, head.Y})
	case DIRECTION_RIGHT:
		s.Body = append(s.Body, Location{head.X + 1, head.Y})
	}
	head = s.GetHead()
	// 蛇头到达食物位置时标记食物已吃,并且追加到蛇尾(s.Body[0]不用剔除, 否则剔除)
	if head == s.Food {
		s.FoodEated = true
		s.RandomFood()
		s.Score += 10
	} else {
		s.Body = s.Body[1:]
	}
	return 0 <= head.X && head.X <= s.MaxX && 0 <= head.Y && head.Y <= s.MaxY
}
// 判断生成的食物坐标是否在蛇身上
func (s *Snake) isFoodInSnake(location Location) bool {
	for _, l := range s.Body {
		if l == location {
			return true
		}
	}
	return false
}
// 生成食物
func (s *Snake) RandomFood() {
	w, h := termbox.Size()
	// 上下两边留点空隙
	location := Location{rand.Intn(w-10) + 5, rand.Intn(h-10) + 5}
	for s.isFoodInSnake(location) {
		location = Location{rand.Intn(w), rand.Intn(h)}
	}
	s.Food = location
}
func Draw(s *Snake) {
	termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
	for _, location := range s.Body {
		termbox.SetCell(location.X, location.Y, '●', termbox.ColorGreen, termbox.ColorDefault)
	}
	termbox.SetCell(s.Food.X, s.Food.Y, '●', termbox.ColorRed, termbox.ColorDefault)
	termbox.Flush()
}
func main() {
	err := termbox.Init()
	if err != nil {
		panic(err)
	}
	defer termbox.Close()
	w, h := termbox.Size()
	// 初始给它三个长度吧, 太小不好看
	snake := Snake{
		Body:      []Location{{0, 0}, {1, 0}, {2, 0}},
		Direction: DIRECTION_RIGHT,
		MaxX:      w,
		MaxY:      h,
		FoodEated: false,
	}
	snake.RandomFood()
	Draw(&snake)
	event_queue := make(chan termbox.Event)
	go func() {
		for {
			event_queue <- termbox.PollEvent()
		}
	}()
	gameFinished := false
	msgPrinted := false
	msg := `\n
*****************************************
		Game Over !
		Score: %d
		Press Esc to exit!
*****************************************
`
loop:
	for {
		select {
		case ev := <-event_queue:
			if ev.Type == termbox.EventKey && ev.Key == termbox.KeyEsc {
				break loop
			} else if ev.Type == termbox.EventKey {
				switch ev.Key {
				case termbox.KeyArrowUp:
					snake.Direction = DIRECTION_UP
				case termbox.KeyArrowDown:
					snake.Direction = DIRECTION_DOWN
				case termbox.KeyArrowLeft:
					snake.Direction = DIRECTION_LEFT
				case termbox.KeyArrowRight:
					snake.Direction = DIRECTION_RIGHT
				}
			}
		default:
			time.Sleep(300 * time.Millisecond)
			if gameFinished && !msgPrinted {
				termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
				termbox.Flush()
				fmt.Printf(msg, snake.Score)
				msgPrinted = true
			} else {
				if success := snake.Move(); !success {
					gameFinished = true
				}
				Draw(&snake)
			}
		}
	}
}

到此这篇关于基于Golang编写贪吃蛇游戏的文章就介绍到这了,更多相关Golang贪吃蛇内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 详解如何使用Go的Viper来解析配置信息

    详解如何使用Go的Viper来解析配置信息

    Viper库为Golang语言开发者提供了对不同数据源和不同格式的配置文件的读取,是Go项目读取配置的神器,我们今天就来讲讲如何使用Viper来解析配置信息,文中通过代码示例讲解非常详细,需要的朋友可以参考下
    2024-01-01
  • golang中判断请求是http还是https获取当前访问地址

    golang中判断请求是http还是https获取当前访问地址

    这篇文章主要为大家介绍了golang中判断请求是http还是https获取当前访问地址示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-10-10
  • Go语言Zap日志库使用教程

    Go语言Zap日志库使用教程

    在项目开发中,经常需要把程序运行过程中各种信息记录下来,有了详细的日志有助于问题排查和功能优化;但如何选择和使用性能好功能强大的日志库,这个就需要我们从多角度考虑
    2023-02-02
  • Golang中crypto/cipher加密标准库全面指南

    Golang中crypto/cipher加密标准库全面指南

    本文主要介绍了Golang中crypto/cipher加密标准库,包括对称加密、非对称加密以及使用流加密和块加密算法,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧
    2024-02-02
  • go语言中int和byte转换方式

    go语言中int和byte转换方式

    这篇文章主要介绍了go语言中int和byte转换方式,需要的朋友可以参考下
    2018-11-11
  • HTTP服务压力测试工具及相关术语讲解

    HTTP服务压力测试工具及相关术语讲解

    这篇文章主要为大家介绍了HTTP服务压力测试工具使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步早日升职加薪
    2022-04-04
  • golang内置函数len的小技巧

    golang内置函数len的小技巧

    len是很常用的内置函数,可以测量字符串、slice、array、channel以及map的长度/元素个数。本文就来介绍一下其他小技巧,感兴趣的可以了解一下
    2021-07-07
  • Go语言学习之数组的用法详解

    Go语言学习之数组的用法详解

    数组是相同数据类型的一组数据的集合,数组一旦定义长度不能修改,数组可以通过下标(或者叫索引)来访问元素。本文将通过示例详细讲解Go语言中数组的使用,需要的可以参考一下
    2022-04-04
  • Go语言之init函数

    Go语言之init函数

    Go语言有一个特殊的函数init,先于main函数执行,实现包级别的一些初始化操作。这篇文章介绍了Go中的Init函数,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-07-07
  • golang中拿slice当queue和拿list当queue使用分析

    golang中拿slice当queue和拿list当queue使用分析

    这篇文章主要为大家介绍了golang 中拿slice当queue和拿list当queue使用分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-08-08

最新评论