返回博客首页
← 所有文章

在 NativeScript 中开始使用增强现实

2018年1月11日 — 作者:TJ VanToll

如果您还没有在移动设备上体验过增强现实,让我用一个非常实用的应用程序来演示,它可以让我在院子里添加虚拟圣诞树。


这个应用程序是 IKEA Place,它是一个使用 Apple 的 ARKit API 构建的 iOS 应用程序。

NativeScript 允许您访问 iOS 和 Android API 以使用 JavaScript 构建移动应用程序,ARKit 也不例外。在本文中,我们将了解如何使用 NativeScript 构建您的第一个 AR iOS 应用程序。

注意:

  • 要在 iOS 上运行 ARKit,您必须拥有 iPhone 6S+、第五代 iPad 或 iPad Pro。您可以在 Apple 的文档中找到受支持设备的完整列表
  • 对 NativeScript 中 Android 的 ARCore 的支持正在进行中。我们优先考虑 iOS 支持,因为 Android 的 ARCore 仍处于“开发者预览”状态,而 iOS 11 中已发布了 ARKit 的稳定版本。

启动您的应用程序

NativeScript 的 AR 支持可通过 nativescript-ar 插件 获得。该插件将 ARKit API 抽象成一系列易于使用的 API,用于常见的 AR 任务。最终,这些相同的 API 也将支持 Android 的 ARCore,以便您可以从单个代码库构建跨平台的 AR 体验。

要开始使用该插件,让我们首先使用 NativeScript CLI 创建一个新的 NativeScript 应用程序。

tns create AR --template tns-template-blank-ng
cd AR

两点说明

现在您有了新的应用程序,您需要使用 tns plugin add 命令安装 nativescript-ar 插件。

tns plugin add nativescript-ar

然后,打开应用程序的 app/home/home.component.ts 文件并将以下代码粘贴到其中。

import { Component, OnInit } from "@angular/core";
import { registerElement } from "nativescript-angular/element-registry";
import { ARMaterial } from "nativescript-ar";
import { Color } from "tns-core-modules/color";

registerElement("AR", () => require("nativescript-ar").AR);

@Component({
  selector: "demo-ar",
  moduleId: module.id,
  template: `
<ActionBar title="NativeScript AR"></ActionBar>

<GridLayout class="page">
  <AR *ngIf="loaded"
    debugLevel="FEATURE_POINTS"
    detectPlanes="true"
    [planeMaterial]="planeMaterial">
  </AR>
</GridLayout>
`
})
export class HomeComponent implements OnInit {
  loaded = false;
  ngOnInit() {
    // Because this is the very first component in the app ensure everything has loaded before starting up.
    setTimeout(() => { this.loaded = true; }, 1000);
  }

  planeMaterial = <ARMaterial>{
    diffuse: new Color("white"),
    transparency: 0.2
  };
}

我们稍后将详细讨论此代码,因为有很多内容。但首先让我们运行该应用程序,以便您可以了解此示例在实际操作中的样子。

与大多数其他 iOS API 不同,您无法使用 iOS 模拟器测试 AR 应用程序。因此,下一步,通过 USB 将您的 iOS 测试设备插入,并运行以下命令将您的应用程序部署到您的设备。

tns run ios

构建和安装完成后,您应该首先看到一个关于相机权限的提示,因为 AR 需要相机访问权限才能工作。

permissions

授予权限后,将设备的摄像头对准平坦的表面。几秒钟后,您应该会看到一些点,然后出现一个透明的平面。下面的视频显示了此过程的样子。

提示:确保您正在测试的区域有充足的光线。如果您在黑暗区域,请尝试打开设备的手电筒以提供更多光线。


使用 ARKit 时,您首先需要检测一个平面,然后才能开始与其交互。为了更详细地讨论此处发生的情况,以及您可能希望从这里开始的地方,让我们回到代码并详细分解正在发生的事情。

分解代码

在 Angular 组件中,template 属性是您定义组件应呈现的 UI 元素的地方。在此 AR 示例中,您的 template 如下所示。

<ActionBar title="NativeScript AR"></ActionBar>

