5

UEFI开发探索95 – 弹跳小游戏

 3 years ago
source link: http://yiiyee.cn/blog/2021/08/19/uefi%e5%bc%80%e5%8f%91%e6%8e%a2%e7%b4%a295-%e5%bc%b9%e8%b7%b3%e5%b0%8f%e6%b8%b8%e6%88%8f/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

请保留-> 【原文:  https://blog.csdn.net/luobing4365 和 http://yiiyee.cn/blog/author/luobing/】

昨天闲暇的时候,把Tim Lewis的迷宫游戏编译了一下,自娱自乐在UEFI下玩了一把。

奇怪的是,CSDN竟然给我发了“入选《游戏领域内容榜》第27名”,如下图:

图1 CSDN奇怪的评选

嗯,虽然从理论上来说,这篇博客也算写了个游戏,可我主要写的是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所示。

图2 弹跳小游戏

Gitee地址:https://gitee.com/luobing4365/uefi-explorer
项目代码位于:/ FF RobinPkg/RobinPkg/Applications/BounceGame下

43 total views, 2 views today


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK