接上文 游戏编程入门(3):绘制基本 GDI 图形

除了经典的矢量图形游戏之外,游戏开发人员都使用图形图像来在视觉上展示游戏的图形部分。本文将介绍如何加载和显示图形图像。

本章内容包括:

  • 位图图像的基础知识以及为什么它们在游戏编程中如此重要
  • 位图图像的内部工作方式
  • 如何开发在游戏中使用的通用位图类
  • 如何在一个幻灯片放映程序中使用位图类来表示幻灯片图像

位图图像的基础知识

Windows游戏中的图像是用位图(bitmap)表示的,位图是一些矩形图形对象,包含一些小方块(称为像素)构成的行和列。

位图的得名是因为行和列决定了怎样将像素映射(map)到屏幕上,而像素本身是由位(bit)构成的。

位图中的每一个像素都是一种纯色,因此可以将位图看成是小彩色方块的一种矩形排列形式。

在Windows中支持两种位图,设备相关位图和设备无关位图。设备相关位图是以某种特定设备决定的方式存储的,而设备无关位图的存储方式使得能够在任何设备上显示它。本文及后使用的位图都是设备无关位图(DIB:device-independent bitmap)。

在Windows程序中使用位图

要想在Windows程序中使用位图,有两种选择:

  • 直接从文件中读取位图
  • 将位图存储为一个应用程序资源并从内存中读取它

直接从文件中读取位图

Windows程序打开磁盘中的位图文件并读入图像数据,然后使用位图图像数据来创建一个GDI 位图图形对象,可以在设备环境上绘制这个图形对象。

将位图存储为一个应用程序资源并从内存中读取它

将位图存储为可执行程序内部的一个资源,意味着一旦编译了程序,就不需要在程序中包括位图文件了。这种做法的优点是能够将游戏发布为一个单独的程序文件。

深入学习位图

要想在游戏中使用位图,必须对它们的结构有一个基本的了解。

位图的结构

位图的结构如图所示:

这幅图解释了每一个位图都包含3个基本部分:

  • 头部
  • 颜色表
  • 图像数据

头部

头部包含属于位图的整体结构的信息,例如其宽度、高度、每像素的位数等。

颜色表

颜色表包含位图的调色板,它是整个图像中使用的颜色列表。

颜色表对于8位位图极其重要,因为它最多描述了图像中的像素使用的256种颜色。相反,24图像的颜色完全可以由像素本身进行描述,所以它们不需要颜色表。更具体的来说,24位图像中的24位被分为3个8位值,分别对应每一个颜色部分—-红色、绿色和蓝色。

图像数据

图像数据是存储位图的实际像素的地方。

例如,如果一个位图是10*12,那么横向是10个像素,纵向是12个像素,一共是120个像素。如果它是一个8位图像,那么每个像素都需要8位(1个字节)来描述其颜色。而对于24位图像,每个像素需要24位。

因此,对于10*12位图的8位版本,图像数据包含120个字节,而24位版本占用360个字节。

当然,所有这些图像数据都是在GDI 函数加载图像并开始使用它之后自动处理的。换句话说,只有第一次从文件或资源中加载位图时才需要关注位图的内部工作方式。

一旦完成了加载,就可以使用一个位图句柄来将其绘制到设备环境中。

开发位图类 Bitmap

创建一个类,包含加载和绘制位图所需的全部代码,然后使用这个类来创建位图对象。

位图类 Bitmap 的工作原理

Bitmap 类的思路是:提供一种从文件或者资源加载位图并将位图绘制到一个设备环境的方法。通过将这些功能结合到一个类中,我们就能在游戏中创建极易使用的Bitmap 对象,并且可以隐藏处理位图的各种烦杂工作。

Bitmap 类有以下需求

  • 从文件中加载位图
  • 从资源中加载位图
  • 创建纯色的空白位图
  • 将位图绘制到设备环境中
  • 获得位图的宽度和高度

Bitmap 类 源代码

Bitmap类的设计和之前的游戏引擎GameEngine设计类似,分为Bitmap.h 和 Bitmap.cpp。

Bitmap.h