<GridLayout class="page">
  <AR *ngIf="loaded"
    debugLevel="FEATURE_POINTS"
    detectPlanes="true"
    [planeMaterial]="planeMaterial">
  </AR>
</GridLayout>

<ActionBar><GridLayout> 是标准的 NativeScript UI 组件。<ActionBar> 在屏幕顶部放置一个导航栏,而 <GridLayout> 允许您在一系列行和列中排列子 UI 元素。如果您使用 <GridLayout> 而不提供 rowscolumns 属性(此处的情况),则子元素将占据整个空间。

<AR> UI 元素是真正发生神奇的地方。当您在屏幕上放置 <AR> 组件时,您将指定屏幕的该部分以显示来自设备摄像头的输入。在这种情况下,<AR> 组件是页面级 <GridLayout> 的唯一子元素,因此占据了屏幕的整个空间。但您也可以选择配置 <AR> 组件以仅占据屏幕的一部分。

<AR> 组件采用许多属性来配置 AR 体验的工作方式。您可以 参考 AR 插件的文档以获取属性的完整列表,但此处列出了此示例使用的属性。

  • debugLevel — 此演示将 debugLevel 设置为 "FEATURE_POINTS",这将启用黄色点,使查找平面更容易。某些应用程序会在生产环境中保留这些点,例如 IKEA Place,而其他应用程序则将 debugLevel 设置为 "NONE" 以隐藏它们。但是,如果您这样做,则需要向用户提供一些明确的指示,表明应用程序需要找到一个平坦的表面。例如,支持 AR 的应用程序 Pokémon GO 不使用点,但会指示用户寻找“平坦、开阔的区域”。
pokemon-go
  • detectPlanes — 此演示将 detectPlanes 设置为 true,以便插件自动尝试查找平面。如果您希望在尝试检测表面之前向用户提供一些说明,则可能需要将此属性设置为 false

  • planeMaterial — 此属性控制插件检测到平面后平面的显示方式。只有当您希望向用户显示平面的视觉表示时,才需要提供此属性。在此演示中,您使用 Angular 的数据绑定机制将此属性设置为以下值。

{
  diffuse: new Color("white"),
  transparency: 0.2
};

您可以尝试不同的颜色和透明度级别,以找到适合您的显示效果。

使用插件 API

现在您已安装了插件并启动了一个基本应用程序,让我们开始做一些更令人兴奋的事情。

再次打开您的 app/home/home.component.ts 并将其内容替换为以下代码,该代码在用户点击平面时添加了一些调用插件 addBox() 方法的代码。

import { Component, OnInit } from "@angular/core";
import { registerElement } from "nativescript-angular/element-registry";
import { AR, ARMaterial, ARPlaneTappedEventData } from "nativescript-ar";
import { Color } from "tns-core-modules/color";

registerElement("AR", () => require("nativescript-ar").AR);

@Component({
  selector: "demo-ar",
  moduleId: module.id,
  template: `
<ActionBar title="NativeScript AR"></ActionBar>

<GridLayout class="page">
  <AR *ngIf="loaded"
    debugLevel="FEATURE_POINTS"
    detectPlanes="true"
    [planeMaterial]="planeMaterial"
    (planeTapped)="onPlaneTapped($event)">
  </AR>
</GridLayout>
`
})
export class HomeComponent {
  loaded = false;
  ngOnInit() {
    setTimeout(() => { this.loaded = true; }, 1000);
  }

  planeMaterial = <ARMaterial>{
    diffuse: new Color("white"),
    transparency: 0.2
  };

  onPlaneTapped(args: ARPlaneTappedEventData): void {
    const ar: AR = args.object;
    ar.addBox({
      position: {
        x: args.position.x,
        y: args.position.y,
        z: args.position.z
      },
      dimensions: {
        x: 0.1,
        y: 0.1,
        z: 0.1
      },
      mass: 20,
      materials: [new Color("blue")]
    });
  }
}

新的代码片段是 <AR> 组件现在具有的 (planeTapped)="planeTapped($event)" 属性,该属性订阅了插件的 planeTapped 事件并调用组件的新 onPlaneTapped() 方法。在该方法中,您对插件的 addBox() 方法执行以下调用。

