模仿老派雪碧闪烁(理论和概念)

时间:2010-06-05 11:47:55

标签: graphics animation xna sprite

我正在尝试开发一个老式的NES风格的视频游戏,精灵闪烁和图形减速。我一直在想我应该用什么类型的逻辑来实现这样的效果。

如果我想要老式的NES风格,我必须考虑以下限制:

  • 一次在屏幕上不超过64个精灵
  • 每条扫描线或Y轴上的每条线不超过8个精灵
  • 如果屏幕上有太多动作,系统将图像冻结一帧以让处理器赶上动作

从我读过的内容来看,如果屏幕上有超过64个精灵,开发人员只会绘制高优先级精灵而忽略低优先级精灵。它们也可以交替,在奇数编号的相对帧上绘制每个偶数编号的精灵。

扫描线问题很有意思。从我的测试来看,通过像NES那样逐个像素地绘制精灵,不可能在XBOX 360 XNA框架上获得良好的速度。这就是为什么在老式游戏中,如果在一条线上有太多的精灵,如果它们被减半,就会出现一些。对于这个项目的所有目的,我将扫描线设置为8像素高,并通过Y定位将每个扫描线的精灵分组在一起。

为了澄清,我将以8x8像素,而不是1x1的像素为画面绘制精灵。

所以,愚蠢的我需要提出一个解决方案....

  • 一次在屏幕上显示64个精灵
  • 每个'scanline'的8个精灵
  • 可以根据优先级绘制精灵
  • 每帧精灵之间可以交替
  • 模拟减速

这是我目前的理论

首先,我提出的一个基本想法是解决精灵优先级问题。假设0-255(0为低)之间的值,我可以分配精灵优先级,例如:

  • 0到63低
  • 63至127为中等
  • 128到191高
  • 192到255最大

在我的数据文件中,我可以将每个精灵分配为特定优先级。创建父对象时,精灵将在其指定范围之间随机分配一个数字。然后我会按照从高到低的顺序绘制精灵,最终目标是绘制每个精灵。

现在,当精灵在框架中绘制时,我会在其初始优先级内随机生成一个新的优先级值。但是,如果没有在框架中绘制精灵,我可以将32添加到其当前优先级。例如,如果系统只能将精灵绘制到135的优先级,那么可以在未绘制3帧(45 + 32 + 32 + 32 = 141)之后绘制初始优先级为45的精灵

理论上,这将允许精灵交替帧,允许优先级,并将精灵限制为每个屏幕64个。

现在,有趣的问题是如何将精灵限制为每个扫描线只有8个?

我在想,如果我将精灵高优先级排序为低优先级,则循环遍历循环直到我达到64个精灵。但是,我不应该只列出列表中的前64个精灵。

在绘制每个精灵之前,我可以检查通过计数器变量在它的相应扫描线中绘制了多少精灵。例如:

  • 0到7之间的Y值属于Scanline 0,scanlineCount [0] = 0
  • 8到15之间的Y值属于Scanline 1,scanlineCount [1] = 0

我可以为每个绘制的帧重置每条扫描线的值。在向下精灵列表的同时,如果在该扫描线中绘制了精灵,则向扫描线的相应计数器添加1。如果它等于8,则不绘制该精灵并转到具有下一个最低优先级的精灵。

SLOWDOWN

我需要做的最后一件事就是模仿减速。我最初的想法是,如果我每帧绘制64个精灵并且还需要绘制更多的精灵,我可以将渲染暂停16ms左右。然而,在我玩过的NES游戏中,如果没有任何精灵闪烁,有时会减速,而即使有一些精灵闪烁,游戏也会漂亮地移动。

也许为每个在屏幕上使用精灵的对象赋予一个值(比如上面的优先级值),如果所有对象的精度超过阈值,那么引入减速?

结论......

我写的所有内容是否合法且可以正常工作,还是一个白日梦?我们可以用我的游戏编程理论思考哪些改进?

2 个答案:

答案 0 :(得分:3)

首先,我喜欢这个主意。制作复古游戏时,如果忽略了复杂性限制,那就不一样了。通过编写一个强制执行的框架是伟大的想法。

我可以说,如果你将引擎抽象为开源“复古游戏”图形引擎,那确实很酷,我一定会加入!

  • 关于你的最高优先级精灵,也许Priority Queue会简化这部分,因为你可以拔出64个最高优先级的精灵。

  • 随着速度减慢,也许在引擎中你可能会有limitValue。每个精灵都是合适的limitUse。添加所有limitUse变量,如果低于limitValue,则不会减慢速度。现在,对于slowDelay = f(limitUseTotal - limitValue),其中f是将过量的精灵转换为计算的减速值的函数。这是你提出的一般想法还是我误解了它?

答案 1 :(得分:3)

要获得优先权,只需使用Z-index即可。这就是GBA所做的,至少;优先级0呈现在最前面,因此具有最弱的优先级。因此,渲染从高到低,如果你渲染了太多的精灵,请停止。

精灵的优先级由精灵表中的索引决定。 GBA有128个条目[0,127]被排列为连续的四个hword(每个16字节)。对于XNA,您可能会使用List<Sprite>或类似代码。也许Sprite[256]可以简化一些事情。

限制精灵渲染很自然地来自这种机制。因为您从255渲染到0,所以您可以跟踪您触摸的扫描线。所以基本上:

int[] numPixelsDrawnPerScanline = new int[Graphics.ScreenHeight];

foreach(Sprite sprite in SpriteTable.Reverse()) {
    int top = Math.Max(0, sprite.Y);
    int bottom = Math.Min(Graphics.ScreenHeight, sprite.Y + sprite.Height);

    // Could be rewritten to store number of pixels to draw per line, starting from the left.
    bool[] shouldDrawOnScanline = new bool[Graphics.ScreenHeight];

    for(int y = top; y < bottom; ++y) {
        bool shouldDraw = numPixelsDrawnPerScanline[y] + sprite.Width <= PixelsPerScanlineThreshold;

        shouldDrawOnScanline[y] = shouldDraw;

        if(shouldDraw) {
            numPixelsDrawnPerScanline[y] += sprite.Width;
        }
    }

    Graphics.Draw(sprite, shouldDrawOnScanline); // shouldDrawOnScanline defines clipping

    skipDrawingSprite:
}

我不太确定你的减速意味着什么,所以我无法回答你问题的这一部分。遗憾。

希望这有帮助。