注意:Google Maps Platform 游戏服务自 2021 年 10 月 18 日起已弃用。现有用户在 2022 年 12 月 31 日之前仍可继续使用。在此期间,我们会继续针对重大错误和服务中断提供支持和修复方案。请参阅游戏服务转换指南,获取相关资源,以帮助您规划项目的后续步骤。

Maps SDK for Unity 主要概念

使用集合让一切井井有条 根据您的偏好保存内容并对其进行分类。

以下部分介绍了对理解和使用 Maps SDK for Unity 至关重要的概念。

MapsService 类和组件

MapsService 类充当与 Maps SDK for Unity 进行交互的入口点。它封装了 ApiKey,并公开了 GameObjectManagerLoadMap 函数,以及来自 GameObject Creation 流水线的事件

如需在 Unity 项目中使用 Maps SDK for Unity,请将地图服务脚本组件添加到场景中的空 GameObject。地图服务会自动将生成的地图 GameObjects 添加为该锚点 GameObject 的子项。将 Maps Service(脚本)附加到基本 GameObject 后,您可以在 Unity Inspector 中访问其公共属性,如下所示。

地图服务(脚本)

地理位置功能作为 Unity GameObject

Maps SDK for Unity 会将 Google 地图数据库中的地理地图项(例如建筑物、道路和水)渲染为游戏中的 Unity GameObject。在运行时,这些对象会作为 MapsService 组件附加的 GameObject 的子对象创建,其名称采用 {MapFeatureType}({PlaceID}) 的形式。

SDK 游戏对象

创建 GameObject

在游戏过程中,SDK 从语义地图图块(通过 Semantic Tile API 中)提取 Google 地图数据库中的地理数据。它会即时解码这些数据,并将其转换为 Unity GameObject。此方法可让您尽早访问地图特征数据(元数据和几何图形数据),因此您可以在 GameObjects 到达流水线终点之前对其进行自定义。

Maps SDK for Unity 在接收矢量数据时首先要做的就是根据该对象构建一个 MapFeature 对象。

在流水线的中间阶段,MapFeature 对象具有特殊性。也就是说,它们变为特定类型(例如 Google.Maps.Feature.ModeledStructure)。这些专门的 MapFeature 对象包含 MapFeatureShape 几何图形详细信息(在 ModeledStructure 中为 ModeledVolume)。这些详细信息包括特定于 MapFeature 的数据(如顶点和三角形)以及用于访问通用字段(如边界框)的共享接口。

几何图形数据会转换为 Unity 网格,并通过 MeshFilter 添加到生成的 GameObject,然后使用 MeshRenderer 显示。

访问流水线

MapFeature 通过在流水线的各个阶段触发的事件提供给您。其中包括 WillCreate 事件(在创建 GameObject 之前立即触发,让您能够指定样式选项,甚至取消创建);以及 DidCreate 事件(在 GameObject 创建后立即触发,以便您对已完成的网格进行添加或更改)。

例如,您可以在 WillCreateExtrudedStructureEvent 触发后检查 ExtrudedStructures,并隐藏所有小于 20 米的建筑物(也可以完全跳过创建这些建筑物)。

事件类型

Google.Maps.Event 命名空间包含每种地理位置地图项的事件类。

每种事件类型都有一个 WillCreate 和一个 DidCreate 公共成员事件对象,您可以订阅这些对象,如以下代码示例所示。

dynamicMapsService.MapsService.Events.ExtrudedStructureEvents.DidCreate.AddListener(args => {

    // Apply nine-sliced walls and roof materials to this building.
    buildingTexturer.AssignNineSlicedMaterials(args.GameObject);

    // Add a border around the building base using the Building Border Builder class,
    // coloring it using the given border Material.
    Extruder.AddBuildingBorder(args.GameObject, args.MapFeature.Shape, BuildingAndRoadBorder);
});

WillCreate 个事件

WillCreate 事件会在创建 MapFeature 后立即触发,但会在通过它生成最终 GameObject 之前触发。WillCreate 事件可让您抑制或自定义通过 MapFeature 创建的 GameObject。WillCreate 事件参数采用以下格式:

using System.ComponentModel;
using Google.Maps.Decoded;
using UnityEngine;

namespace Google.Maps {
  public class WillCreateGameObjectEventArgs<T, U>
      : CancelEventArgs where T : IMapObject, U : IGameObjectStyle {

    public readonly T MapObject;
    public U Style;
    public GameObject Prefab;

    Public WillCreateGameObjectEventArgs(T mapObject, U defaultStyle, GameObject prefab) {
      MapObject = mapObject;
      Style = defaultStyle;
      Prefab = prefab;
    }
  }
}
  • Cancel(继承自 CancelEventArgs)设置为 true 会禁止创建 GameObject。
  • MapObject 是只读的。
  • 通过设置 Style,您可以自定义已创建的 GameObject 的外观。
  • 设置 Prefab 会将原本应生成的 GameObject 替换为预设件。