#pragma once//-----------------------------------------------------------------
// 包含的文件
//-----------------------------------------------------------------
#include <windows.h>//-----------------------------------------------------------------
// Bitmap 类
//-----------------------------------------------------------------
class Bitmap
{protected:// 成员变量 HBITMAP m_hBitmap;           //位图句柄int     m_iWidth, m_iHeight; //位图的宽和高// 帮助器方法,用来释放与位图有关的内存并清除位图句柄void Free();public:// 构造函数和析构函数  3个构造函数分别对应一种创建位图的不同方法Bitmap();//从一个文件中创建位图Bitmap(HDC hDC, LPTSTR szFileName);//从一个资源中创建位图Bitmap(HDC hDC, UINT uiResID, HINSTANCE hInstance);//创建纯色的空白位图Bitmap(HDC hDC, int iWidth, int iHeight, COLORREF crColor = RGB(0, 0, 0));virtual ~Bitmap();// 常规方法 create()用来处理加载位图数据并将其创建为一个GDI 对象,3个Create分别对应3个构造函数 BOOL Create(HDC hDC, LPTSTR szFileName); BOOL Create(HDC hDC, UINT uiResID, HINSTANCE hInstance); BOOL Create(HDC hDC, int iWidth, int iHeight, COLORREF crColor);//提供将位图绘制到设备环境上的方法void Draw(HDC hDC, int x, int y);//Getter方法 获得位图的宽和高int  GetWidth() {return m_iWidth; };int  GetHeight() {return m_iHeight; };
};

Bitmap.cpp

