【问题标题】:Do I need to move the tiles or the player in a 2d tile world?我需要在 2d 瓷砖世界中移动瓷砖或玩家吗?
【发布时间】:2016-11-19 06:37:15
【问题描述】:

我目前正在创建一个 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
    }

【问题讨论】:

  • 只画应该出现在屏幕上的图块,用相机帮你画东西:stackoverflow.com/questions/9997006/…
  • @user3814613 首先,感谢您的评论!我真的不明白你在说什么。我必须重新绘制所有的瓷砖,因为我想让瓷砖移动,这样看起来玩家正在“世界”中行走。另外,我从来没有使用过相机功能,也不知道从哪里开始。
  • 您是否阅读了该评论中链接的帖子?如果你有,你就不必说我不知道从哪里开始。人们提供其他问题的链接以提供帮助;在他们为您完成工作后忽略它们并不是很好。
  • @Ken White 我确实读过它,它是关于 Java 的。据我所知,C# 没有相机控制。这就是为什么我问我应该如何实现“相机控制”或以良好的性能重新绘制每个图块。也许你能帮助我? :)
  • 忽略问题所涉及的语言,并阅读链接答案中解释的概念。它包含许多有关如何处理绘图的有用信息。将相机视为用户的视角 - 如果您正在拍摄电影,它就是镜头前。阅读以了解这些想法;这并不总是与所使用的编码语言有关。

标签: image performance graphics invalidation redraw


【解决方案1】:

您是自己编写磁贴实现吗?问题可能是在每一帧你都在绘制所有的瓷砖。

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

当这个超级精灵的边框比一个阈值(通常是半个瓦片)更接近屏幕边框时,更大的精灵会在当前位置周围重绘——但是没有必要在这个上面绘制所有的瓦片!开始在这个重新定位的精灵上复制超级精灵,您只需要绘制由于偏移而从前一个超级精灵中丢失的图块。

【讨论】:

  • 起初忘记在我的回答中提及这一点。所以+1。顺便说一句,这被称为反向缓冲或双缓冲而不是超级精灵......我的理解是超级精灵用于组合精灵层以提高每个图块的动画性能,而不是作为阴影屏幕......但想法是正确
  • 后/双缓冲具有精确的含义,它每帧都绘制后缓冲区并交换为前缓冲区 - 在这里您构建一个预渲染表,您可以在后缓冲区或前缓冲区上进行 blit之后 - 所以它是正交的 - 但想法是相似的,让显卡推动最多像素而不是 CPU
  • 嗯,我认为你是对的,超级滚动阴影屏幕不完全符合要求...
  • @Spektre 你好。感谢您的帮助:) 我现在不在家,但现在我在家。我会研究这个页面,如果我需要更多帮助,我会通知你。你在问我使用什么语言:c#。而且我只在适合屏幕的情况下绘制图块,仅此而已。
  • 也看看这里,相关docs.google.com/document/d/…
【解决方案2】:

正如 cmets 中所述,您的概念是错误的。所以这里只是简单总结一下如何完成这个任务:

  1. 平铺地图是静态的

从功能的角度来看,玩家移动还是地图无关紧要,但从性能的角度来看,瓷砖的数量远远大于玩家的数量,因此移动玩家的速度更快。

要实现以玩家为中心或跟随视图,您也必须移动相机。

  1. 渲染

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

无论如何,如果您的渲染时间超过 1 毫秒(这很有可能),那么您的计时器就会出错,因为它应该在第一个处理程序停止之前触发多次。由于同步问题,这通常会导致大幅减速,因此生成的 fps 通常甚至比渲染引擎所能提供的还要小。那么如何补救呢?

  1. timer 间隔设置为20ms 或更多
  2. 添加bool _redraw=false 并且仅在需要重绘屏幕时使用它来重绘。因此,在玩家移动、相机移动或转弯等任何动作上,动画更改将其设置为 true
  3. 内部计时器事件处理程序仅在 _redraw==true 时调用您的重绘,然后将其设置为 false。

这将大大提高性能。即使您的重绘所花费的时间超过计时器间隔,这仍然会比您当前的方法快得多。

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

  1. 相机和剪辑

您的地图很可能比屏幕大得多,因此没有必要重新绘制所有图块。您可以查看相机作为选择地图右侧部分的一种方式。如果您的游戏不使用旋转,那么您只需要 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 个平铺放大的矩形)。这样,渲染将独立于您的地图大小。此过程称为剪辑

我强烈建议查看这些相关的 QA:

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

【讨论】:

  • 首先,感谢大家的帮助:) 我已经完成了第 2 步,但我现在不需要第 3 步了。我知道该怎么做。但首先,我想摆脱 de 30 fps p/s。您是否有提示以每 20 毫秒绘制一次完整的地图以达到 50/60 fps 左右?我只绘制与屏幕一样大的地图,因此没有瓷砖出现在它之外。稍后我会实施其余的...感谢您的时间!
  • @MYil 在第一个链接的 QA 中也是我的演示源代码,它足够快。没有看到你是如何渲染的,很难判断瓶颈在哪里......你是如何渲染精灵的?或者你只有瓷砖(没有透明度或面具)?无论如何,错误编码的 gfx 渲染可能会非常缓慢......
  • 感谢您的回答!好吧,我尽量说清楚:) 我有一个二维数组,里面装满了瓷砖。那些瓷砖是类,在这些类中有一个图像。该图像约为1kb。我每 1 毫秒(或 20 毫秒,提供相同的性能)将图像重新绘制为位图。并把它画在一个大画框上。位图大小与图片框大小一样大。每次我按下 keydown 事件时都会重新绘制。然后定时器会打开精灵类中的移动方法,让它移动。让我放一部分我的代码,等一下:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-01-13
  • 1970-01-01
  • 2023-03-02
  • 1970-01-01
  • 2018-02-18
  • 1970-01-01
  • 2018-05-28
相关资源
最近更新 更多