我正在研究和尝试从一周后建立一个血腥解体系统,但我差不多已经放弃了。

我希望在动态载入预设体时,不仅仅是正常位置和方向,还有原部位的变形或动画姿势的系统。

在性能消耗较高的旧骨骼上转移重量时,我有了一个想法,在击中时我将捕捉到原始角色姿势的快照,在runtime时将位置和方向数据传递到每个个体骨骼。像一个臂位位的预设体将包含原始相同的骨骼,而不是集体传递数据。相反,我将数据传递到每个单独的骨骼,如前臂、拇指等,所以它看起来像是变形。

我编写了这个脚本,然而即使重新试了好几次,也没有成功,而且预设体只会在身体关节处重置成原始姿势。

using System;

using System.Collections.Generic;

using UnityEngine;

【DisallowMultipleComponent】

public class DismembermentManager : MonoBehaviour

{

【Serializable】

public class BodyPiecePrefab

{

public string pieceName;

public GameObject prefab;

}

【Serializable】

private 结构 BonePose

{

public Vector3 worldPosition;

public Quaternion worldRotation;

}

[Serialize Field] private Transform characterRoot;

[Serialize Field] private Animator characterAnimator;

[Serialize Field] private List bodyPieces = new List();

[Serialize Field] private float explosionForce = 12f;

[Serialize Field] private float explosionRadius = 4f;

[Serialize Field] private float upwardModifier = 0.3f;

private readonly Dictionary poseSnapshot = new Dictionary();

private readonly List sourceBones = new List();

private bool hasExploded;

private void Awake()

{

if (characterRoot == null)

characterRoot = transform;

if (characterAnimator == null)

characterAnimator = GetComponentInChildren();

缓存source骨骼();

}

[ContextMenu("重建源骨骼缓存") ]

public void CacheSourceBones()

{

sourceBones.Clear();

sourceBones.AddRange(GetComponentsInChildren(true));

}

public void Explode(Vector3 hitPoint, Vector3 hitNormal)

{

if (hasExploded) return;

hasExploded = true;

捕捉姿势快照();

if (characterAnimator != null)

characterAnimator.enabled = false;

foreach (var piece in bodyPieces)

{

if (piece == null || piece.prefab == null)

continue;

GameObject instance = Instantiate(piece.prefab, characterRoot.position, characterRoot.rotation);

应用快照到实例(instance.transform);

启用物理(instance, hitPoint, hitNormal);

}

gameObject.SetActive(false);

}

private void CapturePoseSnapshot()

{

poseSnapshot.Clear();

foreach (var bone in sourceBones)

{

if (bone == null) continue;

poseSnapshot[bone.name] = new BonePose

{

worldPosition = bone.position,

worldRotation = bone.rotation

};

}

}

private void ApplySnapshotToInstance(Transform root)

{

Transform[] all = root.GetComponentsInChildren(true);

Array.Sort(all, (a, b) => GetDepth(a).CompareTo(GetDepth(b)));

foreach (var t in all)

{

if (t == null) continue;

if (poseSnapshot.TryGetValue(t.name, out BonePose pose))

{

t.SetPositionAndRotation(pose.worldPosition, pose.worldRotation);

}

}

}

private int GetDepth(Transform t)

{

int depth = 0;

while (t.parent != null)

{

depth++;

t = t.parent;

}

return depth;

}

private void EnablePhysics(GameObject instance, Vector3 hitPoint, Vector3 hitNormal)

{

Animator anim = instance.GetComponentInChildren(true);

if (anim != null)

anim.enabled = false;

Rigidbody[] rigidbodies = instance.GetComponentsInChildren(true);

Collider[] colliders = instance.GetComponentsInChildren(true);

foreach (var col in colliders)

col.enabled = true;

foreach (var rb in rigidbodies)

{

rb.isKinematic = false;

rb.useGravity = true;

rb.detectCollisions = true;

rb.WakeUp();

Vector3 direction = (rb.worldCenterOfMass - hitPoint).normalized;

if (direction.sqrMagnitude < 0.0001f)

direction = hitNormal.normalized;

rb.AddExplosionForce(explosionForce, hitPoint, explosionRadius, upwardModifier, ForceMode.Impulse);

rb.AddForce(direction * explosionForce, ForceMode.Impulse);

}

}