版本:VS2015 语言:C++
现在估计很少有能使用32位色彩以下的显卡了吧(也没必要吧),所以我就把8位、16位、24位这样的内容跳过,直接上32位,而且所有的代码都是能在Win10 - VS2015中运行的。
因为是32位存储,所以调色板不需要了,那个是在8位中使用的。
好了,让我们来看一下初始化的代码:
// 游戏初始化
int Game_Init(void* params = NULL)
{
// 基础设置
if (FAILED(DirectDrawCreateEx(NULL, (void**)&lpdd, IID_IDirectDraw7, NULL))) //获取d7对象
return 0;
if (FAILED(lpdd->SetCooperativeLevel(main_window_handle,
//DDSCL_NORMAL
DDSCL_FULLSCREEN | DDSCL_ALLOWMODEX | DDSCL_EXCLUSIVE | DDSCL_ALLOWREBOOT
))) //跟windows协作等级设置为全屏,这是最常用的参数
return 0;
if (FAILED(lpdd->SetDisplayMode(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, 0, 0))) //设置显示模式,如果设置为8位会直接出错
return 0;
// 开始创建显示主界面
memset(&ddsd, 0, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS; //表明ddsCaps是个有效成员
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; //表明该界面是主界面
lpdd->CreateSurface(&ddsd, &lpddsprimary, NULL); //根据界面描述创建主界面
return 1;
}
一样的,就是去掉了调色板,首先获取d7对象,然后创建主界面描述,最后根据描述创建主界面。
现在给大家带来最关心的如何使用32位绘图,这边我使用了两中方法,原理是一样的:
LPWSTR msg = new TCHAR[1024];
// 弹出消息
void popMessage(LPWSTR str)
{
MessageBox(main_window_handle, str, TEXT("提示"), MB_OK);
}
// 32位像素上色
// 不理解的童鞋想一下:我们一般说的都是argb,内存中也是如此排列的,a是放在的是最高位,b是最低位,按照习惯从左往右读就是argb。
// 但是要记住char[0]到char[4]这样排列实际上是内存从小到大排列的,所以写的时候感觉是相反的
void Plot_Pixel_Fast32(int x, int y, int red, int green, int blue, int alpha, UCHAR* video_buffer, int lpitch)
{
DWORD pixel_addr = 4 * x + y * lpitch; //获取当前要写入的地址
video_buffer[pixel_addr] = blue; //写入颜色
video_buffer[pixel_addr + 1] = green;
video_buffer[pixel_addr + 2] = red;
video_buffer[pixel_addr + 3] = alpha;
}
// 游戏主循环
int Game_Main(void* params = NULL)
{
// 判断是否要退出
if (KEYDOWN(VK_ESCAPE))
PostMessage(main_window_handle, WM_CLOSE, 0, 0);
// 初始化主界面描述
DDRAW_INIT_STRUCT(ddsd);
if (FAILED(lpddsprimary->Lock(NULL, &ddsd, DDLOCK_WAIT | DDLOCK_SURFACEMEMORYPTR, NULL))) //加锁
{
wsprintf(msg, TEXT("LOCK 出错了"));
popMessage(msg);
}
//画颜色
UCHAR *video_buffer = (UCHAR*)ddsd.lpSurface;
for (int x = 0; x < 640; ++x)
for (int y = 0; y < 480; ++y)
Plot_Pixel_Fast32(x, y, 0, 255, 0, 128, video_buffer, ddsd.lPitch);
if (FAILED(lpddsprimary->Unlock(NULL))) //解锁
{
wsprintf(msg, TEXT("UNLOCK 出错了"));
popMessage(msg);
}
return 1;
}
是不是感觉比8位的还方便?直接在获取主界面缓存中写就行了,让我们再来简化简化,首先加入另一方法:
#define _RGB32BIT(a, r, g, b) ((b) + (g << 8) + (r << 16) + (a << 24))
// 32位像素上色
// 使用第二种方法
void Plot_Pixel_Fast32_2(int x, int y, int red, int green, int blue, int alpha, UINT* video_buffer, int lpitch)
{
video_buffer[x + y * (lpitch>>2)] = (UINT)(_RGB32BIT(alpha, red, green, blue)); //使用宏直接写,有点区别的是lpitch需要除以4,因为lpitch算的是横向的字节数,而我们把主界面的内存弄成UINT型,是32位、4个字节的,上一节中是我理解的不够深刻
}
然后游戏循环中的画颜色步骤替换成一下代码:
UINT *video_buffer = (UINT*)ddsd.lpSurface;
for (int x = 0; x < 640; ++x)
for (int y = 0; y < 480; ++y)
Plot_Pixel_Fast32_2(x, y, 255, 0, 0, 128, video_buffer, ddsd.lPitch);
注意这边用的是int了,最后来看看结果:
嗯,大红色(当然不是用画图工具画出来的!)。绘制的方式还是比较简单的,仔细分清各个步骤,不要写错就能出结果。
下面来看看界面的双重缓存。
现在游戏应该是3层缓存、或者可能更多吧,主要还是提高游戏运行的流畅度,玩家们想想看CPU为什么要加3级高速缓存呢?其实也就是这个道理吧。
让我们来看看代码:
// 游戏初始化
int Game_Init(void* params = NULL)
{
// 基础设置
if (FAILED(DirectDrawCreateEx(NULL, (void**)&lpdd, IID_IDirectDraw7, NULL))) //获取d7对象
return 0;
if (FAILED(lpdd->SetCooperativeLevel(main_window_handle,
//DDSCL_NORMAL
DDSCL_FULLSCREEN | DDSCL_ALLOWMODEX | DDSCL_EXCLUSIVE | DDSCL_ALLOWREBOOT
))) //跟windows协作等级设置为全屏,这是最常用的参数
return 0;
if (FAILED(lpdd->SetDisplayMode(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, 0, 0))) //设置显示模式,如果设置为8位会直接出错
return 0;
// 开始创建显示主界面
memset(&ddsd, 0, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT; //表明ddsCaps是个有效成员,并且拥有后备的缓冲
ddsd.dwBackBufferCount = 1; //表明有一个缓冲
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | //表明该界面是主界面
DDSCAPS_COMPLEX | //表明拥有缓冲链
DDSCAPS_FLIP; //表明是反正结构的一部分,上面的参数相当于是有缓冲,而这个参数表明可以切换缓冲
lpdd->CreateSurface(&ddsd, &lpddsprimary, NULL); //根据界面描述创建主界面
// 开始创建后备界面,
// 这边其实不算是创建,创建主界面的时候所有的后备表面已经创建完成,
// 我们之后的工作只是把最后一个表面的指针取出来
ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER; //表明该界面是后备界面
if (FAILED(lpddsprimary->GetAttachedSurface(&ddsd.ddsCaps, &lpddsback))) //通过主界面创建出备用表面
{
popMessage(TEXT("创建备用表面出错了"));
return 0;
}
return 1;
}
// 游戏主循环
int Game_Main(void* params = NULL)
{
// 判断是否要退出
if (KEYDOWN(VK_ESCAPE))
PostMessage(main_window_handle, WM_CLOSE, 0, 0);
// 初始化主界面描述
DDRAW_INIT_STRUCT(ddsd);
if (FAILED(lpddsback->Lock(NULL, &ddsd, DDLOCK_WAIT | DDLOCK_SURFACEMEMORYPTR, NULL))) //有备用表面时用备用表面加锁
{
wsprintf(msg, TEXT("LOCK 出错了"));
popMessage(msg);
}
//画颜色
UINT *video_buffer = (UINT*)ddsd.lpSurface;
for (int x = 0; x < 640; ++x)
for (int y = 0; y < 480; ++y)
Plot_Pixel_Fast32_2(x, y, 0, 0, 255, 128, video_buffer, ddsd.lPitch);
if (FAILED(lpddsback->Unlock(NULL))) //解锁
{
wsprintf(msg, TEXT("UNLOCK 出错了"));
popMessage(msg);
}
while (FAILED(lpddsprimary->Flip(NULL, DDFLIP_WAIT))); //切换界面,这边的while不是很懂,应该每次只会调用一次
return 1;
}
在创建主界面的时候设置后备的参数,然后再从主界面取出备用界面就OK了。在游戏循环中直接在备用界面中着色,完成后由主界面进行切换。
那么怎么创建3重缓冲呢?书上没有说,我一开始以为要创建两次,然后我就google了一下,在微软的官网的找到了这么一句话:
You do not need to keep track of all surfaces in a triple buffered flippingchain. The only surfaces you must keep pointers to are the primary surface andthe back-buffer surface.
意思就是不用显示调用循环链,直接放到备用缓冲里面就可以了,玩家们仔细看看我对后备缓冲创建时的注释。
嗯,这样就很简单了,直接把dwBackBufferCounting设置为2就行了,其他的操作跟双缓冲一样。
好了,这边的难度感觉确实要高点了,我看的有点慢,下一节还会讲第七章,是如何把图像拷贝到主界面中,按cocos里来说就是创建Sprite精灵。