我是否需要在2d平铺世界中移动磁贴或播放器?

时间:2016-07-16 00:03:48

标签: image performance graphics invalidation redraw

我目前正在创建一个2d平铺游戏,我想知道是否必须移动磁贴或角色?

我问这个问题,因为我已经创建了" 2d平铺地图"但它运行得太慢了,我无法解决它。我现在尝试了一切,结果是我得到了30 fps。

它运行速度太慢的原因是因为每1ms带有一个计时器,所以正在重新绘制磁贴。但我无法弄清楚如何解决这个问题。

如果有人可以帮助我,我会非常感激!

这就是我制作地图的方式:

        public void makeBoard()
    {
        for (int i = 0; i < tileArray.GetLength(0); i++)
        {
            for (int j = 0; j < tileArray.GetLength(1); j++)
            {
                tileArray[i, j] = new Tile() { xPos = j * 50, yPos = i * 50 };
            }
        }
    }

在这里我重新绘制每1毫秒或更高的瓷砖和精灵:

        private void Wereld_Paint_1(object sender, PaintEventArgs e)
    {
        //label1.Text = k++.ToString();
        using (Graphics grap = Graphics.FromImage(bmp))
        {
            for (int i = 0; i < tileArray.GetLength(0); i++)
            {
                for (int j = 0; j < tileArray.GetLength(1); j++)
                {
                    grap.DrawImage(tileArray[i, j].tileImage, j * 50, i * 50, 50, 50);
                }
            }
            grap.DrawImage(player.movingObjectImage, player.xPos, player.yPos, 50, 50);
            grap.DrawImage(enemyGoblin.movingObjectImage, enemyGoblin.xPos, enemyGoblin.yPos, 50, 50);

            groundPictureBox.Image = bmp;
           // grap.Dispose();

        }
    }

这是具有特定间隔的计时器:

        private void UpdateTimer_Tick(object sender, EventArgs e)
    {
        if(player.Update()==true) // true keydown event is fired
        {
            this.Invalidate();
        }
        label1.Text = lastFrameRate.ToString(); // for fps rate show
        CalculateFrameRate(); // for fps rate show
    }

2 个答案:

答案 0 :(得分:1)

正如评论中提到的,你的概念是错误的。所以这里只是一个如何完成这项任务的简单摘要:

  1. 平铺地图是静态的

    从功能的角度来看,如果玩家移动或者地图无关紧要,但从性能的角度来看,瓷砖的数量远远大于玩家的数量,所以移动玩家的速度更快。

    要实现以玩家为中心或遵循视图,您还必须移动相机。

  2. <强>渲染

    每1毫秒重新粉刷疯狂,如果您的场景中等复杂,现在计算机上很可能不可能。人类视觉无论如何都无法检测到它,因此重新绘制25-40 fps的重要性没有意义。需要更高fps的唯一原因是与显示器刷新同步,以避免扫描线伪影(是甚至LCD使用扫描线刷新)。要获得更多的fps,那么你的显示器的刷新率是没有意义的(许多fps玩家会反对,但我们的看法就是他们所说的无论什么......)。

    无论如何,如果你的渲染花费超过1毫秒(这可能更多),那么你的计时器会被拧紧,因为它应该在第一个处理程序停止之前多次触发。由于同步问题,这通常会导致大量的慢速因此,因此生成的fps通常甚至比渲染引擎提供的更小。那么如何解决这个问题?

    1. timer间隔设置为20ms或更多
    2. 添加bool _redraw=false 并且仅在需要重新绘制屏幕时才使用它重绘。因此,在玩家移动,相机移动或转弯等任何动作上,动画更改都会将其设置为true
    3. 内部计时器事件处理程序仅在_redraw==true时调用您的重绘,并在之后将其设置为false。
    4. 这将大大提高性能。即使您的重绘时间超过计时器间隔,这也会比您当前的方法快得多。

      为避免闪烁,请使用后退缓冲

    5. 相机和剪辑

      你的地图很可能比屏幕大得多,所以没有必要重新绘制所有的瓷砖。您可以将相机视为选择地图右侧部分的方法。如果您的游戏不使用轮播,那么您只需position即可zoom/scale。如果你想要旋转那么2D 3x3同质矩阵就是这样。

      假设您只有位置(没有缩放或旋转),那么您可以使用此转换:

      screen_x=world_x-camera_x
      screen_y=world_y-camera_y
      
      world_x=screen_x+camera_x
      world_y=screen_y+camera_y
      

      所以camera是您的相机视图位置,world是您在地图网格中的平铺位置,screen是屏幕上的位置。如果您在地图中获得了indexes的图块,则只需将它们乘以图块大小(以像素为单位)即可获得world坐标。

      要仅选择可见的图块,您需要获取屏幕的角落位置,将它们转换为世界坐标,然后转换为地图中的索引,最后只渲染矩形,这些点在地图中形成这些点+一些误差范围(例如,在所有方向上渲染1个瓦片放大的矩形)。这样渲染将独立于您的地图大小。此过程称为剪辑

    6. 我强烈建议您查看这些相关的 QA

      链接QAs中的演示仅使用GDI和win32表单应用程序中对位图的直接像素访问,因此您可以将性能与代码进行比较(它们应该相似)并调整代码,直到其行为符合要求。

答案 1 :(得分:1)

您是自己编写磁贴实现吗?可能问题在于,您在每一帧都绘制了所有图块。

带有滚动图块的2D引擎应该在比屏幕更大的精灵上绘制图块,然后绘制精灵周围的快速操作(您需要指定您使用的语言,以便我可以提供一些暗示如何实现快速 - 基本上是在视频内存加速blit,但每种语言都有它的方式来实现它)

enter image description here

当这个超级精灵的边框比一个阈值(通常是半个平铺)更靠近屏幕边框时,更大的精灵会在当前位置重新绘制 - 但是不需要在此绘制所有的平铺!开始在这个重新定位的精灵上复制超级写入,你只需要绘制由于偏移而从前一个超级精灵中删除的磁贴。

enter image description here