基于Golang編寫貪吃蛇游戲
基于終端庫termbox-go做了個(gè)貪吃蛇游戲, 功能較簡(jiǎn)單,代碼約160行左右
一:原理介紹
1. 繪制原理
存儲(chǔ)好蛇身和食物坐標(biāo)都存儲(chǔ)在Snake結(jié)構(gòu)中
定時(shí)300毫秒執(zhí)行移動(dòng)蛇身/生成食物,然后清空終端再重新根據(jù)坐標(biāo)繪制點(diǎn)●
達(dá)到模擬動(dòng)畫效果
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.貪吃蛇移動(dòng)過程
原理很簡(jiǎn)單,根據(jù)當(dāng)前行走方向,追加一個(gè)點(diǎn)到[]Localtion如果蛇頭位置不是食物位置,刪除[]Localtion第一個(gè)點(diǎn), 添加一個(gè)點(diǎn)(最后一個(gè)點(diǎn)位置+1)相當(dāng)于行走了
如果恰好是食物位置,添加一個(gè)點(diǎn)(最后一個(gè)點(diǎn)位置+1),再隨機(jī)生成食物
// 移動(dòng)一步, 如果碰壁返回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á)食物位置時(shí)標(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]
}
// 移動(dòng)一步, 如果碰壁返回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á)食物位置時(shí)標(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()
// 初始給它三個(gè)長(zhǎng)度吧, 太小不好看
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)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用go的interface案例實(shí)現(xiàn)多態(tài)范式操作
這篇文章主要介紹了使用go的interface案例實(shí)現(xiàn)多態(tài)范式操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-12-12
Golang timer可能造成的內(nèi)存泄漏問題分析
本文探討了Golang中timer可能造成的內(nèi)存泄漏問題,通過分析一段代碼,解釋了為什么協(xié)程在調(diào)用timer.Stop()后無法正常退出,文章指出,timer.Stop()并不關(guān)閉Channel,導(dǎo)致協(xié)程無法繼續(xù)執(zhí)行,最后,提出了一種修復(fù)方法,并呼吁大家關(guān)注和分享2024-12-12
Go語言單線程運(yùn)行也會(huì)有的并發(fā)問題解析
這篇文章主要為大家介紹了Go語言單線程運(yùn)行的并發(fā)問題解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12
Linux系統(tǒng)下Go語言開發(fā)環(huán)境搭建
這篇文章主要介紹了Linux系統(tǒng)下Go開發(fā)環(huán)境搭建,需要的朋友可以參考下2022-04-04
beego獲取ajax數(shù)據(jù)的實(shí)例
下面小編就為大家分享一篇beego獲取ajax數(shù)據(jù)的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2017-12-12
Golang反射獲取結(jié)構(gòu)體的值和修改值的代碼示例
這篇文章主要給大家介紹了golang反射獲取結(jié)構(gòu)體的值和修改值的代碼示例及演示效果,對(duì)我們的學(xué)習(xí)或工作有一定的幫助,感興趣的同學(xué)可以參考閱讀本文2023-08-08

