通过示例学习围棋:第 5 部分 - 使用围棋创建 Game Boy Advance (GBA) 游戏

2025-06-08

通过示例学习围棋:第 5 部分 - 使用围棋创建 Game Boy Advance (GBA) 游戏

在创建了HTTP REST API 服务器、我们的第一个 Go 语言 CLI(命令行界面)应用程序和我们的Discord 机器人之后,我们现在可以做什么?

编写您自己的 Game Boy Advance 游戏怎么样?!

为什么是 Game Boy Advance 应用程序/游戏?

您可能已经注意到,在我的YouTube 视频系列“以可视化的方式理解 Kubernetes”中(在背景中),我是一名复古游戏迷。

以可视化的方式理解 Kubernetes

我从事开发(和运营)工作已超过 15 年,我喜欢复古游戏,也喜欢下围棋。

但我们只能用 C 语言为 GBA 编写游戏,不是吗?嗯,这并非完全正确,有了TinyGo,我们可以用 Go 语言编写代码,并为各种微控制器、WebAssembly……以及 Game Boy Advance(GBA)游戏机构建游戏!Go 语言真的很酷,我们可以用它做很多事情!

而且,很有趣!

那么为我最喜欢的便携式游戏机之一创建游戏怎么样?

Game Boy Advance

Game Boy Advance

Game Boy Advance (GBA) 是任天堂于 2001 年推出的掌上视频游戏机之一。在此游戏机上,您可以找到 240x160(3:2 宽高比)15 位彩色 LCD 显示屏、多方向键 ( D-pad )、“A”、“B”、“L”、“R”、“START”和“SELECT”按钮用于执行操作。

GBA CPU 基于带有嵌入式内存的 32 位 ARM7TDMI 内核。

该游戏机的优势之一是向后兼容 Game Boy 和 Game Boy Color 游戏。

GBA 游戏装在游戏卡带中:

GBA游戏

如果您对该控制台的技术规格感兴趣,您可以在本文中找到很多有用的信息

初始化

我们在上一篇文章中创建了Git 存储库,因此现在我们只需要在本地检索它:



$ git clone https://github.com/scraly/learning-go-by-examples.git
$ cd learning-go-by-examples


Enter fullscreen mode Exit fullscreen mode

我们将为go-gopher-gba我们的 CLI 应用程序创建一个文件夹并进入该文件夹:



$ mkdir go-gopher-gba
$ cd go-gopher-gba


Enter fullscreen mode Exit fullscreen mode

现在,我们必须初始化 Go 模块(依赖管理):



$ go mod init github.com/scraly/learning-go-by-examples/go-gopher-gba
go: creating new go.mod: module github.com/scraly/learning-go-by-examples/go-gopher-gba


Enter fullscreen mode Exit fullscreen mode

这将创建一个go.mod如下文件:



module github.com/scraly/learning-go-by-examples/go-gopher-gba

go 1.16


Enter fullscreen mode Exit fullscreen mode

在启动我们的超级 Game Boy Advance (GBA) 应用程序之前,作为良好的练习,我们将创建一个简单的代码组织。

创建以下文件夹组织:



.
├── README.md
├── bin
├── go.mod


Enter fullscreen mode Exit fullscreen mode

就这样?是的,我们剩下的代码组织很快就会创建完成 ;-)。

TinyGo

TinyGo

TinyGo是一款适用于嵌入式系统和现代 Web 的 Go 编译器。
您可以在许多微控制器开发板上编译和运行 TinyGo 程序,例如 BBC micro:bit、Arduino Uno、任天堂 Switch 和Game Boy Advance ;-)。

TinyGo 还可以生成非常紧凑的 WebAssembly (WASM) 代码。您可以为 Web 浏览器以及支持 WebAssembly 系统接口 (WASI) 系列接口的服务器和边缘计算环境编写程序。

当我在 2020 年初发现 TinyGo 时,我简直惊呆了:哇,太棒了!!!!

首先,我们将安装 TinyGo。如果你使用的是 MacOS:



$ brew tap tinygo-org/tools
$ brew install tinygo


Enter fullscreen mode Exit fullscreen mode

检查 TinyGo 是否正确安装:



$ tinygo version
tinygo version 0.19.0 darwin/amd64 (using go version go1.16.5 and LLVM version 11.0.0)


Enter fullscreen mode Exit fullscreen mode

让我们创建我们的应用程序!

戈弗公园

我们想要什么?

