上节完成了碰撞检测:

这节我们加上碰撞之后的鲜血喷射效果。
安装下粒子动画库:
npm install --save three.quarks
创建 blood.js
import * as THREE from "three";
import { ApplyCollision, ApplyForce, BatchedParticleRenderer,ConstantValue, GridEmitter, IntervalValue, ParticleSystem, PointEmitter, RandomColor, RenderMode } from "three.quarks";
const group = new THREE.Group();
const batchRenderer = new BatchedParticleRenderer();
group.add(batchRenderer);
const loader = new THREE.TextureLoader();
const texture = loader.load('./point.png');
const particles = new ParticleSystem({
duration: 10,
looping: true,
startLife: new ConstantValue(5, 9),
startSpeed: new IntervalValue(30, 50),
startSize: new IntervalValue(5, 10),
startColor: new RandomColor(
new THREE.Vector4(1, 0, 0, 1),
new THREE.Vector4(1, 0, 0, 0.1)
),
emissionOverTime: new IntervalValue(300, 500),
shape: new PointEmitter(),
material: new THREE.MeshBasicMaterial({
map: texture,
transparent: true,
side: THREE.DoubleSide
})
});
group.add(particles.emitter);
batchRenderer.addSystem(particles);
particles.emitter.position.y = 100;
export {
batchRenderer
}
export default group;
颜色是红色,透明度随机 0.1 到 1。
鲜血喷射用点状粒子发射器。
point.png 从这里下载:


在 main.js 引入下:
import blood, { batchRenderer } from './blood.js';
scene.add(blood);

const clock = new THREE.Clock();
const delta = clock.getDelta();
if(batchRenderer) {
batchRenderer.update(delta);
}

和鲜血的效果还是很像的。
我们给它加上重力以及反弹:

particles.addBehavior(
new ApplyForce(
new THREE.Vector3(0, -1, 0),
new ConstantValue(70)
)
)
particles.addBehavior(new ApplyCollision({
resolve(pos, normal) {
if(pos.y < -100) {
normal.set(0, 1, 0);
return true;
} else {
return false;
}
}
}, 0.1));
重力 70,血的反弹力度比较小 0.1 就好了。
看下效果:

现在留血的效果就比较真实了。
我们在碰撞的时候,在碰撞的位置流血:
首先给 emitter 隐藏,设置下 name:

// particles.emitter.position.y = 100;
particles.emitter.visible = false;
particles.emitter.name = 'bloodEmitter';
碰撞的时候用 intersect 计算相交的包围盒,然后用 getCenter 取中间位置。
把血放在碰撞中间位置开始流:

const box3 = manBox3.intersect(carBox3);
const emitter = blood.getObjectByName('bloodEmitter');
emitter.visible = true;
emitter.position.copy(box3.getCenter(new THREE.Vector3()));
试下效果:

再从侧面撞一下:

位置也是对的。
但是我们血液反弹是固定的 -100
从侧面可以看出来,位置不大对:

我们改一下:

function addCollisionBehavior(y) {
particles.addBehavior(new ApplyCollision({
resolve(pos, normal) {
if(pos.y < y) {
normal.set(0, 1, 0);
return true;
} else {
return false;
}
}
}, 0.1));
}
export {
batchRenderer,
addCollisionBehavior
}
导出一个函数,在碰撞的时候传入 y:

const pos = box3.getCenter(new THREE.Vector3())
emitter.position.copy(pos);
addCollisionBehavior(-pos.y);
再试下:


现在的流血效果就比较真实了。
案例代码上传了小册仓库
总结
这节我们实现了碰撞时的流血粒子效果。
用点状粒子发射器发射血液粒子,设置重力让粒子下落,并且在碰到地面的时候反弹。
我们用包围盒 Box3 的 intersect 方法拿到相交部分,然后 getCenter 拿到碰撞的中心位置,把粒子发射器移动到这个位置来喷射血液。
这样,就实现了碰撞时的流血效果。