在我們平常玩的踩地雷(Minesweeper)遊戲裡,最常見的功能之一就是「選擇難度」。
一般來說會有三種經典模式:
這個功能看似簡單,但在實作時有不少設計上的小細節值得思考。今天就來分享一下我是如何在 Golang + Ebiten 遊戲框架中完成「難度選擇」的。
如果遊戲只有單一難度,玩家會很快失去新鮮感。
難度選擇除了增加遊戲的重玩性之外,也讓遊戲能夠滿足不同層次的玩家需求。
因此,我們需要一個直覺的方式,讓玩家能在遊戲開始前(或重來時)選擇不同難度。
在 UI 設計上,有兩種常見作法:
在這裡我選擇第二種方式:一個按鈕,不同 icon 表示不同難度。
原因是踩地雷遊戲畫面已經很擠了(棋盤很大),UI 應該保持簡潔
以下是簡化後的範例:
紀錄難度選項
type Level int
const (
Easy Level = iota
Medium
Hard
)
// 遊戲畫面狀態
type GameLayout struct {
gameInstance *game.Game // 遊戲物件
ClickCoord *Coord // 使用者點擊座標
elapsedTime int // 經過時間
Rows int // 紀錄遊戲 Row 大小
Cols int // 紀錄遊戲 Col 大小
MineCounts int // 紀錄 MineCounts
ScreenHeight int
ScreenWidth int
level Level
}
func (g *GameLayout) ChangeLevel() {
g.level = (g.level + 1) % 3
}
// Restart - 重新建立 Game 狀態
func (g *GameLayout) Restart() {
g.Rows = LevelSetupMap[g.level].Rows
g.Cols = LevelSetupMap[g.level].Cols
g.MineCounts = LevelSetupMap[g.level].MineCounts
g.ScreenHeight = PanelHeight + gridSize*g.Rows
g.ScreenWidth = gridSize * g.Cols
ebiten.SetWindowSize(g.ScreenWidth, g.ScreenHeight)
ebiten.SetWindowTitle(fmt.Sprintf("%s Mine Sweeper Grid", LevelMessage[g.level]))
g.gameInstance = game.NewGame(g.Rows, g.Cols, g.MineCounts)
}
// 偵測 level icon 有被點擊
if inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft) {
xPos, yPos := ebiten.CursorPosition()
if xPos >= ((g.ScreenWidth-1.5*gridSize)/2+buttonRectRelativePos.Min.X) &&
xPos <= (g.ScreenWidth)/2+buttonRectRelativePos.Max.X+0.5*gridSize &&
yPos >= buttonRectRelativePos.Min.Y &&
yPos <= buttonRectRelativePos.Max.Y+3 {
g.ChangeLevel()
g.Restart()
}
}
Easy
Medium
Hard
為了讓玩家一眼就能辨識當前難度,我設計了三種 icon:
Easy → 🌱 小嫩芽圖案(代表入門)
Medium → ⏳ 沙漏圖案(時間挑戰)
Hard → 💣 地雷圖案(最終 boss)
這樣一來,光看按鈕圖示就知道當前遊戲模式,省去了讀文字的時間。
難度選擇雖然是個小功能,但卻大幅提升了遊戲的完整性。
這個設計同時兼顧了:
明天將要開始撰寫數獨遊戲規則與玩法理解的相關內容
講會說明數獨 9x9 規則、遊戲目標與勝負條件