这个简单的 Game Boy Advance 应用/游戏应该:

  • 显示带有“Gopher”文本和“按下开始按钮”的屏幕
  • 显示两只地鼠
  • 当你按下“开始”按钮时:你的 Gopher 播放器就会出现
  • 使用多方向箭头,您可以将 Gopher 向左、向右、向上或向下移动
  • 当你按下A按钮时:你的 Gopher 会跳起来 :-D
  • 按下SELECT按钮时,将返回“开始”屏幕

作为第一次尝试,这似乎很酷,让我们开始吧!

好的,那我们开始吧!

正如我们在TinyGo 网站上看到的,Game Boy Advance 是一款基于ARM7TDMI微控制器的掌上电子游戏平台。这些信息很有用,我们可以在这个页面上找到 GBA 的软件包链接machine……至于其他信息,我们稍后再讨论缺少正式文档的问题 :-)。

尽管如此,缺乏文档和最新的工作示例并不会阻止我们。因此,我将向您展示代码并逐步向您解释。

由于我们将在应用程序中显示文本消息,因此我们将创建一个fonts文件夹并将我们想要使用的 Go 字体放入其中:



fonts
├── freesansbold24pt7b.go
└── gophers58pt.go


Enter fullscreen mode Exit fullscreen mode

您可以在我的GitHub 存储库中找到这些字体

满足此先决条件后,我们将创建应用程序的代码。为此,我们将创建一个gopher.go文件并将以下代码复制/粘贴到其中。

首先,Go 代码被组织成包。因此,我们初始化名为 main 的包,以及所有需要导入和使用的依赖项/库,并将其添加到主文件中:



package main

import (
    "image/color"
    "machine"
    "runtime/interrupt"
    "runtime/volatile"
    "unsafe"

    "github.com/scraly/learning-go-by-examples/go-gopher-gba/fonts"
    "tinygo.org/x/tinydraw"
    "tinygo.org/x/tinyfont"
)


Enter fullscreen mode Exit fullscreen mode

然后,我们初始化一个变量列表:



var (
    //KeyCodes / Buttons
    keyDOWN      = uint16(895)
    keyUP        = uint16(959)
    keyLEFT      = uint16(991)
    keyRIGHT     = uint16(1007)
    keyLSHOULDER = uint16(511)
    keyRSHOULDER = uint16(767)
    keyA         = uint16(1022)
    keyB         = uint16(1021)
    keySTART     = uint16(1015)
    keySELECT    = uint16(1019)

    // Register display
    regDISPSTAT = (*volatile.Register16)(unsafe.Pointer(uintptr(0x4000004)))

    // Register keypad
    regKEYPAD = (*volatile.Register16)(unsafe.Pointer(uintptr(0x04000130)))

    // Display from machine
    display = machine.Display

    // Screen resolution
    screenWidth, screenHeight = display.Size()

    // Colors
    black = color.RGBA{}
    white = color.RGBA{255, 255, 255, 255}
    green = color.RGBA{0, 255, 0, 255}
    red   = color.RGBA{255, 0, 0, 255}

    // Google colors
    gBlue   = color.RGBA{66, 163, 244, 255}
    gRed    = color.RGBA{219, 68, 55, 255}
    gYellow = color.RGBA{244, 160, 0, 255}
    gGreen  = color.RGBA{15, 157, 88, 255}

    // Coordinates
    x int16 = 100 //TODO: horizontally center
    y int16 = 100 //TODO: vertically center
)


Enter fullscreen mode Exit fullscreen mode

如您所见,我们定义了几个将在代码中使用的变量,以避免代码阅读复杂,并且不会在多段代码中复制/粘贴 RGB 颜色代码。

然后,我们定义我们的main()函数:

  • 配置并注册显示器(控制台的屏幕)
  • 调用绘制文本和 Gophers 的函数
  • update()在中断信号中插入一个函数
  • 定义一个无限循环以避免退出应用程序


func main() {
    // Set up the display
    display.Configure()

    // Register display status
    regDISPSTAT.SetBits(1<<3 | 1<<4)

    // Display Gopher text message and draw our Gophers
    drawGophers()

    // Creates an interrupt that will call the "update" fonction below, hardware way to display things on the screen
    interrupt.New(machine.IRQ_VBLANK, update).Enable()

    // Infinite loop to avoid exiting the application
    for {
    }
}


Enter fullscreen mode Exit fullscreen mode

让我们实现我们的drawGophers()功能:



