这是我参与8月更文挑战的第30天,活动详情查看: 8月更文挑战
Spark AR 是 Facebook 免费创作 AR 作品的平台,使用户能够为 Facebook 和 Instagram 创建交互式增强现实体验,超过 40 万名创作者,190个国家/地区,使用 Spark AR 来创作自己的AR作品
由于该软件无需任何编码知识即可使用,因此任何人现在都可以在 AR 世界中几乎没有经验地制作下一个疯狂式传播的 Instagram AR 特效,引领世界潮流。
专门的 AR 滤镜设计师单价甚至可达到 1000 美元到 3 万美元不等。
除了媒体资产,AR 库中也有一些脚本包,允许你在代码中使用额外的功能。其中一个脚本包是 Cannon.js ,这是一个用 JavaScript 编写的开源 3D 物理引擎。我们将在这个示例项目中使用库来模拟 3D 对象的重力。
有关 Cannon.js 库的更多信息,请参阅 API 文档
加载脚本包
从资产面板中打开 AR 库并选择脚本包(Script Packages)选项卡。
从列表中找到并选择 cannon 。
在 cannon 页面中,单击 Import Free 按钮将包导入到项目中。
脚本包将出现在资产(Assets)面板中,列在 Packages 下。
导入一个 3D 物体
接下来,将添加一个受重力影响的 3D 对象。
在这个例子中,我们将使用 AR 库中的 3D 对象选项卡中的sphere primitive资源,但如果你有自己的 3D 对象,你也可以使用它。
一旦 3D 对象被导入,无论是从 AR 库还是你的本地文件,将它从资产面板拖到场景面板中添加到场景中,以便它在视口和模拟器中可见:
脚本
通过从资产面板中选择 Add Asset 并选择 Script,向项目添加一个新脚本。脚本包可以同时使用 JavaScript 和 TypeScript 文件,但在本例中我们将使用前者。
在 Assets 面板中双击生成的脚本文件,在默认 IDE 中打开它。
下面的示例代码使用了我们在前面的步骤中导入的 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]
})();