Maps SDK for Unity 关键概念

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

MapsService 类和组件

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

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

地图服务(脚本)

以 Unity GameObject 形式提供的地理特征

Maps SDK for Unity 可将 Google 地图数据库中的地理特征(例如建筑物、道路和水)渲染为游戏中的 Unity GameObjects。在运行时,系统会创建为 MapsService 组件所附加到的 GameObject 的子级,其名称格式为 {MapFeatureType} ({PlaceID})

SDK 游戏对象

GameObject 创建

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

收到矢量数据时,Maps SDK for Unity 会做的第一件事就是根据矢量数据构建 MapFeature 对象。

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

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

访问流水线

MapFeature 通过在流水线的各个阶段触发的事件向您显示。其中包括 WillCreate 事件(这些事件会在创建 GameObject 之前触发,允许您指定样式选项,甚至取消创建操作);以及 DidCreate 事件(在 GameObject 创建之后立即触发),可让您向已完成的网格执行添加或更改。

例如,您可以在 ExtrudedStructuresWillCreateExtrudedStructureEvent 触发后对其进行检查,并隐藏 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 事件

DidCreate 事件会在生成 GameObject 后被添加到场景中后触发。这些注解会在 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 中,渲染过程使用着色器材质纹理为 GameObject 增添真实感。着色器定义如何将纹理、颜色和光照应用于显示的几何图形,并将特定纹理、颜色和其他设置存储为材质。您可以使用材质来定义 Surface 的渲染方式,具体方法是添加对 Surface 所用纹理的引用、平铺信息和颜色的引用。

着色器是一些小脚本,其中包含用于根据光照输入和 Material 配置计算每个像素颜色的逻辑。Maps SDK for Unity 附带一个适用于建模结构的标准着色器,以及另一个适用于基本地图功能的着色器,但它也支持高级 Material 应用。在为地图项 GameObject 计算 UV 映射坐标时,采用的方式可以应用任何基本材质,并且未经修改看起来合理。

对于更高级的材料效果,Maps SDK for Unity 通过额外的 UV 通道为每个顶点提供额外的数据,并通过 GoogleMapsShaderLib 库为 cg 着色器提供许多便捷函数。这样可以使用九切片等建筑物纹理,即,将纹理切割为建筑物的屋顶、地面、墙角和瓷砖墙壁。

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

紫外线通道

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

ExtrudedStructure

Walls

ExtrudedStructure 上的每面墙都由以下形式的四边形构成:

Walls

墙壁的 UV 坐标按四边形计算。四边形之间不共用顶点,以允许在墙之间使用硬法线(即让墙的角显示为硬角,而不是软圆角边缘)。

通道 0:(x, y, width, height)
xx 和 y 是相对于墙壁四边形(正方形)左下角的坐标,而 xx 是该墙壁四边形的宽度和高度。x这适用于构成墙壁的每个四边形。
屋顶

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

通道 0:(x, y, width, height)
xy 是每个顶点相对于屋顶左下角(具体而言,最小覆盖轴顶框边界框的角)的坐标。 widthheight 定义屋顶边界框的大小。

Region

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

Segment

通道 0:(x, y, width, length)
xy 是每个顶点的坐标,在计算时将线段视为完全直线,可以让纹理围绕角弯曲。widthlength 决定了线段的尺寸。

ModeledStructure

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

GoogleMapsShaderLib

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

9 切片

GoogleMapsShaderLib 包含一个便捷函数,您可以在 fragment 着色器中使用该函数提供纹理的九维切片。九个切片是一种用纹理覆盖表面的技术,在这种技术中,纹理通过一系列边界将纹理分成九个部分。边界之间的区域将平铺,而边界之外的区域将保持固定,如下所示:

9 切片

例如,将九切纹理应用于建筑物的墙时,纹理的顶部会应用于墙的顶部(就在屋顶下方),纹理的底部会应用于墙的底部(与地面相连),纹理的边会应用于墙的边缘,而纹理中间的区域则被平铺。

在道路(另一个示例)上,您可以使用九种切割方式设置固定宽度的人行道,但车道数量取决于道路的宽度。

您可以通过在着色器中添加 GoogleMapsShaderLib.cginc,然后调用 nineSlice 函数,使用九切片。GoogleMaps.unitypackage 中包含示例着色器和材质,演示了如何使用 nineSlice 函数在不拉伸或撕裂的情况下制作出真实大小可变的摩天大楼。

“材料”位置示例
/Assets/GoogleMaps/Examples/04_Advanced/MoreStyling/Materials/NineSlicing
着色器位置示例
/Assets/GoogleMaps/Examples/04_Advanced/MoreStyling/Materials/NineSlicing/BuildingWall.shader

您可以对任何 MapFeature 使用九维切片,但 ModeledStructures 除外,它目前没有任何纹理坐标。

坐标系

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

Vector3 值相对于浮动出发地,该出发地通常设置为用户的出发位置。因此,您不应在会话之外(即在您的服务器上或用户的设备上)保留 Vector3 值。我们建议您使用经纬度对来指定实际世界的位置。

使用浮点数来避免浮点稳定性问题。Unity 的 Vector3 类使用单精度浮点数,并且可表示的浮点数的密度会随着其幅度的增加而降低(这意味着,较大的浮点数的准确性会降低)。每当用户离原点足够远时,就会造成问题,您就可以更新浮动来源。您可以根据所需的更新频率,将其设置为相对较小的值(例如 100 或 200 米)或更大(大于 1 公里)。

根据初始出发地的纬度,Unity Worldspace 会缩放至 1:1(米)。在墨卡托投影法中,缩放比例因纬度而略有差异,因此,当用户向北和南移动时,Unity 文字空间比例与 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/ 文件夹中。若要在材质中使用该样式,请从 Material Inspector 的着色器下拉菜单中选择 Google > Maps > Shaders > BaseMap Textured

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