func drawGophers() {

    // Display a textual message "Gopher" with Google colors
    tinyfont.DrawChar(display, &fonts.Bold24pt7b, 36, 60, 'G', gBlue)
    tinyfont.DrawChar(display, &fonts.Bold24pt7b, 71, 60, 'o', gRed)
    tinyfont.DrawChar(display, &fonts.Bold24pt7b, 98, 60, 'p', gYellow)
    tinyfont.DrawChar(display, &fonts.Bold24pt7b, 126, 60, 'h', gGreen)
    tinyfont.DrawChar(display, &fonts.Bold24pt7b, 154, 60, 'e', gBlue)
    tinyfont.DrawChar(display, &fonts.Bold24pt7b, 180, 60, 'r', gRed)

    // Display a "press START button" message - center
    tinyfont.WriteLine(display, &tinyfont.TomThumb, 85, 90, "Press START button", white)

    // Display two gophers
    tinyfont.DrawChar(display, &fonts.Regular58pt, 5, 140, 'B', green)
    tinyfont.DrawChar(display, &fonts.Regular58pt, 195, 140, 'X', red)
}


Enter fullscreen mode Exit fullscreen mode

因为我想显示文本信息,所以我用了TinyFont包,里面有我们放在fonts文件夹里的字体……我还用它来显示我们的 Gophers 了!
目前我们无法显示图片和加载精灵图,所以我得想办法显示我们可爱的 Gophers ;-)。

然后,我们需要实现我们的update()功能:



func update(interrupt.Interrupt) {

    // Read uint16 from register regKEYPAD that represents the state of current buttons pressed
    // and compares it against the defined values for each button on the Gameboy Advance
    switch keyValue := regKEYPAD.Get(); keyValue {
    // Start the "game"
    case keySTART:
        // Clear display
        clearScreen()
        // Display gopher
        tinyfont.DrawChar(display, &fonts.Regular58pt, x, y, 'B', green)
    // Go back to Menu
    case keySELECT:
        clearScreen()
        drawGophers()
    // Gopher go to the right
    case keyRIGHT:
        // Clear display
        clearScreen()
        x = x + 10
        // display gopher at right
        tinyfont.DrawChar(display, &fonts.Regular58pt, x, y, 'B', green)
    // Gopher go to the left
    case keyLEFT:
        // Clear display
        clearScreen()
        x = x - 10
        // display gopher at right
        tinyfont.DrawChar(display, &fonts.Regular58pt, x, y, 'B', green)
    // Gopher go to the down
    case keyDOWN:
        // Clear display
        clearScreen()
        y = y + 10
        tinyfont.DrawChar(display, &fonts.Regular58pt, x, y, 'B', green)
    // Gopher go to the up
    case keyUP:
        // Clear display
        clearScreen()
        y = y - 10
        tinyfont.DrawChar(display, &fonts.Regular58pt, x, y, 'B', green)
    // Gopher jump
    case keyA:
        // Clear display
        clearScreen()
        // Display the gopher up
        y = y - 20
        tinyfont.DrawChar(display, &fonts.Regular58pt, x, y, 'B', green)
        // Clear the display
        clearScreen()
        // Display the gopher down
        y = y + 20
        tinyfont.DrawChar(display, &fonts.Regular58pt, x, y, 'B', green)
    }
}


Enter fullscreen mode Exit fullscreen mode

嗯...Aurélie,您能向我们解释一下您在这个功能中做了什么吗?

事实上,之前我们已经注册了 KeyPad,所以每次按下按钮或多向箭头时,update()都会调用这个函数。然后我创建了一个switch case函数,允许我根据按下的按钮执行代码。

最后,我找不到任何现有的方法可以清空屏幕,或者填充整个屏幕的颜色,GBA 机器的 TinyGo 实现也没有所需的功能。所以我用了一个小技巧:我创建了一个小函数,用黑色(因为黑色是显示屏的背景色)绘制一个与屏幕大小相同的矩形。



func clearScreen() {
    tinydraw.FilledRectangle(
        display,
        int16(0), int16(0),
        screenWidth, screenHeight,
        black,
    )
}


Enter fullscreen mode Exit fullscreen mode

安装所需的依赖项

我们在文件中使用外部包/依赖项gopher.go,因此我们必须安装它们:



$ go get tinygo.org/x/tinydraw
$ go get tinygo.org/x/tinyfont


Enter fullscreen mode Exit fullscreen mode

TinyFont

TinyGo 不是为微控制器和轻量级硬件构建 Go 应用程序的独立方法,但主 GitHub 存储库也包含一些有用的工具,例如TinyFont

TinyFont

TinyFont 是 TinyGo 显示器的字体/文本包(基于 Adafruit 的 GFX 库)。

TinyFont 存储库包含几种您可以在应用程序中使用并导入的字体,如下所示:



import (
    "tinygo.org/x/tinyfont/freemono"
    "tinygo.org/x/tinyfont/freesans"
    "tinygo.org/x/tinyfont/freeserif"
)


