【问题标题】:Scaling issue on Android - why is picture so clumsy?Android 上的缩放问题 - 为什么图片如此笨拙?
【发布时间】:2016-09-04 13:27:15
【问题描述】:

我没有 2D 图形和游戏体验。我通过数百个错误和数十个小时的损失自学。我从简单的装扮游戏开始。我使用 Nexus 5x 进行屏幕看起来不错的开发。当我完成一个里程碑时,我尝试在大型联想平板电脑和小型三星迷你手机上玩游戏。看起来很可怕。

原始矢量 PSD 文件看起来很完美,PNG 导出也没有任何问题。但是当我缩放图片时,它是颠簸的。我知道它是位图。但是我在其他地方缩放了另一张图片,看起来不错(两张图片都是 32 位的):

当我从 Google Play 玩一些游戏时,他们从来没有遇到过缩放问题。它们是如何实施的?他们使用向量吗?有什么诀窍吗?

我将所有内容都放入assets 文件夹中,尽管它占用了 1 MB。我反编译了一些 apk,它们没有针对每个分辨率的一组变体。虽然它们可以很好地缩放图片。

一些源代码sn-ps:

protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);

    this.w = w;
    this.h = h;
    canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
    drawCanvas = new Canvas(canvasBitmap);
}

protected void onDraw(Canvas canvas) {
    if (baseBitmap != null) {
        double scale = Math.min((double) w / (double) figure.getW(), (double) h / (double) figure.getH());
        figure.setScaleRatio(scale);
        int sw = (int) (scale * figure.getW());
        int x = ((w - sw) >> 1);
        figure.setX(x);

        paintDressPart(canvas, figure, figure.getMain());
        if (displayedParts != null) {
            for (DressPart dressPart : figure.getParts()) {
                if (displayedParts.contains(dressPart.getId())) {
                    paintDressPart(canvas, figure, dressPart);
                }
            }
        }
    }
}

private void paintDressPart(Canvas canvas, Figure figure, DressPart part) {
    double scale = figure.getScaleRatio();
    int sh = (int) (scale * part.getH());
    int sw = (int) (scale * part.getW());
    int sdx = (int) (scale * part.getDestX());
    int sdy = (int) (scale * part.getDestY());
    scaledRect.set(figure.getX() + sdx, sdy, figure.getX() + sw + sdx, sh + sdy);
    Rect partRect = part.getRect();
    canvas.drawBitmap(baseBitmap, partRect, scaledRect, canvasPaint);
}

如果你想自己看,我在 GitHub 上准备了完整的项目,所以你可以下载并自己运行它:

https://github.com/literakl/DressUp

更新

缩小比例也很糟糕。但感谢 Paavnet 的评论,我意识到 Paint 很重要。在图片上查看差异:

我在 3,2" AVD 图像上运行演示应用程序。图片 278x786 缩放为 107x303。

10 月更新

我重写了应用程序以使用资源文件夹而不是资产。 Android 缩放图片,然后我再次重新缩放它。虽然它看起来比我不使用 Android 资源缩放时要好。资源缩放图片看起来通常比未缩放的 nodpi / assets 图片更好。

我发现 mdpi 效果最好。我什至有 xxhdpi 图片,它看起来比 mdpi 差。我认为即使在 xxhdpi 设备上!但是画得不好可能是这幅画的一个问题。 Android 资源缩放可能会平滑线条的边缘。

【问题讨论】:

  • 我尝试在纵向模式下将图片缩放到全屏,看起来不错。缩小尺寸会丢失细节吗?
  • 如果您将尺寸缩小一半以上,许多图像缩小算法都会出现问题。我会尝试通过多个步骤调整您的大小,看看是否有帮助。
  • 到目前为止,我已经做了很多升级测试。基本上 150x150 和 250x250 对小屏幕和大屏幕都是无用的,而在任何屏幕上 320x586、640x960 或 1242x2208 之间的视觉差异很小。
  • 您是否使用了正确的位图配置?
  • 这里是获取设备指标(例如密度)的方法 WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);最终的 DisplayMetrics displayMetrics = new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(displayMetrics);

标签: android 2d game-development


【解决方案1】:

我遇到了同样的问题 .. 缩放不是那么平滑 .. 最佳标准方法

Bitmap resized = Bitmap.createScaledBitmap(yourBitmap, newWidth, newHeight, true);

您可以在 web 中找到更优化的尺寸转换器 .. 但实际上我以使用结束的最佳解决方案:

