3. 鍵盤、手把和滑鼠處理——在 Golang 透過 Ebiten(炸蝦)來製作 8-Bit 遊戲!

一個不能和使用者互動的遊戲會令人感到十分地無聊,不是嗎?這次你會學到如何在 Ebiten 中偵測鍵盤或是滑鼠和手把的輸入,所以你就可以在玩家按下特定按鍵的時候進行相關回饋。

現在讓我們回到第一章的範例,因為那個範例就足夠應用在這次的教學了:

package main

import (  
    "github.com/hajimehoshi/ebiten"
    "github.com/hajimehoshi/ebiten/ebitenutil"
)

func update(screen *ebiten.Image) error {  
    ebitenutil.DebugPrint(screen, "Our first game in Ebiten!")
    return nil
}

func main() {  
    ebiten.Run(update, 320, 240, 2, "Hello world!")
}

鍵盤輸入

Ebiten 有先幫我們定義了不少的按鍵,近乎所有的鍵盤按鈕滑鼠三鍵),還有遊戲把手上的 15 個按鍵你可以在這邊的文件找到

因為鍵盤按鍵太多這裡就先示範這些基本按鍵,記住!其他按鍵都在上面的文件中可以找到

ebiten.KeyUp,   // 上  
ebiten.KeyDown, // 下  
ebiten.KeyLeft, // 左  
ebiten.KeyRight // 右  

偵測按鍵

現在你知道有哪些按鍵了,接下來要做的就是偵測一個按鍵是否被按下,為此,Ebiten 提供了 ebiten.IsKeyPressed(key Key) 函式,其中要傳入的 key 值就是先前提到的按鍵,例如 ebiten.KeyUp 就是「方向鍵上」,當按鈕被按下時,這個函式就會回傳 true

現在重寫你的 update() 函式,在這裡我們要搭配先前的顯示除錯文字函式來在按鈕被按下時顯示些文字到螢幕上

func update(screen *ebiten.Image) error {  
    // 當「按鍵上」被按下時⋯⋯
    if ebiten.IsKeyPressed(ebiten.KeyUp) {
        ebitenutil.DebugPrint(screen, "You're pressing the 'UP' button.")
    }
    // 當「按鍵下」被按下時⋯⋯
    if ebiten.IsKeyPressed(ebiten.KeyDown) {
        ebitenutil.DebugPrint(screen, "\nYou're pressing the 'DOWN' button.")
    }
    // 當「按鍵左」被按下時⋯⋯
    if ebiten.IsKeyPressed(ebiten.KeyLeft) {
        ebitenutil.DebugPrint(screen, "\n\nYou're pressing the 'LEFT' button.")
    }
    // 當「按鍵右」被按下時⋯⋯
    if ebiten.IsKeyPressed(ebiten.KeyRight) {
        ebitenutil.DebugPrint(screen, "\n\n\nYou're pressing the 'RIGHT' button.")
    }
    return nil
}

順帶一提,為了避免所有文字都重疊在同一行,我們用到了 \n 換行符號。現在執行你的遊戲,然後按按看你的方向鍵,是不是有變化了呢?

滑鼠輸入

滑鼠輸入和鍵盤輸入的偵測函式不太一樣,這點你可以在下列範例看見,但其實最終做法也都一樣。在 Ebiten 中你可以用下列三種鍵值偵測滑鼠按鍵。

ebiten.MouseButtonLeft,  // 滑鼠左鍵  
ebiten.MouseButtonRight, // 滑鼠右鍵  
ebiten.MouseButtonMiddle // 滑鼠中鍵  

現在讓我們再次改寫你的 update() 函式,要注意的是這次的 IsKeyPressed() 被替換成 IsMouseButtonPressed() 了。

func update(screen *ebiten.Image) error {  
    // 當「滑鼠左鍵」被按下時⋯⋯
    if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) {
        ebitenutil.DebugPrint(screen, "You're pressing the 'LEFT' mouse button.")
    }
    // 當「滑鼠右鍵」被按下時⋯⋯
    if ebiten.IsMouseButtonPressed(ebiten.MouseButtonRight) {
        ebitenutil.DebugPrint(screen, "\nYou're pressing the 'RIGHT' mouse button.")
    }
    // 當「滑鼠中鍵」被按下時⋯⋯
    if ebiten.IsMouseButtonPressed(ebiten.MouseButtonMiddle) {
        ebitenutil.DebugPrint(screen, "\n\nYou're pressing the 'MIDDLE' mouse button.")
    }
    return nil
}

現在執行你的遊戲,然後按按看滑鼠的左、中、右按鍵吧!

滑鼠座標偵測

有些時候你會用到滑鼠座標,這在 Ebiten 中十分地簡單,你可以使用 ebiten.CursorPosition() 函式,這個函式會回傳 x, y 座標。

注意,這個範例你需要匯入 fmt 套件,像下面這樣更改你的 import 區塊即可。

