使用 Golang 編寫 2D 遊戲
Ebitengine 是一款基於 Go 語言的 2D 遊戲開發引擎,提供了簡單易用的 API 和跨平臺的支持,開發者可以通過它輕鬆構建遊戲項目。
- 核心特性
- 跨平臺支持
Ebitengine 支持多個平臺,包括 Windows、macOS、Linux,以及 WebAssembly(可運行於瀏覽器中)。這使得開發者可以在不同設備上運行同一個遊戲項目,而無需編寫額外的代碼。
- 易用的 API
Ebitengine 的 API 設計簡單直觀,Go 開發者可以快速上手。通過定義遊戲邏輯的 Update
和渲染的 Draw
方法,開發者可以輕鬆實現遊戲的核心功能。
- 圖像與音頻支持
Ebitengine 內置了圖像渲染和音頻播放的支持,開發者無需依賴額外的庫即可加載圖像資源和播放音效。
- 高性能
由於 Go 語言本身的高效性和 Ebitengine 的優化設計,遊戲的運行性能可以滿足大多數 2D 遊戲的需求。
- 案例:創建一個基礎的 “打磚塊” 遊戲
下面我們將通過一個簡單的 “打磚塊” 遊戲案例,展示如何使用 Ebitengine 創建一個完整的遊戲。
功能描述
-
玩家控制屏幕底部的擋板,反彈小球擊打上方的磚塊。
-
當小球擊中磚塊時,磚塊消失並增加得分。
-
小球掉出屏幕時遊戲結束。
2.1 目錄結構
brickbreaker/
|-- main.go
|-- assets/
|-- paddle.png
|-- ball.png
|-- brick.png
圖片可以使用 python 進行生成簡單的,大家也可以去一些資源網站通過搜索關鍵字(比如磚塊,球,擋板)來獲取
from PIL import Image, ImageDraw
# 生成 paddle.png
paddle = Image.new("RGBA", (100, 20), "white")
paddle.save("paddle.png")
# 生成 ball.png
ball = Image.new("RGBA", (16, 16), (0, 0, 0, 0)) # 透明背景
draw = ImageDraw.Draw(ball)
draw.ellipse((0, 0, 15, 15), fill="white")
ball.save("ball.png")
# 生成 brick.png
brick = Image.new("RGBA", (50, 20), "red")
brick.save("brick.png")
2.2 代碼實現
package main
import (
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
"image/color"
"log"
"strconv"
)
const (
ScreenWidth = 800
ScreenHeight = 600
PaddleWidth = 100
PaddleHeight = 20
BallSize = 16
BrickWidth = 75
BrickHeight = 20
BrickRows = 5
BrickColumns = 10
)
type Brick struct {
x float64
y float64
active bool
}
type Game struct {
paddleX float64
ballX float64
ballY float64
ballVX float64
ballVY float64
bricks []Brick
score int
gameOver bool
}
func (g *Game) initializeBricks() {
g.bricks = nil
padding := (ScreenWidth - (BrickColumns * BrickWidth)) / (BrickColumns + 1)
for row := 0; row < BrickRows; row++ {
for col := 0; col < BrickColumns; col++ {
brick := Brick{
x: float64(padding + col*(BrickWidth+padding)),
y: float64(50 + row*(BrickHeight+10)), // 距離頂部 50px
active: true,
}
g.bricks = append(g.bricks, brick)
}
}
}
func (g *Game) Update() error {
if g.gameOver {
return nil
}
// 玩家控制擋板
if ebiten.IsKeyPressed(ebiten.KeyLeft) {
g.paddleX -= 5
if g.paddleX < 0 {
g.paddleX = 0
}
}
if ebiten.IsKeyPressed(ebiten.KeyRight) {
g.paddleX += 5
if g.paddleX > ScreenWidth-PaddleWidth {
g.paddleX = ScreenWidth - PaddleWidth
}
}
// 更新小球位置
g.ballX += g.ballVX
g.ballY += g.ballVY
// 小球反彈邏輯
if g.ballX < 0 || g.ballX > ScreenWidth-BallSize {
g.ballVX *= -1
}
if g.ballY < 0 {
g.ballVY *= -1
}
// 小球與擋板碰撞檢測
if g.ballY+BallSize >= ScreenHeight-PaddleHeight &&
g.ballX+BallSize >= g.paddleX && g.ballX <= g.paddleX+PaddleWidth {
g.ballVY *= -1
}
// 小球與磚塊碰撞檢測
for i := range g.bricks {
brick := &g.bricks[i]
if brick.active &&
g.ballX+BallSize > brick.x && g.ballX < brick.x+BrickWidth &&
g.ballY+BallSize > brick.y && g.ballY < brick.y+BrickHeight {
brick.active = false
g.ballVY *= -1
g.score += 10
break
}
}
// 遊戲結束檢測:小球掉出屏幕或所有磚塊被擊毀
if g.ballY > ScreenHeight || g.allBricksDestroyed() {
g.gameOver = true
log.Printf("Game Over! Final Score: %d\n", g.score)
}
return nil
}
func (g *Game) allBricksDestroyed() bool {
for _, brick := range g.bricks {
if brick.active {
return false
}
}
return true
}
func (g *Game) Draw(screen *ebiten.Image) {
// 繪製背景
screen.Fill(color.RGBA{0, 0, 0, 255})
// 繪製擋板
paddleRect := ebiten.NewImage(PaddleWidth, PaddleHeight)
paddleRect.Fill(color.White)
geoM := ebiten.GeoM{}
geoM.Translate(g.paddleX, ScreenHeight-PaddleHeight)
screen.DrawImage(paddleRect, &ebiten.DrawImageOptions{
GeoM: geoM,
})
// 繪製小球
ballRect := ebiten.NewImage(BallSize, BallSize)
ballRect.Fill(color.White)
screen.DrawImage(ballRect, &ebiten.DrawImageOptions{
GeoM: func() ebiten.GeoM {
geom := ebiten.GeoM{}
geom.Translate(g.ballX, g.ballY)
return geom
}(),
})
// 繪製磚塊
for _, brick := range g.bricks {
if brick.active {
brickRect := ebiten.NewImage(BrickWidth, BrickHeight)
brickRect.Fill(color.RGBA{255, 0, 0, 255})
geom := ebiten.GeoM{}
geom.Translate(brick.x, brick.y)
screen.DrawImage(brickRect, &ebiten.DrawImageOptions{
GeoM: geom,
})
}
}
// 繪製分數
ebitenutil.DebugPrint(screen, "Score: "+strconv.Itoa(g.score))
// 遊戲結束提示
if g.gameOver {
ebitenutil.DebugPrintAt(screen, "Game Over! Press ESC to exit.", 300, ScreenHeight/2)
}
}
func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
return ScreenWidth, ScreenHeight
}
func main() {
game := &Game{
paddleX: ScreenWidth / 2,
ballX: ScreenWidth / 2,
ballY: ScreenHeight / 2,
ballVX: 4,
ballVY: -4,
}
game.initializeBricks()
if err := ebiten.RunGame(game); err != nil {
log.Fatal(err)
}
}
你將看到一個簡單的遊戲界面,使用鍵盤方向鍵控制擋板,並嘗試反彈小球擊打磚塊。
2.3 環境說明
-
確保你已經安裝了 Go 語言環境。
-
安裝 Ebitengine:
go get github.com/hajimehoshi/ebiten/v2
-
運行代碼:
go run main.go
-
結語
Ebitengine 爲 Go 開發者提供了強大的 2D 遊戲開發能力,案例中的 “打磚塊” 遊戲僅展示了其基本功能。通過靈活使用其 API,你可以開發更復雜的遊戲,如 RPG、平臺跳躍、射擊等。未來,你可以嘗試結合 Ebitengine 的音效、動畫等高級特性,爲你的遊戲增添更多樂趣與深度。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/vmkiOVUEGSTzzONjFx8cFA