Enter fullscreen mode Exit fullscreen mode

如果您想使用个人字体,您可以使用tinyfontgen工具将其从 BDF 格式转换为“TinyGo”兼容格式。

使用示例:



$ tinyfontgen --package my-font --fontname MyFontRegular12pt MyFont-Regular-12pt.bdf --output MyFont-Regular-12pt.go --all


Enter fullscreen mode Exit fullscreen mode

TinyDraw

TinyDraw是一个有用的工具,可让您绘制几何图形(基于 Adafruit GFX 库)。

TinyDraw

借助 TinyDraw,您可以轻松绘制几何形状:



    white = color.RGBA{255, 255, 255, 255}
    green = color.RGBA{0, 255, 0, 255}
    red   = color.RGBA{255, 0, 0, 255}

    // ...

    tinydraw.Line(&display, 100, 100, 40, 100, red)

    tinydraw.Rectangle(&display, 30, 106, 120, 20, white)
    tinydraw.FilledRectangle(&display, 34, 110, 112, 12, green)

    tinydraw.Circle(&display, 120, 30, 20, white)
    tinydraw.FilledCircle(&display, 120, 30, 16, red)

    tinydraw.Triangle(&display, 120, 102, 100, 80, 152, 46, white)
    tinydraw.FilledTriangle(&display, 120, 98, 104, 80, 144, 54, green)


Enter fullscreen mode Exit fullscreen mode

Gopher 字体

由于我们暂时无法使用 TinyFont 在 GBA 硬件中显示图像和精灵,我需要找到一种方法来显示我们最喜欢的 Gophers,诀窍是使用Jaana Dogan (@rakyll)创建的很棒的现有Gopher 字体,谢谢!!

Gopher 字体

您甚至可以在2ttf 游乐场中使用这种字体

2ttf游乐场

GBA模拟器

现在是时候测试我们的应用程序了,但如果您没有真正的 Game Boy Advance (GBA) 复古便携式视频游戏机,这不是问题,因为存在多个模拟器。

TinyGo 推荐并使用mGBA(一款 Game Boy Advance 软件模拟器),因此我们将安装它来测试我们的应用程序。

首先,下载 mGBA,解压它,然后将其复制到您的 PATH 中:



$ cp ./bin/mgba /usr/local/bin/mgba


Enter fullscreen mode Exit fullscreen mode

像往常一样,检查可执行文件是否正常工作:



$ mgba --version
mgba 0.9.2 (f6d5f51d231053cc8a1778b7a139096d2bcf7324)


Enter fullscreen mode Exit fullscreen mode

凉爽的!

您可以使用命令行工具mgba(位于bin文件夹中)运行 mGBA,但您也可以运行可视化应用程序(位于Applications):

mGBA应用程序

默认情况下,控件映射到键盘如下:



    A: X
    B: Z
    L: A
    R: S
    Start: Enter
    Select: Backspace


Enter fullscreen mode Exit fullscreen mode

测试一下!

现在是时候测试我们的小游戏了!



$ tinygo run -target=gameboy-advance gopher.go
tinygo:ld.lld: warning: lld uses blx instruction, no object with architecture supporting feature detected


Enter fullscreen mode Exit fullscreen mode

此命令将编译代码,执行 mGBA 模拟器并加载您的应用程序。

开始菜单

惊人的!

现在,按下“ START ”按钮(键盘上的ENTER键),您应该会看到一只绿色的 Gopher 出现在屏幕中央。

绿色地鼠

使用多方向箭头,您可以向左、向右、向下和向上移动 Gopher。

如果你按下“ A ”按钮(默认为键盘上的 X ),Gopher 就会跳跃 :-)。

您可以观看以下带有演示的视频:

建造它!

您的应用程序已准备就绪,您可以构建它了。
为此,与之前的文章一样,我们将使用Taskfile来自动执行常见任务。

因此,对于这个应用程序,我也创建了一个Taskfile.yml包含以下内容的文件:



version: "3"

tasks:

    run: 
        desc: Run the app
        cmds:
        - GOFLAGS=-mod=mod tinygo run -target=gameboy-advance gopher.go

    build:
        desc: Build the GBA app
        cmds:
        - GOFLAGS=-mod=mod tinygo build -size short -o bin/gopher.gba -target=gameboy-advance gopher.go

    build-mgba:
        desc: Build the GBA app for mGBA
        cmds:
        - GOFLAGS=-mod=mod tinygo build -size short -o bin/gopher.elf -target=gameboy-advance gopher.go
        - mv bin/gopher.elf bin/gopher.gba

    mgba:
        desc: Load the game
        cmds:
        - mgba bin/gopher.gba


