通过示例学习围棋:第 5 部分 - 使用围棋创建 Game Boy Advance (GBA) 游戏
在创建了HTTP REST API 服务器、我们的第一个 Go 语言 CLI(命令行界面)应用程序和我们的Discord 机器人之后,我们现在可以做什么?
编写您自己的 Game Boy Advance 游戏怎么样?!
为什么是 Game Boy Advance 应用程序/游戏?
您可能已经注意到,在我的YouTube 视频系列“以可视化的方式理解 Kubernetes”中(在背景中),我是一名复古游戏迷。
我从事开发(和运营)工作已超过 15 年,我喜欢复古游戏,也喜欢下围棋。
但我们只能用 C 语言为 GBA 编写游戏,不是吗?嗯,这并非完全正确,有了TinyGo,我们可以用 Go 语言编写代码,并为各种微控制器、WebAssembly……以及 Game Boy Advance(GBA)游戏机构建游戏!Go 语言真的很酷,我们可以用它做很多事情!
而且,很有趣!
那么为我最喜欢的便携式游戏机之一创建游戏怎么样?
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 游戏装在游戏卡带中:
如果您对该控制台的技术规格感兴趣,您可以在本文中找到很多有用的信息。
初始化
我们在上一篇文章中创建了Git 存储库,因此现在我们只需要在本地检索它:
$ git clone https://github.com/scraly/learning-go-by-examples.git
$ cd learning-go-by-examples
我们将为go-gopher-gba
我们的 CLI 应用程序创建一个文件夹并进入该文件夹:
$ mkdir go-gopher-gba
$ cd go-gopher-gba
现在,我们必须初始化 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
这将创建一个go.mod
如下文件:
module github.com/scraly/learning-go-by-examples/go-gopher-gba
go 1.16
在启动我们的超级 Game Boy Advance (GBA) 应用程序之前,作为良好的练习,我们将创建一个简单的代码组织。
创建以下文件夹组织:
.
├── README.md
├── bin
├── go.mod
就这样?是的,我们剩下的代码组织很快就会创建完成 ;-)。
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
检查 TinyGo 是否正确安装:
$ tinygo version
tinygo version 0.19.0 darwin/amd64 (using go version go1.16.5 and LLVM version 11.0.0)
让我们创建我们的应用程序!
我们想要什么?
这个简单的 Game Boy Advance 应用/游戏应该:
- 显示带有“Gopher”文本和“按下开始按钮”的屏幕
- 显示两只地鼠
- 当你按下“开始”按钮时:你的 Gopher 播放器就会出现
- 使用多方向箭头,您可以将 Gopher 向左、向右、向上或向下移动
- 当你按下A按钮时:你的 Gopher 会跳起来 :-D
- 按下SELECT按钮时,将返回“开始”屏幕
作为第一次尝试,这似乎很酷,让我们开始吧!
好的,那我们开始吧!
正如我们在TinyGo 网站上看到的,Game Boy Advance 是一款基于ARM7TDMI微控制器的掌上电子游戏平台。这些信息很有用,我们可以在这个页面上找到 GBA 的软件包链接machine
……至于其他信息,我们稍后再讨论缺少正式文档的问题 :-)。
尽管如此,缺乏文档和最新的工作示例并不会阻止我们。因此,我将向您展示代码并逐步向您解释。
由于我们将在应用程序中显示文本消息,因此我们将创建一个fonts
文件夹并将我们想要使用的 Go 字体放入其中:
fonts
├── freesansbold24pt7b.go
└── gophers58pt.go
您可以在我的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"
)
然后,我们初始化一个变量列表:
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
)
如您所见,我们定义了几个将在代码中使用的变量,以避免代码阅读复杂,并且不会在多段代码中复制/粘贴 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 {
}
}
让我们实现我们的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)
}
因为我想显示文本信息,所以我用了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)
}
}
嗯...Aurélie,您能向我们解释一下您在这个功能中做了什么吗?
事实上,之前我们已经注册了 KeyPad,所以每次按下按钮或多向箭头时,update()
都会调用这个函数。然后我创建了一个switch case
函数,允许我根据按下的按钮执行代码。
最后,我找不到任何现有的方法可以清空屏幕,或者填充整个屏幕的颜色,GBA 机器的 TinyGo 实现也没有所需的功能。所以我用了一个小技巧:我创建了一个小函数,用黑色(因为黑色是显示屏的背景色)绘制一个与屏幕大小相同的矩形。
func clearScreen() {
tinydraw.FilledRectangle(
display,
int16(0), int16(0),
screenWidth, screenHeight,
black,
)
}
安装所需的依赖项
我们在文件中使用外部包/依赖项gopher.go
,因此我们必须安装它们:
$ go get tinygo.org/x/tinydraw
$ go get tinygo.org/x/tinyfont
TinyFont
TinyGo 不是为微控制器和轻量级硬件构建 Go 应用程序的独立方法,但主 GitHub 存储库也包含一些有用的工具,例如TinyFont。
TinyFont 是 TinyGo 显示器的字体/文本包(基于 Adafruit 的 GFX 库)。
TinyFont 存储库包含几种您可以在应用程序中使用并导入的字体,如下所示:
import (
"tinygo.org/x/tinyfont/freemono"
"tinygo.org/x/tinyfont/freesans"
"tinygo.org/x/tinyfont/freeserif"
)
如果您想使用个人字体,您可以使用tinyfontgen工具将其从 BDF 格式转换为“TinyGo”兼容格式。
使用示例:
$ tinyfontgen --package my-font --fontname MyFontRegular12pt MyFont-Regular-12pt.bdf --output MyFont-Regular-12pt.go --all
TinyDraw
TinyDraw是一个有用的工具,可让您绘制几何图形(基于 Adafruit GFX 库)。
借助 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)
Gopher 字体
由于我们暂时无法使用 TinyFont 在 GBA 硬件中显示图像和精灵,我需要找到一种方法来显示我们最喜欢的 Gophers,诀窍是使用由Jaana Dogan (@rakyll)创建的很棒的现有Gopher 字体,谢谢!!
您甚至可以在2ttf 游乐场中使用这种字体。
GBA模拟器
现在是时候测试我们的应用程序了,但如果您没有真正的 Game Boy Advance (GBA) 复古便携式视频游戏机,这不是问题,因为存在多个模拟器。
TinyGo 推荐并使用mGBA(一款 Game Boy Advance 软件模拟器),因此我们将安装它来测试我们的应用程序。
首先,下载 mGBA,解压它,然后将其复制到您的 PATH 中:
$ cp ./bin/mgba /usr/local/bin/mgba
像往常一样,检查可执行文件是否正常工作:
$ mgba --version
mgba 0.9.2 (f6d5f51d231053cc8a1778b7a139096d2bcf7324)
凉爽的!
您可以使用命令行工具mgba
(位于bin
文件夹中)运行 mGBA,但您也可以运行可视化应用程序(位于Applications
):
默认情况下,控件映射到键盘如下:
A: X
B: Z
L: A
R: S
Start: Enter
Select: Backspace
测试一下!
现在是时候测试我们的小游戏了!
$ tinygo run -target=gameboy-advance gopher.go
tinygo:ld.lld: warning: lld uses blx instruction, no object with architecture supporting feature detected
此命令将编译代码,执行 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
有了这一点,我们可以轻松构建我们的应用程序:
$ 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
技巧和窍门
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
等一下,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
当你通过 mGBA 模拟器运行它时,游戏崩溃:
$ mgba bin/gba-display.gba
The game crashed!
因此,在与 TinyGo 贡献者讨论之后,如果我构建一个.elf
文件然后将其复制到另一个.gba
文件,它应该可以工作……这是真的。
因此,如果您遇到和我一样的问题,您就知道这个窍门了 ;-)。
如果您想通过另一个 GBA 模拟器VisualBoyAdvance应用程序加载它,则不需要技巧,以下命令将运行工作格式:
$ tinygo build -size short -o bin/gopher.gba -target=gameboy-advance gopher.go
在真实手机上测试!
我不喜欢模拟器,更喜欢实体的复古游戏机,所以我想用真机测试一下。我手头有一台GBA游戏机和一台GBA SP游戏机,而且都还能用,但是我该怎么把gopher.gba
文件传输到我的游戏机上呢?
为此,您可以使用“链接器”,例如我买的“EZ Flash Omega”。它允许您将.gba
文件复制到您插入此 GBA 卡带的 microSD 卡中。
现在是测试的时间了...祝好运!
小型 Gopher 应用程序正在 Game Boy Advance SP 中运行!:-)
很简单!不是吗?
好吧...如果我告诉你创建这个游戏(这个带有可爱 Gophers 的简单应用程序)非常容易,那就是谎言^^。
TinyGo 确实是一个很好的工具,但其痛点之一是文档和具体的最新工作示例 :-(。
我会完全坦诚地告诉你。我对微控制器、电子产品和硬件一无所知,所以在没有具体示例、没有教程、没有完善的文档的情况下,创建这个应用程序并不容易。但我很高兴能够创建它,并希望这篇文章能激励你测试 TinyGo :-)。
此外,我们与TitiMoby合作,创建了一个全新的 GitLab 仓库tinygo-examples,其中包含几个 TinyGo 的可运行示例,这些示例使用了 Adafruit、PyGamer、GBA……以及即将推出的 WebAssembly。
欢迎随时添加您自己的 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