所以,我假设你想在你的一款游戏中添加战争迷雾?
几周前,我为当地一所大学制作了一个小型演示,以展示 A* 寻路功能,所以我想我可以为您添加战争迷雾。结果如下:
初始地图
首先,您从完全可见的完整地图开始
Fog
然后,我添加了一个表面来覆盖整个屏幕(请注意,我的地图比屏幕小,因此对于这种情况,我只是在屏幕上添加了战争迷雾,但如果您有滚动,请确保它覆盖每个地图像素1:1)
mFogOfWar = SDL_CreateRGBSurface(SDL_HWSURFACE, in_Width, in_Height, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
SDL_Rect screenRect = {0, 0, in_Width, in_Height};
SDL_FillRect(mFogOfWar, &screenRect, 0xFF202020);
然后,你需要绘制它...我在绘制游戏对象之后和绘制 UI 之前添加了这个调用
DrawSurface(mFogOfWar, 0, 0);
Where
void RenderingManager::DrawSurface(SDL_Surface* in_Surface, int in_X, int in_Y)
{
SDL_Rect Dest = { in_X, in_Y, 0, 0 };
SDL_BlitSurface(in_Surface, NULL, mScreen, &Dest);
}
这应该会给你以下结果:
《冲孔面》
然后我创建了一个32位的.png
看起来像这样(棋盘显示 alpha)
当渲染我的主角时,我添加了这个调用:
gRenderingManager.RemoveFogOfWar(int(mX) + SPRITE_X_OFFSET, int(mY) + SPRITE_Y_OFFSET);
偏移量只是为了使打孔与精灵居中,基本上,我要传递给的内容RemoveFogOfWar
是我的精灵的中心。
消除战争迷雾
现在是战争迷雾的核心。我做了两个版本,一个版本永久删除了战争迷雾,另一个版本重置了战争迷雾。我的战争迷雾重置依赖于我punch
表面具有轮廓,其中 alpha 重置为0
事实上,我的角色移动的像素比每帧轮廓包含的像素少,否则我会保留Rect
我的打孔器被打在哪里,我会在再次打出新的打孔器之前重新填充它。
由于我找不到与 SDL 的“乘法”混合,因此我决定编写一个简单的函数,在打孔表面上进行迭代并更新战争迷雾表面上的 alpha。最重要的部分是确保您保持在表面的范围内,因此它占用了大部分代码......可能有一些裁剪函数,但我没有费心检查:
void RenderingManager::RemoveFogOfWar(int in_X, int in_Y)
{
const int halfWidth = mFogOfWarPunch->w / 2;
const int halfHeight = mFogOfWarPunch->h / 2;
SDL_Rect sourceRect = { 0, 0, mFogOfWarPunch->w, mFogOfWarPunch->h };
SDL_Rect destRect = { in_X - halfWidth, in_Y - halfHeight, mFogOfWarPunch->w, mFogOfWarPunch->h };
// Make sure our rects stays within bounds
if(destRect.x < 0)
{
sourceRect.x -= destRect.x; // remove the pixels outside of the surface
sourceRect.w -= sourceRect.x; // shrink to the surface, not to offset fog
destRect.x = 0;
destRect.w -= sourceRect.x; // shrink the width to stay within bounds
}
if(destRect.y < 0)
{
sourceRect.y -= destRect.y; // remove the pixels outside
sourceRect.h -= sourceRect.y; // shrink to the surface, not to offset fog
destRect.y = 0;
destRect.h -= sourceRect.y; // shrink the height to stay within bounds
}
int xDistanceFromEdge = (destRect.x + destRect.w) - mFogOfWar->w;
if(xDistanceFromEdge > 0) // we're busting
{
sourceRect.w -= xDistanceFromEdge;
destRect.w -= xDistanceFromEdge;
}
int yDistanceFromEdge = (destRect.y + destRect.h) - mFogOfWar->h;
if(yDistanceFromEdge > 0) // we're busting
{
sourceRect.h -= yDistanceFromEdge;
destRect.h -= yDistanceFromEdge;
}
SDL_LockSurface(mFogOfWar);
Uint32* destPixels = (Uint32*)mFogOfWar->pixels;
Uint32* srcPixels = (Uint32*)mFogOfWarPunch->pixels;
static bool keepFogRemoved = false;
for(int x = 0; x < destRect.w; ++x)
{
for(int y = 0; y < destRect.h; ++y)
{
Uint32* destPixel = destPixels + (y + destRect.y) * mFogOfWar->w + destRect.x + x;
Uint32* srcPixel = srcPixels + (y + sourceRect.y) * mFogOfWarPunch->w + sourceRect.x + x;
unsigned char* destAlpha = (unsigned char*)destPixel + 3; // fetch alpha channel
unsigned char* srcAlpha = (unsigned char*)srcPixel + 3; // fetch alpha channel
if(keepFogRemoved == true && *srcAlpha > 0)
{
continue; // skip this pixel
}
*destAlpha = *srcAlpha;
}
}
SDL_UnlockSurface(mFogOfWar);
}
然后给了我这个keepFogRemoved = false
即使角色四处移动后
而这与keepFogRemoved = true
验证
重要的部分是确保您不会在像素缓冲区之外写入,因此请注意负偏移量或会使您超出宽度或高度的偏移量。为了验证我的代码,我添加了一个简单的调用RemoveFogOfWar
当单击鼠标并尝试角落和边缘以确保我没有遇到“差一”问题时
case SDL_MOUSEBUTTONDOWN:
{
if(Event.button.button == SDL_BUTTON_LEFT)
{
gRenderingManager.RemoveFogOfWar(Event.button.x, Event.button.y);
}
break;
}
Notes
显然,“打孔”不需要 32 位纹理,但这是我能想到的最清晰的方式来向您展示如何做到这一点。只需每像素 1 位(开/关)即可完成此操作。您还可以添加一些渐变,并更改
if(keepFogRemoved == true && *srcAlpha > 0)
{
continue; // skip this pixel
}
对于类似的东西
if(*srcAlpha > *destAlpha)
{
continue;
}
为了保持平滑的混合,如下所示:
3 战争迷雾状态
我想我应该添加这个...我添加了一种创建 3 状态战争迷雾的方法:visible
, seen
and fogged
.
为此,我只需保留SDL_Rect
我上次“击打”战争迷雾的地方,如果阿尔法低于某个值,我会将其限制在该值。
因此,只需添加
for(int x = 0; x < mLastFogOfWarPunchPosition.w; ++x)
{
for(int y = 0; y < mLastFogOfWarPunchPosition.h; ++y)
{
Uint32* destPixel = destPixels + (y + mLastFogOfWarPunchPosition.y) * mFogOfWar->w + mLastFogOfWarPunchPosition.x + x;
unsigned char* destAlpha = (unsigned char*)destPixel + 3;
if(*destAlpha < 0x60)
{
*destAlpha = 0x60;
}
}
}
mLastFogOfWarPunchPosition = destRect;
就在战争迷雾被“打击”的循环之前,我得到了类似于《星际争霸》等游戏中可能出现的战争迷雾:
现在,由于“看到的”战争迷雾是半透明的,因此您需要调整渲染方法以正确剪辑雾中的“敌人”,这样您就看不到它们,但仍然可以看到地形。
希望这可以帮助!