DidCreate 个事件

在生成 GameObject 之后(即向场景添加事件后)触发 DidCreate 事件。他们会在 GameObject 创建成功时通知您,让您可以执行进一步处理。DidCreate 事件参数的格式如下:

using System.ComponentModel;
using Google.Maps.Decoded;
using UnityEngine;

namespace Google.Maps {
  public class DidCreateGameObjectEventArgs<T, U>
      : EventArgs where T : IMapObject, U : IGameObjectStyle {

    public readonly T MapObject;
    public GameObject CreatedObject;

    Public DidCreateGameObjectEventArgs(T mapObject, GameObject createdObject) {
      MapObject = mapObject;
      CreatedObject = createdObject;
    }
  }
}
  • MapObject 是只读的,因此更改它不会导致对场景进行任何更改。
  • 更改 CreatedObject 改变添加到场景中的 GameObject。

建筑物

建筑物分为两种:凸出的建筑物和建模建筑。

挤压式建筑

凸出的建筑物由轮廓(即 2D 空间)和高度生成。SDK 以这种方式表示大多数建筑物,并通过以下三种方式生成这些建筑物:

  • 使用真实身高数据(如果有此信息)。这是默认行为。

  • 为所有建筑物提供固定高度,而不考虑其实际高度。

  • 为所有没有实际高度的建筑物提供备份高度(默认情况下,此值设置为 10 米)。

通过结合使用这三种方法,Maps SDK for Unity 可以创建反映现实世界的真实差异、/恒定高度或两者结合的城市景观。

根据模型估算的结构

建模结构是使用曲面细分三角形的标准 3D 建模方法生成的。这种方法通常用于地标建筑,如自由女神像。

模特自由女神像

正在应用材料

在 Unity 中,渲染进程使用着色器材质纹理为 GameObjects 添加真实感。着色器定义了纹理、颜色和亮度如何应用于显示的几何图形,并将特定纹理、颜色和其他设置用作材料进行存储。您可以使用材质来指定通过何种方式呈现 Surface,即引用它使用的纹理、平铺信息和颜色。

着色器是一种小型脚本,其中包含根据照明输入和 Material 配置计算每个像素颜色的逻辑。Maps SDK for Unity 随附用于建模结构的标准着色器,以及另一个用于基本地图功能的着色器,但它也支持高级 Material 应用。针对地图地图项 GameObjects 计算UV 映射的坐标时,可采用任何基本材料应用,且无需修改即可看起来合理。

为了获得更高级的材质效果,Maps SDK for Unity 通过额外的 UV 通道为每个顶点提供了额外的数据,并通过 GoogleMapsShaderLib 库为 cg 着色器提供了许多便捷函数。这样可以实现九宫格构建纹理之类的操作,即将纹理切分成建筑物的屋顶、地面、墙角和瓷砖墙。

如需了解详情,请参阅《Unity 用户手册》中的创建和使用材料

紫外线通道

每种 MapFeature 类型的 UV 通道都包含以下形式的数据:

ExtrudedStructure

Walls

ExtrudedStructure 中的每面墙都是构建为以下形式的四边形:

Walls

墙壁的 UV 坐标按每个四分位计算。顶点不会在四边形之间共享,这样可允许墙壁之间使用硬线法线(也就是说,墙角的角显示为硬角度,而不是软圆角边缘)。

频道 0:(x, y, width, height)
x 和 y 是墙的这个四边形(方形部分)左下角的坐标,而宽度和高度是墙的四边形的宽度和高度。这适用于组成墙的每一个四边形。
屋顶

屋顶纹理可以选择与轴对齐或与 ExtrudedStructure 的方向对齐。您可以通过 ExtrudedStructureStyle 对象设置此值。

频道 0:(x, y, width, height)
xy 是每个顶点(相对于屋顶的左下角,具体来说是指屋顶最下轴对齐的边界框的下角)的坐标。widthheight 用于定义屋顶边界框的大小。

Region

频道 0:(x, y, width, height)
xy 是各顶点相对于区域轴对齐边界框的左下角坐标。widthheight 用于定义边界框的大小。

Segment

频道 0:(x、y、宽度、长度)
xy 是每个顶点的坐标,计算方式与线段完全直立一样,以便纹理可以绕角弯折。widthlength 可定义线段的尺寸。

ModeledStructure

频道 0:
每个坐标都设置为 (0, 0, 0, 0),因为目前没有实现纹理坐标。

Google 地图 ShaderLib

Maps SDK for Unity 包含一个名为 GoogleMapsShaderLib 的着色器库,可帮助您构建与 MapFeature GameObject 配合使用的着色器。该库在 GoogleMapsShaderLib.cginc 文件中实现。通过在着色器脚本的 CGPROGRAM 标志部分中添加以下 #include 指令,您可以使用该库。

CGPROGRAM
// ...
#include "/Assets/GoogleMaps/Materials/GoogleMapsShaderLib.cginc"
// ...
ENDCG

