191 lines
5.9 KiB
TypeScript
191 lines
5.9 KiB
TypeScript
import { _decorator, Collider, Component, Material, Node, PhysicsGroup, PhysicsSystem, RigidBody, SkinnedMeshRenderer, SphereCollider } from 'cc';
|
|
import { RagdollBone } from './RagdollBone';
|
|
import { RagdollConstraint } from './RagdollConstraint';
|
|
import { GameController } from '../scripts/GameController';
|
|
import { AudioMgr } from '../scripts/framework/audio/AudioMgr';
|
|
import { AssetController } from '../scripts/AssetController';
|
|
const { ccclass, property } = _decorator;
|
|
|
|
@ccclass('Ragdoll')
|
|
export class Ragdoll extends Component {
|
|
@property(Node)
|
|
rootNode: Node = null;
|
|
|
|
@property(Node)
|
|
centerNode: Node = null;
|
|
|
|
@property(SkinnedMeshRenderer)
|
|
renderer: SkinnedMeshRenderer = null;
|
|
|
|
|
|
@property(Material)
|
|
siwang: Material; // 新的材质
|
|
|
|
private realBones: Array<Node> = [];
|
|
private simuBones: Array<Node> = [];
|
|
|
|
private simuBoneJointArr: Array<CANNON.ConeTwistConstraint> = [];
|
|
|
|
private world: any;
|
|
|
|
private isEnabled: boolean = false;
|
|
|
|
|
|
start() {
|
|
this.world = PhysicsSystem.instance.physicsWorld.impl;
|
|
this.initRagdoll()
|
|
}
|
|
|
|
private initRagdoll() {
|
|
// 压入真实骨骼
|
|
this.getAllRealBones(this.rootNode)
|
|
|
|
// 创建模拟骨骼
|
|
this.createSimuBones();
|
|
|
|
// 创建模拟骨骼的关节
|
|
this.createSimuBoneJoints();
|
|
}
|
|
|
|
createSimuBones() {
|
|
for (let boneNode of this.realBones) {
|
|
let node = new Node
|
|
node.addComponent(SphereCollider).radius = 0.2 * node.worldScale.x
|
|
node.addComponent(RigidBody)
|
|
|
|
this.node.addChild(node)
|
|
this.simuBones.push(node)
|
|
|
|
this.syncPositionAndRotation(node, boneNode)
|
|
// 同步之后将位置赋给初始位置
|
|
let ragdollBone = boneNode.getComponent(RagdollBone)
|
|
ragdollBone.simuNode = node
|
|
ragdollBone.initPos = boneNode.getPosition();
|
|
ragdollBone.initRot = boneNode.getRotation();
|
|
}
|
|
|
|
this.scheduleOnce(() => {
|
|
for (let bone of this.simuBones) {
|
|
let body = bone.getComponent(RigidBody)
|
|
body.sleep()
|
|
}
|
|
}, 0)
|
|
}
|
|
|
|
createSimuBoneJoints() {
|
|
for (let boneNode of this.realBones) {
|
|
const joint = boneNode.getComponent(RagdollConstraint)
|
|
if (joint) {
|
|
const simuNodeA = boneNode.getComponent(RagdollBone).simuNode
|
|
const simuNodeB = joint.connectedBone.getComponent(RagdollBone).simuNode
|
|
const bodyA = simuNodeA.getComponent(RigidBody).body.impl
|
|
const bodyB = simuNodeB.getComponent(RigidBody).body.impl
|
|
|
|
let spineJoint = new CANNON.ConeTwistConstraint(bodyA, bodyB, {
|
|
pivotA: new CANNON.Vec3(joint.pivotA.x, joint.pivotA.y, joint.pivotA.z),
|
|
pivotB: new CANNON.Vec3(joint.pivotB.x, joint.pivotB.y, joint.pivotB.z),
|
|
axisA: new CANNON.Vec3(joint.axisA.x, joint.axisA.y, joint.axisA.z),
|
|
axisB: new CANNON.Vec3(joint.axisB.x, joint.axisB.y, joint.axisB.z),
|
|
angle: joint.angle,
|
|
twistAngle: joint.twistAngle
|
|
})
|
|
this.world.addConstraint(spineJoint)
|
|
this.simuBoneJointArr.push(spineJoint)
|
|
}
|
|
}
|
|
}
|
|
|
|
getAllRealBones(node: Node) {
|
|
let bones = node.children;
|
|
for (let boneNode of bones) {
|
|
const bone = boneNode.getComponent(RagdollBone)
|
|
if (bone) {
|
|
this.realBones.push(boneNode)
|
|
bone.ragdoll = this
|
|
}
|
|
this.getAllRealBones(boneNode)
|
|
}
|
|
}
|
|
|
|
lateUpdate(deltaTime: number) {
|
|
if (this.isEnabled) {
|
|
this.updateBones()
|
|
}
|
|
}
|
|
|
|
syncPositionAndRotation(nodeA: Node, nodeB: Node) {
|
|
nodeA.setWorldPosition(nodeB.worldPosition)
|
|
nodeA.setWorldRotation(nodeB.worldRotation)
|
|
}
|
|
|
|
// 将模拟骨骼的位置和转角同步到真实骨骼
|
|
private updateBones() {
|
|
for (let i = 0; i < this.realBones.length; i++) {
|
|
this.syncPositionAndRotation(this.realBones[i], this.simuBones[i])
|
|
}
|
|
}
|
|
|
|
getEnabled() {
|
|
return this.isEnabled;
|
|
}
|
|
|
|
setEnabled(isEnabled: boolean) {
|
|
if (isEnabled && this.isDie) return
|
|
isEnabled ? this.enableRagdoll() : this.disableRagdoll();
|
|
AudioMgr.inst.playOneShot(AssetController.instance.die)
|
|
}
|
|
|
|
isDie: boolean = false
|
|
enableRagdoll() {
|
|
this.isDie = true
|
|
GameController.getInstance().curLevel.onEnemyDie(this)
|
|
// this.renderer.setMaterialInstance(this.siwang, 0)
|
|
|
|
this.renderer.material = this.siwang
|
|
|
|
// 将模拟骨骼的位置和真实骨骼同步
|
|
for (let bone of this.realBones) {
|
|
let ragdollBone = bone.getComponent(RagdollBone)
|
|
this.syncPositionAndRotation(ragdollBone.simuNode, bone)
|
|
let collider = ragdollBone.getComponent(Collider)
|
|
if (collider)
|
|
collider.enabled = false
|
|
}
|
|
|
|
for (let bone of this.simuBones) {
|
|
let body = bone.getComponent(RigidBody)
|
|
body.wakeUp();
|
|
}
|
|
|
|
// 将关节添加至物理世界
|
|
for (let joint of this.simuBoneJointArr) {
|
|
this.world.addConstraint(joint);
|
|
}
|
|
|
|
this.isEnabled = true;
|
|
}
|
|
|
|
disableRagdoll() {
|
|
// 将真实骨骼移动至初始位置
|
|
for (let bone of this.realBones) {
|
|
let ragdollBone = bone.getComponent(RagdollBone);
|
|
bone.setPosition(ragdollBone.initPos);
|
|
bone.setRotation(ragdollBone.initRot);
|
|
}
|
|
|
|
for (let bone of this.simuBones) {
|
|
let body = bone.getComponent(RigidBody)
|
|
body.sleep();
|
|
}
|
|
|
|
// 从物理世界中移除关节
|
|
for (let joint of this.simuBoneJointArr) {
|
|
this.world.removeConstraint(joint);
|
|
}
|
|
|
|
this.isEnabled = false;
|
|
}
|
|
}
|
|
|
|
|