基于Golang編寫貪吃蛇游戲
基于終端庫termbox-go做了個貪吃蛇游戲, 功能較簡單,代碼約160行左右
一:原理介紹
1. 繪制原理
存儲好蛇身和食物坐標(biāo)都存儲在Snake結(jié)構(gòu)中
定時300毫秒執(zhí)行移動蛇身/生成食物,然后清空終端再重新根據(jù)坐標(biāo)繪制點●
達(dá)到模擬動畫效果
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.貪吃蛇移動過程
原理很簡單,根據(jù)當(dāng)前行走方向,追加一個點到[]Localtion如果蛇頭位置不是食物位置,刪除[]Localtion第一個點, 添加一個點(最后一個點位置+1)相當(dāng)于行走了
如果恰好是食物位置,添加一個點(最后一個點位置+1),再隨機(jī)生成食物
// 移動一步, 如果碰壁返回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()
// 蛇頭到達(dá)食物位置時標(biāo)記食物已吃,并且追加到蛇尾(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.生成食物過程
僅需要注意是否生成在蛇身本身,是的話再生成
// 判斷生成的食物坐標(biāo)是否在蛇身上
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()
// 蛇頭到達(dá)食物位置時標(biāo)記食物已吃,并且追加到蛇尾(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
}
// 判斷生成的食物坐標(biāo)是否在蛇身上
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)
}
}
}
}到此這篇關(guān)于基于Golang編寫貪吃蛇游戲的文章就介紹到這了,更多相關(guān)Golang貪吃蛇內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Golang內(nèi)存對齊的規(guī)則及實現(xiàn)
本文介紹了Golang內(nèi)存對齊的規(guī)則及實現(xiàn),通過合理的內(nèi)存對齊,可以提高程序的執(zhí)行效率和性能,通過對本文的閱讀,讀者可以更好地理解Golang內(nèi)存對齊的原理和技巧,并應(yīng)用于實際編程中2023-08-08

