Spark AR —— 使用脚本包添加物理效果【脚本】

551 阅读4分钟

这是我参与8月更文挑战的第30天,活动详情查看: 8月更文挑战

Spark AR 是 Facebook 免费创作 AR 作品的平台,使用户能够为 Facebook 和 Instagram 创建交互式增强现实体验,超过 40 万名创作者,190个国家/地区,使用 Spark AR 来创作自己的AR作品

由于该软件无需任何编码知识即可使用,因此任何人现在都可以在 AR 世界中几乎没有经验地制作下一个疯狂式传播的 Instagram AR 特效,引领世界潮流。

专门的 AR 滤镜设计师单价甚至可达到 1000 美元到 3 万美元不等。

80475685_1198660056999371_6263798669766557696_n.gif

除了媒体资产,AR 库中也有一些脚本包,允许你在代码中使用额外的功能。其中一个脚本包是 Cannon.js ,这是一个用 JavaScript 编写的开源 3D 物理引擎。我们将在这个示例项目中使用库来模拟 3D 对象的重力。

有关 Cannon.js 库的更多信息,请参阅 API 文档

加载脚本包

从资产面板中打开 AR 库并选择脚本包(Script Packages)选项卡。

从列表中找到并选择 cannon

在 cannon 页面中,单击 Import Free 按钮将包导入到项目中。

80475685_1198660056999371_6263798669766557696_n.gif

脚本包将出现在资产(Assets)面板中,列在 Packages 下。

导入一个 3D 物体

接下来,将添加一个受重力影响的 3D 对象。

在这个例子中,我们将使用 AR 库中的 3D 对象选项卡中的sphere primitive资源,但如果你有自己的 3D 对象,你也可以使用它。

80475685_1198660056999371_6263798669766557696_n.gif

一旦 3D 对象被导入,无论是从 AR 库还是你的本地文件,将它从资产面板拖到场景面板中添加到场景中,以便它在视口和模拟器中可见:

image.png

脚本

通过从资产面板中选择 Add Asset 并选择 Script,向项目添加一个新脚本。脚本包可以同时使用 JavaScript 和 TypeScript 文件,但在本例中我们将使用前者。

在 Assets 面板中双击生成的脚本文件,在默认 IDE 中打开它。

image.png

下面的示例代码使用了我们在前面的步骤中导入的 Cannon.js 库来创建重力效果。

复制并粘贴下面的代码到你的脚本中,替换任何现有的代码,以查看视图和模拟器中的物理引擎的行为。

// Load in the required modules, including the Cannon.js script package
const Scene = require('Scene');
const Time = require('Time')
const CANNON = require('cannon');

(async function () {  // Enables async/await in JS [part 1]

    // Reference Sphere object from Scene
    const sphere = await Scene.root.findFirst('Sphere');

    // Create a cannon world
    const world = new CANNON.World();

    // Set the gravity parameters of the cannon world
    // A value of -9.8 in the y axis approximately simulates real world gravity
    world.gravity.set(0, -9.8, 0);

    // Define a set of properties for the sphere
    const radius = 1;
    const sphereProps = {
        mass: 1,
        position: new CANNON.Vec3(0, 10, 0),
        radius: radius,
        shape: new CANNON.Sphere(radius),
    }

    // Create a new body for the sphere with the previously defined set of properties
    const sphereBody = new CANNON.Body(sphereProps);

    // Add the sphere body to the cannon world
    world.addBody(sphereBody);

    // Define a set of properties for the ground
    const groundProps = {
        mass: 0,
        position: new CANNON.Vec3(0, -sphereProps.radius, 0),
        shape: new CANNON.Plane(),
    }

    // Create a new body for the ground with the previously defined set of properties
    const groundBody = new CANNON.Body(groundProps);

    // Rotate the ground so that it is flat and faces upwards
    const angle = -Math.PI / 2;
    const xAxis = new CANNON.Vec3(1, 0, 0);
    groundBody.quaternion.setFromAxisAngle(xAxis, angle);

    // Add the ground body to the cannon world
    world.addBody(groundBody);

    // Define parameters for use when configuring the time step for cannon
    // The time step advances the physics simulation forward in time
    const fixedTimeStep = 1.0 / 60.0;
    const maxSubSteps = 3;
    const timeInterval = 30;
    let lastTime;

    // Create a time interval loop for cannon
    Time.setInterval(function (time) {
        if (lastTime !== undefined) {
            let dt = (time - lastTime) / 1000;

            // Set the step parameters
            world.step(fixedTimeStep, dt, maxSubSteps)

            // Update the position of the sphere in our scene to the position of the cannon sphere body
            sphere.transform.x = sphereBody.position.x;
            sphere.transform.y = sphereBody.position.y;
            sphere.transform.z = sphereBody.position.z;
        }

        lastTime = time
    }, timeInterval);

})(); // Enables async/await in JS [part 2]

你可以改变脚本中的属性和值,以了解它们如何影响 cannon world 的物理现象。具体可以参考 Cannon.js 文档,它提供了深入的描述和示例。

有关上面示例中使用的 Time 模块(启用了基于时间的事件)的更多信息,请参阅以下代码示例及文档

//==============================================================================
// The following example demonstrates how to call a function repeatedly every
// 0.5 seconds as well as call a function once after 5 seconds has passed.
//
// Project setup:
// - Insert a plane
//==============================================================================

// Load the required modules
const Scene = require('Scene');
const Time = require('Time');
const Reactive = require('Reactive');

// Enable async/await in JS [part 1]
(async function() {
  // Locate the plane in the Scene
  const [plane] = await Promise.all([
    Scene.root.findFirst('plane0')
  ]);

  // Create a variable used in the timers
  const timeInMilliseconds = 500;

  //==============================================================================
  // Increase and decrease the scale of the plane every 0.5 seconds
  //==============================================================================

  // Create a boolean to determine if the plane's width is doubled or not
  var isPlaneDoubleWidth = false;

  // Store a reference to the plane's transform signal
  const planeTransform = plane.transform;

  // Store a reference to the plane's initial x-axis scale value
  const planeWidth = planeTransform.scaleX.pinLastValue();

  // Create a function that changes the width of the plane
  function changePlaneWidth() {

    // If the plane's width is not doubled...
    if (!isPlaneDoubleWidth) {

      // Multiply the x-axis scale value of the plane by 2, doubling it
      planeTransform.scaleX = Reactive.mul(planeWidth, 2);

    // Otherwise...
    } else {

      // Set the x-axis scale back to it's original value, halving it
      planeTransform.scaleX = planeWidth;

    }

    // Update the boolean
    isPlaneDoubleWidth = !isPlaneDoubleWidth;

  }

  // Create an interval timer that calls the changePlaneWidth function every 0.5
  // seconds
  const intervalTimer = Time.setInterval(changePlaneWidth, timeInMilliseconds);

  //==============================================================================
  // Stop the interval timer after 5 seconds using a timeout timer
  //==============================================================================

  // Create a function that stops the interval timer
  function stopIntervalTimer() {
    Time.clearInterval(intervalTimer);
  }

  // Create a timeout timer that calls the stopIntervalTimer function after 5
  // seconds have passed
  const timeoutTimer = Time.setTimeout(stopIntervalTimer, timeInMilliseconds * 10);
// Enable async/await in JS [part 2]
})();