不同的硬件厂商为安卓用户带来了不同大小、不同体验的设备。因此,我们一直在努力帮助开发者将游戏呈现给尽可能多的Android设备,让开发过程变得更简单。更高效,更轻松。本文向您介绍了一系列新的Android游戏开发工具,以及调试、打包和分发游戏的技巧。更高效的游戏开发工具要想做好,必先利其器。我们对开发工具做了很多优化。游戏引擎主要是用C和C++编写的,而大多数AndroidAPI旨在从Kotlin等托管代码中调用。所以我们在AndroidStudio中实现了同时调试C/C++代码和托管代码的能力,这样就可以如下图在Kotlin和C/C++代码中分别设置断点,然后在两个编程中环境单独跟踪执行。您甚至可以在托管代码和本机代码之间跳转以观察执行情况。△通过AndroidStudio演示托管代码和C/C++的同时调试另一方面,AndroidStudio可以通过代码自动补全来加速你编写代码的速度,同时也支持你快速插入JNI函数原型在两种不同的环境中进行编程。此外,在构建过程中使用了Cmake和Gradle,这使您可以更好地利用可移植构建。对于那些在MicrosoftWindows操作系统上的VisualStudio中编写C/C++跨平台游戏的开发人员,我们在Android游戏开发工具包(AndroidGameDevelopmentKit)中提供了一个AndroidGameDevelopment扩展,将于2021年7月推出(AGDE,AndroidGame开发扩展)。它进一步简化了您的开发过程,并帮助您直接在VisualStudio环境中使用一系列支持C和C++的调试器和分析工具构建Android设备。并且AGDE可以很容易地与各种构建系统集成,也可以使用UnrealEngine轻松集成到您的工作流程中,这样您就不需要为桌面设备、游戏机和Android设备单独使用它。工具集和构建系统。配置AGDE的过程也很简单。安装扩展后,切换到您的VisualStudio项目并添加AndroidAPK模板。在工具栏可以访问各种常用的Android开发工具,如SDK和NDK管理器、虚拟设备管理器、设备文件管理器、Logcat和性能分析器等,如下图所示:△AndroidinVisualStudioToolbar配置完成后项目中的Android构建目标,你只需要像开发一个标准的桌面VisualStudio目标一样操作,构建、部署和调试相关的所有操作都是一样的。您可以轻松地在调试器中设置断点,切换到反汇编代码以查看寄存器和内存块中的值,并通过并行堆栈查看并发性。△使用VisualStudio调试Android构建目标另外,你不仅可以在专用的Logcat面板中搜索日志输出,按类型过滤,还可以通过AGDE快速访问AndroidStudio中原先提供的独立CPU和内存分析器,如图下图右侧:△SearchableLogcat面板(左);独立的CPU和内存分析器(右)我们为这些工具设计了新的界面,增强了多项功能并支持本机内存采样。AndroidStudio和AndroidGameDevelopment扩展的结合为您提供了一套丰富的工具来高效地开发游戏。AndroidGameDevelopmentKit俗话说,好马配好鞍。有这些好用的工具是不够的,我们还需要将这些工具集成到Android的托管代码API中。为此,我们在Android游戏开发工具包(AGDK)中提供了一个新的C/C++库供您使用。GameActivityGameActivity几乎是原生实现的Android标准活动(C/C++)。它适用于Jetpack库以及各种Android界面库。这使您可以完全使用C/C++编写游戏循环,同时利用构建在Jetpack之上的各种库。此外,GameActivity被渲染到SurfaceView中,因此您可以轻松地混合和匹配AndroidUI元素,例如WebView、MapView以及AdsSDK等各种服务所需的视图。这样只需要在托管代码中传递一个简单加载C/C++游戏模块的类就可以完成游戏循环逻辑的集成。相关代码如下:△要显示的视图publicclassMyGameActivityextendsGameActivity{static{//加载你的游戏库System.loadLibrary("game");}}△极简托管代码需要修改AndroidManifest.xml文件中的meta-data,用于告诉GameActivity需要哪个库开始执行游戏循环。activityandroid:name=".MyGameActivity"android:label="@string/app_name">△修改AndroidManifest.xmlGameActivity为您提供了几种与Android的生命周期事件相匹配的原生回调方法,这些事件回调可以很容易地集成到您的游戏循环中。△Android生命周期对应的回调方法下面是一段非常简单的代码,我们将展示一个基于native_app_glue库的案例。native_app_glue库提供了一种不寻常的执行模式。voidandroid_main(structandroid_app*app){//你的游戏引擎NativeEngine*engine=newNativeEngine(app);//你的游戏循环引擎->GameLoop();deleteengine;}△这里基于native_app_glue的例子,android_main()函数会在一个不同于主线程的新线程中被调用。您可以从与线程关联的ALooper中获取Android的生命周期事件,如下所示:while(1){intevents;结构android_poll_source*source;//如果没有动画发生,阻塞进程直到捕获到事件while((ALooper_pollAll(IsAnimating()?0:-1,Null,&events,(void**)&source))>=0){//处理事件if(source!=NULL){source->process(mApp,source);}}}△使用ALooper获取并处理Android生命周期事件除了可以捕捉生命周期事件,还可以使用该方法监听文件描述符(filedescriptor)。FramePacingAPIFramePacingAPI可以帮助开发者解决较短游戏帧带来的延迟,避免较长游戏帧带来的延迟。如果设备支持选择刷新率的功能,则可以为玩家提供更加灵活流畅的显示效果。实现GameActivity后,您可以选择使用OpenGL/ES或Vulkan将内容渲染到表面。无论您选择哪种API,AndroidFramePacing库都会帮助您妥善处理渲染过程。它将游戏的逻辑和渲染循环与Android的显示子系统和底层显示相关硬件同步,以实现更流畅的渲染。OboeAPI△OboeAPI一款制作精良的游戏离不开优秀的音效,这也是Oboe音频库提供的能力。您可以在Android4.1及更高版本上使用它的API。在API级别27的设备上,Oboe使用AAudio来尽可能协调设备上的软件和硬件,以实现最低的音频延迟。对于版本较低的设备,Oboe会使用OpenSLES来尽可能保证兼容性。虽然Oboe引入了许多新功能,例如重采样、格式转换和高性能通道计数转换,但它还包括针对一些已知音频问题的内置解决方法。游戏输入AndroidGameDevelopmentKit还提供了两个可以与GameActivity互操作的库,分别用于处理软键盘和处理游戏中的输入信号。游戏文本输入GameTextInput在底层做了很多复杂的工作。可以将Android系统的软键盘连接到你游戏中的文本编辑器,还包括显示和隐藏软键盘的操作。△游戏中的文本输入逻辑如果结合GameActivity使用该库,无论是否使用native_app_glue,系统都可以自动完成相关配置。请看下面的代码,GameTextInput库会将输入状态传递给您的游戏,这样您的文本编辑器就会正确反映IME的状态。/***获取最后接收到的文本输入状态*/voidGameActivity_getTextInputState(GameActivity*activity,GameTextInputGetStateCallbackcallback,void*context);△修改AndroidManifest.xml文件游戏手柄输入游戏中另一种常见的输入设备是游戏手柄(游戏手柄)。您可以使用游戏控制器库来充分利用物理游戏手柄。当控制器连接到设备或从设备断开连接时,此库会通知您的游戏,并提供有关按钮布局、方向轴和其他关键控制器元数据的信息。GameController库也被封装在底层,让你无需复杂的实现就可以无缝轻松地连接各种手柄,甚至可以接受鼠标作为输入设备。支持大屏幕游戏在智能电视上玩游戏如果您的游戏可以使用方向键控制并且在横向模式下运行良好,则它可以在许多电视设备上运行。为了支持在智能电视上运行,你需要对AndroidManifest.xml文件做一些修改://你需要在几个uses-feature标签中声明这些权限:android.hardware.touchscreenandroid.hardware.faketouchandroid.hardware.telephonyandroid.hardware.cameraandroid.hardware.nfcandroid.hardware.location.gpsandroid.hardware.microphoneandroid.hardware.sensor//如有需要请添加android.required="false"△修改AndroidManifest.xml文件需要在manifest中声明这些权限不是必须的,即required="false"。这是因为你在AndroidManifest中提到的很多权限和功能并不是必须的,而且这些功能在智能电视上往往是不支持的,比如触摸屏、摄像头、加速度传感器等。你可以声明游戏支持使用通过添加以下代码将遥控器作为控制器使用:必要时,可以声明android:required="true",表示电视在启动应用前需要检测遥控器是否可用。在ChromeOS上运行ChromeOS现在是第二大桌面操作系统,拥有大量游戏玩家。它可以开箱即用地运行Android游戏,并内置了GooglePlay商店。玩家可以在运行Chrome操作系统的设备上横向玩游戏。大多数设备都提供了触摸屏供玩家操作,而对于一些没有触摸屏的设备,通常会使用鼠标或触控板来模拟触摸屏操作。当然,直接支持键鼠输入的游戏往往能给玩家带来更好的游戏体验。您可以使用类似下面的代码来捕获游戏中的鼠标或触控板事件:relativepositionvalhorizo??ntalOffset:Float=motionEvent.xvalverticalOffset:Float=motionEvent.yreturntrue}△捕获鼠标或触摸板事件代码示例你可以使用上面的代码在API级别26或更高版本上捕获更多的鼠标或触控板光标精确控制。例如,在大多数情况下,您可能希望在较大屏幕上以适当的相对大小保持与用户交互的虚拟控件。这就需要你考虑不同的屏幕尺寸和屏幕显示密度,计算出虚拟控件的大小。按照下面的代码,您可以获取当前显示器的显示指标,然后获取X轴和Y轴的密度和每英寸像素数,然后计算虚拟控件的缩放比例。valdm=resources.displayMetrics//用于精确缩放valxdpi=dm.xdpivalydpi=dm.ydpi//使用缩放因子计算DPIvaldensityDpi=dm.density*160.0fvalscaledDensityDpi=dm.scaledDensity*160.0f△在托管代码中您还可以使用dm.density和dm.scaledDensity在屏幕的DisplayMetrics中获取近似比例因子。其中1.0对应的DPI值为160。ScaleDensity用于根据用户喜好缩放Android应用程序的字体,所以当你的游戏支持这个特性时,一定会带来更好的用户体验。有时,出于性能原因,您可能希望设置显示密度的上限。如果您愿意,可以使用surfaceHolder.setFixedSize自动且经济地缩放表面。这不仅节省了内存(RAM),还减少了需要着色的像素数量,进一步节省了电力和热量。具体参考如下代码:varwidth=mSurfaceView.widthvarheight=mSurfaceView.heightvaldm=resources.displayMetricsif(dm.density>maxDensity){valnewScaleFactor=maxDensity/dm.densityif(newScaleFactor!=scaleFactor){width=(newScaleFactor*width).toInt()height=(newScaleFactor*height).toInt()mSurfaceView.holder.setFixedSize(width,height)}}△使用surfaceHolder.setFixedSize缩放接口也可以使用C/C++代码来自动实现Scaling,比如下面的代码:int32_tret=ANativeWindow_setBuffersGeometry(window,width,height,0);△使用C/C++实现自动缩放由于触摸事件总是发生在屏幕坐标内,无论是托管代码还是C/C++实现,都需要调整触摸事件以匹配新界面。不同的游戏引擎有不同的后台缓冲区缩放方法。下图列出的Unity、UnrealEngine、Godot在这方面有明显的区别。△不同游戏引擎缩放后台缓冲区的方法比较Unity可以通过多种方式缩放后台缓冲区。您可以在AndroidPlayer设置中调整最大DPI值:首先选择FixedDPI,然后拖动选择合适的DPI阈值。虚幻引擎4支持移动内容缩放因子,它提供了超出设备本机分辨率的几个额外选项。Godot允许您以多种方式缩放渲染的内容,并且您可以使用基本窗口宽度来避免渲染时的高DPI。此外,Godot还支持脚本获取屏幕的DPI值。如果您需要您的游戏支持在更多架构上运行的ChromeOS设备,您还需要在构建脚本中添加更多ABI条目:externalNativeBuild{cmake{abiFilters'armeabi-v7a','arm64-v8a','x86','x86_64'}}△在Gradle构建脚本中添加x86/x86_64,因为许多运行ChromeOS的设备使用x86或x86_64架构。如果你使用某个游戏引擎开发Android游戏,那么你可以从图中查看目前已知的支持x86/x86_64的引擎版本:存储玩游戏时,只需选择这些附加架构并将它们上传到同一播放列表下。这种统一的分发方式可以最大程度地简化您的游戏分发过程。AndroidAppBundle是专门为Android平台设计的一种用于打包和发布应用程序的文件格式。它允许您在不增加文件大小的情况下将各种体系结构的二进制库文件包含在同一个包中。然后,GooglePlay会为分发下载的每个平台自动生成一个专用的APK文件。处理较大的素材使用PAD分发游戏资源△使用基于AndroidAppBundle的PAD格式很多游戏需要加载庞大的资源和素材文件,比如3D模型、贴图、音效、视频过场等。PlayAssetDelivery(PAD)是AndroidAppBundle的扩展格式。使用它,您可以将单个工件发布到GooglePlay商店,其中包含游戏的代码部分和资源部分。△使用PlayAssetDeliveryPAD这种格式进行了大幅改进和优化,为您提供更高效的游戏分发体验。使用PAD交付游戏资产时,保证代码和资产的版本一致,让用户一打开游戏就可以获得最新的二进制文件和资产,不再需要等待资产更新。而GooglePlay的自动更新功能还可以帮你自动处理增量更新,让用户可以直接下载现有游戏版本的更新部分,而不用重新下载整个游戏。使用PAD的另一个好处是内容下载的优化,这意味着用户可以在首次运行游戏时加载必要的资产,然后在需要新内容时继续按需加载。使用纹理压缩来区分设备您可以使用纹理压缩格式(texturecompressionformat)来划分这些内容,GooglePlay会帮助您选择最合适的纹理分发到不同的设备上,从而确保您的游戏在大多数设备上保持一致devices可以达到最高的渲染效率和最好的渲染效果。△使用纹理压缩来划分内容分发使用设备类来划分△使用设备类来区分资源文件。可以根据内存大小、设备型号、物理屏幕大小等硬件特性等信息进行划分,以较小的安装包覆盖较大的设备群。总结提高Android游戏开发效率,不仅是每一位开发者的期盼,也是我们多年来不懈的理想。通过本文,我们分享了一些有效开发Android游戏的工具和技巧:Android游戏开发工具包中的新Android游戏开发扩展、AndroidGPUInspector、GameActivity、软键盘、游戏手柄和高性能音频库以及Android性能调优工具;还向您展示了PlayAssetDelivery格式在分发游戏资产方面的强大功能。希望本篇内容能加深大家对Android开发的理解,帮助大家开发热门游戏!