155 lines
5.8 KiB
C#

using UnityEngine;
namespace Passer.CreatureControl {
public class Creature : MonoBehaviour {
/// <summary>
/// The (hopefully rigged) 3D model of the creature
/// </summary>
public Transform model;
/// <summary>The target bones rig</summary>
/// The target bones rig contain the target pose of the creature
/// The creature movements will try to move the creature such that the target pose is reached
/// as closely as possible
public TargetRig targetRig;
/// <summary>
/// The positional different between the target rig and model root
/// </summary>
public Vector3 targetToModelTranslation;
/// <summary>
/// The rotational difference between the target rig and the model root
/// </summary>
public Quaternion targetToModelRotation;
#region Init
/// <summary>
/// Ensure a target rig is available
/// </summary>
/// <param name="targetRigResourceName">The name of the target rig resource</param>
/// <returns>True when the target rig has been updated</returns>
/// The parameter is used to instantiate a new target rig when none has been found.
public bool CheckTargetRig(string targetRigResourceName) {
if (this.targetRig != null)
return false;
// See if there is a target rig, but we just haven't found it
this.targetRig = this.GetComponentInChildren<TargetRig>();
if (this.targetRig == null) {
// Nope, there is no target rig, so instantiate it using the given resource name
GameObject targetsRigPrefab = Resources.Load<GameObject>(targetRigResourceName);
GameObject targetRig = Instantiate(targetsRigPrefab);
targetRig.name = "Target Rig";
targetRig.transform.SetPositionAndRotation(this.transform.position, this.transform.rotation);
targetRig.transform.SetParent(this.transform);
this.targetRig = targetRig.GetComponent<TargetRig>();
}
return true;
}
/// <summary>
/// Ensure a target rig is available
/// </summary>
/// <returns>True when the target rig has been updated</returns>
/// This tries to instantiate the default target rig resource
public virtual bool CheckTargetRig() {
return CheckTargetRig("TargetRig");
}
/// <summary>
/// Ensure that the creature rig is available
/// </summary>
/// <returns>True when the creature rig has been updated</returns>
public bool CheckModel() {
if (this.model != null)
return false;
// We determine the model root as the parent of the renderers
SkinnedMeshRenderer[] skinnedMeshRenderers = this.GetComponentsInChildren<SkinnedMeshRenderer>();
foreach (SkinnedMeshRenderer skinnedMeshRenderer in skinnedMeshRenderers) {
Transform rendererParent = skinnedMeshRenderer.transform.parent;
if (this.model == null || this.model == rendererParent)
this.model = rendererParent;
else
// Oops! There are multiple renders with different parents....
Debug.LogWarning("Unclear model root");
// We still return a model root, but this may not be the correct one...
}
return this.model != null;
}
#endregion Init
#region Start
/// <summary>
/// Start the creature
/// </summary>
protected virtual void Start() {
this.CheckTargetRig();
this.CheckModel();
this.targetRig.MatchTo(this);
}
#endregion Start
#region Update
/// <summary>
/// Update the creature
/// </summary>
public virtual void Update() {
if (this.targetRig == null)
// Without a target rig, the creature cannot move
return;
UpdatePose();
// copy animator root motion to the creature
this.transform.SetPositionAndRotation(targetRig.transform.position, targetRig.transform.rotation);
// As target rig is probably a child of this.transform,
// We need to restore the position/rotation of the targetsRig.
targetRig.transform.SetPositionAndRotation(this.transform.position, this.transform.rotation);
}
/// <summary>
/// Update the pose of the creature using the target rig
/// </summary>
public void UpdatePose() {
if (this.targetRig == null)
return;
this.targetRig.Pose();
UpdateModel();
}
/// <summary>
/// Update the bones of the creature's rig from the target rig pose
/// </summary>
public virtual void UpdateModel() {
Vector3 newPosition = this.targetRig.transform.position + this.targetToModelTranslation;
Quaternion newOrientation = this.targetRig.transform.rotation * this.targetToModelRotation;
this.model.SetPositionAndRotation(newPosition, newOrientation);
}
#endregion Update
#region Scene view
/// <summary>
/// Update the pose of the creature when the application is not running
/// </summary>
void OnDrawGizmos() {
// This ensures that the model is always following the target rig
if (Application.isPlaying == false) {
this.UpdatePose();
}
}
#endregion Scene view
}
}