import (  
    "github.com/hajimehoshi/ebiten"
    "github.com/hajimehoshi/ebiten/ebitenutil"
    "fmt"
)

然後讓我們在 update() 實作偵測滑鼠座標的函式。

func update(screen *ebiten.Image) error {  
    // 從 CursorPosition() 取得 X, Y 座標
    x, y := ebiten.CursorPosition()

    // 顯示一段「X: xx, Y: xx」格式文字
    ebitenutil.DebugPrint(screen, fmt.Sprintf("X: %d, Y: %d", x, y))

    return nil
}

執行你的遊戲,然後移動你的游標就可以看到效果了。

請記得,如果你之後用不到 fmt 函式的時候要將 fmt 從引用套件列表中移除,否則會報錯喔。

手把輸入

如果你家裡有 PlayStation 3 的手把的話,恭喜你!你等一下可以接到你的電腦上用了。沒有的話其實網路上大約 200 元新台幣也可以買得到 USB 手把,但按鍵編號可能略有不同,這裡先統一用 PlayStation 3 的遊戲手把當作教學範例。

按鍵映射圖

這裡有個 PlayStation 3 手把的映射圖,請注意不同手把可能有不同的按鍵映射,而且在不同平台和瀏覽器的按鍵映射也不同幹!),在 FirefoxmacOS 原生則是這樣。

Chrome 中按鍵映射是這樣。

我會建議你如果之後遊戲要支援手把的話,搞個自訂設置讓玩家自訂比較快⋯⋯

手把範例

現在讓我們來偵測手把上左半邊的方向鍵是否被按下,先以上方 Chrome 的映射圖為例,我們會用到下列按鍵:

ebiten.GamepadButton12, // 上  
ebiten.GamepadButton13, // 下  
ebiten.GamepadButton14, // 左  
ebiten.GamepadButton15, // 右  

現在讓我們來修改你的 update() 函式吧。

func update(screen *ebiten.Image) error {  
    // 當遊戲手把「方向鍵上」被按下時⋯⋯
    if ebiten.IsGamepadButtonPressed(0, ebiten.GamepadButton12) {
        ebitenutil.DebugPrint(screen, "You're pressing the 'UP' gamepad button.")
    }
    // 當遊戲手把「方向鍵下」被按下時⋯⋯
    if ebiten.IsGamepadButtonPressed(0, ebiten.GamepadButton13) {
        ebitenutil.DebugPrint(screen, "\nYou're pressing the 'DOWN' gamepad button.")
    }
    // 當遊戲手把「方向鍵左」被按下時⋯⋯
    if ebiten.IsGamepadButtonPressed(0, ebiten.GamepadButton14) {
        ebitenutil.DebugPrint(screen, "\n\nYou're pressing the 'LEFT' gamepad button.")
    }
    // 當遊戲手把「方向鍵右」被按下時⋯⋯
    if ebiten.IsGamepadButtonPressed(0, ebiten.GamepadButton15) {
        ebitenutil.DebugPrint(screen, "\n\n\nYou're pressing the 'RIGHT' gamepad button.")
    }

    return nil
}

接著執行你的遊戲,試試看你手把上左半邊的方向鍵

蘑菇頭偵測

在 Ebiten 中你甚至可以偵測手把上那兩個蘑菇頭(就是可以讓你推來推去的那兩個),其映射圖如下。

我們會用 ebiten.GamepadAxis(0, axis int) 函式來取得蘑菇頭的數值,對應上述的映射圖,ebiten.GamepadAxis(0, 0) 會取得左手邊蘑菇頭的水平數值

在這裡稍微說明一下,GamepadAxis() 返回的會是浮點數,基準值是 0.0,當你向一邊推到底的時候會是 -1.0,反之,另一邊推到底會是 1.0

讓我們將 update() 函式改成下列這樣:

func update(screen *ebiten.Image) error {  
    axes := []float64{
        ebiten.GamepadAxis(0, 0), // 0:左邊的水平值
        ebiten.GamepadAxis(0, 1), // 1:左邊的垂直值
        ebiten.GamepadAxis(0, 2), // 2:右邊的水平值
        ebiten.GamepadAxis(0, 3)} // 3:右邊的垂直值

    ebitenutil.DebugPrint(screen, fmt.Sprintf("0: %0.6f, 1: %0.6f", 
         axes[0], 
         axes[1]))
    ebitenutil.DebugPrint(screen, fmt.Sprintf("\n2: %0.6f, 3: %0.6f", 
         axes[2], 
         axes[3]))

    return nil
}

接著存檔,執行你的遊戲,然後推動手把上的兩個蘑菇頭就可以看到像下面這樣的結果。

現在你已經懂的如何處理玩家的輸入了,在下個章節將會提到如何在螢幕上顯示正體中文還有多國語言文字,下次見!


封面手把圖示來源:gamepad by Hea Poh Lin from the Noun Project