//-----------------------------------------------------------------
// 包含的文件
//-----------------------------------------------------------------
#include "Bitmap.h"//-----------------------------------------------------------------
// Bitmap 的构造函数和析构函数
//-----------------------------------------------------------------// 默认的构造函数初始化成员变量
Bitmap::Bitmap(): m_hBitmap(NULL), m_iWidth(0), m_iHeight(0)
{
}// 从一个文件中创建位图
Bitmap::Bitmap(HDC hDC, LPTSTR szFileName): m_hBitmap(NULL), m_iWidth(0), m_iHeight(0)
{Create(hDC, szFileName);
}// 从一个资源中创建位图
Bitmap::Bitmap(HDC hDC, UINT uiResID, HINSTANCE hInstance): m_hBitmap(NULL), m_iWidth(0), m_iHeight(0)
{Create(hDC, uiResID, hInstance);
}// 创建纯色的空白位图
Bitmap::Bitmap(HDC hDC, int iWidth, int iHeight, COLORREF crColor): m_hBitmap(NULL), m_iWidth(0), m_iHeight(0)
{Create(hDC, iWidth, iHeight, crColor);
}Bitmap::~Bitmap()
{Free();
}//-----------------------------------------------------------------
// Bitmap 帮助器方法,用来释放与位图有关的内存并清除位图句柄
//-----------------------------------------------------------------
void Bitmap::Free()
{// 若位图句柄有效(即存在)if (m_hBitmap != NULL){//删除GDI 位图图像并清除句柄DeleteObject(m_hBitmap);m_hBitmap = NULL;}
}//-----------------------------------------------------------------
// Bitmap 常规方法,3个Create()和Draw()
//-----------------------------------------------------------------//从一个文件中加载位图并将其创建为一个GDI 对象
BOOL Bitmap::Create(HDC hDC, LPTSTR szFileName)
{// 清空以前的任何位图信息(使用于对不同的位图重复使用同一个Bitmap对象的情况)Free();// 打开文件HANDLE hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ, NULL,OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);//检查得到的文件句柄以确保顺利打开文件if (hFile == INVALID_HANDLE_VALUE)return FALSE;// 从文件中读 位图的 文件头(文件头包含位图文件本身的信息)BITMAPFILEHEADER  bmfHeader;DWORD             dwBytesRead;BOOL bOK = ReadFile(hFile, &bmfHeader, sizeof(BITMAPFILEHEADER),&dwBytesRead, NULL);//进行检查,确保正确读取if ((!bOK) || (dwBytesRead != sizeof(BITMAPFILEHEADER)) ||(bmfHeader.bfType != 0x4D42)){CloseHandle(hFile);return FALSE;}BITMAPINFO* pBitmapInfo = (new BITMAPINFO);if (pBitmapInfo != NULL){// 从文件中读 位图的 信息头部bOK = ReadFile(hFile, pBitmapInfo, sizeof(BITMAPINFOHEADER),&dwBytesRead, NULL);if ((!bOK) || (dwBytesRead != sizeof(BITMAPINFOHEADER))){CloseHandle(hFile);Free();return FALSE;}// 存储位图的宽度和高度m_iWidth = (int)pBitmapInfo->bmiHeader.biWidth;m_iHeight = (int)pBitmapInfo->bmiHeader.biHeight;/* 计算biSizeImage填充回去,是增加的代码(因为无压缩BMP文件的pBitmapInfo->bmiHeader.biSizeImage 里面的值不一定是图像的真实大小,可能是0或者随意的值。所以需要重新计算*/pBitmapInfo->bmiHeader.biSizeImage =m_iHeight*m_iWidth*((int)pBitmapInfo->bmiHeader.biBitCount)/8;// 复制图像数据,调用CreateDIBSection()以从原始位图数据中获得一个GDI 位图对象的句柄PBYTE pBitmapBits;m_hBitmap = CreateDIBSection(hDC, pBitmapInfo, DIB_RGB_COLORS,(PVOID*)&pBitmapBits, NULL, 0);if ((m_hBitmap != NULL) && (pBitmapBits != NULL)){SetFilePointer(hFile, bmfHeader.bfOffBits, NULL, FILE_BEGIN);bOK = ReadFile(hFile, pBitmapBits, pBitmapInfo->bmiHeader.biSizeImage,&dwBytesRead, NULL);if (bOK)return TRUE;}}// 读取数据的过程中发生错误时释放位图内存Free();return FALSE;
}//从一个资源中加载位图并将其创建为一个GDI 对象
BOOL Bitmap::Create(HDC hDC, UINT uiResID, HINSTANCE hInstance)
{// Free any previous DIB infoFree();// 找到位图资源HRSRC hResInfo = FindResource(hInstance, MAKEINTRESOURCE(uiResID), RT_BITMAP);if (hResInfo == NULL)return FALSE;// 将位图资源加载到内存中HGLOBAL hMemBitmap = LoadResource(hInstance, hResInfo);if (hMemBitmap == NULL)return FALSE;// 锁定资源,以便访问其原始数据PBYTE pBitmapImage = (BYTE*)LockResource(hMemBitmap);if (pBitmapImage == NULL){FreeResource(hMemBitmap);return FALSE;}// 存储位图的宽度和高度BITMAPINFO* pBitmapInfo = (BITMAPINFO*)pBitmapImage;m_iWidth = (int)pBitmapInfo->bmiHeader.biWidth;m_iHeight = (int)pBitmapInfo->bmiHeader.biHeight;/* 计算biSizeImage填充回去,是增加的代码(因为无压缩BMP文件的pBitmapInfo->bmiHeader.biSizeImage 里面的值不一定是图像的真实大小,可能是0或者随意的值。所以需要重新计算*/pBitmapInfo->bmiHeader.biSizeImage =m_iHeight*m_iWidth*((int)pBitmapInfo->bmiHeader.biBitCount)/8;// 复制图像数据,并以此为基础使用CreateDIBSection获得一个位图句柄PBYTE pBitmapBits;m_hBitmap = CreateDIBSection(hDC, pBitmapInfo, DIB_RGB_COLORS,(PVOID*)&pBitmapBits, NULL, 0);if ((m_hBitmap != NULL) && (pBitmapBits != NULL)){const PBYTE pTempBits = pBitmapImage + pBitmapInfo->bmiHeader.biSize +pBitmapInfo->bmiHeader.biClrUsed * sizeof(RGBQUAD);CopyMemory(pBitmapBits, pTempBits, pBitmapInfo->bmiHeader.biSizeImage);// 解锁并释放位图资源UnlockResource(hMemBitmap);FreeResource(hMemBitmap);return TRUE;}// 在发生错误时执行一些清理工作UnlockResource(hMemBitmap);FreeResource(hMemBitmap);Free();return FALSE;
}//创建纯色的空白位图并将其创建为一个GDI 对象
BOOL Bitmap::Create(HDC hDC, int iWidth, int iHeight, COLORREF crColor)
{// 创建纯色的位图m_hBitmap = CreateCompatibleBitmap(hDC, iWidth, iHeight);if (m_hBitmap == NULL)return FALSE;// 设置宽度和高度m_iWidth = iWidth;m_iHeight = iHeight;// 创建一个兼容的设备环境用以包含要绘制的位图HDC hMemDC = CreateCompatibleDC(hDC);// 创建一个指定颜色的纯白画刷用以填充位图HBRUSH hBrush = CreateSolidBrush(crColor);//将位图选入设备环境HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemDC, m_hBitmap);// 用纯色画刷填充位图RECT rcBitmap = { 0, 0, m_iWidth, m_iHeight };FillRect(hMemDC, &rcBitmap, hBrush);// 清理图形对象SelectObject(hMemDC, hOldBitmap);DeleteDC(hMemDC);DeleteObject(hBrush);return TRUE;
}//绘制位图
void Bitmap::Draw(HDC hDC, int x, int y)
{//确保位图句柄有效if (m_hBitmap != NULL){// 创建一个兼容的设备环境来临时存储位图HDC hMemDC = CreateCompatibleDC(hDC);// 将位图选入设备环境中HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemDC, m_hBitmap);// 将源设备环境的一个图像绘制到目的设备环境上的指定位置BitBlt(hDC, x, y, GetWidth(), GetHeight(), hMemDC, 0, 0, SRCCOPY);// 清理临时设备环境SelectObject(hMemDC, hOldBitmap);DeleteDC(hMemDC);}
}

