请保留-> 【原文: https://blog.csdn.net/luobing4365 和 http://yiiyee.cn/blog/author/luobing/】
最近一直在写YIE002开发探索的博客,偶尔看看其他人写的和BIOS开发相关的博客。
经常看的博客有Tim Lewis、Vincent Zimmer等,总是能学到一些东西。
今天下班后,可能是因为最近公司的事情比较繁杂,精神有点不振。虽然计划了近期要写的嵌入式代码,可是怎么也提不起劲,开发工具都不想打开。
我无聊地翻阅着常看的几位作者的博客,Tim Lewis有个关于UEFI和C++的议题,稍微有点兴趣,就点进去看了。
他的代码放在了sourceforge上,地址为:https://sourceforge.net/projects/syslibforuefi/。下载了之后,我浏览了目录结构,发现几个有意思的程序。其中有些是可以在UEFI下玩的小游戏,看来程序员的想法很类似啊,我之前也开发了UEFI下的贪吃蛇游戏。
今天休息一下,拜读下顶级BIOS程序员写的UEFI程序。
1 Maze程序结构分析
源程序在文末给出了,可以试玩一下。
整个迷宫程序的主要实现目标包括:
1) 自动生成迷宫;
2) 通过方向键,控制角色通过迷宫。
实现的目标比较简单,从代码结构来看,主要包含如下函数,如图1所示。
整体的程序结构还是非常容易理解的,而且作者写了非常详细的注释,很容易看明白。基本步骤如下:
1)定义全局变量
// Global Bitmaps
UINTN mBgWidth; // width of background image.
UINTN mBgHeight; // height of background image.
EFI_GRAPHICS_OUTPUT_BLT_PIXEL *mBgBlt; // background image.
EFI_GRAPHICS_OUTPUT_BLT_PIXEL *mRockBlt;// rock image.
EFI_GRAPHICS_OUTPUT_BLT_PIXEL *mCharBlt;// character image.
EFI_GRAPHICS_OUTPUT_BLT_PIXEL *mDisplay;// copy of grid display.
UINT32 mDisplayWidth; // width (in pixels) of grid display.
UINT32 mDisplayHeight; // height (in pixels) of grid display.
EFI_GRAPHICS_OUTPUT_BLT_PIXEL **mGrid = NULL; // grid of boxes for the game
INT32 mGridHeight; // number of columns in the grid.
INT32 mGridWidth; // number of rows in the grid.
UINTN mGenImageWidth; // width of images that take up one box
UINTN mGenImageHeight; // height of images that take up one box
INT32 mCharacterX; // character's X cell location.
INT32 mCharacterY; // character's Y cell location.
INT32 mEntranceX;
INT32 mEntranceY;
INT32 mExitX;
INT32 mExitY;
其中,与迷宫背景、障碍物(也即石头)、角色相关的图像,都使用EFI_GRAPHICS_OUTPUT_BLT_PIXEL型指针变量mBgBlt、mRockBlt和mCharBlt保存了。
2)设置迷宫
设置迷宫是本游戏中最重要的部分,为保证可以随机生成迷宫,在main()函数开始,使用用当前时间生成了新的seed:
srand((unsigned)time(NULL));
而生成迷宫的核心函数为MazeCreate(),它调用了MazeGenerate()来随机产生迷宫。代码内容如下:
VOID MazeCreate(VOID)
{
assert(mGridWidth % 2 == 1);
assert(mGridHeight % 2 == 1);
MazeGenerate(0, 1, 1, (mGridWidth - 1)/2, (mGridHeight - 1)/2, 1);
}
VOID
MazeGenerate(
int Index, // backtrack index.
int X, // grid horizontal position to investigate.
int Y, // grid vertical position to investigate.
int W, // number of unique horizontal maze positions.
int H, // number of unique vertical maze positions.
int Visited // number of maze positions visited.
)
{
int neighbor_valid; // valid neighbors: 0 = none.
int neighbor_x[4]; // array of grid positions of possible neighbor (x).
int neighbor_y[4]; // array of grid positions of possible neighbor (y).
int step[4]; // array of valid possible neighbors.
int x_next; // next selected grid position (x).
int y_next; // next selected grid position (y).
int random; // randomly selected neighbor to try.
if (Visited < H * W) {
neighbor_valid = 0;
// Add the left neighbor if it is completely blocked off.
if (X - 2 > 0 && MazeIsClosed(X - 2, Y)) {
neighbor_x[neighbor_valid] = X - 2;;
neighbor_y[neighbor_valid] = Y;
step[neighbor_valid] = 1;
neighbor_valid++;
}
// Add the up neighbor if it is completely blocked off.
if (Y - 2 > 0 && MazeIsClosed(X, Y - 2)) {
neighbor_x[neighbor_valid] = X;
neighbor_y[neighbor_valid] = Y - 2;
step[neighbor_valid] = 2;
neighbor_valid++;
}
// Add the down neighbor if it is completely blocked off.
if (Y + 2 < H * 2 + 1 && MazeIsClosed(X, Y + 2)) {
neighbor_x[neighbor_valid] = X;
neighbor_y[neighbor_valid] = Y + 2;
step[neighbor_valid] = 3;
neighbor_valid++;
}
// Add the right neighbor if it is completely blocked off.
if (X + 2 < W * 2 + 1 && MazeIsClosed(X + 2, Y)) {
neighbor_x[neighbor_valid] = X + 2;
neighbor_y[neighbor_valid] = Y;
step[neighbor_valid] = 4;
neighbor_valid++;
}
// Count the number of neighbors that are completely surrounded by walls.
// If there are none, then backtrack to the previous location and try
// again.
if (neighbor_valid == 0) { // backtrack
x_next = mBacktrackX[Index];
y_next = mBacktrackY[Index];
Index--;
// There is at least one neighbor that was completely blocked off, so
// randomly select among them.
} else {
random = rand() % neighbor_valid;
// Find the next grid location to try from among the valid neighbors.
x_next = neighbor_x[random];
y_next = neighbor_y[random];
// Record the next location in the backtrack location list.
Index++;
mBacktrackX[Index] = (char)x_next;
mBacktrackY[Index] = (char)y_next;
// Make sure there is an opening between the next location and the
// location by putting a path there.
switch (step[random]) {
case 1: MazeSet(x_next + 1, y_next, PATH); break;
case 2: MazeSet(x_next, y_next + 1, PATH); break;
case 3: MazeSet(x_next, y_next - 1, PATH); break;
case 4: MazeSet(x_next - 1, y_next, PATH); break;
default: assert(FALSE); break;
}
Visited++;
}
// Recursively generate the maze from the next location selected.
MazeGenerate(Index, x_next, y_next, W, H, Visited);
}
}
MazeGenerate()函数非常有意思,它使用递归的方式,保证生成的迷宫能有出口。而出口的位置,是由随机函数rand()来决定的。
3) 游戏控制
游戏控制通过函数RunGame()实现,它接收用户对方向键的输入,控制角色在迷宫中行走。RunGame()对觉得的操作,则由函数PlayerMove()来实现,具体内容如下:
EFI_STATUS
PlayerMove(IN DIRECTION dir)
{
int xd;
int yd;
int newx;
int newy;
switch (dir) {
case NORTH: xd = 0; yd = -1; break;
case SOUTH: xd = 0; yd = 1; break;
case EAST: xd = 1; yd = 0; break;
case WEST: xd = -1; yd = 0; break;
}
newx = mCharacterX + xd;
newy = mCharacterY + yd;
if (newx < 0 || newx >= mGridWidth || newy < 0 || newy >= mGridHeight || !GridIsBackground(newx, newy)) {
return EFI_UNSUPPORTED;
}
GridSet(newx, newy, mCharBlt);
GridSetBackground(mCharacterX, mCharacterY);
mCharacterX = newx;
mCharacterY = newy;
return EFI_SUCCESS;
}
此函数根据用户的输入,在当前位置显示角色,并消去上一位置的角色图形。而当角色的位置与迷宫障碍物相同时(也即石头组成的墙壁),则不进行任何操作。
2 编译运行
使用如下命令编译:
C:\vUDK2018\edk2>build -p RobinPkg\RobinPkg.dsc -m RobinPkg\Applications\Maze\MazeGame.inf -a IA32
将编译好的MazeGame.efi,以及项目工程中的bg.bmp、char.bmp和object.bmp拷贝到Tiancore模拟器所在的目录,启动UEFI Shell,运行MazeGame.efi,效果如图2所示。
Gitee地址:https://gitee.com/luobing4365/uefi-explorer
项目代码位于:/ FF RobinPkg/RobinPkg/Applications/Maze下
1,622 total views, 4 views today