更新于 

使用yuka控制实体运动(二)

物体漫游 WanderBehavior

yuka.WanderBehavior 能够实现物体漫游运动

漫游属性如下:

  • radius 漫游半径
  • distance 漫游圆圈投射到实体前方的距离
  • jitter 每帧沿球面最大位移量

(没搞懂这三个量的关系)

添加漫游行为

1
2
const wanderBehavior = new YUKA.WanderBehavior(3,10,10)
vehicle.steering.add(wanderBehavior)

这里提到了Yuka.Vehicle类实例进行旋转的方式:

1
vehicle.rotation.fromEuler(0, 2 * Math.PI * Math.random(), 0)
问题

从外界导入的gltf模型尺寸不合适,
有时需要使用scale进行缩放,

1
vehicle.scale = new YUKA.Vector3(0.01, 0.01, 0.01)

缩放之后,WonderBehavior的参数似乎也发生了变化,
常见的问题是:在模型缩小之后,
物体的漫游半径过于庞大,导致模型朝起始方向无限漫游。
这个问题目前还没解决。

AnimationGroup

对于大量需要执行相同动画的模型,
比起对每个模型都创建一个控制动画的Mixer,
可以直接创建一个动画组AnimationGroup

1
2
3
4
5
6
7
8
9
10
const animationGroup = new THREE.AnimationObjectGroup()
const mixer = new THREE.AnimationMixer(model)
const action = mixer.clipAction(
TRHEE.AnimationClip.findByName(originModel.animations,'animate_name')
)
action.play()
for(let i = 0;i<100;i++){
const model = SkeletonUtils.clone(originModel)
animationGroup.add(model)
}

追赶行为 PursuitBehavior

Pursuit追赶是SeekBehavior的另一种形式,
区别在于Seek行为中,追随物体会严格跟随被追随物体的轨迹,
Persuit行为中追随物体会引入轨迹预测机制,提前进行拦截

追随动作的添加:

1
2
const pursuitBehavior = new YUKA.PursuitBehavior(evader, 0)
pursuit.steering.add(pursuitBehavior)

目标物体的轨迹:

1
2
3
const elapsed = time.getElapsed()
target.x = Math.cos(elapsed) * Math.sin(elapsed * 0.2) * 6
target.z = Math.sin(elapsed * 0.8) * 6

PursuitBehavior的第二个参数predictionFactor用于控制预测的程度。

predictionFactor=0时,就相当于SeekBehavior
predictionFactor=0时,就相当于SeekBehavior
predictionFactor=5
predictionFactor=5

物体跟随 OffsetPursuitBehavior

物体跟随追赶的区别在于:
物体跟随者会和被跟随者保持一定的距离,
或者说是相对被跟随者有一个相对坐标。

1
2
const offsetPursuitBehavior = new YUKA.OffsetPursuitBehavior(leader, offset)
follower.steering.add(offsetPursuitBehavior)

如果跟随者的速度跟不上跟随者,
就会出现相对坐标变形的情况,
解决这个问题的方式就是给跟随者一个很高的速度
能够允许它时刻跟随上leader,并保持相对距离。

变形情况
变形情况
跟随物体加速防止变形
跟随物体加速防止变形

物体干预 Alignment/Cohesion/Separation

之前在wanderBehavior漫游行为中,
实现了50个实体自由漫游的效果,
但是这50个实体相互之间没有任何作用,
yuka中提供了一些可以使实体之间相互干预的类:

  • Alignment 同化
    • 能够将实体临近圈neightborhoodRadius范围内的其他实体同化
  • Cohesion 聚合力
  • Separation 斥力

yuka的每种behavior都存在weight属性,
表示力的权重,权重越高的behavior对物体运动轨迹的影响越大。

要想创建一个集群式的尸体群落,
个体与个体之间需要既有聚合力又有斥力,个体漫游,整体同化,
各个力的权重如下:

1
2
3
4
5
6
7
8
9
10
11
const alignmentBehavior = new YUKA.AlignmentBehavior()
alignmentBehavior.weight = 2

const cohesionBehavior = new YUKA.CohesionBehavior()
cohesionBehavior.weight = 0.9

const separationBehavior = new YUKA.SeparationBehavior()
separationBehavior.weight = 0.2

const wanderBehavior = new YUKA.WanderBehavior()
wanderBehavior.weight = 0.8

以上的作用力的计算都需要使用到影响力圈这个概念,
相互作用力的效果会随着影响力圈的变大而变大。

1
2
vehicle.updateNeighborhood = true
vehicle.neighborhoodRadius = 10
鲨鱼过境
鲨鱼过境

中点追踪 InterposeBehavior

InterposBehavior需要指定2个被追随者,
追随目标会落在这两者的中点位置。

1
2
const interposeBehavior = new YUKA.InterposeBehavior(evader1, evader2)
vehicle.steering.add(interposeBehavior)
创建变化线段

变化线段的顶点会不断变化,
因此在创建Geometry的时候,
要用setFromPoints留几个空端点:

1
2
3
4
5
6
7
const lineGeo = new THREE.BufferGeometry().setFromPoints([
new THREE.Vector3(),
new THREE.Vector3()
])
const lineMat = new THREE.LineBasicMaterial({ color: 0xffaa00 })
const line = new THREE.Line(lineGeo, lineMat)
scene.add(line)

需要改变端点的时候,需要将线段的position缓冲区取出来,
使用setXYZ函数修改顶点坐标数据,
函数的第一个参数表示顶点的序号,其余参数分别表示xyz,
最后需要将needsUpdate赋值为true,表示更新

1
2
3
4
5
const linePosition = line.geometry.attributes.position
linePosition.setXYZ(0, evader1.position.x, 0, evader1.position.z)
linePosition.setXYZ(1, evader2.position.x, 0, evader2.position.z)
linePosition.needsUpdate = true
renderer.render(scene, camera);