Bitmap 类 代码解析

类定义

Win32 定义了几个与位图有关的数据结构,因此我们在Bitmap 类代码中经常会看到几个不同的数据结构,其中一个结构是BITMAPINFOHEADER,它存储了与位图有关的头部信息。在从文件或资源中读取位图时,就将其头部存储在一个BITMAPINFOHEADER 结构中。

需要注意的是,我们的Bitmap 类只是设计用来加载BMP文件格式的24位未压缩位图图像。

构造函数和析构函数

所有Bitmap( )构造函数都非常简单,只是调用相应的Create( )函数,执行根据一个文件、一个资源或一种纯色来创建位图的具体工作。

构造函数的思路是,直接调用Create( )方法,利用数据加载Bitmap对象。

析构函数调用Free( )方法,释放与位图有关的内存并清除位图句柄。

Free( )方法首先查看位图句柄m_hBitmap是否有效,在这种情况下,它删除GDI 位图图像并清除句柄。这是释放与位图有关的内存所需的全部操作。

创建位图的三种方法

第一个Create( )方法负责从一个文件中加载位图。

这个方法首先调用Free( ),以确保清除了以前的任何位图数据,这适用于对不同的位图重复使用同一个Bitmap 对象的情况。然后打开文件,检查得到的文件句柄以确保顺利打开了文件而没有出现错误。然后从文件中读取位图的文件头,执行一些检查以确保正确读取而没有出现错误。文件头包含位图文件本身的信息,而信息头部包含位图的信息。接下来读取信息头部,并执行另一个错误检查。

从文件中正确读取头部之后,Create( )方法就可以开始读取位图图像数据的实际工作了。需要说明的是CreateDIBSection( ) Win32函数的用法。

CreateDIBSection()
功能:从原始位图数据中获得一个GDI 位图对象的句柄。

函数原型

HBITMAP CreateDIBSection(HDC hdc,CONST BITMAPINFO *pbmi,UINT iUsage,VOID** ppvBits,HANDLE hSection,DWORD dwOffset);

函数参数

hdc:设备环境句柄。如果iUsage的值是DIB_PAL_COLORS,那么函数使用该设备环境的逻辑调色板对与设备无关位图的颜色进行初始化。

pbmi:指向BITMAPINFO结构的指针,该结构指定了与设备无关位图的各种属性,其中包括位图的维数和颜色。

iUsage:指定由pbmi参数指定的BITMAPINFO结构中的成员bmiColors数组包含的数据类型(要么是逻辑调色板索引值,要么是原文的RGB值)。下列值是系统定义的,其含义为:

DIB_PAL_COLORS:表示成员bmiColors是hdc指定的设备环境的逻辑调色板,使用的是16位索引值数组。

DIB_RGB_COLORS:表示结构BITMAPINFO中包含了RGB值的数组。

ppvBits:指向一个变量的指针,该变量接收一个指向DIB位数据值的指针。

hSection:文件映射对象的句柄。函数将使用该对象来创建DIB(与设备无关位图)。该参数可以是NULL。

如果hSection不是NULL,那么它一定是文件映射对象的句柄。该对象是通过调用带有PAGE_READWRITE或PAGE_WRITECOPY标志的CreateFileMapping函数创建的。不支持只读的DIB类型。通过其他方法创建的句柄将会引起CreateDIBSection函数执行失败。

