
什么是 three.js Procedural Animals?
three.js Procedural Animals 是一个包含 24 种运行时生成动物的 three.js 库。无需下载模型、纹理、绑定骨骼或动画片段,只需调用 createAnimal(),该库就会通过代码构建出完整的动画动物。
每种动物都基于有符号距离场雕刻而成,并经过网格化、绑定骨骼、蒙皮处理,覆盖毛发、羽毛或鳞片,还配备基于逆向运动学的步态和动作动画。可用的身体类型包括有爪动物、有蹄动物、鸟类、水生动物、蛇类、蜘蛛和跳跃动物。
你可以在在线展示中探索所有物种、变体、动作和调试视图,也可以在 GitHub 仓库中查看源代码。
主要特性
- 24 种物种:从狼、猫、马和兔子,到鸟类、鱼类、蛇类和蜘蛛,选择丰富。
- 基于种子的个体:种子可以生成可重复的体型、比例、毛被、性别、年龄和变体选择,除非你手动覆盖这些设置。
- 运行时动画:动物会自动选择合适的步态,并提供坐下、睡觉、进食、攻击和跳跃等动作。
- 五种质量等级:可选择
hero、high、medium、low和crowd。 - 基于 Worker 的构建:默认在模块 Web Worker 中执行构建,也支持回退到调用线程。
- 离线烘焙:将个体预先构建为
.animal文件,加载时间约为 20 毫秒。 - 面向游戏的控制:控制动物转向、移动到目标位置、跟随物理刚体、追踪目标、播放动作并接收游戏事件。
- TypeScript 支持:该软件包使用 ES 模块,并包含 TypeScript 类型声明。
安装
该库唯一的对等依赖是 three.js r160 或更高版本。使用 npm 安装软件包和 three.js:
npm install procedural-animals three如果该软件包尚未发布到 npm,可以直接从 GitHub 安装:
npm install github:majidmanzarpour/threejs-procedural-animals three创建你的第一只动物
首先导入 three.js 和 createAnimal。由于构建网格、骨架、覆盖层及相关数据需要一定时间,该函数是异步的。
import * as THREE from 'three';
import { createAnimal } from 'procedural-animals';
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(innerWidth, innerHeight);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xa9c1e0);
const camera = new THREE.PerspectiveCamera(
40,
innerWidth / innerHeight,
0.1,
500
);
camera.position.set(6, 3, 8);
const sun = new THREE.DirectionalLight(0xfff3e4, 2.7);
sun.position.set(6, 10, 4);
sun.castShadow = true;
scene.add(
sun,
new THREE.HemisphereLight(0xdde6f2, 0x5f5a52, 1.15)
);
const floor = new THREE.Mesh(
new THREE.PlaneGeometry(200, 200).rotateX(-Math.PI / 2),
new THREE.MeshStandardMaterial({ color: 0x77736a })
);
floor.receiveShadow = true;
scene.add(floor);
async function main() {
const wolf = await createAnimal('wolf', {
seed: 3,
quality: 'high'
});
scene.add(wolf.object);
let last = performance.now();
renderer.setAnimationLoop((now) => {
const dt = (now - last) / 1000;
last = now;
wolf.move({
speed: wolf.gears.walk,
heading: now / 1000 * 0.3
});
wolf.update(dt);
camera.lookAt(wolf.position);
renderer.render(scene, camera);
});
}
main();这个示例包含几条重要规则:
- 在
async函数中创建动物,并等待其结果。 - 将
animal.object添加到场景中,但保持该对象的变换为单位变换。动物会自行管理移动。 - 使用
move()、moveTo()或follow()等方法控制动物。 - 每帧准确调用一次
update(dt),其中dt以秒为单位。 - 使用
animal.gears中的速度,因为可用的档位名称可能因物种而异。
避免在入口模块中使用顶层
await。根据项目文档,它可能导致 Vite 生产构建死锁。
移动与导航
直接控制
move() 方法接收以米/秒为单位的速度和以弧度为单位的朝向角。朝向为 0 时,动物面向正 Z 轴。
wolf.move({
speed: wolf.gears.walk,
heading: Math.PI / 2
});需要停止动物移动时,调用 stop():
wolf.stop();移动到目标位置
使用 moveTo() 让动物前往世界坐标中的某个点。到达目标位置后,它会触发 arrive 事件。
wolf.moveTo(
new THREE.Vector3(10, 0, 4),
{ speed: 3 }
);
wolf.on('arrive', () => {
console.log('The wolf arrived.');
});跟随物理刚体
如果另一个系统控制某个刚体,请在每帧将其当前位置和速度传递给 follow():
wolf.follow(body.position, body.velocity);追踪目标
使用 lookAt() 让动物将头部转向目标。传入 null 可停止追踪。
wolf.lookAt(player.position);
// Later:
wolf.lookAt(null);播放动作
每个物种都支持常见动作 jump、sit、lie、sleep、eat、drink、attack、hit、death 和 stand。物种还可以提供专属动作。例如,蛇可以盘绕和突袭,鸟可以起飞和降落,兔子可以跳跃和跺脚。
play() 方法返回一个 Promise,其结果可以是 done、interrupted、stopped 或 refused。
const result = await wolf.play('attack');
console.log(result);请检查 animal.actions,不要假设某个物种支持专属动作。你可以使用 stopAction() 停止指定动作:
wolf.stopAction('attack');响应动物事件
事件 API 让游戏系统能够响应动画和移动。可用事件包括 footstep、attackHit、actionStart、actionEnd、arrive、takeoff、land、death、vocalize 和 wingbeat。
const onFootstep = (event) => {
sfx.play('step', event.position, event.strength);
};
wolf.on('footstep', onFootstep);
wolf.on('attackHit', (event) => {
damage(event.position, event.direction);
});
// Remove a listener when it is no longer needed.
wolf.off('footstep', onFootstep);配置地形和水域
ground 选项是一个函数,用于返回 X 和 Z 坐标处的地形高度。其坐标必须位于 animal.object.parent 的坐标空间中,返回的高度必须与场景中渲染的地形一致。
const deer = await createAnimal('deer', {
ground: (x, z) => terrainHeightAt(x, z),
position: new THREE.Vector3(4, 0, 8),
heading: Math.PI
});游泳动物需要 water 函数以及一个位于水面下方的起始位置。该函数返回水面高度;没有水的地方返回 null。
const fish = await createAnimal('fish', {
water: (x, z) => 2,
position: new THREE.Vector3(0, 1, 0)
});
scene.add(fish.object);陆地动物也可以使用水域定义来饮水和涉水。鸟类可以接收一个 perches 数组,其中包含它们可以飞往的 THREE.Vector3 点。
选择物种、变体和个体
该库包含狼、狗、狐狸、猫、狮子、猎豹、熊、鼠、兔子、马、鹿、牛、猪、羊、山羊、野猪、青蛙、鸡、乌鸦、鹰、鱼、鲨鱼、蛇和蜘蛛。
使用 seed 创建可重复生成的个体。你也可以显式选择其变体、性别或年龄:
const cat = await createAnimal('cat', {
seed: 12,
quality: 'high',
variant: 'tuxedo',
sex: 'female',
age: 'adult'
});字符串形式的物种标识符会按需加载。如果应用只需要一个物种,可以直接导入该物种,以保持包内容精简:
import wolfSpecies from 'procedural-animals/species/wolf';
import { createAnimal } from 'procedural-animals';
const wolf = await createAnimal(wolfSpecies, {
seed: 3
});选择合适的质量等级
- hero:约 110,000 至 130,000 个顶点,适用于电影级特写镜头。
- high:约 65,000 至 80,000 个顶点,适用于玩家控制的动物。这是默认值。
- medium:约 25,000 至 32,000 个顶点,适用于中距离视图和手机设备。
- low:约 11,000 至 15,000 个顶点,适用于约 10 至 20 只动物的兽群。
- crowd:约 5,000 至 8,000 个顶点,适用于约 100 只动物的群体,并使用一次绘制调用。
你可以使用 setQuality() 更改现有网格的毛发细节。对于大型群体,将 crowd 等级与 setLod(2) 结合使用,可以减少动作细节:
const sheep = await createAnimal('sheep', {
seed: 5,
quality: 'crowd'
});
sheep.setLod(2);构建结果会按物种、种子、质量和变体进行缓存。因此,一个使用六个种子的 100 只动物兽群只需要六个不同的构建结果。文档记录的 CPU 成本约为每只动物每帧 0.1 毫秒。
配置 Web Worker
动物构建大约需要 0.4 至 6 秒。默认情况下,构建会在模块 Web Worker 中运行。如果 Worker 不可用,库会回退到调用线程。
Vite 配置
// vite.config.js
export default {
worker: { format: 'es' },
build: { target: 'es2022' },
optimizeDeps: {
exclude: ['procedural-animals']
}
};webpack 5 和 Parcel 2 不需要额外的 Worker 配置。
其他打包工具
对于其他打包工具,请手动打包 Worker:
npx esbuild node_modules/procedural-animals/src/worker.js --bundle --format=iife --minify --outfile=public/animal-worker.js然后注册一个用于创建 Worker 的工厂函数:
import { setWorkerFactory } from 'procedural-animals';
setWorkerFactory(
() => new Worker('/animal-worker.js')
);你可以为某个动物禁用 Worker 构建,但这样做会在构建期间阻塞调用线程:
const wolf = await createAnimal('wolf', {
worker: false
});预烘焙动物以加快加载
对于已知的个体,可以提前将其烘焙为 .animal 文件。这样可将运行时构建延迟替换为加载延迟,README 估计加载大约需要 20 毫秒。
npx procedural-animals-bake wolf --seed 3 --quality high -o public/wolf-3.animal将生成的文件获取为 ArrayBuffer,并通过 baked 选项传入:
const wolf = await createAnimal('wolf', {
baked: fetch('/wolf-3.animal')
.then((response) => response.arrayBuffer())
});烘焙出的个体应与命令准备的物种和配置相对应。
高级技巧
使用只读运动状态
读取 position、heading、speed、velocity 和 state,用于协调摄像机、游戏逻辑或界面。不要通过操作动物的根对象来产生移动。
将道具附加到命名点
attachments 集合提供了 mouth、head 和 back 等附加点。鸟类还提供足部附加点,而马提供包括 seat、马镫和嚼子在内的骑乘相关附加点。请将道具设为这些附加对象的子对象,而不是手动近似其动画位置。
使用调试和覆盖层控制
动物 API 包含 setDebug(mode, on),支持 albedo、weights、material 和 bare 模式。使用 setCoverings(on) 可切换毛发外壳。
wolf.setDebug('weights', true);
wolf.setCoverings(false);计算朝向目标点的方向
由于朝向角为零时面向正 Z 轴,计算指向目标的方向时请使用 Math.atan2(dx, dz):
const dx = target.x - wolf.position.x;
const dz = target.z - wolf.position.z;
const heading = Math.atan2(dx, dz);
wolf.move({
speed: wolf.gears.walk,
heading
});清理资源
当动物被永久移除时调用 dispose()。这会释放其 GPU 资源,并将其从场景中移除。
wolf.dispose();实用导出项和文档
主包还导出了 listSpecies、loadSpecies、setWorkerFactory、clearBuildCache 和 QUALITY:
import {
createAnimal,
listSpecies,
loadSpecies,
setWorkerFactory,
clearBuildCache,
QUALITY
} from 'procedural-animals';有关每个选项、事件负载、状态字段和配方,请参阅仓库中的完整参考和 TypeScript 声明。
结语
three.js Procedural Animals 提供了一种代码优先的方式,可将多样化、带骨骼绑定且支持动画的野生动物添加到 three.js 应用中。从 createAnimal() 开始,将返回的对象添加到场景,通过动物 API 控制它,并在每帧更新一次。工作线程、烘焙、基于种子的缓存以及多个质量级别,让同一套工作流既能支持精细的近景展示,也能支持大规模群体。
