返回

文章详情

Game Boy Advance开发:日志记录到控制台

Hacker News2026年7月4日 17:12

使用printf()(在C语言中)或console.log()(在JavaScript中)向控制台打印内容是开发过程中一个有用的工具。在制作GBA游戏时,你可能会认为必须放弃这一点,或者只能记录到GBA那小小的屏幕上。如果你使用mGBA,你可以在游戏中获得printf()的功能。这很简单。工作原理在进行GBA开发时,你通常会写入像REG_DISPCNT这样的寄存器。这是一个内存映射寄存器,是你的游戏告诉GBA硬件你希望它执行的简单方式。这只是GBA内存映射中的一个地址,当写入时,GBA硬件会接收你发送的数据并相应地行动。mGBA添加了一些额外的内存映射寄存器以供日志记录。这些寄存器的工作方式与REG_DISPCNT相似,不过它们只在你的游戏在mGBA中运行时有效。以下是这些mGBA特定寄存器的定义。# define REG_LOG_ENABLE ( vu32 * ) ( 0x4FFF780 ) # define REG_LOG_BUFFER ( vu32 * ) ( 0x4FFF600 ) # define REG_LOG_SEND ( vu32 * ) ( 0x4FFF700 )所以,要记录一些数据,你首先需要向REG_LOG_ENABLE发送一个特殊值以开启日志记录。然后你将数据写入REG_LOG_BUFFER。当你完成后,向REG_LOG_SEND写入日志级别,此时mGBA将接收你发送的数据并将其输出到日志。这听起来有点复杂,实际上如此。但你只需将所有这些细节隐藏在一个函数里,再也不用考虑它们。这个函数如下。# define REG_LOG_ENABLE ( vu32 * ) ( 0x4FFF780 ) # define REG_LOG_BUFFER ( vu32 * ) ( 0x4FFF600 ) # define REG_LOG_SEND ( vu32 * ) ( 0x4FFF700 ) # define MGBA_LOG_MAX_LINE 256 # define ERROR 0x101 # define WARNING 0x102 # define INFO 0x103 # define DEBUG 0x104 void mgbalog ( u32 level , const char * msg ) { * REG_LOG_ENABLE = 0xC0DE ; tonccpy ( ( void * ) REG_LOG_BUFFER , msg , MGBA_LOG_MAX_LINE ) ; * REG_LOG_SEND = level ; }然后你可以在游戏中需要记录日志的地方使用它。# include "mgbalog.h" void myFunc ( ) { . . . mgbalog ( DEBUG , "hello mGBA log" ) ; }运行游戏时,前往Tools > View logs...打开日志窗口。mGBA日志窗口显示我们的日志消息。而且这就是你的消息。在日志记录时,你可以选择在不同的级别记录:错误、警告、信息或调试。这就是mgbalog()中的日志级别用途。你将所需的日志级别写入REG_LOG_SEND,这样就可以告诉mGBA在何种级别存储该消息,以及你已经完成了写入,以便mGBA可以处理它。记录到终端每次打开日志窗口查看你的日志可能会很麻烦。如果你从命令行启动mGBA,你还可以告诉它将日志写入终端。为此,再次进入日志窗口,单击高级设置,然后在底部检查"Log to console"。在日志高级设置窗口中勾选“记录到控制台”。mGBA本身会记录很多内容。你也可以在此对话框中全部开启/关闭。现在,如果你通过命令行启动游戏,日志输出也会出现在那里。你可以使用--log-level控制想要的日志级别。例如,下面是如何只将DEBUG记录到控制台。mgba --log-level 16 yourRom.gba以下是日志级别:1 - 致命错误 2 - 错误 4 - 警告 8 - 信息 16 - 调试 32 - 存根 64 - 游戏内错误你可以组合它们,因此如果你想要调试和错误,可以使用--log-level 18添加格式化记录一个静态字符串通常没有用。幸运的是,添加printf风格的格式化很简单。我的博客不允许使用<和>的C包含,所以我添加了空格来绕过这个问题。我真的需要修复这个...# include < stdarg.h > # include < stdio.h > static char logBuffer [ MGBA_LOG_MAX_LINE ] ; void mgbalog ( u32 level , const char * format , . . . ) { * REG_LOG_ENABLE = 0xC0DE ; va_list formatArgs ; va_start ( formatArgs , format ) ; vsnprintf ( logBuffer , MGBA_LOG_MAX_LINE , format , formatArgs ) ; va_end ( formatArgs ) ; tonccpy ( ( void * ) REG_LOG_BUFFER , logBuffer , MGBA_LOG_MAX_LINE ) ; * REG_LOG_SEND = level ; }现在你可以做类似mgbalog(DEBUG, "pos.x=%i", pos.x);的事情。开发过程中仅记录日志日志会增加你的二进制文件,用消耗内存和CPU,通常在游戏最终版本中是不可取的。这一点在你添加格式化时尤为明显,类似于标准库中的vsnprintf对这台可怜的小GBA来说是相当昂贵的。我将我的日志记录实现隐藏在MGBALOG定义后面。所以当我想要日志记录时,我定义MGBALOG。如果没有定义,则与日志相关的内容将不会添加到二进制文件中。# ifdef MGBALOG void mgbalog ( u32 level , const char * format , . . . ) { . . . } # endif这很好,但这意味着如果你不定义MGBALOG,必须在整个代码库中删除所有日志记录调用,否则会出现编译错误。这可以通过更多的定义技巧轻松解决。这是我的mgbalog.h # pragma on

赞助内容

NordVPN Next-gen Antivirus

本站免费、广告极少。如果觉得有帮助,可以请我们喝杯咖啡 —— 任何金额都对持续运营有实际帮助。

请我喝杯咖啡