基于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贪吃蛇内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Golang爬虫框架 colly的使用

    Golang爬虫框架 colly的使用

    本文主要介绍了Golang爬虫框架 colly的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-07-07
  • GO的range如何使用详解

    GO的range如何使用详解

    本文主要介绍了GO的range如何使用详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-02-02
  • golang beego框架环境搭建过程

    golang beego框架环境搭建过程

    这篇文章主要为大家介绍了golang beego框架环境搭建的过程脚本,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步早日升职加薪
    2022-04-04
  • Go项目开发中如何读取应用配置详解

    Go项目开发中如何读取应用配置详解

    本文主要介绍了Go项目开发中如何读取应用配置详解,Go生态中有很多包可以加载并解析配置,最受欢迎的是Viper包,下面就来详细的介绍一下
    2024-05-05
  • Golang空接口与类型断言的实现

    Golang空接口与类型断言的实现

    本文主要介绍了Golang空接口与类型断言的实现,文中根据实例编码详细介绍的十分详尽,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03
  • Go语言算法之寻找数组第二大元素的方法

    Go语言算法之寻找数组第二大元素的方法

    这篇文章主要介绍了Go语言算法之寻找数组第二大元素的方法,以实例形式分析了不排序、只循环一次来实现寻找数组第二大元素的技巧,是比较典型的算法,需要的朋友可以参考下
    2015-02-02
  • 使用Golang获取音视频时长信息的示例代码

    使用Golang获取音视频时长信息的示例代码

    这篇文章主要介绍了如何使用Golang获取音视频时长信息,文中通过代码示例讲解的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2024-03-03
  • golang连接redis库及基本操作示例过程

    golang连接redis库及基本操作示例过程

    这篇文章主要介绍了golang连接redis库及基本操作示例过程,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步早日升职加薪
    2022-04-04
  • Go语言中日志统一处理详解

    Go语言中日志统一处理详解

    在现代软件开发中,日志记录是一项至关重要的任务,它不仅帮助开发人员诊断问题,还有助于监控和维护应用程序,本文主要来和大家聊聊日志的统一处理,感兴趣的小伙伴可以了解下
    2024-01-01
  • Golang内存对齐的规则及实现

    Golang内存对齐的规则及实现

    本文介绍了Golang内存对齐的规则及实现,通过合理的内存对齐,可以提高程序的执行效率和性能,通过对本文的阅读,读者可以更好地理解Golang内存对齐的原理和技巧,并应用于实际编程中
    2023-08-08

最新评论