基于Golang編寫貪吃蛇游戲
基于終端庫termbox-go
做了個貪吃蛇游戲, 功能較簡單,代碼約160行左右
一:原理介紹
1. 繪制原理
存儲好蛇身和食物坐標(biāo)都存儲在Snake結(jié)構(gòu)中
定時300毫秒執(zhí)行移動蛇身/生成食物,然后清空終端再重新根據(jù)坐標(biāo)繪制點(diǎn)●
達(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)前行走方向,追加一個點(diǎn)到[]Localtion
如果蛇頭位置不是食物位置,刪除[]Localtion
第一個點(diǎn), 添加一個點(diǎn)(最后一個點(diǎn)位置+1
)相當(dāng)于行走了
如果恰好是食物位置,添加一個點(diǎn)(最后一個點(diǎn)位置+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() // 上下兩邊留點(diǎn)空隙 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() // 上下兩邊留點(diǎn)空隙 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)文章
Go項(xiàng)目開發(fā)中如何讀取應(yīng)用配置詳解
本文主要介紹了Go項(xiàng)目開發(fā)中如何讀取應(yīng)用配置詳解,Go生態(tài)中有很多包可以加載并解析配置,最受歡迎的是Viper包,下面就來詳細(xì)的介紹一下2024-05-05Golang內(nèi)存對齊的規(guī)則及實(shí)現(xiàn)
本文介紹了Golang內(nèi)存對齊的規(guī)則及實(shí)現(xiàn),通過合理的內(nèi)存對齊,可以提高程序的執(zhí)行效率和性能,通過對本文的閱讀,讀者可以更好地理解Golang內(nèi)存對齊的原理和技巧,并應(yīng)用于實(shí)際編程中2023-08-08