如果hSection不是NULL,那么函数CreateDIBSection将在hSection引用的文件映射对象中偏移量为dwOffset处获取位图的位数据值。应用程序可以在以后通过调用GetObject函数来检索hSection句柄,其中GetObject函数使用了GreateDIBSection函数返回的GBITMAP类型的对象。

如果hSection为NULL,那么系统将为与设备无关位图(DIB)分配内存。在这种情况下,函数CreateDIBSection将忽略参数dwOffset,应用程序无法在以后获取指向内存的句柄。通过调用GetObject函数来填充的DIBSECTION结构成员dshSection也将成为NULL。

DwOffset:指定从hSection引用的文件映射对象开始处算起的偏移量,超过这个偏移量的地方就是位图的位数据值开始存放的地方。在hSection为NULL时忽略该值。位图的位数据值是以DWORD为单位计算的。

返回值:如果函数执行成功,那么返回值是一个指向刚刚创建的与设备无关位图的句柄,并且*ppvBits指向该位图的位数据值;如果函数执行失败,那么返回值为NULL,并且*ppvBit也为NULL,若想获得更多错误信息,请调用GetLastError函数。

第二个Create( )方法用来从一个资源中加载位图

这个方法大致遵循与第一个Create( )方法相同的模式,只是在这里,位图信息时从内存中的一个资源中检索的,而不是从文件中读取的。

第一步找到位图资源,将资源加载到内存中,然后锁定资源,以便访问其原始数据。接下来存储位图的宽度和高度,复制图像数据,并以此为基础使用CreateDIBSection( )获得一个位图句柄。最后,在发生错误时执行一些清理工作。

最后一个Create( )方法创建纯色的空白位图。

这个方法与另外两个Create( )方法有很大的不同,因为它不涉及任何现有的位图数据,而使用一个名为CreateCompatibleBitmap( )的Win32 函数,根据提供的设备环境创建一个全新的位图。

这个Create( ) 方法中的大多数工作都涉及使用一种纯色来填充位图,首先创建一个兼容的设备环境以包含要绘制的位图,然后创建一个指定颜色的纯色画刷。接下来,将位图选入设备环境并使用纯色画刷进行填充。之后清理图形对象。

绘制位图

Draw( )方法也涉及绘制位图,因此它实际上与第三个Create( )方法相似。不过在这里,是将位图绘制到一个外部的设备环境中,而不是在位图自身上进行绘制。

Draw( )方法接受一个设备环境和一个xy坐标作为参数。绘制位图的第一步是确保位图句柄有效。如果句柄检查正常,则创建一个兼容的设备环境来临时存储位图,将位图选入设备环境。因为绘制图像总是从一个设备环境到另一个设备环境。

位图实际上是使用Win32 BitBlt( )函数绘制的,这个函数将源设备环境中的一个图像绘制到目标设备环境上的指定位置

绘制位图图像有时候称为 位块传输( blitting ),在绘制图像时就是在块传输图像位。

开发Slideshow 幻灯片 示例

Slideshow 目录结构和效果图

Slideshow 目录结构:

Slideshow 效果图:

Slideshow 源代码

Resource.h

//-----------------------------------------------------------------
// Slideshow Resource Identifiers
// C++ Header - Resource.h
//-----------------------------------------------------------------//-----------------------------------------------------------------
// 图标                    Range : 1000 - 1999
//-----------------------------------------------------------------
#define IDI_SLIDESHOW       1000
#define IDI_SLIDESHOW_SM    1001//-----------------------------------------------------------------
// 位图                  Range : 2000 - 2999
//-----------------------------------------------------------------
#define IDB_IMAGE1          2000
#define IDB_IMAGE2          2001
#define IDB_IMAGE3          2002
#define IDB_IMAGE4          2003
#define IDB_IMAGE5          2004

Slideshow.h

#pragma once//-----------------------------------------------------------------
// 包含的文件
//-----------------------------------------------------------------
#include <windows.h>
#include "Resource.h"
#include "GameEngine.h"
#include "Bitmap.h"//-----------------------------------------------------------------
// 全局变量
//-----------------------------------------------------------------
HINSTANCE   g_hInstance;       //程序实例句柄
GameEngine* g_pGame;           //游戏引擎指针
const int   g_iNUMSLIDES = 6;  //更改幻灯片的张数
Bitmap*     g_pSlides[g_iNUMSLIDES]; //存储Bitmap对象的指针
int         g_iCurSlide;       //当前幻灯片在数组中的索引