着色器库捆绑在 GoogleMaps.unitypackage 中。导入该软件包后,您可以在项目文件夹 /Assets/GoogleMaps/Materials/ 中找到 GoogleMapsShaderLib.cginc

九片

GoogleMapsShaderLib 提供了一个便捷函数,您可以在片段着色器中使用该函数提供九个切片的纹理。九宫格是一种使用纹理覆盖表面的技术,其中纹理通过一系列边界划分为九个部分。边界之间的区域是平铺的,边界之外的区域保持不变,如下所示:

九片

例如,在将 9 个切片的纹理应用于建筑物的墙时,纹理的顶部将应用于墙的顶部(在屋顶正下方),纹理的底部应用到墙底部(连接到地面),纹理的边应用到墙的边缘,并且纹理的中间区域均匀地位于墙的中间。

在道路上(再举一个例子),九分路让您能够使用固定宽度的人行道,但车道数可变,具体取决于道路的宽度。

您可以通过在着色器中包含 GoogleMapsShaderLib.cginc,然后调用 nineSlice 函数来使用九切片。GoogleMaps.unitypackage 中提供了一些示例着色器和材质,以说明如何使用 nineSlice 函数打造大小可变的真实摩天大楼,而无需拉伸或撕裂。

Material 位置示例
/Assets/GoogleMaps/Examples/04_Advanced/MoreStyling/Materials/NineSlicing
着色器位置示例
/Assets/GoogleMaps/Examples/04_Advanced/MoreStyling/Materials/NineSlicing/BuildingWall.shader

您可以在任何 MapFeatureModeledStructures 除外,后者当前没有任何纹理坐标)上使用九分割。

坐标系

Maps SDK for Unity 坐标系使用网络墨卡托投影法在球面 WGS 84 纬度和经度 Unity 空间空间 (Vector3) 之间进行转换。

Vector3 值是相对于浮动出发地的,后者通常设为用户的出发位置。因此,您不应将 Vector3 值保存在会话之外(即,在服务器上或用户的设备上)。我们建议您使用纬度/经度对来指定物理位置。

浮点来源用于避免浮点稳定性问题。Unity 的 Vector3 类使用单精度浮点数,并且可表示的浮点数的密度随着其数量的增加而降低(意味着较大的浮点数变得不太准确)。只要用户离开远离出现问题的位置,就可以更新浮动来源。您可以将其设置为相对较小的值(例如 100 米或 200 米)或更大的值(大于 1 公里),具体取决于您更新内容的频率。

Unity Worldspace 会根据初始来源的纬度调整为 1:1(米)。在墨卡托投影法中,由于纬度不同,缩放比例会略有差异,因此,随着用户向北和向南移动,Unity Wordspace 的比例与 1:1 略有不同;但是,用户预计移动速度不够快(或者足够快),因此不会受到明显影响。

Maps SDK for Unity 包含用于在 Google.Maps.LatLng 和 Unity Worldspace (Vector3) 之间进行转换的转换函数,转换时考虑浮动来源和缩放比例。

加载错误

从网络加载地图数据时发生的错误可以通过 MapLoadErrorEvent 事件进行处理。如果您不添加事件处理脚本,Maps SDK for Unity 会自行处理大多数类型的错误。不过,由于存在错误,应用必须执行某项操作。这由 MapLoadErrorArgs.DetailedErrorCode 指定,如下所述。

UnsupportedClientVersion

此版本的 Maps SDK for Unity 已不再受支持,并且可能会与当前的 API 密钥结合使用。通常,您的应用应提示用户更新到较新版本。

此错误通常表示 Maps SDK for Unity 版本过旧。在极少数情况下,如果我们发现某个版本的 Maps SDK for Unity 或 API 密钥存在严重问题,我们可能会使用这种方法。我们将尽一切努力传达这一点,并确保当您的应用有可用的更新版本时,上述变更不会发生。

最佳做法是,在发生此错误时确保您的应用具有适当的升级路径,从而允许用户迁移到具有受支持 SDK 版本的较新版本应用。如需了解详情,请参阅客户端终止开关文档。

已知问题

基本地图项由后到后渲染,而不进行 Z 测试,因为此类型的所有地图项都在同一平面中渲染。您必须在应用于基本地图功能的任何替换材料上将 Z-testing 设置为 ZTest Always,以确保正确渲染这些功能。

Google 在 GoogleMaps.unitypackage 中提供了示例着色器来解决这些问题。此文件称为 "BaseMapTextured.shader,位于 /Assets/GoogleMaps/Materials/ 文件夹中。若要在材质上使用该材质,请从材质检查器的着色器下拉列表中选择 Google > 地图 > 着色器 > BaseMap Textured

设置 Feature.RegionFeature.AreaWater 对象的样式时,您可以使用 Material、自定义颜色或通过在样式对象内的 FillModeType 枚举中选择的自动生成的颜色来应用填充。Auto 颜色是根据 Region 的使用类型的值生成的。