Enter fullscreen mode Exit fullscreen mode

有了这一点,我们可以轻松构建我们的应用程序:



$ task build
task: [build] GOFLAGS=-mod=mod tinygo build -size short -o bin/gopher.gba -target=gameboy-advance gopher.go
tinygo:ld.lld: warning: lld uses blx instruction, no object with architecture supporting feature detected
   code    data     bss |   flash     ram
   4128   28536    3116 |   32664   31652

$ ll bin/gopher.gba
-rwxr-xr-x  1 aurelievache  staff   229K  7 aoû 18:53 bin/gopher.gba


Enter fullscreen mode Exit fullscreen mode

技巧和窍门



    build-mgba:
        desc: Build the GBA app for mGBA
        cmds:
        - GOFLAGS=-mod=mod tinygo build -size short -o bin/gopher.elf -target=gameboy-advance gopher.go
        - mv bin/gopher.elf bin/gopher.gba


Enter fullscreen mode Exit fullscreen mode

等一下,build-mgba任务里这个奇怪的招数是什么?

目前,mGBA 存在一个错误:tinygo build命令没有构建正确的 GBA ROM,mGBA 无法正确加载和运行。



$ tinygo build -target=gameboy-advance -o bin/gba-display.gba gba-display.go 
tinygo:ld.lld: warning: lld uses blx instruction, no object with architecture supporting feature detected

$ file bin/gba-display.gba
bin/gba-display.gba: data


Enter fullscreen mode Exit fullscreen mode

当你通过 mGBA 模拟器运行它时,游戏崩溃:



$ mgba bin/gba-display.gba
The game crashed!


Enter fullscreen mode Exit fullscreen mode

游戏崩溃

因此,在与 TinyGo 贡献者讨论之后,如果我构建一个.elf文件然后将其复制到另一个.gba文件,它应该可以工作……这是真的。

因此,如果您遇到和我一样的问题,您就知道这个窍门了 ;-)。

如果您想通过另一个 GBA 模拟器VisualBoyAdvance应用程序加载它,则不需要技巧,以下命令将运行工作格式:



$ tinygo build -size short -o bin/gopher.gba -target=gameboy-advance gopher.go

Enter fullscreen mode Exit fullscreen mode




在真实手机上测试!

我不喜欢模拟器,更喜欢实体的复古游戏机,所以我想用真机测试一下。我手头有一台GBA游戏机和一台GBA SP游戏机,而且都还能用,但是我该怎么把gopher.gba文件传输到我的游戏机上呢?

为此,您可以使用“链接器”,例如我买的“EZ Flash Omega”。它允许您将.gba文件复制到您插入此 GBA 卡带的 microSD 卡中。

现在是测试的时间了...祝好运!

GBA SP 上的 Gopher

小型 Gopher 应用程序正在 Game Boy Advance SP 中运行!:-)

很简单!不是吗?

好吧...如果我告诉你创建这个游戏(这个带有可爱 Gophers 的简单应用程序)非常容易,那就是谎言^^。

TinyGo 确实是一个很好的工具,但其痛点之一是文档和具体的最新工作示例 :-(。

我会完全坦诚地告诉你。我对微控制器、电子产品和硬件一无所知,所以在没有具体示例、没有教程、没有完善的文档的情况下,创建这个应用程序并不容易。但我很高兴能够创建它,并希望这篇文章能激励你测试 TinyGo :-)。

如果您想测试 TinyGo,您可以通过现有的游乐场来玩

此外,我们与TitiMoby合作,创建了一个全新的 GitLab 仓库tinygo-examples,其中包含几个 TinyGo 的可运行示例,这些示例使用了 Adafruit、PyGamer、GBA……以及即将推出的 WebAssembly。
欢迎随时添加您自己的 TinyGo 示例!:-)

TinyGo 示例

结论

正如您在本文和之前的文章中所看到的,可以使用 Go 创建应用程序:CLI、REST API、Discord 机器人……以及微控制器和复古控制台的应用程序!:-)

我们的 Go 小型 Gopher GBA 应用程序的所有代码均可在以下位置找到:https://github.com/scraly/learning-go-by-examples/tree/main/go-gopher-gba

在接下来的文章中,我们将使用 Go 创建其他类型的应用程序。

希望你会喜欢它。

鏂囩珷鏉ユ簮锛�https://dev.to/aurelievache/learning-go-by-examples-part-5-create-a-game-boy-advance-gba-game-in-go-5944
PREV
以可视化的方式理解 Docker(🎥 视频):第一部分 – 图像
NEXT
让我们构建最小的区块链