写一个用Golang运行的小游戏打发时间---贪吃蛇
为了实现可视化,我使用了Go的GUI库fyne

思路:

  1. 定义一个结构体Snake表示贪吃蛇,包含贪吃蛇的长度、方向、身体坐标等属性。
  2. 定义一个结构体Game表示游戏状态,包含贪吃蛇、食物、得分等属性。
  3. 实现游戏画面的显示,使用fyne库中的Canvas组件,绘制贪吃蛇和食物的图形。
  4. 实现键盘事件的监听,根据不同的按键来改变贪吃蛇的方向。
  5. 实现贪吃蛇的移动逻辑,每个时间间隔更新贪吃蛇的位置,判断是否吃到食物或碰到墙壁或自己的身体,更新得分并重新生成食物。
  6. 完成游戏结束的处理,当贪吃蛇碰到墙壁或自己的身体时,游戏结束,显示得分和重新开始按钮。

代码:

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()

这就是一个简单的贪吃蛇游戏的实现。由于篇幅限制,这里只是提供了一个基本的实现,并未对游戏进行优化或添加更多的功能。