ar.addBox({
  position: {
    x: args.position.x,
    y: args.position.y,
    z: args.position.z
  },
  dimensions: {
    x: 0.1,
    y: 0.1,
    z: 0.1
  },
  mass: 20,
  materials: [new Color("blue")]
});

此代码的作用是获取用户点击的坐标(args.position.xargs.position.yargs.position.x),并在那里放置一个具有特定质量和大小的蓝色方块。

这就是它在实际操作中的样子。


addBox() 方法是 nativescript-ar 插件提供的众多方法之一。查看 插件的 API 文档,以获取有关可用方法及其功能的更多信息。在结束之前,让我们再看看一种方法。

添加 3D 模型

向现实中添加方块、文本和球体很有趣,但通常 AR 应用程序需要使用自定义 3D 模型。为此,nativescript-ar 插件提供了一个自定义的 addModel() 方法。

警告:查找 3D 模型并使其与 ARKit 一起工作可能会很痛苦。 插件的文档中提供了一些关于如何查找模型并使其工作的建议,但可能需要一些反复试验才能使事情正常运行。好消息是您正在使用 ARKit,因此您找到的任何关于为 ARKit 创建模型的现有指南都适用于 NativeScript。

对于此演示,首先下载 来自主要 nativescript-ar 演示的三个 .bae 文件,然后将其放置在应用程序的 app/App_Resources 文件夹中,如下所示。

app/App_Resources/
├── Android
│   └── ...
└── iOS
    ├── Models.scnassets
    │   ├── Ball.dae
    │   ├── Car.dae
    │   └── Tree.dae
    └── ...

接下来,重新打开您的 home.component.ts 文件,并将其内容替换为以下代码,该代码调用 nativescript-ar 插件的 addModel() 方法。

import { Component, OnInit } from "@angular/core";
import { registerElement } from "nativescript-angular/element-registry";
import { AR, ARNode, ARMaterial, ARPlaneTappedEventData } from "nativescript-ar";
import { Color } from "tns-core-modules/color";

registerElement("AR", () => require("nativescript-ar").AR);

@Component({
  selector: "demo-ar",
  moduleId: module.id,
  template: `
<ActionBar title="NativeScript AR"></ActionBar>

<GridLayout class="page">
  <AR *ngIf="loaded"
    debugLevel="FEATURE_POINTS"
    detectPlanes="true"
    [planeMaterial]="planeMaterial"
    (planeTapped)="onPlaneTapped($event)">
  </AR>
</GridLayout>
`
})
export class HomeComponent implements OnInit {
  loaded = false;
  ngOnInit() {
    setTimeout(() => { this.loaded = true; }, 1000);
  }

  planeMaterial = <ARMaterial>{
    diffuse: new Color("white"),
    transparency: 0.2
  };

  onPlaneTapped(args: ARPlaneTappedEventData): void {
    const ar: AR = args.object;
    ar.addModel({
      name: "Models.scnassets/Car.dae",
      position: {
        x: args.position.x,
        y: args.position.y,
        z: args.position.z
      },
      scale: 1,
      mass: 20
    });
  }
}

提示:将 Car.dae 替换为 Bal.daeTree.dae 以尝试其他模型。

当您的应用程序在设备上刷新时,您将能够将虚拟汽车放置到您的本地现实中。如果我使用该应用程序将新汽车放在我的车道上,则如下所示。


一些警告。

  • 首先,汽车模型很大。如果您尝试将汽车放在桌子上,结果不会很好。您可以尝试使用 scale 属性来玩迷你汽车。
  • 其次,使用 ARKit 检测大型平面可能会……很有趣。您很有可能在某个时候会做类似下面视频中的事情,公平地说,这有点好玩。


总结

您可以在 NativeScript 中使用 AR 做很多事情。在本文中,您学习了基础知识,例如如何检测平面、如何添加方块以及如何使用简单的 3D 模型。

您是否在 NativeScript 中使用 AR 做了一些很酷的事情?如果是,请在评论中告诉我们。并在未来关注此博客上关于 AR 的更多高级内容 😄