第一。使用 Vector SVG 资源 .. 这是 100% 以完美质量工作。只需确保 SVG 元素与 android 兼容(请参阅:https://developer.android.com/studio/write/vector-asset-studio.html

优点:

  • 最好的质量
  • 有效尺寸
  • 可轻松编辑

缺点:

  • 并非所有元素都受支持。

或者

第二。提供高分辨率图像(在 xxxhdpi 中一张就足够了)

优点:

  • 质量好

缺点:

  • 可能会导致性能问题

我希望这可能会有所帮助,'。

【讨论】:

  • 谢谢,我会试试那个方法。 Android Studio 中的 SVG 将为每个维度(hdpi、xx)生成一组 PNG 文件。这将使我的应用程序臃肿。 :-(
  • 感谢提示,它看起来好多了。
【解决方案2】:

他们使用向量吗?

SVG/Vector 是一种方便的解决方案,但矢量艺术需要极高的精度,使其不适用于许多艺术风格。使用基本形状的矢量艺术很容易,但很难添加尽管可以使用 png,jpeg 轻松添加带有 SVG 符号样式的简单细节。您可以观看 SVG 上的性能模式视频,其中 google 人建议 SVG 进行图标设计。大多数游戏不使用它来制作丰富而复杂的图像。

所以我不会去SVG 特别是在游戏中小细节很重要的地方。

它们是如何实现的?有什么诀窍吗?

  1. 一种方法是仅发布具有单个常规资产支持(MDPI、HDPI)的应用程序,如果屏幕密度需要高分辨率,则向用户显示一个对话框,询问他是否要下载高分辨率分辨率资产。

  2. 您可以使用DisplayMetrics获得更高密度的图像并在运行时根据手机密度要求将其缩小。理想情况下,您应该保持相同的纵横比。适合为 4:3 显示器制作的游戏在 16:9 上看起来很糟糕,但在 16:10 上拟合 16:9,没人会注意到。read for more

您可以使用这种方式轻松获取屏幕的高度和宽度(考虑到您使用SurfaceView作为最佳实践

DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int screenHeight = metrics.heightPixels;
int screenWidth = metrics.widthPixels;

在旋转时获得新的宽高:

surfaceChanged(SurfaceHolder holder, int format, int width, int height)

该方法为您提供表面的宽度/高度,因此您现在可以使用:

Bitmap.createScaledBitmap (Bitmap src, int dstWidth, int dstHeight, boolean filter)

从 Bitmap 类中重新调整图像的大小,使其与您正在绘制的表面的大小成比例。您可以研究一些技术来找到合适的缩放因子。

  1. OpenGL,kotlin 可以为您提供创建丰富图形应用程序的优势。通过这些您可以控制内存和GPU 性能,这基本上是大多数游戏的支柱,而且您可以利用控制渲染的优势使用GPU and native memory space 更有效,还有更多你可以想到的,如果你不熟悉这些可能需要一些时间,但是一旦你掌握了它们,它们就可以做很多神奇的把戏。

  2. Webp 将帮助您将大图像的大小减小到相当大的差异。应该尝试 webp。 (建议,webp 在某些设备上对图像的 alpha 通道有一点问题,但对图像大小压缩来说太好了)

结论:使用 OpenGL、SurfaceView、NDK 以及使用缩放因子 docs link 有效加载图像实现最佳实践,也应该尝试一下。

您还可以查看9patch 图像,您可以在其中设置约束以仅缩放图像的某些部分而不是整体。它非常有用。

更新:如建议的那样,如果您要使用更高分辨率的图像,则 also look into thisthis 禁用缩放或手动计算缩放因子。祝你好运

注意:如果您愿意,可以随时使用有效信息编辑此答案。

【讨论】:

  • 感谢您提供详细信息。我现在不使用 SurfaceView 或 OpenGL。完整的源代码在上面/在 github。缩放时我保持纵横比。
  • 更高密度的图片是什么意思?我可以看到这张图片的 72x72 dpi:github.com/literakl/DressUp/blob/master/app/src/main/assets/…。我应该要求更高的 DPI 变体吗?或者只是在编辑器中更改此属性并保存?谢谢。
  • 您可以将图像保持为 512x512,并可以将其缩小为 72x72 dpi 的新位图,但如果您放大 72x72 图像以获得更高的显示密度(大屏幕),您将获得类似的模糊效果你提到的一个问题,因为我建议尝试使用 OpenGL,SurfaceView 因为 SurfaceView 可以显示帧的速度是普通视图的两倍加上 OpenGL 让你几乎可以直接与 GPU 和内存对话,因此虽然 opengl 有点像,但可以节省时间和更多的电力c,c++,让你深入学习
  • 尺寸最大可以达到 2560X1600 和最小 800X400,因此您可以在范围之间选择一个 dpi,略高于平均水平,因此在运行时进行一些缩放,您可以尝试接近标准的 480 dpi 或在 android.dev 网站上查看 9patch 或 webp 图像
  • 你会不会这么好心,并尝试提交SurfaceView的代码sn-p与缩放位图?我听说 OpenGL 中的实现有多丑陋。但这可能不是真的。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-27
  • 1970-01-01
  • 2014-09-16
  • 2017-09-05
  • 1970-01-01
  • 2023-03-05
相关资源
最近更新 更多