【问题标题】:Android scrolling background of bitmap tiles位图瓦片的Android滚动背景
【发布时间】:2011-02-23 18:10:56
【问题描述】:

我试图确定在 Android SurfaceView 上滚动由平铺位图组成的背景的“最佳”方式。我实际上已经成功地这样做了,但想确定是否有更有效的技术,或者我的技术是否适用于所有 Android 手机。

基本上,我创建了一个新的、可变的位图,它比我的 SurfaceView 的尺寸略大。具体来说,我的位图在顶部、底部、左侧和右侧容纳了额外的一行瓷砖。我在我的新位图周围创建了一个画布,并将我的位图图块绘制到它上面。然后,只需将背景位图的“Surfaceview 大小”子集绘制到 SurfaceHolder 的画布上,我就可以向上滚动到任何方向的图块。

我的问题是:

  1. 有没有比在 SurfaceHolder 的画布上绘制背景位图更好的位 blit 技术?

  2. 当我滚动到背景位图的边缘并希望将地图移动一格长度时,最好的做法是什么? 在我看来,我的选择是:

一个。单独重绘我背景中的所有瓷砖,在一个方向上移动瓷砖长度。 (这让我觉得效率低下,因为它需要许多小的位图绘制)。

b.只需将背景位图制作得如此之大,以使其包含整个滚动世界。 (这可能需要一个非常大的位图,但只需要创建一次。)

c。复制背景位图,将其绘制到自身上,但在我们滚动的方向上移动一个平铺长度,并使用几个单独的位图绘制来绘制新显示的平铺行或列。 (这里我假设一个大的位图绘制比覆盖相同范围的多个小位图更有效。)

感谢您阅读所有这些内容,如果您有任何建议,我将不胜感激。