Slideshow.cpp

//-----------------------------------------------------------------
// 包含的文件
//-----------------------------------------------------------------
#include "Slideshow.h"//-----------------------------------------------------------------
// 游戏事件函数
//-----------------------------------------------------------------//初始化游戏
BOOL GameInitialize(HINSTANCE hInstance)
{// 创建游戏引擎g_pGame = new GameEngine(hInstance, TEXT("Slideshow"),TEXT("Slideshow"), IDI_SLIDESHOW, IDI_SLIDESHOW_SM);if (g_pGame == NULL)return FALSE;// 设置帧率g_pGame->SetFrameRate(1);// 设置实例句柄g_hInstance = hInstance;return TRUE;
}//开始游戏
void GameStart(HWND hWindow)
{// 创建并加载幻灯片位图  这段代码阐明创建位图的三种方法HDC hDC = GetDC(hWindow);g_pSlides[0] = new Bitmap(hDC, TEXT("Image1.bmp"));g_pSlides[1] = new Bitmap(hDC, TEXT("Image2.bmp"));g_pSlides[2] = new Bitmap(hDC, TEXT("Image3.bmp"));g_pSlides[3] = new Bitmap(hDC, IDB_IMAGE4, g_hInstance);g_pSlides[4] = new Bitmap(hDC, IDB_IMAGE5, g_hInstance);g_pSlides[5] = new Bitmap(hDC, 640, 480, RGB(0, 0, 255));// 设置第一个幻灯片g_iCurSlide = 0;
}//游戏结束
void GameEnd()
{// 清理幻灯片位图for (int i = 0; i < g_iNUMSLIDES; i++)delete g_pSlides[i];// 清除游戏引擎delete g_pGame;
}void GameActivate(HWND hWindow)
{
}void GameDeactivate(HWND hWindow)
{
}//绘制当前的幻灯片位图
void GamePaint(HDC hDC)
{// 绘制当前幻灯片位图g_pSlides[g_iCurSlide]->Draw(hDC, 0, 0);
}//游戏循环 逐个显示幻灯片,几秒一个
void GameCycle()
{static int iDelay = 0;// 设置转到下一个幻灯片之前的3秒延迟if (++iDelay > 3){// 恢复延迟计数器iDelay = 0;// 显示下一张幻灯片if (++g_iCurSlide == g_iNUMSLIDES)g_iCurSlide = 0;// 强制重新绘制,以便绘制下一个幻灯片InvalidateRect(g_pGame->GetWindow(), NULL, FALSE);}
}

Slideshow 源代码解析

GameCycle( ) 函数

GameCycle( )函数负责在一次短暂的停顿之后移动到下一张幻灯片。

因为游戏引擎限制了最小帧频为1帧/秒,所以必须在GameCycle( )函数中进一步实现延迟,从而放慢幻灯片的放映。

对于观看每一张幻灯片来说,3秒钟是一个合理的延迟,因此GameCycle( )函数使用了一个静态变量iDelay来延迟各张幻灯片,在进入下一张幻灯片之前等待3秒钟。能够这样做是因为帧频设置为每秒1个周期,可以在第3个周期(即每3秒)才更新幻灯片。

为了移动到下一张幻灯片,需要增加iCurSlide。如果增加iCorSlide时超过了最后一张幻灯片,将从第一张幻灯片重新开始放映幻灯片。

源代码 下载

http://pan.baidu.com/s/1ge2Vzr1

