安卓游戏开发手册-全-

安卓游戏开发手册(全)

原文:zh.annas-archive.org/md5/677EF72CE0EEA561393A2FD5106AE241

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

乐趣是生活中娱乐的必要关键词。有许多平台专为娱乐而设,游戏是其中之一。世界各地有许多类型的游戏。曾经游戏局限于体育、棋盘游戏、纸牌游戏等。然后,游戏进入了数字领域,有了特定的游戏设备。逐渐地,它们现在已经进入了移动平台。Android 是最有前途的平台之一。Android 市场每天都在增长,而 Android 游戏也在随之增长。

本书主要面向游戏程序员。许多人认为游戏编程与其他任何编程工作相同。然而,我的个人观点不同——游戏编程不是坐在开放的代码编辑器旁,输入计算机语言,而是创造一种传播娱乐的媒介。

本书主要关注开发游戏的技术部分,特别是针对 Android。它将帮助开发者更好地创建游戏。游戏编程远比技术更为逻辑。我在这本书中试图用我迄今为止的职业经验来阐明这种逻辑。

本书涵盖内容

第一章,Android 游戏开发,将介绍在 Android 平台上游戏开发的准则和规则。

第二章,介绍不同的 Android 平台,将披露当前 Android 设备的各种变体,如智能手机、电视、平板电脑和智能手表。它将详细阐述在这些平台上创建游戏时可能遇到的所有可能困难和可能的解决方案。

第三章,不同的 Android 开发工具,将介绍开发 Android 应用程序的不同工具以及如何选择特定目的的合适工具。

第四章,行业中的 Android 开发风格和标准,将涵盖游戏开发领域中当前的开发风格和标准。这将主要讨论 Android SDK 上的 Java 游戏编码标准和风格。

第五章,理解游戏循环和帧速率,将演示使用 Android SDK(Java)创建和维护游戏循环。本章还将涵盖游戏循环对帧速率的影响。

第六章,改善 2D/3D 游戏的性能,将解释在 Android 上开发 2D 和 3D 游戏的所有限制,以及改进性能的常见错误和避免这些错误的方法。

第七章,使用着色器,将描述在 Android 平台上使用着色器。它通过 OpenGL 揭示了着色器在游戏开发中的作用范围。

第八章,性能和内存优化,将提供优化任何 Android 游戏的深入知识。

第九章,测试代码和调试,将教你调试 Android 游戏的不同方法。

第十章,VR 游戏中 Android 的发展前景,将介绍在 Android 上进行虚拟现实游戏开发的前景。本章描述了 VR 的各种发展前景以及在游戏开发中的未来。

第十一章,使用 C++和 OpenGL 开发 Android 游戏,将简要解释使用 C++和 OpenGL 进行游戏开发。

第十二章,“打磨 Android 游戏”,将专注于完成 Android 游戏并使其准备好发布。

第十三章,“第三方集成、货币化和服务”,将详细说明任何第三方工具或 SDK 的可能集成以实现游戏的货币化。

本书所需内容

假设读者已经是在 Android 平台上工作的游戏开发人员。您需要对使用 Java 和 C++进行 Android 编程有清晰的了解。

读者需要在各种 Android 开发平台上工作;大部分代码都使用 Android SDK。您还需要了解有关广告、分析、应用内购买等多个第三方 SDK 的概念。

本书适合对象

本书适合任何具有先前开发 Android 游戏知识的游戏开发人员。对游戏开发有良好的理解以及对 Android 平台应用程序开发和 Java/C++的基本知识将受到赞赏。

约定

在本书中,您将找到一些区分不同信息类型的文本样式。以下是这些样式的一些示例及其含义的解释。

文本中的代码单词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 句柄显示如下:“我们可以通过使用include指令包含其他上下文。”

代码块设置如下:

<application
<!-- other declarations and tags -->
android:isGame="true"
<!-- other declarations and tags -->
>

任何命令行输入或输出都以以下方式编写:

cd platform-tools

新术语重要词汇以粗体显示。您在屏幕上看到的词语,例如菜单或对话框中的词语,会在文本中显示为:“在配置项目窗口中,输入应用程序的名称。”

注意

警告或重要说明会显示为这样的框。

提示

提示和技巧会显示为这样。

第一章:安卓游戏开发

在过去的十年里,游戏开发已经成为一个非常受欢迎的职业。以前,它局限于个人电脑、游戏机和一些嵌入式游戏设备。如今的世界完全配备了现代化的设备,技术更好,便携性更好,灵活性更好,质量更好。这为开发者创造更高质量的游戏和减少限制打开了大门。

安卓是一个现代操作系统,被广泛应用于许多硬件平台。因此,安卓世界已成为游戏开发者的目标。最有效和有用的目标是安卓智能手机和平板电脑。根据全球移动操作系统市场份额的调查,2015 年安卓占据了 78-80%的份额。安卓现在不仅是一个移动操作系统,还被用于电视和智能手表。因此,安卓在游戏开发者中的受欢迎程度正在飙升。

这本书对那些已经有安卓游戏开发背景的人会有所帮助。让我们从以下主题开始:

  • 安卓游戏开发

  • 游戏不仅仅是一个应用程序。

  • 为你的游戏选择目标设备配置

  • 在安卓上制作游戏的最佳实践

安卓游戏开发

现在让我们专注于本书的主题。尽管游戏开发涵盖了许多平台和技术,但本书只关注安卓。

安卓是基于 Linux 内核的移动操作系统。目前由谷歌开发。自 2008 年以来,该操作系统已发布了许多版本。但在发布安卓 2.2(Froyo)和安卓 2.3(Gingerbread)之后,这个操作系统引起了许多游戏开发者的注意。安卓使用所谓的达尔维克虚拟机DVM),这是Java 虚拟机(JVM)的开源实现。达尔维克和标准 JVM 之间有一些差异,有些微妙,有些不那么微妙。DVM 也不是针对 Java SE 或 Java ME,而是针对一个名为 Apache Harmony Java 的 Apache 实现。如果你刚好从 Java ME 过渡过来,这些都会造成一些学习曲线。谷歌在安卓 4.4(KitKat)中推出了一种名为安卓运行时ART)的 DVM 替代方案,ART 从安卓 5.0(棒棒糖)开始取代了 DVM。ART 主要特点是提前编译AOT)和改进的垃圾回收过程,并提供更小的内存占用以优化内存操作。然而,大多数游戏开发者使用 DVM 来支持旧版本的安卓设备。

当许多硬件平台开始采用这个操作系统时,安卓游戏开发开始大规模开展。安卓主要被用于移动和平板设备。当移动游戏行业开始从 Symbian 或 Java 迁移到安卓或其他智能移动操作系统时,安卓游戏开发开始蓬勃发展。

安卓游戏成功的原因有几个:

  • 流畅的用户界面

  • 更好的互动性

  • 触摸界面

  • 更好的外观和感觉

  • 更好的硬件平台

  • 更多设计灵活性

使用通用操作系统总比使用嵌入式实时操作系统RTOS)更容易。用户不需要花时间学习不同硬件的可用性。安卓就是这样一个易于使用的操作系统。

在安卓中,视觉用户界面非常吸引人,因为它总是在比 Symbian、Java 或嵌入式操作系统更好的硬件配置上运行。这增强了用户体验,这也是为什么它被这么多组织所采用的原因之一。随着安卓用户群的增加,许多游戏开发者开始瞄准这个平台。

从游戏设计的角度来看,增强的安卓功能列表给移动游戏的探索提供了灵活性。因此,游戏设计风格得到了提升。

当前世界上有各种类型的运行 Android 的硬件。除了手机,Android 还被用于平板电脑、电视、手表、游戏机、数码相机、个人电脑和其他设备上。如今,游戏开发者几乎在每个 Android 平台上进行开发。

功能和支持

直接操作界面是 Android 的顶级特性。它通过持续表示感兴趣的对象、动态实时操作和动态反馈与用户进行交互。Android 主要使用触摸界面,实时操作如滑动、拖动、轻击和多点触控,在 Android 游戏开发中被广泛使用。

Android 应用程序开发主要基于 Java(SDK)和 C++(NDK),这是世界上最常见的编程语言。因此,开发游戏变得更加容易。

出色的多媒体支持使 Android 在赢得流行度方面迈出了一大步。游戏开发者现在可以在游戏中自由使用多媒体对象,以提高游戏质量。

自 2.2 版(Froyo)以来,谷歌开发了一个名为 Google Play 服务的集成服务。这是一个封闭的系统级 API 服务提供商,在游戏开发中非常有用。

对于 Android 开发,有大量的第三方工具可用,这也使游戏开发者的工作变得更加轻松。我们可以提到一些工具,如 Android Studio、App Inventor、Corona、Delphi、Testdroid、Sample Directmedia Layer、Visual Studio、Eclipse IDE 和 RubyMotions。

Android 设备硬件配置必须遵循一个最低配置列表,这样开发者就可以很容易地识别配置。此外,它必须保持一个最低标准,以便轻松运行应用程序。

与 Android 设备(主要是手机或平板电脑)相关联的传感器有很多,这对于设计游戏的控制是一个非常好的选择。

Android 通过蓝牙、Wi-Fi、GSM/CDMA/EDGE、LTE、NFC、IDEN 等提供了出色的连接功能。这些帮助游戏开发者轻松创建多人游戏。

虚拟现实是另一个领域,Android 通过 Cardboard SDK 被使用。我们稍后会更详细地讨论这个话题。

这些是游戏开发者应该牢记的特性。其他特性对于游戏开发来说不那么重要。然而,总是有机会去探索更多,并创建一些特定特性导向的游戏。

挑战

在 Android 平台上开发游戏的主要挑战是以高效的方式充分利用其功能。

Android 设备配置范围广泛。因此,针对大多数设备设计游戏是一个很大的挑战。

许多 Android 游戏开发者为特定的硬件配置设计和构建游戏,比如 Tegra 或 Snapdragon,或者像 Xperia Play 这样的特定设备。Nvidia 的 Tegra 是这些情况下最常用的芯片;THD品牌通常表示游戏是专为 Tegra 构建的。Nvidia 在桌面端与开发者合作方面拥有丰富的经验,并将这一专业知识带到了移动设备上。鼓励 Android 游戏开发者利用 Tegra 特定的 API 来构建他们的游戏。这种情况的问题在于大多数用户的手机上并没有 Tegra。事实上,许多本来可能搭载这种游戏友好芯片的 LTE 手机现在都转向了 Snapdragon S4。对于开发者来说,要在不同的图形处理器上保持性能非常困难。

用户体验

Android 游戏可以通过其功能提供出色的用户体验。

游戏控制可以利用加速计或重力传感器进行基于物理的机制(如果硬件支持),这总是实时互动的一个额外优势。

在触摸屏设备上,可以通过 Android 体验到诸如滑动、拖动、捏合和多点触控等动态控制。

Android 支持 OpenGL 以获得更好的图形渲染,从而增强游戏的视觉质量。

Android 中的 Miracast 是另一个功能,它使游戏可以使用多个显示屏和屏幕共享,以获得更好的体验。

设计约束

任何游戏的开发都需要一个设计,Android 也不例外。Android 游戏的设计需要对目标硬件有很多了解。Android 有成千上万种不同的设备。设计师必须非常谨慎地选择他们的目标,然后设计游戏范围。

正如之前所述,这不仅对程序员是一个挑战,对设计师也是如此。不同的 Android 设备具有不同的配置,但对于设计师来说,有必要针对共同特征。

游戏不仅仅是一个应用程序

应用程序开发人员经常转向游戏开发,反之亦然。许多人不改变他们的风格,并相应地进行游戏开发。每个游戏开发者都应该记住游戏不仅仅是一个应用程序

游戏与应用程序

游戏可以简称为交互式娱乐系统。游戏的主要目标是提供乐趣,无论是软件还是体育锻炼。另一方面,应用程序的主要目标是通过机械工作使生活更轻松。因此,这两者的开发方法完全不同。然而,这仍然是一个讨论的焦点,因为每个游戏都是一个应用程序。任何应用程序都可以适应游戏的特性,以提供更好的用户体验。

很难区分游戏开发与应用程序开发的复杂性。然而,游戏开发有优势。大多数应用程序开发者不必过多关注速度性能,而所有游戏开发者都必须关注游戏的速度和帧率。

每个游戏肯定都是一个应用程序,但并非每个应用程序都是一个游戏。这个陈述本身传达了这样一个信息,即在一个参考标准上,游戏开发的参数比应用程序更多,但它必须具有应用程序的所有特性。

应用程序开发是技术导向的,而游戏开发是以乐趣为导向的。这增加了游戏开发的困难。乐趣是一种情感,没有参数可以计算。因此,在制作游戏时,开发人员永远无法知道游戏在乐趣方面到底能达到什么程度。另一方面,应用程序开发人员非常确定,只要所有规格满足要求,应用程序目标就可以实现。

游戏开发非常严格地需要数学来处理物理或图形方面;即使 AI 也需要大量的数学来处理低级别的东西。应用程序更多地受技术驱动,图形的使用有限。

任何符合游戏资格的应用程序必须满足以下标准:

  • 它必须在乐趣方面满足一组用户

  • 必须有一组里程碑供应用程序用户实现

  • 它应该奖励用户达到里程碑

  • 它应该有一个更动态的用户界面

  • 必须有更好的视觉冲击

  • 它应该是以性能为驱动而不是以功能为驱动

Android 应用程序和游戏的生命周期

应用程序生命周期适用于在同一平台上制作的任何游戏。但是游戏的周期更多,如下图所示:

Android 应用程序和游戏的生命周期

应用程序的生命周期比游戏生命周期简单。游戏周期在应用程序生命周期的运行阶段内运行。这通常被称为游戏循环。这将在后面详细讨论。

这个游戏循环在游戏状态上运行。应用程序可能只有一个运行状态,但有多个游戏更新状态。在典型的游戏开发系统中,至少有两个更新状态。一个取决于游戏循环的执行,另一个取决于时间间隔。第二个实际上控制着帧率。

游戏和应用程序的性能

游戏和应用程序的性能管理系统之间存在明显的差异。性能是游戏开发中最大的要求之一,而对于应用程序来说,它只是一个推荐的特性,因为帧率不会影响质量。

公认的事实是,游戏比同样规模的应用程序更重。游戏在重复的帧上运行一组任务。这增加了处理器的指令流量。在应用程序中,通常没有循环;应用程序的状态取决于用户的操作。在这种情况下,处理器有足够的时间来执行指令,因为没有重复发送指令。

游戏和应用程序的内存管理

不是游戏的应用程序与游戏有不同的内存管理。在游戏的情况下,多媒体资产是主要对象,占用堆的比例比类对象大。但在应用程序的情况下,情况正好相反。应用程序只需要加载它们需要的对象,即类对象。

对于任何游戏开发者来说,内存优化是必须的。由于内存的广泛使用,开发者不能承担在内存中加载未使用的对象,或者由于处理不当的内存指针导致的内存泄漏。这直接影响游戏的运行。对于应用程序,内存优化显然是一个好的实践,但大多数情况下对应用程序的运行没有直接或间接的影响。然而,一个好的程序员应该始终了解内存优化。

选择目标设备配置

如前所述,安卓有各种设备配置。因此,对于安卓游戏开发者来说,非常重要的是要非常谨慎地选择目标。一般的方法应该具有这些参数:

  • 游戏规模

  • 目标受众

  • 功能要求

  • 可移植性的范围

游戏规模

这基本上是游戏制作的规模。规模越大,需要的配置就越好。这主要包括游戏大小,即它在设备上消耗的内存量。许多安卓设备配置的 RAM 和内部存储空间非常低。如果目标设备没有所需的配置,游戏将无法运行。即使游戏经过了充分优化,也可能因为运行的硬件平台而失败。

每个游戏都需要一组递归执行的进程,这需要处理器速度。如果游戏的进程密集,并且目标设备的处理器速度较慢,游戏将出现一些可怕的帧率问题,或者崩溃。

每个安卓游戏开发者在选择目标设备时必须了解内存、处理器和其他约束的要求。

让我们以一个需要至少 120MB 磁盘空间安装、512MB RAM 运行和 1.2 GHz 处理器速度达到良好帧率的安卓游戏为例。现在考虑一个移动设备,它完全符合这些规格,但作为开发者,不能假设设备没有安装或并行运行其他应用程序。因此,在这种情况下,即使设备符合要求,也有一个合理的假设,即游戏将无法获得所需的支持。因此,对于这个例子游戏,目标设备必须比最低要求更高配置。

现在,让我们看看相反的情况。假设相同的游戏要求,考虑一个具有 8GB 可用存储空间、2GB RAM 和 2GHz 多核处理器的设备。毫无疑问,游戏将在该设备上以最佳性能运行,但该设备本可以支持更大规模的游戏。因此,在这种情况下,资源利用效率不高。这就是移植的作用。游戏开发者应提升游戏质量,并为这些高端配置设备创建不同的版本。

在行业中,通常会排除一些设备,以使游戏能够正常运行。在一些情况下,游戏开发者会创建单独的游戏版本,以支持大多数设备并保持游戏质量。

目标受众

目标受众是制作游戏的特定群体。人们认为特定的一群人会从游戏中获得最多的乐趣,或者他们会比其他人更多地玩这个游戏。

每个游戏设计都有其目标受众。目标设备的集合是目标受众的直接结果。例如,如果目标受众是 25 至 40 岁的职业人士,无论游戏规模如何,为 Android 电视创建游戏都没有意义。这是因为这个特定的受众大多会使用移动设备,因为他们没有时间坐在电视机前。因此,针对这个目标受众的目标设备列表应包含移动设备。

我们可以看到同一类别的设备之间存在很大的差异。现在,让我们以 Android 手机为例,因为这是最常用的 Android 类别。我们可以看到市场上有各种 Android 设备。大多数 Android 手机价格相对较低,功能较少。使用这类手机的目标受众大部分属于亚洲或第三世界国家。因此,在为这个目标受众制作游戏时,开发者应考虑最低配置目标。

功能需求

功能需求完全取决于游戏设计。当我们谈论 Android 游戏时,主要关注的是移动和平板平台。大多数 Android 游戏都是为这些设备制作的。

如果我们考虑其他平台,如手表、电视或游戏机,功能集会有所不同。电视提供较大的显示屏,用户控制较少,手表的显示区域有限,配置最低,游戏机具有更好的图形质量和专用控制等。识别所需的硬件设备的功能列表非常重要。

可能存在这样一种情况,游戏中使用了加速计、蓝牙、Wi-Fi 或其他特殊功能,因此所选的硬件平台必须具备这些功能。然而,普通的移动设备和平板设备几乎具有游戏开发者通常会使用的相同功能集。当为某些特定的硬件平台(如游戏机或 VR 设备)制作 Android 游戏时,这种功能依赖变得非常具体。

可移植性的范围

在选择目标硬件设备时,每个游戏开发者都必须考虑游戏的可移植性。游戏变得越可移植,选择目标硬件所需的工作量就越少。

游戏的可移植性始终取决于游戏开发者的愿景。移植可以采用两种不同的方法:平台移植和硬件移植。在这里,我们只关注硬件移植,因为我们已经确定了平台为 Android。

游戏开发者应该专注于以下几点,以增加游戏的可移植性:

  • 创建不同的资产集

  • 设计不同的控制集

  • 查找和列出功能的替代方案

  • 控制内存使用

  • 控制帧率

一个好的便携游戏是所有这些前述要点的平衡组合。大多数情况下,目标硬件是根据其他参数首先选择的,然后开发者才会着手游戏的可移植性。

制作 Android 游戏的最佳实践

制作 Android 游戏并不是一件大事。但是通过正确的方式制作游戏,使游戏在尽可能多的设备上看起来很棒,并且性能良好,这是非常重要的。最佳实践应该关注以下几点:

  • 维护游戏质量

  • 极简用户界面

  • 支持最大分辨率

  • 支持最大设备

  • 后台行为

  • 中断处理

  • 维护电池使用

  • 对多种视觉质量的扩展支持

  • 引入社交网络和多人游戏

让我们在这里简要讨论一下。随着书籍的进展,我们将在后面详细阐述这些内容。

维护游戏质量

市场上有数百万款游戏,每周都有数千款新游戏推出。因此,仅仅制作一个好游戏已经不够了。每个开发者都应该定期维护他们的游戏,以适应其他改进游戏的质量。

开发者应该时刻关注用户的评论和投诉。根据这些反馈,游戏的质量可以得到很大的改善。在游戏上市之前,没有人能够准确预测用户对游戏的反应。因此,在大多数情况下,游戏的设计或其他方面都会经历剧烈的变化,以保持消费者的满意度。

还有一些其他跟踪消费者/玩家行为的方法。有几种工具可以高效地完成这项工作,比如 Google Analytics、Game Analytics、Flurry 等等。除了这些内部集成,用户在商店或博客上的评论也有助于维护游戏的质量。

修复游戏中的错误是提高游戏质量的另一个重要因素。在开发过程中不可能摆脱游戏内的所有错误。应用商店的错误报告工具对于跟踪游戏上市后的重大崩溃和 ANR 非常有用。除此之外,开发者可以使用 Android 错误报告来跟踪真实用户的错误和 bug。Android 在 2.2 版本及以后提供了这个功能。

提高游戏质量的另外两个因素是稳定的游戏玩法和一致的帧率。

极简用户界面

这是 Android 游戏的典型设计实践。许多开发者犯的一个常见错误是设计一个冗长而繁琐的用户界面来引导用户进入游戏。这一部分应该尽可能地简短。玩家应该在第一次体验游戏时尽量少地付出努力。大多数用户会因为繁重的 UI 界面而离开游戏。

从技术上讲,开发者应该注意设备的 UI 选项,比如菜单、返回和主页。这些是 Android 手机和平板电脑平台上最常见的选项。所有这些选项的行为应该在游戏内进行控制,因为用户在玩游戏时可能会意外按到/触摸到它们。此外,应该有一个快速的界面来退出游戏。

基本上,拥有最少的用户界面和较少的屏幕切换可以节省大量时间,这对游戏的游戏时间有直接影响。

支持最大分辨率

这是制作一个好的 Android 游戏的一个非常明显的要点。游戏必须支持尽可能多的分辨率。特别是 Android 在市场上有许多不同的屏幕尺寸可用。

Android 有一系列不同的分辨率集:

  • LDPI(大约 120 dpi)

  • MDPI(大约 160 dpi)

  • HDPI(大约 240 dpi)

  • XHDPI(大约 320 dpi)

  • XXHDI(大约 480 dpi)

  • XXXHDPI(大约 640 dpi)

如果他们不遵循多个分辨率规范,开发者也可以选择作为最后手段的屏幕兼容性选项。然而,建议不要使用这个 Android 的功能,因为它可能会显著降低视觉质量。这个选项在 Android API 版本 11 中默认是禁用的。

支持最大设备

除了不同的屏幕尺寸,Android 还有各种设备配置。大多数开发者只通过屏幕分辨率来过滤设备列表,这是一个不好的做法。Android 游戏开发者应该始终考虑目标设备配置以及分辨率。

在构建他们的应用程序时,开发者应该记住不要对特定的键盘布局、触摸界面或其他交互系统做出假设,除非游戏被限制只能在这些设备上使用。

在内存和性能方面优化应用程序也有助于支持更多设备。开发者不应该将其限制在只支持少数设备。最佳地利用磁盘空间和处理器可以增加支持范围的机会。

一个游戏应用程序构建可以通过一些简单的技巧支持更多设备。在 Android 活动启动时,开发者应该检测硬件信息,并使用它来创建某种规则,通过这些规则可以控制整个游戏的质量和处理速度。

后台行为

在主线程运行时,游戏中的一些任务可能会在后台运行。这些被称为异步任务,主要用于加载大文件或从互联网获取数据。

另一种后台任务称为服务,即使主应用程序线程没有运行,也可以工作。这是一个非常有用的功能,用于与安装了游戏的设备进行通信。

对于任何游戏开发者来说,合理使用这些功能是一个很好的做法。通常大量的数据需要更长时间,但不应该暂停游戏循环。在另一种情况下,异步任务用于游戏与互联网或其他连接进行通信。这个功能有助于保持主线程运行,并提供动态反馈。

后台服务对增加开发者和用户之间的通信很有用。它们可以提供用户活动信息,以改进游戏,并通知用户有关最新更新或信息。

中断处理

中断处理是游戏开发中最棘手的部分之一。正如我们之前讨论过的游戏循环一样,循环会在任何外部中断时暂停或有时终止。在进行中的游戏周期中,中断不应该影响游戏体验。对开发者来说,一个非常常见的问题是游戏在被中断后重新启动。如果 Android 游戏活动在空闲状态下保持很长时间,或者其他活动需要运行,Android 很可能会终止游戏活动。在这些情况下,大多数时候玩家会失去他/她的进度。

定期保存用户进度是一个很好的做法,以避免数据或进度的丢失。但保存数据可能会导致游戏循环出现延迟,并且可能会显著降低帧率。游戏开发者应该确定可以在不影响游戏体验的情况下保存数据的状态。

在多活动应用程序中处理这个问题的方法是检测并暂停/恢复所有正在运行的线程。很多时候,游戏开发者会保持线程运行,因为主要目标只是在中断时正确地暂停/恢复游戏循环。在大多数情况下,所有的后台进程都不会暂停,导致游戏出现异常行为。

维护电池使用

Android 游戏成功的原因之一是功耗效率。Android 硬件平台很可能是移动设备,其电源来源有限。因此,节能应用程序总是首选。

电池的主要部分被渲染和网络连接所消耗。从游戏的角度来看,渲染和连接都是必要的。因此,游戏消耗大量电力的可能性很大。

大多数游戏开发人员都非常注重视觉效果。这会提高图形质量,同时也会增加电池消耗。因此,开发人员始终应更多关注图形资产的技术质量是一个非常好的做法。资产不应该增加处理或渲染,因为开发人员经常使用非优化的资产。

另一个消耗大量电池的过程是后台服务。这些广泛用于与消费者更好地连接或用于一些基于网络的服务。从技术上讲,这个过程频繁地 ping 以保持与所需网络的连接。开发人员可以控制这种频率。避免这种情况的另一种方法是通过使用 Android PackageManager杀死长时间未连接或已从网络断开连接的服务。

在许多情况下,人们会发现一个游戏变得流行,或者用户数量比其他质量更好的游戏更多,仅仅是因为电池消耗较低。

如果开发人员可以确定连接丢失,那么除了连接更改接收器之外,可以使用本机 API 禁用所有接收器。相反,一旦开发人员连接,他们可以停止监听连接更改,并在执行更新之前立即检查应用程序是否在线;然后可以重新安排定期更新警报。

开发人员可以使用相同的技术来延迟需要更高带宽才能完成的下载,只需启用广播接收器,监听连接更改,并在应用程序连接到 Wi-Fi 后才启动下载。这可以显著减少电池使用。

对多种视觉质量的扩展支持

这一部分实际上是从支持多种分辨率开始的。我们已经讨论了具有不同 dpi 的多尺寸屏幕。以下列表是 Android 设备遵循的另一个标准:

  • QVGA(低 PPI)

  • WQVGA(中低 PPI)

  • HVGA(中等 PPI)

  • WVGA(中高 PPI)

  • SVGA(高 PPI)

  • VGA(非常高 PPI)

使用这种标准创建图形始终是有益的,以实现跨设备的最佳视觉质量。这种表示法主要取决于屏幕大小,而不是分辨率。在 Android 设备上,相同的分辨率在不同的屏幕尺寸上运行是非常常见的。专门为目标设备优化的资产将有助于提高视觉质量。

引入社交网络和多人游戏

游戏行业的风格和标准正在迅速变化。现在游戏被用于社交连接,即在单个平台上连接多个真实用户。非常谨慎地使用这种社交元素可以显著增加用户群和留存率。

在许多游戏中,有可能让多个用户一起体验相同的游戏状态,并通过实时互动来改善游戏玩法。一些棋类游戏,如国际象棋、飞行棋和蛇梯棋,就是这种可能的例子。除此之外,一些实时在线多人游戏也正处于巅峰状态。

谷歌通过 Google Play 服务拥有自己的多人游戏功能。除了流行的回合制和实时多人游戏支持外,谷歌还通过 Wi-Fi 引入了一项功能,可以将靠近的玩家连接到单个平台上,称为Google Nearby。还有许多其他第三方平台支持多人游戏。

总结

制作安卓游戏并不困难,但要制作一款成功的游戏就不同了。从技术角度来看,一款成功的游戏必须提供流畅的游戏体验,为用户提供出色、迅速的游戏体验。出色的视觉质量和更好的图形总是吸引用户和其他潜在玩家,而较少的错误可以消除用户在游戏过程中的烦恼,游戏可以按计划进行。广泛的设备支持可以增加用户数量和游戏次数,资源的最佳利用确保了最小可能的应用程序包大小,最后,开发者与用户之间良好的关系,通过出色的沟通技巧,可以消除用户的一些疑虑和困惑。

我们已经简要介绍了所有这些要点,以便让您了解如何制作一款成功的安卓游戏。制作安卓游戏与制作任何软件并无二致。然而,为了实现游戏的乐趣元素,游戏必须遵循一些实践。您将在本书的后面详细了解如何制作高效的安卓游戏。您还将最终了解安卓平台的游戏开发的几个方面。您将认识并了解由各种制造商制造的当前可用安卓设备的现状。有许多类型的设备,我们将一一了解。

我们将尝试探索一种更好、更高效的安卓游戏开发方法,针对不同硬件平台有许多开发流程、风格和标准。我们将进一步深入,针对 2D、3D 和虚拟现实游戏制定特定的开发标准。我们还将讨论原生开发,包括着色器和各种优化技术。

最后,我们将探讨各种使游戏成功的方法,从盈利的角度来看是足够好的。由于开发者必须了解每个用户的行为以使游戏更好,您可以通过本书了解从用户那里收集到的数据的力量。

第二章:不同 Android 平台的介绍

第一款商用发布的 Android 设备是HTC Dream。在 2008 年,这款手机推出了一个新的基于 Linux 的操作系统 Android。从那时起,成千上万的制造商开始为他们的设备使用 Android。起初,Android 在移动操作系统中变得流行,如 Symbian、Java ME、Blackberry 和 iOS。新一代技术对于一个新的、轻量级、用户友好和价格实惠的操作系统有了需求。Android 满足了这些要求,并比 Blackberry 和其他竞争操作系统更快地获得了动力。

根据最新的市场研究,在 2016 年第一季度,Android 占据了 76%的市场份额,这本身就解释了它的成功。随着时间的推移,Android 已经将其领土从手机扩展到其他有用的硬件平台,如平板电脑、电视、手表、游戏机等等。

在本章中,我们将从游戏开发的角度探索这些平台。让我们快速看一下我们将要涵盖的主题:

  • 探索 Android 手机

  • 探索 Android 平板

  • 探索 Android 电视

  • 探索 Android 游戏机

  • 探索 Android 手表

  • Android 手机开发见解

  • Android 平板开发见解

  • Android 电视和 STB 的开发见解

  • Android 游戏机的开发见解

  • Android 手表的开发见解

  • 每个平台都有自己的特色

  • 为同一款游戏进行跨平台开发

  • 设计前所需的限制测量

我们将尝试了解所有平台及其详细信息,以便开发游戏。现代世界已经见证了游戏现在不仅仅局限于 PC 或游戏机。它们已经成为几乎所有事物的一部分。因此,对于 Android 游戏开发者来说,对可能对游戏有用的所有可能的硬件有一个良好的了解是非常重要的,这也为一些其他可能性打开了机会。

探索 Android 手机

Android 手机设备对于游戏开发者来说是最重要的设备。移动技术在过去的十年里经历了巨大的革命,从黑白像素手机到现代智能手机。目前,与最接近的竞争对手 iPhone 相比,Android 手机设备在市场上领先了很大一部分。

最初的 Android 游戏在 Android 1.6 发布后获得了动力,随后是 Android 2.3。即使在今天,仍有许多设备在运行 Android 2.3。这就是为什么许多流行的跨平台游戏引擎支持 Android 2.3。

曾经有一段时间,Android 的最低要求是 32MB 的 RAM,32MB 的磁盘空间和 200MHz 的处理器。如果我们看一下当前的设备规格,就会发现有了很大的变化。如今,Android 手机设备平均拥有 1GB 的 RAM,1GHz 的处理器和 4GB 的磁盘空间。大多数设备都有多核处理器。然而,这种提升并没有简化游戏开发者的生活;相反,它增加了更多的复杂性。

让我们来看一下配置较低的低预算 Android 设备的规格。以下示例表格显示了 Micromax Bolt A24 的配置:

处理器 Cortex A5
速度 1GHz
RAM 256MB
闪存 512MB
屏幕模式 NA
屏幕分辨率 480x640
屏幕尺寸 2.8 英寸
Android 版本 2.3(姜饼)

以下是它的外观(图片来源:www.androided.in/wp-content/uploads/2014/02/Micromax-BoltA24.jpg):

探索 Android 手机

现在让我们来看一下配置非常高的高预算 Android 设备。以下表格显示了三星 Galaxy S6 的配置:

处理器 Cortex A57
速度 2.1GHz 四核
RAM 3GB
闪存内存 128 GB
屏幕模式 NA
屏幕分辨率 1440x2560
屏幕尺寸 5.1 英寸
安卓版本 5.0.2(棒棒糖)

它看起来是这样的(图片来源:talishop.ru/data/big/eew.jpg):

探索安卓手机

每个安卓游戏开发者都应该清楚地意识到,单个游戏构建无法在所有配置上实现最佳性能。很显然,如果游戏在 Micromax Bolt A24 设备上运行良好,那么它肯定会在三星 Galaxy S6 上表现不佳。所谓的“表现不佳”,是指三星 Galaxy S6 上的游戏质量远低于预期。因此,对游戏可移植性有一些想法是非常好的。我们将在后面详细讨论这个问题。

以下图表显示了自 2012 年以来手机市场份额:

探索安卓手机

正如你所看到的,大部分市场被安卓占据,而 iOS 则落后很多。

其他移动操作系统如 Windows Phone、BlackBerry、Java、Symbian、Bada、QT 等都远远落后。因此,可以预测未来的手机市场将由安卓和 iOS 主导。

安卓手机市场日益扩大。设计和开发变得更加艰难、棘手,市场竞争也更加激烈。每天都有数以万计的新游戏在诸如 Google Play 商店、亚马逊应用商店、一些特定职业商店以及许多个人在线网站上推出。

我们之前已经讨论过游戏的目标受众。在探索安卓手机设备时,我们需要了解用户群体。在大多数情况下,用户类别是通过设备配置或价格群体来识别的。

探索安卓平板电脑

安卓平板与安卓手机非常相似。安卓手机和安卓平板的主要规格区别在于物理尺寸和屏幕分辨率。一般来说,安卓平板的 PPI 比手机低。然而,并没有硬性规定或任何特定系统来衡量这一点。

运行安卓的最低要求是 200 MHz 处理器,32 MB RAM 和 32 MB 磁盘空间。安卓需要 ARMv5 或更高的处理器,尽管安卓 4 需要 ARMv7。以前,起步年龄的平板几乎都是最低硬件系统。

平板电脑起源于平板电脑的概念;它们由触摸屏显示器、摄像头、麦克风、扬声器和一些物理按钮组成。平板电脑通常比手机或 PDA 大。

最低配置的安卓平板是 Coby Kyros MID7047,如图所示(图片来源:www.evisionstore.com/catalogo/coby_mid7012-4g.jpg):

探索安卓平板电脑

以下表格显示了它的规格:

处理器 Cortex A5
速度 1 GHz
RAM 512 MB
闪存内存 4 GB
屏幕模式 WVGA
屏幕分辨率 800x480
屏幕尺寸 7.00 英寸
安卓版本 4.0(ICS)

2013 年,安卓平板销量约为 62%,销量超过 1.21 亿台设备。再加上亚马逊 Kindle Fire 销售了约 700 万台设备。

随着安卓平板的销量增加,配置也在不断提升。让我们来看看索尼在 2015 年 6 月发布的一款平板配置。这款型号被称为索尼 Xperia Z4(图片来源:tecneticoc1.wpengine.com/wp-content/uploads/2015/03/z4tab.png):

探索安卓平板电脑

以下表格显示了它的规格:

处理器 骁龙 810
速度 2 GHz 八核
RAM 3 GB
闪存 32 GB
屏幕分辨率 2560x1600
屏幕尺寸 10.1 英寸
Android 版本 Android 5.0(棒棒糖)

我们可以清楚地观察到这两款平板电脑之间的巨大配置差异。有趣的是,即使在今天,这两种配置仍然共存,并且人们在购买它们。

Android 平板电脑通常比智能手机更大更重。它们还具有更好的电池容量。因此,平板电脑的游戏应该是节能的。然而,现代平板电脑与智能手机并没有太大区别。智能手机的尺寸越来越大,智能手机的功能也被添加到平板电脑上。现在,人们甚至可以将平板电脑用作手机。

探索 Android 电视和机顶盒

在 Android 手机和平板电脑取得成功之后,Android 开始向其他硬件平台扩张。电视成为下一个目标,因为智能电视的概念已经存在市场上。因此,通过 Android,电视机成为了互动的。Google 推出了一些额外的配件,以提高 Android 电视的用户体验。

首款面向消费者推出的 Android 电视设备是 2014 年 11 月 3 日的 Nexus Player。其规格如下:

  • 英特尔 Atom Z3560 1.8 GHz 四核处理器

  • 1 GB RAM

  • 8 GB 闪存

  • Android 5.1.1 棒棒糖

这就是它的样子(图片来源:1.bp.blogspot.com/-H1rp1fboU6g/VX2Huu6ClyI/AAAAAAAA3hA/5te4NZ65Tgg/s1600/nexus_player.jpg):

探索 Android 电视和机顶盒

这是第一种 Android 电视设备,与 Google TV 非常相似,也可以与 Apple TV 相媲美。

除了 Nexus Player,还有一些其他支持 Android 的机顶盒设备:

  • Freebox Mini 4K:这是一款 4K 能力的电视机顶盒,最初由一家法国 ISP 与光纤调制解调器一起提供。

  • Forge TV:这是一款具有高性能硬件规格的电视/微型游戏机,由 Razer 于 2015 年 1 月 6 日宣布。它配备了高通骁龙 805 处理器、2 GB RAM 和 16 GB 闪存。

  • Shield Android TV:这款设备是由 NVIDIA 于 2015 年 3 月 3 日宣布的。它声称能与第八代游戏机展开激烈竞争。这套硬件配备了一款 NVIDIA 品牌的游戏手柄。

  • OgleBox Android TV:这款设备主要在澳大利亚提供区域性内容。它于 2015 年 3 月宣布。

  • LG UPlus Android TV:韩国电信公司 LG UPlus 推出了一款 Android 电视,搭载 U+ tvG 4K UHD 和 U+ tvG Woofer IPTV 机顶盒。

  • Arcadyan BouygtelTV:2015 年 6 月,法国电信公司 Bouygues Telecom 宣布推出一款名为“Miami”的集成式基于 Android TV 的机顶盒。

除了这些基于机顶盒/游戏机的 Android 设备外,许多电视制造商也在瞄准推出 Android 电视。索尼、夏普、飞利浦、LG、三星等公司都在向 Android 迁移。飞利浦宣布他们未来 80%的电视型号将采用 Android(图片来源:www.techdigest.tv/wp-content/uploads/2014/07/kogan-smart-tv-may-2013.jpg):

探索 Android 电视和机顶盒

让我们来看一个示例智能电视的规格:

  • 型号:VU 32K160M LED 电视

  • 操作系统:Android v4.4 奇巧猫

  • 处理器:2.0 GHz 八核 GPU,搭载 Amlogic 四核 S802 ARM Cortex 处理器 CPU

  • 显示:32 英寸 LED 屏幕(1366x768 像素),采用消色技术和全色优化器,为图像、视频、游戏等提供世界级的观看体验

  • 设计:该型号采用 A+级纯棱镜面板覆盖,使图片更加清晰和详细。而且它比普通智能电视型号更薄。其尺寸为 29 英寸 x 19 英寸 x 7 英寸,重量为 7.3 公斤。

  • RAM 和存储: Vu 32K160M 电视有 2 GB DDR3 RAM 和 8 GB NAND 闪存存储。

  • 视频: 它可以播放 @60fps 的 1080p 视频,并支持各种文件格式。

  • 连接: Vu 电视支持蓝牙 v4.0、Wi-Fi 和以太网。它让用户可以从电视屏幕上浏览他们喜爱的网站和查看邮件。它有三个 USB 端口和两个 HDMI 端口。

这个规格足够吸引游戏开发人员积极参与他/她的下一个游戏。

谷歌还发布了一个名为 ADT-1 的安卓电视开发套件。这个硬件开发套件是在谷歌 I/O 期间向应用程序开发人员提供的(图片来源:chezasite.com/media/2014-post-icon/adt-1-android-tv-reference-design-1920x1080.jpg):

探索安卓电视和机顶盒

早期的谷歌电视设备因性能不佳的规格而受到批评。幸运的是,谷歌已经试图解决这个问题,因为他们以一些相当令人印象深刻的规格强势出击:

  • Tegra 4 芯片组

  • 2 GB RAM

  • 16 GB 内部存储

  • 2×2 MIMO 双通道 Wi-Fi

  • 蓝牙 4.0

  • 以太网端口

  • HDMI 端口

  • 安卓 L 开发者预览

安卓电视在开箱即用时已完全解锁,因此谷歌邀请开发人员为这个平台开发任何有助于它的东西。现在还为时过早来判断这种开放性会带来什么结果,但开发人员应该很快就会探索并为这个设备开发。

探索安卓游戏机

连接到电视的一个小型适配器和一个控制器设备,一起被称为游戏机套装。安卓是一个便宜、低成本的操作系统,可以轻松地在任何移动硬件平台上使用。

首款基于安卓的游戏机之一是 OUYA(图片来源:cdn2.pu.nl/media/misc/ouya_wall_ins.jpg):

探索安卓游戏机

这些游戏机被称为微型游戏机。几年前,这类游戏机的规格是这样的:

  • 型号: OUYA

  • 处理器: ARM Cortex A9

  • 速度: 1.7 GHz 四核

  • 系统芯片: NVIDIA Tegra 3

  • 闪存: 8 GB

  • RAM: 1 GB DDR3

  • 显示: 高清(720p)或全高清(1080p)

  • 图形处理器: NVIDIA GeForce ULP GPU

  • 安卓版本: 安卓 4.1 果冻豆

  • 连接: Wi-Fi、蓝牙和局域网

现在让我们来看一下现代安卓游戏机的规格:

  • 型号: NVIDIA Shield

  • 处理器: ARM Cortex A57 + A53(64 位)

  • 速度: 1.9 GHz 四核 + 1000 MHz 四核

  • 系统芯片: NVIDIA Tegra X1

  • 闪存: 500 GB 硬盘

  • RAM: 3 GB

  • 连接: Wi-Fi、蓝牙、局域网、USB 和 HDMI

  • 显示: 支持 4K 分辨率

这是 NVIDIA Shield 安卓游戏机 (cdn0.vox-cdn.com/thumbor/72vPz7fqWT7ButeiG17cW_jjP2Y=/0x0:1920x1080/1600x900/cdn0.vox-cdn.com/uploads/chorus_image/image/45812214/shield-hero-image.0.0.jpg)

探索安卓游戏机

现在让我们来看另一款现代安卓游戏机,名为 Razor Forge TV(图片来源:android.hu/img/2015/04/gallery-04.jpg):

探索安卓游戏机

其规格如下:

  • 型号: Razor Forge TV

  • 处理器: 高通骁龙 805

  • 速度: 2.5 GHz 四核

  • GPU: Adreno 420

  • 闪存: 16 GB

  • RAM: 2 GB

  • 连接: Wi-Fi、蓝牙、局域网、USB 和 HDMI

  • 安卓版本: 5.0 棒棒糖

游戏机是专门用于游戏的设备。然而,如今,游戏机可以用于各种用途,但主要目标仍然是一样的。

从之前的规格示例中,我们可以了解到安卓游戏机的发展。开发者为游戏机工作在特定目标设备上。即使游戏是可携带的,游戏机的质量也必须得到保证。

在最近的市场研究中,有人说 PlayStation 4、Xbox One 和任天堂 Wii U 将成为硬核游戏机玩家的主要平台。然而,亚马逊、谷歌和其他公司推出的安卓游戏机预计将以更快的速度增长,并为休闲到中核心玩家提供了一个从沙发上玩游戏的经济实惠方式。很明显,随着时间的推移,更广泛的游戏机玩家群体可能会考虑安卓。

这开启了安卓游戏开发的新时代。游戏机游戏与典型的移动游戏不同,而大多数安卓游戏开发者都在其中。然而,随着自 2014 年以来安卓游戏机数量的增加,越来越多的开发者对此产生了兴趣。

除了之前讨论过的游戏机之外,还有一些其他的,比如以下的:

这款游戏机引入的一个新功能是,该设备能够通过一个名为 Looking Glass 的可视化工具运行 iOS 游戏。在这个设备进入市场之前,过于兴奋地期待它的表现可能不太明智。但如果真的能实现,结果可能会令人惊讶。

探索安卓游戏机

安卓游戏机的发展可能是游戏的未来;然而,其他顶级游戏机的存在不会灭绝。用户群体每天都在增加,平台上的游戏数量也在增加。

作为安卓游戏开发者,一个人不应该局限于传统的游戏平台,比如智能手机和平板电脑。时代在迅速变化。开发者应该保持自己的更新。

探索安卓手表

在智能技术时代,人们预期每种可能的小工具都能更智能地工作。手表也不例外。手表的类型从模拟变成了数字,现在又变成了纯粹的计算机智能手表。作为一个非常灵活的开源操作系统,安卓是智能手表最受欢迎的选择之一(图片来源:photos.appleinsidercdn.com/bigger-wimm-130830.jpg):

探索安卓手表

从早期版本开始,安卓手表就有能力玩游戏,因此进入了游戏开发领域。先进的设备和小型计算机一样出色。它们包括互联网、传感器、摄像头、蓝牙、Wi-Fi、扬声器、卡槽等许多功能。

与其他计算机一样,智能手表可以从内部或外部传感器收集信息。它可以控制或从其他仪器或计算机中检索数据。它可以支持蓝牙、Wi-Fi 和 GPS 等无线技术。然而,"手表电脑"也可能只是作为远程系统的前端,就像使用蜂窝技术或 Wi-Fi 的手表一样。

Android Wear 于 2014 年 3 月 18 日首次由谷歌宣布。与此同时,许多电子设备制造商也宣布成为 Android Wear 的合作伙伴。这些公司包括三星、摩托罗拉、LG、HTC、华硕等。

2014 年 12 月,操作系统升级到 Android 5.0(棒棒糖)。我们可以看到在这个时期周围有一系列的 Android 手表发布。LG 开始发货 LG G Watch,摩托罗拉宣布 Moto 360,华硕发布了 ZenWatch。

最新的智能手表提供了一系列吸引人的功能。用户可以通过手机语音获取方向,选择包括自行车在内的交通方式,并开始旅程。在旅行时,手表会显示方向,并实际上使用触觉交互来指示转弯,帮助佩戴者在不看手机甚至手表屏幕的情况下旅行。用户可以使用他们的 Android Wear 手表来控制手机。可以请求音乐(例如,"OK Google,播放滚石乐队")。屏幕上会显示一个播放控制卡,包括音量、跳过和媒体图像,音乐可以从手腕上控制,用户可以自由移动。

让我们来看一下 Android 可穿戴设备的一些规格。首先是 LG G Watch:

处理器 高通骁龙 400
闪存 4 GB
RAM 512 MB
电池 400 毫安时
连接性 蓝牙 4.0
传感器 陀螺仪、加速度计、指南针
Android 版本 4.3
显示 1.6 英寸
分辨率 280x280

这种配置在市场上已经不复存在了,但用户还在。因此,在为 Android 可穿戴设备开发游戏时,开发者也应该考虑这些配置。

Android 可穿戴设备制造商也在大规模升级他们的设备硬件。现在让我们通过比较两个版本来看一下设备的演变:

索尼智能手表 2 索尼智能手表 3
处理器 ARM Cortex M4 ARM A7 四核
速度 180 MHz 1.2 GHz
闪存 256 MB 4 GB
RAM 64 MB 512 MB
电池 225 毫安时 420 毫安时
连接性 蓝牙 3.0 蓝牙 4.0 和 Wi-Fi 准备
传感器 陀螺仪、加速度计、指南针、接近、环境光和 IP57 防尘防水 陀螺仪、加速度计、指南针、接近、环境光和 IP68 防尘防水
Android 版本 4.0 4.3+
显示 1.6 英寸 1.6 英寸
分辨率 220x176 320x320

我们可以看到在技术和硬件升级方面的巨大提升。这就是市场的增长,也是应用程序的增长。

对 Android 手机的开发见解

正如我们之前讨论过的,对于 Android 平台上的任何游戏开发者来说,主要的开发目标是 Android 手机。我们也注意到了 Android 手机的各种技术规格。当游戏开发者将目标最大化地放在这个平台上时,他们必须注意设备类别。

大多数 Android 设备都支持常见的触摸界面、物理 Home 按钮、物理锁定按钮、返回按钮以及音量上下键进行用户交互。除此之外,加速度计也可以是用户界面的一个良好媒介。

游戏大多是视觉化的,因此游戏开发者应该始终关注设备的图形性能。最新的 Android 手机中有一个独立的图形处理器,但质量有所不同。

视觉卓越不仅仅取决于 GPU,显示屏质量也非常重要。低 PPI 屏幕无法提供高质量的显示。第一代 Android G1 手机的屏幕分辨率为 240x320,属于 LDPI 分辨率类别。因此,无论开发者如何努力,游戏的视觉质量都无法达到卓越。幸运的是,随着时间的推移,设备制造商在提高视觉质量和性能方面付出了很多努力。但这一特性是以电池寿命为代价的。它获得的质量越高,消耗的电池就越多。

以前,很少有 Android 手机(例如 Android HTC Dream G1)有物理 QWERTY 键盘。这使得从 Symbian 或 BlackBerry 移植游戏控制系统到 Android 变得更容易(图片来源:s.androidinsider.ru/2015/02/htc-dream.@750.jpg):

Android 手机的开发见解

如今,Android 游戏的控制系统完全改变了,以适应市场上其他智能手机游戏的控制风格。

在当前情况下,移动电池的平均容量约为 2750 毫安。一些制造商通过降低显示质量来提供更高的电池容量。由于移动设备的物理尺寸和重量限制,不可能将电池容量增加到一定限度以上。

以最大化设备为目标总是一个好主意,只要在性能和游戏体验之间保持平衡。

设备市场对各种设备都是开放的。虽然旧配置不再生产,但这些设备还没有过时。这就是为什么开发者对他们的游戏有最低要求。

智能手机是任何游戏开发组织的主要目标。用户群不断增加,Android 的升级帮助这个平台更快地增长。曾经有一段时间,人们认为 BlackBerry 是唯一的智能手机。但当前市场表明,时代已经改变,开发者也改变了。

对于 Android 手机游戏开发,开发者应该牢记以下限制和特点:

  • 小的显示区域

  • 广泛的分辨率和像素密度

  • 全屏多点触控界面

  • 陀螺仪、加速计、指南针、环境光等传感器支持

  • 广泛的 RAM

  • 各种处理器和性能

  • 电池寿命

  • 更多的中断机会

Android 手机现在是更有利可图的平台之一。在市场份额方面,没有其他移动操作系统能与 Android 竞争。因此,开发者总是投身于 Android。这个平台成功的一些经济原因如下:

  • 拥有庞大的用户群,吸引广告商作为广告平台

  • 通过成熟的商店进行简单的货币交易

  • 轻松进行游戏和应用的跨推广

Android 平板的开发见解

Android 游戏开发在 Android 平板出现之前主要局限于智能手机。在平板上玩游戏更有趣。平板游戏因以下原因在短时间内变得流行起来:

  • 更大的屏幕:尽管分辨率相同的更大屏幕会影响视觉质量,但它提供了更大的可见性。更大的可见性增强了艺术资产以展现其细节,这对于小屏幕智能手机来说并不总是可能的。

  • 更大的物理尺寸:更大的物理尺寸迫使玩家用双手玩游戏,这样可以更好地控制设备和控制。

  • 更大的空间/可玩区域:更大的可玩区域可以提供更多的控制空间。这意味着玩家在使用触摸控制系统时不需要那么准确,因此可以更专注于游戏本身。因此,它增强了游戏体验。

  • 少限制:持续玩游戏会严重消耗电池。在手机的情况下,主要目标是保持与网络的连接。因此,保持设备活跃非常重要。但平板没有特定的目标。它是一个多用途的实用设备。其中一个目的可以是玩游戏。因此,除了游戏之外,没有必要为其他工作节省电源。

  • 少打扰:我们都知道在进行中的工作中任何打扰都会很烦人。游戏体验也是如此。任何打扰都会导致游戏的主要暂停,大多数时候,玩家会在那个时候退出。在平板上,自动打扰的机会比手动或物理打扰要少;这意味着在平板上玩游戏时会少一些烦恼。

对于安卓平板游戏开发,开发者应该牢记以下限制和特点:

  • 大屏幕显示区域

  • 广泛的分辨率范围,相对较低的像素密度

  • 全屏多点触控界面

  • 传感器支持陀螺仪、加速计、指南针、环境光等

  • 广泛的 RAM

  • 广泛的处理器和性能

  • 打扰的机会较少

平板电脑起源于一个小型便携计算设备的概念,可以成为智能手机和个人电脑/笔记本电脑之间的桥梁。

更大的屏幕总是帮助用户更轻松地与游戏互动。游戏设计师有更多的空间可以利用。然而,这稍微增加了开发者的头疼,因为视觉质量必须在相同的硬件限制内保持不变。

以前,大多数平板使用移动处理器,但现在平板制造商正在使用笔记本处理器。英特尔酷睿就是一个例子。设备中使用的处理器能力越强,它能提供的质量就越好。

曾经有一段时间,安卓游戏首先面向手机,然后再移植到平板上。但现在情况已经改变。现在智能手机游戏的开发和平板游戏之间的界限非常微弱。大多数情况下,同一个 APK 可以支持手机和平板,几乎具有相同的质量和性能。不再专门为平板移植。

关于安卓电视和机顶盒的开发见解

首先,安卓电视游戏开发需要专注于两个特定的事情:

  • 大型共享显示屏

  • 横向分辨率较低的 dpi

对于玩家来说,大屏幕总是一个加分项,但这增加了图形设计师根据硬件限制进行优化的工作量。同时,显示屏可以与多个用户共享,因此开发者必须确保所有用户的操作始终可以与显示屏同步。

与其他安卓设备相比,电视屏幕要大得多。从 5 英寸到 50 英寸再到 150 英寸的屏幕可以暴露出图形质量差的问题。因此,在开发安卓电视游戏时需要考虑以下几点:

  • 检查游戏的纹理——在安卓电视上拉伸时,低分辨率纹理通常看起来很差

  • 在电视上,3D 模型可能会有锯齿状的曲线,因为多边形太少

  • 如果发射器、图案或颜色太少,粒子效果可能需要重新调整以适应电视的大屏幕

  • 在小屏幕的安卓设备上通常不需要抗锯齿,但对于电视来说,它会对视觉产生明显的影响。

现在下一个挑战是输入控制系统。游戏可以有多个控制器。电视可以直接由其他安卓设备或遥控器控制。

然而,任何安卓游戏机或机顶盒也可以使用。在这种情况下,游戏控制器或 D-pad 控制对游戏更有用。

要使用控制器或 D-pad,游戏开发人员应该非常具体地使用适当的控制按钮来实现每个功能。当多个玩家一起玩游戏时,每个人都有自己的控制器,重要的是要映射每个玩家-控制器对。

AndroidManifest.xml文件的application标签下指定游戏是一个可选的优势,如下所示:

<application
<!-- other declarations and tags -->
android:isGame="true"
<!-- other declarations and tags -->
>

这将有助于将游戏与其他常规应用程序区分开,并将其显示在 Android TV 主页的游戏类别下。

根据要求,还可以进行其他声明。

要声明对游戏控制器的支持,请使用以下代码:

< uses-feature android:name="android.hardware.gamepad" android:required="true"/>

开发人员必须在AndroidManifest.xml文件中包含“touchscreen required false”声明,因为 Play Store 用于过滤。如果缺少这一声明,Play Store 在搜索结果中不会向 Android TV 用户显示应用程序:

< uses-feature android:name="android.hardware.touchscreen" android:required="false"/>

对于所有 Android TV 游戏开发人员来说,指定 Android 的非必需功能以摆脱额外的库麻烦是非常好的做法。例如,电视没有加速计或重力传感器,因此将它们标记为非必需是一个很好的开发实践。可以按照以下方式进行:

<manifest ...>
  <application ...>
    ...
    <!-- Requiring the camera removes this listing from Android TV search results -->
    <uses-feature android:name="android.hardware.camera" 
      android:required="true" />

    <!-- Making accelerometer optional has no impact on Android TV filtering -->
    <uses-feature android:name="android.hardware.sensor.accelerometer" android:required="false" /> 
  </application>
</manifest>

UI 和游戏设计

每个游戏 UI 都必须遵循支持控制方案的设计,也就是说,UI 必须完全支持输入控制器和元素,并且屏幕必须相应地进行设计。这是移动/平板游戏开发和 Android TV 游戏开发之间的主要区别。

由于我们正在讨论 Android TV 上的开发,开发人员必须考虑在离沙发或豆袋 4-10 英尺的距离上玩 Android TV 游戏的感觉。以下是开发人员应该查找的检查清单:

  • 所有文本都应该清晰可读

  • UI 按钮和其他元素应该与整体布局相匹配,而不会影响整个屏幕的可读性和可见性

  • 在玩游戏时,控制器必须控制所有可能的任务,因为没有人愿意经常起来控制电视

过扫描

与手机和平板电脑屏幕不同,电视屏幕可能会在屏幕边缘失去一些空间以进行过扫描。尽管现在许多电视使用固定像素技术如 LCD,但许多品牌仍然会失去边缘细节。请确保在电视屏幕的外部留出一个区域,不要放置重要的 UI 和游戏元素。一个很好的经验法则是在完全空白的空间周围留出 5-10%的边距,并在绘制重要元素之前留出 10-20%的边距。

如果开发人员在其 UI 中使用标准的 Android 组件,则可以使用 Android Jelly Bean 中提供的内置过扫描支持。如果 UI 是自定义的 OpenGL 或 OpenGL ES 代码,或者使用游戏引擎的 UI 系统,则开发人员必须在 Android TV 界面设计中考虑过扫描。

Android 游戏机的开发见解

今天的现代移动设备完全有能力运行质量适中的游戏,处理能力有限,可与 PC 和游戏机相媲美。预计到 2015-2016 年,移动游戏的收入将超过游戏机和 PC。现在的问题是,“游戏机市场会生存下去吗?”答案是

游戏机专门设计和配置,以提供最佳的游戏体验,而智能手机则设计用于更好的通信和网络连接,计算能力有限。作为游戏机操作系统的 Android 已经被证明是成功的。

Android 手机和 Android 游戏机的配置几乎没有太大区别。处理器、内存容量和其他一些变化可以看到,但最重要的区别是输入系统。手机、平板电脑、可穿戴设备都有触摸界面,而游戏机使用典型的游戏控制器。

从开发的角度来看,Android 游戏机位于手机和 PC 之间。因此,除了设计外,控制和硬件的使用是主要的两个参数。

我们已经讨论了市场上可用的各种类型的游戏机。作为开发平台,它并没有太多的独特性。

Android 手表开发见解

可穿戴游戏直接在可穿戴设备上运行,使开发人员可以访问低级硬件,如传感器、活动、服务等,直接在可穿戴设备上。当开发人员想要发布到 Google Play 商店时,还需要一个包含可穿戴应用程序的手持游戏。可穿戴设备不支持 Google Play 商店,因此用户下载手持游戏,该游戏会自动将可穿戴游戏推送到可穿戴设备上。手持游戏还可用于进行大量处理、网络操作或其他工作,并将结果发送到可穿戴设备。

要在 Android 可穿戴设备上开发游戏,需要遵循一些技术步骤。这不是一般的 Android 游戏开发:

  • Android SDK 工具必须更新到 23.0 或更高版本

  • SDK 中的 Android 平台支持必须更新到 Android 4.4.2 (API 20)或更高版本

  • 开发需要一个 Android 可穿戴设备或模拟器

创建和设置可穿戴应用程序

可以按以下步骤完成:

在 Android Studio 中选择新项目。在配置项目窗口中,输入应用程序名称和包名称。在表单因素窗口中,执行以下步骤:

  1. 选择手机和平板电脑,在最低 SDK下选择API 9: Android 2.3 (姜饼)

  2. 选择Wear,在最低 SDK下选择API 20: Android 4.4 (KitKat Wear)

  3. 在第一个添加活动窗口中,为mobile添加一个空白活动。

  4. 在第二个添加活动窗口中,为wear添加一个空白活动。

当向导完成时,Android Studio 将创建一个包含两个模块mobilewear的新项目。开发人员现在有了一个用于手持设备和可穿戴设备应用程序的项目,可以在其中创建活动、服务和自定义布局。手持设备应用程序完成大部分繁重的工作,如网络通信、密集处理或需要长时间用户交互的任务。当应用程序完成这些操作时,应用程序应通过通知或将数据同步和发送到可穿戴设备来通知可穿戴设备的结果。

注意

wear模块还包含一个使用WatchViewStubHello World活动。这个类根据设备的屏幕是圆形还是方形来填充布局。WatchViewStub类是可穿戴支持库提供的 UI 小部件之一。

在项目中包含正确的库

Android 应用/游戏有很多库支持。每个开发人员都需要确定他们的正确需求,以包含正确的库。

以下是一些在可穿戴设备上进行游戏开发的有用库的列表:

  • 通知:Android v4 支持库(或 v13,包括 v4)包含了扩展手持设备现有通知以支持可穿戴设备的 API。对于仅出现在可穿戴设备上的通知(即由在可穿戴设备上运行的应用发出的通知),开发人员可以在可穿戴设备上使用标准框架 API(API 级别 20),并在游戏或应用程序的mobile模块中删除支持库依赖。

  • 可穿戴数据层:要使用可穿戴数据层 API 在可穿戴设备和手持设备之间同步和发送数据,开发人员需要最新版本的 Google Play 服务。如果开发人员不使用这些 API,从两个模块中删除依赖关系。

  • 可穿戴 UI 支持库:这是一个非官方库,包括为可穿戴设备设计的 UI 小部件。安卓平台鼓励开发者在应用程序中使用它们,因为它们体现了最佳实践,但它们随时可能会发生变化。但是,如果库被更新,应用程序不会崩溃,因为它们被编译到项目中。要从更新的库中获得新功能,开发者只需静态链接新版本,并相应地更新应用程序。这个库只适用于开发者创建可穿戴应用程序。

与安卓版本的硬件兼容性问题

现在让我们再次看一下绝对最低的硬件要求,因为我们已经知道安卓不兼容 ARM v4 处理器,而安卓 4.0+需要 ARM v7 或更高版本。安卓可穿戴设备运行在安卓 4.4 或更高版本。因此,开发者必须支持从 ARM v7 开始。

平台特定特性

到目前为止,我们已经讨论了所有安卓硬件平台。每个平台在配置、大小、形状、实用性和功能方面都有自己的特点。

现在让我们总结一下在开发相同游戏时应考虑的特定平台要点。

安卓手机

这种类型的安卓硬件平台是全球最著名和广泛使用的设备。典型的安卓手机特定功能包括:

  • 小屏幕

  • 高 dpi 显示

  • 广泛的硬件配置范围

  • 全触摸屏

  • 最大传感器支持

  • 多用途

  • 最大用户群

安卓平板电脑

这种类型的安卓硬件平台是全球第二大最著名和广泛使用的设备,具有略微不同的实用功能。典型的安卓平板电脑特定功能包括:

  • 相对较大的屏幕

  • 低 dpi 显示

  • 全触摸屏

  • 特定用途设备

安卓电视和机顶盒

这种类型的安卓硬件平台正在迅速传播,作为比普通电视更受欢迎的智能电视,具有更多功能和能力。典型的安卓电视和机顶盒特定功能包括:

  • 最大的显示单元

  • 无触摸界面

  • D-pad 或基于控制器的输入系统

  • 固定的横向方向

  • 有限的硬件支持

  • 适合多人游戏

安卓游戏机

除了著名的游戏机如 PS3、PS4 和 Xbox 之外,安卓游戏机如今也越来越受欢迎。典型的安卓游戏机特定功能包括:

  • 专用的硬件系统用于游戏

  • 全控制器输入系统

  • 多分辨率大屏幕支持

  • 硬件特定开发

  • 最佳的安卓平台,提供多人游戏体验

安卓手表

这是安卓上最常用的可穿戴平台。该设备的主要特点是提供健康信息。然而,游戏也在这个设备上流行起来。典型的安卓手表特定功能包括:

  • 非常小的显示屏

  • 有限的硬件支持

  • 较少的内存和处理能力

  • 触摸屏界面

  • 非常便携

  • 需要单独的可穿戴设备开发环境

总结

在开始开发之前,开发者必须清楚了解硬件和软件规格。在本章中,您已经了解了安卓上可能运行的不同硬件平台。通过这些知识,作为开发者,您可以轻松地确定您的目标受众。选择特定的一组硬件平台作为目标要容易得多。

在不久的将来,安卓将会进入虚拟现实世界,采用不同的技术。我们将在本书的后面讨论这些。到目前为止,您已经知道手机、平板电脑、电视、机顶盒、游戏机和手表是各种硬件平台。它们都能够运行安卓游戏。然而,安卓游戏机是唯一专用于游戏的硬件平台。

尽管游戏机是专门的游戏平台,但安卓手机和安卓平板是开发者最瞄准的平台。它们提供了运行几乎所有类型游戏所需的所有支持和设施。这些平台还有用户数量的额外优势。大多数开发者都瞄准这些平台,以获取尽可能多的用户。

第三章:不同的 Android 开发工具

我们已经讨论了游戏开发的不同 Android 目标设备。在本章中,我们将看看为 Android 开发游戏的不同方法和工具。除了开发技能和知识外,了解可以使开发过程更轻松和有效的有用软件非常重要。

Android 游戏开发得到许多强大的工具和库的支持。让我们来看看开发过程中必备的工具列表:

  • Android SDK

  • Android 开发工具

  • Android 虚拟设备

  • Android 调试桥

  • 达尔维克调试监视服务器

这些是 Android 游戏开发人员系统中必须安装的必备工具。没有这些工具,就无法为 Android 平台开发任何东西。尽管 ADB 和 AVD 对于开发来说并不是必需的,但它们需要用于在物理设备和虚拟设备上测试和部署游戏,以便调试游戏。

Android SDK

Android SDK 是构建 Android 应用程序所需的主要开发工具包。不详细介绍,可以说 SDK 是任何 Android 开发的骨架。这个 SDK 本身带有数十个支持工具。它包含平台细节、API 和库,以及 ADT 和 AVD。因此,将 Android SDK 集成到系统中提供了开发人员所需的所有必要工具。始终使用最新平台和其他工具更新 SDK 是一个非常好的做法。

升级可以通过 Android SDK 管理器进行。但是,平台选择是手动的,建议根据需求仅安装必要的平台。另一个最佳实践是将最新发布的平台与 Android 的最低目标版本一起使用(图片来源:photos4.meetupstatic.com/photos/event/1/1/0/f/highres_441724367.jpeg):

Android SDK

Android 开发工具

Android 开发工具ADT)是专为 Eclipse IDE 设计的插件,旨在为构建 Android 应用程序提供强大的集成环境。

ADT 扩展了 Eclipse 的功能,使开发人员能够快速设置新的 Android 项目,创建应用程序 UI,根据 Android 框架 API 添加包,使用 Android SDK 工具调试应用程序,甚至导出签名(或未签名).apk文件以分发应用程序。

在 Eclipse 中使用 ADT 进行开发是非常推荐的,也是最快速的入门方式。通过提供的引导式项目设置以及工具集成、自定义 XML 编辑器和调试输出窗格,ADT 极大地提高了开发 Android 应用程序的速度。

然而,谷歌正在取消对 Eclipse 的 ADT 支持,因此建议开发人员切换到 Android Studio。

Android 虚拟设备

Android 虚拟设备AVD)是真实设备的软件生成模型,可以配置自定义硬件规格。它也可以是真实设备的虚拟副本。这是任何 Android 开发人员最重要的工具之一。这使开发人员能够在典型的 Android 环境中测试应用程序,而无需使用实际的硬件设备,以缩短开发时间(图片来源:www.geeknaut.com/images/2014/08/top-android-emulators-for-windows3.png):

Android 虚拟设备

配置 AVD

AVD 包括以下内容:

  • 硬件配置文件:该配置文件描述虚拟设备的硬件特性。可以配置硬件选项,如 QWERTY 键盘、摄像头、集成内存等。

  • 系统镜像映射:可以根据已安装的 Android 平台集合配置正在运行的 Android 平台版本。Android 平台可以通过 Android SDK 管理器安装。

  • 专用磁盘空间:可以使用此功能在开发机上设置专用存储区域,用于保存模拟器的用户数据和虚拟 SD 卡。

  • 其他功能:开发人员甚至可以指定虚拟设备的外观和感觉,如设备外壳、屏幕尺寸和外观。

以下是通过 AVD 管理器创建 AVD 的简要过程,该管理器位于 <SDK 路径>/tools 目录中:

  1. 在主屏幕上,点击创建虚拟设备

  2. 选择硬件窗口中,选择设备配置,如Nexus 5,然后点击下一步,然后点击完成

  3. 要通过使用现有设备配置文件作为模板开始自定义设备,请选择设备配置文件,然后点击克隆设备。或者,要创建完整的自定义模拟器,请点击新硬件配置文件

  4. 设置以下内容以创建新的自定义模拟器:

  • 设备名称

  • 屏幕尺寸

  • 屏幕分辨率

  • RAM

  • 输入选项

  • 支持的状态

  • 摄像头选项

  • 传感器选项

  1. 设置完每个属性后,点击完成

开发人员还可以使用命令行创建新的自定义模拟器,如下所示:

android create avd -n <name> -t <targetID> [-<option> <value>] ...

在这里,可以设置以下选项:

  • name:这将是自定义 AVD 名称

  • targetID:这将是自定义 ID

  • option:这可以包括设备屏幕密度、分辨率、摄像头等选项。

开发人员可以执行此命令来使用先前定义的 AVD:

android list targets

然后,开发人员可以运行以下命令:

emulator –avd <avd_name> [options]

Android 调试桥

Android 调试桥adb)是用于在开发环境和虚拟设备或连接的 Android 设备之间建立通信的工具。它是一个客户端-服务器命令行程序,由三个元素组成:

  • 开发机上的客户端:作为客户端工作,可以通过 adb 命令调用。其他 Android 工具,如 ADT 插件和 DDMS,也会创建 adb 客户端。

  • 守护进程:在每个模拟器或设备实例上后台运行的后台进程。

  • 开发机上的服务器:这是在开发机上运行的后台进程,负责管理客户端和服务器之间的通信。

启动 adb 时,客户端首先检查是否已经运行了 adb 服务器进程。如果没有,它会启动服务器进程。服务器启动后,它会绑定到本地 TCP 端口 5037,并监听从 adb 客户端发送的命令——所有 adb 客户端都使用端口 5037 与 adb 服务器通信。

服务器然后建立到所有运行的模拟器/设备实例的连接。它通过扫描范围为 55555585 的奇数端口来定位模拟器/设备实例,这是模拟器/设备使用的范围。服务器在找到 adb 守护程序时,会建立到该端口的连接。请注意,每个模拟器/设备实例都会获取一对连续端口——用于控制台连接的偶数端口和用于 adb 连接的奇数端口。

一旦服务器建立了到所有模拟器实例的连接,开发人员就可以使用 adb 命令访问这些实例。由于服务器管理着对模拟器/设备实例的连接,并处理来自多个 adb 客户端的命令,开发人员可以从任何客户端(或脚本)控制任何模拟器/设备实例。

在 Android 设备上使用 adb

要记住的第一件事之一是将开发设备置于 USB 调试模式。这可以通过转到设置,点击开发人员选项,并为 Android 5.0 及以上版本勾选名为USB 调试的复选框来完成(对于其他 Android 版本,请参考www.recovery-android.com/enable-usb-debugging-on-android.html)。如果不这样做,开发 PC 将无法识别设备。

最重要的是知道如何通过命令行进入 adb 文件夹。这可以通过cd(更改目录)命令完成。因此,如果(在 Windows 上)SDK 文件夹称为android-SDK,并且位于根目录(c:)中,您可以输入以下命令:

cd c:/android-SDK

然后,要进入 adb 文件夹,请使用以下命令:

cd platform-tools

此时,提示会显示如下内容:

C:\android-SDK\platform-tools>

现在开发人员可以连接设备,并测试 adb 连接,在找到并安装特定设备的驱动程序之后:

adb devices

如果一切设置正确,应该会列出已连接的设备。手机或平板电脑将被分配一个编号,因此如果它不说“Droid Razr”或“Galaxy Nexus”,也不要感到惊讶。

对于普通用户,adb 更多地是用于基本黑客任务的工具,而不是任务本身。除非开发人员知道自己在做什么,他们可能不应该在没有明确指示的情况下过多地探索。在对设备进行 root 时,了解这些基础知识可以帮助节省一些时间,并让开发人员提前做好准备。

除了特定设备的 root 指令之外,开发人员需要的下一步是手机或平板电脑的驱动程序。

通常,最简单的方法是简单地通过谷歌搜索特定设备加驱动程序。因此,如果开发人员有一个 Droid Razr,他/她应该搜索Droid Razr Windows Drivers。这几乎总是会将开发人员引向最佳链接。

另一个选项,仅适用于原生 Android 设备的是从 SDK 下载 USB 驱动程序。要做到这一点,再次启动 SDK 管理器。转到左侧的可用软件包选项卡,展开第三方附加组件条目,然后展开Google Inc.附加组件条目。最后,勾选Google USB 驱动程序软件包的条目。

请注意,USB 驱动程序包与 OS X 不兼容。

Dalvik 调试监视服务器

Dalvik 调试监视服务器DDMS),无论是通过独立应用程序还是具有相同名称的 Eclipse 透视图访问,都提供了方便的功能,用于检查、调试和与模拟器和设备实例交互。您可以使用 DDMS 来检查运行中的进程和线程,浏览文件系统,收集堆和其他内存信息,附加调试器,甚至拍摄屏幕截图。对于模拟器,您还可以模拟模拟位置数据,发送短信和发起来电:

Dalvik 调试监视服务器

如前面的屏幕截图所示,DDMS 主要可以跟踪、更新和显示以下信息:

  • 所有运行中的进程

  • 每个进程的所有运行线程

  • 每个进程的已用堆

  • 所有日志消息

在 Android 上,每个应用程序都在自己的进程中运行,每个进程都在自己的虚拟机中运行。调试器可以连接到 VM 的公开端口。DDMS 在启动时连接到 adb。成功连接后,在 adb 和 DDMS 之间创建了一个 VM 监视服务,该服务在设备上启动和结束 VM 时通知 DDMS。DDMS 通过 adb 检索 VM 的进程 ID,并在通过设备上的 adb 守护程序运行的活动 VM 时,打开到 VM 的调试器的连接。DDMS 现在可以使用自定义的线路协议与 VM 通信。

DDMS 还监听默认调试端口,称为基本端口。基本端口是一个端口转发器,可以接受来自任何调试端口的 VM 流量并将其转发到调试器。转发的流量由 DDMS 设备视图中当前选择的进程确定。

其他工具

前面部分提到的元素是 Android 开发的最低要求,可以使用它们创建完整的应用程序。但是,借助其他一些工具的支持,开发过程可以变得更加容易。让我们看看一些这样的工具。这些工具对于 Android 开发并非强制使用,但建议使用以获得更好的开发过程。

Eclipse

尽管 Eclipse 不是唯一可用于开发 Android 应用程序的 Java 开发环境,但它是迄今为止最受欢迎的。这在一定程度上是由于其成本(免费!),但主要是由于 Android 工具与 Eclipse 的强大集成。这种集成是通过 Eclipse 的 ADT 插件实现的,可以从 Android 网站下载。

对于许多开发人员来说,使用 Eclipse 进行 Android 开发是一个众所周知的做法。其中一些原因如下:

  • 免费的 Eclipse IDE

  • 直接的 Android 插件支持

  • 直接的 DDMS 支持

  • 简单的界面

  • Android NDK 支持

Android Studio 的推出降低了 Eclipse 在 Android 开发人员中的流行度,因为 Android Studio 具有内置的支持任何 Android 开发所需的一切。此外,在设计视图中使用起来更加简化。谷歌本身正在大力推广这个新工具。

Eclipse Android 开发存在一些缺点,因为它使用 Android SDK 作为第三方工具。主要缺点如下:

  • 通过 Eclipse 进行调试有时很困难

  • ADB 配置很棘手

  • Android 清单必须手动管理

  • 通过 Eclipse IDE 进行设计视图非常复杂

Eclipse 是一个出色的独立 IDE,但在 Android 开发方面,Android Studio 赢得了比赛。

层次结构查看器

层次结构查看器,无论是通过独立应用程序还是相对较新的 Eclipse 透视图访问,都用于查看布局和屏幕在运行时的解析方式。

它提供了应用程序布局和视图层次结构的图形表示,并可用于诊断布局问题(图片来源:media-mediatemple.netdna-ssl.com/wp-content/uploads/2012/03/da_hierarchy_viewer.png):

层次结构查看器

Draw 9-Patch

在图形设计方面,Draw 9-patch 工具非常有用。该工具允许您将传统的 PNG 图形文件转换为可伸缩的图形,对于移动开发使用更加灵活和高效。该工具简化了在即时显示结果的环境中创建 NinePatch 文件的过程:

Draw 9-Patch

ProGuard

ProGuard 与 Android 开发没有直接关联,但它有助于保护开发者的知识产权。对于 Android 游戏开发人员来说,使用 ProGuard 是一种非常常见的做法。

ProGuard 基本上将成员和方法包装成不可读的代码结构。该工具可以配置为混淆生成的二进制文件。ProGuard 还有助于优化二进制文件,从而减小整体包大小。

当开发人员尝试将预编译的 JAR 文件集成到 Android 项目中时,ProGuard 可能很难使用。如果 JAR 已经通过 ProGuard 进行了优化,类结构可能会产生冲突。在这种情况下,必须配置 ProGuard 以排除预编译的 JAR,以实现成功构建。

始终建议使用 ProGuard 来保护游戏类免受逆向工程或反编译。

资产优化工具

我们都知道 Android 硬件配置的广泛范围。始终有必要优化资产以减少运行时内存使用和不必要的数据处理。在游戏中,图形资产占据了大部分存储空间和内存。

完整的资产优化

未经优化的资产可能包含一些不必要的数据,例如不透明资产中的透明信息,EXIF 数据,未使用的颜色信息,额外的位深度等。

资产优化工具有助于摆脱这一负担。但是,使用此类工具可能会使资产质量下降。开发人员在使用这些工具时应非常谨慎。

例如,如果一个资产应该是 24 位的,但是使用了 8 位优化工具进行了优化,它肯定会失去其视觉质量。因此,不建议对任何游戏进行过度优化,开发人员有责任使用适当的优化技术来维护游戏质量。

以下是一些此类资产优化工具:

  • PNGOUT

  • TinyPNG

  • RIOT

  • JPEGmini

  • PNGGauntlet

借助这些工具,艺术资源的大小可以优化到 80-90%。但许多开发人员不喜欢定期使用它们,原因如下:

  • 开发人员不会单独优化每个资产,这导致了一些资产的质量损失。

  • 选择正确的优化工具真的很困难。有时,相同的工作可能需要多个工具,这会减慢整体开发过程。

创建精灵

在许多情况下,人们注意到游戏中使用了大量小型艺术资源。这可能导致游戏的性能严重滞后。建议使用精灵构建工具将这些资源合并到一个资源中,以节省空间和时间。SpriteBuilder 和 TexturePacker 是此类工具的两个很好的例子。

测试工具

对于任何开发过程,测试都非常重要。对于 Android 游戏开发,也有一些工具和流程可以使测试变得更容易。

创建测试用例

活动测试以结构化方式编写。确保将测试放在与受测试代码不同的包中。按照惯例,您的测试包名称应该与应用程序包名称相同,后缀为.tests。在您创建的测试包中,添加您的测试用例的 Java 类。按照惯例,您的测试用例名称也应该与您想要测试的 Java 或 Android 类的名称相同,但后缀为Test

要在 Eclipse 中创建新的测试用例,请执行以下步骤:

  1. 在包资源管理器中,右键单击您的测试项目的/src目录,然后选择新建 |

  2. 名称字段设置为<package_name>.tests(例如com.example.android.testingfun.tests),然后单击完成

  3. 右键单击您创建的测试包,并选择新建 |

  4. 名称字段设置为<activity_name>Test(例如MyFirstTestActivityTest),然后单击完成

设置测试装置

测试装置由必须初始化以运行一个或多个测试的对象组成。要设置测试装置,您可以重写测试中的setUp()tearDown()方法。测试运行器在运行任何其他测试方法之前自动运行setUp(),并在每个测试方法执行结束时运行tearDown()。您可以使用这些方法将测试初始化和清理的代码与测试方法分开。

要在 Eclipse 中设置测试装置,请按照下面列出的步骤进行操作:

  1. 在包资源管理器中,双击您之前创建的测试用例,以打开 Eclipse Java 编辑器,然后修改您的测试用例类以扩展ActivityTestCase的子类之一。例如:
public class MyFirstTestActivityTest extends ActivityInstrumentationTestCase2<MyFirstTestActivity> {
  1. 接下来,向您的测试用例添加构造函数和setUp()方法,并为您想要测试的活动添加变量声明。例如:
public class MyFirstTestActivityTest
        extends ActivityInstrumentationTestCase2<MyFirstTestActivity> {

    private MyFirstTestActivity mFirstTestActivity;
    private TextView mFirstTestText;

    public MyFirstTestActivityTest() {
        super(MyFirstTestActivity.class);
    }

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        mFirstTestActivity = getActivity();
        mFirstTestText =
                (TextView) mFirstTestActivity
                .findViewById(R.id.my_first_test_text_view);
    }
}

构造函数由测试运行器调用以实例化测试类,而setUp()方法在测试运行器运行测试类中的任何测试之前由测试运行器调用。

通常,在setUp()方法中,您应该调用setUp()的超类构造函数,这是 JUnit 所要求的。

您可以通过以下方式初始化测试装置状态:

  1. 定义存储装置状态的实例变量。

  2. 创建并存储对要测试的活动实例的引用。

  3. 获取要测试的活动中的任何 UI 组件的引用。

开发人员可以使用getActivity()方法获取对要测试的活动的引用。

添加测试前提条件

作为一种合理检查,验证测试装置是否已正确设置,并且您要测试的对象是否已正确实例化或初始化是一个好习惯。这样,您就不必因为测试装置的设置有问题而看到测试失败。按照惯例,用于验证测试装置的方法称为testPreconditions()

例如,您可能希望向您的测试用例添加一个类似于以下内容的testPreconditions()方法:

public void testPreconditions() {
    assertNotNull("mFirstTestActivity is null", mFirstTestActivity);
    assertNotNull("mFirstTestText is null", mFirstTestText);
}

断言方法来自 Junit 的Assert类。通常,您可以使用断言来验证您要测试的特定条件是否为真。

如果条件为假,则断言方法会抛出AssertionFailedError异常,然后通常由测试运行器报告。您可以在断言方法的第一个参数中提供一个字符串,以便在断言失败时提供一些上下文详情。

如果条件为真,则测试通过。在两种情况下,测试运行器都会继续运行测试用例中的其他测试方法。

添加测试方法来验证一个活动

接下来,添加一个或多个测试方法来验证活动的布局和功能行为。

例如,如果您的活动包括一个 TextView,您可以添加一个类似于以下内容的测试方法来检查它是否具有正确的标签文本:

public void testMyFirstTestTextView_labelText() {
    final String expected =
            mFirstTestActivity.getString(R.string.my_first_test);
    final String actual = mFirstTestText.getText().toString();
    assertEquals(expected, actual);
}

testMyFirstTestTextView_labelText()方法只是检查 TextView 的默认文本(由布局设置)是否与strings.xml资源中定义的预期文本相同。

注意

在命名测试方法时,您可以使用下划线来区分正在测试的内容和正在测试的具体情况。这种风格使得更容易看到确切测试了哪些情况。

在进行这种类型的字符串值比较时,最好从资源中读取预期字符串,而不是在比较代码中硬编码字符串。这样可以防止在资源文件中修改字符串定义时轻松破坏您的测试。

要执行比较,将预期和实际字符串作为参数传递给assertEquals()方法。如果这些值不相同,断言将抛出AssertionFailedError异常。

如果您添加了一个testPreconditions()方法,请在 Java 类中的testPreconditions()定义之后放置您的测试方法。

您可以在 Eclipse 的“包资源管理器”中轻松构建和运行您的测试。要构建和运行您的测试,请按照以下步骤进行:

  1. 将 Android 设备连接到计算机。在设备或模拟器上,打开设置菜单,选择开发者选项,并确保USB 调试已启用。

  2. 在“项目资源管理器”中,右键单击您之前创建的测试类,然后选择运行为 | Android JUnit 测试

  3. Android 设备选择器对话框中,选择刚刚连接的设备,然后单击确定

  4. 在 JUnit 视图中,验证测试是否通过,没有错误或失败。

性能分析工具

在屏幕上放置像素涉及四个主要的硬件部件:CPU 计算显示列表,GPU 将图像渲染到显示器,内存存储图像和数据,电池提供电力。每个硬件部件都有约束;推动或超过这些约束会导致您的应用变慢,降低显示性能,或耗尽电池。

要发现导致特定性能问题的原因,您需要深入了解,使用工具收集有关应用程序执行行为的数据,将这些数据显示为列表和图形,理解和分析所见的内容,并改进您的代码。

Android Studio 和您的设备提供了性能分析工具,记录和可视化应用的渲染、计算、内存和电池性能。

Android Studio

Android Studio 是 Android 应用开发的官方 IDE,基于 IntelliJ IDEA。除了您期望从 IntelliJ 获得的功能外,Android Studio 还提供了以下功能:

  • 灵活的基于 Gradle 的构建系统

  • 构建变体和多个.apk文件生成

  • 代码模板可帮助您构建常见的应用程序功能

  • 具有拖放主题编辑支持的丰富布局编辑器

  • lint 工具用于捕捉性能、可用性、版本兼容性和其他问题

  • ProGuard 和应用签名功能

  • 内置对 Google Cloud 平台的支持,轻松集成 Google Cloud 消息传递和 App Engine

如果您是 Android Studio 或 IntelliJ IDEA 界面的新手,本节将介绍一些关键的 Android Studio 功能。

Android 项目视图

默认情况下,Android Studio 在 Android 项目视图中显示项目文件。此视图显示项目结构的扁平化版本,可快速访问 Android 项目的关键源文件,并帮助您使用基于 Gradle 的构建系统。Android 项目视图:

  • 在模块层次结构的顶层显示最重要的源目录

  • 将所有模块的构建文件分组到一个公共文件夹中

  • 将每个模块的所有清单文件分组到一个公共文件夹中

  • 显示所有 Gradle 源集的资源文件

  • 将不同语言环境、方向和屏幕类型的资源文件分组到单个资源类型的单个组中

Android 项目视图在项目层次结构的顶层显示所有构建文件,位于Gradle Scripts下。每个项目模块都显示为项目层次结构的顶层文件夹,并包含以下四个元素:

  • java/:模块的源文件

  • manifests/:模块的清单文件

  • res/:模块的资源文件

  • Gradle Scripts/:Gradle 构建和属性文件

注意

例如,Android 项目视图将不同屏幕密度下ic_launcher.png资源的所有实例分组到同一个元素下。

磁盘上的项目结构与此扁平化表示不同。要切换回分隔的项目视图,请从项目下拉菜单中选择您的项目。

内存和 CPU 监视器

Android Studio 提供了内存和 CPU 监视器视图,以便您可以轻松监视应用的性能和内存使用情况,跟踪 CPU 使用情况,查找已释放的对象,定位内存泄漏,并跟踪连接设备使用的内存量。在设备或模拟器上运行您的应用程序时,单击运行时窗口左下角的Android选项卡,以启动Android 运行时窗口。单击内存|CPU选项卡。

在 Android Studio 中监视内存使用情况时,您可以启动垃圾回收,并同时将 Java 堆转储到 Android 特定的 HPROF 二进制格式文件中。HPROF 查看器显示类、每个类的实例和引用树,以帮助您跟踪内存使用情况和查找内存泄漏。

Android Studio 允许您跟踪内存分配,因为它监视内存使用情况。跟踪内存分配可以让您监视在执行某些操作时对象的分配位置。了解这些分配可以让您调整与这些操作相关的方法调用,以优化应用程序的性能和内存使用。

内存和 CPU 监视器

跨平台工具

虽然我们只谈论 Android 游戏开发,但是没有跨平台支持,游戏开发是不可能高效的。我们已经讨论了游戏设计的灵活性。从典型的技术角度来看,应该可以将游戏部署到各种平台,如 iOS、Windows、游戏机等。

请始终记住,跨平台移动开发并不像只编写一次代码,通过工具进行翻译,然后将 iOS 和 Android 应用程序发布到各自的应用商店那样简单。

使用跨平台移动开发工具可以减少在两个平台上开发应用程序所需的时间和成本,但需要更新 UI 以匹配每个系统。例如,需要在两者之间进行调整,以使菜单和控制命令与 Android 设备和 iOS 设备的 UX 相匹配,因为它们在操作方式上有所不同。

有很多支持跨平台开发的工具。让我们来看看其中的一些:

Cocos2d-x

Cocos2d 主要用于二维游戏开发。它为开发者提供了基于其首选编程语言的五种不同分支或平台的选择,如 C++、JavaScript、Objective C、Python 和 C#(图片来源:www.cocos2d-x.org/attachments/802/cocos2dx_landscape.png):

Cocos2d-x

主要,这个工具对 Android、iOS 和 Windows Phone 都很有效。开发平台主要是 2D;然而,从 Cocos2d-x 3.x 开始,也可以开发 3D 游戏。

Cocos2d-x 与原生 Android 兼容,并且可以分别支持不同的处理器架构。这个工具在 Unix 环境中工作。

有一个庞大的开发者社区在 Cocos2d-x 上开发游戏。以下是从 Android 游戏开发角度看这个跨平台开发引擎的优缺点:

优点如下:

  • 支持最常见的编程语言,如 C++

  • 在本地环境中工作

  • 轻量级和优化库

  • 常见的 OpenGL 渲染系统

  • 支持所有智能手机功能的 2D 开发

  • 完全免费的开源引擎

缺点如下:

  • 主要支持 2D 开发

  • 跨平台部署很棘手和复杂

  • 性能和内存优化较弱

  • 没有可视化编程支持

  • 引擎内没有提供调试工具

  • 主要在手机平台上运行

Unity3D

Unity3D 是 Android 和 iOS 游戏开发者中最流行的跨平台引擎。虽然它主要针对移动平台进行了优化,但它足够强大,可以为其他主要游戏平台部署游戏,例如 PC、Mac、游戏机、Web、Linux、Xbox、PlayStation 等。目前,它支持 17 种不同的游戏开发平台(图片来源:img.danawa.com/images/descFiles/3/545/2544550_1_1390443907.png):

Unity3D

一旦您在所有选择的平台上都有了游戏,Unity3D 甚至会帮助您将其分发到适当的商店,获得社交分享,并跟踪用户分析。

Unity3D 拥有最大的游戏开发者社区,几乎在游戏开发的各个方面都有很大的支持。它有自己的商店,您可以在那里找到有效的预构建自定义库、预构建插件等,这有助于开发者大大减少开发时间。以下是 Unity3D 的主要优缺点。

优点如下:

  • 支持 17 种不同的游戏平台

  • 非常简单的部署程序

  • 可视化编辑器支持可视化编程

  • 内置强大的调试工具

  • 庞大的库支持

  • 无忧开发

  • 内置强大的内存和性能优化器

缺点如下:

  • 相对较大的库大小

  • 性能稍重(然而,它正在日益改进)

  • 仅支持脚本语言(C#,JavaScript 和 Boo)

  • 商业用途并非完全免费

  • 主要适用于 3D

虚幻引擎

最近发布的虚幻引擎 4 是一个非常强大的跨平台游戏引擎。此前,该引擎仅专注于主机和 PC 平台,但现在已将其支持扩展到 Android 和 iOS 等移动游戏平台(图片来源:up.11t.ir/view/691714/1425334231-unreal-engine-logo.png):

虚幻引擎

关于虚幻引擎 4 是否比 Unity3D 更好有很多争论。它们都有各自的优缺点。让我们来看看虚幻引擎 4 的优缺点:

优点如下:

  • 蓝图功能允许灵活的可视化编程

  • 通用 C++语言更适合开发人员

  • 图形处理非常出色

  • 内置动态阴影系统

  • 简单易懂,开始制作游戏

  • 在设备可扩展性方面提供广泛支持

  • 编辑器中的材料设计

缺点如下:

  • 移动优化仍未达到标准

  • 缺乏 2D 开发工具

  • 缺乏第三方插件的可用性

  • 在移动开发中使用精灵很麻烦

  • 仍专注于高配置硬件平台

PhoneGap

由 Adobe 拥有,PhoneGap 是初次应用程序开发者可以使用的免费资源,用于将代码从 HTML5,CSS 和 JavaScript 翻译过来。

他们在各个平台上维护 SDK,因此您可以为每个平台开发应用程序,这样您就少了一件要担心的事情。一旦您的应用程序完成,您可以与团队成员分享,以便审查是否需要进行任何改进。

除了 iOS 和 Android,PhoneGap 还为 BlackBerry 和 Windows 创建应用程序。因此,它真正是一个跨平台移动开发工具(图片来源:blogs.perceptionsystem.com/wp-content/uploads/2016/03/phonegap.png):

PhoneGap

PhoneGap 具有以下优点:

  • 几乎支持所有移动平台

  • 轻量级应用程序构建

  • 支持 HTML,CSS 和 JavaScript

  • Cordova 应用程序的安装方式与本机应用程序相同

  • PhoneGap 是开源和免费的

它有以下缺点:

  • 缺乏平台支持

  • 缺乏第三方插件

  • 本机 UI 仍然难以使用

Corona

Corona 的 SDK 承诺您可以在下载后的五分钟内开始编写新应用程序。这是另一个专为 2D 游戏图形优化的跨平台移动开发工具,可以帮助您比从头开始编写代码快 10 倍制作游戏(图片来源:qph.ec.quoracdn.net/main-qimg-fad64a16e531773325448e6ca699d117):

Corona

Corona 的编程语言是 Lua,它是用 C 编写的,使其成为一种跨平台语言。Corona 选择 Lua 是因为他们发现它对于移动应用程序来说非常强大,占用空间很小。

Corona 具有以下优点:

  • 在 FPS 方面具有良好的应用程序性能

  • 良好的内置模拟器

  • 轻量级应用程序构建

它有以下缺点:

  • 使用不太流行的脚本语言 Lua

  • 不免费

  • 插件支持较少

  • 没有设备上的调试支持

使用 JavaScript,Titanium 的 SDK 可以创建原生的 iOS 和 Android 应用程序,同时在制作所有应用程序时可以重复使用 60%到 90%的相同代码,从而为您节省大量时间(图片来源:mobile.e20lab.info/wp-content/uploads/sites/2/2014/04/titanium.png):

Titanium

由于 Titanium 是一个开源工具,成千上万的开发人员不断为其做出贡献,使其变得更好,并赋予它更多功能。如果您发现系统中有错误,您也可以这样做。

优点如下:

  • 初始阶段的快速灵活性

  • 轻量级应用程序构建

  • 常见的 JavaScript 语言

  • 在 Android 和 iOS 上的 Web 和移动支持

  • 开源

缺点如下:

  • 缺乏插件支持

  • 缺乏平台支持范围

  • 基于脚本的开发增加了复杂性和工作量

  • 性能因不同平台而异

  • 与其他工具相比,优化不足

总结

开发工具对于任何游戏开发都是必不可少的;然而,在游戏设计和前期分析阶段,它们一直是低优先级的。当需要这些工具时,才意识到它们的必要性。

我们只讨论了 Android 开发的必备工具。但是现代游戏开发需要在硬件平台和操作系统上具有灵活性。这就是跨平台开发引擎出现的地方。这些工具帮助开发过程变得更快更高效;然而,这是以性能略微下降和更大的构建大小为代价的。在大多数情况下,开发人员对跨平台引擎的控制有限,但如果游戏是在原生 SDK 上开发的,就可以获得完全控制。

开发工具不仅在开发和调试方面有用——它们在优化游戏以及数据保护方面非常高效,这可能对游戏没有直接影响。一个优秀的开发人员必须使用优化工具来提供性能更好的游戏。

第四章:行业中的 Android 开发风格和标准

在 Android 中没有书面规则或指导来编写代码,除了语法语法。然而,全球大多数开发人员都遵循一些基本的编写 Android 代码的风格和标准。Android 基于 Java,因此大多数样式遵循 Java 标准。

在 Android 游戏开发方面,应遵循一些设计风格。它们不涵盖游戏设计,而更多是技术设计。这些样式和标准指示了适当的项目结构,类结构和文件夹/文件管理。

典型的游戏设计还涉及在 Android 平台上工作时遵循一些规则。在行业中,游戏设计方面遵循了一些风格。

在本章中,我们将通过以下主题查看这些风格和标准:

  • Android 编程结构

  • 游戏编程规范

  • 技术设计标准

  • 游戏设计标准

  • 其他风格和标准

  • 不同的开发引擎有不同的风格

  • 行业最佳实践

Android 编程结构

Android 风格或建议并不是明确的编程规则。然而,良好的编程实践总是包括一套规则。在 Android 中编码,代码结构遵循 Android 基本结构和层次结构。

Android 通常遵循 Java 的标准和风格。因此,Android 编程结构基本上是 Java 结构,遵循面向对象的风格。

类形成

Java 类格式应保持一致,并遵循 Javadoc 规则;标准结构应遵循以下顺序:

  1. 版权信息

  2. 许可信息

  3. 包声明

  4. 库导入

  5. 类描述和目的

  6. 类定义

  7. 全局变量

  8. 构造函数

  9. 方法

这是版权和许可信息格式:

/*
* Copyright (C) <year> authority
* 
* <License information and other details>
*/

这是类和方法描述格式:

/*
* <Description>
* <Purpose>
*/

调用层次结构

与编码风格一样,没有定义的调用层次结构。然而,在 Android 游戏中,大多数开发人员遵循基本方法。在这种方法中,项目结构主要有三种类:

  • 管理器和控制器

  • 关联

  • 实用类

游戏需要管理器和控制器来实施游戏规则和法规。它还用于控制游戏元素和状态的行为。大多数游戏包括多个部分或屏幕,例如菜单,级别选择,游戏玩法等。

这些部分可以称为状态,其中使用的元素称为关联。关联可能是独立的类。实用类基本上通过提供预定义功能来支持开发,例如游戏声音实现,记录存储管理,常用工具,网络连接等:

![调用层次结构](img/B05069_04_01.jpg)

调用层次结构

在游戏循环中,主 Android 游戏线程循环将控制权交给主游戏管理器。主游戏管理器负责将线程和渲染控制传递给所需的状态管理器。主游戏管理器还负责根据当前线程的初始化和销毁状态管理器。此外,主游戏管理器在整个游戏周期内都是活动的,并管理主游戏活动。

状态管理器负责管理状态(主菜单,级别选择,游戏中等)和所有所需的关联类。状态管理器将调用传递给相应的关联类以执行特定任务。

实用类可以从层次结构中的任何层访问,具体取决于要求。

这是一个通用的架构,不是强制性的。许多开发人员以不同的方式设计调用层次结构。一切都很好,游戏流程也在不牺牲类/数据安全的情况下正常运行。

游戏编程规范

游戏程序员与其他软件程序员并没有太大的不同。然而,游戏编程需要比软件编程更强的逻辑能力。游戏设计感也是不同的。

游戏是一个互动娱乐系统。软件或应用程序旨在减少人类的实时计算任务。因此,很明显游戏的目的与软件或应用程序非常不同。

这就是为什么游戏编程必须遵循一些规范和协议。

游戏编程可以分为以下几个类别:

  • 游戏性编程

  • 图形编程

  • 技术编程

游戏性编程

这是游戏部门最相关的编程。游戏性编程需要强大的逻辑、数学和分析能力。还需要一定的游戏设计感。游戏性编程包括人工智能、用户控制、物理、显示控制等等。

游戏程序员是与最负责任的设计师打交道的人,用于原型游戏。

图形编程

所有的视觉效果和影响都是由图形程序员制作的。他们负责游戏的视觉质量。在游戏运行时,有很大的空间来操纵图形资产。

图形编程就是要充分利用 GPU。如今,大多数游戏都是图形密集型的。最新的设备有独立、强大的 GPU 来支持高负荷的图形。

现代游戏展现出优质的图形显示。所有新的 3D 实时光照、粒子系统效果、视觉动态效果等完全依赖于图形程序员。挑战在于提高视觉质量而不影响游戏性能。

大部分时间,游戏性能会因为大量艺术资产处理而显著下降,这绝不是期望的情况。着色器或图形程序员必须在质量和性能之间取得平衡。

当游戏在不同平台上移植时,这通常是有效的。正如我们之前讨论过的,Android 上运行着一系列硬件平台,具有广泛的硬件规格。这种移植需要单独的着色器和资产。着色器程序指示图形处理器以特定方式渲染每个像素。

技术编程

这部分编程与游戏没有太多关系;然而,它确保了游戏的性能。除了性能外,网络管理、插件开发、优化等也是技术编程的一部分。以下是技术编程可能涉及的领域的列表:

  • 声音编程

  • 网络编程

  • 游戏工具编程

  • 研究和开发编程

声音编程

如今,声音是游戏的必要部分。一些游戏甚至是围绕音乐制作的。因此,声音编程已成为游戏编程的一部分。声音程序员主要具有数字信号处理的知识。声音程序员必须与声音设计师合作。

现代游戏使用 3D 音响系统。声音编程在不影响性能的情况下发挥着传递高质量声音的关键作用。

网络编程

老式游戏大多是为了在单个实例上运行。大部分时间,游戏不会与其他实例通信,因此在那个时候并不需要太多的网络编程。现代是网络时代;仅有一个实例是不够的。多人游戏如今非常普遍;即使是独立游戏也会与其他游戏实例通信,只是为了社交和货币化。

网络编程负责网络延迟、数据包优化、连接处理和维护通信。网络程序员还负责管理客户端-服务器通信和创建架构。

一些游戏在服务器上运行。客户端充当服务器上运行的游戏实例的显示设备。交易遵循实时同步。少数游戏甚至采用异步通信。网络编程确保了这种架构中的平稳和适当的过渡。

游戏工具编程

游戏开发离不开某些工具的支持。工具程序员可以让其他开发人员的生活变得天堂或地狱。适当的游戏工具编程可以极大地简化开发过程。开发工具的帮助可以节省大量时间和精力。

我们已经讨论了一些开发工具。然而,并不是所有的工具都必须与游戏无关。可能有一些工具是特定于游戏的。例如,可以制作一个工具来生成游戏中需要使用的数据库。

研究和开发编程

这种编程不是主要用于制作游戏。相反,这种编程有助于使游戏变得更好,并找到用于即将推出的游戏的新技术。

这种类型的编程需要对技术理解、硬件平台和本地开发具有强大的技能。程序员应该具有本地语言和汇编或硬件级语言的知识。

在 Android 游戏开发的情况下,研究程序员被指派探索新的 Android 设备以及新的功能和规格。然后,这些程序员尝试发现如何在游戏中以最佳方式使用这些功能。

这种编程完全负责具有各种传感器(如重力、光线、加速计等)使用等功能的游戏。最近在 Android 设备上开发虚拟现实就是这种实验的一个实际例子。

技术设计标准

大多数情况下,游戏开发围绕游戏设计展开;然而,开发过程受技术设计控制。技术设计考虑了实际游戏设计和需求的实时可行性的每一个可能方面。

技术设计包含以下部分:

  • 游戏分析

  • 设计模式和流程图

  • 技术规范

  • 工具和其他要求

  • 资源分析

  • 测试要求

  • 范围分析

  • 风险分析

  • 变更日志

游戏分析

技术设计的这一部分彻底分析了游戏设计,并找出了技术在其中发挥重要作用的部分。游戏逻辑开发不是本节的一部分。然而,当游戏逻辑需要硬件依赖时,技术设计也会被考虑在内。

许多开发人员和组织在制定游戏设计后,开始实际开发过程之前制定技术设计文档的习惯。这有助于定义时间表并预测即将出现的挑战,以及可能的解决方案。

设计模式和流程图

本部分设计了游戏的类图和层次结构。游戏的流程图和服务器-客户端架构(如果需要)也在这里定义。

游戏技术设计的这一部分为开发人员提供了即将到来的开发的清晰图景。技术设计文档的这一部分应清楚地声明游戏模块的每一部分、程序结构、调用层次结构、第三方工具集成、数据库连接和服务器调用管理。

这样的图表视觉展示,展示流程图,总是任何开发过程的一个良好开端。

技术规范

技术规范指定了开发平台、目标设备集和目标操作系统。它还可能提到开发项目所需的硬件系统和软件。

在实际开发之前,这种识别是必不可少的。任何软件要运行,都需要一个能够支持软件的硬件平台。开发人员必须了解目标设备,并必须提供这些设备进行单元测试。任何额外的需求也在技术设计的这一部分中得到确认。

因此,基本上,这个规范有两个不同的部分。第一个是指定游戏指定运行的目标系统。第二个是根据设计确定创建游戏所需的系统。

工具和其他需求

技术设计中的这一部分涉及额外的工具和系统需求。在许多情况下,这一部分包含在技术规范中。然而,这部分有不同的目的。

这可能需要开发一个新的工具来进行实际的游戏开发。因此,工具程序员被引用到这一部分。Android 游戏开发并不在这一部分的范围之外。尽管大多数工具已经为 Android 开发准备就绪,但一些情况可能也需要一个特定于游戏的工具。在这种情况下,工具设计和单独的技术设计以及工具的使用在本节中提到。

资源分析

资源分析是关于人员依赖、人员技能水平、技术依赖和其他资源依赖的报告。这有助于估计项目成本并决定开发时间表。

测试需求

这是技术设计的另一个重要部分。测试是游戏开发过程中不可或缺的一部分。技术设计应该定义测试程序以及定义的测试用例。

游戏开发负责人确定了测试的阶段及其需求。测试工具可能在本节中声明。我们已经在前一章中提到了测试工具。在某些情况下,可能需要定制工具。

测试需求有四个主要部分:

  • 测试资源需求

  • 测试工具需求

  • 测试用例

  • 测试时间线

范围分析

每个游戏都有一个预定义的有限范围。特别是在 Android 设备上,种类最多,需要定义范围。在所有 Android 平台上以相同设计运行游戏几乎是不可能的。

技术设计的这一部分指示了游戏的可能范围。这可能确定运行游戏所需的最低配置、推荐配置和目标配置,以使游戏在最大性能下运行。

游戏范围定义了硬件平台的最小和最大范围。大多数开发人员喜欢将游戏设计范围最小化,以针对最大的硬件设备。技术设计文件是开发人员获取游戏性能范围的良好参考。

风险分析

在生产开始之前制作技术设计文件,因此有许多领域必须事先假定。这显然增加了项目的风险。然而,清楚地了解风险总是有助于开发人员在实际问题发生时找到解决方案。

这就是为什么风险分析对于任何技术设计标准都是强制性的原因。风险可以在不同领域进行分析。

在开发游戏时,技术需求或游戏设计可能会发生变化。因此,应该计算风险以适应这些变化,而不影响主要项目流程。

技术发展迅速。因此,在风险分析部分,技术变化也应该得到解决。在游戏开发的常见情况下,技术可能在开发过程中发生变化,以提高游戏质量。

变更日志

变更日志是包含技术文件从第一稿开始的所有变更的列表,根据日期和版本号进行排序。这有助于跟踪游戏的演变。

游戏设计标准

游戏设计几乎在游戏行业的每个组织中都有记录。这是几乎所有开发人员经常使用的标准之一。有时会跳过技术设计,以节省时间,一些设计师会在游戏设计中包含来自技术文件中最需要的部分。然而,这种方法是不推荐的。

维护标准游戏设计的基本方法包括以下部分:

  • 游戏概述

  • 游戏玩法细节

  • 游戏进展

  • 故事板和游戏元素

  • 关卡设计

  • 人工智能

  • 艺术风格

  • 技术参考

  • 变更日志

游戏概述

这一部分定义了游戏的性质以及目标受众。这一部分包含了关于游戏概念,玩法和外观的简要介绍。工作标题在之前提到。

游戏概述基本上是对即将制作的游戏的几乎所有方面的简要介绍。这一部分可能展示了市场研究,以支持所选择的游戏概念和类型。

游戏玩法细节

游戏玩法控制和控制游戏玩法的首选用户界面在这一部分中被定义。这是游戏中最重要的部分之一。游戏玩法应该针对其所针对的每个硬件平台进行优化。游戏可能部署到手机,平板电脑和游戏机上。因此,出于明显的原因,为不同的控制方案定义了不同的控制方案。

游戏进展

游戏进展定义了游戏的生命周期及其随时间的演变。游戏是一个动态的娱乐系统。因此,用户在任何时候都不能感到无聊,这一部分负责用户留存。

故事板和游戏元素

游戏设计的这一部分定义了游戏概念的背景。这并不意味着有一个真实基于故事的游戏背景。然而,每个游戏都必须有一些元素或对象,围绕这些元素或对象进行游戏。

例如,一个横向滚动的奔跑游戏将有一个角色,一些障碍,环境物体等。它们被称为游戏元素。奔跑的原因是背景故事。

举个例子,让我们假设一个井字棋游戏。背景故事并不是必要的;然而,叉,圈,和网格是游戏的元素,需要被设计和风格化。

关卡设计

关卡是游戏进展的结果。每个关卡都有一个概要,介绍,材料或元素,以及一个目标。根据游戏的不同,可以提供更多信息。

人工智能

人工智能帮助游戏在实时场景中体验。它可以是对手,敌人,障碍,友好支持,情况检测,碰撞检测,路径规划,或者任何自动确定游戏状态的东西。

人工智能对每个游戏都是强制性的。它应该意味着在一个领域内执行某项任务的数学或物理算法。

艺术风格

游戏设计文件还包括外观和风格的风格和方向。设计师也可以包括一些参考。这给了艺术家在思考艺术方向时一个头脑风暴。艺术是吸引用户最有力的部分。

这一部分不包括艺术的技术规范。开发人员可以在这里包括一些技术指导,以优化游戏内使用的资产。

技术参考

在设计文件的这一部分,包括了所有的技术参考。对于 Android 游戏开发,这一部分可能包括一系列具有最低规格的设备,目标平台,基础图形引擎,开发引擎等。这是实际技术设计文件的缩小版本。当开发人员或组织选择不制作任何单独的技术文件时,他们在这个范围内提到所有的技术规格。

变更日志

变更日志记录了文件的更改历史,包括版本和日期。这与任何变更日志文档具有相同的目的。

其他风格和标准

在前面的章节中提到的标准定义了制作游戏的一般流程。我们将讨论一些在游戏开发行业广泛使用的这些流程。

大多数大型组织都遵循一定的项目管理和跟踪系统。这可能会使开发过程变慢,但足够有效地减少风险并提高游戏质量。一些小型组织或个人开发者为了尽快完成产品而不遵循这样的流程。

这些风格是相互对立的,有不同的后果。然而,建议您遵循一种有利于长远发展的程序。快速修复不能成为永久解决方案。

另一个常用的做法是修补代码以解决错误。这也极易受到诸如项目崩溃、截止日期失败和产生重大错误等威胁。在游戏开发中,最常见的问题是设备崩溃,这在任何硬件平台上都是最不期望发生的。在大多数情况下,这是因为糟糕地处理异常而发生的。

玩游戏并理解游戏对于制作游戏是非常必要的。大多数游戏开发组织鼓励开发者玩游戏并研究游戏。对于 Android 开发者来说,多玩不同平台的游戏是非常好的练习。已经确定 Android 是最好的移动或小型硬件操作系统。它也在大型平台上有所发展。作为 Android 开发者,时刻关注其他平台的特性和发展,并尝试在 Android 中实现它们是一种良好的实践。这是 Android 游戏研发团队的工作。

不同的开发引擎的不同风格

我们已经讨论了一些开发工具和引擎。当前的游戏行业不鼓励仅针对特定硬件或操作平台开发游戏。我们可以找到很多平台独占的游戏,但这意味着一个商业决定。

很明显,相同的开发方法不适用于每个开发引擎。例如,在本地 Android 中的开发风格将不同于 Unity3D 游戏引擎中的开发风格。基本原因是:

  • 不同的编程语言

  • 不同的工作原则

  • 不同的目标平台

不同的编程语言

每种编程语言都有自己的编程风格和结构。通过 C++在 Android NDK 上开发游戏与使用 Java 在 Android SDK 上制作游戏是不同的。使用第三方跨平台引擎开发游戏也是不同的。

我们这里不是在谈论语法上的差异。这是关于编码风格。在 Android NDK 中使用 C++与在 Unreal Engine 4 或 Cocos2d-x 中使用 C++是不同的。尽管 C++核心库是相同的,但每个工具都引导开发者朝着不同的风格方向以获得最佳结果。

不仅仅是 C++和 Java,游戏行业还在使用 C#、Python、JavaScript、Lua、Boo 等。许多引擎支持多种编程语言,以吸引更多的开发者。

不同的工作原则

不同的游戏引擎或游戏制作工具遵循不同的工作原则。开发者应该足够灵活,适应这些不同的系统。不同引擎总是有不同的代码结构、文件夹结构和程序层次。

对于本地开发,是开发者设定标准。引擎带有一套标准,预期所有在该引擎上工作的开发者都会遵循相同的原则。

例如,Unity3D 的工作原理与 Unreal Engine 或 Cocos2d-x 大不相同。Cocos2d-x 不支持可视化编程,而 Unreal Engine Blueprint 则完全支持可视化编程。因此,尽管部署目标相同,开发方法必须不同。

不同的目标平台

现代跨平台游戏开发工具已经最小化了风格和标准的差异。然而,对于很少数的工具来说,风格和标准仍然不同。

现在,如果我们只谈论安卓,那么考虑一下上面的不同硬件平台。开发风格并不总是指编程。它涉及到从设计到部署的完整项目维护。安卓控制台开发与安卓手机开发不同。

从游戏玩法角度来看,设计风格通常随着游戏时间、控制和外观而变化。在控制台上的平均游戏时间可能长达 2 小时,而移动设备的游戏时间几乎是这个时间的 5%。触摸界面与按键界面大不相同,而游戏控制器界面也不同。因此,即使开发者计划使用相同的引擎部署相同的游戏,由于非常明显的原因,界面设计风格也会发生变化。

行业最佳实践

尽管存在大量的风格和标准,大多数开发者喜欢遵循一些共同的标准,以在游戏开发过程中创建稳定性。让我们讨论一些行业常见的标准领域:

  • 设计标准

  • 编程标准

  • 测试标准

设计标准

每款游戏的设计和概念都各不相同。最佳的设计标准做法是将其适当地记录下来,并留有改进的余地。文件必须足够清晰,以便用户理解。无论概念是什么,开发者都不能在没有对标准有适当理解的情况下实施它。

设计范围不能太开放,以至于可以改变整个游戏;这会导致生产时间严重延误。然而,它应该始终有限的范围,以随着想法的增长改善生产时间。

设计必须指定目标类型和受众,以及有效的原因。这也应包括可能的目标硬件平台。

对于安卓开发,移动游戏是当今最大的行业。因此,大多数安卓开发者主要关注移动游戏。然而,设计师应该始终留有余地,使游戏能够部署到其他平台,如可穿戴设备、控制台等。

编程标准

编程是设计的执行。这是任何游戏制作中最重要的部分。标准的代码应该是可读的、模块化的,并且有适当的文档记录。以前有两种编程方法:过程化和面向对象。在现代游戏行业中,开发者遵循面向对象的方法。因此,编程标准发生了很大变化。以前,使用m_l_作为变量前缀来指示它们在面向对象结构中的状态是常见做法。还有一些其他符号,如ifb等,用来表示变量类型。

现代标准主要遵循驼峰和帕斯卡命名法作为它们的命名约定。常见做法是对所有类、接口、枚举、事件、结构、命名空间和方法名使用帕斯卡命名法,其他元素应使用驼峰命名法。

编程语言中的驼峰命名法意味着名称的第一个字母应为小写,这被称为小驼峰命名法。帕斯卡命名法规定名称的第一个字母应大写,这被称为大驼峰命名法

在任何语言的编程中,方法中的参数数量或每行的字数都没有技术上的限制。然而,常见的行业做法是方法的参数数量应在八个以内,编码每行的字数不应超过 20 个。

手动限制的原因是为了减少复杂性,提高代码的可读性。出于同样的原因,方法体应限制在 200 行以内,并且分割类结构始终是首选。

测试标准

测试是对设计实施的验证过程。测试还检查开发标准,并确保游戏的质量。

在大多数情况下,测试程序主要包括自动化测试和手动测试两个部分。程序员必须编写用于检查核心开发的自动化测试代码。这部分被称为测试代码,不得包含在主要开发项目中。需要专门的测试人员进行手动测试。他们的工作是从用户的角度确保游戏的质量。

大多数游戏开发公司都遵循测试程序的检查表。这个检查表通常包含定义的测试用例。测试用例主要由开发人员和设计师定义,测试人员需要执行这些用例。我们将在后面的章节中详细讨论测试。

总结

任何软件开发都必须遵循一定的协议和标准。游戏开发也不例外。遵循标准有助于产品在较长时间内持续存在。现代 Android 游戏的生命周期包括推出后的许多更新,在许多情况下游戏会持续多年。对于一个组织来说,同一个开发人员可能不会长时间地在同一个游戏上工作,这在游戏行业中是非常普遍的情况。

开发项目必须足够易读,以便新开发人员采用,并且足够灵活,以适应游戏更新的新变化。

最后,让我们总结一下在 Android 游戏开发行业中常见的强制性任务。游戏开发人员应该遵循游戏开发原则。首先,他们必须创建适当的游戏设计文件,以便程序员和艺术家能够清楚地理解。然后,他们应该创建适当的技术设计文件,为程序员和游戏工程师提供所有可能的技术信息。组织中的特定开发流程定义和维护开发标准。程序员必须以模块化的方式编写代码,以避免未来的更改,并增加代码的可重用性。适当的命名约定总是有助于更好地理解代码,并为轻松编辑和重用做好准备。

游戏开发人员应该遵循的另一种做法是玩很多游戏并享受游戏。

第五章:了解游戏循环和帧率

游戏循环是游戏的运行主体,帧率是其结果。没有定义游戏循环,游戏就无法制作;没有测量帧率,性能就无法判断。

游戏开发的这两个方面在任何游戏开发项目中都是通用的。然而,游戏循环的可伸缩性和性质在不同设备上有所不同,不同平台上可能有不同的帧率测量标准。

对于原生开发,游戏循环由开发人员自行创建和维护。然而,在大多数游戏引擎中,循环已经定义了所有必要的控制和范围。

我们将通过以下主题详细了解游戏开发中最重要的这两个部分:

  • 游戏循环介绍

  • 使用 Android SDK 创建示例游戏循环

  • 游戏生命周期

  • 游戏更新和用户界面

  • 中断处理

  • 游戏状态机的一般概念

  • FPS 系统

  • 硬件依赖

  • 性能和内存之间的平衡

  • 控制 FPS

游戏循环介绍

游戏循环是核心循环,依次执行用户输入、游戏更新和渲染。这个循环理想情况下每帧运行一次。因此,游戏循环是运行帧率控制游戏最重要的部分。

典型的游戏循环有三个步骤:

  1. 用户输入

  2. 游戏更新

  3. 渲染!游戏循环介绍

简单的游戏循环

用户输入

本节检查游戏的 UI 系统是否接收到了外部输入。它设置了游戏在下一次更新时需要进行的更改。在不同的硬件平台上,游戏循环的这部分变化最大。为不同的输入类型创建通用功能是一种最佳实践,以确保标准化。

输入系统不被视为游戏循环的一部分;然而,用户输入检测是游戏循环的一部分。该系统不断监视输入系统,无论是否发生事件。

当活动游戏循环运行时,用户可以在游戏过程中的任何时间触发任何事件。通常,输入系统维护着队列。每个队列代表不同类型的可能输入事件,如触摸、按键、传感器读数等。

用户输入监视器按照循环顺序在特定间隔内检查这些队列。如果在队列中发现任何事件,它会进行必要的更改,这将影响游戏循环中下一次更新的调用:

用户输入

用户输入工作原理

游戏更新

游戏循环的游戏更新部分管理和维护完整的游戏状态。该部分还负责运行游戏逻辑、游戏状态的更改、加载/卸载资源以及设置渲染管线。

游戏控制通常由游戏更新部分管理。通常,主游戏管理器在游戏更新部分的顶层工作。我们在前一节讨论了游戏程序结构。

任何游戏一次只能运行一个特定状态。状态可以通过用户输入或任何自动化的 AI 算法进行更新。所有 AI 算法都是逐帧地在游戏更新周期上运行的。

状态更新

如前所述,状态可以从游戏更新中进行更新。状态也是由游戏更新初始化和销毁的。初始化和销毁每个状态只发生一次,状态更新可以在每个游戏周期中调用一次。

状态更新

状态更新调用流程

渲染帧

游戏循环中的渲染部分负责设置渲染管线。在游戏循环的这一部分中不会运行任何更新或 AI 算法。

曾经有一段时间,开发者对渲染管线有完全控制。开发者可以操纵并设置每一个顶点。现代游戏开发系统与这种渲染系统没有太多关系。图形库负责渲染系统的所有控制。然而,在非常高的层面上,开发者只能设置渲染顶点的顺序和数量。

在帧速率控制方面,渲染是最重要的角色之一,保持其他连续进程的稳定性。从处理的角度来看,显示和内存操作需要最长的执行时间。

典型的 Android 图形渲染遵循 OpenGL 管线:

渲染帧

使用 Android SDK 创建一个示例游戏循环

Android SDK 开发从一个活动开始,游戏在单个或多个视图上运行。大多数情况下,考虑使用单个视图来运行游戏。

不幸的是,Android SDK 没有提供预定义的游戏循环。然而,循环可以以许多种方式创建,但基本机制保持不变。

在 Android SDK 库中,View类包含一个抽象方法OnDraw(),其中每个可能的渲染调用都排队等候。这个方法在绘图发生任何改变时被调用,这会使之前的渲染管线无效。

逻辑如下:

使用 Android SDK 创建一个示例游戏循环

让我们来看一个使用 Android View创建的基本游戏循环。在这里,一个自定义视图是从 Android View扩展而来的:

/*Sample Loop created within OnDraw()on Canvas 
* This loop works with 2D android game development
*/
@Override
public void onDraw(Canvas canvas)
{
  //If the game loop is active then only update and render
  if(gameRunning)
  {
    //update game state
    MainGameUpdate();

    //set rendering pipeline for updated game state
    RenderFrame(canvas);
    //Invalidate previous frame, so that updated pipeline can be
    // rendered
    //Calling invalidate() causes recall of onDraw()
    invalidate();
  }
  else
  {
    //If there is no active game loop
    //Exit the game
    System.exit(0);
  }
}

在当前的 Android 游戏开发时代,开发者使用SurfaceView而不是ViewSurfaceView继承自View,并且更适用于使用 Canvas 制作的游戏。在这种情况下,一个定制视图是从SurfaceView扩展并实现SurfaceHolder.Callback接口。在这种情况下,重写了三种方法:

/* Called When a surface is changed */
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{
}
/* Called on create of a SurfaceView */
@Override
public void surfaceCreated(SurfaceHolder holder)
{
}
/* Called on destroy of a SurfaceView is destroyed */
@Override
public void surfaceDestroyed(SurfaceHolder holder)
{
}

在开发游戏时,开发者不需要每次更改表面。这就是surfaceChanged方法应该有一个空体来作为基本游戏循环的原因。

我们需要创建一个定制的游戏线程并重写run()方法:

public class BaseGameThread extends Thread
{
  private boolean isGameRunning;
  private SurfaceHolder currentHolder;
  private MyGameState currentState;
  public void activateGameThread(SurfaceHolder holder, MyGameState state)
  {
    currentState = state;
    isGameRunning = true;
    currentHolder = holder;
    this.start();
  }

  @Override
  public void run()
  {
    Canvas gameCanvas = null;
    while(isGameRunning)
    {
      //clear canvas
      gameCanvas = null;
      try
      {
        //locking the canvas for screen pixel editing
        gameCanvas  = currentHolder.lockCanvas();
        //Update game state
        currentState.update();
        //render game state
        currentState.render(gameCanvas);
      }
      catch(Exception e)
      {
        //Update game state without rendering (Optional)
        currentState.update();
      }
    }
  }
}

现在,我们准备从定制的SurfaceView类开始新创建的游戏循环:

public myGameCanvas extends SurfaceView implements SurfaceHolder
{
  //Declare thread
  private BaseGameThread gameThread;
  private MyGameState gameState;
  @Override
  public void surfaceCreated(SurfaceHolder holder)
  {
    //Initialize game state
    gameState = new MyGameState();
    //Instantiate game thread
    gameThread = new BaseGameThread();
    //Start game thread
    gameThread. activateGameThread(this.getHolder(),gameState);
  }

  @Override
  public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
  {
  }

  @Override
  public void surfaceDestroyed(SurfaceHolder holder)
{
}
}

实现游戏循环可以有很多方法。然而,基本方法遵循这里提到的两种方式之一。一些开发者更喜欢在游戏视图内部实现游戏线程。处理输入是游戏循环的另一个重要部分。我们将在本章后面讨论这个话题。

游戏循环的另一个部分是每秒帧数FPS)管理。最常见的机制之一是使用Thread.sleep()来计算循环以固定速率执行的时间。一些开发者创建了两种更新机制:一种基于 FPS,另一种基于每帧无延迟。

大多数基于物理的游戏需要一个更新机制,以便在所有设备上以统一的实时间隔运行。

对于小规模开发,行业中有一些开发者采用第一种方法,但不遵循典型的循环。这种系统根据所需的动作使当前绘制无效。在这种情况下,游戏循环不依赖于固定的 FPS。

游戏生命周期

Android 游戏生命周期几乎与任何其他应用程序的生命周期相似,除了游戏循环机制。大多数情况下,应用程序状态会随着外部干扰而改变。状态也可以通过其他方式进行操作,游戏具有能够干扰主游戏循环的算法或人工智能。

Android 游戏是通过活动初始化的。onCreate()方法用于初始化。然后,游戏线程启动并进入游戏循环。游戏循环可以被外部中断打断。

在游戏开发中,始终要保存当前游戏状态并正确暂停循环和线程。在恢复游戏时,应该很容易返回到上一个状态。

游戏更新和用户界面

我们已经在之前涵盖了一些更新和接口机制。运行中的游戏状态可以通过用户输入或内部 AI 算法来改变:

游戏更新和用户界面

大多数情况下,游戏更新每帧调用一次,或者在固定时间间隔后调用一次。无论哪种方式,算法都会改变游戏状态。您已经了解了用户输入队列。在每个游戏循环周期中,都会检查输入队列。

例如,具有触摸界面的移动游戏循环的工作方式如下:

/* import proper view and implement touch listener */
public class MyGameView extends View implements View.OnTouchListener
/* declare game state */
private MyGameState gameState;
/* set listener */
public MyGameView (Context context)
{
  super(context);
  setOnTouchListener(this);
  setFocusableInTouchMode(true);
  gameState = new MyGameState();
}

/* override onTouch() and call state update on individual touch events */
@Override
public boolean onTouch(View v, MotionEvent event) 
{
  if(event.getAction() == MotionEvent.ACTION_UP)
  {
    //call changes in current state on touch release
    gameState.handleInputTouchRelease((int)event.getX(), (int)event.getY());
    return false;
  }
  else if(event.getAction() == MotionEvent.ACTION_DOWN)
  {
    //call changes in current state on touch begin
    gameState.handleInputTouchEngage((int)event.getX(), (int)event.getY());
  }
  else if(event.getAction() == MotionEvent.ACTION_MOVE)
  {
    //call changes in current state on touch drag
    gameState.handleInputTouchDrag((int)event.getX(), (int)event.getY());
  }
  return true;
}

现在,让我们以相同的方式来看待输入队列系统:

Point touchBegin = null;
Point touchDragged = null;
Point touchEnd = null;

@Override
public boolean onTouch(View v, MotionEvent event)
{
  if(event.getAction() == MotionEvent.ACTION_UP)
  {
    touchEnd = new Point(int)event.getX(), (int)event.getY());
    return false;
  }
  else if(event.getAction() == MotionEvent.ACTION_DOWN)
  {
    touchBegin = new Point(int)event.getX(), (int)event.getY());

  }
  else if(event.getAction() == MotionEvent.ACTION_MOVE)
  {
    touchDragged = new Point(int)event.getX(), (int)event.getY());

  }
  return true;
}

/* declare checking input mechanism */
private void checkUserInput() 
{
  if(touchBegin != null)
  {
    //call changes in current state on touch begin
    gameState. handleInputTouchEngage (touchBegin);
    touchBegin = null;
  }

  if(touchDragged != null)
  {
    //call changes in current state on touch drag
    gameState. handleInputTouchDrag (touchDragged);
    touchDragged = null;
  }

  if(touchEnd != null)
{
    //call changes in current state on touch release  
    gameState.handleInputTouchRelease (touchEnd);
    touchEnd = null;
  }
}

/* finally we need to invoke checking inside game loop */
@Override
public void onDraw(Canvas canvas)
{
  //If the game loop is active then only update and render
  if(gameRunning)
  {
    //check user input
    checkUserInput();
    //update game state
    MainGameUpdate();

    //set rendering pipeline for updated game state
    RenderFrame(canvas);
    //Invalidate previous frame, so that updated pipeline can be
    // rendered
    //Calling invalidate() causes recall of onDraw()
    invalidate();
  }
  else
  {
    //If there is no active game loop
    //Exit the game
    System.exit(0);
  }
}

相同的过程也可以用于SurfaceView游戏循环方法。

中断处理

游戏循环是一个持续的过程。每当发生中断时,有必要暂停每个运行的线程并保存游戏的当前状态,以确保它能够正确恢复。

在 Android 中,任何中断都会触发onPause()

@Override
protected void onPause() 
{
  super.onPause();
  // pause and save game loop here
}
// When control is given back to application, then onResume() is // called.
@Override
protected void onResume() 
{
  super.onResume();
  //resume the game loop here
}

现在,我们需要更改实际运行游戏循环的类。

首先,声明一个布尔值来指示游戏是否暂停。然后,在游戏循环中进行检查。之后,创建一个处理这个变量的静态方法:

private static boolean gamePaused = false;
@Override
public void onDraw(Canvas canvas)
{
  if(gameRunning && ! gamePaused)
  {
    MainGameUpdate();
    RenderFrame(canvas);

    invalidate();
  }
  else if(! gamePaused)
  {
    //If there is no active game loop
    //Exit the game
    System.exit(0);
  }
}

public static void enableGameLoop(boolean enable)
{
  gamePaused = enable;
  if(!gamePaused)
  {
    //invalidation of previous draw has to be called from static
    // instance of current View class
    this.invalidate();
  }
  else
  {
    //save state
  }
}

游戏状态机的一般概念

游戏状态机在游戏循环的更新周期内运行。游戏状态机是将所有游戏状态绑定在一起的机制。在旧的技术中,这是典型的线性控制流。然而,在现代开发过程中,它可以是在多个线程中并行运行的控制流。在游戏开发的旧架构中,鼓励只有一个游戏线程。开发人员过去会避免并行处理,因为它容易受到游戏循环和定时器管理的影响。然而,即使在现代开发中,许多开发人员仍然在可能的情况下更喜欢使用单个线程进行游戏开发。借助各种工具和高级脚本语言的帮助,大多数游戏开发人员现在都使用虚拟并行处理系统。

简单游戏状态机的一个过程是创建一个公共状态接口,并为每个游戏状态重写它。这样,在游戏循环内管理状态就变得容易了。

让我们看一个简单的游戏状态机管理器的循环。该管理器应该执行四个主要功能:

  • 创建状态

  • 更新状态

  • 渲染状态

  • 改变状态

示例实现可能如下所示:

public class MainStateManager
{
  private int currentStateId;
  //setting up state IDs
  public Interface GameStates
  {
    public static final int STATE_1 = 0;
    public static final int STATE_2 = 1;
    public static final int STATE_3 = 2;
    public static final int STATE_4 = 3; 
  }

  private void initializeState(int stateId)
  {
    currentStateId = stateId;
    switch(currentStateId)
    {
      case STATE_1:
        // initialize/load state 1
      break;
      case STATE_2:
        // initialize/load state 2
      break;
      case STATE_3:
        // initialize/load state 3
      break;
      case STATE_4:
        // initialize/load state 4
      break;
    }
  }
}
/*
* update is called in every cycle of game loop.
* make sure that the state is already initialized before updating the state
*/
private void updateState()
{
  switch(currentStateId)
  {
    case STATE_1:
      // Update state 1
    break;
    case STATE_2:
      // Update state 2
    break;
    case STATE_3:
      // Update state 3
    break;
    case STATE_4:
      // Update state 4
    break;
  }
}
/*
* render is called in every cycle of game loop.
* make sure that the state is already initialized before updating the state
*/
private void renderState()
{
  switch(currentStateId)
  {
    case STATE_1:
      // Render state 1
    break;
    case STATE_2:
      // Render state 2
    break;
    case STATE_3:
      // Render state 3
    break;
    case STATE_4:
      // Render state 4
    break;
  }
}
/*
* Change state can be triggered from outside of manager or from any other state
* This should be responsible for destroying previous state and free memory and initialize new state
*/
public void changeState(int nextState)
{
  switch(currentStateId)
  {
    case STATE_1:
      // Destroy state 1
    break;
    case STATE_2:
      // Destroy state 2
    break;
    case STATE_3:
      // Destroy state 3
    break;
    case STATE_4:
      // Destroy state 4
    break;
  }
  initializeState(nextState);
}
}

在某些情况下,开发人员还会通过状态管理器将输入信号传递到特定状态。

FPS 系统

在游戏开发和游戏行业中,FPS 非常重要。游戏质量的衡量取决于 FPS 计数。简单来说,游戏的 FPS 越高,越好。游戏的 FPS 取决于指令和渲染的处理时间。

执行游戏循环需要一些时间。让我们看一下游戏循环内 FPS 管理的示例实现:

long startTime;
long endTime;
public final int TARGET_FPS = 60;

@Override
public void onDraw(Canvas canvas)
{
  if(isRunning)
  {
    startTime = System.currentTimeMillis();
    //update and paint in game cycle
    MainGameUpdate();

    //set rendering pipeline for updated game state
    RenderFrame(canvas);

    endTime = System.currentTimeMillis();
    long delta = endTime - startTime;
    long interval = (1000 - delta)/TARGET_FPS;

    try
    {
      Thread.sleep(interval);
    }
    catch(Exception ex)
    {}
    invalidate();
  }
}

在上面的示例中,我们首先记录了循环执行之前的时间(startTime),然后记录了循环执行之后的时间(endTime)。然后我们计算了执行所需的时间(delta)。我们已经知道应该花费多少时间(interval)来保持最大帧率。因此,在剩余的时间里,我们让游戏线程在再次执行之前进入休眠状态。这也可以应用于不同的游戏循环系统。

在使用SurfaceView时,我们可以在run()方法中的游戏循环内声明 FPS 系统:

long startTime;
long endTime;
public final int TARGET_FPS = 60;
@Override
public void run()
{
  Canvas gameCanvas = null;
  while(isGameRunning)
  {
    startTime = System.currentTimeMillis();
    //clear canvas
    gameCanvas = null;
    try
    {
      //locking the canvas for screen pixel editing
      gameCanvas  = currentHolder.lockCanvas();
      //Update game state
      currentState.update();
      //render game state
      currentState.render(gameCanvas);
      endTime = System.currentTimeMillis();
      long delta = endTime - startTime;
      long interval = (1000 - delta)/TARGET_FPS;

      try
      {
        Thread.sleep(interval);
      }
      catch(Exception ex) 
      {}
    }
    Catch(Exception e)
    {
      //Update game state without rendering (Optional)
      currentState.update();
    }
  }
}

在这个过程中,我们限制了 FPS 计数,并尝试在预定义的 FPS 上执行游戏循环。这个系统的一个主要缺点是这种机制在很大程度上取决于硬件配置。对于无法在预定义的 FPS 上运行循环的慢硬件系统,这个系统没有效果。这是因为间隔时间大多为零或小于零,因此没有每帧周期。

硬件依赖

我们之前讨论过硬件配置在 FPS 系统中起着重要作用。如果硬件无法以特定频率运行一组指令,那么任何开发人员都无法在目标 FPS 上运行游戏。

让我们列出游戏中占用大部分处理时间的任务:

  • 显示或渲染

  • 内存加载/卸载操作

  • 逻辑操作

显示或渲染

显示处理主要取决于图形处理器和需要显示的内容。当涉及与硬件的交互时,处理变慢。使用着色器操作和映射渲染每个像素都需要时间。

曾经有时候以 12 帧的帧率运行游戏是困难的。然而,在现代世界,一个出色的显示质量游戏需要以 60 帧的帧率运行。这只是硬件质量的问题。

大屏幕需要大量的缓存内存。例如,硬件具有大而密集的显示屏,但缓存内存较少,无法保持良好的显示质量。

内存加载/卸载操作

内存是系统的硬件组件。再次强调,与内存组件交互需要更多时间。从开发人员的角度来看,当我们分配内存、释放内存和进行读写操作时,需要时间。

从游戏开发的角度来看,有四种类型的内存最重要:

  • 堆内存

  • 堆栈内存

  • 寄存器内存

  • 只读存储器(ROM)

堆内存

堆内存是用户手动管理的内存。这个内存必须手动分配和手动释放。在 Android 的情况下,垃圾收集器负责释放被标记为非引用的内存。这个内存位置是随机存取内存类别中最慢的。

堆栈内存

这段内存用于在方法内声明的元素。程序解释器会自动分配和释放这个内存段。这个内存段仅适用于本地成员。

寄存器内存

寄存器内存是最快的。寄存器内存用于存储当前进程和频繁使用的数据。在寄存器内存更好和更快的设备上,游戏开发人员可以实现更高的帧率。

只读存储器(ROM)

只读存储器(ROM)是永久存储器。特别是在游戏开发中,大量的资源存储在 ROM 中。在加载/卸载这些资源时需要最长的时间。程序需要从 ROM 中将必要的数据加载到 RAM 中。因此,拥有更快的 ROM 有助于在加载/卸载操作期间获得更好的 FPS。

逻辑操作

开发人员应该以最有效的方式定义指令,以便利用硬件。从技术角度来看,每条指令以二进制指令形式进入堆栈。处理器在一个时钟周期内执行一条指令。

例如,让我们看一个构造不好的逻辑指令:

char[] name = "my name is android";
for(int i = 0; i < name.length; i ++)
{
  //some operation
}

每次调用length并使用后增量运算符都会增加处理器的指令,最终增加执行时间。现在,看看这段代码:

char[] name = "my name is android";
int length = name.length;
for(int i = 0; i < length; ++ i)
{
  //some operation
}

这段代码执行了相同的任务;然而,在这种方法中,处理开销大大减少了。这段代码所做的唯一妥协是阻塞了一个整数变量的内存,并保存了与length相关的许多嵌套任务。

时钟速度更快的处理器可以更快地执行任务,这直接意味着更好的 FPS。然而,任务量的管理取决于开发人员,就像前面的例子所示。

每个处理器都有一个数学处理单元。处理器的性能因处理器而异。因此,开发人员总是需要检查数学表达式,以了解它是否可以简化。

性能和内存之间的平衡

正如您之前学到的,内存操作需要很长时间。然而,开发人员总是有限的内存。因此,在性能和内存之间保持平衡是非常必要的。

从 ROM 加载或卸载任何资产到 RAM 都需要时间,因此建议您不要为依赖 FPS 的游戏执行此类操作。这种操作会显著影响 FPS。

假设一个游戏在运行一个游戏状态时需要大量资产,而目标设备可用的堆有限。在这种情况下,开发人员应该对资产进行分组。只有在必要的情况下,才能在运行状态的游戏中加载小资产。

有时,许多开发人员预加载所有资产并从缓存中使用。这种方法使游戏玩起来更加流畅和快速。然而,如果在不需要的特定游戏状态下加载缓存中的资产,可能会在发生中断时导致游戏崩溃。Android 操作系统完全有权清除不活动或最小化应用程序所占用的内存。当发生中断时,游戏进入最小化状态。如果新应用程序需要内存而没有空闲内存可用,那么 Android 操作系统会终止不活动的应用程序并为新应用程序释放内存。

因此,根据游戏状态,将资产集分成部分总是一个很好的做法。

控制 FPS

我们已经看到了一些定义 FPS 系统的方法。我们也已经讨论了该系统的主要缺点。因此,我们可以根据当前游戏循环周期中生成的实时 FPS 来操纵游戏循环:

long startTime;
long endTime;
public static in ACTUAL_FPS = 0;

@Override
public void onDraw(Canvas canvas)
{
  if(isRunning)
  {
    startTime = System.currentTimeMillis();
    //update and paint in game cycle
    MainGameUpdate();

    //set rendering pipeline for updated game state
    RenderFrame(canvas);

    endTime = System.currentTimeMillis();
    long delta = endTime - startTime;
    ACTUAL_FPS = 1000 / delta;
    invalidate();
  }
}

现在,让我们来看一下混合 FPS 系统,我们将最大 FPS 限制为 60。否则,游戏可能会通过实际 FPS 进行操纵:

long startTime;
long endTime;
public final int TARGET_FPS = 60;
public static int ACTUAL_FPS = 0;

@Override
public void onDraw(Canvas canvas)
{
  if(isRunning)
  {
    startTime = System.currentTimeMillis();
    //update and paint in game cycle
    MainGameUpdate();

    //set rendering pipeline for updated game state
    RenderFrame(canvas);

    endTime = System.currentTimeMillis();
    long delta = endTime - startTime;

    //hybrid system begins
    if(delta < 1000)
    {
      long interval = (1000 - delta)/TARGET_FPS;
      ACTUAL_FPS = TARGET_FPS;
      try
      {
        Thread.sleep(interval);
      }
      catch(Exception ex) 
      {}
    }
    else
    {
      ACTUAL_FPS = 1000 / delta;
    }
    invalidate();
  }
}

总结

游戏循环主要是游戏开发的逻辑方法。在许多情况下,开发人员不选择这种机制。有些游戏可能是典型的互动游戏,没有连续运行的算法。在这种情况下,可能不需要游戏循环。游戏状态可以根据输入更新到游戏系统。

然而,例外不能成为例证。这就是为什么遵循游戏循环以保持开发标准是一种工业方法,而不管游戏设计如何。

您在这里了解了游戏循环和游戏状态管理。开发人员可以自由地以不同的方式发明和执行游戏循环。许多游戏引擎具有不同的控制游戏循环和管理游戏状态的方式。游戏循环和状态管理的理念和概念可能会根据游戏需求而改变。

然而,开发人员应始终牢记,他们使用的技术不应影响游戏性能和 FPS。此外,开发人员需要保持代码的可读性和灵活性。某些方法可能会消耗更多内存并运行得更快,反之亦然。Android 具有各种硬件配置,因此可能不是所有硬件上都具有相同的处理和内存支持。最终,在内存和性能之间取得平衡是创造更好游戏的关键。

我们将在后面的章节中深入研究性能和内存管理。我们将尝试从不同的角度来看待游戏开发的这些部分,比如 2D/3D 游戏、VR 游戏、优化技术等。

第六章:提高 2D/3D 游戏性能

曾几何时,移动平台上的游戏局限于黑白像素游戏,其他游戏媒介也严重依赖像素图形。现在时代已经改变。3D 游戏在手持设备上轻松运行。然而,对 2D 资产的需求尚未改变。即使在 hardcore 3D 游戏中,2D 资产也是必需的。很少有游戏是完全 2D 的。

我们将在以下主题的帮助下讨论 2D 和 3D 游戏的性能:

  • 2D 游戏开发的限制

  • 3D 游戏开发的限制

  • Android 的渲染管道

  • 通过 OpenGL 渲染

  • 优化 2D 资产

  • 优化 3D 资产

  • 常见的游戏开发错误

  • 2D/3D 性能比较

2D 游戏开发的限制

从 2D 游戏开发的角度来看,主要的限制如下:

  • 2D 艺术资产

  • 2D 渲染系统

  • 2D 映射

  • 2D 物理

2D 艺术资产

艺术资产的限制主要限于图形或视觉资产,包括图像、精灵和字体。不难理解,较大的资产将比较小的资产需要更多的时间来处理和渲染,从而导致性能质量较低。

一组 2D 艺术资产

在 Android 游戏开发中,不可能通过一组资产提供最大的显示质量。这是大多数 Android 游戏开发人员选择高分辨率资产作为基础构建的原因。这通常对高配置硬件平台表现良好,但在低配置设备上无法提供良好的性能。许多开发人员选择为多分辨率硬件平台进行移植的选项。这再次需要时间来完成项目。

多分辨率使用相同的资产集

许多时候,开发人员选择忽略一组硬件平台。在移动游戏行业中,选择更高分辨率的艺术资产并将其缩小适应较低分辨率设备是一种常见做法。如今,大多数硬件平台都有更好的 RAM。因此,这个过程对开发人员来说变得更加方便。

屏幕上绘制的资产数量

游戏性能并不总是取决于资产大小;它还取决于屏幕上绘制的资产数量。精灵表的概念已经发展,以减少屏幕上绘制元素的数量。

通常,系统会为单个艺术资产发出绘制指令。随着资产数量的增加,需要更多这样的绘制指令才能在每个游戏循环周期内完成渲染。显然,这个过程会减慢处理器的速度,游戏性能变差。

精灵表可以包含单个图像中的多个资产。因此,只需要一个绘制指令就可以渲染精灵的所有资产。然而,精灵表的物理大小是受限制的。不同硬件平台的设备的最大尺寸各不相同。最方便的是,1024x1024 的精灵是目前几乎所有可用设备支持的最安全选项。

使用字体文件

几乎每个游戏都使用除了 Android 默认系统字体之外的自定义或特殊字体。在这些情况下,字体源文件必须包含在游戏构建中。有多种使用不同字体的方法。我们将在这里讨论其中三种:

  • 精灵字体

  • 位图字体

  • TrueType 字体

精灵字体

这是一种典型的老派技术,但在某些情况下仍然有效。开发人员创建一个包含所有必要字符的精灵表。所有字符都在数据文件中映射。这种映射用于裁剪每个字符并相应地形成单词。

这种字体的一些优点包括:

  • 开发人员完全控制映射

  • 字符风格可以根据需求定制

  • 可以实现快速处理速度;但这将取决于开发效率

这种字体的一些缺点包括:

  • 它们增加了开发开销

  • 系统效率完全取决于开发人员的技能

  • 在多语言支持的情况下很难映射字符

  • 任何更改都需要大量迭代才能达到完美

这种样式现在通常不再使用,因为我们有许多设计师和时尚字体可用。

位图字体

位图字体系统是从精灵字体继承而来的。它更新了预定义的映射样式和支持开发过程的库。它还使用一个或多个精灵表和一个数据文件。位图字体的工作原理与精灵字体相同。有很多工具可以直接从 TrueType 字体创建这样的字体,并进行一些样式化。

这种字体的一些优点包括:

  • 它与任何现有的代码库兼容,无论渲染框架是 OpenGL、DirectX、Direct Draw 还是 GDI+

  • 易于集成

  • 它可以操纵现有 TrueType 字体的样式

这种字体的一些缺点包括:

  • 精灵字体的相同缺点在这里也适用,只是开发开销更小

  • 放大位图字体会导致模糊的输出

TrueType 字体

这是大多数平台支持的通用字体格式,包括 Android。这是在游戏中集成各种字体的最快速的方法。

这种字体的一些优点包括:

  • 通用字体样式

  • 最大的平台支持

  • 易于多语言实现

  • 这是一种矢量字体,所以它没有缩放问题

  • 易于获得特殊字符

这种字体的一些缺点包括:

  • 使用这种字体样式可能会使游戏多花费几千字节

  • 并非所有脚本语言都受 TTF 支持

2D 渲染系统

Android 提供了通过 API 框架将 2D 资产渲染到画布的范围。画布可以与ViewSurfaceView中的Drawable对象一起使用。

画布充当实际绘图表面的接口,所有的图形对象都可以在其上绘制。在onDraw()回调方法中绘制画布上的图形对象。开发人员只需指定图形对象及其在画布上的位置。

画布本身具有一组默认的绘图方法,可以渲染几乎每种类型的图形对象。以下是一些例子:

  • drawBitmap()方法用于以位图格式绘制图像对象。但是,图像不需要以位图格式。

  • drawRect()drawLine()方法用于在画布上绘制原始形状。

  • drawText()方法可用于使用特定字体样式在画布上渲染文本。

画布可以在 Android 架构中的视图中使用。

2D 映射

2D 映射基于简单的 2D 坐标系。唯一的区别是与传统坐标系相比相反的y轴:

2D 映射

在 Android 2D 中,原点位于画布的左上角。所有的几何计算都是基于这种映射的。然而,它对性能没有直接影响,就像基于 2D 画布的应用程序一样。许多开发人员习惯于根据传统系统映射他们的图形资产,并颠倒垂直轴以在画布上渲染它。这需要一些额外的计算。

关于 2D 渲染系统还有一个性能约束。全球通用的开发方法是拥有一组最小的图形资产,并尽可能多地使用它们。经常会导致同一像素多次渲染。这会影响处理速度,从而影响 FPS。

例如,位图A、位图B、位图C和位图D以一种使ABC重叠在一起,而D保持独立的方式在画布上呈现。以下情况发生:

  • 在只绘制一个位图的区域R0中的像素将被渲染一次

  • 在两个位图重叠的区域R1中的像素将被渲染两次

  • 在区域R2中,三个位图重叠的像素将被渲染三次

这在这里显示:

2D 映射

现在,在区域R1R2,所有像素都被渲染多次。在这个系统中,像素数据信息将附加到先前的数据上,导致最终像素值。在这个系统中,处理开销增加。因此,性能下降。

即使在今天,这仍然是 2D 游戏编程的常见做法。原因如下:

  • 透明度混合

  • 模块化图形资源

  • 低构建大小

  • 通过叠加多个资源轻松构建屏幕

有时,可能会出现设备的图形处理器性能非常低,多次渲染同一个像素对性能有很大影响。在这种情况下,双缓冲机制会有很大帮助。

双缓冲系统是指创建一个缓冲的可显示资源,使用图形资源创建显示屏,然后只在屏幕上绘制一次这个缓冲对象。它可以防止以下问题:

  • 屏幕闪烁

  • 一个像素的多次绘制

  • 资源的撕裂

2D 物理

2D 物理只考虑x-y平面上的所有计算。市场上有很多 2D 物理引擎可用。Box2D是最流行的一个。物理引擎包括实时物理的每一个机制和计算。

实时物理计算比游戏所需的要复杂得多。让我们讨论一下一些可用的物理引擎。

Box2D

Box2D 是一个基于 C++的开源物理引擎。它包括几乎可以用于各种游戏的固体物理的每一个方面。它的一些值得一提的特性如下:

  • 刚体的动态碰撞检测

  • 碰撞状态回调,如碰撞进入、退出、停留等

  • 多边形碰撞

  • 垂直、水平和抛射运动

  • 摩擦物理

  • 扭矩和动量物理

  • 基于枢轴点和关节的重力效应

LiquidFun

LiquidFun 是一个液体物理的物理引擎。这个引擎实际上是基于 Box2D 的。谷歌发布了这个开源物理引擎来覆盖液体物理的公式和机制。LiquidFun 可以用于 Android、iOS、Windows 和其他一些流行的平台。LiquidFun 支持 Box2D 的每一个特性,以及液体粒子物理。这包括以下内容:

  • 波浪模拟

  • 液体下落和粒子模拟

  • 液体搅拌模拟

  • 固体和液体动态碰撞

  • 液体混合

对游戏性能的影响

碰撞检测是一个昂贵的过程。多边缘和多边形碰撞会增加处理开销。刚体和碰撞表面的数量对性能有最大的影响。这就是为什么液体物理比固体物理慢的原因。

让我们来看一下主要影响:

  • 任何刚体的每次变换都需要刷新整个系统的碰撞检查

  • 物理引擎负责重复的变换,这导致了繁重的过程

物理引擎计算刚体上的每一个可能的力。并不是所有的游戏都需要进行每一项计算。游戏开发并不总是需要实时实现物理。然而,游戏需要实时可视化。

2D 碰撞检测

大多数游戏使用盒子碰撞系统来检测大多数碰撞。矩形碰撞检测是最便宜的方法,可以在游戏内用于检测碰撞。

有时,三角形和圆形碰撞检测也用于 2D 游戏以提高碰撞检测的准确性。需要在使用这些方法之间取得平衡。

例如,如果我们需要检测两个圆之间的碰撞,我们可以选择以下任何一个系统:

  • 将每个圆视为矩形并检测它们之间的碰撞

  • 将一个圆视为矩形,并检测圆和矩形之间的碰撞

  • 应用实际的圆形碰撞检测方法

让我们考虑两个圆,它们的原点分别是O1O2,直径分别是R1R2

O1位于(Ox1, Oy1)

O2位于(Ox2, Oy2)

矩形碰撞

如果我们想象圆在 2D 画布上是矩形,那么它会是这样的:

矩形碰撞

矩形碰撞检测指的是这个公式。

输入反馈将如下:

xMin1 = x1(第一个矩形在x轴上的最小坐标)

yMin1 = y1(第一个矩形在y轴上的最小坐标)

xMax1 = x1m(第一个矩形在x轴上的最大坐标)

yMax1 = y1m(第一个矩形在y轴上的最大坐标)

xMin2 = x2(第二个矩形在x轴上的最小坐标)

yMin2 = y2(第二个矩形在y轴上的最小坐标)

xMax2 = x2m(第二个矩形在x轴上的最大坐标)

yMax2 = y2m(第二个矩形在y轴上的最大坐标)

在给定的情况下,我们将有以下情况:

x1 = Ox1 – (R1 / 2)

y1 = Oy1 – (R1 / 2)

x1m = Ox1 + (R1 / 2) = x1 + R1

y1m = Oy1 + (R1 / 2) = y1 + R1

x2 = Ox2 – (R2 / 2)

y2 = Oy2 – (R2 / 2)

x2m = Ox2 + (R2 / 2) = x2 + R2

y2m = Oy2 + (R2 / 2) = y2 + R2

这两个矩形是否碰撞的条件如下:

if( x1m < x2 )
{
  // Not Collide
}
else if( y1m < y2 )
{
  // Not collide
}
else if( x1 > x2m )
{
  //Not collide
}
else if( y1 > y2m )
{
  //Not collide
}
else
{
  //Successfully collide 
}

矩形和圆的碰撞

现在,只考虑第二个圆作为矩形,我们会得到这样:

矩形和圆的碰撞

由于我们已经讨论了相同系统的坐标系的一般概念,我们可以直接推导出数值:

Px1 = Ox2 – (R2 / 2)

Py1 = Oy2 – (R2 / 2)

Px2 = Ox2 – (R2 / 2)

Py2 = Oy2 + (R2 / 2)

Px3 = Ox2 + (R2 / 2)

Py3 = Oy2 + (R2 / 2)

Px4 = Ox2 + (R2 / 2)

Py4 = Oy2 – (R2 / 2)

x2m = Ox2 + (R2 / 2) = x2 + R2

y2m = Oy2 + (R2 / 2) = y2 + R2

radius1 = (R1 / 2)

distanceP1 = squareRoot(((Px1 – Ox1) (Px1 – Ox1)) + ((Py1 – Oy1)* (Py1 – Oy1)))*

distanceP2 = squareRoot(((Px2 – Ox1) (Px2 – Ox1)) + ((Py2 – Oy1)* (Py2 – Oy1)))*

distanceP3 = squareRoot(((Px3 – Ox1) (Px3 – Ox1)) + ((Py3 – Oy1)* (Py3 – Oy1)))*

distanceP4 = squareRoot(((Px4 – Ox1) (Px4 – Ox1)) + ((Py4 – Oy1)* (Py4 – Oy1)))*

碰撞和非碰撞条件如下:

if ( (Ox1 + radius1) < x2 )
{
  //Not collide
}
else if ( Ox1 > x2m )
{
  //Not collide
}
else if ( (Oy1 + radius1) < y2 )
{
  //Not collide
}
else if ( Oy1 > y2m )
{
  //Not collide
}
else 
{
if (distanceP1 <= radius1)
{
  //Successfully collide
}
else if (distanceP2 <= radius1)
{
  //Successfully collide
}
else if (distanceP3 <= radius1)
{
  //Successfully collide
}
else if (distanceP4 <= radius1)
{
  //Successfully collide
}
else if ( Ox1 >= Px1 && Ox1 <= x2m &&
(Oy1 + radius1) >= Py1 && (Oy1 <= y2m))
{
  //Successfully collide
}
else if ( Oy1 >= Py1 && Oy1 <= y2m &&
(Ox1 + radius1) >= Px1 && (Ox1 <= x2m))
{
  //Successfully collide
}
else
{
  //Not collide
}

圆和圆的碰撞

最后,实际的碰撞检测系统是圆和圆的碰撞:

圆和圆的碰撞

从逻辑上讲,这是找出圆形碰撞的最简单的方法。

首先,计算两个圆的原点之间的距离:

originDistance = squareRoot ( ((Ox2 – Ox1) (Ox2 – Ox1)) + ((Ox2 – Ox1)* (Ox2 – Ox1)))*

现在,我们需要检查距离是否小于或等于两个圆的半径之和:

if (originDistance <= ((R1 + R2) / 2))
{
  //Successfully Collide
}
else
{
  //Not Collide
}

性能比较

对于第一种方法,执行检查需要最少的时钟周期。然而,它并不那么准确。特别是当开发人员使用更大的圆时,缺乏准确性就会变得明显。

第三种方法是完全准确的,但处理时间更长。在运行时有许多圆相互碰撞的情况下,这个过程和数学计算可能会导致性能延迟。

总体而言,第二种方法是解决这个问题的最糟糕的方式。然而,在非常特定的情况下,可以使用这种方法。当开发人员想要准确检测圆和矩形的碰撞时,只有这种方法才能尝试。

检测这种类型的碰撞可能有多种解决方案。在性能方面,您在这里学到的方法和解决方案是最有效的几种解决方案之一。

在准确检测矩形和圆形碰撞时,还有一种更流行的方法,即通过增加圆的直径来创建一个更大的圆角矩形,增加宽度和高度。这个过程更重,但更准确。

3D 游戏开发的限制

Android 本地的 3D 游戏开发非常复杂。Android 框架不支持直接的 3D 游戏开发平台。Android Canvas 直接支持 2D 游戏开发。开发人员需要 OpenGL 支持才能为 Android 开发 3D 游戏。

开发受到 Android NDK 的支持,它基于 C++。我们将讨论一下在 Android 上使用 OpenGL 支持的 3D 开发的一些限制。

Android 提供了 OpenGL 库进行开发。开发人员需要首先设置场景、光线和摄像机才能开始任何开发过程。

顶点和三角形

顶点指的是 3D 空间中的一个点。在 Android 中,Vector3可以用来定义顶点。三角形由三个这样的顶点组成。任何三角形都可以投影到一个 2D 平面上。任何 3D 对象都可以简化为围绕其表面的三角形集合。

例如,一个立方体表面是两个三角形的集合。因此,一个立方体可以由 12 个三角形组成,因为它有六个表面。三角形的数量对渲染时间有很大影响。

3D 变换矩阵

每个 3D 对象都有自己的变换。Vector可以用来指示其位置、缩放和旋转。通常,这通过一个称为变换矩阵的矩阵来表示。变换矩阵的维度为 4 x 4。

让我们假设矩阵为T

3D 变换矩阵

这里:

  • {a, b, c, e, f, g, i, j, k}代表线性变换

  • {d, h, l}代表透视变换

  • {m, n, o}代表绕xyz轴的平移

  • {a, f, k}代表沿xyz轴的局部缩放

  • {p}代表整体缩放

  • {f, g, i, k}代表绕x轴的旋转,其中a = 1

  • {a, c, i, k}代表绕y轴的旋转,其中f = 1

  • {a, b, e, f}代表绕z轴的旋转,其中k = 1

任何 3D 对象都可以使用这个矩阵和相应的变换 3D 向量进行平移。自然地,矩阵计算比 2D 简单线性计算更重。随着顶点数量的增加,计算数量也会增加。这会导致性能下降。

3D 对象和多边形数量

任何 3D 模型或对象都有被称为多边形的表面。较少的多边形意味着较少的三角形,这直接减少了顶点数量:

3D 对象和多边形数量

这是一个简单的多边形分布的 3D 对象表面的例子。一个六边形有四个三角形和六个顶点。每个顶点都是一个 3D 向量。每个处理器都需要时间来处理每个顶点。建议您检查每个绘制周期中将绘制的总多边形数量。许多游戏因多边形数量过高和无法管理而遭受显著的 FPS 下降。

Android 是专门针对移动操作系统的。大多数情况下,它具有有限的设备配置。通常,管理 Android 上 3D 游戏的多边形数量成为开发人员的问题。

3D 渲染系统

Android 使用 OpenGL 提供了一个 3D 渲染平台,包括框架和 NDK。Android 框架提供了GLSurfaceViewGLSurfaceView.Renderer来渲染 Android 中的 3D 对象。它们负责在屏幕上生成模型。我们已经通过 OpenGL 讨论了 3D 渲染管线。

3D 渲染将所有对象映射到一个 3D 世界坐标系,遵循右手拇指系统:

3D 渲染系统

3D 网格

3D 网格是由顶点、三角形和表面创建的。网格用于确定物体的形状。纹理被应用到网格上以创建完整的模型。

创建网格是 3D 模型创建中最棘手的部分,因为基本的优化可以在这里应用。

以下是创建网格的过程:

3D 网格

一个 3D 模型可以包含多个网格,它们甚至可以互换。网格负责模型的细节质量和渲染性能。对于 Android 开发,建议对网格的顶点和三角形数量保持一定限制,以提高渲染性能。

材料、着色器和纹理

在通过网格形成模型结构后,纹理被应用于其上,以创建最终模型。然而,纹理是通过材质应用并由着色器操纵的:

材料、着色器和纹理

纹理

纹理是应用于模型的 2D 图像,以增加模型的细节和视图质量。这个图像被映射到网格的表面上,以便每个表面渲染纹理的特定部分。

着色器

着色器用于操纵纹理的质量、颜色和其他属性,使其更加逼真。大多数情况下,不可能创建具有所有属性正确设置的纹理。3D 模型的可见性取决于光源、强度、颜色和材质类型。

材料

材料确定纹理属性和着色器属性。在将其应用于网格以创建模型之前,材料可以被称为着色器和纹理的容器。

碰撞检测

3D Android 游戏的碰撞检测可以分为两种类型:

  • 原始碰撞器

  • 网格碰撞器

原始碰撞器

这些碰撞器由立方体、球体、圆柱体、棱柱体等基本的 3D 元素组成。这种碰撞检测系统遵循一定的几何模式和规则。这就是为什么它相对于任意网格碰撞器来说比较简单的原因。

大多数情况下,开发人员会为许多模型分配原始碰撞器,以提高游戏的性能。这种方法显然不如实际的碰撞器精确。

网格碰撞器

网格碰撞器可以检测实际的任意碰撞检测。这种碰撞检测技术处理开销很大。有一些算法可以减少处理开销。四叉树、kd 树和 AABB 树是这种碰撞检测技术的几个例子。然而,它们并不能显著减少 CPU 开销。

最古老但最准确的方法是对每个表面进行三角形到三角形的碰撞检测。为了简化这种方法,每个网格块都被转换为盒子。生成特殊的 AABB 树或四叉树以减少顶点检查。

这可以通过合并两个盒子碰撞器来进一步减少到八叉树顶点映射。通过这种方式,开发人员可以减少碰撞检查以减少 CPU 开销。

射线投射

射线投射是一种几何系统,用于检测 3D 图形对象的表面。这个系统用于解决 3D 计算机图形的几何问题。在 3D 游戏中,所有 3D 对象都被投影到 2D 视图中。在 2D 电子显示器的情况下,没有射线投射是无法确定深度的:

射线投射

从原点发射的每条射线都可以检测不同对象上的形状、距离、碰撞检测、旋转和缩放等信息。

在 Android 游戏中,射线投射被广泛用于处理屏幕上的触摸输入。大多数游戏使用这种方法来操纵游戏中使用的 3D 对象的行为。

从开发性能的角度来看,射线投射是一个在大规模使用时成本相当高的系统。这需要一系列几何计算,导致处理开销。随着射线数量的增加,处理过程变得更加繁重。

在一个点上使用多个射线投射时,始终要控制好。

“世界”的概念

3D 游戏中的“世界”是实际世界的实时模拟,具有区域限制。世界是用 3D 模型创建的,这些模型指的是现实世界中的实际物体。游戏世界的范围是有限的。这个世界遵循特定的比例、位置和旋转,以及相应的摄像机。

摄像机的概念对于模拟这样一个世界是必不可少的。可以使用多个摄像机来渲染同一个世界的不同视角。

在游戏行业中,游戏世界是根据需求创建的。这意味着不同游戏的世界是不同的。但是一些参数保持不变。这些参数如下:

  • 有限元素

  • 光源

  • 相机

游戏世界的元素

世界由游戏设计中所需的元素组成。每个游戏可能需要不同的元素。然而,跨游戏的两个共同点是天空和地形。大多数元素通常放置在地形上,光源在天空中。然而,许多游戏在游戏的不同范围内提供不同的光源。

元素可以分为两类:可移动对象和静态对象。游戏的刚体与这些元素相关联。通常,静态对象不支持运动物理学。

对世界中的对象进行优化对性能至关重要。每个对象都有一定数量的顶点和三角形。我们已经讨论了 3D 对象顶点的处理开销。一般来说,世界优化基本上是对世界中每个元素的优化。

游戏世界中的光源

游戏世界必须有一个或多个光源。光源用于暴露世界中的元素。多个光源对用户体验有很大的视觉影响。

游戏开发过程总是需要至少一个优秀的光照艺术家。现代游戏使用光照图来增强视觉质量。游戏世界中的光与影是完全依赖于光照图的。

毫无疑问,光是游戏世界中的必要元素。然而,处理光和阴影的后果是大量的处理。所有顶点都需要根据特定的着色器处理光源。使用大量光源会导致性能低下。

光源可以是以下类型:

  • 区域光

  • 聚光灯

  • 点光源

  • 定向光

  • 环境光

  • 体积光

区域光

这种光源用于照亮矩形或圆形区域。本质上,它是定向光,以相等的强度照亮区域:

游戏世界中的光源

聚光灯

聚光灯用于以圆锥形方向聚焦于特定对象:

游戏世界中的光源

点光源

点光源照亮光源的所有方向。一个典型的例子是灯泡的照明:

游戏世界中的光源

定向光

定向光是投射在 3D 世界中某个地方的一组平行光束。一个典型的例子是阳光:

游戏世界中的光源

环境光

环境光是任意方向上的一组光束。通常,这种光源的强度较低。由于光束没有特定的方向,并且不会产生任何阴影:

游戏世界中的光源

L1L2L3L4在这里是环境光源。

体积光

体积光是修改后的点光源类型。这种光源可以转换为在定义的几何形状内的一组光束。任何光束都是这种光源的完美例子:

游戏世界中的光源

游戏世界中的相机

相机是游戏世界中最后但最重要的元素。相机负责渲染游戏屏幕。它还确定要添加到渲染管线中的元素。

游戏中使用的相机有两种类型。

透视相机

这种类型的相机通常用于渲染 3D 对象。可见的比例和深度完全取决于这种类型的相机。开发人员通过操纵视野和近/远范围来控制渲染管线:

游戏世界中的相机

正交相机

这种类型的相机用于从 2D 透视渲染对象,而不考虑对象。正交相机在同一平面上渲染对象,而不考虑深度。开发人员操纵相机的有效宽度和高度来控制 2D 渲染管线。这种相机通常用于 2D 游戏和在 3D 游戏中渲染 2D 对象:

游戏世界中的相机

除此之外,游戏相机也可以根据其性质和目的进行分类。以下是最常见的变化。

固定相机

固定相机在执行过程中不会旋转、平移或缩放。通常,2D 游戏使用这样的相机。固定相机在处理速度方面是最方便的相机。固定相机没有任何运行时操作。

旋转相机

这种相机在运行时具有旋转功能。这种相机类型在体育模拟或监控模拟游戏中非常有效。

移动相机

当平移在运行时可以改变时,相机可以被称为移动。这种类型的相机通常用于游戏的俯视图。这种相机的典型用途是像《帝国时代》、《英雄公司》、《部落冲突》等游戏。

第三人称相机

这种相机主要是游戏设计的一部分。这是一个移动相机,但这个相机跟随特定的对象或角色。角色应该是用户角色,因此所有的动作和移动都由这个相机跟踪,包括角色和对象。大多数情况下,这个相机可以根据玩家的动作进行旋转或推动。

第一人称相机

当玩家扮演主角时,这个相机用于实现玩家眼睛的典型视图。相机根据玩家的动作移动或平移。

Android 中的渲染管线

现在让我们来看看 Android 中的渲染管线类型。

2D 渲染管线

在 Android 的 2D 绘图系统中,所有资产首先绘制在画布上,然后画布被渲染在屏幕上。图形引擎根据给定位置在有限的画布上映射所有资产。

通常,开发人员单独使用小资产,导致每个资产执行映射指令。建议您尽可能使用精灵表来合并尽可能多的小资产。然后可以应用单个绘制调用来在画布上绘制每个对象。

现在,问题是如何创建精灵以及其他后果是什么。以前,Android 不能支持尺寸超过 1024 x 1024 像素的图像或精灵。自 Android 2.3 以来,开发人员可以使用 4096 x 4096 的精灵。然而,使用这样的精灵可能会导致所有小资产的永久内存占用。许多低配置的 Android 设备不支持在应用程序加载期间加载如此大的图像。最佳做法是开发人员限制自己在 2048 x 2048 像素。这将减少内存使用峰值,以及对画布的大量绘制调用。

3D 渲染管线

Android 使用 OpenGL 在屏幕上渲染资产。因此,Android 3D 的渲染管线基本上是 OpenGL 管线。

让我们来看看 OpenGL 渲染系统:

3D 渲染管线

现在,让我们详细看一下前述渲染流程图的每个步骤:

  1. 顶点着色器处理具有顶点数据的单个顶点。

  2. 控制着色器负责控制顶点数据和镶嵌的补丁。

  3. 多边形排列系统使用由顶点创建的每对相交线排列多边形。因此,它创建边缘而不重复顶点。

  4. 镶嵌是在不重叠或有任何间隙的情况下对多边形进行平铺的过程。

  5. 几何着色器负责优化原始形状。因此会生成三角形。

  6. 在构建多边形和形状之后,模型被裁剪以进行优化。

  7. 顶点后处理用于过滤掉不必要的数据。

  8. 然后对网格进行光栅化。

  9. 片段着色器用于处理光栅化生成的片段。

  10. 所有像素在分割后都经过映射,并使用处理后的数据进行处理。

  11. 网格被添加到帧缓冲区进行最终渲染。

优化 2D 资源

任何数字游戏都无法在没有 2D 艺术资源的情况下制作。游戏内必须以某种形式存在 2D 资源。因此,就游戏组件优化而言,每个 2D 资源也应该被优化。优化 2D 资源意味着这三个主要方面。

大小优化

每个资产帧应该只包含在游戏中使用的有效像素。不必要的像素会增加资产大小,并在运行时增加内存使用。

数据优化

并非所有图像都需要像素的完整数据信息。根据图像格式,每个像素可能存储大量数据。例如,全屏不透明图像永远不应该包含透明数据。同样,根据颜色集,图像必须以 8 位、16 位或 24 位格式进行格式化。

图像优化工具可用于执行此类优化。

流程优化

在优化期间压缩的数据量越大,解压缩和加载到内存所需的时间就越长。因此,图像优化直接影响处理速度。

从另一个角度来看,创建图像图集或精灵表是减少图像处理时间的另一种方法。

优化 3D 资源

3D 艺术资源有两个部分需要优化。 2D 纹理部分需要以相同的 2D 优化风格进行优化。开发人员需要考虑的唯一一件事是,在优化后,着色器对结构应该有相同的效果。

其余的 3D 资产优化完全取决于顶点数量和模型多边形。

限制多边形数量

很明显,使用大量多边形来创建网格可以创建更多细节。但是,我们都知道 Android 是一个移动操作系统,它总是有硬件限制。

开发人员应该计算网格中使用的多边形数量以及在单个绘制周期内屏幕上呈现的多边形总数。根据硬件配置,总会有限制。

因此,限制每个网格的多边形和顶点数量总是有利于实现特定的帧速率或性能。

模型优化

模型是用多个网格创建的。在最终模型中使用单独的网格总是会导致大量处理。这对游戏艺术家来说是一项重大工作。如果使用多个网格,可能会发生多重重叠。这会增加顶点处理。

绑定是最终确定模型的另一个重要部分。一个好的绑定者会为最少的处理定义骨骼。

常见的游戏开发错误

在每个开发阶段都不可能查看每个性能方面。在临时模式下使用资产和编写代码,并在最终游戏中使用它是一种非常常见的做法。

这会影响整体性能和未来的维护程序。以下是在游戏开发过程中常见的一些错误。

使用未优化的图像

艺术家创建艺术资产,开发人员直接将其集成到游戏中进行调试构建。然而,大部分时间,这些资产甚至没有针对发布候选版本进行优化。

这就是为什么可能会有大量高位图像,其中资产包含有限的信息。Alpha 信息可能存在于不透明图像中。

使用完整的实用第三方库

现代开发风格不要求每个开发模块都从头开始编写。大多数开发人员使用预定义的第三方库来进行常见的实用机制。

大多数情况下,这些包含大部分可能方法的包,而其中很少有实际在游戏中使用。开发人员大多数情况下在没有任何过滤的情况下使用这些包。在这种情况下,大量未使用的数据会在运行时占用内存。

通常,第三方库没有编辑功能。在这种情况下,开发人员应根据其特定需求非常谨慎地选择这些包。

使用未经管理的网络连接

在现代安卓游戏中,使用互联网连接非常普遍。许多游戏使用基于服务器的游戏玩法。在这种情况下,整个游戏在服务器上运行,并且服务器和客户端设备之间频繁传输数据。每个数据传输过程都需要时间,连接会显著消耗电池电量。

糟糕管理的网络状态经常会冻结应用程序。特别是对于实时多人游戏,处理大量数据。在这种情况下,应该创建并正确管理请求和响应队列。然而,开发人员经常跳过这一部分以节省开发时间。

未经管理的连接的另一个方面是服务器和客户端之间不必要的数据包传输。因此,每次传输数据时都涉及额外的解析过程。

使用次标准的编程

我们已经讨论了编程风格和标准。模块化编程方法可能会增加一些额外的过程,但长期管理编程需要模块化编程。否则,开发人员最终会重复代码,这会增加过程开销。

内存管理也需要良好的编程风格。在一些情况下,开发人员分配内存但经常忘记释放内存。这会导致大量内存泄漏。有时,应用程序由于内存不足而崩溃。

次标准的编程包括以下错误:

  • 多次声明相同的变量

  • 创建许多静态实例

  • 编写非模块化的编码

  • 不正确的单例类创建

  • 运行时加载对象

采取捷径

这是不良开发风格中最有趣的事实。在开发过程中采取捷径在游戏开发人员中非常普遍。

制作游戏主要是逻辑开发。解决逻辑问题可能有多种方法。开发人员经常选择最方便的方式来解决这些问题。例如,开发人员大多数情况下使用冒泡排序方法来满足大部分排序需求,尽管知道这是最低效的排序过程。

在游戏中多次使用这样的捷径可能会导致可见的进程延迟,直接影响帧率。

2D/3D 性能比较

2D 和 3D 的安卓游戏开发是不同的。事实上,3D 游戏处理比 2D 游戏更加繁重。然而,游戏规模始终是决定因素。

不同的外观和感觉

3D 的外观和感觉与 2D 完全不同。在 3D 游戏中使用粒子系统来提供视觉效果非常普遍。在 2D 游戏中,使用精灵动画和其他转换来展示这些效果。

2D 和 3D 外观的另一个区别是动态光和阴影。动态光始终是更高视觉质量的一个因素。如今,大多数 3D 游戏使用动态光照,这对游戏性能有显著影响。在 2D 游戏中,光管理是通过资源完成的。因此,在 2D 游戏中没有额外的光和阴影处理。

在 2D 游戏中,游戏屏幕是在画布上渲染的。只有一个固定的视角。因此,摄像头的概念局限于固定摄像头。然而,在 3D 游戏中,情况就不同了。可以实现多种类型的摄像头。可以同时使用多个摄像头以获得更好的游戏体验。通过多个摄像头渲染对象会导致更多的处理开销。因此,它会降低游戏的帧率。

在使用 2D 物理和 3D 物理之间存在显著的性能差异。3D 物理引擎比 2D 物理引擎要重得多。

3D 处理比 2D 处理要重得多

在游戏行业中,接受 3D 游戏的 FPS 比 2D 游戏要少是一种常见做法。在 Android 中,2D 游戏的标准接受的 FPS 约为 60 FPS,而 3D 游戏即使以低至 40 FPS 的速度运行也是可以接受的。

这背后的逻辑原因是,从处理的角度来看,3D 游戏比 2D 游戏要重得多。主要原因如下:

  • 顶点处理:在 3D 游戏中,每个顶点在渲染过程中在 OpenGL 层上进行处理。因此,增加顶点数量会导致更重的处理。

  • 网格渲染:网格由多个顶点和许多多边形组成。处理网格会增加渲染开销。

  • 3D 碰撞系统:3D 动态碰撞检测系统要求对每个碰撞体的顶点进行计算以进行碰撞。这个计算通常由 GPU 完成。

  • 3D 物理实现:3D 变换计算完全取决于矩阵操作,这总是很重的。

  • 多摄像头使用:使用多个摄像头并动态设置渲染管线需要更多的内存和时钟周期。

设备配置

Android 平台支持各种设备配置选项。在前几章中,我们已经看到了这样的变化。在不同配置上运行相同的游戏不会产生相同的结果。

性能取决于以下因素。

处理器

在 Android 设备中使用了许多处理器,涉及核心数量和每个核心速度。速度决定了在单个周期内可以执行的指令数量。曾经有一段时间,Android 使用的是速度低于 500 MHz 的单核 CPU。现在我们有了每个核心超过 2 GHz 速度的多核 CPU。

RAM

可用 RAM 是决定性能的另一个因素。重型游戏在运行时需要更多的 RAM。如果 RAM 有限,频繁的加载/卸载过程会影响性能。

GPU

GPU 决定了渲染速度。它充当了图形对象的处理单元。更强大的处理器可以处理更多的渲染指令,从而提高性能。

显示质量

显示质量实际上与性能成反比。更好的显示质量必须由更好的 GPU、CPU 和 RAM 支持,因为更好的显示总是由更大的分辨率、更好的 dpi 和更多的颜色支持组成。

我们可以看到各种不同显示质量的设备。Android 本身已经根据这个特性划分了资源:

  • LDPI:Android 的最低 dpi 显示(~120 dpi)

  • MDPI:Android 的中等 dpi 显示(~160 dpi)

  • HDPI:Android 的高 dpi 显示(~240 dpi)

  • XHDPI:Android 的额外高 dpi 显示(~320 dpi)

  • XXHDPI:Android 的额外额外高 dpi 显示(~480 dpi)

  • XXXHDPI:Android 的额外额外额外高 dpi 显示(~640 dpi)

可以很容易地预测,随着硬件技术的进步,列表将在不久的将来包含更多选项。

电池容量

电池容量是应用性能中的一个奇怪因素。更强大的 CPU、GPU 和 RAM 需要更多的电力。如果电池无法提供电力,那么处理单元就无法以最高效率运行。

总结这些因素,我们可以很容易地用性能做出一些关系方程:

  • CPU 与性能成正比

  • GPU 与性能成正比

  • RAM 与性能成正比

  • 显示质量与性能成反比

  • 电池容量与性能成正比

总结

随着更高质量和性能的 3D 游戏范围不断扩大。然而,这需要硬件支持运行 Android 平台。旧设备尚未过时。

当同一应用在不同设备上运行时,这就成为一个严重的问题。这对开发人员来说是一个挑战,要在不同设备上运行同一应用。

在渲染、处理和资产方面,2D 和 3D 游戏之间存在许多技术差异。开发人员应始终使用优化的方法来创建资产和编写代码。另一种提高性能的方法是为 2D 和 3D 游戏的不同硬件系统移植游戏。

自上个十年以来,硬件平台已经有了革命性的升级。相应地,游戏的性质也发生了变化。然而,2D 游戏的范围仍然存在着大量的可能性。

有许多用于开发 2D 和 3D 游戏的框架和引擎。对多个操作系统的支持也增加了对 2D 和 3D 游戏的价值。

提高性能更多是一个逻辑任务,而不是技术任务。有一些可用的工具来完成这项工作,但选择权在开发人员手中。因此,选择合适的工具是必要的,制作 2D 和 3D 游戏应该有不同的方法。

我们已经讨论了 2D 和 3D 开发中的渲染过程。我们将在本书的后面进一步利用 Android 中的着色器来增强渲染,并尝试探索优化 Android 游戏的各种技术。

第七章:使用着色器

每个游戏的成功在很大程度上取决于其外观和感觉。这直接意味着游戏必须具有引人注目的图形显示。由于空间和堆限制,通常无法提供最高质量的图形资产。因此,必须有一种方法在运行时创建或改进图形资产以供显示。这种必要性催生了着色器的概念。

着色器可以操作任何可视元素,并在渲染之前调整可绘制元素的每个像素。大多数情况下,着色器针对特定的图形处理器进行了优化。然而,如今,着色器可以编写以支持多个平台上的多个处理器。

Android 在 Android 框架本身中提供了使用着色器的选项。此外,还可以使用 OpenGL 着色器,并借助 Android NDK 进行自定义。有许多场合,借助着色器可以在没有优秀原始艺术资产的情况下提供精美的图形质量。

我们将从 Android 游戏开发的角度讨论着色器,涵盖以下主题:

  • 着色器介绍

  • 着色器的工作原理

  • 着色器的类型

  • Android 库着色器

  • 编写自定义着色器

  • 通过 OpenGL 的着色器

  • 游戏中的着色器使用

  • 着色器和游戏性能

着色器介绍

许多开发人员在 Android 上开发游戏,但对着色器了解不多。在大多数情况下,开发人员不需要使用着色器,或者在游戏开发框架或引擎中有一些预定义的着色器。

1988 年,动画工作室 Pixar 引入了现代着色器概念。然而,当时的 GPU 无法处理着色器。OpenGL 和 Direct3D 是支持着色器的第一批图形库。GPU 开始通过 2D 像素着色支持着色器。很快,它得到了增强,以支持顶点着色器。如今,OpenGL 3.2 和 Direct3D 10 库也支持几何着色器。

现在让我们深入了解着色器的定义、必要性和在 Android 游戏中的范围。

什么是着色器?

简而言之,着色器是一组指令,用于操纵输入图形资产的视觉显示。

让我们稍微详细解释一下定义。所有指令基本上都是通过编程完成的。这就是为什么着色器的概念只存在于计算机图形中的原因。着色器能够根据指令和输入资产执行计算,以产生更有效的输出可显示资产。

典型的着色器可以处理顶点或像素。像素着色器可以计算资产的颜色、深度和 alpha 属性。顶点着色器可以计算顶点的位置、颜色、坐标、深度、光照等。因此,着色器可以根据操作基本类型主要分为两类:

  • 2D 着色器

  • 3D 着色器

着色器的必要性

在游戏开发的正常实践中,Android 开发人员不太关心着色器。但是,着色器的必要性是不可避免的。最初,对于小规模游戏,艺术资产是未经改进的。对资产的任何修改都是通过更新艺术资产本身的旧流程来管理的。

着色器可以最小化这种额外耗时的工作。同一资产可以被操纵以在屏幕上创建不同的对象。例如,您可以在对象失焦时使其模糊,改变游戏过程中精灵的颜色以表示不同的团队或玩家,创建艺术资产的蒙版等等。

着色器具有以下好处:

  • 当不同的着色器应用于相同的艺术资产时,根据运行时的要求,会产生不同的资产。因此,着色器可以节省额外的艺术创作时间。

  • 游戏中一次性集成可绘制对象可以通过不同的着色器产生不同的视觉体验。

  • 由于艺术资产被最小化,使用着色器可以减少游戏构建大小。

  • 相同的资产集会有更多的视觉差异。

  • 通过着色器可以通过简单的艺术重复操纵视觉内容来创建动画。

  • 着色器在运行时创建视觉效果很有用。

然而,使用着色器可能会导致一些负面后果:

  • 使用着色器会增加处理时间,因为在运行时操纵视觉资产

  • 未经优化地使用着色器可能导致更多的堆内存消耗,因为其中将存储各种中间实例

  • 有时,着色器在处理时负责对象的扭曲

  • 使用着色器使艺术资产容易受到质量损失

只有前两种是使用着色器的实际直接后果。其余的问题只有在开发人员使用编写不好的着色器或有故障的着色器时才会发生。因此,选择特定任务的完美着色器是非常必要的。

有时,着色器处理需要很长时间,导致 FPS 输出不佳。一些旧的 GPU 不支持所有类型的着色器。因此,开发人员应检查和确认着色器要执行的硬件平台。

着色器的范围

着色器可以用于与计算机图形相关的各种领域,如图像处理、摄影、数字动画、视频/电脑/数字游戏等。

游戏行业是使用着色器的最大社区之一。安卓平台也不例外。安卓游戏开发人员在 3D 和 2D 游戏中大规模使用着色器。

坦率地说,2D 游戏对着色器的范围不大。只有像素着色器可以操纵像素的颜色、不透明度、饱和度和色调。当相同的原始资产用于不同的可见性时,这是很有用的。

例如,一个 2D 板球游戏有许多队伍,他们穿着不同的服装以区分它们。开发人员将所有精灵动画资产设计成一个设计,并应用着色器以不同方式操纵颜色以区分不同的队伍。因此,输出的精灵具有不同的可见性,并且玩家可以轻松识别它们。

着色器的工作原理

我们已经讨论过着色器处理顶点或像素。因此,基本的工作原理是在运行时更改或操纵数据:

着色器的工作原理

着色器过程是一组特定的指令,用于处理顶点或片段。可以为各种类型的处理编写不同的着色器程序。

顶点着色器用于改变模型的形状;它还可以改变表面形成系统。

像素/片段着色器可以改变像素颜色值以及不透明度。像素数据可以通过着色器程序合并、修改或替换,以形成新的数字图像。

着色器的类型

游戏行业中使用了许多着色器。它们根据其行为和特性进行分类。以下是一些着色器:

  • 像素着色器

  • 顶点着色器

  • 几何着色器

  • 镶嵌着色器

让我们详细看看这些类型。

像素着色器

像素着色器是在纹理或数字图像上工作的 2D 着色器。像素着色器处理单个像素的颜色和其他属性。每个单个像素称为片段。这就是为什么像素着色器经常被称为片段着色器的原因。

顶点着色器

顶点着色器主要作用于网格或模型的顶点。每个模型的网格由多个顶点组成。顶点着色器只能应用于 3D 模型。因此,顶点着色器是一种 3D 着色器。

几何着色器

几何着色器用于创建新的基本图形元素。在应用顶点着色器以执行渲染管线后,几何着色器用于创建点、线和三角形以形成表面。

镶嵌着色器

这是一个典型的 3D 着色器,用于简化和改进镶嵌期间的 3D 网格。它分为两个着色器:

  • Hull 着色器或镶嵌控制着色器

  • 域着色器或镶嵌进化着色器

这两个着色器一起使用以减少网格带宽。

镶嵌着色器有能力以显著减少可绘制顶点数的方式改进 3D 模型。因此,渲染变得更快。

Android 库着色器

Android 在android.graphics包中的框架中提供了着色器选项。一些知名和广泛使用的着色器也在 Android 库中。其中一些如下:

  • BitmapShader:这可以用于以纹理格式绘制位图。它还支持位图的平铺或镜像。它非常适用于创建具有平铺的地形。

  • ComposeShader:这用于合并两个着色器。因此,它非常适用于掩模或合并两种不同着色器的颜色。

  • LinearGradient:这用于沿着给定的线段创建渐变,并带有定义的颜色集。

  • RadialGradient:这用于创建沿着给定圆弧段的渐变,并带有定义的颜色集。提供了径向原点和半径来创建渐变。

  • SweepGradient:这用于在给定半径周围创建扫描渐变颜色。

这里是一个例子:

@Override
protected void onDraw ( Canvas c)
{
  float px = 100.0f;
  float py = 100.0f;
  float radius = 50.0f;

  int startColor = Color.GREEN;
  int targetColor = Color.RED;

  Paint shaderPaint = new Paint();
  shaderPaint.setStyle(Paint.Style.FILL);

  //LinearGradient Example to a circular region
  LinearGradient lgs = new LinearGradient( px, py, px + radius, py + radius, 
    startColor, targetColor, Shader.TileMode.MIRROR);
  shaderPaint.setShader(lgs);
  c.drawCircle( px, py, radius, shaderPaint);

  //RadialGradient Example to a circular region
  px = 200.0f;
  py = 200.0f;
  RadialGradient rgs = new LinearGradient( px, py, radius, 
    startColor, targetColor, Shader.TileMode.MIRROR);
  shaderPaint.setShader(rgs);
  c.drawCircle( px, py, radius, shaderPaint);

  //SweepGradient Example to a circular region
  px = 300.0f;
  py = 300.0f;
  shaderPaint.setShader(new SweepGradient(px, py, startColor, targetColor));
  c.drawCircle( px, py, radius, shaderPaint);
}

这是它的样子:

Android 库着色器

这些选项非常适合使用不同的样式创建不同对象的相同原始对象。

编写自定义着色器

开发人员可以根据自己的需求编写自定义着色器。Android 提供了android.graphics.Shader类。使用提供的原始着色器很容易创建自己的着色器类。

自定义着色器可能不仅包括一个着色器。它可以是各种着色器的组合。例如,考虑使用运动触摸事件在圆形视口中遮罩图像:

private float touchX;
private float touchY;
private boolean shouldMask = false;

private final float viewRadius;
private Paint customPaint;

@Override
public boolean onTouchEvent(MotionEvent motionEvent) 
{
  int pointerAction = motionEvent.getAction();
  if ( pointerAction == MotionEvent.ACTION_DOWN || 
  pointerAction == MotionEvent.ACTION_MOVE )
    shouldMask = true;
  else
    shouldMask = false;

  touchX = motionEvent.getX();
  touchY = motionEvent.getY();
  invalidate();
  return true;
}

@Override
protected void onDraw(Canvas canvas) 
{
  if (customPaint == null) 
  {
    Bitmap source = Bitmap.createBitmap( getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
    Canvas baseCanvas = new Canvas(source);
    super.onDraw(baseCanvas);

    Shader customShader = new BitmapShader(source, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

    customPaint = new Paint();
    customPaint.setShader(customShader);
  }

  canvas.drawColor(Color.RED);
  if (shouldMask) 
  {
    canvas.drawCircle( touchX, touchY - viewRadius, viewRadius, customPaint);
  }
}

这个例子是图片游戏中最常用的着色器样式之一。您也可以实现这样的着色器来创建隐藏物体游戏。

另一个用例是突出显示屏幕上的特定对象。同样的可视圆可以用来只显示突出的对象。在这种情况下,颜色可以是半透明的,以显示暗淡的背景。

通过 OpenGL 的着色器

在 Android 中,OpenGL 支持为 Android 3D 游戏实现着色器。OpenGL ES 2.0 是 Android 上支持着色器的平台。在手动创建着色器时,它有两个功能段:

  • 着色器

  • 程序

着色器被转换成中间代码以支持程序在 GPU 上运行。在编译阶段,着色器被转换。这就是为什么着色器在程序执行之前需要重新编译的原因。

在我们的例子中,我们将使用android.opengl包的GLSurfaceView

对于 3D 游戏,Android 开发人员可以使用这个包在 Android SDK 上玩转着色器。这个包提供了使用 Java 创建和使用 OpenGL 着色器的 API。

我们将使用GLSurfaceView而不是普通的 AndroidViewSurfaceView。实现将如下所示:

import android.opengl.GLSurfaceView;
import android.content.Context;

public class MyGLExampleView extends GLSurfaceView 
{
  private final GLRenderer mRenderer;

  public MyGLExampleView (Context context) 
  {
    super(context);

// Set OpenGL version 2.0 as we will be working with that particular library
    this.setEGLContextClientVersion(2);

// Set the Renderer for drawing on the GLSurfaceView
    MyOpenGLRendererExample = new MyOpenGLRendererExample (context);
    setRenderer(mRenderer);

// Render the view only when there is a change in the //drawing data
    setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
  }

  @Override
  public void onPause() 
  {
    super.onPause();
  }

  @Override
  public void onResume() 
  {
    super.onResume();
  }
}

我们需要为视图创建一个渲染器,通过 OpenGL 绘制对象:

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;

import android.content.Context;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView.Renderer;
import android.opengl.Matrix;
import android.util.Log;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

public class MyOpenGLRendererExample implements Renderer 
{

  // Declare matrices
  private float[] matatrixProjection = new float[32];
  private float[] matrixView = new float[32];
  private float[] matatrixProjectionOnView = new float[32];

  // Declare Co-ordinate attributes
  private float vertexList[];
  private short indicxList[];
  private FloatBuffer vertexBuffer;
  private ShortBuffer drawBuffer;

  private final String vertexShader =
          "uniform    mat4        uMVPMatrix;" +
          "attribute  vec4        vPosition;" +
          "void main() {" +
          "  gl_Position = uMVPMatrix * vPosition;" +
          "}";

  private final String pixelShader =
          "precision mediump float;" +
          "void main() {" +
          "  gl_FragColor = vec4(0.5,0,0,1);" +
          "}";

  // Declare Screen Width and Height HD display
  float ScreenWidth = 1280.0f;
  float ScreenHeight = 800.0f;

  private int programIndex = 1;

  public MyOpenGLRendererExample (Context context)
  {

  }

  @Override
  public void onDrawFrame(GL10 param) 
  {
    renderView(matatrixProjectionOnView);
  }

  @Override
  public void onSurfaceChanged(GL10 objGL, int width, 
int height) 
  {
    ScreenWidth = (float)width;
    ScreenHeight = (float)height;

    GLES20.glViewport(0, 0, (int)ScreenWidth, 
(int)ScreenHeight);

    //reset matrices
    for( int i = 0; i < 32 ; ++ i )
    {
      matatrixProjection[i] = 0.0f;
      matrixView[i] = 0.0f;
      matatrixProjectionOnView[i] = 0.0f;
    }

    Matrix.orthoM(matatrixProjection, 0, 0f, ScreenWidth, 
0.0f, ScreenHeight, 0, 50);

    Matrix.setLookAtM(matrixView, 0, 0f, 0f, 1f, 0f, 0f, 
0f, 0f, 1.0f, 0.0f);

    Matrix.multiplyMM(matatrixProjectionOnView, 0, 
matatrixProjection, 0, matrixView, 0);
  }

  @Override
  public void onSurfaceCreated(GL10 gl, EGLConfig config) 
  {
    //create any object 
    //Eg. Triangle:: simplest possible closed region

    createTriangle();

    // Set the color to black
    GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1);

    // Create the shaders
    int vertexShaderTmp = 
loadShader(GLES20.GL_VERTEX_SHADER, vertexShader);

    int pixelShaderTmp = 
loadShader(GLES20.GL_FRAGMENT_SHADER, pixelShader);

    int programIndexTmp = GLES20.glCreateProgram();

    GLES20.glAttachShader(programIndexTmp, 
vertexShaderTmp);

    GLES20.glAttachShader(programIndexTmp, 
pixelShaderTmp);

    GLES20.glLinkProgram(programIndexTmp);

    // Set shader program
    GLES20.glUseProgram(programIndexTmp);
  }

  void renderView(float[] matrixParam) 
  {
    int positionHandler = 
GLES20.glGetAttribLocation(programIndex, "vPosition");

    GLES20.glEnableVertexAttribArray(positionHandler);
    GLES20.glVertexAttribPointer(positionHandler, 3, 
GLES20.GL_FLOAT, false, 0, vertexBuffer);

    int mtrxhandle = 
GLES20.glGetUniformLocation(programIndex, "uMVPMatrix");

    GLES20.glUniformMatrix4fv(mtrxhandle, 1,
 false, matrixParam, 0);

    GLES20.glDrawElements(GLES20.GL_TRIANGLES, 
indicxList.length, GLES20.GL_UNSIGNED_SHORT, drawBuffer);

    GLES20.glDisableVertexAttribArray(positionHandler);  
  }

  void createTriangle()
  {
    // We have to create the vertexList of our triangle.
    vertexList = new float[]
    {
      20.0f, 200f, 0.0f,
      20.0f, 300f, 0.0f,
      200f, 150f, 0.0f,
    };

    //setting up the vertex list in order
    indicxList = new short[] {0, 1, 2};

    ByteBuffer bytebufVertex = 
ByteBuffer.allocateDirect(vertexList.length * 4);

    bytebufVertex.order(ByteOrder.nativeOrder());
    vertexBuffer = bytebufVertex.asFloatBuffer();
    vertexBuffer.put(vertexList);
    vertexBuffer.position(0);

    ByteBuffer bytebufindex = 
ByteBuffer.allocateDirect(indicxList.length * 2);

    bytebufindex.order(ByteOrder.nativeOrder());
    drawBuffer = bytebufindex.asShortBuffer();
    drawBuffer.put(indicxList);
    drawBuffer.position(0);

    int vertexShaderTmp = 
loadShader(GLES20.GL_VERTEX_SHADER, vertexShader);

    int pixelShaderTmp = 
loadShader(GLES20.GL_FRAGMENT_SHADER, pixelShader);

    int program = GLES20.glCreateProgram();
    if (program != 0) 
    {
      GLES20.glAttachShader(program, vertexShaderTmp);
      GLES20.glAttachShader(program, pixelShaderTmp);
      GLES20.glLinkProgram(program);

      int[] linkStatus = new int[1];

      GLES20.glGetProgramiv(program, 
GLES20.GL_LINK_STATUS, linkStatus, 0);

      if (linkStatus[0] != GLES20.GL_TRUE) 
      {
        Log.e("TAG_EXAMPLE_OPENGL", "Linking Failed !! 
Error:: " + GLES20.glGetProgramInfoLog(program));

        GLES20.glDeleteProgram(program);
        program = 0;
      }
    }
  }

// method to create shader
   int loadShader(int type, String shaderCode)
   {
        int shader = GLES20.glCreateShader(type);

        GLES20.glShaderSource(shader, shaderCode);
        GLES20.glCompileShader(shader);

        return shader;
    }
}

顶点着色器代码(String vs_SolidColor)有两个需要的参数。uMVPMatrix参数是mat4类型,它保存了可以用来平移位置的变换矩阵。uMVPMatrix参数是一个统一矩阵。vPosition参数是vec4类型,它保存了顶点的位置。

这个系统可以应用于三角形表面。

在游戏中使用着色器

着色器在游戏和动画中被广泛使用,特别是在创建动态照明、改变色调和进行动态视觉改进时。有时,世界环境是用着色器创建的。

2D 游戏空间中的着色器

只有像素着色器可以在 2D 游戏中使用。数字图像的每个像素被视为一个片段。这就是为什么像素着色器也被称为片段着色器的原因。像素着色器只能执行颜色更改、平铺和遮罩。

BitmapShaderComposeShaderLinearGradientRadialGradientSweepGradient是 Android 2D 着色器的变体。

2D 游戏世界是由图像创建的。开发人员通常选择创建不同的资源,以赋予相同对象不同的外观和感觉。在这个过程中,开发人员最终会创建一个几乎相同的使用集,但 APK 更大的 APK。

精灵也是着色器可以发挥重要作用的领域。当使用相同的精灵创建不同的对象时,某些片段的颜色需要动态改变。像素着色器在这里非常有用。

2D 空间中的着色器用于改变颜色,模糊段,改变亮度,改变不透明度,着色图像等。

3D 游戏空间中的着色器

在 3D 游戏中,着色器最常见的用途是动态阴影。在现代游戏开发中,阴影是改善游戏体验的不可避免的元素。应用纹理后,3D 模型看起来很真实。

在 Android 中,通过 OpenGL 应用 3D 着色器。我们已经讨论了一个例子:

3D 游戏空间中的着色器

只有顶点信息的原始模型

这是一个没有任何光照或着色器的简单模型。让我们应用一些着色器,使其具有坚实的 3D 外观:

3D 游戏空间中的着色器

应用简单的平面着色器

现在,开发人员可以应用任何纹理或颜色来赋予其不同的感觉。在这一部分,开发人员可以选择通过颜色或纹理来限制这一点。通常在这种情况下使用纹理,以使模型在视觉上更真实。然而,这比仅仅进行颜色操作要昂贵得多。

在这里我们将看到颜色和光照的变化,以获得完全不同的感觉。对于游戏的不同场景需求,有不同的处理程序。

然而,这个例子只是着色器如何操纵 3D 模型以获得不同外观和感觉的视觉表示:

3D 游戏空间中的着色器

着色器和游戏性能

着色器通常是处理密集型的。片段着色器处理纹理的每个片段并操纵其数据。大纹理可能会导致游戏循环中出现明显的延迟。

我们可以从不同的角度看待着色器,以创造出性能的概念。大纹理会降低性能,而许多小纹理也会影响性能。它们之间必须保持平衡,以便在实时使用着色器时具有可行性。

创建阴影是着色器的广泛用途之一。然而,阴影处理的质量与性能成反比。在高质量游戏中,我们可以体验实时阴影。着色器映射对象的顶点并根据光线方向进行处理。然后将其投影到X-Z平面上以创建阴影。阴影与平面上的对象和其他阴影合并。

着色器可用于改善世界的可见性,使用不同的光线、材质和颜色。

在游戏中使用着色器的一些优点:

  • 渲染资源的完全灵活性

  • 减少资源包,增加可重用性

  • 动态视觉效果

  • 动态光照和阴影

  • 实时精灵操作

使用着色器有一些缺点:

  • 帧率相对较低

  • 性能下降

  • 需要支持的硬件平台和图形驱动程序

尽管有一些缺点,着色器已经被证明足够成为游戏开发的固有部分。任何性能下降都可以通过升级硬件和图形驱动程序来处理。

如今,着色器正在针对资源有限的嵌入式设备进行优化。这甚至为在几乎每个平台上增加着色器的使用提供了机会,而不会显著影响性能。

总结

自 Android API 级别 15 起,框架支持 OpenGL ES 2.0。这为图形程序员在 Android 游戏中实现着色器提供了巨大的灵活性。

几乎每种硬件配置都支持着色器在 GPU 上运行。然而,使用着色器的规模决定了性能。在现代,这实际上并不是一个问题。

着色器在游戏中被广泛使用。在图形编程的各个方面,着色器已经证明了它们的重要性。所有著名和成功的游戏开发者都承认了着色器的重要性。图形艺术家不需要担心游戏中的所有视觉内容,这显著减少了开发时间。

因此,着色器在游戏中被广泛使用。新的着色器现在带有额外的功能。着色器的升级周期已经减少。然而,硬件也在升级,以支持图形更新的新技术。

看到一个简单的立方体变成具有相同方向的任何东西,感觉就像魔术。这种魔术将在未来更大规模地持续发生。

开发一款游戏并不足够。着色器在减少内存使用方面有很大帮助,但它们会增加处理开销。我们将在下一章尝试探索各种存储和处理的优化技术。

第八章:性能和内存优化

优化是任何开发周期中最重要的任务之一。尤其是对于游戏来说是不可避免的。游戏优化显著提高了性能。通过优化,可以针对更多的硬件平台。

您已经了解到安卓支持一系列硬件平台。每个平台都有单独的配置。通过优化硬件资源的使用,游戏可以在更多的硬件平台上运行。这种技术也可以应用于视觉质量。并非所有设备都具有相同的显示质量,因此为低分辨率优化资源可以节省大量的存储空间以及运行时的堆内存。

在编程中,开发人员经常编写中间代码,然后忘记对其进行优化。这可能导致大量的性能损失,甚至导致游戏崩溃。

我们将通过以下主题讨论安卓游戏开发中各种优化的范围:

  • 安卓游戏中的优化领域

  • 性能和内存管理之间的关系

  • 安卓中的内存管理

  • 安卓中的处理段

  • 不同的内存段

  • 内存优化的重要性

  • 性能优化

  • 增加帧率

  • 性能优化的重要性

  • 常见的优化错误

  • 最佳优化实践

安卓游戏中的优化领域

我们都知道在任何开发项目中都需要优化。在游戏开发的情况下,这一事实仍然如此。在游戏开发项目中,流程始于有限的资源和设计。开发后,预期游戏能在尽可能多的设备上以最高质量运行。为了实现这一点,内存和性能优化变得必不可少。因此,让我们讨论以下四个优化领域:

  • 资源优化

  • 设计优化

  • 内存优化

  • 性能优化

资源优化

资源优化基本上是优化艺术、声音和数据文件。

艺术优化

我们已经讨论了许多优化技术和工具。在这里,我们将讨论艺术优化的必要性。

艺术在游戏中在视觉上是最重要的部分。提高艺术品的显示质量会增加处理和存储成本。

大纹理占用大量内存。然而,将艺术放大以适应更大分辨率的屏幕会影响视觉质量。因此,必须取得平衡。此外,各种安卓设备对纹理大小有各种限制。此外,着色器在较大纹理上的工作需要更多时间。

开发人员常犯的一个错误是对完全不透明的纹理使用 alpha 信息。这会显著增加纹理大小。

艺术资源可以根据艺术风格进行优化。许多开发人员使用单色纹理而不是渐变色。单色信息可以容纳在 8 位像素数据中。这再次节省了磁盘空间和处理时间。

尽管存在这些优化范围,开发人员可能不会全部使用,以增加灵活性,从而在不花费太多时间进行优化的情况下创建高质量的视觉艺术。

声音优化

声音是游戏中另一个重要的资源。音频可以被压缩以节省空间和精力。在安卓游戏行业中,一种常见做法是对长音频文件使用压缩格式。

在运行时压缩和解压文件需要时间。因此,如果 SFX 被压缩,动态使用可能会成为问题。它可能会触发显著且可见的卡顿。开发人员喜欢对 SFX 使用未压缩格式,并对长时间连续播放的声音(如背景音乐)使用压缩格式。

数据文件优化

有时,游戏开发人员使用单独的数据文件来创建灵活的项目结构,以便与外部工具交互或获得更好的数据接口。这些文件通常以文本、XML、JSON 或二进制格式存在。开发人员可能会创建自己的二进制模型数据格式。

如果使用正确的算法,二进制数据可以快速处理。数据优化并没有太多技术性。然而,开发人员始终需要检查数据量和总文件大小。

设计优化

设计优化用于增加游戏的可扩展性、质量体验、灵活性和耐久性。主要方法是围绕核心游戏概念重组或修改游戏参数。

让我们从功能的角度将这一部分分为两部分:

  • 游戏设计优化

  • 技术设计优化

游戏设计优化

游戏在游戏设计优化阶段可能与最初的想法完全不同。设计优化是基于某些任务进行的。开发人员需要找到不同的方式来传达基本的游戏理念。然后,他们可以选择最佳的方式,进行一些分析。

游戏设计应该足够灵活,以适应运行时变化,以改善整体体验并增加用户数量。高度优化的游戏设计足够高效,可以预测用户行为、各种设备上的游戏性能,甚至货币化。

游戏控制系统设计必须足够优化,以便轻松完成所有任务。游戏控制应该易于发现和理解。对于 Android 触摸设备,控件的放置也非常重要。

技术设计优化

技术设计优化仅限于开发周期。它设置了项目结构、程序结构、开发平台依赖等。

技术设计文档还规定了游戏的范围和规模。这些规范有助于在设备上顺利运行游戏,因为硬件平台已经在技术设计文档中涵盖。

这是一个预开发过程。这份文件需要处理一些假设。这些假设应该足够优化,以便在实时情况发生时进行演变。

技术设计还可以在开发过程中处理以下任务。通过优化这些任务,实施和执行将更加容易:

  • 程序架构

  • 系统架构

  • 系统特性

  • 定义的依赖关系

  • 影响

  • 风险分析

  • 假设

所有这些任务都可以优化,以便更轻松地进行更好的开发周期,游戏将更加精致,并且性能更高。

内存优化

内存优化对于任何软件开发过程都是必需的。内存根据硬件配置有其物理限制,但游戏和应用程序不能为每个设备单独制作。

在技术设计中,应该提到游戏在所有目标硬件平台上的内存使用范围。现在,游戏占用的内存超出预期是非常常见的情况,最终导致游戏崩溃。开发人员会收到内存溢出异常。

为了避免这种情况,有两个主要的事情需要注意:

  • 保持内存峰值在定义范围内

  • 不要在内存中保留不必要的数据加载

Android 使用分页和映射来管理内存使用。不幸的是,它不提供内存交换。Android 知道在哪里找到分页数据并相应地加载。

以下是一些优化 Android 游戏内存的技巧。

不要在运行时创建不必要的对象

开发人员经常在循环内创建中间数据对象。这会留下内存印记,供垃圾收集器收集。以下是一个例子:

//Let's have an integer array list and fill some data
List<int> intListFull = new ArrayList<int>();
//Fill data
for( int i = 0; i < 10; ++ i)
{
  intListFull.add(i);
}

// No we can have two different approach to print all 
// values as debug log.
// Approach 1: not optimized code
for ( int i = 0; i < intListFull.size() ; ++ i)
{
  int temp = intListFull.get(i);
  Log.d("EXAMPLE CODE", "value at " + i + " is " + temp);
}
// List size will be calculated in each cycle, temp works 
//as auto variable and create one memory footprint in each 
//loop. Garbage collector will have to clear the memory. 

// Approach 2: optimized code
int dataCount = intListFull.size();
int temp;
for ( int i = 0; i < dataCount ; ++ i)
{
  temp = intListFull.get(i);
  Log.d("EXAMPLE CODE", "value at " + i + " is " + temp);
}
// only two temporary variable introduced to reduce a foot 
//print in each loop cycle.

尽可能使用基本数据类型

用户定义的数据类型比原始数据类型占用更多的内存空间。声明一个整数所占用的空间比将整数嵌入类中要少。在 Android 中,如果开发人员使用Integer类而不是int,数据大小会增加四倍。

对于 Android 编译器(32 位),int消耗 4 字节(32 位),而Integer消耗 16 字节(128 位)。

对于现代 Android 设备,有限使用这种数据类型可能对内存没有显著的影响。然而,大量使用非原始数据类型可能会导致大量的内存块,直到开发人员或垃圾收集器释放内存。

因此,开发人员应避免使用enum,而是使用静态最终的intbyteenum作为用户定义的数据类型,比原始数据类型占用更多的内存。

不要使用未管理的静态对象

在旧版 Android 中,静态对象不会自动销毁是一个常见问题。开发人员过去需要手动管理静态对象。在新版 Android 中,这个问题已经不存在。然而,在游戏中创建许多静态对象并不是一个好主意,因为静态对象的寿命等于游戏的寿命。它们会长时间地直接阻塞内存。

使用太多静态对象可能导致内存异常,最终导致游戏崩溃。

不要创建不必要的类或接口

每个类或接口在其实例中都有一些额外的绑定空间。模块化编程方法要求在编码结构中尽可能多地进行分解。这与类或接口的数量成正比。这被认为是一种良好的编程实践。

然而,这对内存使用有影响。更多的类消耗更多的内存空间来存储相同数量的数据。

使用最小可能的抽象

许多开发人员在多个层次上使用抽象以获得更好的编程结构。限制自定义库的某一部分并仅提供选择性的 API 非常有用。在游戏开发方面,如果开发人员只开发游戏,那么抽象的使用并不是非常必要的。

抽象导致更多的指令,直接导致更多的处理时间和更多的内存使用。因此,即使抽象有时可能很方便,开发人员在开发游戏时使用抽象之前应该三思而后行。

例如,一个游戏可能有一组不同的敌人。在这种情况下,创建一个单一的敌人接口,并为不同的敌人对象实现它,有助于创建一个简单和方便的程序层次结构。然而,不同的敌人可能有完全不同的属性。因此,抽象的使用将取决于游戏设计。无论情况如何,如果开发人员使用抽象,那么它将始终增加要在运行时处理的指令集。

对服务进行检查

服务对于在后台完成一个任务非常有用,但在处理和内存方面非常昂贵。除非必要,开发人员不应该保持服务运行。自动管理服务生命周期的最佳方法是使用IntentService,它在工作完成后将结束。对于其他服务,开发人员有责任确保在任务完成后调用stopServicestopSelf

这个过程对游戏开发非常有效,因为它积极支持用户和开发人员之间的动态交流。

优化位图

位图是游戏中最重的资源。在游戏开发中,大部分堆内存都被位图使用。因此,优化位图可以显著优化运行时堆内存的使用。

通常,将位图加载到内存所需的内存由以下公式给出:

BitmapSize = BitmapWidth * BitmapHeight * bytePerPixel

例如,如果以ARGB_8888格式(4 字节)加载一个 480 x 800 大小的位图,内存将如下:

位图大小 = 480 x 800 x 4 = 1536000 字节〜1.5mb

在 Android 中,格式可以是以下类型之一:

  • ARGB_8888(4 字节)

  • RGB_565(2 字节)

  • ARGB_4444(2 字节)(在 API 级别 13 中已弃用)

  • ALPHA_8(1 字节)

每个位图将根据前述公式占用内存。因此,建议根据需要将位图加载到内存中,以避免不必要的堆使用。

释放不必要的内存块

就像我们之前讨论释放内存一样,同样的方法也可以应用于任何对象。任务完成后,实例应设置为 null,以便垃圾收集器可以识别并释放分配的内存。

在游戏状态机中,类结构应提供一个接口来释放实例化对象的内存。可能会出现这样的情况,其中一些成员对象已完成其任务,而另一些仍在使用中,因此等待整个类实例被释放是一个坏主意。开发人员应选择性地释放未使用对象的内存,而不删除类实例。

使用 zipalign 和 ProGuard 等外部工具

ProGuard 工具通过删除未使用的代码和使用安全和编码的命名结构重命名类、字段和方法,有效地缩小、优化和混淆代码。ProGuard 可以使代码更加紧凑,这直接影响 RAM 的使用。

在游戏开发中,开发人员经常使用许多第三方库,这些库可能已经使用 ProGuard 预编译。在这些情况下,开发人员必须配置 ProGuard 以排除这些库。还可以保护代码库免受盗窃。

zipalign 可用于重新对齐发布的 APK。这进一步优化 APK,以使用更少的空间并具有更紧凑的大小。通常,大多数 APK 构建框架会自动提供 zipalign。但是,开发人员可能需要在少数情况下手动使用它。

性能优化

性能意味着游戏在目标平台上运行的流畅程度,并在整个游戏过程中保持良好的 FPS。在 Android 游戏的情况下,我们已经了解到各种硬件配置的广泛范围。在所有设备上保持相同的性能实际上是不可能的。这就是开发人员选择目标硬件和最低硬件配置的原因,以确保游戏的性能足够好以发布。但是,期望也因设备而异。

在实际开发约束中,性能优化受限于目标硬件集。因此,内存在开发过程中有自己的优化空间。

从编程角度来看,性能优化可以通过更加关注编写和构造代码来实现:

  • 尽量每个任务使用最少的对象

  • 尽量减少浮点数的使用

  • 尽量减少抽象层

  • 尽可能使用增强循环

  • 避免为内部使用的变量使用 getter/setter

  • 对常量使用 static final

  • 尽量减少内部类的使用

尽量每个任务使用最少的对象

创建不必要的对象会增加处理开销,因为它们必须在新的内存段中初始化。多次使用相同对象执行相同任务要快得多。以下是一个例子:

public class Example
{
  public int a;
  public int b;

  public int getSum()
  {
    return (a + b);
  }
}
//Lets have a look on un-optimized code
// Here one object of Example class is instantiating per loop //cycle 
// Same is freed and re-instantiated
public class ExecuterExample
{
  public ExecuterExample()
  {
    for ( int i = 0; i < 10; ++ i)
    {
      Example test = new Example();
      test.a = i;
      test.b = i + 1;
      Log.d("EXAMPLE", "Loop Sum: " + test.getSum());
    }
  }
}
// Optimized Code would look like this
// Here only one instance will be created for entire loop
public class ExecuterExample
{
  public ExecuterExample()
  {
    Example test = new Example();
    for ( int i = 0; i < 10; ++ i)
    {
      test.a = i;
      test.b = i + 1;
      Log.d("EXAMPLE", "Loop Sum: " + test.getSum());
    }
  }
}

尽量减少浮点数的使用

在机器级语言中,不存在整数或浮点数。它总是表示真或假的位(在技术语言中为 0 和 1)。因此,整数可以直接由一组位表示,但浮点数需要额外的处理开销。

直到某个时间点,编程语言中没有使用浮点数。后来,转换出现了,并且引入了额外的处理要求。

尽量减少抽象层

很明显,抽象需要每个层面额外的处理。因此,随着抽象层的增加,处理变得更慢。

尽可能使用增强循环

在数组和列表解析的情况下,增强的for循环比通常的传统for循环要快得多,因为它没有迭代变量系统,可以直接访问每个数组或列表元素。

这是一个非增强循环的例子:

int[] testArray = new int[] {0, 1, 2, 3, 5};
for (int i = 0; i < testArray.length; ++ i)
{
  Log.d("EXAMPLE", "value is " + testArray[i]);
}

这是一个增强循环的例子:

int[] testArray = new int[] {0, 1, 2, 3, 5};
for (int value : testArray)
{
  Log.d("EXAMPLE", "value is " + value);
}

避免内部使用变量的 getter/setter

使用 getter 和 setter 来访问或更改对象的任何内部元素的状态。在高级推理中,它不遵循数据封装的基本概念。然而,在 Android 游戏开发中广泛使用 getter 和 setter。

在许多情况下,开发人员从类对象内部使用 getter 和 setter。这不必要地增加了处理时间,导致性能下降。因此,开发人员应尽量少使用 getter 和 setter,并确保它们不被内部使用。

使用静态 final 来定义常量

常量在运行时不应更改。对于全局常量,数据直接与类对象相关联。因此,我们需要解析类对象才能访问它。

使用静态是一个很好的主意,可以摆脱这个额外的进程。使用静态常量可以显著增加元素的可访问性。然而,开发人员也需要检查内存使用情况。

尽量少使用内部类

每个内部类都会增加一层处理。有时,为了以高效和可读的方式构建代码库,使用内部类是很好的。然而,这会增加处理开销。因此,开发人员应尽量少使用内部类以优化性能。

性能和内存管理之间的关系

在 Android 游戏开发中,性能和内存优化经常相互冲突。为了保持游戏的视觉质量,必须使用更好的艺术资源,这最终会增加内存开销和性能滞后。

优化内存需频繁进行内存操作,导致性能下降。为了提高性能,对象必须能够顺利处理。显然,两者都不能达到极端水平。

在它们之间取得平衡是优化整个游戏以顺利运行而不耗尽内存的唯一方法。

Android 中的内存管理

让我们讨论一下 Android 中的内存管理系统。它直接影响游戏开发过程。在 Android 中,游戏被视为应用程序。开发人员经常在游戏的运行时和最小化状态下遇到内存问题。有三个主要主题需要讨论以了解工作原理:

  • 应用程序共享内存

  • 内存分配和释放

  • 应用程序内存分配

应用程序共享内存

Android 使用 Linux 内核,Linux 使用“共享”页面在运行进程或服务之间共享相同的内存段。例如,Android 经常在进程之间共享“代码”内存。很多时候,外部库和 JVM 的可执行代码内存可以安全地在进程之间共享,而不会创建死锁。数据页面可以在进程之间暂时共享,直到一个进程修改了共享内存。

Android 为每个应用程序或进程分配专用内存。这称为私有内存。同一进程也可能使用共享内存。Android 会自动设置一个上限,取决于两者的总和,以确定进程或应用程序何时会被终止,特别是在后台运行时。这个上限称为比例设置大小PSS):

应用程序共享内存

如果一个应用的 PSS 很高,那么这个进程可能被 Android 杀死的可能性很高。特别是如果应用程序依赖一些后台活动或服务来执行某些任务,这种情况可以通过编程方式处理以保持内存使用情况,开发人员必须确保游戏在任何时候使用的内存尽可能少,特别是当应用程序进入后台时。在后台释放不再需要的内存和对象,并在进入后台时断开不再需要的任何共享内存可能是一个不错的主意。这将减少您的应用程序被 Android 系统意外杀死的机会。

内存分配和释放

Android 内存管理系统为每个应用程序定义了一个虚拟上限,即逻辑堆大小。如果有必要,可以增加这个逻辑堆大小,但前提是有空闲内存可用。然而,这个逻辑堆大小并不是应用程序的实际分配内存。计算得到的 PSS 是在运行时可能变化的实际物理上限,并且存在共享内存依赖。

应用程序内存不能使用比 PSS 更多的物理内存。因此,在达到此限制后,如果应用程序尝试分配更多内存,那么它将收到系统抛出的OutOfMemoryError。在紧急情况下,Android 可能会杀死其他空闲或后台进程以为运行中的应用程序腾出内存。在这些情况下,应用程序内存将被释放:

  • 如果应用程序退出

  • 如果进程变得不活动,而其他进程需要内存

  • 如果应用程序因任何原因崩溃

应用程序内存分配

Android 为每个应用程序设置了堆大小的硬限制,以维持多任务环境。根据设备的 RAM 容量,确切的堆大小限制因硬件配置而异。如果应用程序达到堆容量并尝试分配更多内存,它将收到OutOfMemoryError,并且应用程序将被 Android 杀死。

开发人员需要检查设备上可用内存的数量,然后确定平均目标内存使用量。开发人员可以通过调用getMemoryClass()来查询操作系统的内存量。这将返回一个整数,表示应用程序堆可用的 MB 数。

Android 中的处理段

游戏基本上是功能上的一个应用程序。多个应用程序或游戏可以在 Android 平台上运行。但是,对于游戏来说,一次只有一个游戏是活动的,而其他应用程序在后台运行。

让我们看看 Android 如何处理其应用程序。

应用程序优先级

Android 设置了运行应用程序的优先级,并且根据需求可以杀死低优先级的运行应用程序。

每个应用程序都使用一些内存和处理带宽。可能出现多个应用程序同时运行的情况。如果一个新应用程序想要运行,那么 Android 会为新应用程序分配内存和处理带宽。如果没有足够的带宽或可用的处理,那么 Android 会杀死一个或多个优先级较低的运行中的应用程序。

Android 通过以下状态设置优先级:

  • 活动进程

  • 可见进程

  • 活动服务

  • 后台进程

  • 空进程应用程序优先级

活动进程

活动进程基本上是指与平台频繁通信并在前台运行的进程。当必要时,这个进程是 Android 中最后一个被杀死的。

活动进程满足以下标准:

  • 它在前台运行

  • 它是可见的

  • 至少有一个 Android 活动正在运行

  • 它与用户界面积极交互

  • 所有事件处理程序处于活动状态

可见进程

这个过程基本上是一个活动的进程,不在前台,也不与用户界面交互。这是 Android 平台的第二高优先级。

这个过程的标准如下:

  • 它在后台运行

  • 它有可见的活动

  • 它不与用户界面交互

  • UI 事件处理程序不活动

  • 进程事件处理程序是活动的

活动服务

活动服务是支持进行中的进程而没有可见界面的服务。Android 将首先终止这些服务,然后才是实际的活动进程。

此服务遵循以下标准:

  • 它没有可见的界面

  • 它支持或为相应的活动进程工作

  • 它在后台运行

后台进程

后台进程基本上是最小化或不活动的进程。这些进程在屏幕上不可见。这些进程的线程不运行,但应用程序状态保存在内存中。这些进程容易被处理器终止。这些进程在中断后可以恢复。

这些是不活动/最小化的进程。它们保留在内存中。应用程序保持暂停状态。

空进程

空进程也称为空进程。空进程实际上是空的。它在内存中不保存任何应用程序数据或状态。这个进程有最高的优先级,以便被操作系统终止。

应用程序服务

Android 应用服务是实际应用程序过程的一部分。这些服务可以在父进程内部和外部运行。

让我们澄清关于服务的两个常见误解:

  • 服务不是一个独立的进程

  • 服务不是线程

事实上,服务是应用程序过程的一部分,而不是独立的进程。服务不是线程。它们是在后台运行的进程的一部分,即使主应用程序处于暂停状态,它们也会继续运行。

服务旨在执行单个任务,不会回调父应用程序。这就是为什么它们可以在应用程序关闭后继续运行。

服务生命周期

服务由父应用程序进程启动,如下所示:

Context.startService();

启动后,服务开始在后台执行单个任务。任务完成后,服务可以停止自己。例如,简单的文件下载服务在成功下载任务后会停止。许多游戏开发者在他们的游戏中使用这些功能来提高用户体验。

这些服务可以与一个或多个进程绑定以实现交互。应用程序可以向绑定的服务发送请求并获得响应,从而创建服务器-客户端架构。但这些绑定的服务在最后一个应用程序组件绑定到服务之前有限的生命周期。

资源处理

Android 有自己的资源处理结构。它有一些预定义的资源类型:

  • 可绘制资源

  • 布局资源

  • 颜色资源

  • 菜单资源

  • Tween 动画资源

  • 其他资源

可绘制资源

所有可绘制资源都属于这个类别,包括帧动画。Android 提供了专门用于所有可绘制资源的res/drawable/项目路径。所有位图、各种 XML 和预定帧动画都可以放在这里。

这些可以通过R.drawable类访问。

布局资源

所有定义的布局都属于这个类别。Android 提供了专门用于所有布局文件的res/layout/项目路径。布局对于定义应用程序 UI 非常有用。

这些可以通过R.layout类访问。

颜色资源

颜色资源基本上是一个颜色列表,随着适用对象视图的改变而改变。Android 将其存储在层次结构中的res/color/文件夹中。

这些可以通过R.color类访问。

菜单资源

所有菜单内容都可以在这里定义。Android 提供了专门用于所有drawable资源的res/menu/项目路径。

这些可以通过R.menu类访问。

Tween 动画资源

所有 Tween 动画资源都属于这个类别。Android 提供了专门用于所有 Tween 动画资源的res/anim/项目路径。

这些可以通过R.anim类访问。

其他资源

所有其他资源都放在res/values/文件夹中。许多开发人员在这个类别下定义了样式的字符串。

这些可以通过R.values类访问。

不同的内存段

在应用程序运行时,根据行为使用了三种主要的内存段:

  • 堆栈内存

  • 堆内存

  • 寄存器内存

堆栈内存

所有自动变量和处理过程中的运行时分配将存储在堆栈内存段中。垃圾收集器在使用后释放内存。因此,与堆栈内存相关联的没有手动内存管理过程。

然而,大量使用自动变量也可能导致内存错误。这就是为什么我们已经讨论过为什么最小化不必要的自动变量声明是必要的原因。

堆栈内存也用于执行程序指令。每个指令都被解析为一个单独的操作,并由解释器放入堆栈中。然后,使用递归过程来执行所有指令堆栈并返回结果。

让我们看看堆栈内存如何处理对象和基本类型:

public class ExampleClass
{
  public ExampleClass()
  {
    int bitMapCount = 0;  // primitive type
    Bitmap testBmp = BitmapFactory.decodeFile("bitmap path"); // Object loading
    bitMapCount = 1;
  }
}

在这个例子中,bitMapCount是一个int局部变量,直接存储在堆栈中。这个变量使用的内存将在作用域结束后立即被释放。

然而,testBmp是一个位图对象,将被分配在堆中,但引用将存储在堆栈中。当程序指针离开作用域时,引用将被自动删除,垃圾收集器可以识别为testBmp分配的堆内存具有零引用,并释放这个内存段。

堆内存

堆内存是存储所有类实例和数组实例的段。JVM 在实例化任何对象时分配这个内存。

垃圾收集器在应用程序运行时不会自动操作这个内存段。开发人员有责任在使用后释放内存。在 Android 的情况下,只有在运行应用程序中没有对内存段的引用时,垃圾收集器才会释放内存。

游戏资源是存储在这个内存段中的主要元素。艺术是其中最重要的资产。因此,优化位图对堆内存的使用有直接影响。开发人员经常为资产分配内存并且不会打破引用。这导致内存块在整个运行时期被占用。

以下是一个例子:

// create a bitmap in a class constructor having global
// scope with public access
public class ExampleClass
{
  public Bitmap testBmp;
  public ExampleClass()
  {
    testBmp = BitmapFactory.decodeFile("bitmap path");
  }
}

在这个例子中,位图的内存将在使用后仍然被占用,直到ExampleClass实例在内存中。解释器没有立即释放内存段的指令,因为testBmp仍然引用了分配给位图的内存。

我们可以通过一些修改以以下方式优化这个问题:

public class ExampleClass
{
  public Bitmap testBmp;
  public ExampleClass()
  {
    testBmp = BitmapFactory.decodeFile("bitmap path");
  }
  // create a method to free memory allocated for the
  // bitmap after use
  public void unloadBitmap()
  {
    testBmp = null;
  }
}

在这种情况下,在使用位图后调用unloadBitmap()将从testBmp中删除加载的位图的引用。因此,垃圾收集器将找到这个内存位置为零引用内存,并释放它以供其他分配使用。

寄存器内存

在 Android 开发中,开发人员不必担心寄存器内存。寄存器直接与处理器相关联,处理器将最重要和经常使用的数据存储在这个内存段中。

寄存器内存是任何应用程序运行时使用的最快的内存段。

内存优化的重要性

无论游戏如何,外观如何,设计得多好,如果游戏在目标平台上无法运行,那么它就不会成功。我们已经知道 Android 有各种硬件配置。

硬件的主要变化是特定于处理器和内存。在处理器方面,这取决于其速度和质量。在内存或 RAM 方面,只取决于容量。

即使在今天,Android 设备的 RAM 也可以从 512MB 到 4GB 不等。内存优化应该始终以设计为目标的最低 RAM 为目标。因此,内存优化对于在最小可用 RAM 上运行游戏非常重要。

有时,开发人员将峰值使用量限制在目标内存限制内。然而,他们在测试设备上执行,这大部分时间并不反映实时情况。总是存在误差范围。因此,并不总是真实情况下游戏在一定的 RAM 限制下运行时,总是能提供相同的内存。这就是内存优化发挥重要作用的地方。它在创建游戏在实时情况下运行的缓冲范围方面起到了很大作用。

可能存在这样的情况,即使应用程序不需要所需的 RAM 量,也会耗尽内存。这清楚地表明应用程序存在内存泄漏问题。内存泄漏是游戏开发中最常见的问题之一。正确优化内存有助于解决这个问题。

内存优化的另一个方面是增加游戏保持在后台的概率。当应用程序进入后台时,如果 Android 需要为其他前台应用程序释放内存空间,可能会杀死应用程序。内存优化确保应用程序在运行时占用尽可能少的内存。因此,对于使用较少内存的应用程序,可以将状态数据保存在缓存中更长时间。

许多游戏在后台使用游戏服务。如果应用程序不活动,那么服务也有可能被操作系统杀死。

优化整体性能

早些时候,我们仅从编程角度讨论了性能优化。让我们讨论 Android 游戏性能优化的其他方面。

开发人员可以通过以下几点从设计到开发优化性能:

  • 选择基本分辨率

  • 定义可移植范围

  • 程序结构

  • 管理数据库

  • 管理网络连接

选择基本分辨率

从 Android 游戏开发的角度来看,选择基本分辨率可能是最重要的设计决策。基本分辨率定义了图形或视觉元素的比例。开发人员选择的分辨率越大,存储和处理时间就越多。基本分辨率还负责存储位图的质量和颜色信息。相对较低的分辨率不需要在可见资产中包含许多细节,这可以优化位图数据。然而,随着分辨率的增加,需要更多的数据来保留细节。最终,这对处理有重要影响。

随着技术的进步,Android 设备的分辨率越来越大。因此,开发人员现在选择更大的分辨率来支持更高范围的设备。

定义可移植范围

这也是设计阶段的优化。在这个阶段,开发人员需要决定支持的硬件平台范围。这包括各种配置。我们已经知道,Android 设备范围包括在内存、处理速度、图形质量等方面存在大量变化。

如果范围支持类似设备的可移植范围,那么优化变得更容易。然而,这并不适用于大多数游戏开发情况。通常,开发人员应将优化分为三个部分:

  • 低性能设备

  • 平均性能设备

  • 高性能设备

因此,理想情况下,应该有三层优化来正确定义可移植范围。

程序结构

程序结构是另一个非常重要的技术设计决策,对于性能和内存优化都很重要。这包括我们已经讨论过的所有编程优化参数。

此外,程序层次结构对性能也很重要。通常,开发人员会创建不必要的中间调用来解析多个层。一些单例类在这里有助于显著优化性能。正确的游戏状态机设计也有助于优化性能。

管理数据库

有许多游戏主要是数据驱动的。在这种情况下,需要正确管理数据库。

例如,一个问答游戏必须在数据库中维护一个问题库,以避免频繁更新游戏构建。数据库查询需要时间来执行,因为中间还有一个网络层。因此,游戏层发送查询到数据库。然后,数据库获取数据,相应地绑定并发送回游戏。然后,游戏必须解绑接收到的数据以使用它。使用最少的查询调用是最小化性能开销的唯一方法。使用更快的数据库也有助于游戏表现良好。

管理网络连接

现代游戏已经发展到多人游戏和服务器控制机制,这减少了频繁更新游戏构建的工作。在这两种情况下,网络连接需要以适当的方式实现。目前主要有两种类型的多人游戏架构:

  • 回合制多人游戏

  • 实时多人游戏

相对来说,管理回合制多人游戏系统比实时多人游戏系统要容易。还有另一种名为异步多人游戏的多人游戏模式。

每个网络调用都会导致性能延迟,因为游戏依赖于来自服务器的数据。因此,客户端-服务器架构需要优化,以实现以下目标:

  • 较少的延迟时间

  • 较少的层处理

  • 较少的对服务器的 ping 次数

增加帧率

性能优化的最终目标是提高帧率。高帧率自动提供流畅的游戏体验。然而,开发人员必须确保帧率效果在游戏中以流畅和效果的形式可见。

对于当前的移动游戏行业来说,2D 游戏或中等规模的 3D 游戏的平均 FPS 为 60 被认为是高性能。另一方面,大型 3D 游戏可能认为平均 FPS 为 30-35 是良好的性能。

具有更高 FPS 的高性能游戏为改善用户体验提供了进一步的视觉效果。这直接影响货币化。

性能优化的重要性

正如我们刚刚讨论的,性能优化直接影响帧率,而帧率又直接影响游戏体验。然而,性能优化还有其他重要性:

  • 由于非优化的程序,游戏可能会崩溃或进入无响应状态

  • 性能优化也直接影响内存

  • 性能优化可以扩大支持的硬件平台范围

常见的优化错误

游戏行业现在是增长最快的行业之一。为了跟上速度并在市场上站稳脚跟,许多公司计划缩短开发周期并限制优化。在这种情况下,开发人员通常会有意或无意地犯以下错误:

  • 编程错误

  • 设计错误

  • 错误的数据结构

  • 错误使用游戏服务

编程错误

编程是一个手动的过程,犯错是人之常情。因此,显而易见,没有错误和完全优化的游戏编码。然而,程序员可以通过几种方式最小化错误,以获得优化的游戏代码库。让我们讨论在 Android 开发游戏时程序员犯的主要错误。

程序员经常创建许多临时变量并忘记跟踪它们。通常,这些变量会占用不必要的内存并增加处理调用。

排序在游戏开发中被广泛使用。有几种排序算法。大多数情况下,开发者选择方便的技术而不是高效的技术。对于大型数组或列表,这可能会导致严重的流程延迟。

为了方便访问,使用太多静态实例也是一个不良的实践。使用静态可能会加快处理速度,但制作太多静态实例并不是一个好主意,因为它会在其生命周期内占用大量内存空间。许多程序员甚至忘记手动释放这些内存。

创建抽象层并广泛使用它们会使流程变慢。然而,这通常是一个良好的编程实践,但对于游戏编程来说,只在有限的情况下有所帮助。

方便的循环使用是游戏中另一个不良的编程实践。有几种处理循环的方法。程序员应该首先确定哪种方法最适合算法。

游戏编程大多是关于逻辑开发而不是技术。为某些任务建立完美的逻辑可能需要时间。许多游戏程序员并不考虑执行一个任务的多种方式。大多数情况下,这会留下很大的优化空间未被发掘。

设计错误

设计师在定义硬件范围和游戏范围时经常犯错误。这两个因素都是创建优化游戏设计的非常重要的因素。

另一个错误是将目标分辨率定错。目标分辨率直接影响艺术资源的大小。定错目标分辨率会导致不必要的缩放,增加额外的处理开销。

错误的游戏数据结构

数据结构是游戏编程中不可避免的一部分。Android 支持动态数组初始化。然而,许多开发人员更喜欢使用列表来存储数据。列表比数组慢得多。只有在绝对必要时才应该使用列表。

为数据驱动的游戏找到完美的数据结构是开发者的责任。适当的技术设计应该包括数据结构模型及其使用。

错误使用游戏服务

服务在某些时候非常有用。在现代游戏行业中,服务用于数据的下载/上传,推送通知,游戏中的深度链接,或者服务器连接。然而,服务会带来巨大的处理和内存消耗成本。运行服务也会导致大量的能耗。

因此,只有在没有其他办法时才应该强制使用服务。

最佳优化实践

有一些定义明确且合乎逻辑的优化技术可供选择。我们将讨论与 Android 游戏开发相关的主要范围和领域:

  • 游戏设计约束

  • 游戏开发优化

  • 游戏数据结构模型

  • 使用游戏资源

  • 处理缓存数据

设计约束

定义目标硬件平台并承认其限制总是最佳实践。技术设计可以根据它来构建开发约束。

可扩展性和可移植性也应该在设计游戏时确定。这应该给开发者一个初步的平台限制以及其他约束。我们已经讨论了设计优化。在进入开发之前,所有这些部分都应该进行评估。

在设计游戏时,需要固定目标屏幕大小和分辨率,并创建适合多种分辨率的布局。这是因为 Android 有许多不同的屏幕大小,正如前面所讨论的。

选择最低的 Android 版本和目标 Android 版本在构建开发项目时给开发者带来了优势,因为支持的 API 级别和平台功能已经定义好了。

开发优化

这是优化中最重要的部分之一。以下是一些成功进行优化开发过程的提示:

  • 使用尽可能多的安卓提供的文件夹结构,以便项目可扩展性。

  • 根据安卓提供的 dpi 列表使用资源格式。

  • 开发人员应避免缩放图像。这有效地减少了内存和处理开销。

  • 对于多种用途使用精灵也是创建动画的良好实践。

  • 平铺技术在减少内存消耗方面非常有用。

  • 覆盖onDraw()方法始终是刷新旧渲染管道并使用系统化绘制顺序的良好实践。

  • 尽可能使用基于 XML 的布局;然而,游戏对于这个安卓功能的使用范围非常有限。

数据结构模型

数据结构是游戏程序设计中不可避免的部分,无论游戏的规模如何。每个游戏都会处理各种目的的数据,如排序、搜索、存储等。

有许多数据结构模型可用于各种操作。每种操作都有其优点和缺点。开发人员必须根据需求选择最有效的方法。

让我们以数组和链表之间的数据存储比较为例。实际上,链表比数组更灵活和动态。然而,这种特性会导致处理速度较慢和内存消耗较高的代价。

开发人员可能并不总是需要存储动态数据。例如,如果需要存储板球队数据,那么数组就足够了,因为每边总是有 11 名球员,并且在游戏过程中不能修改。在这种特定情况下,这将使过程比使用链表更快更高效。

在另一种情况下,对于射击游戏,开发人员无法预测用户在游戏过程中可能发射的子弹数量。因此,队列数据结构将是最有效的,以处理所有发射的子弹。

类似地,堆栈和树结构可以在适当的情况下选择。对于排序和搜索算法也可以采用相同的方法。

资产使用技术

我们已经对游戏资产进行了分类。现在让我们从优化技术和最佳实践的角度来讨论它们。

艺术资产

一组艺术资产可以应用单独的优化技术。艺术资产是游戏的面孔。因此,必须确保视觉足够吸引人,以开始游戏。

正如我们已经讨论过的,更好的艺术资产会消耗内存和性能。然而,这可以最小化到一定程度。有几种艺术资产优化工具。然而,使用不合适的工具可能会导致数据丢失,最终导致视觉质量不佳。

艺术在视觉质量方面不应妥协。通常,艺术家开发的资产在游戏中不能完美反映,因为优化不当。

我们已经讨论了艺术资产应该如何制作。现在,让我们假设一些艺术只使用 8 位数据空间作为原始格式,但是导出时是 24 位格式。那么,开发人员可以使用工具将资产优化为典型的 8 位格式,而不影响视觉质量。

这个规则也适用于完全不透明的资产。开发人员可以摆脱透明信息,以获得优化的艺术资产。

音频资产

音频资产也是独立的资产。音频已经成为扩展用户体验的非常重要的资产。音频配置可以根据频率、位深度和压缩技术的广泛范围而变化。每种配置变化都有不同的处理和内存消耗水平。

因此,声音优化也是优化过程中非常重要的一部分。在安卓游戏开发行业中的常见做法是为 SFX 和音乐文件选择两种不同的音频格式。

开发人员通常忽视的一件事是音频信息数据。一些安卓设备有特定的频率上限,但当使用更多频率时,声音通常会更好。因此,确定安卓游戏声音的上限是一个技术设计层面的步骤。因此,每个声音都应该在接近范围内制作。

声音设计师需要在限制内保持质量。通过这种方式,音频资产可以在开发时进行优化。

其他资产

除了艺术和音频,游戏中可能还会使用其他数据资产。数据格式可以是任何形式,如二进制、文本、XML、JSON 或自定义。自定义格式基本上与二进制格式相同,只是加了一些加密。

在游戏开发中,将数据集分开使用是一种常见做法。单独的数据集有助于构建项目结构,并灵活地使用相同的代码来产生不同的输出。通常,开发人员更新数据源以更新完整的游戏体验,而无需创建新的 APK。这在长期内减少了开发时间,以便维护游戏并进行简单的更新。

从优化的角度来看,这些数据源应该被优化到足够快速地处理并且不消耗太多内存。然而,读写外部文件需要时间。通常,二进制文件是处理速度最快且大小最小的。然而,在读取二进制数据后,必须对其进行解析以在游戏中使用,这最终会增加处理时间。

最常用的数据格式是 XML 和 JSON。安卓库支持它们两者,包括通用解析器。开发人员可以在不进行额外处理的情况下获得可用的数据。然而,数据可以根据游戏的要求在游戏过程中进行操作。

处理缓存数据

缓存是一个类似于 RAM 的内存段,但比传统 RAM 更快的功能。处理器可以更快地访问这个段。因此,从逻辑上讲,缓存应该只存储经常使用的数据。

处理缓存数据的最佳方式是监控应用程序内存的使用情况。通常,操作系统应至少有 10%的空闲内存可用。经测试,一个应用程序可以使用平均 2%的总空闲内存。

然而,开发人员无法在技术上控制缓存。他们只能确保最常用的元素被优化得足够完美,以便执行器自动为它们使用缓存内存。

总结

优化是任何软件开发中最重要的任务之一,尤其是在游戏开发中,逻辑编程占据了技术编程的主导地位。对于技术编程来说,有大量的优化工具和技术可供使用,因为它有最常见的算法来实现。然而,在游戏编程的情况下,每个游戏玩法都需要不同的算法集。在许多情况下,还需要单独制作人工智能算法。因此,程序员很有可能需要找到一种有效的方法来优化新编写的算法。

我们已经讨论了在安卓游戏开发中所有可能的优化范围。技术优化是强制性的,因为它有固定的指导方针要遵循。然而,逻辑开发将取决于游戏算法及其要求。因此,对于游戏开发人员来说,优化安卓游戏是额外的努力。

有时,开发人员会过度优化游戏。这是不推荐的。过度优化通常会降低游戏的质量。因此,在技术设计时,应声明优化情况。

大多数大规模开发过程都有一个单独定义的优化任务集。一些开发者选择开发动态优化过程。这意味着开发者在不同阶段、不同规模上优化游戏。这两种过程都是有效的,但第一种在逻辑上更合理,因为定义一个单独的任务总是会给出整体优化的暂定时间。这有助于更好地管理整个游戏开发过程。

所有优化过程都经过测试阶段的验证。所有设计、工程和艺术工作都在游戏开发的这一阶段进行测试。我们将在本书的下一章更深入地讨论测试。

第九章:测试代码和调试

“无 bug 的产品是一个神话”是开发行业中常见的短语。一个无问题和无故障的应用程序或任何其他产品在理性上是不可能的。然而,开发人员总是可以尽量减少 bug 和问题的数量,以便游戏可以尽可能少地出现问题,并以最大可能的效率支持最多的平台。

我们将通过以下主题讨论 Android 游戏开发中各种调试方面的范围:

  • Android AVD

  • Android DDMS

  • Android 设备调试

  • 监控内存占用

  • 不同调试语句的战略放置

  • Android 游戏中的异常处理

  • 在跨平台引擎中工作时调试 Android

  • 最佳测试实践

Android AVD

AVD 是调试 Android 游戏最重要和重要的部分。在最初阶段,该概念始于模拟器。有一些预定义的模拟器可以用于在开发 PC 上运行构建。Android 模拟器提供了类似实时设备的界面。

AVD 具有一些功能,可以提供设备 RAM、Android 版本、屏幕大小、显示 dpi、键盘和不同的视觉皮肤。旧版 AVD 大多看起来都一样。

Android AVDs

在当前版本的 Android Studio 中,提供了大多数 Android 设备类别。开发人员可以根据目标开发平台创建 AVD。

类别如下:

  • Android 手机

  • Android 平板电脑

  • Android 电视

  • Android 可穿戴设备

AVD 可以通过 Android SDK 中提供的 AVD 管理器工具来创建或操作。AVD 管理器可以管理 AVD 的每个属性。该工具还可以帮助开发人员创建自定义 AVD。

让我们来看看每个不同 AVD 的属性因素:

  • 名称

  • 分辨率

  • 显示尺寸

  • Android 版本 API 级别

  • Android 目标版本

  • CPU 架构

  • RAM 数量

  • 硬件输入选项

  • 其他选项

  • 扩展 AVD 设置和创建

AVD 的名称

名称仅用于识别 AVD。可以分配任何内容,并且以后可以更改。在创建时也可以更改预定义的 AVD 名称。

AVD 分辨率

AVD 分辨率是可见性的最重要因素之一。有一些预定义的分辨率标准,但它们也可以更改。如今,大多数开发人员选择在实际硬件平台上广泛使用的分辨率。

分辨率的另一个用途是检查和验证游戏的显示可移植性。大多数游戏都是以目标分辨率制作的。然后,可以在各种分辨率上测试游戏以检查兼容性。

通常,如果纵横比相同,多个分辨率不会产生任何问题。然而,在 Android 的情况下,我们可以找到不同设备的多个纵横比。AVD 的分辨率因子有助于适应游戏并检查其在多个纵横比下的兼容性。

AVD 显示尺寸

这是 AVD 上的可见空间或可见显示区域。一个高分辨率的 AVD 可以有一个小的显示区域。这直接意味着 AVD 具有高 dpi 值,这意味着更高的显示质量。

AVD 的这一部分有助于确保游戏的视觉质量。然而,在开发系统中设置实际显示区域并不总是可能的,因为开发系统有自己的显示限制。

Android 版本 API 级别

在开发 Android 游戏时,开发人员需要将 API 使用限制在某个版本上。API 版本可能会在将来的 Android 版本中被弃用,甚至停止使用。为了检查这一因素,开发人员可以为 AVD 设置 API 版本。

Android 目标版本

这是将用于运行 AVD 的 Android 版本。这可以验证清单目标 Android 版本和最低版本范围。

CPU 架构

Android 设备主要使用三种 CPU 架构:armeabi、armeabi-v7 和 x86。这对游戏没有直接影响。然而,处理速度和质量会随着 CPU 架构的不同而有所变化。

开发人员应该记住,实际设备上具有不同 CPU 架构的游戏性能总是会与 AVD 上的表现不同。因此,它可能给开发人员一个性能的想法,但需要在实际设备上进行测试。

RAM 数量

RAM 数量指定了 AVD 可用的总内存量,可用于检查游戏在各个级别的内存消耗。

最好预测各种设备的内存溢出问题。通过在 AVD 上同时运行多个应用程序,可以创建一个实时克隆。默认值设置为 66MB。开发人员可以根据需求设置任何值。

外部存储也可以定义为虚拟设备的 SD 卡。

硬件输入选项

在 Android 设备上,可以有许多类型的硬件输入分布在各种硬件中。最常见的变体如下:

  • 触摸屏

  • 触摸板

  • 键盘

  • 自定义控制器

  • 硬件按钮

许多硬件平台选择了这些变体的组合。AVD 为所有这些输入系统创建了一个虚拟系统。

其他选项

还有一些其他选项可供操作。如果开发系统连接了摄像头,那么 AVD 也可以使用前置和后置摄像头。

此外,虚拟加速计、传感器等可以与 AVD 关联。

扩展 AVD 设置

通过现代 AVD 管理工具可以制作自定义 AVD。开发人员可以设计具有自定义外观和完全自定义硬件配置的虚拟设备。

Android DDMS

DDMS 可用于分析运行中应用程序的所有运行时详细信息,如内存消耗、进程调用等。

Android DDMS 的主要功能是提供端口、在设备上进行屏幕截图、线程详细信息、堆详细信息和 Logcat 处理。此服务可用于欺骗呼叫和短信。

Android DDMS 广泛用于设备调试。特别是在游戏开发过程中,它经常被用作逐行调试系统。这对于识别不需要的加载对象和资产,以及跟踪运行时异常非常有用:

Android DDMS

Android DDMS 可用于执行以下活动。

连接 Android 设备文件系统

DDMS 可以连接到设备文件系统,并提供基于文件浏览器的操作,通过 PC 在设备上复制、修改或删除任何文件。然而,这种方法或功能对 Android 游戏开发并不是非常重要。

性能分析方法

另一个有趣的 DDMS 功能是对某些方法的分析或跟踪矩阵。它提供以下主题的信息:

  • 执行时间

  • 操作次数

  • 单元格数量

  • 执行期间的内存使用

扩展此功能,开发人员甚至可以通过调用startMethodTracing()stopMethodTracing()来控制方法的数据分析。

开发人员需要关注两件事:

  • 在 Android 2.1 之前,设备上必须安装有 SD 卡,并且应用程序需要有读写权限

  • 从 Android 2.2 开始,设备可以直接将分析数据流式传输到开发 PC

线程信息监控

DDMS 提供了所选设备上每个进程运行的每个线程的详细信息。然而,游戏大多在单个线程上运行。随着设备的不断改进,游戏也在使用多线程功能来支持渲染、处理、文件 I/O 和网络等各种操作。

堆信息监控

DDMS 为运行中的进程提供了堆使用情况。对于游戏开发人员来说,跟踪执行期间的游戏进程堆非常有用。

跟踪内存分配

这对于跟踪运行时对象的每个内存分配非常有用。这可以给出每个类的每个特定对象的每个细节。这意味着开发人员可以找出每个类占用多少内存。这有助于以更有效的方式实现内存优化。

监控和管理网络流量

从 Android 4.0 开始,DDMS 具有详细网络使用选项卡,用于跟踪游戏何时发出网络请求。使用此功能,开发人员可以监视网络数据传输。这个选项对于优化网络开发非常有用。它可以通过在使用网络套接字之前对其应用“标签”来区分不同的流量类型。

使用 Logcat 跟踪日志信息

日志是跟踪几乎任何东西的最有用的调试技术。在运行时正确使用日志来检查某些对象的数据或值是一个很好的实践。对于游戏的逻辑开发非常有用。

在游戏开发中,不同游戏的逻辑需求将不同。因此,必须有大量的代码是第一次编写的。预定义的测试用例是不可用的。可以使用 DDMS 中的 Logcat 来克服这种不足。

Logcat 提供以下类型的日志信息:

  • 冗长的

  • 调试

  • 错误

  • 警告

  • 信息

模拟设备操作

正如我们之前讨论过的 Android 虚拟设备一样,DDMS 也可以在 AVD 上工作。因此,模拟实时场景以调试正在开发的游戏变得更加容易。

最常用的仿真如下:

  • 模拟来电

  • 模拟收到消息

  • 模拟运行时网络状态更改

这三种情况是运行时最常见的情况。因此,这些情况可以在没有物理设备的情况下进行检查。自 Android 设备开始以来,中断处理一直是困难的。事实上,如果中断没有得到正确处理,这对程序员来说可能是一场噩梦。

在中断后,游戏崩溃/冻结/重新启动的常见问题。很多时候,一些不必要的服务或进程可能会中断,并且它们可能会在中断期间改变游戏状态。在 AVD 上模拟每种可能的中断总是加快调试或中断处理过程的一个额外优势。

Android 设备测试和调试

Android 设备调试是任何 Android 游戏开发过程中最重要的部分。让我们将这个主题分成两个部分:

  • 设备测试

  • 设备调试

设备测试

游戏开发者面临的主要挑战是在大量不同的设备上运行游戏。这些不同的设备包括不同的显示器、不同的分辨率、不同的 Android 操作系统版本、不同的处理器和不同的内存容量。由于这些原因,Android 设备测试很重要,必须付出很大的努力和计划。

通常,在游戏开发周期中,第一点测试由开发人员进行。这个过程确保游戏在设备上运行。

然后,测试人员或一组测试人员从各个方面在不同的设备上测试游戏。这是设备测试的主要部分。

通常,主要的测试阶段根据游戏开发阶段分为四个部分:

  • 原型测试

  • 完整测试

  • 回归测试

  • 发布测试或运行测试

换句话说,在每个类别中有类似的分布,如下所示:

  • Pre-alpha 测试

  • Alpha 测试

  • Beta 测试

  • 发布候选测试

还有许多其他可能遵循典型软件测试的测试程序。然而,在游戏开发中,通常会遵循这些方法。让我们简要描述这些阶段。

原型测试

开发人员和设计师一起开发基本游戏想法的可玩阶段,具有初始的游戏规则。这些规则和游戏玩法在原型测试阶段进行测试。

理想情况下,核心游戏玩法在这个阶段进行测试,以分析游戏概念的可行性、潜力和范围。

原型测试可能是游戏开发过程中最重要的部分。这个阶段决定了游戏概念的未来,并有助于为概念开发元游戏和货币化模型。

全面测试

通常情况下,在每个阶段提交的前几个构建被提交进行全面测试。这会揭示游戏的每一个可能的问题,包括崩溃、冻结、视觉问题、可玩性、游戏规则和设计缺陷。

大多数问题通常在这个阶段报告,这最终意味着该游戏构建的可能完成时间和工作量。

回归测试

回归测试是在全面测试之后进行的。开发人员、设计师和制作人在全面测试期间对每个问题进行评估。他们选择要解决的问题,问题解决后,提交给测试团队进行回归测试。

在回归测试中,测试人员通常选择问题并专门检查它是否真的解决了。如果问题在修复的构建中发生,则测试人员会重新打开问题进行下一个回归周期。这个周期会持续,直到所有报告的问题都得到解决。

发布测试或运行测试

这可能是游戏测试中最机械化的测试阶段。在这个阶段,测试人员在各种目标设备上运行通过的回归测试构建,只是为了检查游戏是否在该硬件上运行。这就是为什么这个阶段经常被称为“运行测试”的原因。

尽可能多地使用物理设备进行此段测试以进行兼容性检查。在这个测试阶段之后,创建最终设备支持列表。几乎不可能安排所有可用的设备并对它们进行运行测试。因此,开发人员根据其配置和性能对设备进行分组。行为相似的设备被放在同一类别中,实际上只有一两个设备被安排用于整个组的运行测试。

设备调试

我们已经看到设备测试主要是测试人员的工作。现在,我们将看到设备调试基本上是开发人员的工作。然而,通常情况下,开发人员和测试人员都会进行调试。

在游戏行业中,设备调试主要用于查找运行时崩溃、冻结、内存问题、网络问题和性能问题。通过设备调试,开发人员收集以下信息:

  • 运行时最大堆消耗

  • 各种设备的平均 FPS 或多套设备

  • 不必要的加载对象

  • 硬件按钮行为

  • 网络请求和响应

使用断点

在设备调试的情况下,断点非常有用。游戏线程在断点处暂停,可以通过 DDMS 获得状态信息。游戏编程主要涉及定制算法,可能在运行时产生一些异常行为。在这种情况下,断点非常有用。开发人员可以在断点后逐行调试逻辑,以找到并修复行为的根本原因。

监视内存占用

内存占用是在运行时使用内存的迹象和方式。从游戏内存使用优化的角度来看,监视内存占用非常重要:

  • 检查日志消息

  • 检查堆更新

  • 跟踪内存分配

  • 检查整体内存使用

  • 跟踪内存泄漏

检查日志消息

使用日志消息一直是最有效和最直接的调试技术。消息日志对于跟踪程序控制流和运行时对象跟踪非常有用。

Dalvik 消息日志

Dalvik 消息日志对于跟踪内存很有用。每当发生垃圾收集时,垃圾收集器可以通过 Dalvik 日志消息打印以下信息:

  • 垃圾收集原因:此信息显示了触发垃圾收集的原因。原因可以是GC_CONCURRENTGC_FOR_MALLOCGC_HPROF_DUMP_HEAPGC_EXPLICITGC_EXTERNAL_ALLOC

  • 释放的内存量:此部分说明了垃圾收集器释放的内存量(以 KB 为单位)。

  • 当前堆内存状态:这显示了堆内存使用的百分比和活动对象内存/总堆内存。

  • 外部内存状态:可能会有一些分配外部内存的操作。这部分显示了分配的内存/垃圾收集限制。

  • 垃圾收集器暂停时间:暂停时间在垃圾收集开始时和结束时触发。通常,在堆较大的情况下,暂停时间会更长。

ART 消息日志

ART 消息日志还能够显示或跟踪内存印记。但是,除非明确请求,否则不会触发。

如果垃圾收集器暂停时间超过 5 毫秒,或者垃圾收集器执行时间超过 100 毫秒,则会打印垃圾收集器日志。在 ART 的情况下,以下信息可以显示为日志:

  • 垃圾收集原因:在 ART 日志消息中,开发人员可以将ConcurrentAllocExplicitNativeAllocCollectorTransitionHomogeneousSpaceCompactDisableMovingGcHeapTrim作为收集原因。

  • 垃圾收集器的名称:ART 有几种不同的垃圾收集器可以参与收集过程。可以通过收集日志的字段了解名称。ART 有这些收集器:Concurrent Mark SweepCMS)、Concurrent Partial Mark SweepCPMS)、Concurrent Sticky Mark SweepCSMS)和 Marksweep plus Semispace。

  • 释放的对象计数:这显示了垃圾收集器从内存中释放的对象总数。

  • 释放的内存量:这显示了垃圾收集器释放的总内存量。

  • 释放的大对象计数:这显示了从大对象范围中由收集器释放的对象数量。

  • 从大对象中释放的内存量:这显示了从大对象范围中由收集器释放的内存量。

  • 当前堆内存状态:这与 Dalvik 日志中的相同——活动对象计数/总堆内存。

  • GC 暂停时间:在 ART 暂停时间部分,这与运行垃圾收集器修改的对象引用数量成正比。与 Dalvik 不同,ART CMS 垃圾收集器在收集过程结束时只有一个暂停时间。

检查堆更新

开发人员可以每次更新时检查堆使用情况。这可以清楚地显示内存印记。可以使用多种工具来监视堆。市场上有很多设备内存监视器可用。DDMS 设备监视器就是其中之一。它是一个强大的工具,在游戏运行时观察堆使用情况。

Android SDK 自带了一个内置的设备监视器,位于<sdk>/tools/monitor

Android Studio 中的内存监视器对于 Android Studio 用户非常有用。监视器可以与 Android 应用程序交互,以观察每次垃圾收集时的堆更新。通过这种方式,Android 开发人员可以了解应用程序各个部分的确切内存使用情况。

有时,开发人员会打开/关闭方法来检查确切的堆使用情况。因此,进一步优化变得更容易。

跟踪内存分配

这对于内存优化很有帮助。内存分配可以通过分配跟踪器进行监视。

在一定阶段的内存优化之后,需要进行内存分配跟踪。这有助于识别每个对象的内存分配。通常,许多无用的对象会留在内存中。开发人员可以识别这些对象并将它们移除,以实现更大的内存优化。

内存分配跟踪器在 Android SDK 的设备监视器和 Android Studio 的分配跟踪器中都可用。

然而,并不需要从性能关键的代码路径中删除所有分配;然而,分配跟踪器可以帮助开发人员识别代码中的重要问题。例如,一些应用程序可能在每次绘制时创建一个新的Paint对象。将此对象移入全局成员是一个简单的修复方法,有助于提高性能:

跟踪内存分配

让我们快速查看所获得的分配信息:

  • s1:这是当前正在跟踪的对象包

  • s2:这显示已选择分配选项卡

  • s3:这用于启动/停止跟踪对象

  • s4:这更新包分配

  • s5:这显示分配详情

在游戏开发中,内存中的对象数量是巨大的,因此手动跟踪对象的分配和释放非常困难。这个监控工具有助于发现在优化过程中可能被忽视的隐藏点。

检查整体内存使用

Android 游戏的整体内存使用分布在 RAM 的不同段中。这为应用程序的性能和内存安全性提供了一个大致的概念。

基本上有两种类型的分配。

私有 RAM

这是游戏在运行时使用的专用内存部分。Android 操作系统将此内存分配给应用程序。私有 RAM 分为两个部分:

  • 清理 RAM

  • 脏 RAM

私有脏 RAM 是最昂贵的,因为它只能被特定应用程序使用(在我们的情况下,是一个 Android 游戏)。

比例设置大小(PSS)

这部分 RAM 被多个进程使用。基本上是共享内存。任何应用程序进程独有的 RAM 页面直接影响其 PSS,而与其他进程共享的页面仅按比例影响 PSS 值。

跟踪内存泄漏

内存泄漏对软件开发构成严重威胁。因此,追踪内存泄漏并解决它是绝对必要的。当一个进程分配内存并丢失引用指针时,就不可能在进程内释放内存。

有一些调试工具可以追踪内存泄漏。然而,还有另一个免费且更有效的解决方案。开发人员可以随时监视内存消耗。游戏在更新循环中运行。因此,可以跟踪不同游戏周期的内存峰值。如果峰值不断增加,意味着内存的分配/释放存在泄漏。现在,开发人员可以检查每个对象的大小并追踪泄漏。这个过程的另一个好处是在内存中找到不必要的对象以及内存泄漏。

不同调试语句的战略放置

调试语句是任何开发过程中最重要的部分。通过调试语句可以跟踪和追踪任何事物。然而,作为系统打印调用,每个调试语句都会对性能产生影响,这直接影响运行时的 FPS。这就是为什么调试语句的放置策略是绝对必要的。

让我们看看与以下类别相关的策略:

  • 内存分配

  • 跟踪对象状态

  • 检查程序流程

  • 跟踪对象值

内存分配

在游戏开发对象周期中,对象应该在初始化时分配一次,并在销毁时释放。然而,由于手动编程错误,开发人员忘记释放内存。在这种情况下,当系统自动调用垃圾收集器时,内存将被清理。这样就观察到了性能上的延迟。

现在,作为追踪这种错误的战略放置,应该在构造函数和析构函数中放置两个调试消息。

此外,初始化每个对象后放置一个调试语句可以确保对象成功初始化。这可以显示它消耗的内存量。

在运行时跟踪对象状态

对象可以在游戏过程中的任何时候初始化。现在,初始化过程中的任何外部依赖都可能导致分配失败。因此,对象进入空状态,如果不正确处理,可能会导致异常。

一个成功的调试语句和一个失败的调试语句(带有原因)可以帮助开发人员纠正问题。

很多时候,错误的释放也会改变对象的状态。因此,调试语句可以确定这个地方。开发人员可以借助调试语句来解决对象和程序流的问题。

检查程序流程

每个方法中的调试语句清楚地显示了调用层次结构和程序流程。一个模块化的程序可以通过这个系统进行测试。然后,每个模块的开始都可以用一个调试语句进行测试。

任何错误或不必要的调用都可以通过这个过程来移除或纠正。正确的程序流程可以确保运行时的一定帧率。因此,这种方法可以用来优化性能。

跟踪对象值

即使对象成功初始化,内容可能也不正确。因此,放置一个调试语句来检查加载/初始化的内容是必要的,以避免未来的冲突。

当从外部来源加载数据时,这是非常有用的。调试语句用于验证初始化后加载的数据。任何程序模块都可以使用对象跟踪方法进行设计,从而得到更好的编程结构。

Android 游戏中的异常处理

异常处理可能不是调试的一部分,但它有助于减少异常和不必要的应用程序崩溃。

Android 中的异常处理与 Java 异常处理相同。

语法

异常处理的标准 Java 语法如下:

try
{
  // Handled code here
} 
catch (Exception e)
{
  // Put debug statement with exception reason
}
finally
{
  // Default instruction if any
}

可疑的代码应该放在try块中,并且异常应该在catch块中处理。如果模块需要执行一些默认任务,那么将其放在finally块中。catchfinally块可能并不总是在异常处理中定义。然而,建议在每个try块失败时处理异常,这是一个良好的编程实践。这个过程需要分析模块,找出任何脆弱的代码块。

这是一个处理异常的简单例子,还包括其他易受攻击的默认任务。

这是最初的程序设计:

try
{
  // Task 1 which might throw exception
}
catch ( Exception e)
{
  // Handles exception
}
finally
{
  // Task 2 which might throw exception
}

程序应该这样写:

void func1()
{
  try
  {
    funcTask();
  }
  catch ( Exception e)
  {
    // Handles exception
  }
}

void funcTask()
{
  try
  {
    // Task 1
  }
  finally
  {
    // Task 2
  }
}

开发人员应该记住以下几点:

  • try块只能与catch块一起使用

  • try块只能与finally块一起使用

  • try块可以与catchfinally块一起使用

  • try块不能单独使用

  • 嵌套的try...catch是可能的,但不建议作为良好的编程实践

范围

根据异常类型和原因,有很多预定义的异常范围。然而,在游戏开发过程中处理的主要异常如下:

  • 空指针异常

  • 索引越界异常

  • 算术异常

  • 输入/输出异常

  • 网络异常

  • 自定义异常

空指针异常

这是游戏开发中最常见的异常之一。当代码中引用任何空对象时,会抛出NullPointerException。开发人员应该跟踪对象的初始化和使用来纠正这个问题。

这是一个例子:

class A
{
  public int num;
  public A()
  {
    num = 10;
  }
}
// some method in other class which is called during runtime.
void testFunc()
{
  A objA = null;
  Log.d("TAG", "num = " + objA.num);
}

这将抛出一个异常,因为objA被初始化为 null。因此,这个对象在内存中找不到,引用指针也不存在。现代智能编译器可以在编译时检测到这个明显的异常,但代码可能是这样的,我们定义了另一个包含testFunc()方法的类:

class RootClass
{
  public A objA;
  public RootClass()
  {
    objA = null;
    testFunc();
  }

  void testFunc()
  {
    Log.d("TAG", "num = " + objA.num);
  }
}

在这种情况下,大多数智能编译器无法检测到即将发生的异常。为了处理这个问题,开发人员应该在testFunc()方法中添加几行代码:

void testFunc()
{
  try
  {
    Log.d("TAG", "num = " + objA.num);
  }
  catch (NullPointerException e)
  {
    Log.d("TAG", "Exception:: " + e);
  }
}

索引超出范围异常

当访问一个索引地址时抛出此异常,该地址应该是连续内存分配的一部分,但实际上并不是。在游戏开发中,最常见的是ArrayIndexOutOfBoundsException

例如,如果一个数组包含五个字段,程序尝试访问超过五个字段,那么就会抛出此异常。让我们看一下这段代码:

int[] arrayNum = new int[5];
for ( int i = 0; i < 5; ++ i)
  arrayNum[i] = i;

Log.d("TAG", "arrayNum[5] is " + arrayNum[5]);

在这里,异常将出现在日志语句中,因为arrayNum[5]表示数组中的第六个元素,而该元素不存在。

算术异常

数学表达式可以表示一个未定义的值,但在编程方面,“未定义”无法定义。因此,会抛出ArithmeticException

例如,如果解释器尝试将任何值除以零,那么结果变为未定义,这将作为异常抛出。在计算 tan 90°的值时也会出现相同的结果。

一个简单的情况可能是这样的:

void divideFunct(int num, int deno)
{
  try
  {
    Log.d("TAG", "Division Result = " + (num / deno));
  }
  catch (ArithmeticException ae)
  {
    Log.d("TAG", "number cannot divided by zero");
  }
}

输入/输出异常

计算系统的输入/输出功能取决于其硬件。然而,在游戏中,输入/输出异常发生在读/写操作期间。大多数游戏都是数据驱动的。基本原则是向游戏软件提供数据以控制游戏中的元素。这些数据通常存储在单独的二进制、文本、XML 或 JSON 文件中。

作为位于特定路径的单独文件,这些文件可能会丢失,特别是当这些数据文件从其他位置下载时,因为可能会出现连接中断,文件可能无法保存。在这种情况下,当游戏软件尝试加载这些文件时,就会抛出IOException

让我们看一个快速的例子:

try 
{
  File dir = Environment.getExternalStorageDirectory();
  File objFile = new File(dir, "tmpPath/myfile.txt");
}
catch (IOException e) 
{
  Log.d("TAG", "Error reading file :: " + e);
}

网络异常

这是多人游戏的时代,这需要强制性的网络连接。因此,应用程序取决于现有网络连接的质量和连接性。然而,移动网络连接状态可能随时发生变化。通常,游戏开发人员忽略网络错误,导致游戏运行中崩溃、冻结或某些故障。

常见的异常处理包括HttpRetryExceptionUnresolvedAddressExceptionNetworkErrorException。如果任何 HTTP 请求不能自动重试,那么就会抛出HttpRetryException。如果应用程序想要连接到某个地址,但该地址未找到,那么就会抛出UnresolvedAddressExceptionNetworkErrorException用于处理任何网络故障,如网络丢失/中断、使用错误协议的网络等。

自定义异常

这通常用于两个目的:

  • 游戏异常处理

  • 游戏支持工具异常处理

游戏过程中可能会在运行时创建逻辑异常。然而,在游戏开发中,这种异常的范围很小。大多数 Android 开发人员不会这样做。

工具编程也是游戏开发过程中的重要部分。因此,如果需要,可能的异常应该由自定义异常处理。

在使用跨平台引擎进行 Android 调试

现代游戏编程通常不针对单一平台。大多数游戏都是跨平台的。跨平台游戏引擎对这种开发非常有用。

大多数引擎都配备了内置的性能分析器,并提供一些调试游戏的功能。然而,性能分析器功能完全依赖于特定游戏引擎的制造商。

所有本地平台都提供完整的调试信息。游戏引擎创建一个包装器,自动从一个平台配置切换到另一个,并在一个共同的用户界面中显示性能分析器的详细信息。

然而,这些跨平台调试工具会消耗额外的处理和内存。在某种程度上,它们限制了游戏资源的消耗到一定水平,并带有误差范围。

最佳测试实践

在 Android 游戏开发行业中有许多用于测试的标准。测试确保应用程序发布后的正确性、稳定性、功能行为和耐久性。Android 游戏测试的最常见方法是手动测试。

然而,这个过程绝对不是最佳的。作为 Android 开发人员,单元测试始终是一种最佳实践,可以节省时间并获得准确的测试结果。

工具和 API

有几种工具和 Android API 可用于执行测试过程。其中一些是内置的,如 Android 测试支持库、Dumpsys、Monkeyrunner 等。

大多数这些测试工具可以通过命令行触发,并通过 Android 调试桥运行。

Monkey 工具创建一个虚拟环境,模拟用户的点击、触摸、滑动等操作,以确定实时结果。Monkey 可以通过以下命令运行:

adb shell monkey –p <Game Package Name> <Event Count>

Dumpsys 在 Android 应用程序运行时提供系统状态。可以通过以下命令触发:

adb shell dumpsys <option>

Dumpsys 能够提供关于运行服务、输入系统、网络状态、RAM 使用等信息。

测试技术

在游戏行业中,通常使用两种测试技术:自动化测试和手动测试。我们已经简要讨论了手动测试过程。现在让我们来看看自动化测试。

自动化测试需要工具和额外的编程工作。游戏 UI、内存消耗、网络连接和输入系统测试可以自动化。单独的测试在模拟器上或实际设备上运行,以确定测试结果,并将其保存在开发系统的指定位置。

可以编写单元测试代码来验证游戏的单个模块的逻辑。单元测试可用于测试应用程序的最小可能组件,如元素、类和方法。单元测试进一步分为两个阶段:

  • 本地测试

  • 仪器化测试

本地测试

这种类型的单元测试在本地机器上运行在 JVM 上。这节省了大量的测试时间。本地测试要么不依赖于 Android 框架,要么依赖有限,可以通过虚拟对象满足。

仪器化测试

仪器化测试对 Android 框架有完全的依赖,必须在 Android 模拟器或 Android 设备上运行。这种测试技术用于测试 Android 游戏的运行时行为。它可以提供正在运行的应用程序的所有系统和调试信息。然而,这种技术不能轻易地与虚拟对象一起使用。开发人员需要在 Android 环境中定义测试对象数据,然后才能运行测试。

总结

任何开发过程如果没有质量和性能保证都是不完整的。测试是开发阶段,需要在技术和逻辑上验证游戏是否能在真实市场中运行。

测试、调试和分析游戏的阶段确保了针对目标 Android 平台范围的最佳质量。通常,Android 游戏在少数 Android 设备上可以运行,但在所有目标设备上都不能运行。开发人员可以通过详细的测试程序识别和解决一些特定设备的问题。

第十章:Android 在 VR 游戏中的范围

从简单的角度来看,“虚拟”和“现实”是两个相反的词。因此,一个自然的问题是它们如何能一起表示某种意义。这个短语描述了通过数字计算系统体验想象的或真实环境的经历。

游戏环境也是想象的或实时的。然而,这些环境不会通过触摸、气味、声音和视觉来呈现实时体验。因此,游戏在虚拟现实VR)的帮助下有了新的探索范围。

让我们通过以下主题探讨 VR 在 Android 游戏中的概念和范围:

  • 理解 VR

  • 游戏中的 VR

  • Android 在 VR 的未来

  • 为 VR 设备开发游戏

  • Cardboard SDK 简介

  • 使用 Cardboard SDK 开发游戏的基本指南

  • 通过 Google VR 进行 VR 游戏开发

  • Android VR 开发最佳实践

  • Android VR 游戏市场的挑战

  • 扩展的 VR 游戏概念和开发

理解 VR

在数字计算世界中,VR 意味着数字计算创建的实时环境。这意味着环境并不存在于地球上,但可以通过数字计算体验。然而,并不总是必须 VR 总是复制真实环境。它有能力复制一个想象的世界或环境,可以显示在计算机屏幕或 VR 头盔(头戴式显示器)设备上(图片来源:lh3.ggpht.com/uv8mx61-jsrbcu-EPNw1wIi4BCXg7338alepVlr7xKbKJf7eZ9EXT2U3roA8SWx1RC8=h900-rw):

理解 VR

Shadowgun VR 游戏的实际屏幕显示

VR 的演变

VR 概念似乎代表了现代技术,但事实是 VR 概念是在 20 世纪第二季度引入的。1935 年,斯坦利·G·韦恩鲍姆写了一篇名为《皮格马利翁的眼镜》的短篇科幻小说,在其中发现了一个护目镜的描述。护目镜描述了通过触觉和气味的虚拟体验的全息记录。从技术上讲,它定义了虚拟现实。

在 20 世纪中叶,这个概念通过视觉、气味、触觉和声音的虚拟化得到了改进。最终,在 1968 年,伊凡·萨瑟兰和他的学生鲍勃·斯普劳尔创造了世界上第一个 VR 头戴显示设备。

现代 VR 系统

现代 VR 设备在 1990 年后得到了发展。它们更轻,具有更好的显示和计算设备。世嘉在 1991 年为街机游戏和游戏机开发了世嘉 VR。该设备配备了液晶显示屏、立体声耳机和惯性传感器。

后来,在 21 世纪之后,VR 设备在许多方面得到了改进。许多技术公司对开发更好的 VR 系统表现出了浓厚的兴趣。如今,VR 设备在市场上是商业化的。VR 现在已经集成在最新的移动设备中,配备了输入控制器系统。

有许多公司开发在 VR 设备上运行的应用程序,用于多种目的。VR 技术的许多其他用途正在不断推出和改进。

使用 VR

VR 每天都在现代世界的许多领域扩展。让我们快速看一下 VR 可用性的领域:

  • 视频游戏

  • 教育和学习

  • 建筑设计

  • 美术

  • 城市设计

  • 动态图片

  • 医疗疗法

视频游戏

视频游戏在技术上由显示、声音和各种类型的交互系统组成。VR 设备已被证明具有运行游戏应用程序所需的所有必要组件。VR 的游戏化始于 20 世纪末。从那时起,VR 一直被用于游戏行业。

游戏基本上是一个互动娱乐系统。VR 只是支持视频游戏系统的环境。VR 系统可以将用户带入游戏世界与元素进行互动。

我们将在本章后面详细讨论 VR 在游戏中的作用。

教育和学习

实地考察和教育科目的可视化对学习过程有很大的影响。很多时候,不可能对每个科目提供实际的课程。虚拟现实有助于在许多教育主题上创造虚拟、实际和视觉上的影响。许多学院和培训师使用这种方法进行更好的教学。

培训是虚拟现实教育的另一个重要方面。例如,虚拟现实被用来培训飞行员在发达国家驾驶战斗机。这降低了事故的发生几率,培训候选人可以体验驾驶喷气式飞机的实时感受。

它被广泛用于培训医学生进行各种领域的治疗。

建筑设计

虚拟现实在建筑设计中被广泛使用。它可以在不实际实施的情况下浏览拟议的设计。许多建筑公司使用虚拟现实来展示设计。

有许多虚拟现实软件可以从建筑设计的数字副本中构建虚拟现实应用程序。

美术

这是虚拟现实的一个较少为人所知和较少被探索的用途。然而,许多优秀的艺术家已经利用虚拟现实来创造一个可导航的艺术虚拟世界。一些艺术博物馆可以通过虚拟现实技术进行虚拟参观。

城市设计

在现代世界中,城市设计和规划使用虚拟现实来模拟和验证设计。城市设计也用于发现设计中的漏洞和缺陷。在城市/城镇重建、交通规划、景观设计等方面,虚拟现实技术使城市设计变得更加容易。

电影

我们可以找到一些电影运用了虚拟现实技术的概念。电影《阿凡达》就是一个很好的例子。整个概念都建立在虚拟现实技术之上。这个概念描绘了一个虚拟的生活和活动,借助技术,角色可以在现实中不在场的情况下体验虚拟世界。

电影通过虚拟现实模拟已经变得多维化。如今,观众的体验通过虚拟现实得到了提升。

医疗疗法

虚拟现实疗法VRT)在医学科学中非常受欢迎,特别是在心理治疗中。据记录,全球虚拟现实疗法的成功率超过 90%。

虚拟现实疗法主要用于治疗恐高、恐飞、恐虫、恐动、公开演讲等人群,创造一个受控的虚拟环境。

Android 游戏中的虚拟现实

在 Android N 的最新版本发布之前,Android 并没有直接支持虚拟现实。谷歌意识到虚拟现实是应用的未来。此前,谷歌发布了 Cardboard SDK 来开发 Android 上的虚拟现实应用。这个 SDK 仍然存在于市场上,并被广泛使用。

市场上已经有许多针对 Android 虚拟现实头盔的游戏。虚拟现实头盔的数量日益增加。虚拟现实以前并不是主流的 Android 游戏开发的一部分,但据信它很快将成为主流,因为 Android 现在已经包含了一个专门的虚拟现实设置。

Android 虚拟现实游戏的历史

在 70 年代末,虚拟现实系统开始以更好的设备迅速发展。结果是虚拟现实在 2016 年被纳入主流 Android SDK。最新的 Android 设备都支持虚拟现实。据信,大多数即将推出的设备都将支持虚拟现实头盔。

以前,操作虚拟现实头盔需要高端 PC 或游戏机。但现在,虚拟现实头盔被设计成支持移动设备。虚拟现实头盔被视为 Android 可穿戴设备的一部分,具有高配置。

技术规格

从技术上讲,虚拟现实系统一直存在严重的延迟问题。然而,随着技术的改进,延迟正在减少,体验也在变得更好。一些著名的虚拟现实头盔包括 Oculus Rift、HTC Vive、三星 Gear VR 等。

让我们来看看哪些 VR 设备直接兼容安卓设备。以前,VR 设备中有集成显示器。然而,现在,安卓设备可以直接与具有多个输入处理器的 VR 头盔相关联。VR 头盔面临的一个限制是它们只兼容特定的移动手机,因为它们自身的物理尺寸和硬件规格。VR 头盔设计用于适配特定的屏幕尺寸。然而,市场上有一些支持多种屏幕尺寸的 VR 套装。

当前安卓 VR 游戏行业

安卓 VR 游戏行业正在迅速增长。几乎所有新手机都支持 VR 应用。许多设备制造商提供专门为特定手机设计的外部 VR 套装。

以前,体验 VR 需要单独的设备,比如 Oculus VR 套装。然而,借助安卓 VR 开发,大多数移动设备都能运行 VR 应用,并且可以通过外部 VR 套装来体验。

安卓在 VR 中的未来

众所周知,VR 游戏正在占领游戏市场。安卓正在不断发展。最新发布的安卓 N 版本具有全新的 VR 支持维度。这清楚地表明安卓在 VR 游戏方面有着巨大的潜力。谷歌有可能正在开发一个未来的 VR 专用平台。

市场上会有更多兼容 VR 的设备。因此,安卓平台上的 VR 游戏有着光明的未来。

谷歌 Daydream

谷歌 Daydream 是下一代 VR 开发平台。据说它是谷歌 Cardboard 的继任者。最新的安卓 N 将包括对谷歌 Daydream 的支持,并且已经决定将支持 Daydream 的手机称为“Daydream-ready phones”。

谷歌 Daydream 和 Android N 将把安卓上的 VR 游戏带到数字游戏世界的新高度。游戏的体验和质量将会更好、更流畅、更逼真。

针对 VR 设备的游戏开发

移动游戏行业中有很大的空间可以用于 VR。基于安卓和谷歌 VR 平台,开发者现在正在针对 VR 开发游戏。VR 游戏在性质上与其他游戏不同。它将用户带入游戏世界。当然,游戏设计和规划也与其他移动游戏不同。

VR 游戏设计

VR 并不适合每种游戏类型。因此,VR 游戏设计需要相应调整。在游戏中有角色时,VR 游戏更适合,最好是第一人称射击游戏或一些 RPG 或赛车游戏。

设计师需要记住整个游戏世界必须通过游戏由用户来体验。因此,游戏体验是任何 VR 游戏最重要的因素。

一般来说,游戏设计师从一个想法开始设计游戏。然后,相应的控制、环境和体验被考虑进去。然而,在 VR 游戏的情况下,开发者或设计师已经有了一组定义好的特性,他们通过设计来实现这个想法。

VR 目标受众

全球有数十亿部手机。然而,只有很少一部分实际上兼容 VR 并且能够流畅运行 VR 游戏。随着时间的推移,越来越多的设备具备了 VR 功能。

在安卓上玩 VR 游戏不仅需要手机,还需要兼容的 VR 头盔。并不是每个用户都会去购买额外的设备来玩 VR 游戏。这就是为什么休闲玩家不是 VR 游戏的主要目标受众。相反,典型的玩家和游戏爱好者现在是实际的目标受众。

VR 的用途广泛。其中一个主要用途是模拟,包括教育。教育过程的游戏化为学生开辟了一个巨大的目标受众。另一个主要目标受众是对新事物充满好奇和活力的年轻人。

VR 游戏开发的限制

VR 游戏开发并不需要非凡的技能。开发人员应该熟悉并精通安卓系统,足够高效地理解 VR 规格和平台限制。在安卓平台上开发 VR 游戏时有一些限制:

  • 有限的手柄支持

  • 有限且特定的目标受众

  • 有限的控制

  • 有限的图形质量和最大体验

Cardboard SDK 简介

Google Cardboard 是谷歌于 2014 年开发并发布的 VR 平台,用于与智能手机配合使用的头戴设备。该平台旨在鼓励大规模的低成本 VR 应用程序开发,至今已被证明是成功的。2016 年 5 月 18 日,谷歌宣布 Daydream 是该平台的下一个步骤。

“Cardboard”这个名字来源于用硬纸板制成的 VR 设备的概念,这使得设备的成本大大降低。然而,许多第三方公司正在使用各种材料遵循相同的构建架构,以增加其风格和质量。

目前,Google Cardboard 只能用于为安卓和 iOS 创建 VR 应用程序。这改变了 VR 开发的概念,原来只限于典型的设备和硬件规格:

Cardboard SDK 简介

Cardboard 头盔组件

典型的 Google Cardboard 头盔包括以下部分:

  • 一块剪成精确形状的硬纸板

  • 45 毫米焦距镜片

  • 磁铁或电容贴

  • 钩和环尼龙扣

  • 一个橡皮筋

  • 一个可选的近场通信标签

硬纸板设备的每个部分都是预装的或者有一个机械槽可以安装。组装整套设备非常容易和快速。橡皮筋最后安装到头盔上。然而,用任何方式拿着组装好的 VR 头盔都能实现 VR 体验的目的。

组装套件后,将兼容的智能手机插入 VR 头盔的设备槽中,并由相应的组件固定在那里。

Cardboard 应用的工作原理

兼容 Google Cardboard 的应用程序将智能手机显示图像分成两部分,每个眼睛一部分。可以通过每个 45mm 镜片看到图像。它对每个显示部分应用桶形畸变以抵消镜片的枕形畸变。因此,创建了一个完整的广阔的 3D 世界。

最初的 Cardboard 头盔可以容纳屏幕尺寸达 5.7 英寸(140 毫米)的手机,并使用磁铁作为输入按钮,这也需要智能手机设备中的指南针传感器。后来,按钮被一个导电杠替代。

升级和变种

谷歌更新了设计,并于 2016 年发布了下一代 Cardboard VR 头盔,可与 6 英寸(150 毫米)显示屏的手机配合使用。它还用导电杠更新了输入按钮,触发智能手机屏幕上的触摸事件,以实现更好的设备兼容性。

Google 允许多家供应商和制造商使用不同的材料和风格制造兼容 Cardboard 的头盔。今天,我们可以看到这种产品的许多变种。

使用 Cardboard SDK 开发游戏的基本指南

为 Cardboard SDK 或其他任何 VR 组件开发游戏与其他安卓游戏不同。让我们通过以下几点快速了解 Cardboard 开发风格和标准的基础知识:

  • 启动和退出 VR 游戏

  • VR 设备适应

  • 显示属性

  • 游戏组件

  • 游戏控制

  • 游戏音频设置

  • 用户焦点辅助

  • 终极 VR 体验

启动和退出 VR 游戏

通常,在启动安卓游戏后,它会执行一些自动任务并带用户进入菜单选择操作。在 VR 游戏中,需要花时间将安卓设备正确安装到 VR 头盔上,因此开发人员在启动游戏后不会执行任何自动任务。游戏应该等待用户在适合运行的完美状态下开始。

为了更好和更常见的体验,游戏应该提示用户使用 VR 标志或按钮来开始 VR 游戏。

标准 VR 游戏有两种可能的退出方式:

  • 按下返回按钮

  • 按下主页按钮

按下返回按钮

如果 VR 有一个用于显示退出弹出窗口的 2D 界面,那么使用返回按钮是最好的选择。在玩安卓 VR 游戏时,不太可能意外按到返回按钮,因为设备安装在 VR 头显上。通常情况下,只需按一次返回按钮即可退出游戏,因为符合上述标准。否则,退出弹出窗口可以起到作用,类似于非 VR 游戏。

按下主页按钮

通常情况下,按下主页按钮会将安卓应用程序推到后台,而不会关闭应用程序,在 VR 游戏中也是如此。

VR 设备适应

使用 Cardboard SDK 的安卓 VR 游戏应该适应 VR 头显的物理特性。Google Cardboard SDK 具有自动执行此任务的功能。开发者可以依赖 SDK 根据 VR 头显调整应用程序设置和配置。Cardboard SDK 本身包含了几种特定 Cardboard 设备的调整设置。SDK 可以为 VR 头显的几种特定镜片配置立体设置和矫正失真。

建议开发者使用 Cardboard SDK 功能,支持尽可能多的 VR 头显,以便单个游戏为用户提供最佳的 VR 体验,而不会出现任何麻烦。

显示属性

在许多安卓设备中,有一个名为“Lights Out”的功能。在这些设备中,主页、菜单和返回控件被隐藏在“Lights Out”功能下。VR 游戏使用滑屏技术通过 VR 头显镜片生成 3D 体验。这就是为什么在全屏模式下运行 VR 游戏非常必要。系统控件或状态栏可能会出现在用户的外围视野中,阻挡或分散他们的注意力,影响真正的 VR 体验。

游戏内组件

通常情况下,VR 游戏体验是通过设备屏幕在环境中进行的。在游戏过程中,很少会触发任何弹出窗口或其他不需要的组件。

开发者不应调用任何会在游戏过程中触发任何弹出窗口或不需要的中断的 API。安卓目前不支持任何 2D 组件渲染。强制渲染 2D 元素可能会导致 VR 显示的迷失。即使不是这样,用户也需要将设备从 VR 头显中取出,然后执行所需的操作来摆脱弹出窗口,这一点都不方便。

游戏控件

安卓 VR 头显只有一个按钮。因此,在 VR 游戏中,控件设计不遵循传统方式。让我们来看看 VR 游戏的控件方案。

控件概念

UI 控件通常出现在游戏启动时。开发者应该将 UI 控件放在初始视野范围内,以便用户可以找到它们开始游戏。如果控件不在可见范围内,用户可能会感到困惑。在这种情况下,他们可能会四处寻找控件,或者直接离开游戏。在这两种情况下,用户可能会失去对游戏的兴趣。

在玩游戏时,如果有任何用户界面,这些控件需要随着视野移动。否则,用户可能需要回到 UI 元素所在的位置。

控件类型

虽然头显设备只有一个控制单元,但可能有几种使用按钮和其他选项的方式。最常见的控件类型如下:

  • 融合按钮

  • 视觉倒计时

融合按钮

Cardboard VR 头盔只有一个按钮在设备的一侧。它可以用来点击目标。这个按钮的一个用途是触发 VR 世界中的虚拟按钮,它将融合。这意味着在关注虚拟融合按钮一段时间后,将触发相应的任务。然而,这可能会令人沮丧,因为用户需要等待那段时间。为了解决这个问题,开发者应该在可能的情况下给用户一个立即点击虚拟按钮的选项:

熔丝按钮

可视倒计时

在使用带有计时器的熔丝按钮时,用户可能会在不知情的情况下关注按钮,并在一定时间后,它会改变游戏状态。在这种情况下,用户可能会感到困惑,无法继续相同的游戏体验。开发者应该通过视觉方式指示用户,在一定时间内会发生某些事情:

可视倒计时

熔丝按钮指示

我们已经知道 VR 游戏不支持 2D UI 按钮。因此,开发者使用熔丝按钮。熔丝按钮可以是游戏中的任何元素。必须有指示来引导用户关注特定的熔丝按钮。然后,可以在倒计时或点击时执行操作。

例如,开发者经常使用一些发光、闪烁、摇晃或其他动态机制来使对象可点击。周围的任务或动作由环境来定义以指示任务。

控制放置

当元素足够大以便关注时,激活熔丝按钮总是一个好的做法。这可以避免用户产生很多困惑。此外,场景中不应该有难以定位并可能造成困惑的相邻熔丝按钮。

通过 Google VR 进行 VR 游戏开发

谷歌通过 Google VR 发布了一个针对 Android 的 VR 开发工具包,其中包括 Android SDK 和 Android NDK。这些 SDK 支持 Cardboard 和 Daydream VR 平台。

开发者可以通过以下任务跳入 VR 游戏开发:

  • 头部跟踪系统

  • 空间音频

  • 动态渲染

  • UI 处理

  • 3D 校准

  • 镜头失真校正

  • 立体几何配置

让我们来看看用于 VR 游戏开发的 Android SDK:

  • 使用 Android SDK 的 Google VR

  • 使用 Android NDK 的 Google VR

使用 Android SDK 的 Google VR

我们将通过 Android SDK 来看一下 VR 开发。可以使用 Gradle 来构建 VR 应用程序。Gradle 可以独立使用,也可以与 Android Studio 一起使用。

开发者可以使用 Android Studio 之外的其他工具,但强烈建议在 Android 构建中使用 Android Studio。这是在 Android 平台上开发 VR 应用程序最方便的方法。

开发者需要检查以下因素,以便为 Android 创建 VR 应用程序构建:

  • Android Studio 版本 1.0 或更高

  • Android SDK 版本 23 或更高

  • Gradle 版本 23.0.1 或更高

使用 Android Studio 可以避免用户配置 Gradle 设置来构建应用程序包。它还可以帮助开发者识别并在需要时更新 Gradle。

开发者可以选择使用 Gradle 来构建 Android 应用程序项目。在这种情况下,开发者需要手动编辑每个模块的build.gradle文件,以包括 Gradle 构建的.AAR声明。

修改必须这样做:

dependencies
{
  compile(name:'audio', ext:'aar')
  compile(name:'common', ext:'aar')
  compile(name:'core', ext:'aar')
}

repositories
{
  flatDir
  {
    dirs 'libs'
  }
}

Android Studio 会自动进行这些更改,并为每个模块声明依赖项。这将告诉 Gradle 在相应模块的libs子目录中查找三个.AAR声明。如果没有名为libs的子目录,则开发者需要在模块目录内创建一个libs子目录,并手动复制所需的.AAR文件。

使用 Android NDK 的 Google VR

使用 Android NDK 进行 VR 应用开发与 SDK 开发并没有太大的不同。它需要以下组件:

  • Android Studio 版本 1.0 或更高

  • Android NDK

  • Google VR SDK for Android

建议使用 Android NDK 与 Daydream 开发平台。因此,开发者需要为 Daydream SDK 设置开发环境。

Google VR 开发工具包从版本 v0.8.0 的测试版开始支持 NDK 开发。从版本 v0.8.1 开始,它包括了一个本地的头部跟踪系统来增强开发。

Android SDK 足以开发 Cardboard 的 VR 游戏,但一些开发者喜欢使用 C++等本地语言来开发游戏。一些开发者选择使用 C++和 OpenGL 来开发 VR 游戏,以便更好地理解技术。这样,VR 游戏也可以在其他 VR 平台上使用。

Android VR 开发最佳实践

开发者在开始使用 Cardboard 开发 VR 体验之前,需要有编写 Android 常规游戏的经验。以下是开发者在为 Google Cardboard 开发 VR 游戏时需要注意的几个方面:

  • 绘制调用限制

  • 三角形数量限制

  • 保持稳定的 FPS

  • 克服过热问题

  • 追求更好的音频体验

  • 设置适当的项目设置

  • 使用适当的测试环境

绘制调用限制

VR 游戏显然是 3D 游戏,需要进行广泛的渲染过程。将绘制调用最小化以限制渲染时间并减少 GPU 开销是一种良好的实践。

一般来说,根据当前可用设备的列表,开发者应该将每帧的渲染调用限制在 100 次。在当前行业中,大多数开发者都在努力将绘制调用保持在 50 到 100 之间。

三角形数量限制

我们已经讨论了在 3D 游戏中顶点和三角形的功能。相同的逻辑也适用于 VR 游戏。然而,在 VR 游戏中保持性能比普通的 3D 游戏更加困难。这就是为什么 VR 游戏通常使用 100k 以内的三角形数量。

目前,性能良好的 VR 游戏的平均三角形数量约为 50k 至 80k。开发者需要简化所有的 3D 对象,并将它们优化为可能的最小三角形和顶点,以在运行时获得良好的 FPS。

保持稳定的 FPS

对于任何 Android 游戏来说,保持稳定和良好的 FPS 是必要的。Android 的 VR 游戏也不例外。通过上述限制,开发者可以减少渲染时间以获得更好的性能。

VR 游戏使用分屏技术,这是一个繁重的过程。因此,开发者应该决定并使用尽可能少的游戏对象或元素,以保持稳定的 FPS。

对于 VR 游戏开发者来说,以有限的资源提供更好的视觉效果是一个巨大的挑战。设计和创建艺术资源是一个重要部分。使用最少的三角形来制作低多边形模型会产生较差的视觉质量。然而,通过对模型提供出色的纹理支持和在虚拟现实世界中进行战略光照映射,可以改善这种质量。

克服过热问题

过热是 Android VR 游戏的常见问题。由于 CPU 和 GPU 的广泛使用,设备在运行 VR 应用时会发热。开发者无法完全解决这个问题。然而,开发者可以优化 VR 游戏以减少处理器使用。游戏应该限制网络使用和其他设备组件,以最小化电池消耗。

更好的音频体验

Android VR 游戏是在额外的 VR 设备的支持下进行的,例如 Google Cardboard 或类似设备。我们已经在本章讨论过这些设备。这些设备没有音频支持;VR 游戏默认使用设备扬声器。

没有适当的音频体验,虚拟现实体验就不完整。在游戏中使用 3D 环境音频并建议用户在玩 VR 游戏时戴上耳机,这总是一个好的做法。

设置适当的项目设置

在项目设置 VR 项目设置正确可以在未来避免开发者的头疼。特别是为了获得更好的项目性能,很重要在项目开始时正确设置质量设置。没有先前的项目配置,整个项目和性能规划都不会有成果。

使用适当的测试环境

为了任何游戏的成功,特别是对于大规模游戏如 VR 游戏,设置测试环境是非常重要的。开发者必须了解每个开发阶段的状态,以便他们可以做出未来的决定,使游戏变得更好。通过这种方式,开发过程可以顺利进行。

建议您从 VR 设备外部运行 VR 游戏进行测试。还建议您使用 adb 检查每个调试条件和语句。由于游戏必须使用 VR 设备进行测试,因此很难使用普通的调试桥接。开发者需要设置无线调试桥接来克服这个问题。调试桥接也可以用于从 VR 设备外部运行和停止游戏。通过这种方式,开发者可以节省大量时间,用于改进游戏。

安卓 VR 游戏市场的挑战

我们已经了解了在安卓平台上开发 VR 游戏的技术挑战。现在,让我们看看开发者在开发或为安卓平台上的 VR 游戏进行货币化时可能面临的其他挑战:

  • 目标受众少

  • 游戏类型有限

  • 长时间游戏时间

  • 设备支持有限

  • 实时约束

目标受众少

很少有安卓用户熟悉 VR 概念和技术。大多数用户只是普通的手机持有者,没有 VR 头盔。因此,已经有一大部分受众不在 VR 游戏的范围内。这就是为什么 VR 游戏市场受众有限于拥有 VR 头盔的人群。

游戏类型有限

我们已经看到了 VR 游戏中可能的游戏类型。VR 游戏不能支持迄今为止发布的所有游戏类型,可能在不久的将来也无法做到。这种限制对于安卓 VR 游戏市场来说是一个严重的挑战,它影响了其在获得盈利地位和设计货币化方面的地位。

长时间游戏时间

安卓 VR 游戏必须使用配备安卓手机的 VR 头盔进行游戏。通常游戏时间较长,需要时间和精力来设置一个可玩的环境。

安卓游戏用户通常沉迷于快速和灵活的游戏时间。大多数游戏可以立即暂停和恢复,方便游戏。然而,VR 游戏不能立即暂停或恢复。用户更喜欢在有长时间空闲的时候玩 VR 游戏。

设备支持有限

安卓 VR 游戏需要高水平的硬件配置来运行游戏。硬件平台必须有足够的能力来处理游戏,渲染单元必须以最短的时间内以最大可能的质量渲染游戏。

市场上有很多廉价和低配置的手机。数百万安卓用户使用这些手机。然而,大多数手机无法稳定且可接受的 FPS 运行 VR 游戏。安卓 VR 游戏的开发者不得不排除这些设备。这种限制显著减少了受支持设备的数量。

实时约束

大多数 VR 游戏玩家是游戏玩家或游戏爱好者。然而,他们在玩 VR 游戏时也有一些实时约束。用户或玩家必须选择一个游戏情境,其中可能中断的机会最小。

玩家通常更喜欢坐在一个地方或者在宽敞的地方玩游戏,以避免发生任何意外。他们的眼睛被 VR 头盔遮住,所以用户需要采取这些安全措施。这些问题无法解决,这影响了 VR 游戏市场,导致了低会话次数和时间。

扩展的 VR 游戏概念和开发

我们只谈到了 Android 的 VR 游戏方面的一些内容。这些想法已经被实施或正在实施。然而,虚拟游戏还有更广泛的概念。

游戏开发者现在正试图利用 VR 技术制作实时体验。有一些类似数字游戏的实时游戏,比如彩弹激光标签等。借助 VR 技术,这些体验可以达到一个新的水平。在一个与 VR 应用环境物理同步的预定义竞技场中使用 VR 头盔可以让用户进入游戏。这种游戏概念已经开始成形。一些用于 VR 游戏的测试竞技场已经通过各种其他物理设备和传感器的支持被创建出来。这些设备和传感器可以实时跟踪用户的动作,并通过 VR 头盔显示在虚拟世界中复制出来。

诸如行走、蹲下、在虚拟世界中触摸某物以及射击虚拟敌人等简单动作已经被实现。开发人员和研究人员正在不断改进。然而,出于以下原因,Android 平台尚未迈出这种 VR 开发的步伐:

  • Android 是一个最适合便携设备的移动操作系统。

  • 大多数 Android 设备是移动电话或平板电脑。

  • 在 Android 上管理一个具有大型物理设置的专用硬件平台是困难的。实时操作系统RTOS)对于这类系统的性能要比 Android 好得多。

  • 这种 Android 设置的市场前景不佳,成本可能会很高。

尽管在大规模设置 Android VR 游戏中存在这些问题,但人们相信 Android 很快将成为这类系统的一部分。

总结

这是科技时代。VR 技术已经将游戏体验提升到了一个新的水平。Android 现在是游戏开发最为进步的平台。将两个领先的平台结合起来肯定可以将移动游戏体验提升到一个新的水平。

在 VR 游戏中,用户有机会置身于游戏环境中。然而,在为 Android 开发具有 VR 功能的游戏时存在许多限制。谷歌已经宣布了即将推出的 VR 开发平台 Daydream,其中包括带有独立控制器的扩展控制。

VR 游戏行业正在迅速增长。它有自己的一套优势和劣势。然而,毫无疑问,游戏体验远比传统的游戏系统要好得多。因此,可以毫不夸张地说,Android VR 游戏行业的未来是光明的。

第十一章:使用 C ++和 OpenGL 进行 Android 游戏开发

我们已经看到了 Android 应用程序和 Android 游戏之间的区别。 Android SDK 非常擅长处理这两者。当然,引起的问题是“在本地语言(如 C 和 C ++)中需要单独的开发工具集是什么要求?”与 Java 相比,C 和 C ++更难管理和编写代码。答案就在问题本身。 Java 架构在 JVM 上运行,与 Android 操作系统相关联。这会产生额外的延迟,导致性能滞后。滞后的规模取决于应用程序的规模。

高度 CPU 密集型的应用程序可能会在 Java 架构中导致大量可见的延迟。本地语言代码可以更快地处理。此外,本地代码可以根据 CPU /平台架构的不同而变化,而在 Android SDK 使用的 Java 架构中是不可能的。

Android 使用 OpenGL 渲染系统。因此,使用 OpenGL 制作的应用程序也可以选择 Android 作为目标平台。本地代码有助于直接使用 OpenGL 构建应用程序。

我们将通过以下主题在本章中详细了解这些方面:

  • 介绍 3Android NDK

  • 游戏中的 C ++-优缺点

  • 本地代码性能

  • OpenGL 简介

  • 使用 OpenGL 进行渲染

  • 不同的 CPU 架构支持

Android NDK 简介

Android 实际上是基于 Java 架构的。但是,Android 应用程序的一部分可以使用 C 和 C ++等本地语言开发。这就是 Android NDK 出现的地方。

Android NDK 是一个工具集,用于开发将与硬件交互得更快的应用程序模块。众所周知,C 和 C ++能够直接与本地组件交互,从而减少应用程序和硬件之间的延迟。

NDK 的工作原理

Android 本地代码段通过Java 本机接口JNI)与主应用程序进行交互。 Android NDK 附带一个构建脚本,将本地代码转换为二进制代码并将其包含在主 Android 应用程序中。

该二进制代码基本上是一个本地代码库,可以根据需要在任何 Android 应用程序中使用。 NDK 构建脚本创建.so文件并将其添加到应用程序路径中。

Android 构建过程创建 Dalvik 可执行文件(.dex)以在 Android OS 的 Dalvik 虚拟机(或 ART)上运行。 Java 可执行文件识别本地库并加载已实现的方法。本地方法使用native关键字声明:

public native void testFunc (int param);

该方法始终具有公共访问权限,因为本地库始终被视为外部来源。在这里,开发人员应始终牢记,对于所有包含的本地库的同一声明,永远不应该有多个方法的定义。这将始终创建编译错误。

本地构建过程可以将本地项目构建为两种类型的库:

  • 本地共享库

  • 本地静态库

本地共享库

本地构建脚本从 C ++文件创建.so文件,称为本地共享库。但是,它并不总是在真正意义上在应用程序之间共享。 Android 应用程序是一个 Java 应用程序,但是本地应用程序可以通过本地共享库触发。

对于游戏开发,如果游戏是用本地语言编写的,则游戏代码将包含在共享库中。

本地静态库

本地静态库基本上是已编译对象的集合,并由.a文件表示。这些库包含在其他库中。编译器可以在编译过程中删除未使用的代码。

构建依赖

Android SDK 能够使用 Java 构建和打包 Android 应用程序项目为 APK 文件。然而,NDK 不足以构建和打包 APK 文件。除了 NDK 之外,创建 Android 应用程序 APK 的依赖如下:

  • Android SDK

  • C++编译器

  • Python

  • Gradle

  • Cygwin

  • Java

Android SDK

Android 应用程序基本上是 Java 应用程序。因此,有必要拥有 Android SDK 来创建 Android 应用程序包。

C++编译器

本机 Android 应用程序是用 C++编写的,因此需要 C++编译器在开发平台上编译代码库。C++编译器是平台相关的,因此可能不是每个平台上都是相同的 C++编译器。

例如,在 Windows 机器上,目前在开发行业中使用 C++11 编译器,而在 Linux 机器上使用 GC++编译器。

这可能会在语法和 API 调用方面为实际开发项目创建不同的代码库。

Python

Python 是一种独立的开发语言。它可以用于创建 Android 应用程序,并通过将源代码转换为本机语言来支持多个平台。在 Android NDK 开发的情况下,Python 用于将 C++代码转换为本机二进制。

Gradle

Gradle 被构建脚本和 Android 本机构建工具使用,将本机代码转换为共享库。它还提供了一个虚拟的 Unix 环境来制作应用程序包。

Cygwin

Android 需要一个 Unix 环境来构建 NDK 应用程序项目。Windows 系统没有 Unix 环境。需要 Cygwin 来提供虚拟的 Unix 环境来支持构建平台。

Java

最后但并非最不重要的是需要 Java 来创建 Android 应用程序包。然而,对于任何类型的 Android 开发,都需要 Java。

本机项目构建配置

为了从本机源代码创建应用程序包,Android 项目需要以下配置。本机项目构建取决于这两个文件中定义的配置:

  • Android.mk

  • Application.mk

Android.mk 配置

位置

Android.mk文件可以在<应用程序项目路径>/jni/中找到。

配置选项

Android.mk文件包含以下选项来创建应用程序包:

  • CLEAR_VARS:这清除本地和用户定义的变量。这个选项由include $(CLEAR_VARS)语法调用。

  • BUILD_SHARED_LIBRARY:这包括所有本地文件,在LOCAL_MODULELOCAL_SRC_FILES中定义,以共享库的形式。它由include $(BUILD_SHARED_LIBRARY)语法调用。

  • BUILD_STATIC_LIBRARY:这指定静态库以创建由共享库使用的.a文件。它由include $(BUILD_STATIC_LIBRARY)语法调用。

  • PREBUILT_SHARED_LIBRARY:这表示特定路径上的预构建共享库,用于从本地包含构建依赖的共享库。它由include $(PREBUILT_SHARED_LIBRARY)语法调用。

  • PREBUILT_STATIC_LIBRARY:这表示特定路径上的预构建静态库,用于从本地包含构建依赖的共享库。它由include $(PREBUILT_STATIC_LIBRARY)语法调用。

  • TARGET_ARCH:这表示基本处理器架构系列,如 ARM、x86 等。

  • TARGET_PLATFORM:这定义目标 Android 平台。所述平台必须通过 Android SDK 管理器安装在开发系统中。它指示 Android API 级别以创建应用程序包。

  • TARGET_ARCH_ABI:这表示目标处理器架构的特定 ABI,如 armeabi、armeabi-v7、x86 等。

  • LOCAL_PATH:这指向当前文件目录。这个变量不会被CLEAR_VARS命令清除。它由LOCAL_PATH := $ (call my-dir)语法调用。

  • LOCAL_MODULE:这表示所有唯一的本地模块名称。它由LOCAL_MODULE := "<模块名称>"语法调用。

  • LOCAL_MODULE_FILENAME:这表示包含已定义LOCAL_MODULE的库名称。它由LOCAL_MODULE_FILENAME := "<模块库文件名>"语法调用。

  • LOCAL_SRC_FILES:这表示要编译为共享库的所有本地源代码文件路径。它由LOCAL_SRC_FILES := <本地源文件路径>语法调用。

还有其他可选配置可以在此文件中设置,例如LOCAL_C_INCLUDESLOCAL_CFLAGSLOCAL_CPP_EXTENSIONLOCAL_CPP_FEATURESLOCAL_SHARED_LIBRARIESLOCAL_STATIC_LIBRARIESLOCAL_EXPORT_CFLAGS

Application.mk 配置

位置

Application.mk文件可以位于<应用程序项目路径>/jni/

配置选项

Application.mk文件包含以下选项以创建应用程序包:

  • APP_PROJECT_PATH:这是项目根目录的绝对路径。

  • APP_OPTIM:这表示可选设置,用于将构建包创建为发布版或调试版。

  • APP_CFLAGS:这定义了一组 C 编译器标志,用于构建而不是在Android.mk文件中更改。

  • APP_CPPFLAGS:这定义了一组 C++编译器标志,用于构建而不是在Android.mk文件中更改。

  • APP_BUILD_SCRIPT:这是一个可选设置,用于指定除默认的jni/Android.mk脚本之外的构建脚本。

  • APP_ABI:此选项指定要为 Android 应用程序包进行优化的 ABI 集。以下是每个 ABI 支持的完整列表和关键字:

  • ARMv5:armeabi

  • ARMv7:armeabi-v7a

  • ARMv8:arm64-v8a

  • Intel 32 位:x86

  • Intel 64 位:x86_64

  • MIPS 32 位:mips

  • MIPS 64 位:mips64

  • ALL-SET:all

  • APP_PLATFORM:此选项指定目标 Android 平台。

  • NDK_TOOLCHAIN_VERSION:此选项指定 GCC 编译器的版本。默认情况下,64 位和 32 位分别使用版本 4.9 和 4.8 进行编译。

  • APP_STL:这是一个可选的配置,用于链接替代 C++实现。

  • APP_LDFLAGS:在构建共享库和可执行文件的情况下,此选项用于将链接标志链接到构建系统以链接应用程序。

用于游戏的 C++-优点和缺点

C++和 Java 之间有一个永无止境的争论。然而,我们不会讨论争议,而是试图从游戏开发的角度来看待它们。C++在性能上略优于 Java,而 Java 以其简单性而闻名。

可能有许多程序员更喜欢 C++而不是 Java,或者反之亦然。在游戏开发中,编程语言的个人选择并不重要。因此,使用 NDK 或 SDK 必须根据需求来确定。建议您始终使用 Android SDK 来开发应用程序,而不是使用 NDK。

让我们讨论使用本地语言进行游戏编程的优缺点。

使用 C++的优势

让我们首先从以下几点来看看使用 C++进行游戏编程的积极方面:

  • 通用游戏编程语言

  • 跨平台可移植性

  • 更快的执行

  • CPU 架构支持

通用游戏编程语言

在游戏开发的情况下,C++被广泛用于许多平台,特别是用于主机和 PC 游戏开发。这就是为什么许多游戏引擎选择 C++作为主要编程语言的原因。

有时,学习许多编程语言以在不同架构的不同平台上工作是困难的。C++提供了这个问题的最常见解决方案,因为大多数程序员都熟悉 C++库和 API 的使用。

跨平台可移植性

相同的 C++代码被编译为针对特定操作平台的库。因此,同一个项目可以编译为不同的平台。因此,如果游戏是用 C++编写的,那么将游戏移植到各种平台就会变得非常容易。

例如,著名而有效的跨平台游戏引擎 Cocos2d-x 使用 C++作为开发语言。因此,相同的游戏可以轻松移植到许多平台,如 Android、iOS、Mac OS 和 Windows。

更快的执行

C++能够很好地与平台硬件交互,并且使用 C++编写游戏有助于提高性能。然而,在 Android 的情况下,如果游戏不是 CPU 密集型,性能提升几乎是不可察觉的。

CPU 架构支持

C++代码可以针对特定的目标 CPU 架构进行编译,如 x86、ARM、Neon 或 MIPS。这种规范表明在特定处理器上有更好的性能。

在 Android NDK 中为 CPU 架构配置编译器确保在每个平台上获得最佳结果。然而,并不总是需要为避免额外的编译而定义每个平台。

使用 C++的缺点

现在,让我们通过以下几点来讨论问题的另一面:

  • 高程序复杂性

  • 依赖平台的编译器

  • 手动内存管理

高程序复杂性

C++带来了额外的程序复杂性。在 Java 编程的情况下,JVM 完全负责内存管理,并遵循面向对象的概念。C++在提供这一特性方面存在不足。因此,开发人员需要额外的工作来处理每个编程方面。

与 Java 相比,C++本身的架构更加复杂。如果使用 C++,面临异常和错误的机会会增加。

依赖平台的编译器

使用 C++进行跨平台开发很容易。然而,在大多数情况下,配置构建脚本可能会很麻烦。同样的游戏由于错误的配置而无法在移植的平台上运行是非常常见的情况。此外,由于游戏在其他平台上成功运行,因此很难找出问题所在。

大多数情况下,不同的平台使用不同的 C++编译器。因此,需要额外的努力来识别特定于平台的代码,并在必要时为每个平台找到替代方案。

手动内存管理

Java 不需要开发人员实现内存管理,内存由 JVM(在 Android 的情况下是 DVM)高效管理。因此,不会出现内存泄漏或碎片化的情况。JVM 会运行垃圾收集器来自动释放未使用的内存。然而,垃圾收集器的调用会消耗一些性能,频繁的垃圾收集器调用可能会导致严重的性能下降。

开发人员应该使用最佳内存,因为如果代码中有任何活动引用,垃圾收集器无法识别未使用的内存块。

结论

C++有其自身的优势。然而,当涉及到为 Android 进行游戏编程时,在技术上并没有太大帮助。因此,如果我们比较选择 C++和在 Android 上使用 Java 编码所需的工作量和风险,Java 应该始终优先考虑。DVM 能够有效地运行 Java 代码,以在 Android 设备上实现合理的性能。此外,Android NDK 库实际上并不是为开发独立的 Android 应用程序而设计的。尽管它具有本地活动支持,作为 DVM 和用 C++编写的本地应用程序之间的中间层,但并没有太大帮助。

如果开发人员选择不跨平台,并将游戏范围限制在 Android 上,那么建议您使用 Android SDK 而不是 Android NDK。这将减少开发的麻烦和复杂性,同时性能损失可以忽略不计。

本地代码性能

正如我们已经知道的,本地代码可以以更快的处理速度运行。这可以进一步针对特定的 CPU 架构进行优化。这种性能提升的主要原因是在内存操作中使用指针。但是,这取决于开发人员和编码风格。

让我们看一个简单的例子,我们可以更好地理解本地语言中的性能增益。

考虑这段 Java 代码:

int[] testArray = new int[1000];
for ( int i = 0; i < 1000; ++ i)
{
  testArray[i] = i;
}

在这种情况下,数组中 1000 个字段的地址由 JVM(在 Android Dalvik 系统中为 DVM)处理。因此,解释器每次解析到第i个位置并执行赋值操作,这需要很长时间。

现在,让我们使用本地 C/C++语言实现相同的功能并使用指针:

int testArray[1000];
int *ptrArray = testArray;
for ( int i = 0; i < 1000; ++ i)
{
  *ptrArray = i;
  ptrArray += i * sizeof(int);
}

在这个例子中,解释器不需要解析到目标内存位置。ptrArray指出了位置的地址。因此,值可以直接赋给内存位置。

特别是对于多维数组,在正确编写的本地代码中可以观察到显著的性能增益,用于相同功能。本地代码的另一个重要用途是二进制数据处理和图像处理,在这些情况下,大量数据一次性处理。

使用 OpenGL 进行渲染

Android 使用 OpenGL 进行渲染。Android SDK 库包括专门针对 Android 进行优化的 OpenGL 库。Android 从 API 级别 4 开始支持 OpenGL,然后随着级别的增加而增加其支持。目前,OpenGL 的最大支持版本是从 API 级别 21 开始的 OpenGL ES 3.1。

OpenGL 版本

不同的 OpenGL 版本具有不同的功能集。版本 1.0 和 2.0 在编码风格、API 便利性、功能和特性支持方面有很多不同之处。让我们讨论一下对 Android 开发具有重要意义的以下 OpenGL ES 版本:

  • OpenGL ES 1.x

  • OpenGL ES 2.0

  • OpenGL ES 3.0

  • OpenGL ES 3.1

OpenGL 1.x

从 Android API 级别 4 开始支持 OpenGL 版本 1.x,使用共享的 OpenGL ES 1.x 库libGLESv1.so。头文件gl.hglext.h包含了 OpenGL 功能所需的所有必要 API。

OpenGL 2.0

在当前行业中,开发人员更倾向于在游戏中使用 OpenGL ES 2.0,因为几乎每台设备都支持这个 OpenGL 版本,并且它提供了对游戏有用的顶点和片段着色器。OpenGL ES 2.0 可以通过在项目中包含libGLESv2.so共享库来用于 Android 本地开发项目,如下所示:

LOCAL_LDLIBS := -lGLESv2

头文件是gl2.hgl2ext.h。OpenGL ES 2.0 从 Android API 级别 5 开始支持。

OpenGL 3.0

从 Android API 级别 21 开始,支持 OpenGL ES 3.0。开发人员可以包含libGLESv3.so来使用 OpenGL 3.1,如下所示:

LOCAL_LDLIBS := -lGLESv3

头文件是gl3.hgl3ext.h

OpenGL 3.1

从 Android API 级别 21 开始,支持 OpenGL ES 3.1。开发人员可以包含libGLESv3.so来使用 OpenGL 3.1,如下所示:

LOCAL_LDLIBS := -lGLESv3

头文件是gl31.hgl3ext.h

许多 Android 设备不支持 OpenGL ES 3.0 和 OpenGL ES 3.1。如果开发人员打算使用它们,则在使用版本之前应该进行 OpenGL 版本检查。此外,必须使用适当的 OpenGL ES 版本在特定设备上运行游戏。最新的 Android N 支持 OpenGL ES 3.2。

检测和设置 OpenGL 版本

这段 Android Java 代码可以用来为 Android 游戏实现适当的 OpenGL ES 支持:

private GLSurfaceView glSurfaceView;
void setOpenGLVersion()
{
  final boolean supportOpenGLEs3 = configurationInfo.reqGlEsVersion >= 0x30000;

  if (supportOpenGLEs3) 
  {
    glSurfaceView = new GLSurfaceView(this);
    glSurfaceView.setEGLContextClientVersion(3);
    glSurfaceView.setRenderer(new RendererWrapper());
    setContentView(glSurfaceView);
  }
  else
  {
  final boolean supportOpenGLEs2 = configurationInfo.reqGlEsVersion >= 0x20000;

  if (supportsOpenGLEs2) 
    {
      glSurfaceView = new GLSurfaceView(this);
      glSurfaceView.setEGLContextClientVersion(2);
      glSurfaceView.setRenderer(new RendererWrapper());
      setContentView(glSurfaceView);
    }
    else
    {
      glSurfaceView = new GLSurfaceView(this);
      glSurfaceView.setEGLContextClientVersion(1);
      glSurfaceView.setRenderer(new RendererWrapper());
      setContentView(glSurfaceView);
    }
  }
}

纹理压缩和 OpenGL

纹理压缩对由 OpenGL 处理的渲染过程有重要影响。它可以增加或减少不同类型的纹理压缩的性能。让我们快速看一下一些重要的纹理压缩格式:

  • ATC

  • PVRTC

  • DXTC

ATC

ATI 纹理压缩通常称为 ATITC。该压缩支持 RGB 和带或不带 alpha 通道。这是 Android 最常见和广泛使用的压缩技术。

PVRTC

Power VR 纹理压缩使用 2 位和 4 位像素压缩,带有或不带有 alpha 通道。这被全球许多游戏开发人员使用。

DXTC

DXTC 也被称为 S3 纹理压缩,也用于 OpenGL。这使用 4 位或 8 位 ARGB 通道。

OpenGL 清单配置

Android 需要应用程序中使用的 OpenGL 版本定义,以及其他所需选项。

以下是 OpenGL ES 的版本声明语法:

<uses-feature android:glEsVersion=<Target version goes here> android:required="true" />

以下是目标版本选项:

  • 0x00010000代表版本 1.0

  • 0x00010001代表版本 1.1

  • 0x00020000代表版本 2.0

  • 0x00030000代表版本 3.0

  • 0x00030001代表版本 3.1

  • 0x00030002代表版本 3.2

以下是纹理压缩声明的可选设置:

<supports-gl-texture android:name=<Compression support type goes here> />

这些是压缩类型选项:

  • GL_OES_compressed_ETC1_RGB8_texture

  • GL_OES_compressed_paletted_texture

  • GL_EXT_texture_compression_s3tc

  • GL_IMG_texture_compression_pvrtc

  • GL_EXT_texture_compression_dxt1

  • GL_EXT_texture_compression_dxt2

  • GL_EXT_texture_compression_dxt3

  • GL_EXT_texture_compression_dxt4

  • GL_EXT_texture_compression_dxt5

  • GL_AMD_compressed_3DC_texture

  • GL_EXT_texture_compression_latc

  • GL_AMD_compressed_ATC_texture

  • GL_ATI_texture_compression_atitc

然而,并非所有设备都支持所有纹理压缩。开发人员应根据硬件和 Android 版本要求始终选择目标纹理压缩。

注意

如果目标设备不支持声明的纹理格式,则 Google 会自动进行设备过滤。

选择目标 OpenGL ES 版本

正如您已经了解的,不是所有设备都支持所有 OpenGL 版本。因此,在开发游戏之前选择正确的 OpenGL 版本非常重要。在选择 OpenGL 版本时,应评估以下几个因素:

  • 性能

  • 纹理支持

  • 设备支持

  • 渲染特性

  • 编程舒适性

性能

注意到 OpenGL 版本 3.x 比 OpenGL 版本 2.x 更快,后者比 OpenGL 1.x 更快。因此,在游戏中始终最好使用最新可能的版本。

纹理支持

纹理压缩支持因 OpenGL 版本而异。旧版本支持旧的纹理压缩因素。此外,Android 版本支持并非所有 OpenGL 版本都通用。因此,最好使用最新可能的版本来支持纹理。

设备支持

这个限制使开发人员脚踏实地。并非所有设备都支持最新版本的 OpenGL。因此,为了针对更广泛的设备范围,用户应将 OpenGL 版本更改为 2.0,因为大多数设备支持这个版本。

渲染特性

随着 OpenGL 版本的增加,功能列表成为选择 OpenGL 版本的重要因素。开发人员必须了解开发应用程序所需的支持,并相应地选择版本。

编程舒适性

OpenGL 的各个版本之间存在巨大的编码风格和 API 更改。开发人员应该选择版本,如果可以在公司轻松开发。

不同的 CPU 架构支持

开发人员有机会为单独的处理器架构优化 Android 应用程序。从高层次的角度来看,这是一个很好的功能。然而,这个功能是以巨大的成本为代价的。让我们来看看这个功能的细节。

可用的 CPU 架构

以下是 NDK 构建当前支持的架构:

  • ARM

  • x86

  • Neon

  • MIPS

ARM

ARM代表Acorn RISC Machine。这是一个基于RISCReduced Instruction Set Computing)的处理器,主要针对嵌入式或移动计算。正如其基础所说,它对于 Android 等操作系统非常高效。

目前,Android 平台上使用最多的处理器来自 ARM 家族。它可以进一步细分如下:

  • ARMv5TE

  • ARMv7

  • ARMv8

x86

英特尔推出了处理器的x86架构。最初,这些处理器主要用于台式机/笔记本电脑。然而,它们经过优化后也可以用于移动设备,以 Celeron 或 Atom 处理器的形式。

Android NDK 构建可以设置两种类型的 x86 架构:

  • i686

  • x86-64

Neon

Neon架构是基于 ARM 技术的,以进一步优化移动计算。Android 构建也可以针对这种特定架构进行优化。所有 Cortex 处理器基本上都是基于 Neon 的处理器。

MIPS

MIPS代表无互锁流水线级微处理器。在这个类别中有 32 位和 64 位处理器的变体。正如名称所示,这种架构用于嵌入式设备中的微处理器进行小规模计算。后来,它以 64 位架构引入到 Android 中。然而,这种类型的处理器在今天的 Android 系统中很少使用。

集成多种架构支持的优缺点

Android 移动设备在内存和处理能力方面有不同的配置。包括单独的架构支持可能会增加与更大的构建大小相对应的性能。

本地构建工具为每个目标处理器构建一个单独的共享库,并将其包含在构建包中。

以下是提供单独处理器架构支持的一些优缺点。

现在让我们先看看优点:

  • 更快的操作:为单独的处理器提供单独的架构会导致游戏指令的处理速度更快。如果处理器架构受到 Android 应用的支持,那么处理器就不需要执行任何转换,并且可以以更快的速度运行指令。

  • 处理器的最佳使用:操作系统总是寻找集成处理器的特定架构。相同的架构可以最佳地利用处理器。

  • 最低功耗:最佳处理直接意味着最佳和最低的处理功耗。

  • 最佳内存使用:如果 Android 应用支持相同的处理器架构,处理器就不需要使用额外的运行时内存来执行指令。

现在让我们看看缺点:

  • 更大的构建大小:为单独的架构使用单独的共享库会显著增加构建包的大小。整个本地指令代码都会在具有不同处理器优化的单独共享库中重新编写。

  • 减少目标设备数量:如果 APK 的大小很大,将其适应低存储设备会带来更多问题。因此,设备支持变得更少。

总结

我们在本章中简要介绍了 Android NDK,并解决了一些关于本地开发的疑惑。有许多开发人员认为用本地语言开发游戏可以获得巨大的处理能力。然而,这并不总是正确的。处理和性能取决于开发风格和标准。在大多数常见情况下,本地开发和 SDK 开发之间的差异是可以忽略不计的。

OpenGL 在任何情况下都可以与 Android 一起工作。后端渲染基于 OpenGL,无论是 NDK 还是 SDK。我们已经讨论了 OpenGL 的所有技术方面。在这里,您了解了哪个版本的 OpenGL 适用于 Android 以及我们应该使用哪个版本。显然,OpenGL ES 2.0 是一个不错的选择,因为大多数 Android 设备都支持它。另一方面,OpenGL ES 1.0 已经过时,而 OpenGL ES 3.0 尚未得到大多数 Android 设备的支持。

到目前为止,我们已经涵盖了 Android 游戏开发的几乎所有方面。然而,完成游戏的实现并不意味着开发周期的完成。开发人员需要在游戏达到发布准备状态后对其进行打磨,以提高其整体质量。我们将在下一章讨论游戏的打磨,以表示游戏开发过程的完成。

第十二章:抛光安卓游戏

开发的游戏质量大部分取决于最终的抛光。抛光基本上是开发的一个阶段,在这个阶段游戏在各个方面得到改进,以提供最大的用户体验。这种改进没有限制。大多数游戏开发人员将大部分时间用于抛光。

在抛光阶段,游戏应该准备好发布。在开发过程的最后,开发人员大多面临时间紧迫。抛光需要大量时间。有很多例子表明,开发人员选择在发布后抛光游戏。然而,从用户体验和留存的角度来看,这是不推荐的。

抛光工作由整个开发团队完成,包括设计师、艺术家和开发人员。确保游戏的目标抛光水平是产品经理和制作人的责任。

许多开发人员选择与大量但有限数量的用户进行游戏测试。然后,问题和改进被记录下来进行抛光。开发人员使用了几种抛光安卓游戏的方法。我们将在本章讨论抛光的一般和广泛使用的方法和实践。

我们将详细讨论以下主题:

  • 抛光要求

  • 游戏测试

  • 关注用户体验

  • 安卓特定的抛光

  • 游戏可移植性

抛光要求

抛光任何游戏都定义了开发的质量。因此,在将游戏发布到市场之前,抛光游戏是绝对必要的。抛光游戏在可见性、流畅性和用户体验方面比未经抛光的游戏表现得更好。

抛光安卓游戏涵盖游戏的所有三个开发组成部分:

  • 开发抛光

  • 美术抛光

  • 设计抛光

开发抛光

抛光工程或游戏开发过程的技术方面,以改善游戏的流畅性为主要目标。这部分包括编程优化、内存优化和剥离不必要的代码块,以避免任何额外的处理。

开发抛光可以进一步分为三个阶段:

  • 内存优化

  • 性能优化

  • 可移植性

内存优化

我们已经在上一章中详细讨论了内存优化。内存优化确保游戏以最少的内存使用运行。在某种程度上,它有助于增加设备支持和游戏稳定性。一个好的游戏必须具有优秀的内存管理,以便即使内存容量有限,也能平稳运行。

性能优化

性能优化确保游戏在每个目标安卓设备上都能平稳运行。然而,并不总是可能在所有设备上测试这种流畅性。大多数情况下,开发人员选择几个几乎等同于其他目标设备的设备来测试游戏。

可移植性

多分辨率支持和多平台支持也是开发抛光的一部分。因此,游戏可以以最小的努力达到最大可能的受众。对于许多安卓游戏来说,可移植性可能是成功的关键。

美术抛光

在这个阶段进行游戏美术的抛光。抛光游戏美术的主要目标是在相同的美术空间内提供更好的视觉质量。

游戏美术是游戏的最初推动力。因此,游戏美术的抛光可能会在市场上创造或毁灭游戏的未来。特别是对于安卓来说,在市场上有各种不同视觉质量的设备变化,游戏美术的抛光变得极为有用。

美术抛光主要分为三个阶段:

  • UI 抛光

  • 动画抛光

  • 营销图形

UI 抛光

UI 推动游戏流程。因此,UI 美术应该轻松传达用户在游戏中漫游的期望路径。因此,相应地抛光 UI 美术是绝对必要的。

动画打磨

几乎每个游戏都使用动画来实现各种目的。打磨动画意味着增加视觉效果,让用户从开发者的角度看游戏。主要用于体育游戏、FPS 和 RPG,动画是不可避免的。动画决定了游戏玩法的性格。

营销图形

当涉及到游戏时,营销资产是首先被可视化的东西。它们制造了炒作和兴趣,让用户开始玩游戏。如果营销艺术不足以吸引用户玩游戏,那么可能会有相当大的损失,无论实际游戏质量如何。

设计打磨

设计是一个预制阶段,并且可以在生产过程中进行改进的一般概念。然而,重要的是在开发后完善设计,以便最终应用程序可以提高质量。它有五个阶段:

  • 设计用户体验

  • 打磨游戏流程

  • 打磨元游戏

  • 游戏经济平衡

  • 游戏难度平衡

设计用户体验

用户体验是用户从用户角度看游戏的整体游玩和浏览体验。有几种情况下,游戏由于用户体验设计不佳而未能留住用户。因此,UX 必须通过实际用户行为的帮助进行打磨。

打磨游戏流程

在游戏开发过程中,游戏流程可能包含一些不必要的循环或操作。用户应该以最少的操作获得游戏的最大体验。每个操作都应该足够简化,以便用户在没有任何教程的情况下理解。然而,将游戏流程简化到那个水平并不总是可能的。但它应该足够简化,以便易于理解。

打磨元游戏

元游戏基本上是核心游戏玩法的容器或包装。打磨元游戏意味着打磨包装,使游戏对用户更有趣和吸引力。元游戏也负责货币化。因此,非常精心打磨的元游戏增加了成功的收入机会。

游戏经济平衡

许多开发者通常会在元游戏打磨的同时打磨经济平衡。然而,根据核心游戏模型,有许多方面需要分别注意。几乎每个游戏都与经济相关。这一方面应该在整个游戏过程中保持平衡,以使用户保持活跃并给他们一种进步的感觉。

游戏难度平衡

正如他们所说,一只手上的所有手指都不一样。同样,用户效率也不一样。它最有可能会变化,并反映在游戏排行榜上。因此,游戏的难度应该平衡,以便几乎每个玩家都有机会继续玩游戏。

游戏测试

游戏测试是规划游戏打磨的一部分。游戏测试是在游戏根据最初设计制作完成后进行的。它基本上揭示了整个游戏过程中的用户行为。

以下是游戏测试期间的探索领域:

  • 用户游戏难度水平

  • 用户在游戏过程中的操作

  • 用户在浏览游戏时的操作

  • 用户是否付费

  • 游戏是否运行顺畅

  • 用户是否能够接受游戏玩法

  • 用户留存

游戏测试是计划在有针对性的一小群用户中进行的。通常,开发者会在特定地区发布游戏的测试版进行游戏测试。前述观点基本上是游戏测试的优势。进行这种行为的唯一缺点是,由于初始游戏计划不佳,开发者可能会失去一些游戏测试地区的受众,但这在游戏测试后可以改进。因此,建议您在进行游戏测试之前完成具有最初阶段规划的完整游戏体验,并使游戏发布准备就绪。

用户游戏难度水平

游戏的难度方面因游戏设计和核心玩法而异。同一款游戏的所有用户在玩游戏时并不都同样高效。游戏测试揭示了用户在玩游戏时所面临的困难。

在收集了游戏测试结果的数据后,游戏平衡得到改进。这直接影响游戏的打磨。

用户在游戏过程中的操作

这一部分通常揭示了用户对游戏操作的使用。例如,它揭示了游戏机制是否支持少量游戏控制,如滑动、点击不同按钮、选择选项等。开发者在游戏测试期间收集所有这些数据。甚至每个动作的反应时间也可能被考虑。

根据这些数据,开发者可以了解游戏控制的便捷程度。用户是否能正确使用控制决定了游戏的成功与否。有时,如果开发者在用户操作方面遇到严重问题,他们会改变游戏控制。

用户在浏览游戏时的操作

在游戏测试阶段记录了用户在 UI 浏览期间的所有操作。游戏的 UI 流程和导航风格在整个过程中得到验证。有时,用户可能会忽略 UI 的某个部分。对于开发者来说,很难从用户的角度识别出这样的 UI 部分,尽管开发者可以轻松地浏览这些部分,因为他们自己已经实现了这些 UI 部分。这种情况表明,被相当数量的用户忽略的 UI 部分并没有得到足够的突出。

UI 中可能有几个不是主要游戏流程的直接部分,比如排行榜、优惠墙、成就、帮助、设置、IAP 屏幕、次要游戏模式等。如果用户长时间不访问这样的 UI 部分,而开发者无法预测到这一点,那么开发者可能会选择改变 UI 风格或找到替代解决方案。元游戏的成功大部分取决于这种打磨。游戏货币化也可以得到很大的改善。

用户是否付费与否

有几种游戏货币化模式可供选择。基本的三种类型是高级、免费和免费增值。开发者采用任何模式来为游戏产生收入。

顾名思义,高级游戏基本上是付费游戏。这意味着用户在第一次购买时就购买了整个游戏。因此,在这种情况下,用户在玩游戏时不需要付费。免费游戏完全免费玩,没有任何付费以获得任何优势的规定。开发者可以通过游戏广告计划收入。用户在开始玩游戏后有选择支付的选项。开发者设计元游戏使用户为了获得优势或增加游戏进度而支付游戏。

在游戏测试阶段,开发者监控用户是否为游戏付费。在免费增值模式中,开发者定义用户应付费以更快或更顺畅地进展的阶段。通过游戏测试验证这一计划以预测未来的收入。

游戏是否运行流畅

正如我们之前已经讨论过的,从优化的角度来看,流畅的游戏玩法是游戏打磨的主要部分之一。初始测试是在少数限制设备上进行的。然而,在游戏测试的情况下,更可靠的是专注于实时场景和真实设备以验证流畅的游戏玩法。然而,Android 上有各种硬件配置。开发者必须在游戏测试之前决定测试配置并设定基准。

开发者可以通过游戏测试记录实时 FPS、崩溃和其他性能数据。然后进一步优化游戏以达到目标可玩性。

用户是否能适应游戏玩法

并非每款游戏都容易理解。用户通常不会花时间去阅读单独的游戏说明部分来理解游戏,这是已经被证实的常见行为。相反,他们直接跳入游戏玩耍。因此,普通用户通常需要相当长的时间来理解游戏玩法。

一些开发者使用交互式教程来帮助用户理解游戏控制、玩法和游戏目标。有时,完成交互式教程才能继续游戏。这是解决问题的最佳方案。

然而,设计交互式教程的方式有很多种。游戏可能无法通过设计不佳的教程来理解。无法预测用户适应游戏玩法所需的时间。因此,通过游戏测试来了解用户是否在计划的时间内理解了游戏变得非常重要。这对用户留存有很大的影响。

用户留存

用户留存预测直接关联着游戏收入的预测,这代表着游戏的商业成功。如果用户第一次玩游戏后就再也不回来,那就意味着用户没有被留存。用户留存有几个部分:日留存、周留存、月留存等。

在游戏测试阶段,开发者会统计重复玩游戏的用户数量以及离开游戏的用户数量。开发者甚至会收集有关用户离开游戏的时间和具体点的数据。这可能揭示游戏模型存在问题。这个问题可以被纠正以留住更多的用户。

关注用户体验

游戏质量方面,用户体验(UX)是最重要的考虑因素。因此,对游戏的 UX 进行精细打磨变得极为必要。

我们可以将 UX 的打磨分为以下几个类别:

  • 视觉效果

  • 音效

  • 交易效果

  • 动作反馈

视觉效果

游戏的用户体验主要是视觉的。因此,每个视觉效果都为游戏增加了一层打磨,提高了体验质量。有几种类型的用户。视觉效果确保了用户的参与。基本上,打磨视觉效果意味着每个动作反馈都应该是视觉的。

例如,有一些用户可能是色盲。对于他们来说,仅有颜色视觉是不够的。通过引入具有不同形状的物体的视觉效果或其他动作,可以改善这种情况。

音效

声音定义了游戏的情绪。声音设计师根据游戏类型设计声音。有两种不同类型的音效:

  • 主题音乐

  • SFX

主题音乐

主题音乐是持续循环播放的音乐。它为游戏创造了一种氛围。大多数情况下,它增强了游戏玩耍或浏览 UI 时的乐趣。一个好的游戏必须有一个与游戏相配的主题。

SFX

SFX 是基于事件的声音,可以针对游戏中的特定动作或事件进行指定。SFX 的一些常见用途包括按钮点击、用户操作、游戏胜利、游戏失败、游戏开始等。

交易效果

大多数游戏有多个阶段的动作。主要的交易是各个阶段之间的交易。更流畅的交易效果会带来更好的用户体验,因为用户对游戏流程有清晰的了解。

还可能有其他交易。例如,如果游戏支持游戏内货币系统,那就必须有货币交易。大多数情况下,用户不会注意数字和文本的变化。然而,可见的交易会让用户注意到这些数字。

动作反馈

现在有许多游戏严格遵循动作反馈系统。用户的每个操作都应该有反馈。反馈可以是视觉的、声音的,或者两者兼有。更显著的反馈会带来更好的用户体验。

安卓特定的优化

安卓有一套特定的功能和限制。这为安卓特定的优化打开了可能性。这可以在以下安卓平台和设备的功能或限制上完成:

  • 充分利用硬件按钮

  • 坚持基本的安卓功能和功能

  • 更长时间的后台运行

  • 遵循谷歌 Play 商店效率指南

充分利用硬件按钮

典型的安卓手机或平板设备有以下按钮:

  • 主页按钮

  • 返回按钮

  • 菜单按钮

  • 音量增加按钮

  • 音量减按钮

  • 锁定/解锁/电源按钮

每个按钮都有基于安卓标准的功能。在游戏中使用这些按钮来实现完全相同的功能是一种良好的做法。

例如,按下返回按钮应该将用户带回到游戏的上一个屏幕或上一个状态。在游戏中最常见的使用返回按钮的方式是暂停游戏循环。同样,音量增加/减少按钮应该直接影响游戏声音,符合原生功能。

坚持基本的安卓功能和功能

在安卓游戏中实现基本的安卓功能并使用安卓特定功能是一种良好的做法。我们刚刚谈到了在安卓设备上使用设备按钮。

从功能的角度来看,安卓标准功能支持从游戏本身中退出游戏应用。与 iOS 不同,安卓游戏可以被退出。

更长时间的后台运行

用户通常不总是以传统方式退出游戏。相反,主页按钮被用来快速退出游戏。在这种情况下,游戏会进入后台并继续运行,除非用户恢复游戏或操作系统终止进程。游戏可以在后台停留的时间越长,游戏恢复的速度就越快。

主要是,使用低内存和低进程开销可以增加游戏在后台持续存在的时间。有时可能会有几次中断。在这种情况下,最好的做法是恢复到相同的状态,以获得更好的用户体验。

遵循谷歌 Play 商店效率指南

尽管安卓是一个开放源代码平台,但谷歌对安卓应用有一些指南;这些也适用于游戏。显然,谷歌 Play 商店是目前市场上吸引全球观众的最大平台。因此,遵循他们的指南以获得推荐总是明智的决定。

谷歌 Play 商店上有数百万款应用程序。如果没有被推荐,要吸引用户去玩某个特定的游戏或应用程序是非常困难的。

游戏可移植性

优化是最好的阶段,游戏可移植性应该在不影响游戏本身的情况下最大化。在这个阶段,可通过以下三种方式增加可移植性:

  • 支持各种屏幕尺寸

  • 支持多种分辨率

  • 支持多种硬件配置

支持各种屏幕尺寸

安卓在屏幕尺寸方面有很多变化。游戏控制系统是受不同尺寸影响最大的部分。设计游戏时,控制系统也应根据用户的便利性进行规划。

对于安卓手机游戏开发,安卓平板控件通常与安卓手机控件有所不同。平板的屏幕尺寸通常比手机大。因此,用户可以使用更多的空间。游戏应该针对小屏幕和大屏幕情况进行优化,以便更容易控制。

支持多种分辨率

相比之下,有一些安卓设备具有相同的屏幕尺寸,但分辨率不同。在这种情况下,主要的区别在于可见性。因此,支持多种分辨率设备需要更多的艺术技巧。

许多开发人员使用不同的艺术资源来支持不同分辨率的设备。我们已经讨论了安卓中不同分辨率的 dpi。因此,可以检测设备分辨率并相应地使用艺术资源。

特别是对于谷歌 Play 商店的游戏,谷歌支持在同一个应用下使用四种不同的应用程序包。因此,开发人员可以灵活地为同一个游戏创建和使用四个不同的 APK。然而,还有更多的分辨率。因此,还有其他几种方法可以实现它们。

集成游戏特定服务器是完成工作的最流行的方式之一。开发人员不会在 APK 中包含大部分艺术资源。相反,他们将不同分辨率的不同艺术包放在游戏特定服务器上。因此,游戏可以在需要时下载特定分辨率的资源。通过这种方式,开发人员设法将 APK 大小保持在最小限度。

支持多种硬件配置

单个游戏无法在每个硬件平台上产生相同的视觉和性能影响。游戏开发人员努力保持一定的标准,以确保游戏在多种配置上平稳运行。

有时,游戏会专门针对某些硬件平台进行优化。这种优化的常见例子是处理器架构。我们已经讨论了安卓游戏中使用的各种处理器架构。因此,游戏可以为单独的处理器架构进行移植。

支持尽可能多的硬件配置对于目标或获取用户非常重要。开发人员可能需要编写单独的代码来执行这样的游戏优化功能。

总结

游戏优化是游戏开发不可避免的一部分。然而,游戏的改进是没有限制的。开发人员应该计划优化阶段和变化,以支持并满足开发时间表。游戏优化帮助游戏获得更多用户,更多留存,并最终获得更多收入。一个经过精心打磨的游戏涵盖了本章讨论的每个部分。

游戏必须看起来好,感觉好,并且足够有趣以持续下去。最后但同样重要的是,它应该是高质量的,以便用户为之付费或推荐其他用户。游戏优化增加了游戏的寿命。它帮助开发人员规划更新和功能,并使用户在游戏中停留更长的时间。

到目前为止,我们已经涵盖了安卓游戏开发的几乎每个方面。然而,即使在开发游戏之后,开发人员也不能休息。有一些特定的参数需要满足才能使游戏成功。因此,开发人员必须在游戏中包含一些非游戏功能和功能。我们将深入探讨这些额外的第三方集成,并尝试在本书的最后一章中探索盈利技术。

第十三章:第三方集成、货币化和服务

Android 游戏开发或任何其他智能手机游戏开发都不完整,如果不实现后台服务。后台服务有助于游戏扩展和提升,达到更高的水平。

随着时间的推移,游戏开发的风格发生了很大变化。引入了新的风格和货币化技术。为支持这些技术,创建了新的游戏服务。许多科技公司开始推出自己的服务,以创建新的行业。我们所做的任何专业工作主要是为了谋生,游戏行业也不例外。然而,这个行业的目标是娱乐、乐趣和设备与用户之间的互动。开发人员通过这种方式赚钱。所有第三方集成和服务都有助于开发人员货币化游戏,直接或间接地增加收入。

服务可以是任何不特定于游戏的后台支持,可以改善游戏体验。大多数服务使用互联网和设备硬件和软件程序。大多数基于服务器的服务与应用程序一起工作,提供服务。

我们将通过以下主题在本章中详细了解这些方面:

  • Google Play 服务

  • 多人游戏实现

  • 分析工具

  • Android 应用内购买集成

  • Android 游戏内广告

  • 货币化技术

  • 规划游戏收入

  • 用户获取技术

  • 推出 Android 游戏

  • 发布 Android 游戏

Google Play 服务

Google 目前是 Android 应用的最大平台。此外,Google 是 Android 操作系统的所有者。因此,没有人比 Google 更适合成为 Android 平台的服务提供商。

Google Play 服务是为所有 Android 设备提供访问所有 Google 服务产品 API 的后台服务。它于 2012 年推出,以支持 Android 开发并将其提升到新的水平。

Google Play 服务包中最常用的服务有:

  • Google Analytics

  • Google IAB

  • Google 排行榜

  • 推送通知

Google Analytics

Google Analytics 是一种用于跟踪游戏中每一个事件的服务。这可以揭示用户行为、用户操作、每天玩游戏的用户数量、每个用户的游戏时间等。因此,开发人员不会错过任何数据。这些分析数据帮助开发人员识别游戏中的关键部分。借助这些帮助,开发人员可以改进游戏,提供更好的体验。

重要性

在测试或游戏测试阶段,通常无法追踪每个问题。当游戏规模变大,用户基数庞大时,更有可能暴露未知问题。Google Analytics 在这些领域有所帮助,不仅可以了解用户当前行为,还可以了解游戏性能。

集成技巧

Google Analytics 主要用于跟踪游戏事件。因此,跟踪事件必须非常谨慎地决定。触发点也必须以战略方式放置。开发人员可能不需要所有事件和行为数据。过多的跟踪甚至可能对应用程序有害,因为会使用更多数据并在游戏中进行更多处理。

开发人员应始终优先考虑事件。事件应基于游戏流程设计进行跟踪。然后应通过用户操作进行验证。

从货币化的角度来看,跟踪用户何时遇到付费墙或哪个部分被访问更多应始终是优先考虑的。在最受欢迎的部分放置简单的广告可能会增加应用的收入。

最佳利用

Google Analytics 工具的最佳利用方式与任何其他 Android 应用程序分析工具并无二致。这个工具是跟踪用户活动的最佳方式,通过数据,开发人员可以轻松预测用户对游戏的动机或意图。

Google IAB

在现代游戏世界中,有许多货币化应用程序的方法。应用内计费是最受欢迎的方法之一。Google Play 服务配备了 Google 应用内计费工具。该工具直接与 Google Play 商店相关联。

通过这个工具,开发者可以在游戏内设计一些可购买的内容。对用户来说,从 Android 应用程序的内置商店购买非常容易。

Google IAB 模型

让我们快速看一下 Google IAB 中的三种可购买选项。我们稍后将详细查看它们:

  • 消耗品

  • 不可消耗的物品

  • 订阅

消耗品

用户可以从商店多次购买此物品。Google 不跟踪这类物品。这类物品最常见的例子是游戏虚拟货币。许多游戏都围绕虚拟货币设计,大多数情况下,这个因素是游戏货币化的支柱。

不可消耗的物品

它们基本上是用户的一次性购买。Google 始终跟踪用户通过 Google IAB 为任何应用程序所做的这些购买。即使用户卸载应用程序并重新安装,也可以将不可消耗的购买恢复到用户的账户中。

该类别下最常见的物品是游戏模式。在许多游戏中,有一些开放模式和一些可以购买。该系统还与试用和购买货币化方面合作。

订阅

订阅基本上是一种基于时间的货币化模型。这主要用于典型的基于服务的应用程序,如音乐频道、电视频道、图书馆频道等。然而,很少有游戏使用订阅来实现货币化。

集成 Google IAB

Google Play 服务配备了 IAB API。开发者需要在 Google Play 商店上注册应用程序以获得实时访问权限。该系统使用称为 SKU 的项目 ID。每个 SKU 代表 Play 商店中的一个项目。开发者可能不会为特定游戏使用在 Google Play 账户中创建的所有 SKU。

Google IAB 的优势和劣势

我们已经注意到,Google IAB 提供了一个在应用程序内实施直接数字购买系统的平台。这为开发者和消费者节省了大量的工作和时间。让我们快速看一下 Google IAB 的优势:

  • Google IAB 提供了一个直接的平台,可以在应用程序内购买应用程序组件或服务

  • Google IAB 简化了应用程序的货币化方面

  • Google IAB 为消费者提供了多种付款选项,方便快捷

  • Google IAB 存储和管理不可消耗物品的购买

  • Google IAB 具有无忧实施和出色的客户支持

  • 易于退款流程完全由 Google IAB 管理

到目前为止,Google IAB 已经证明是开发者和消费者或用户的一个优秀系统。然而,Google IAB 仍然在一些领域落后。现在,让我们看看 Google IAB 需要改进的领域:

  • Google IAB 只通过 Google Play 服务提供计费服务

  • Google IAB 仍然不支持运营商计费

  • 并非每个用户都愿意向 Google 提供信用卡信息

尽管存在这些问题,Google IAB 仍然是 Android 开发者最受欢迎的计费平台。Google 已经开始在 Google IAB 中包括运营商计费服务,这可能是最重要的功能。

Google 排行榜

排行榜是游戏和类似竞技应用程序的平台,每个用户都可以在其他用户中跟踪自己的进度。排行榜已经证明是许多游戏的推动力。Google Play 服务配备了 Android 应用程序的内置排行榜系统。

意义

在游戏中集成排行榜总是一个加分项,因为它帮助用户即使在游戏不是多人游戏时也能相互竞争。人类心理是试图变得比其他人更优越。利用这个功能,可以比平常有更多的用户参与。然而,排行榜上的竞争标准必须谨慎选择。

排行榜驱动游戏的一个很好的例子是糖果传奇。用户非常活跃,玩这个简单的游戏以领先他们在排行榜上的朋友。

集成谷歌排行榜

没有单独的谷歌排行榜包。这可以通过集成谷歌游戏服务本身来包含。然而,排行榜必须在谷歌游戏账户中设置才能使用。

开发人员可以选择任何参数或计算来存储排行榜数据。谷歌排行榜支持来自单个游戏的多个排行榜的数据。大多数开发人员有效地使用这个功能来显示不同的领导者列表,取决于时间段、地区或其他一些定制参数。

各种排行榜

谷歌排行榜的主要变体有两种类型:

  • 社交排行榜

  • 公共排行榜

社交排行榜

社交排行榜只列出与玩家圈子相连的玩家。对于这个功能,玩家必须登录他们各自的谷歌账户。这对于那些在同一个应用程序中玩并选择分享他们的活动的玩家有限制。

公共排行榜

公共排行榜存储了选择公开发布分数的玩家的数据。否则,即使他们的分数比公共排行榜上的现有玩家更好,这些数据也不会被谷歌排行榜显示。

存储和显示排行榜的选项

排行榜存储可以分为两种类型,根据升序和降序。就谷歌排行榜而言,它们被称为:

  • 越大越好

  • 越小越好

分数始终是一个数字值,再次分为三种格式:

  • 数字值格式

  • 时间格式

  • 货币格式

在数字值的情况下,开发人员可以指定小数点的位置。在时间格式的情况下,开发人员需要以毫秒传递分数,它将自动解释为hh:mm:ss格式。在货币格式的情况下,开发人员需要事先指定货币及其单位值。排行榜只接受单位值,并将其转换为指定的单位格式。

排行榜可以有独特的图标来显示或指示独特的排行榜。

推送通知

推送通知服务可以通过谷歌云消息GCM)服务实现。让我们快速看一下云消息架构。

主要有四个组件用于使用 GCM 实现 Android 的推送通知:

  • 数据库

  • 服务器

  • 目标设备

  • GCM 服务

数据库

数据库存储了客户或目标设备与 GCM 服务的注册详细信息。因此,每个设备只需要注册一次。相同的详细信息用于向注册的目标设备发送推送通知。

服务器

开发人员需要建立一个服务器来实现和控制推送通知。

目标设备

目标或客户设备是从 GCM 推送消息的平台。每个目标设备都有一个通过 GCM 的唯一注册 ID。没有注册,目标设备无法接收任何通知。

GCM 服务

GCM 服务负责注册设备并向它们推送消息。服务器向 GCM 服务请求一组注册 ID 和定制消息。GCM 只负责将给定内容推送到指定设备:

GCM 服务

使用 GCM 的推送通知系统的工作流程

现在,让我们讨论推送通知的工作流程。在前面的图表中,推送通知系统根据指示的索引(例如,abc,等等)工作:

  1. a:客户端或目标设备请求 GCM 注册应用程序 ID 和发送者 ID。

  2. b:GCM 在成功注册后将注册 ID 发送回发送者。

  3. c:设备将注册 ID 发送到开发人员的服务器。

  4. d:服务器将注册 ID 存储到数据库中。

  5. e:开发人员启动具有自定义内容的推送通知的过程。

  6. f:服务器从数据库中获取注册 ID 列表。

  7. g:数据库提供所有注册 ID。

  8. h:服务器使用开发人员指定的内容和注册 ID 请求 GCM。

  9. i:GCM 根据其注册 ID 将相同的内容推送到相应的目标设备。

集成推送通知

集成推送通知分为三个步骤:

  1. 应用集成

  2. GCM 设置

  3. 服务器设置

应用集成

开发人员需要为应用程序设置一个 GCM 客户端,这是 GCM 通信的媒介。以下是关于 GCM 通信服务的客户端开发的简要介绍。

它需要一组清单权限:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<permission android:name="com.example.gcm.permission.C2D_MESSAGE"
        android:protectionLevel="signature" />
<uses-permission android:name="com.example.gcm.permission.C2D_MESSAGE" />

清单还需要声明 GCM 接收器和 GCM 服务:

<receiver android:name="com.google.android.gms.gcm.GcmReceiver"
            android:exported="true" android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.REGISTRAION" />
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<category android:name="com.example.gcm" />
      </intent-filter>
</receiver>
<service android:name=".GcmService" android:exported="false">
      <intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
      </intent-filter>
</service>

让我们看一下使用 GCM 注册应用程序的注册过程。注册可以通过许多方式进行,取决于开发风格。我们将在主要的 Android 活动中遵循最简单的流程,并存储应用程序的一次性注册 ID。

以下是所需的声明:

private final Context testContext = this;
private final String SENDER_ID = "<Application ID from Google developer console>";
private final String SHARED_PREF = "com.test.gcmclient_preferences";
private final String GCM_TOKEN = "testgcmtoken";

注册代码应放在onCreate()中:

SharedPreferences appPrefs = testContext.getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE);
String token = appPrefs.getString(GCM_TOKEN, "");
if (token.isEmpty()) 
{
  try 
  {
    InstanceID instanceID =  InstanceID.getInstance(testContext);
    token = instanceID.getToken(SENDER_ID,  GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
    if (token != null && !token.isEmpty()) 
    {
      SharedPreferences.Editor prefsEditor = appPrefs.edit();
      prefsEditor.putString(GCM_TOKEN, token);
      prefsEditor.apply();
    }
  }
  catch (Exception e) 
  {
    e.printStackTrace();
  }
}

现在,让我们定义GCMService.java来处理 GCM 消息:

public class GcmService extends GcmListenerService 
{
  @Override
  public void onMessageReceived(String from, Bundle data) 
  {
    JSONObject jsonObject = new JSONObject();
    Set<String> keys = data.keySet();
    for (String key : keys) 
    {
      try 
      {
        jsonObject.put(key, data.get(key));
      } 
      catch (JSONException e) 
      {
        e.printStackTrace();
      }
    }
    try 
    {
      sendNotification("Received: " + jsonObject.toString(5));
    } 
    catch (JSONException e) 
    {
      e.printStackTrace();
    }
  }

  @Override
  public void onDeletedMessages() 
  {
    Log.d("Message is deleted …");
  }

  @Override
  public void onMessageSent(String msgId) 
  {
    Log.d("Message is sent …" + msgId);
  }

  @Override
  public void onSendError(String msgId, String error) 
  {
    Log.d("Sending Error … Msg" + msgId);
    Log.d("Error …" + error);
  }
  private void sendNotification(final String msg) 
  {
    Log.d("Sending Msg …" + msg);
  }
}

GCM 设置

Google Play 服务带有 GCM 系统。必须从 Google API 控制台启用 GCM。每个注册的应用程序都有自己独特的应用程序 ID,这是配置推送通知系统所需的。

以下是启用 Android 项目的 GCM 的步骤:

  1. 在 Google Cloud 平台上创建一个项目。

  2. 使用 Google API 生成 API 密钥。

  3. 为 Android 创建服务器密钥。

  4. 为应用程序启用 GCM。

服务器设置

通知服务器开发可以通过任何云连接服务器技术实现。开发人员需要通过满足以下标准来设置应用程序服务器:

  • 应用服务器应该能够与应用程序进行通信

  • 应用服务器应该能够向 GCM 连接服务器发送格式正确的请求

  • 应用服务器应该能够处理应用程序请求并使用指数退避重新发送它们

  • 应用服务器应该能够将 API 密钥和客户端注册令牌存储在安全的数据库中

推送通知的重要性

推送通知是现代游戏开发中不可避免的一部分。推送通知用于以下原因:

  • 用户留存

  • 用户控制

  • 了解用户行为

  • 替代通信渠道

用户留存

推送通知为用户提供游戏的最新更新和信息。有许多情况下,用户下载游戏然后忘记了它。有时,用户中途离开游戏。推送通知帮助这些用户重新对游戏产生兴趣。这个过程提高了用户的留存率。

用户控制

通过设备设置和通知中心,开发人员可以控制用户将看到的内容,并相应地引导用户。

了解用户行为

使用用户控件,开发人员可以跟踪用户在通知上的行为。根据用户的行为,开发人员可以了解用户喜欢和不喜欢什么。

替代通信渠道

与最终用户进行沟通的方式有很多种。大多数情况下,用户并不经常与开发者进行沟通。因此,单向通信渠道被证明是有效的。推送通知系统完美地适应了这一角色。这是传递有关游戏的最新消息、更新、优惠和功能的最佳媒介。在某些设计模型中,它可以用于向用户传递游戏状态信息。

多人游戏实施

曾经多人游戏局限于传统的个人电脑和游戏机。现代游戏产业广泛使用社交网络。这自动为多人游戏开辟了机会。

改进的硬件系统和现代连接的持续网络支持增强了多人游戏的世界。多人游戏主要可以分为两类:

  • 实时多人游戏

  • 回合制多人游戏

实时多人游戏

实时多人游戏就像一起进行体育比赛,每个玩家同时对游戏或其他玩家的任何行动做出反应。例如,足球比赛由 22 名球员进行,每个球员都会根据情况同时行动。如果我们从数字游戏的角度想象同样的情景,那就是实时多人游戏。

在多人游戏的一般结构中,应该有一个服务器,游戏运行其中并进行所有逻辑和计算。服务器在需要时与数据库交互,用户没有任何控制权。

客户端或终端设备是与用户交互的唯一媒介。然而,在许多情况下,客户端端会使用一些额外的层来执行一些操作,而无需服务器验证,以保持实时多人游戏的即时性。

让我们来看看实时多人游戏系统的一般架构:

实时多人游戏

实施实时多人游戏的最佳实践是在每个终端应用层引入同步层。这一层充当终端设备应用和服务器之间的媒介。

回合制多人游戏

回合制多人游戏是一种系统,只有一个玩家有机会一次玩游戏。例如,在国际象棋游戏中,当一个玩家轮到时,另一个玩家保持空闲。

一般来说,回合制多人游戏系统也是由服务器控制的。服务器可以是客户端之一。然而,执行层的工作如下图所示。许多时候,数据库也是架构的可选部分。服务器负责在特定终端设备上激活 UI 控制,并且只监听该设备。让我们来看看架构图:

回合制多人游戏

在安卓游戏中可能存在更多类型的多人游戏模型。到目前为止,我们只讨论了在互联网上实施的模型。在安卓中也可以进行本地多人游戏。我们可以将这些游戏分类为以下几类:

  • 单屏实时多人游戏

  • 传递和玩回合制多人游戏

  • 本地网络多人游戏

单屏实时多人游戏

这种多人游戏依赖于硬件特性、其他配置和可行性。设备必须支持同时多个输入,以支持实时多人游戏。对于安卓触摸设备,建议开发者针对大屏设备,以提供更多的控制空间,同时支持多个玩家。

传递和玩回合制多人游戏

这是一种单屏幕回合制多人游戏模型。在这种模型中,所有参与者应该手动管理游戏顺序,以与游戏轮次同步。

在这里,一个玩家在玩完自己的回合后将同一设备交给下一个玩家。然后,下一个玩家根据游戏的当前状态做出反应。在这种模型中,游戏状态直到每个回合完成之前都不会改变。

在单屏多人游戏模型中,不需要网络连接,数据库是一个可选组件,可以存储在设备内存中。

本地网络多人游戏

实时和回合制多人游戏模型都可以使用本地网络连接来实现。在这种情况下,参与的设备之一必须充当服务器,并使用蓝牙、Wi-Fi 或红外连接控制本地网络连接上的游戏玩法。

分析工具

我们已经讨论了 Google Analytics。除了 Google 之外,还有其他几种分析服务。我们将从一个一般的角度讨论分析。

分析工具对游戏来说是不可避免的。它们帮助开发者更好地了解用户,直接影响游戏质量、用户留存和货币化。

分析工具的要求

多年来,开发者一直在以各种形式收集和分析用户数据。我们经常遇到关于许多产品的表格和分析调查。这些数据帮助开发者或制造商修改或改进产品。

让我们通过以下几点来看一下分析数据的种类及其重要性:

  • 用户行为

  • 游戏崩溃报告

  • 游戏事件触发器

  • 游戏会话时间

  • 游戏频率

  • 游戏平衡

  • 用户留存

  • 防盗版

用户行为

分析工具可以跟踪每个用户的每一个动作。这些数据可以进一步分析,并猜测用户在游戏中的行为。这种行为验证了应用程序或游戏的元设计。

游戏崩溃报告

几乎所有的分析工具都可以报告每次崩溃事件及其原因和位置。然而,游戏代码的编码包无法完全揭示位置,其中类和其成员以无意义的符号编码。

这有助于开发者识别玩游戏的设备以及崩溃的确切问题。

游戏事件触发器

开发者可以从游戏本身设置触发器来跟踪游戏的任何或每一个方面。这些可以是游戏内的任何事件。游戏开发者通常使用这种触发系统来启动游戏、结束游戏以及一些战略事件,比如 IAP、广告显示、模式选择(如果有的话)等。

游戏会话时间

分析工具通过触发应用程序进入前台或应用程序启动,以及应用程序进入后台或应用程序退出之间的两个事件来跟踪游戏时间。通过计算两者之间的时间,分析工具通知开发者用户在单个会话中在应用程序内的总时间。

游戏频率

这基本上是每个用户启动游戏的平均次数。因此,开发者可以通过数据增加或改进会话。开发者可以对给定时间的频率进行分类,比如每日频率、每周频率或每月频率。

游戏平衡

开发者可以收集用户得分和游戏时间的数据来检测每个玩家的难度。然后,开发者可以相应地平衡游戏。然而,每个玩家的能力和技能都不同。因此,开发者必须设定一些标准来全球平衡游戏。

用户留存

用户留存是开发者从游戏中产生收入最重要的方面之一。这意味着用户重复玩游戏的次数。用户留存也可以是基于时间的,比如每天、每周和每月。

防盗版

在安卓游戏中,可能存在高级或免费游戏模型。在这种模型中,用户用真实货币购买游戏或游戏内的一些组件。盗版对于许多黑客或追踪者来说是一种旧的做法。他们可以侵入支付系统或解码安全层,以免费提供付费游戏或付费组件。盗版对于开发者来说是一个在产生收入方面的主要问题。

分析工具可以检查游戏并提供用户详细信息以验证购买,从而增加额外的安全层以防止游戏盗版。

分析工具的货币化方面

分析工具在前面提到的所有点上都很有用。所有功能都直接或间接地与游戏收入相关。一些功能帮助开发者改进游戏质量,其余功能可以直接增加游戏收入,或者可以帮助开发者通过分析数据规划更多收入。

根据分析数据,开发者可以执行以下操作:

  • 识别游戏的热门地区

  • 识别用户的喜好和厌恶

  • 验证和改进元游戏

  • 跟踪付费用户

  • 跟踪和计算广告显示

识别游戏的热门地区

识别游戏最受欢迎的地区有助于开发者通过广告或一些付费内容在某个地区规划更多收入。特别是对于免费或免费游戏,极其必要找到用户经常访问的游戏部分。

识别用户的喜好和厌恶

游戏可能有几个部分。用户可能喜欢其中一些,不喜欢其他部分。除非开发者发布游戏或对大量用户进行游戏测试,很难预测用户的喜好和厌恶。

通过分析数据,开发者可以轻松指出用户喜欢或不喜欢的部分。开发者可以改变策略或计划更好的游戏更新。

验证和改进元游戏

游戏通常有两个开发领域:游戏玩法和元游戏。元游戏设计是通过预测用户对模型的接受来完成的。只有分析工具可以在游戏发布后验证这一预测。

跟踪付费用户

开发者可以通过分析工具跟踪哪些用户遇到了付费墙,以及谁实际上为游戏付费。这些数据直接影响游戏收入。

跟踪和计算广告显示

开发者实际上可以跟踪广告调用和广告显示的次数。因此,更容易预测广告收入,开发者甚至可以计划更好地填充广告。

一些有用的分析工具

我们已经在 Google Play 服务下讨论了 Google 分析工具。市场上有许多与 Google Analytics 一样优秀的分析工具,可以作为替代的好选择。在使用分析工具方面,开发者没有限制。大多数工具都是免费且易于使用,开发者甚至可以集成多个工具以实现不同的目的。

让我们快速看一下这些工具:

Flurry

最受欢迎的游戏分析工具之一是 Flurry。Flurry 几乎具备用于分析目的的每一个功能。这款轻量级的 SDK 易于安装,开发者可以立即开始获取数据。

GameAnalytics

GameAnalytics 是一款免费且功能强大的游戏开发者分析工具。它可以帮助您了解玩家行为,并通过动态仪表板上的分析数据构建更好的游戏。

Crashlytics

Crashlytics 是最强大和高效的错误报告工具。它可以拦截任何错误和异常,并提供尽可能详细的信息。Crashlytics 对开发者来说是轻量级且易于使用的。

AppsFlyer

AppsFlyer 是一个单一的实时仪表板,是一个带有分析功能的全能营销工具。它基本上使用 AppsFlyer 的NativeTrackTM来为游戏提供分析支持。

Apsalar

Apsalar 主要用于广告归因。它可以很好地查看游戏营销的投资回报率。它还有助于找出哪些营销活动有效,哪些需要避免。他们还提供了一些出色的营销工具,比如SmartTags,可以为开发者提供更详细的营销分析。

Mixpanel

Mixpanel 的好处主要是针对非技术人员,他们可以轻松创建自定义查询,而不需要了解 SQL。强大的界面允许开发人员对用户进行分段,并查看哪些部分对游戏效果最好。

Localytics

Localytics 提供了大多数数据分析功能。该平台提供实时分析、再营销数据、归因等。Localytics 的消息功能与其他一般的分析工具不同。

Appcelerator

Appcelerator 是一款用于移动应用测试、部署和分析的企业套件。该工具的基本功能是一个交互式的基于平板电脑的移动应用,可以在多个平台上使用,并立即提供对五个关键移动指标的洞察:留存、参与、采用、质量和转化。

Android 应用内购买集成

应用内购买是一种功能,通过该功能可以使用多种支付网关从应用内购买应用的组件。这是 Android 游戏的主要货币化方面之一。

什么是应用内购买?

在现代游戏行业中,免费游戏正在蓬勃发展。这意味着用户可以免费玩游戏,但他们必须为某些组件或游戏进度优势付费。这种模式已被证明是成功的,因为它在数字游戏的免费和高级概念方面都得到了支持。

应用内购买完美地实现了这一目的。我们已经讨论了 Google 应用内结算服务,这只是通过 Google 进行应用内购买的手段。但还有其他支持相同功能的服务。

一般来说,游戏应该提供应用内购买项目,让用户选择购买以下类型的内容:

  • 解锁游戏中的某些功能

  • 购买某些物品以获得比其他玩家更大的优势

  • 解锁游戏内的一些模式

  • 增加游戏的易玩性

  • 移除烦人的广告

有许多类型的用户对同一游戏有不同的要求和不同的技能。应用内购买为他们所有人提供了按照自己的便利玩游戏的机会,同时开发者也能赚一些钱。

应用内购买选项

您了解到 Google IAB 并不是 Android 应用内购买的唯一选择。还有一些其他具有几乎相同功能的选择。根据支付方式,有不同的服务类型。用户可能不会选择一种支付方式,但如果提供了几种选择,那么购买的机会肯定会增加。

为用户提供尽可能多的支付选项始终是一个好习惯。出于以下原因,需要多种购买选项:

  • 并非所有用户都可能有信用卡

  • 并非所有用户都可能有借记卡

  • 并非所有用户都可能激活网上银行

  • 并非所有用户都有足够的通话时间余额

  • 并非所有用户都喜欢直接使用真实货币

开发人员应该提供尽可能多的选项来解决这些问题,让用户使用真实现金进行游戏。目前,可用的计费服务支持多种支付方式,但我们可以将这些服务分类为两个主要部分:

  • 商店计费服务

  • 运营计费服务

商店计费服务

商店计费服务是基于用户下载游戏的商店。游戏应该连接到一个提供了 API 的商店才能访问这个功能。我们已经讨论过,Google IAB 是一种商店计费服务,包括多种支付方式,包括信用卡、借记卡、选择性运营计费等。

然而,Google IAB 并不是唯一可用的商店计费服务。除了 Google 之外,最值得一提的商店计费是亚马逊计费服务,它提供了几乎与 Google 相同的功能。

亚马逊计费服务

亚马逊计费服务的工作方式与 Google IAB 完全相同。但是,API 和集成与 Google IAB 略有不同。

开发人员需要包括com.amazon.device.iap包来集成亚马逊 IAP。这个过程主要有三个组成部分:

  • ResponseReceiver

  • PurchasingService

  • PurchasingListener

响应接收器

亚马逊 IAP 是一个异步过程。它作为一个后台服务运行,需要实现一个响应接收器。开发人员需要在清单文件中声明接收器。

PurchasingService

PurchasingService类用于检索有关用户的各种信息,执行购买,并通知亚马逊购买服务有关购买完成的信息。

PurchasingListener

PurchasingListener接口用于处理来自亚马逊服务器的异步回调。应用的 UI 线程处理所有回调,因此开发人员应该在 UI 线程上检查运行的进程。

从功能和集成的角度看,亚马逊 IAP 与 Google 类似。还有其他商店可能支持自己的计费服务。还有另一种选择,就是实现开发者自己的支付门户。然而,大多数安卓游戏开发者更倾向于使用主流的计费服务。

运营计费服务

一些游戏开发者使用运营计费服务进行货币化。运营计费意味着用户通过其手机余额向开发者支付应用内产品。这个手机余额由连接提供商管理。

目前,Google IAB 已开始在商店计费中支持运营计费。

应用内购买的类型

开发人员主要可以设计他们的 IAP 产品为三种类型。这些产品的类型取决于游戏设计和游戏类型。这些类型是:

  • 可消耗物品

  • 非消耗品

  • 订阅

可消耗物品

这些物品是用于在应用内消耗的。在 Google IAB 的情况下,这些产品被称为非托管产品。

计费服务提供商不会跟踪用户对这些物品的消耗。大多数情况下,游戏内货币、增强道具、额外生命等是这类产品的主要类型。用户可以多次购买同一物品。

可消耗物品必须在计费服务器上定义,以便让计费服务理解。

非消耗品

非消耗品是指使用后不会过期的物品。计费服务器会跟踪这些购买。

当用户购买这种类型的物品并卸载应用后,重新安装应用时,这些购买可以被检索到。这意味着用户只需要在应用的整个生命周期内购买这个产品一次。

订阅

这是购买应用内某些功能的时间或可用性。在游戏中,订阅的用途非常有限。然而,这是一个为有限的时间或使用提供某些功能或服务的好选择。

在持续时间内,用户可能不会购买相同的物品,但是有一个可再生的功能,允许用户在服务期限到期后再次订阅相同的东西。

安卓游戏内广告

游戏内广告对于免费和付费游戏的货币化来说是最重要的因素。开发者利用他们的游戏平台展示广告以产生收入。

它是如何运作的:

  1. 广告商向各种广告代理提交广告。

  2. 每个广告都有一个特定的价值和时间限制,分别称为广告活动成本和广告活动时间。

  3. 开发者与这些代理订阅。

  4. 开发者集成代理广告平台以包括和展示广告。

  5. 开发者设定广告类型、流派和级别的参数。

  6. 当应用程序向代理服务器发出广告请求时,它会寻找符合开发者预定义标准的可用或正在运行的广告活动。

  7. 在成功匹配后,服务器会将广告元素发送到客户端应用程序。

  8. 应用程序加载广告。

  9. 应用程序根据请求展示广告。

  10. 服务器会记录成功展示广告的次数,并根据广告活动成本计算收入。

  11. 开发者在满足代理的一定标准后收到收入。

广告要求

完全免费的游戏除了广告或赞助之外没有任何收入来源。我们只会在这里看广告。让我们了解游戏内广告的要求。

我们都在努力谋生。安卓是一个开源平台,大部分用户都是免费用户。这意味着开发者只剩下一个选择。与其他货币化方面相比,广告是一个可以依赖的好平台。

作为一个行业,广告已经存在很久,并且在市场上证明了它的可持续性。游戏内推广只是展示广告的另一种方式。这样一来,对于开发者和广告商来说总是双赢的局面。

广告货币化术语

现在,我们将讨论典型的游戏广告平台。开发者需要熟悉一些术语,以适应游戏内广告:

  • eCPM

  • CPC/CPA

  • CPI

  • RPM

  • 填充率

eCPM

eCPM代表有效每千次展示成本,它是通过广告横幅或广告活动产生的广告收入计算得出的结果,除以该横幅或广告活动的广告展示次数,以 1,000 为单位表示,最后用字母M表示。

CPC/CPA

CPC代表每次点击成本,这意味着开发者会在用户点击展示的广告时赚取一定金额。CPA代表每次行动成本,与 CPC 类似。

CPI

CPI代表每次展示成本,这意味着开发者会在应用程序内成功展示任何广告时赚取一定金额。一般来说,这些收入低于 CPC。

RPM

RPM代表每千次展示收入。它表示从一千次插页广告中产生的总收入。RPM 包括所有类型的收入模式。RPM 由以下公式计算:

RPM = (总收入) / (广告展示次数 / 1000)

填充率

填充率是服务器成功展示广告的百分比。我们已经知道应用程序向广告服务器请求广告。这被称为“请求”。如果服务器成功地根据请求展示广告,那么广告被称为“展示”。所以我们有了我们的填充率,如下:

填充率 = (展示次数/请求次数)100%*

广告类型

有几种广告样式可以用于安卓游戏:

  • 横幅广告

  • 插页广告

  • 视频广告

  • 游戏内动态广告

横幅广告

横幅广告通常是具有持续显示功能的广告,用户无法关闭或隐藏。然而,CPI 的广告活动价值很低,但 CPC 是可以接受的。许多开发者现在避免使用横幅广告,因为它占据了游戏屏幕的大部分空间。横幅广告以给定的矩形形状显示在可见显示的边缘。

可能的横幅显示位置如下:

  • 左上角

  • 顶部中心

  • 右上角

  • 底部左侧

  • 底部中心

  • 右下角

根据当前情况的尺寸变化如下表所示:

横幅类型 目标 像素尺寸
标准横幅 手机和平板电脑 320 x 50
大横幅 手机和平板电脑 320 x 100
IAB 全尺寸横幅 平板电脑 468 x 60
IAB 排行榜 平板电脑 728 x 90
智能横幅 手机和平板电脑 屏幕宽度 x 32 屏幕宽度 x 50 屏幕宽度 x 90

插页广告

插页广告是基于各种广告活动的全屏可点击图像广告。通常,插页广告有一个定义的关闭按钮,供用户关闭广告并返回游戏。

当插页广告显示时,广告视图会置于前景,将主游戏视图推到后台。因此,每次游戏线程触发中断时都会显示广告。

这种类型的广告在游戏中被广泛使用,因为可以产生可观的收入。游戏货币化设计在插页广告中起着重要作用。每个广告位置都必须基于分析数据进行战略性安排。

整合最佳实践

整合插页广告应遵循一些逻辑的广告显示周期:

整合最佳实践

遵循这个周期总是一个好的做法。广告应在显示之前加载并处于就绪状态。关闭广告后,下一个广告应立即加载以避免加载延迟。

视频广告

视频广告是最新的收入生成程序之一。这种类型的广告具有最高费率。然而,视频广告的可用性相对较少,比图像插页广告少。有两种类型的广告:

  • 全长广告

  • 短长度广告

全长广告

全长广告通常较长。这种类型的广告通常是可跳过的,提供了一定时间后跳过的选项。

短长度广告

短长度广告相对较小,没有跳过选项。

游戏内动态广告

这个概念提供了在预定义位置和大小内显示任何可用广告横幅的选项。无论实际广告尺寸如何,该机制都会将广告调整为应用程序中给定的尺寸。

货币化技术

货币化基本上是从任何应用程序中生成收入的系统或策略。开发者需要根据游戏需求决定他们的游戏货币化模式。我们可以将这些模式分类为四类:

  • 高级模式

  • 免费模式

  • 免费模式

  • 试玩购买模式

高级模式

这是典型的先付费后玩模式。用户需要在下载游戏之前付费。通常,这些游戏没有应用内购买或游戏内广告。这只是用户购买游戏玩法的一次性费用,通常所有用户都有相同的游戏进度机会。

免费模式

这种模式提供免费游戏,但可能包括游戏内广告以产生收入。用户可以免费玩完整的游戏,但对于任何操作都没有额外特权。

免费模式

这种模式提供免费游戏,游戏可以完全免费玩,不需要花费任何真实货币。然而,这种模式提供应用内购买,以提供额外内容或游戏进度的便利。

试玩购买模式

这个模型已经存在多年了。然而,很少有开发者喜欢使用这个模型。开发者创建了相同游戏的不同特性的独立版本。

免费版本通常具有有限的内容或有限的使用。这个版本可能包含广告,也可能不包含广告。游戏的完整版本通常遵循典型的高级游戏模型。有时,开发者在免费版本的游戏中使用应用内购买来解锁完整版本,这是一种聪明的做法,因为它消除了创建和管理两个不同应用程序的麻烦。

规划游戏收入

就我们讨论的范围而言,我们对游戏收入的产生有了一个公平的了解。游戏开发者不能一直开发游戏而不产生收入或没有强大的财务支持。现在让我们讨论游戏规划,以便让开发者继续开发游戏。

收入与利润

大多数新的游戏开发者不知道收入和利润是两回事。

收入是游戏直接从用户那里产生的总金额。制作游戏可能会花费开发者一些钱,而每个第三方媒介可能会收取一定比例的收入或某些服务的费用。在支付所有必要的款项和分成后,剩下的金额被称为利润。因此,高收入并不意味着高利润。

然而,没有产生收入,就不可能有任何利润。因此,开发者必须规划收入以产生利润。

收入来源

现在,我们知道产生收入是必要的。为了产生收入,开发者必须了解可能的收入来源。我们将在这里讨论主要的来源:

  • 广告收入

  • 应用内购买收入

  • 其他来源

广告收入

特别是对于免费和免费游戏,广告是主要的收入来源之一。有很多广告代理通过广告服务器提供广告。不同的广告活动可能有不同的价值。

还有另一个平台叫做广告调解。这个平台提供来自不同代理的广告。有时,这个平台有助于找到可用广告中的最高价格。这个特殊功能被称为实时竞价。

应用内购买收入

这是主要用于免费游戏模型的一种产生收入的方式。开发者免费提供游戏,但某些内容和功能被锁在游戏内。一旦用户习惯了游戏并愿意花额外的钱来加强游戏的控制力,他们就会使用应用内购买。

通过应用内购买规划收入完全取决于游戏设计和市场行为。一些游戏模型需要内容,一些需要功能,而一些则需要两者兼具。

应用内购买可以通过几种计费和购买服务进行,我们已经讨论过。然而,选择特定的服务可能会影响收入的产生。因此,开发者在与计费服务提供商签约之前应始终研究市场趋势。

其他收入来源

除了广告和应用内购买之外,还有其他收入来源。Offerwall 和优惠券系统是另外两个选择。开发者可能选择品牌和赞助来为游戏赚更多的钱。然而,从一般的角度来看,这些并不是收入来源的手段。

随着行业的现代化,可能会出现新的收入来源,以帮助开发者成长并制作更好的游戏。

区域收入计划的变化

有几种类型的用户。大多数情况下,游戏行业市场因地区、年龄组和性别而异。如果开发者计划增加收入,那么他们必须在收入计划中考虑这些因素。

然而,并不总是可能同时或在一个单一计划中使用所有这些因素。目前,行业中的开发者大多根据地区的基础变化收入计划。市场已经确定,用户行为在很大程度上取决于地区。

例如,亚洲用户的行为可能与非洲或美洲用户不同,他们的消费能力也不同。因此,开发者应该根据用户的消费能力和消费行为来规划游戏收入。在一些地区,用户不愿意用真钱支付。在这种情况下,开发者必须采取不同的方法来产生收入。

用户群体变化

正如我们已经说过的,用户群体因地区而异。例如,赛车是世界各地平均玩得最多的游戏类型之一。然而,产生收入并不相同。在许多地区,人们更看重时间而不是金钱,而在其他地区可能相反。因此,如果赛车游戏内的购买元素可以帮助用户节省一些时间来进行游戏进度,这在所有地区可能都不起作用。有些人喜欢花更多时间来实现进度,而不愿意付费。开发者必须有一个计划,通过某种方式将游戏时间转化为收入。

用户行为变化

世界各地的典型用户行为数据显示出很大的变化。其中一个主要的变化是游戏类型。例如,板球在一些国家或地区是非常著名和热门的游戏类型,那些地方的人们对这项运动有专业、心理或感情上的联系。在美洲地区,这项运动并不受到太多欣赏。同样的行为,在亚洲人中棒球也不那么受欢迎。开发者应该始终分析最大可能用户的用户行为数据,以规划从游戏中获得最大收入。

用户获取技术

如果一个游戏没有用户,那就和废品一样。这并不意味着游戏质量或设计不好。在安卓游戏市场上,已经发布了超过 500 万款游戏。每天都有数百款游戏发布,这加剧了竞争。

在这个庞大的群体中,一个游戏可能会消失,不管它的质量如何,都是因为没有或者有很差的营销策略。一个游戏只有在拥有大量用户和良好的留存率时才能成功。

让我们通过以下主题来看一下用户获取技术:

  • 游戏推广渠道

  • 游戏博客和论坛讨论

  • 付费用户获取

  • 其他技术

游戏推广渠道

有几种方法可以在市场上推广游戏。有一些游戏推广渠道在各种媒介上做广告。特定的游戏类型频道推广相同类型的游戏。让我们看看其中的一些媒介:

  • YouTube 频道

  • 安卓论坛

  • 体育论坛

  • Facebook 推广

  • Twitter 和其他社交平台

YouTube 频道

有几个 YouTube 频道对安卓游戏进行评测和推广。许多用户关注特定频道以寻找更好的游戏。开发者可以通过这些频道来推广他们的游戏,让用户了解游戏。

从这些频道得到的良好的游戏评价可以为开发者带来大量用户。然而,这些频道可能会向开发者收费来评测他们的游戏。从这些频道可能会找到成千上万的用户。

安卓论坛

有数百个安卓论坛可供选择,有成千上万的活跃参与者在讨论游戏、应用、开发风格和标准等。这些论坛也是推广安卓游戏的好平台。然而,开发者应该具体到话题,游戏应该有被讨论的潜力。通过这些渠道可以获得几百名用户。

如果开发者使用了任何安卓特有的特性,并通过技术实现了一些新的东西,这些论坛是一个很好的途径,可以接触到对技术热衷并渴望新实现的用户。

体育论坛

有许多针对特定运动的论坛。这种方法主要适用于体育类游戏。开发者应该与论坛的其他成员讨论同一运动的游戏。例如,如果开发者制作了一款板球游戏,那么游戏应该通过板球论坛进行发布和推广。

这种方法有一个额外的优势。由于论坛是针对同一运动的,开发者可能会找到一些对该运动有专业知识的人,他们可以分享对游戏的宝贵意见,这可能会使游戏变得更好。

Facebook 推广

Facebook 目前是最大的社交平台,拥有数十亿用户。开发者通常使用这个平台来推广游戏。社交网络可以为游戏找到大量用户。

每款游戏都应该有一个由开发者妥善维护的页面。这个页面是用户和开发者之间的沟通媒介之一。这样的页面可以用来讨论游戏的特点和元素,以便新用户在开始玩游戏之前就对游戏有一个很好的了解。

Twitter 和其他社交平台

Twitter 和其他社交平台也对游戏推广和增加用户群体有用。及时发布关于游戏更新和特点的推文可以帮助增加用户数量。

社交平台不一定是数字或网络平台。它可以是任何东西,比如现实生活中的社交活动。许多开发者组织活动来展示他们的游戏,或者参加各种活动和比赛以获得认可。对于一款好游戏的良好认可可以帮助获得更多用户。

游戏博客和论坛讨论

游戏博客和为开发的游戏创建论坛可以帮助获取用户。然而,这种技术在游戏发展出一个体面的用户群体之后才会起作用,这样就会有大量的人参与讨论。

开发者可以为游戏创建一个游戏博客,用户可以在博客上分享他们对游戏的意见、批评或建议。这可以使游戏变得出名,这总是有助于获得用户。

付费用户获取

有许多营销机构为游戏找到用户。通常,这些机构会向开发者收取用户获取费用。如果开发者有能力花费真金白银来获取用户,那么这可能是最好的解决方案。

用户获取费用可能会因地区而异。开发者需要根据游戏类型和种类进行更多研究;获得的用户可以通过更多收入回报开发者。有时,错误的推广选择和错误获取的用户群可能会导致游戏失败。

其他技术

除了上述技术,还有几种方法可以获得用户。开发者可以提出自己的游戏推广想法。其中一些如下:

  • 许多时候,开发者会单独接触用户来推广游戏

  • 许多时候,游戏是通过朋友和家人口头宣传的

  • 许多时候,开发者会为游戏进行宣传活动

  • 开发者可以寻求一个好的发行商来帮助获取更多用户

  • 有时,名人被用来推广游戏

没有固定的游戏推广和获取用户的路径。保持所有选项开放并以获得最大可能的结果为目标是一种良好的习惯。

用户留存技术

建立良好的用户群体可能不足以产生足够的收入以获得游戏利润。因此出现了“用户留存”这个术语。这意味着反复玩游戏的用户数量。

用户可能下载游戏,玩几次后可能再也不回来。在另一种情况下,用户可能一次又一次地回到游戏中。用户留存是根据周或月的使用时间参数计算的。这意味着在给定时间段内有多少用户回到游戏中。

免费和免费游戏的收入大部分取决于用户留存,因为开发者通过几种收入生成计划将用户在游戏中花费的时间转化为收入。这就是为什么用户留存对于游戏业务至关重要。

除了核心元游戏之外,还有许多技巧可以提高用户留存率。让我们通过以下几点讨论主要技巧:

  • 每日奖励

  • 排行榜和成就

  • 积分墙集成

  • 推送通知

  • 频繁更新

每日奖励

每日奖励是游戏开发者中最受欢迎的用户留存技术。在这个系统中,用户每天回到游戏都会得到额外的东西。连续玩几天会奖励用户更多的物品和元素。

这个系统激励用户不断回到游戏中。因此,开发者可以获得更多的游戏时间来将其转化为收入。

排行榜和成就

排行榜和成就被广泛用于留存用户。两者都给用户竞争和进步的动力。为了在游戏中取得进步,用户必须回到游戏并在游戏中花费时间。

积分墙集成

开发者使用一些真实世界的优惠来留住用户。诸如优惠券和折扣之类的真实优惠总是吸引用户。这激励他们经常来到积分墙。积分墙不仅有助于留住用户,还有助于从各种优惠活动中产生更多收入。

推送通知

推送通知可以通知用户有关游戏的最新信息和更新。即使用户没有玩游戏,推送通知也可以帮助他们对游戏产生兴趣,这可能会让用户重新开始玩游戏。

有时,用户下载游戏后就忘了。在这种情况下,推送通知会提醒用户玩游戏。它还会告诉他们游戏内的进度。

频繁更新

开发者应该经常更新游戏,以跟上榜单并引起用户的注意。这间接吸引更多用户并帮助留住现有用户。

每个游戏商店都会通知现有用户有关游戏或应用的最新更新信息,以便用户可以更新他们的游戏并继续玩。

推广安卓游戏

成功的游戏意味着利润和名声。一个游戏可以通过良好的货币化设计和营销而获利。然而,要成名并不容易。一个游戏如果在各种地方被特色展示,就会变得出名。

游戏可以通过以下特质和标准来进行特色展示:

  • 创意和独特性

  • 用户评论和评分

  • 下载次数

  • 收入金额

创意和独特性

游戏行业中有许多游戏评论家和评论者。有许多文章、博客、杂志和网站都在关注、评论和谈论游戏。游戏的创意和独特性是这些媒体的最大因素。游戏的质量取决于游戏美术、游戏设计和可玩性。良好的艺术风格、良好的设计和可玩性可以使游戏被游戏商店、杂志或博客特色展示。通过这种方式,开发者可以使游戏出名,这可能会带来更多的用户和收入。

用户评论和评分

发布游戏后,游戏的命运取决于用户。如果游戏收到不好的评论和差评,新用户就无法被吸引到游戏中来。因此,游戏将无法被特色展示并获得动力。开发者应始终关注游戏评分和用户评论。开发者应积极回应用户遇到的问题,并感谢好评和好评。通常情况下,人们会注意到游戏在发布初期表现不佳。然而,对用户评论持积极态度,他们在后期表现良好。

下载次数

下载量是另一个游戏特色的标准。一旦下载量增加,游戏被商店自身推荐的可能性就更大。然而,评分也是这种推荐的因素。开发者应该集中精力尽快增加下载量,以便被推荐或进入榜单。

收入金额

安卓游戏可以根据收入生成量在最赚钱榜单上进行特色展示。进入最赚钱榜单意味着用户正在为游戏付费,或者游戏正在产生大量收入。进入最赚钱榜单总是增加游戏的可见度,间接地增加下载量和收入。然而,为了保持在榜单上,开发者应该根据用户需求不断更新游戏,并且重点关注用户留存。

发布安卓游戏

到目前为止,你已经学会了如何接触用户,以及如何从游戏中获得收入。然而,这些都是游戏在市场上发布后的步骤。开发者可以通过两种方式发布游戏:

  • 自我出版

  • 通过出版商发布

让我们快速看一下游戏开发的这一部分。

自我出版

当用户以自己的名义和标语发布时,这被称为自我出版。在这种情况下,开发者保留游戏 100%的股权,并拥有游戏的知识产权。自我出版游戏完全由开发者控制。开发者对游戏、游戏评分和评论、游戏收入和用户满意度负有全部责任。

通过出版商发布

通常,开发者在发布游戏后没有足够的带宽来承担全部的游戏责任。在这种情况下,开发者可以接触已建立的出版商来发布游戏。有时,出版商有他们自己的条款和条件,以及对游戏的要求才能发布游戏。开发者需要遵守这些条件,以减少责任并获得更好的营销。

总结

在本章中,你了解了整个游戏开发周期。开发者应该有能力为游戏做出正确的决定,以获得成功。众所周知,成功并不容易。本章展示了游戏成功所需关注的所有因素。

制作一个好游戏是不够的;制作一个独特的游戏也不够;制作好的图形也不够;拥有良好的设计也不够。游戏开发者必须在自己无法完成的情况下寻求其他第三方服务的帮助。使用社交平台也是必须的。

最后,选择正确的出版地点并针对游戏的正确受众可以带来成功。在安卓特定的游戏领域,已经有建立的出版社、商店和其他第三方服务提供商。开发者需要在游戏制作完成后仔细组合它们。否则,一个好游戏很可能会在数百万款安卓游戏中迷失。

posted @ 2024-05-22 15:12  绝不原创的飞龙  阅读(34)  评论(0编辑  收藏  举报