【问题讨论】:

    标签: android


    【解决方案1】:

    我最初在“Box Fox”平台游戏和 RTS 中使用了与您类似的技术,但发现如果您滚动到需要重绘位图的程度,它会导致相当明显的延迟。

    我目前对这些游戏的方法类似于您的选项 C。我将平铺地图图层绘制到一个大位图(约 7x7)网格上,占用比屏幕更大的区域。当用户滚动到这个网格的边缘时,我将网格中的所有位图移过来(将结束位图移动到前面),更改网格的偏移量,然后重新绘制新的边缘。

    我不太确定软件渲染(您的选项 C 或我当前的方法)哪个更快。我认为如果您更改为 OpenGL 渲染,我的方法可能会更快,因为您不必在用户滚动时将尽可能多的纹理数据上传到显卡。

    我不推荐选项 A,因为正如您所建议的那样,为平铺地图绘制的数百个小位图会降低性能,并且在更大的屏幕上会变得非常糟糕。选项 B 甚至可能不适用于许多设备,因为在许多手机上堆空间限制设置得非常低,因此很容易出现“位图大小超过 VM 预算”错误。

    此外,如果您不需要地图/背景的透明度,请尝试使用 RGB_565 位图,因为在软件中绘图要快得多,并且占用的内存更少。

    顺便说一句,我的手机和 10" 平板电脑在我的 RTS 上使用上面的方法都被限制在 60fps,在软件中渲染,并且可以平滑地滚动地图。所以你肯定可以得到一些不错的速度android 软件渲染器。我有一个为我的游戏构建的 2D OpenGL 包装器,但还不需要切换到它。

    【讨论】:

    • 我发现了一个问题。它在正 x 和 y 方向上工作,但是当我将画布位图向上或向左移动一格时,生成的位图/画布上会出现垃圾。这是因为将画布的位图复制到自身上总是从左上角的像素开始……这在一个方向上很好,但在相反的方向上意味着你正在读/写一个你已经读/写过的像素到!我已经通过使用 copy-canvas-shifted 向右/向下滚动的方法解决了这个问题,但另一种方法是在另一个方向滚动。有人知道解决方法吗?
    • @MacD 不确定为什么要将位图复制到它自己上,我从不使用此解决方案复制位图。移动发生在存储位图的数组上,而不是位图本身,这样会快得多。
    • 嗯......那么你如何“移动阵列”?我的“快速”方法是:canvas.setBitmap(canvasBMP); canvas.drawBitmap(canvasBMP, shiftleft, 0), null;换个方向,我必须先将 canvasBMP 复制到另一个 BMP。但是转移一个后备阵列?它是如何在画布上绘制的?
    • 我有这样的事情:Bitmap[][] bitmapArray; 要向左移动,我会这样做:Bitmap[] tempArray=new LayerBitmapBuffer[bitmapCount]; for (int y=0;y<bitmapCount;++y) { layerBitmapBufferTemp[y]=bitmapArray[0][y]; } for (int x=1;x<bitmapCount;++x) { for (int y=0;y<bitmapCount;++y) { bitmapArray[x-1][y]=bitmapArray[x][y]; } } for (int y=0;y<bitmapCount;++y) { bitmapArray[bitmapCount-1][y]=tempArray[y]; //These are the bitmaps that need to be redrawn }
    • 啊...感谢您的澄清!我了解支持位图数组。我也使用了一个,但是对于屏幕上的实际绘图,我使用仍在画布中的位图,移动该画布位图并将其绘制到自身上,然后在移动的一侧填充一条瓷砖。这意味着您不需要每次都将所有图块绘制到屏幕上;你只需要一个更大的位图绘制操作和一行或一列的瓦片来绘制。
    【解决方案2】:

    我认为一种有效的方法是使用 canvas.translate

    在第一次绘图时,整个画布必须用瓷砖填充。新的安卓手机可以轻松快速地做到这一点。

    滚动背景时,我会使用canvas.translate(scrollX, scrollY),,然后我会逐个绘制瓷砖以填补空白,但是,我会使用 canvas.drawBitmap(tileImage[i], fromRect, toRect, null) 通过设置 fromRect 和 toRect 对应于 scrollX 和 scrollY,只会绘制需要显示的图块部分。

    所以一切都将由数学完成,不会为背景创建新的位图 - 节省一些内存。

    编辑: 然而,使用 canvas.translate 和 surfaceView 存在问题,因为它是双缓冲的,并且 canvas.translate 只会同时翻译一个缓冲区而不是第二个缓冲区,因此在依赖时必须考虑这种缓冲区的交替在surfaceView上保存绘制的图像。

    【讨论】:

      【解决方案3】:

      我在地图应用程序中的解决方案依赖于 2 级缓存,首先使用位图和位置创建图块对象,这些对象要么存储在磁盘上,要么存储在矢量中(同步对我来说很重要,多线程 HTTP 通信无处不在这个地方)。

      当我需要绘制背景时,我会检测可见区域并获取我需要的所有图块的列表(这是经过高度优化的,因为它经常被调用)然后要么从内存中提取图块,要么从磁盘加载。即使在稍旧的手机上,我也能获得非常合理的性能,并且滚动流畅,没有打嗝。

      作为警告,我允许未准备好磁贴并将它们与加载图像交换,我不知道这是否适合您,但如果您在 APK 中加载了所有磁贴,您应该没问题。

      【讨论】:

        【解决方案4】:

        我正在使用您的原始方法来绘制透视滚动背景。几天前,我在使用一种简单的技术来进行透视滚动星域模拟时完全偶然想到了这个想法。该应用程序可以在这里找到:Aurora2D.apk

        只需倾斜您的设备或摇动它以使背景滚动(请原谅 2 个弹跳精灵 - 它们可以帮助我用一种有效的方法来显示轨迹)。如果您找到更好的方法,请告诉我,因为多年来我编写了几种不同的方法,而这一种似乎更胜一筹。如果您想比较代码,请给我发邮件。

        【讨论】:

          猜你喜欢
          • 2016-07-21
          • 2011-09-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-03-10
          • 2015-05-31
          • 1970-01-01
          相关资源
          最近更新 更多