You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

167 lines
6.0 KiB
C#

using UnityEngine;
using System.Collections;
namespace RootMotion.Dynamics
{
/// <summary>
/// Dynamic pin, drag and mapping weight control based on collisions with objects.
/// </summary>
public class PuppetControllerLite : MonoBehaviour, ICollisionEventListener
{
[System.Serializable]
public class Group
{
public string name;
[Tooltip("The muscle groups to apply this pinWeightMlp and muscleWeightMlp to.")]
public int[] indices = new int[0];
[Range(0f, 1f)] public float pinWeightMlp = 0.5f;
[Range(0f, 1f)] public float muscleWeightMlp = 0.5f;
[Tooltip("When the puppet is touched, sets muscle Rigidbody drag to this value to reduce the rubber chicken effect.")]
public float drag = 2f;
[Tooltip("The time of blending in this script's effects when the puppet is touched.")]
public float blendInTime = 0.05f;
[Tooltip("The time of blending out this script's effects when the puppet is not touched any more.")]
public float blendOutTime = 1f;
public bool enabled { get; private set; }
public float mappingWeight { get; private set; }
private float dam = 0f;
private float damTime = -100f;
private float damV;
private float map, mapV;
public void TryDamage(Collision collision, CollisionEventBroadcaster broadcaster)
{
bool included = false;
for (int i = 0; i < indices.Length; i++)
{
if (broadcaster.muscle.index == indices[i])
{
included = true;
break;
}
}
if (!included) return;
damTime = Time.time;
enabled = true;
}
public void Update(PuppetMasterLite puppetMaster)
{
if (!enabled) return;
// Dynamically adjust drag, pin and mapping weights based on collisions so we can have perfect animation until there is a collision
//dragW = Mathf.MoveTowards(dragW, 1f, Time.deltaTime * 2f);
bool unpinned = puppetMaster.pinWeight <= 0f;
float damTarget = Time.time > damTime + 0.2f ? 0f : 1f;
if (unpinned) damTarget = 1f;
float mapTarget = damTarget;
if (unpinned) mapTarget = 1f;
float sDampTime = damTarget > dam ? blendInTime : blendOutTime;
dam = Mathf.SmoothDamp(dam, damTarget, ref damV, sDampTime);
if (damTarget < dam && dam < 0.001f) dam = 0f;
float mDampTime = mapTarget > map ? blendInTime : blendOutTime;
map = Mathf.SmoothDamp(map, mapTarget, ref mapV, mDampTime);
if (mapTarget < map && map < 0.001f) map = 0f;
if (unpinned) dam = Mathf.Min(dam, map);
float d = unpinned ? 0f : drag * map;
float angularD = unpinned ? 0.05f : drag * map;
mappingWeight = Mathf.Lerp(0f, 1f, map);
for (int i = 0; i < indices.Length; i++)
{
int index = indices[i];
puppetMaster.muscles[index].pinWeightMlp = Mathf.Lerp(1f, pinWeightMlp, dam);
puppetMaster.muscles[index].muscleWeightMlp = Mathf.Lerp(1f, muscleWeightMlp, dam);
puppetMaster.muscles[index].rigidbody.drag = d;
puppetMaster.muscles[index].rigidbody.angularDrag = angularD;
//puppetMaster.muscles[index].mappingWeightMlp = mappingWeight;
}
if (dam <= 0f && map < 0f) enabled = false;
}
}
public PuppetMasterLite puppetMaster;
public LayerMask collisionLayers;
[Tooltip("When the puppet is touched, sets pin weight and muscle weight values for these groups.")]
public Group[] groups = new Group[0];
void Start()
{
foreach (MuscleLite m in puppetMaster.muscles)
{
var b = m.joint.gameObject.AddComponent<CollisionEventBroadcaster>();
b.listener = this;
b.muscle = m;
}
}
private bool NeedToUpdate()
{
foreach (Group group in groups)
{
if (group.enabled) return true;
}
return false;
}
void FixedUpdate()
{
if (!NeedToUpdate()) return;
float maxMappingWeight = 0f;
foreach (Group group in groups)
{
group.Update(puppetMaster);
maxMappingWeight = Mathf.Max(maxMappingWeight, group.mappingWeight);
}
foreach (MuscleLite m in puppetMaster.muscles)
{
m.mappingWeightMlp = maxMappingWeight;
}
}
// Called by CollisionEventBroadcaster when it collides with something
public void OnCollisionEnterEvent(Collision collision, CollisionEventBroadcaster broadcaster)
{
ProcessCollisionEvent(collision, broadcaster);
}
public void OnCollisionStayEvent(Collision collision, CollisionEventBroadcaster broadcaster)
{
ProcessCollisionEvent(collision, broadcaster);
}
private void ProcessCollisionEvent(Collision collision, CollisionEventBroadcaster broadcaster)
{
if (collision.collider.transform.root == transform) return;
if (!LayerMaskExtensions.Contains(collisionLayers, collision.collider.gameObject.layer)) return;
foreach (Group group in groups)
{
group.TryDamage(collision, broadcaster);
}
}
public void OnCollisionExitEvent(Collision collision, CollisionEventBroadcaster broadcaster) {}
}
}