游戏编程入门(4):绘制图形图像相关推荐

  1. 游戏编程入门之绘制动画的精灵(爆炸特效)

    代码: MyDirectX.h: 1 #pragma once 2 //header files 3 #define WIN32_EXTRA_LEAN 4 #define DIRECTINPUT_VE ...

  2. 游戏编程入门(3):绘制基本 GDI 图形

    接上文 游戏编程入门(2):创建游戏引擎和使用该引擎制作小游戏 本篇内容包括: 使用Windows图形设备接口绘制图形的基础知识 设备环境是什么以及它为什么对GDI图形如此重要 如何在Windows中 ...

  3. python游戏编程入门 免费-python游戏编程入门 python游戏编程入门课

    python游戏编程入门 python游戏编程入门课 什么是python游戏编程入门?首先我们需要认识什么是Python Python既是一个软件工具包,也是一种语言.Python软件包包含了一个名为 ...

  4. 游戏编程入门(5):使用键盘和鼠标控制游戏

    接上文 游戏编程入门(4):绘制图形图像 本文内容包括: 如何有效地检测和响应键盘输入 如何处理鼠标输入 如何开发带有动画图形对象的程序,并且可以使用键盘和鼠标来控制动画图形对象 用户输入设备 输入设 ...

  5. Pygame游戏编程入门笔记

    目录 1. Python游戏编程入门 2. 建立开发环境 3. 简单示例 4. 绘制不同形状 5. 事件监听举例 6. 用pygame打印文本 7. 键盘事件 8. 鼠标事件 9. 轮询键盘 10. ...

  6. 游戏编程入门(7):使用子画面动画移动对象

    在几乎所有游戏中,图形的核心都是动画.如果没有动画,那么就没有移动.如果没有移动,那么我们就只能玩棋类游戏和纸牌类游戏. 本文介绍了与游戏中的动画有关的概念,并介绍如何设计和开发一个通用的子画面类,允 ...

  7. 游戏编程入门(完):开发 Stunt Jumper(摩托车跳跃)游戏

    Stunt Jumper 是一个横向滚动的摩托车跳跃游戏,玩家必须仔细控制摩托车手的速度,以便帮助它越过一排汽车. 本章内容包括: Stunt Jumper 的基本思路 如何设计 Stunt Jump ...

  8. python游戏编程入门免费_python游戏编程入门 python游戏编程入门课

    python游戏编程入门 python游戏编程入门课 什么是python游戏编程入门?首先我们需要认识什么是Python Python既是一个软件工具包,也是一种语言.Python软件包包含了一个名为 ...

  9. python游戏编程入门-python游戏编程入门

    <Python游戏编程入门> 这些文章负责整理在这本书中的知识点.注意事项和课后习题的尝试实现. 并且对每一个章节给出的最终实例进行分析和注释. 初识pygame:pie游戏 pygame ...

最新文章

  1. Swift:subscript
  2. 在Zf2中实现Controller按照URL自动注册
  3. 【GOTC 预告】王思宇:从 OpenKruise 看云原生应用负载发展趋势
  4. mybatis 自动生成integer_Intellij IDEA 中使用 MyBatis-generator 自动生成 MyBatis代码
  5. android jar 反射,android 第三方jar库 反射得到自己的资源ID
  6. java7和java8切换_切换表达式到Java吗?
  7. Boost 1.34.0 终于5.12发布了
  8. Sublime Text3 + Golang搭建开发环境
  9. 最小生成树相关知识链接
  10. 【语音编辑】基于matlab语音编辑【含Matlab源码 539期】
  11. Day217.项目总结 -谷粒学院
  12. 峰峰值-峰值-平均值-有效值之间有什么关系?
  13. 采集卡属于计算机设备还是采集设备,显卡能做视频采集卡吗?
  14. 注册表怎么禁用计算机,如何禁用注册表,注册表禁用和启动的方法
  15. 如何使用计算机搜索文件,win7系统如何使用搜索筛选功能快速查找文件
  16. php投票系统中各个文件的作用说明,简述一些php对文件的操作,投票活动的实现...
  17. vxworks下的脚本script创建和使用(相当于windows下的autoexec.bat)
  18. 如何快速掌握一门新的技术
  19. python汇率的转换程序_如何实现python汇率转换代码
  20. 糖友秦女士反映时刻注意自己的饮食太痛苦了

热门文章

  1. win10浏览器 html乱码怎么解决,win10浏览器乱码如何解决_win10浏览器字体乱码修复方法...
  2. 手机必备的5个宝藏APP,个个用了一次都不想卸载,舍不得分享
  3. MDK Keil 工程的Group文件夹图标显示雪花(星号)的原因及解决办法
  4. 成都奔驰加装原厂无钥匙舒适进入 蔚一名车汇
  5. jquery.slimscroll.js 插件库的应用
  6. 【医药医疗】医防融合慢病管理系统原型
  7. boost配置(VC/CodeBlocks)与编译(bjam+msvc/mingW)
  8. 重装系统后磁盘分区不显示该怎么办?教你这样解决方法
  9. PCBA产业摩尔MES解决方案
  10. <<多模态预训练—泛读系列(一)>>ViLBERT—(NeurIPS-2019),VL-BERT—(ICLR-2020),VisualBERT—(ACL-2020)