写一个用Golang运行的小游戏打发时间---贪吃蛇
为了实现可视化,我使用了Go的GUI库fyne
思路:
- 定义一个结构体Snake表示贪吃蛇,包含贪吃蛇的长度、方向、身体坐标等属性。
- 定义一个结构体Game表示游戏状态,包含贪吃蛇、食物、得分等属性。
- 实现游戏画面的显示,使用fyne库中的Canvas组件,绘制贪吃蛇和食物的图形。
- 实现键盘事件的监听,根据不同的按键来改变贪吃蛇的方向。
- 实现贪吃蛇的移动逻辑,每个时间间隔更新贪吃蛇的位置,判断是否吃到食物或碰到墙壁或自己的身体,更新得分并重新生成食物。
- 完成游戏结束的处理,当贪吃蛇碰到墙壁或自己的身体时,游戏结束,显示得分和重新开始按钮。
代码:
package main
import (
"fmt"
"image/color"
"math/rand"
"time"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
)
const (
cellSize = 20 // 每个格子的大小
gridWidth = 30 // 游戏区域的宽度(格子数)
gridHeight = 20 // 游戏区域的高度(格子数)
)
var (
directions = []fyne.Key{
fyne.KeyUp,
fyne.KeyDown,
fyne.KeyLeft,
fyne.KeyRight,
}
)
// Snake表示贪吃蛇
type Snake struct {
Body []fyne.Position // 身体坐标
Length int // 长度
Direction fyne.Key // 方向
lastMoveAt time.Time // 上次移动时间
}
// NewSnake创建一个新的贪吃蛇
func NewSnake() Snake {
head := fyne.NewPos(cellSize*gridWidth/2, cellSize*gridHeight/2)
return Snake{
Body: []fyne.Position{head},
Length: 1,
Direction: fyne.KeyRight,
lastMoveAt: time.Now(),
}
}
// Move移动贪吃蛇
func (s *Snake) Move() {
if time.Since(s.lastMoveAt) < 100*time.Millisecond {
return
}
head := s.Body[0]
var newHead fyne.Position
switch s.Direction {
case fyne.KeyUp:
newHead = fyne.NewPos(head.X, head.Y-cellSize)
case fyne.KeyDown:
newHead = fyne.NewPos(head.X, head.Y+cellSize)
case fyne.KeyLeft:
newHead = fyne.NewPos(head.X-cellSize, head.Y)
case fyne.KeyRight:
newHead = fyne.NewPos(head.X+cellSize, head.Y)
}
s.Body = append([]fyne.Position{newHead}, s.Body[:s.Length-1]...)
s.lastMoveAt = time.Now()
}
// EatFood吃掉食物
func (s *Snake) EatFood() {
s.Length++
s.Body = append([]fyne.Position{s.Body[0]}, s.Body...)
}
// CollideWith自己的身体
func (s *Snake) CollideWithSelf() bool {
head := s.Body[0]
for _, b := range s.Body[1:] {
if head == b {
return true
}
}
return false
}
// CollideWithWall撞到墙壁
func (s *Snake) CollideWithWall() bool {
head := s.Body[0]
return head.X < 0 || head.X >= cellSize*gridWidth || head.Y < 0 || head.Y >= cellSize*gridHeight
}
// Game表示游戏状态
type Game struct {
Snake Snake // 贪吃蛇
Food fyne.Position // 食物坐标
Score int // 得分
Over bool // 游戏是否结束
WinSize fyne.Size // 窗口大小
}
// NewGame创建一个新的游戏状态
func NewGame(winSize fyne.Size) *Game {
return &Game{
Snake: NewSnake(),
Food: fyne.NewPos(rand.Intn(gridWidth)*cellSize, rand.Intn(gridHeight)*cellSize),
WinSize: winSize,
}
}
// Update更新游戏状态
func (g *Game) Update() {
if g.Over {
return
}
g.Snake.Move()
if g.Snake.CollideWithSelf() || g.Snake.CollideWithWall() {
g.Over = true
return
}
if g.Snake.Body[0] == g.Food {
g.Snake.EatFood()
g.Food = fyne.NewPos(rand.Intn(gridWidth)*cellSize, rand.Intn(gridHeight)*cellSize)
g.Score++
}
}
// Draw绘制游戏画面
func (g *Game) Draw() fyne.CanvasObject {
snakeColor := color.RGBA{0, 255, 0, 255}
foodColor := color.RGBA{255, 0, 0, 255}
objects := []fyne.CanvasObject{}
// 绘制贪吃蛇
for _, b := range g.Snake.Body {
objects = append(objects, canvas.NewRectangle(snakeColor).SetMinSize(fyne.NewSize(cellSize, cellSize)).SetPosition(b))
}
// 绘制食物
objects = append(objects, canvas.NewCircle(foodColor, cellSize/2).SetPosition(g.Food).SetMinSize(fyne.NewSize(cellSize, cellSize)))
// 绘制得分
scoreLabel := widget.NewLabel(fmt.Sprintf("Score: %d", g.Score))
scoreLabel.Move(fyne.NewPos(g.WinSize.Width-scoreLabel.MinSize().Width-10, 10))
objects = append(objects, scoreLabel)
return container.NewWithoutLayout(objects...)
}
// SetDirection设置贪吃蛇的方向
func (g *Game) SetDirection(direction fyne.Key) {
if direction == fyne.KeyReturn && g.Over {
*g = *NewGame(g.WinSize)
return
}
if contains(directions, direction) {
g.Snake.Direction = direction
}
}
// contains检查是否包含指定元素
func contains(keys []fyne.Key, key fyne.Key) bool {
for _, k := range keys {
if k == key {
return true
}
}
return false
}
NewGameUpdateDrawSetDirection
mainfyne
func main() {
// 创建一个新的游戏对象
game := NewGame(fyne.NewSize(gridWidth*cellSize, gridHeight*cellSize))
// 创建一个窗口
app := app.New()
window := app.NewWindow("Snake Game")
window.Resize(game.WinSize)
// 设置事件处理程序
window.SetContent(canvas.NewRasterWithPixels(func(w, h int) []byte {
img := image.NewRGBA(image.Rect(0, 0, w, h))
draw.Draw(img, img.Bounds(), &image.Uniform{color.Black}, image.ZP, draw.Src)
// 绘制游戏画面
canvasObj := game.Draw()
canvasObj.Paint(img)
return img.Pix
}))
// 监听按键事件
window.Canvas().SetOnKeyDown(func(event *fyne.KeyEvent) {
game.SetDirection(event.Name)
})
// 定期更新游戏状态
go func() {
for {
time.Sleep(time.Second / fps)
game.Update()
window.Canvas().Refresh(game.Draw)
}
}()
// 显示窗口
window.ShowAndRun()
}
mainfynegame.Draw
game.SetDirectionwindow.ShowAndRun()
这就是一个简单的贪吃蛇游戏的实现。由于篇幅限制,这里只是提供了一个基本的实现,并未对游戏进行优化或添加更多的功能。