Golang

关注公众号 jb51net

关闭
首页 > 脚本专栏 > Golang > Golang贪吃蛇

基于Golang编写贪吃蛇游戏

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

您可能感兴趣的文章:
阅读全文