SDL - 绘制'负面'圈子(战争迷雾)

wil*_*ill 7 c++ sdl

我有这个800x600square我想画到屏幕上.我想在其中"剪切"圆圈(其中alpha为0).基本上我是在地图上绘制这个整个矩形所以在我绘制的这些'圆圈'中,你可以看到地图,否则你会看到灰色方块

ema*_*tel 26

所以,我假设你试图为你们中的一个游戏增加战争迷雾?

几个星期前我为一个当地大学制作了一个小型演示来展示A*寻路,所以我想我可以为你增加战争迷雾.结果如下:

初始地图

首先,您从完整的地图开始,完全可见

完整的地图

多雾路段

然后,我添加了一个表面来覆盖整个屏幕(注意我的地图比屏幕小,所以对于这种情况我只是在屏幕上添加了战争迷雾,但如果你有滚动,请确保它覆盖每个地图像素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);
Run Code Online (Sandbox Code Playgroud)

然后,你需要绘制它......我在绘制游戏对象之后和绘制UI之前添加了这个调用

DrawSurface(mFogOfWar, 0, 0);
Run Code Online (Sandbox Code Playgroud)

哪里

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);
}
Run Code Online (Sandbox Code Playgroud)

哪个应该给你以下结果:

一切都在变幻莫测

"打孔表面"

然后我创建了一个32位.png,看起来像这样(棋盘显示alpha)

冲床

渲染我的主角时,我添加了这个调用:

gRenderingManager.RemoveFogOfWar(int(mX) + SPRITE_X_OFFSET, int(mY) + SPRITE_Y_OFFSET);
Run Code Online (Sandbox Code Playgroud)

偏移量只是以精灵为中心,基本上,我传递的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);
}
Run Code Online (Sandbox Code Playgroud)

keepFogRemoved = false即使在角色四处走动之后,这也给了我这个

战争迷雾

而这与 keepFogRemoved = true

战争之雾永久

验证

重要的是确保你不要在像素缓冲区之外写字,所以要注意负偏移或偏移会使你超出宽度或高度.为了验证我的代码,我添加了一个简单的调用,RemoveFogOfWar当单击鼠标并尝试角落和边缘以确保我没有"一个一个"的问题

case SDL_MOUSEBUTTONDOWN:
    {
        if(Event.button.button == SDL_BUTTON_LEFT)
        {
            gRenderingManager.RemoveFogOfWar(Event.button.x, Event.button.y);
        }
        break;
    }
Run Code Online (Sandbox Code Playgroud)

笔记

显然,你不需要32位纹理来"冲击",但这是我能想到的最清晰的方式来向你展示如何做到这一点.可以使用每像素1位(开/关)来完成.您还可以添加一些渐变,并更改

if(keepFogRemoved == true && *srcAlpha > 0)
{
    continue; // skip this pixel
}
Run Code Online (Sandbox Code Playgroud)

喜欢的东西

if(*srcAlpha > *destAlpha)
{
    continue;
}
Run Code Online (Sandbox Code Playgroud)

为了保持这样的平滑混合:

在此输入图像描述

3国战大雾

我想我应该补充这...我添加了一个方法来创建战争3状态雾:visible,seenfogged.

为了做到这一点,我只是保留了SDL_Rect我最后"打了"战争迷雾的地方,如果alpha值低于某个值,我就把它夹在那个值上.

所以,只需添加即可

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;
Run Code Online (Sandbox Code Playgroud)

就在战争迷雾被"打了"的循环之前,我得到了类似于星际争霸游戏中的战争迷雾:

3状态

现在,由于"看见"的战争迷雾是半透明的,你需要调整你的渲染方法来正确剪辑雾中的"敌人",所以你看不到它们,但你仍然可以看到地形.

希望这可以帮助!

  • 我非常感谢你在答案中所做的工作.这更像是一个教程!+1 (6认同)