请保留-> 【原文: https://blog.csdn.net/luobing4365 和 http://yiiyee.cn/blog/author/luobing/】
昨天闲暇的时候,把Tim Lewis的迷宫游戏编译了一下,自娱自乐在UEFI下玩了一把。
奇怪的是,CSDN竟然给我发了“入选《游戏领域内容榜》第27名”,如下图:
嗯,虽然从理论上来说,这篇博客也算写了个游戏,可我主要写的是UEFI程序啊。除了这篇博客的名字外,在关键字里我也没有设置“游戏”字样。
实在搞不懂CSDN的评判算法。
不管了,昨天看Tim Lewis的代码中,还有一些游戏,今天想再编译试试。
1 Bounce游戏
在早期的Windows XP中,有一个让人印象深刻的屏保。桌面上一个小球,在屏幕上来回撞,运行的过程中画出各种轨迹。
Bounce就是用来实现类似功能的UEFI程序。
不过,直接编译后,发现无法运行。看了下代码,图像是通过类似HII的方式来组织的。
不大习惯这种方式,我还是决定自己把程序修改一下。
1.1 游戏架构
从功能上来说,要实现的比较简单:
1) 图像显示(包括指定位置的图像显示);
2) 遇到障碍物的动作处理(主要是屏幕四周)。
这是个粗糙的架构,在此基础上,可以扩展多个图像碰撞后的处理,实现类似以前的屏保的效果。
实际上,UEFI开发探索系列博客中所开发的图像处理函数,完全可以直接使用。
为了方便,我也是在之前的代码基础上进行移植的。基础工程是以前的MyGuiFrame,修改了其中与图像处理相关Picture.c,以及添加了一个文件处理函数,位于FileRW.c中。
1.2 移植和编写代码
主要的编写步骤如下。
1)编写文件读取到内存的函数
修改原来通过GUID获取文件的方法,直接读取bmp图像到内存。文件处理的函数,放在了FileRW.c中,如下所示:
//robin 增加一个处理图像文件的加载函数
EFI_STATUS
LoadFile(
IN CONST CHAR8 *FilePath,
OUT VOID **File,
OUT UINT32 *FileSize
)
{
FILE *f = fopen(FilePath, "rb");
if (f == NULL) {
return EFI_NOT_FOUND;
}
fseek(f, 0, SEEK_END);
*FileSize = ftell(f);
fseek(f, 0, SEEK_SET); //same as rewind(f);
*File = malloc(*FileSize);
if (*File == NULL) {
return EFI_OUT_OF_RESOURCES;
}
if (fread(*File, *FileSize, 1, f) < 1) {
return EFI_DEVICE_ERROR;
}
fclose(f);
return EFI_SUCCESS;
}
2)编写图像显示函数
图像显示的函数包括DisplayImage()和DisplayImageAt(),内容如下:
EFI_GRAPHICS_OUTPUT_BLT_PIXEL *mBlt;
UINTN mHeight;
UINTN mWidth;
int DisplayImageAt (UINTN X, UINTN Y)
{
gGraphicsOutput->Blt (
gGraphicsOutput,
mBlt,
EfiBltBufferToVideo,
0,
0,
X,
Y,
mWidth,
mHeight,
mWidth * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)
);
return 0;
}
EFI_STATUS
DisplayImage (
IN CONST CHAR8 *BmpFilePath
)
{
EFI_STATUS Status;
UINT8 *ImageData;
UINTN ImageSize;
UINTN BltSize;
UINTN CoordinateX;
UINTN CoordinateY;
//robin 修改原有代码,将图像文件加载
Status = LoadFile(BmpFilePath, (VOID **)&ImageData, &ImageSize);
if (EFI_ERROR(Status)) { //print error messages
return FALSE;
}
BltSize = 0;
Status = ConvertBmpToGopBlt (
ImageData,
ImageSize,
(VOID **) &mBlt,
&BltSize,
&mHeight,
&mWidth
);
if (EFI_ERROR (Status)) {
free (ImageData);
}
CoordinateX = (gGraphicsOutput->Mode->Info->HorizontalResolution / 2) - (mWidth / 2);
CoordinateY = (gGraphicsOutput->Mode->Info->VerticalResolution / 2) - (mHeight / 2);
DisplayImageAt ((UINTN) CoordinateX, (UINTN) CoordinateY);
return EFI_SUCCESS;
}
DisplayImageAt()在指定位置处,将内存mBlt中存储的图像,直接显示到屏幕上;而DisplayImage()则完成了将BMP图像加载到内存mBlt,以及调用DisplayImageAt()显示图像的功能。
3)实现弹跳函数Bounce
弹跳函数通过UEFI定时器,按一定的频率移动图像,如果图像撞到屏幕四周,则反弹回来继续移动。
逻辑结构还是比较简单的,内容如下:
EFI_GRAPHICS_OUTPUT_BLT_PIXEL mBoundBg = {201, 174, 255, 0};
EFI_EVENT mEvent;
EFI_STATUS bounce (VOID)
{
EFI_EVENT events[2];
UINTN index;
EFI_INPUT_KEY Key;
INTN DeltaX;
INTN DeltaY;
INTN CurrentX;
INTN CurrentY;
INTN NewX;
INTN NewY;
gBS->CreateEvent (
EVT_TIMER,
0,
NULL,
NULL,
&mEvent
);
gBS->SetTimer (
mEvent,
TimerPeriodic,
// 10*1000*1000 //fix time? 1s
10*1000*80 //80ms
);
Key.ScanCode = SCAN_NULL;
DeltaX = 3;
DeltaY = 3;
events[0] = gST->ConIn->WaitForKey;
events[1] = mEvent;
CurrentX = (gGraphicsOutput->Mode->Info->HorizontalResolution / 2) - (mWidth / 2);
CurrentY = (gGraphicsOutput->Mode->Info->VerticalResolution / 2) - (mHeight / 2);
do {
gBS->WaitForEvent (2, events, &index);
if (index == 0) {
gST->ConIn->ReadKeyStroke(gST->ConIn, &Key);
continue;
} else {
NewX = CurrentX + DeltaX;
NewY = CurrentY + DeltaY;
if (NewX + mWidth > gGraphicsOutput->Mode->Info->HorizontalResolution) {
//bounce X
NewX = gGraphicsOutput->Mode->Info->HorizontalResolution - mWidth;
DeltaX = -DeltaX;
} else if (NewX < 0) {
//bounce X
NewX = 0;
DeltaX = -DeltaX;
}
if (NewY + mHeight > gGraphicsOutput->Mode->Info->VerticalResolution) {
//bounce Y
NewY = gGraphicsOutput->Mode->Info->VerticalResolution - mHeight;
DeltaY = -DeltaY;
} else if (NewY < 0) {
//bounce Y
NewY = 0;
DeltaY = -DeltaY;
}
gGraphicsOutput->Blt(
gGraphicsOutput,
&mBoundBg,
EfiBltVideoFill,
0,
0,
CurrentX,
CurrentY,
mWidth,
mHeight,
0
);
DisplayImageAt (NewX, NewY);
CurrentX = NewX;
CurrentY = NewY;
}
} while (Key.ScanCode != SCAN_END);
gST->ConOut->Reset (gST->ConOut, FALSE);
return EFI_SUCCESS;
}
4)实现主功能
在主程序中,添加对bmp图像的加载显示,并实现弹跳效果:
if (EFI_ERROR (DisplayImage("bounce.bmp"))) {
return 1;
}
bounce();
为了方便演示,我把屏幕分辨率改为了800×600。
经过以上步骤,就完成了弹跳小游戏的编程了。
2 测试弹跳小游戏
使用如下命令编译:
C:\vUDK2018\edk2>build -p RobinPkg\RobinPkg.dsc -m RobinPkg\Applications\BounceGame\BounceGame.inf -a IA32
把编译好的程序BounceGame.efi以及项目文件夹下的bounce.bmp拷贝到模拟器所在文件夹,运行BounceGame.efi即可看到效果。
在Tianocore模拟器中,运行效果如图2所示。
Gitee地址:https://gitee.com/luobing4365/uefi-explorer
项目代码位于:/ FF RobinPkg/RobinPkg/Applications/BounceGame下
1,999 total views, 4 views today