initial upload

This commit is contained in:
tom.hempel
2025-09-21 22:42:26 +02:00
commit d03bcd4ba5
6231 changed files with 351582 additions and 0 deletions

View File

@ -0,0 +1,95 @@
using UnityEngine;
using System.Collections;
namespace RootMotion {
/// <summary>
/// The Cartesian axes.
/// </summary>
[System.Serializable]
public enum Axis {
X,
Y,
Z
}
/// <summary>
/// Contains tools for working with Axes that have no positive/negative directions.
/// </summary>
public class AxisTools {
/// <summary>
/// Converts an Axis to Vector3.
/// </summary>
public static Vector3 ToVector3(Axis axis) {
if (axis == Axis.X) return Vector3.right;
if (axis == Axis.Y) return Vector3.up;
return Vector3.forward;
}
/// <summary>
/// Converts a Vector3 to Axis.
/// </summary>
public static Axis ToAxis(Vector3 v) {
float absX = Mathf.Abs(v.x);
float absY = Mathf.Abs(v.y);
float absZ = Mathf.Abs(v.z);
Axis d = Axis.X;
if (absY > absX && absY > absZ) d = Axis.Y;
if (absZ > absX && absZ > absY) d = Axis.Z;
return d;
}
/// <summary>
/// Returns the Axis of the Transform towards a world space position.
/// </summary>
public static Axis GetAxisToPoint(Transform t, Vector3 worldPosition) {
Vector3 axis = GetAxisVectorToPoint(t, worldPosition);
if (axis == Vector3.right) return Axis.X;
if (axis == Vector3.up) return Axis.Y;
return Axis.Z;
}
/// <summary>
/// Returns the Axis of the Transform towards a world space direction.
/// </summary>
public static Axis GetAxisToDirection(Transform t, Vector3 direction) {
Vector3 axis = GetAxisVectorToDirection(t, direction);
if (axis == Vector3.right) return Axis.X;
if (axis == Vector3.up) return Axis.Y;
return Axis.Z;
}
/// <summary>
/// Returns the local axis of the Transform towards a world space position.
/// </summary>
public static Vector3 GetAxisVectorToPoint(Transform t, Vector3 worldPosition) {
return GetAxisVectorToDirection(t, worldPosition - t.position);
}
/// <summary>
/// Returns the local axis of the Transform that aligns the most with a direction.
/// </summary>
public static Vector3 GetAxisVectorToDirection(Transform t, Vector3 direction) {
return GetAxisVectorToDirection(t.rotation, direction);
}
/// <summary>
/// Returns the local axis of a rotation space that aligns the most with a direction.
/// </summary>
public static Vector3 GetAxisVectorToDirection(Quaternion r, Vector3 direction)
{
direction = direction.normalized;
Vector3 axis = Vector3.right;
float dotX = Mathf.Abs(Vector3.Dot(r * Vector3.right, direction));
float dotY = Mathf.Abs(Vector3.Dot(r * Vector3.up, direction));
if (dotY > dotX) axis = Vector3.up;
float dotZ = Mathf.Abs(Vector3.Dot(r * Vector3.forward, direction));
if (dotZ > dotX && dotZ > dotY) axis = Vector3.forward;
return axis;
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: cd8e6d88a74a14a3195290100d6d4a99
timeCreated: 1439820535
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,137 @@
using UnityEngine;
using System.Collections;
namespace RootMotion {
/// <summary>
/// Contains the information about which way the limbs should be bent.
/// </summary>
[System.Serializable]
public class BipedLimbOrientations {
[System.Serializable]
public class LimbOrientation {
public Vector3 upperBoneForwardAxis;
public Vector3 lowerBoneForwardAxis;
public Vector3 lastBoneLeftAxis;
public LimbOrientation(Vector3 upperBoneForwardAxis, Vector3 lowerBoneForwardAxis, Vector3 lastBoneLeftAxis) {
this.upperBoneForwardAxis = upperBoneForwardAxis;
this.lowerBoneForwardAxis = lowerBoneForwardAxis;
this.lastBoneLeftAxis = lastBoneLeftAxis;
}
}
public LimbOrientation leftArm, rightArm, leftLeg, rightLeg;
/// <summary>
/// Initializes a new instance of the <see cref="RootMotion.FinalIK.FullBodyBipedBendDirections"/> class.
/// </summary>
public BipedLimbOrientations (LimbOrientation leftArm, LimbOrientation rightArm, LimbOrientation leftLeg, LimbOrientation rightLeg) {
this.leftArm = leftArm;
this.rightArm = rightArm;
this.leftLeg = leftLeg;
this.rightLeg = rightLeg;
}
/// <summary>
/// Gets the local bend directions of the standard UMA skeleton.
/// </summary>
public static BipedLimbOrientations UMA {
get {
return new BipedLimbOrientations(
new LimbOrientation(Vector3.forward, Vector3.forward, Vector3.forward),
new LimbOrientation(Vector3.forward, Vector3.forward, Vector3.back),
new LimbOrientation(Vector3.forward, Vector3.forward, Vector3.down),
new LimbOrientation(Vector3.forward, Vector3.forward, Vector3.down)
);
}
}
/// <summary>
/// Gets the local bend directions of the standard 3ds Max Biped skeleton.
/// </summary>
public static BipedLimbOrientations MaxBiped {
get {
return new BipedLimbOrientations(
new LimbOrientation(Vector3.down, Vector3.down, Vector3.down),
new LimbOrientation(Vector3.down, Vector3.down, Vector3.up),
new LimbOrientation(Vector3.up, Vector3.up, Vector3.back),
new LimbOrientation(Vector3.up, Vector3.up, Vector3.back)
);
}
}
/*
/// <summary>
/// Contains the local axes of the limb bones that they should bend towards.
/// </summary>
[System.Serializable]
public class Limb {
public Vector3 upper = Vector3.forward, lower = Vector3.forward, last = Vector3.right;
public Limb (Vector3 common) {
this.upper = common;
this.lower = common;
this.last = common;
}
public Limb (Vector3 upper, Vector3 lower, Vector3 last) {
this.upper = upper;
this.lower = lower;
this.last = last;
}
}
public Limb leftArm, rightArm, leftLeg, rightLeg;
/// <summary>
/// Initializes a new instance of the <see cref="RootMotion.FinalIK.FullBodyBipedBendDirections"/> class.
/// </summary>
public FullBodyBipedBendDirections(Vector3 upper, Vector3 lower, Vector3 last) {
this.leftArm = new Limb(upper, lower, last);
this.rightArm = new Limb(upper, lower, last);
this.leftLeg = new Limb(upper, lower, last);
this.rightLeg = new Limb(upper, lower, last);
}
/// <summary>
/// Initializes a new instance of the <see cref="RootMotion.FinalIK.FullBodyBipedBendAxes"/> struct.
/// </summary>
public FullBodyBipedBendDirections(Limb leftArm, Limb rightArm, Limb leftLeg, Limb rightLeg) {
this.leftArm = leftArm;
this.rightArm = rightArm;
this.leftLeg = leftLeg;
this.rightLeg = rightLeg;
}
/// <summary>
/// Gets the local bend directions of the standard UMA skeleton.
/// </summary>
public static FullBodyBipedBendDirections UMA {
get {
return new FullBodyBipedBendDirections(
new Limb(Vector3.back, Vector3.back, Vector3.back),
new Limb(Vector3.back, Vector3.back, Vector3.forward),
new Limb(Vector3.forward, Vector3.forward, Vector3.down),
new Limb(Vector3.forward, Vector3.forward, Vector3.down)
);
}
}
/// <summary>
/// Gets the local bend directions of the standard 3ds Max Biped skeleton.
/// </summary>
public static FullBodyBipedBendDirections MaxBiped {
get {
return new FullBodyBipedBendDirections(
new Limb(Vector3.up, Vector3.up, Vector3.up),
new Limb(Vector3.up, Vector3.up, Vector3.down),
new Limb(Vector3.up, Vector3.up, Vector3.back),
new Limb(Vector3.up, Vector3.up, Vector3.back)
);
}
}
*/
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 1bdb26a92391749189aae4c5290f8395
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,240 @@
using UnityEngine;
using System.Collections;
using System;
namespace RootMotion {
/// <summary>
/// Class for identifying biped bones based on most common naming conventions.
/// </summary>
public static class BipedNaming {
/// <summary>
/// Type of the bone.
/// </summary>
[System.Serializable]
public enum BoneType {
Unassigned,
Spine,
Head,
Arm,
Leg,
Tail,
Eye
}
/// <summary>
/// Bone side: Left and Right for limbs and Center for spine, head and tail.
/// </summary>
[System.Serializable]
public enum BoneSide {
Center,
Left,
Right
}
// Bone identifications
public static string[]
typeLeft = {" L ", "_L_", "-L-", " l ", "_l_", "-l-", "Left", "left", "CATRigL"},
typeRight = {" R ", "_R_", "-R-", " r ", "_r_", "-r-", "Right", "right", "CATRigR"},
typeSpine = {"Spine", "spine", "Pelvis", "pelvis", "Root", "root", "Torso", "torso", "Body", "body", "Hips", "hips", "Neck", "neck", "Chest", "chest"},
typeHead = {"Head", "head"},
typeArm = {"Arm", "arm", "Hand", "hand", "Wrist", "Wrist", "Elbow", "elbow", "Palm", "palm"},
typeLeg = {"Leg", "leg", "Thigh", "thigh", "Calf", "calf", "Femur", "femur", "Knee", "knee", "Foot", "foot", "Ankle", "ankle", "Hip", "hip"},
typeTail = {"Tail", "tail"},
typeEye = {"Eye", "eye"},
typeExclude = {"Nub", "Dummy", "dummy", "Tip", "IK", "Mesh"},
typeExcludeSpine = {"Head", "head"},
typeExcludeHead = {"Top", "End" },
typeExcludeArm = {"Collar", "collar", "Clavicle", "clavicle", "Finger", "finger", "Index", "index", "Mid", "mid", "Pinky", "pinky", "Ring", "Thumb", "thumb", "Adjust", "adjust", "Twist", "twist"},
typeExcludeLeg = {"Toe", "toe", "Platform", "Adjust", "adjust", "Twist", "twist"},
typeExcludeTail = {},
typeExcludeEye = {"Lid", "lid", "Brow", "brow", "Lash", "lash"},
pelvis = {"Pelvis", "pelvis", "Hip", "hip"},
hand = {"Hand", "hand", "Wrist", "wrist", "Palm", "palm"},
foot = {"Foot", "foot", "Ankle", "ankle"};
#region Public methods
/// <summary>
/// Returns only the bones with the specified BoneType.
/// </summary>
public static Transform[] GetBonesOfType(BoneType boneType, Transform[] bones) {
Transform[] r = new Transform[0];
foreach (Transform bone in bones) {
if (bone != null && GetBoneType(bone.name) == boneType) {
Array.Resize(ref r, r.Length + 1);
r[r.Length - 1] = bone;
}
}
return r;
}
/// <summary>
/// Returns only the bones with the specified BoneSide.
/// </summary>
public static Transform[] GetBonesOfSide(BoneSide boneSide, Transform[] bones) {
Transform[] r = new Transform[0];
foreach (Transform bone in bones) {
if (bone != null && GetBoneSide(bone.name) == boneSide) {
Array.Resize(ref r, r.Length + 1);
r[r.Length - 1] = bone;
}
}
return r;
}
/// <summary>
/// Gets the bones of type and side.
/// </summary>
public static Transform[] GetBonesOfTypeAndSide(BoneType boneType, BoneSide boneSide, Transform[] bones) {
Transform[] bonesOfType = GetBonesOfType(boneType, bones);
return GetBonesOfSide(boneSide, bonesOfType);
}
/// <summary>
/// Gets the bone of type and side. If more than one is found, will return the first in the array.
/// </summary>
public static Transform GetFirstBoneOfTypeAndSide(BoneType boneType, BoneSide boneSide, Transform[] bones) {
Transform[] b = GetBonesOfTypeAndSide(boneType, boneSide, bones);
if (b.Length == 0) return null;
return b[0];
}
/// <summary>
/// Returns only the bones that match all the namings in params string[][] namings
/// </summary>
/// <returns>
/// The matching Transforms
/// </returns>
/// <param name='transforms'>
/// Transforms.
/// </param>
/// <param name='namings'>
/// Namings.
/// </param>
public static Transform GetNamingMatch(Transform[] transforms, params string[][] namings) {
foreach (Transform t in transforms) {
bool match = true;
foreach (string[] naming in namings) {
if (!matchesNaming(t.name, naming)) {
match = false;
break;
}
}
if (match) return t;
}
return null;
}
/// <summary>
/// Gets the type of the bone.
/// </summary>
public static BoneType GetBoneType(string boneName) {
if (isSpine(boneName)) return BoneType.Spine;
if (isHead(boneName)) return BoneType.Head;
if (isArm (boneName)) return BoneType.Arm;
if (isLeg(boneName)) return BoneType.Leg;
if (isTail(boneName)) return BoneType.Tail;
if (isEye(boneName)) return BoneType.Eye;
return BoneType.Unassigned;
}
/// <summary>
/// Gets the bone side.
/// </summary>
public static BoneSide GetBoneSide(string boneName) {
if (isLeft(boneName)) return BoneSide.Left;
if (isRight(boneName)) return BoneSide.Right;
return BoneSide.Center;
}
/// <summary>
/// Returns the bone of type and side with additional naming parameters.
/// </summary>
public static Transform GetBone(Transform[] transforms, BoneType boneType, BoneSide boneSide = BoneSide.Center, params string[][] namings) {
Transform[] bones = GetBonesOfTypeAndSide(boneType, boneSide, transforms);
return GetNamingMatch(bones, namings);
}
#endregion Public methods
private static bool isLeft(string boneName) {
return matchesNaming(boneName, typeLeft) || lastLetter(boneName) == "L" || firstLetter(boneName) == "L";
}
private static bool isRight(string boneName) {
return matchesNaming(boneName, typeRight) || lastLetter(boneName) == "R" || firstLetter(boneName) == "R";
}
private static bool isSpine(string boneName) {
return matchesNaming(boneName, typeSpine) && !excludesNaming(boneName, typeExcludeSpine);
}
private static bool isHead(string boneName) {
return matchesNaming(boneName, typeHead) && !excludesNaming(boneName, typeExcludeHead);
}
private static bool isArm(string boneName) {
return matchesNaming(boneName, typeArm) && !excludesNaming(boneName, typeExcludeArm);
}
private static bool isLeg(string boneName) {
return matchesNaming(boneName, typeLeg) && !excludesNaming(boneName, typeExcludeLeg);
}
private static bool isTail(string boneName) {
return matchesNaming(boneName, typeTail) && !excludesNaming(boneName, typeExcludeTail);
}
private static bool isEye(string boneName) {
return matchesNaming(boneName, typeEye) && !excludesNaming(boneName, typeExcludeEye);
}
private static bool isTypeExclude(string boneName) {
return matchesNaming(boneName, typeExclude);
}
private static bool matchesNaming(string boneName, string[] namingConvention) {
if (excludesNaming(boneName, typeExclude)) return false;
foreach(string n in namingConvention) {
if (boneName.Contains(n)) return true;
}
return false;
}
private static bool excludesNaming(string boneName, string[] namingConvention) {
foreach(string n in namingConvention) {
if (boneName.Contains(n)) return true;
}
return false;
}
private static bool matchesLastLetter(string boneName, string[] namingConvention) {
foreach(string n in namingConvention) {
if (LastLetterIs(boneName, n)) return true;
}
return false;
}
private static bool LastLetterIs(string boneName, string letter) {
string lastLetter = boneName.Substring(boneName.Length - 1, 1);
return lastLetter == letter;
}
private static string firstLetter(string boneName) {
if (boneName.Length > 0) return boneName.Substring(0, 1);
return "";
}
private static string lastLetter(string boneName) {
if (boneName.Length > 0) return boneName.Substring(boneName.Length - 1, 1);
return "";
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 9b405eec4454c42e3b38bafdae66701a
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,596 @@
using UnityEngine;
using System.Collections;
using System;
namespace RootMotion {
/// <summary>
/// Contains references to bones common to all biped characters.
/// </summary>
[System.Serializable]
public class BipedReferences {
#region Main Interface
/// <summary>
/// The root transform is the parent of all the biped's bones and should be located at ground level.
/// </summary>
public Transform root;
/// <summary>
/// The pelvis (hip) bone.
/// </summary>
public Transform pelvis;
/// <summary>
/// The first bone of the left leg.
/// </summary>
public Transform leftThigh;
/// <summary>
/// The second bone of the left leg.
/// </summary>
public Transform leftCalf;
/// <summary>
/// The third bone of the left leg.
/// </summary>
public Transform leftFoot;
/// <summary>
/// The first bone of the right leg.
/// </summary>
public Transform rightThigh;
/// <summary>
/// The second bone of the right leg.
/// </summary>
public Transform rightCalf;
/// <summary>
/// The third bone of the right leg.
/// </summary>
public Transform rightFoot;
/// <summary>
/// The first bone of the left arm.
/// </summary>
public Transform leftUpperArm;
/// <summary>
/// The second bone of the left arm.
/// </summary>
public Transform leftForearm;
/// <summary>
/// The third bone of the left arm.
/// </summary>
public Transform leftHand;
/// <summary>
/// The first bone of the right arm.
/// </summary>
public Transform rightUpperArm;
/// <summary>
/// The second bone of the right arm.
/// </summary>
public Transform rightForearm;
/// <summary>
/// The third bone of the right arm.
/// </summary>
public Transform rightHand;
/// <summary>
/// The head.
/// </summary>
public Transform head;
/// <summary>
/// The spine hierarchy. Should not contain any bone deeper in the hierarchy than the arms (neck or head).
/// </summary>
public Transform[] spine = new Transform[0];
/// <summary>
/// The eyes.
/// </summary>
public Transform[] eyes = new Transform[0];
/// <summary>
/// Check for null references.
/// </summary>
public virtual bool isFilled {
get {
if (root == null) return false;
if (pelvis == null) return false;
if (leftThigh == null || leftCalf == null || leftFoot == null) return false;
if (rightThigh == null || rightCalf == null || rightFoot == null) return false;
if (leftUpperArm == null || leftForearm == null || leftHand == null) return false;
if (rightUpperArm == null || rightForearm == null || rightHand == null) return false;
foreach (Transform s in spine) if (s == null) return false;
foreach (Transform eye in eyes) if (eye == null) return false;
return true;
}
}
/// <summary>
/// Gets a value indicating whether this <see cref="BipedReferences"/> is empty.
/// </summary>
public bool isEmpty {
get {
return IsEmpty(true);
}
}
/// <summary>
/// Gets a value indicating whether this <see cref="BipedReferences"/> is empty. If includeRoot is false, returns true(is empty) even if root Transform has been assigned.
/// </summary>
public virtual bool IsEmpty(bool includeRoot) {
if (includeRoot && root != null) return false;
if (pelvis != null || head != null) return false;
if (leftThigh != null || leftCalf != null || leftFoot != null) return false;
if (rightThigh != null || rightCalf != null || rightFoot != null) return false;
if (leftUpperArm != null || leftForearm != null || leftHand != null) return false;
if (rightUpperArm != null || rightForearm != null || rightHand != null) return false;
foreach (Transform s in spine) if (s != null) return false;
foreach (Transform eye in eyes) if (eye != null) return false;
return true;
}
/// <summary>
/// Returns true if the References contain the specified Transform
/// </summary>
public virtual bool Contains(Transform t, bool ignoreRoot = false) {
if (!ignoreRoot && root == t) return true;
if (pelvis == t) return true;
if (leftThigh == t) return true;
if (leftCalf == t) return true;
if (leftFoot == t) return true;
if (rightThigh == t) return true;
if (rightCalf == t) return true;
if (rightFoot == t) return true;
if (leftUpperArm == t) return true;
if (leftForearm == t) return true;
if (leftHand == t) return true;
if (rightUpperArm == t) return true;
if (rightForearm == t) return true;
if (rightHand == t) return true;
if (head == t) return true;
foreach (Transform s in spine) if (s == t) return true;
foreach (Transform e in eyes) if (e == t) return true;
return false;
}
/// <summary>
/// Params for automatic biped recognition. (Using a struct here because I might need to add more parameters in the future).
/// </summary>
public struct AutoDetectParams {
/// <summary>
/// Should the immediate parent of the legs be included in the spine?.
/// </summary>
public bool legsParentInSpine;
public bool includeEyes;
public AutoDetectParams(bool legsParentInSpine, bool includeEyes) {
this.legsParentInSpine = legsParentInSpine;
this.includeEyes = includeEyes;
}
public static AutoDetectParams Default {
get {
return new AutoDetectParams(true, true);
}
}
}
/// <summary>
/// Automatically detects biped bones. Returns true if a valid biped has been referenced.
/// </summary>
public static bool AutoDetectReferences(ref BipedReferences references, Transform root, AutoDetectParams autoDetectParams) {
if (references == null) references = new BipedReferences();
references.root = root;
// If that failed try the Animator
var animator = root.GetComponent<Animator>();
if (animator != null && animator.isHuman) {
AssignHumanoidReferences(ref references, animator, autoDetectParams);
return true; // Assume humanoids are always valid
}
// Try with naming and hierarchy first
DetectReferencesByNaming(ref references, root, autoDetectParams);
Warning.logged = false;
if (!references.isFilled) {
Warning.Log("BipedReferences contains one or more missing Transforms.", root, true);
return false;
}
string message = "";
if (SetupError(references, ref message)) {
Warning.Log(message, references.root, true);
return false;
}
if (SetupWarning(references, ref message)) {
Warning.Log(message, references.root, true);
}
return true;
}
/// <summary>
/// Detects the references based on naming and hierarchy.
/// </summary>
public static void DetectReferencesByNaming(ref BipedReferences references, Transform root, AutoDetectParams autoDetectParams) {
if (references == null) references = new BipedReferences();
Transform[] children = root.GetComponentsInChildren<Transform>();
// Find limbs
DetectLimb(BipedNaming.BoneType.Arm, BipedNaming.BoneSide.Left, ref references.leftUpperArm, ref references.leftForearm, ref references.leftHand, children);
DetectLimb(BipedNaming.BoneType.Arm, BipedNaming.BoneSide.Right, ref references.rightUpperArm, ref references.rightForearm, ref references.rightHand, children);
DetectLimb(BipedNaming.BoneType.Leg, BipedNaming.BoneSide.Left, ref references.leftThigh, ref references.leftCalf, ref references.leftFoot, children);
DetectLimb(BipedNaming.BoneType.Leg, BipedNaming.BoneSide.Right, ref references.rightThigh, ref references.rightCalf, ref references.rightFoot, children);
// Find head bone
references.head = BipedNaming.GetBone(children, BipedNaming.BoneType.Head);
// Find Pelvis
references.pelvis = BipedNaming.GetNamingMatch(children, BipedNaming.pelvis);
// If pelvis is not an ancestor of a leg, it is not a valid pelvis
if (references.pelvis == null || !Hierarchy.IsAncestor(references.leftThigh, references.pelvis)) {
if (references.leftThigh != null) references.pelvis = references.leftThigh.parent;
}
// Find spine and head bones
if (references.leftUpperArm != null && references.rightUpperArm != null && references.pelvis != null && references.leftThigh != null) {
Transform neck = Hierarchy.GetFirstCommonAncestor(references.leftUpperArm, references.rightUpperArm);
if (neck != null) {
Transform[] inverseSpine = new Transform[1] { neck };
Hierarchy.AddAncestors(inverseSpine[0], references.pelvis, ref inverseSpine);
references.spine = new Transform[0];
for (int i = inverseSpine.Length - 1; i > -1; i--) {
if (AddBoneToSpine(inverseSpine[i], ref references, autoDetectParams)) {
Array.Resize(ref references.spine, references.spine.Length + 1);
references.spine[references.spine.Length - 1] = inverseSpine[i];
}
}
// Head
if (references.head == null) {
for (int i = 0; i < neck.childCount; i++) {
Transform child = neck.GetChild(i);
if (!Hierarchy.ContainsChild(child, references.leftUpperArm) && !Hierarchy.ContainsChild(child, references.rightUpperArm)) {
references.head = child;
break;
}
}
}
}
}
// Find eye bones
Transform[] eyes = BipedNaming.GetBonesOfType(BipedNaming.BoneType.Eye, children);
references.eyes = new Transform[0];
if (autoDetectParams.includeEyes) {
for (int i = 0; i < eyes.Length; i++) {
if (AddBoneToEyes(eyes[i], ref references, autoDetectParams)) {
Array.Resize(ref references.eyes, references.eyes.Length + 1);
references.eyes[references.eyes.Length - 1] = eyes[i];
}
}
}
}
/// <summary>
/// Fills in BipedReferences using Animator.GetBoneTransform().
/// </summary>
public static void AssignHumanoidReferences(ref BipedReferences references, Animator animator, AutoDetectParams autoDetectParams) {
if (references == null) references = new BipedReferences();
if (animator == null || !animator.isHuman) return;
references.spine = new Transform[0];
references.eyes = new Transform[0];
references.head = animator.GetBoneTransform(HumanBodyBones.Head);
references.leftThigh = animator.GetBoneTransform(HumanBodyBones.LeftUpperLeg);
references.leftCalf = animator.GetBoneTransform(HumanBodyBones.LeftLowerLeg);
references.leftFoot = animator.GetBoneTransform(HumanBodyBones.LeftFoot);
references.rightThigh = animator.GetBoneTransform(HumanBodyBones.RightUpperLeg);
references.rightCalf = animator.GetBoneTransform(HumanBodyBones.RightLowerLeg);
references.rightFoot = animator.GetBoneTransform(HumanBodyBones.RightFoot);
references.leftUpperArm = animator.GetBoneTransform(HumanBodyBones.LeftUpperArm);
references.leftForearm = animator.GetBoneTransform(HumanBodyBones.LeftLowerArm);
references.leftHand = animator.GetBoneTransform(HumanBodyBones.LeftHand);
references.rightUpperArm = animator.GetBoneTransform(HumanBodyBones.RightUpperArm);
references.rightForearm = animator.GetBoneTransform(HumanBodyBones.RightLowerArm);
references.rightHand = animator.GetBoneTransform(HumanBodyBones.RightHand);
references.pelvis = animator.GetBoneTransform(HumanBodyBones.Hips);
AddBoneToHierarchy(ref references.spine, animator.GetBoneTransform(HumanBodyBones.Spine));
AddBoneToHierarchy(ref references.spine, animator.GetBoneTransform(HumanBodyBones.Chest));
// Make sure the neck bone is not above the arms
if (references.leftUpperArm != null) {
if (!IsNeckBone(animator.GetBoneTransform(HumanBodyBones.Neck), references.leftUpperArm)) AddBoneToHierarchy(ref references.spine, animator.GetBoneTransform(HumanBodyBones.Neck));
}
if (autoDetectParams.includeEyes) {
AddBoneToHierarchy(ref references.eyes, animator.GetBoneTransform(HumanBodyBones.LeftEye));
AddBoneToHierarchy(ref references.eyes, animator.GetBoneTransform(HumanBodyBones.RightEye));
}
}
/// <summary>
/// Checks the setup for definite problems.
/// </summary>
public static bool SetupError(BipedReferences references, ref string errorMessage) {
if (!references.isFilled) {
errorMessage = "BipedReferences contains one or more missing Transforms.";
return true;
}
if (LimbError(references.leftThigh, references.leftCalf, references.leftFoot, ref errorMessage)) return true;
if (LimbError(references.rightThigh, references.rightCalf, references.rightFoot, ref errorMessage)) return true;
if (LimbError(references.leftUpperArm, references.leftForearm, references.leftHand, ref errorMessage)) return true;
if (LimbError(references.rightUpperArm, references.rightForearm, references.rightHand, ref errorMessage)) return true;
if (SpineError(references, ref errorMessage)) return true;
if (EyesError(references, ref errorMessage)) return true;
return false;
}
/// <summary>
/// Checks the setup for possible problems.
/// </summary>
public static bool SetupWarning(BipedReferences references, ref string warningMessage) {
if (LimbWarning(references.leftThigh, references.leftCalf, references.leftFoot, ref warningMessage)) return true;
if (LimbWarning(references.rightThigh, references.rightCalf, references.rightFoot, ref warningMessage)) return true;
if (LimbWarning(references.leftUpperArm, references.leftForearm, references.leftHand, ref warningMessage)) return true;
if (LimbWarning(references.rightUpperArm, references.rightForearm, references.rightHand, ref warningMessage)) return true;
if (SpineWarning(references, ref warningMessage)) return true;
if (EyesWarning(references, ref warningMessage)) return true;
if (RootHeightWarning(references, ref warningMessage)) return true;
if (FacingAxisWarning(references, ref warningMessage)) return true;
return false;
}
#endregion Main Interface
// Determines whether a Transform is above the arms
private static bool IsNeckBone(Transform bone, Transform leftUpperArm) {
if (leftUpperArm.parent != null && leftUpperArm.parent == bone) return false;
if (Hierarchy.IsAncestor(leftUpperArm, bone)) return false;
return true;
}
// Determines whether a bone is valid for being added into the eyes array
private static bool AddBoneToEyes(Transform bone, ref BipedReferences references, AutoDetectParams autoDetectParams) {
if (references.head != null) {
if (!Hierarchy.IsAncestor(bone, references.head)) return false;
}
if (bone.GetComponent<SkinnedMeshRenderer>() != null) return false;
return true;
}
// Determines whether a bone is valid for being added into the spine
private static bool AddBoneToSpine(Transform bone, ref BipedReferences references, AutoDetectParams autoDetectParams) {
if (bone == references.root) return false;
bool isLegsParent = bone == references.leftThigh.parent;
if (isLegsParent && !autoDetectParams.legsParentInSpine) return false;
if (references.pelvis != null) {
if (bone == references.pelvis) return false;
if (Hierarchy.IsAncestor(references.pelvis, bone)) return false;
}
return true;
}
// Tries to guess the limb bones based on naming
private static void DetectLimb(BipedNaming.BoneType boneType, BipedNaming.BoneSide boneSide, ref Transform firstBone, ref Transform secondBone, ref Transform lastBone, Transform[] transforms) {
Transform[] limb = BipedNaming.GetBonesOfTypeAndSide(boneType, boneSide, transforms);
if (limb.Length < 3) {
//Warning.Log("Unable to detect biped bones by bone names. Please manually assign bone references.", firstBone, true);
return;
}
// Standard biped characters
if (limb.Length == 3) {
firstBone = limb[0];
secondBone = limb[1];
lastBone = limb[2];
}
// For Bootcamp soldier type of characters with more than 3 limb bones
if (limb.Length > 3) {
firstBone = limb[0];
secondBone = limb[2];
lastBone = limb[limb.Length - 1];
}
}
// Adds transform to hierarchy if not null
private static void AddBoneToHierarchy(ref Transform[] bones, Transform transform) {
if (transform == null) return;
Array.Resize(ref bones, bones.Length + 1);
bones[bones.Length - 1] = transform;
}
// Check if the limb is properly set up
private static bool LimbError(Transform bone1, Transform bone2, Transform bone3, ref string errorMessage) {
if (bone1 == null) {
errorMessage = "Bone 1 of a BipedReferences limb is null.";
return true;
}
if (bone2 == null) {
errorMessage = "Bone 2 of a BipedReferences limb is null.";
return true;
}
if (bone3 == null) {
errorMessage = "Bone 3 of a BipedReferences limb is null.";
return true;
}
Transform duplicate = (Transform)Hierarchy.ContainsDuplicate(new Transform[3] { bone1, bone2, bone3 });
if (duplicate != null) {
errorMessage = duplicate.name + " is represented multiple times in the same BipedReferences limb.";
return true;
}
if (bone2.position == bone1.position) {
errorMessage = "Second bone's position equals first bone's position in the biped's limb.";
return true;
}
if (bone3.position == bone2.position) {
errorMessage = "Third bone's position equals second bone's position in the biped's limb.";
return true;
}
if (!Hierarchy.HierarchyIsValid(new Transform[3] { bone1, bone2, bone3 })) {
errorMessage = "BipedReferences limb hierarchy is invalid. Bone transforms in a limb do not belong to the same ancestry. Please make sure the bones are parented to each other. " +
"Bones: " + bone1.name + ", " + bone2.name + ", " + bone3.name;
return true;
}
return false;
}
// Check if the limb is properly set up
private static bool LimbWarning(Transform bone1, Transform bone2, Transform bone3, ref string warningMessage) {
Vector3 cross = Vector3.Cross(bone2.position - bone1.position, bone3.position - bone1.position);
if (cross == Vector3.zero) {
warningMessage = "BipedReferences limb is completely stretched out in the initial pose. IK solver can not calculate the default bend plane for the limb. " +
"Please make sure you character's limbs are at least slightly bent in the initial pose. " +
"First bone: " + bone1.name + ", second bone: " + bone2.name + ".";
return true;
}
return false;
}
// Check if spine is properly set up
private static bool SpineError(BipedReferences references, ref string errorMessage) {
// No spine might be a valid setup in some cases
if (references.spine.Length == 0) return false;
for (int i = 0; i < references.spine.Length; i++) {
if (references.spine[i] == null) {
errorMessage = "BipedReferences spine bone at index " + i + " is null.";
return true;
}
}
Transform duplicate = (Transform)Hierarchy.ContainsDuplicate(references.spine);
if (duplicate != null) {
errorMessage = duplicate.name + " is represented multiple times in BipedReferences spine.";
return true;
}
if (!Hierarchy.HierarchyIsValid(references.spine)) {
errorMessage = "BipedReferences spine hierarchy is invalid. Bone transforms in the spine do not belong to the same ancestry. Please make sure the bones are parented to each other.";
return true;
}
for (int i = 0; i < references.spine.Length; i++) {
bool matchesParentPosition = false;
if (i == 0 && references.spine[i].position == references.pelvis.position) matchesParentPosition = true;
if (i != 0 && references.spine.Length > 1 && references.spine[i].position == references.spine[i - 1].position) matchesParentPosition = true;
if (matchesParentPosition) {
errorMessage = "Biped's spine bone nr " + i + " position is the same as its parent spine/pelvis bone's position. Please remove this bone from the spine.";
return true;
}
}
return false;
}
// Check if spine is properly set up
private static bool SpineWarning(BipedReferences references, ref string warningMessage) {
// Maybe need to add something here in the future
return false;
}
// Check if eyes are properly set up
private static bool EyesError(BipedReferences references, ref string errorMessage) {
// No eyes might be a valid setup
if (references.eyes.Length == 0) return false;
for (int i = 0; i < references.eyes.Length; i++) {
if (references.eyes[i] == null) {
errorMessage = "BipedReferences eye bone at index " + i + " is null.";
return true;
}
}
Transform duplicate = (Transform)Hierarchy.ContainsDuplicate(references.eyes);
if (duplicate != null) {
errorMessage = duplicate.name + " is represented multiple times in BipedReferences eyes.";
return true;
}
return false;
}
// Check if eyes are properly set up
private static bool EyesWarning(BipedReferences references, ref string warningMessage) {
// Maybe need to add something here in the future
return false;
}
// Check if BipedIK transform position is at the character's feet
private static bool RootHeightWarning(BipedReferences references, ref string warningMessage) {
if (references.head == null) return false;
float headHeight = GetVerticalOffset(references.head.position, references.leftFoot.position, references.root.rotation);
float rootHeight = GetVerticalOffset(references.root.position, references.leftFoot.position, references.root.rotation);
if (rootHeight / headHeight > 0.2f) {
warningMessage = "Biped's root Transform's position should be at ground level relative to the character (at the character's feet not at its pelvis).";
return true;
}
return false;
}
// Check if the character is facing the correct axis
private static bool FacingAxisWarning(BipedReferences references, ref string warningMessage) {
Vector3 handsLeftToRight = references.rightHand.position - references.leftHand.position;
Vector3 feetLeftToRight = references.rightFoot.position - references.leftFoot.position;
float dotHands = Vector3.Dot(handsLeftToRight.normalized, references.root.right);
float dotFeet = Vector3.Dot(feetLeftToRight.normalized, references.root.right);
if (dotHands < 0 || dotFeet < 0) {
warningMessage = "Biped does not seem to be facing its forward axis. " +
"Please make sure that in the initial pose the character is facing towards the positive Z axis of the Biped root gameobject.";
return true;
}
return false;
}
// Gets vertical offset relative to a rotation
private static float GetVerticalOffset(Vector3 p1, Vector3 p2, Quaternion rotation) {
Vector3 v = Quaternion.Inverse(rotation) * (p1 - p2);
return v.y;
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 0bb327e2dbd674e81906dcacf4777dbf
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,17 @@
using UnityEngine;
using System.Collections;
namespace RootMotion {
/// <summary>
/// Adding comments to GameObjects in the Inspector.
/// </summary>
public class Comments : MonoBehaviour {
/// <summary>
/// The comment.
/// </summary>
[Multiline]
public string text;
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 92471cc1733c49042be56cd39b4b6e5e
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,18 @@
using UnityEngine;
using System.Collections;
namespace RootMotion {
// Just for displaying a GUI text in the Game View.
public class DemoGUIMessage : MonoBehaviour {
public string text;
public Color color = Color.white;
void OnGUI() {
GUI.color = color;
GUILayout.Label(text);
GUI.color = Color.white;
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: b527358a3a7e442d3b6f2f52806aac84
timeCreated: 1444293804
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,162 @@
using UnityEngine;
using System.Collections;
using System;
namespace RootMotion {
/// <summary>
/// Contains tools for working on Transform hierarchies.
/// </summary>
public class Hierarchy {
/// <summary>
/// Make sure the bones are in valid %Hierarchy
/// </summary>
public static bool HierarchyIsValid(Transform[] bones) {
for (int i = 1; i < bones.Length; i++) {
// If parent bone is not an ancestor of bone, the hierarchy is invalid
if (!IsAncestor(bones[i], bones[i - 1])) {
return false;
}
}
return true;
}
/// <summary>
/// Checks if an array of objects contains any duplicates.
/// </summary>
public static UnityEngine.Object ContainsDuplicate(UnityEngine.Object[] objects) {
for (int i = 0; i < objects.Length; i++) {
for (int i2 = 0; i2 < objects.Length; i2++) {
if (i != i2 && objects[i] == objects[i2]) return objects[i];
}
}
return null;
}
/// <summary>
/// Determines whether the second Transform is an ancestor to the first Transform.
/// </summary>
public static bool IsAncestor(Transform transform, Transform ancestor) {
if (transform == null) return true;
if (ancestor == null) return true;
if (transform.parent == null) return false;
if (transform.parent == ancestor) return true;
return IsAncestor(transform.parent, ancestor);
}
/// <summary>
/// Returns true if the transforms contains the child
/// </summary>
public static bool ContainsChild(Transform transform, Transform child) {
if (transform == child) return true;
Transform[] children = transform.GetComponentsInChildren<Transform>() as Transform[];
foreach (Transform c in children) if (c == child) return true;
return false;
}
/// <summary>
/// Adds all Transforms until the blocker to the array
/// </summary>
public static void AddAncestors(Transform transform, Transform blocker, ref Transform[] array) {
if (transform.parent != null && transform.parent != blocker) {
if (transform.parent.position != transform.position && transform.parent.position != blocker.position) {
Array.Resize(ref array, array.Length + 1);
array[array.Length - 1] = transform.parent;
}
AddAncestors(transform.parent, blocker, ref array);
}
}
/// <summary>
/// Gets the last ancestor that has more than minChildCount number of child Transforms
/// </summary>
public static Transform GetAncestor(Transform transform, int minChildCount) {
if (transform == null) return null;
if (transform.parent != null) {
if (transform.parent.childCount >= minChildCount) return transform.parent;
return GetAncestor(transform.parent, minChildCount);
}
return null;
}
/// <summary>
/// Gets the first common ancestor up the hierarchy
/// </summary>
public static Transform GetFirstCommonAncestor(Transform t1, Transform t2) {
if (t1 == null) return null;
if (t2 == null) return null;
if (t1.parent == null) return null;
if (t2.parent == null) return null;
if (IsAncestor(t2, t1.parent)) return t1.parent;
return GetFirstCommonAncestor(t1.parent, t2);
}
/// <summary>
/// Gets the first common ancestor of the specified transforms.
/// </summary>
public static Transform GetFirstCommonAncestor(Transform[] transforms) {
if (transforms == null) {
Debug.LogWarning("Transforms is null.");
return null;
}
if (transforms.Length == 0) {
Debug.LogWarning("Transforms.Length is 0.");
return null;
}
for (int i = 0; i < transforms.Length; i++) {
if (transforms[i] == null) return null;
if (IsCommonAncestor(transforms[i], transforms)) return transforms[i];
}
return GetFirstCommonAncestorRecursive(transforms[0], transforms);
}
/// <summary>
/// Gets the first common ancestor recursively.
/// </summary>
public static Transform GetFirstCommonAncestorRecursive(Transform transform, Transform[] transforms) {
if (transform == null) {
Debug.LogWarning("Transform is null.");
return null;
}
if (transforms == null) {
Debug.LogWarning("Transforms is null.");
return null;
}
if (transforms.Length == 0) {
Debug.LogWarning("Transforms.Length is 0.");
return null;
}
if (IsCommonAncestor(transform, transforms)) return transform;
if (transform.parent == null) return null;
return GetFirstCommonAncestorRecursive(transform.parent, transforms);
}
/// <summary>
/// Determines whether the first parameter is the common ancestor of all the other specified transforms.
/// </summary>
public static bool IsCommonAncestor(Transform transform, Transform[] transforms) {
if (transform == null) {
Debug.LogWarning("Transform is null.");
return false;
}
for (int i = 0; i < transforms.Length; i++) {
if (transforms[i] == null) {
Debug.Log("Transforms[" + i + "] is null.");
return false;
}
if (!IsAncestor(transforms[i], transform) && transforms[i] != transform) return false;
}
return true;
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 3a3d07bf34b8e46d69638784c913e10a
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,28 @@
using UnityEngine;
using System.Collections;
namespace RootMotion
{
/// <summary>
/// Comment attribute for Editor.
/// </summary>
public class InspectorComment : PropertyAttribute
{
public string name;
public string color = "white";
public InspectorComment(string name)
{
this.name = name;
this.color = "white";
}
public InspectorComment(string name, string color)
{
this.name = name;
this.color = color;
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 8445754565080cd469dedb4d0423de77
timeCreated: 1533045022
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,363 @@
using UnityEngine;
using System.Collections;
namespace RootMotion {
/// <summary>
/// Interpolation mode.
/// </summary>
[System.Serializable]
public enum InterpolationMode
{
None,
InOutCubic,
InOutQuintic,
InOutSine,
InQuintic,
InQuartic,
InCubic,
InQuadratic,
InElastic,
InElasticSmall,
InElasticBig,
InSine,
InBack,
OutQuintic,
OutQuartic,
OutCubic,
OutInCubic,
OutInQuartic,
OutElastic,
OutElasticSmall,
OutElasticBig,
OutSine,
OutBack,
OutBackCubic,
OutBackQuartic,
BackInCubic,
BackInQuartic,
};
/// <summary>
/// Class for various interpolation methods.
/// </summary>
public class Interp
{
#region Public methods
/// <summary>
/// Interpolate the specified t by InterpolationMode mode.
/// </summary>
/// <param name='t'>
/// T.
/// </param>
/// <param name='mode'>
/// InterpolationMode.
/// </param>
public static float Float(float t, InterpolationMode mode) {
float interpT = 0;
switch (mode) {
case InterpolationMode.None:
interpT = Interp.None(t, 0, 1);
break;
case InterpolationMode.InOutCubic:
interpT = Interp.InOutCubic(t, 0, 1);
break;
case InterpolationMode.InOutQuintic:
interpT = Interp.InOutQuintic(t, 0, 1);
break;
case InterpolationMode.InQuintic:
interpT = Interp.InQuintic(t, 0, 1);
break;
case InterpolationMode.InQuartic:
interpT = Interp.InQuartic(t, 0, 1);
break;
case InterpolationMode.InCubic:
interpT = Interp.InCubic(t, 0, 1);
break;
case InterpolationMode.InQuadratic:
interpT = Interp.InQuadratic(t, 0, 1);
break;
case InterpolationMode.OutQuintic:
interpT = Interp.OutQuintic(t, 0, 1);
break;
case InterpolationMode.OutQuartic:
interpT = Interp.OutQuartic(t, 0, 1);
break;
case InterpolationMode.OutCubic:
interpT = Interp.OutCubic(t, 0, 1);
break;
case InterpolationMode.OutInCubic:
interpT = Interp.OutInCubic(t, 0, 1);
break;
case InterpolationMode.OutInQuartic:
interpT = Interp.OutInCubic(t, 0, 1);
break;
case InterpolationMode.BackInCubic:
interpT = Interp.BackInCubic(t, 0, 1);
break;
case InterpolationMode.BackInQuartic:
interpT = Interp.BackInQuartic(t, 0, 1);
break;
case InterpolationMode.OutBackCubic:
interpT = Interp.OutBackCubic(t, 0, 1);
break;
case InterpolationMode.OutBackQuartic:
interpT = Interp.OutBackQuartic(t, 0, 1);
break;
case InterpolationMode.OutElasticSmall:
interpT = Interp.OutElasticSmall(t, 0, 1);
break;
case InterpolationMode.OutElasticBig:
interpT = Interp.OutElasticBig(t, 0, 1);
break;
case InterpolationMode.InElasticSmall:
interpT = Interp.InElasticSmall(t, 0, 1);
break;
case InterpolationMode.InElasticBig:
interpT = Interp.InElasticBig(t, 0, 1);
break;
case InterpolationMode.InSine:
interpT = Interp.InSine(t, 0, 1);
break;
case InterpolationMode.OutSine:
interpT = Interp.OutSine(t, 0, 1);
break;
case InterpolationMode.InOutSine:
interpT = Interp.InOutSine(t, 0, 1);
break;
case InterpolationMode.InElastic:
interpT = Interp.OutElastic(t, 0, 1);
break;
case InterpolationMode.OutElastic:
interpT = Interp.OutElastic(t, 0, 1);
break;
case InterpolationMode.InBack:
interpT = Interp.InBack(t, 0, 1);
break;
case InterpolationMode.OutBack:
interpT = Interp.OutBack(t, 0, 1);
break;
default: interpT = 0;
break;
}
return interpT;
}
/// <summary>
/// Interpolate between two verctors by InterpolationMode mode
/// </summary>
public static Vector3 V3(Vector3 v1, Vector3 v2, float t, InterpolationMode mode) {
float interpT = Interp.Float(t, mode);
return ((1 - interpT) * v1) + (interpT * v2);
}
/// <summary>
/// Linear interpolation of value towards target.
/// </summary>
public static float LerpValue(float value, float target, float increaseSpeed, float decreaseSpeed) {
if (value == target) return target;
if (value < target) return Mathf.Clamp(value + Time.deltaTime * increaseSpeed, -Mathf.Infinity, target);
else return Mathf.Clamp(value - Time.deltaTime * decreaseSpeed, target, Mathf.Infinity);
}
#endregion Public methods
#region Interpolation modes
private static float None (float t, float b, float c) { // time, b, distance,
return b + c * (t);
}
private static float InOutCubic(float t, float b, float c) {
float ts = t * t;
float tc = ts * t;
return b + c * (-2 * tc + 3 * ts);
}
private static float InOutQuintic (float t, float b, float c) {
float ts = t * t;
float tc = ts * t;
return b + c * (6 * tc * ts + -15 * ts * ts + 10 * tc);
}
private static float InQuintic (float t, float b, float c) {
float ts = t * t;
float tc = ts * t;
return b + c * (tc * ts);
}
private static float InQuartic (float t, float b, float c) {
float ts = t * t;
return b + c * (ts * ts);
}
private static float InCubic (float t, float b, float c) {
float ts = t * t;
float tc = ts * t;
return b + c * (tc);
}
private static float InQuadratic (float t, float b, float c) {
float ts = t * t;
return b + c * (ts);
}
private static float OutQuintic (float t, float b, float c) {
float ts = t * t;
float tc = ts * t;
return b + c * (tc * ts + -5 * ts * ts + 10 * tc + -10 * ts + 5 * t);
}
private static float OutQuartic (float t, float b, float c) {
float ts = t * t;
float tc = ts * t;
return b + c * (-1 * ts * ts + 4 * tc + -6 * ts + 4 * t);
}
private static float OutCubic (float t, float b, float c) {
float ts = t * t;
float tc = ts * t;
return b + c * (tc + -3 * ts + 3 * t);
}
private static float OutInCubic (float t, float b, float c) {
float ts = t * t;
float tc = ts * t;
return b + c * (4 * tc + -6 * ts + 3 * t);
}
private static float OutInQuartic (float t, float b, float c) {
float ts = t * t;
float tc = ts * t;
return b + c * (6 * tc + -9 * ts + 4 * t);
}
private static float BackInCubic (float t, float b, float c) {
float ts = t * t;
float tc = ts * t;
return b + c *(4 * tc + -3 * ts);
}
private static float BackInQuartic (float t, float b, float c) {
float ts = t * t;
float tc = ts * t;
return b + c * (2 * ts * ts + 2 * tc + -3 * ts);
}
private static float OutBackCubic (float t, float b, float c) {
float ts = t * t;
float tc = ts * t;
return b + c * (4 * tc + -9 * ts + 6 * t);
}
private static float OutBackQuartic (float t, float b, float c) {
float ts = t * t;
float tc = ts * t;
return b + c * (-2 * ts * ts + 10 * tc + -15 * ts + 8 * t);
}
private static float OutElasticSmall (float t, float b, float c) {
float ts = t * t;
float tc = ts * t;
return b + c * (33 * tc * ts + -106 * ts * ts + 126 * tc + -67 * ts + 15 * t);
}
private static float OutElasticBig (float t, float b, float c) {
float ts = t * t;
float tc = ts * t;
return b+c*(56*tc*ts + -175*ts*ts + 200*tc + -100*ts + 20*t);
}
private static float InElasticSmall (float t, float b, float c) {
float ts = t * t;
float tc = ts * t;
return b + c * (33 * tc * ts + -59 * ts * ts + 32 * tc + -5 * ts);
}
private static float InElasticBig (float t, float b, float c) {
float ts = t * t;
float tc = ts * t;
return b + c * (56 * tc * ts + -105 * ts * ts + 60 * tc + -10 * ts);
}
private static float InSine (float t, float b, float c) {
c -= b;
return -c * Mathf.Cos(t / 1 * (Mathf.PI / 2)) + c + b;
}
private static float OutSine (float t, float b, float c) {
c -= b;
return c * Mathf.Sin(t / 1 * (Mathf.PI / 2)) + b;
}
private static float InOutSine (float t, float b, float c) {
c -= b;
return -c / 2 * (Mathf.Cos(Mathf.PI * t / 1) - 1) + b;
}
private static float InElastic (float t, float b, float c) {
c -= b;
float d = 1f;
float p = d * .3f;
float s = 0;
float a = 0;
if (t == 0) return b;
if ((t /= d) == 1) return b + c;
if (a == 0f || a < Mathf.Abs(c)){
a = c;
s = p / 4;
}else{
s = p / (2 * Mathf.PI) * Mathf.Asin(c / a);
}
return -(a * Mathf.Pow(2, 10 * (t-=1)) * Mathf.Sin((t * d - s) * (2 * Mathf.PI) / p)) + b;
}
private static float OutElastic (float t, float b, float c) {
c -= b;
float d = 1f;
float p = d * .3f;
float s = 0;
float a = 0;
if (t == 0) return b;
if ((t /= d) == 1) return b + c;
if (a == 0f || a < Mathf.Abs(c)){
a = c;
s = p / 4;
}else{
s = p / (2 * Mathf.PI) * Mathf.Asin(c / a);
}
return (a * Mathf.Pow(2, -10 * t) * Mathf.Sin((t * d - s) * (2 * Mathf.PI) / p) + c + b);
}
private static float InBack(float t, float b, float c){
c -= b;
t /= 1;
float s = 1.70158f;
return c * (t) * t * ((s + 1) * t - s) + b;
}
private static float OutBack (float t, float b, float c) {
float s = 1.70158f;
c -= b;
t = (t / 1) - 1;
return c * ((t) * t * ((s + 1) * t + s) + 1) + b;
}
#endregion Interpolation modes
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 8d23298dba9a44325997366a9dc5314f
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,142 @@
using UnityEngine;
using System.Collections.Generic;
namespace RootMotion {
/// <summary>
/// This class contains tools for working with LayerMasks.
/// Most of this was copied from Unity Wiki: http://wiki.unity3d.com/index.php?title=LayerMaskExtensions.
/// </summary>
public static class LayerMaskExtensions
{
/// <summary>
/// Does the LayerMask contain a specific layer index?
/// </summary>
public static bool Contains(LayerMask mask, int layer) {
return mask == (mask | (1 << layer));
}
/// <summary>
/// Creates a LayerMask from an array of layer names.
/// </summary>
public static LayerMask Create(params string[] layerNames)
{
return NamesToMask(layerNames);
}
/// <summary>
/// Creates a LayerMask from an array of layer indexes.
/// </summary>
public static LayerMask Create(params int[] layerNumbers)
{
return LayerNumbersToMask(layerNumbers);
}
/// <summary>
/// Creates a LayerMask from a number of layer names.
/// </summary>
public static LayerMask NamesToMask(params string[] layerNames)
{
LayerMask ret = (LayerMask)0;
foreach(var name in layerNames)
{
ret |= (1 << LayerMask.NameToLayer(name));
}
return ret;
}
/// <summary>
/// Creates a LayerMask from a number of layer indexes.
/// </summary>
public static LayerMask LayerNumbersToMask(params int[] layerNumbers)
{
LayerMask ret = (LayerMask)0;
foreach(var layer in layerNumbers)
{
ret |= (1 << layer);
}
return ret;
}
/// <summary>
/// Inverts a LayerMask.
/// </summary>
public static LayerMask Inverse(this LayerMask original)
{
return ~original;
}
/// <summary>
/// Adds a number of layer names to an existing LayerMask.
/// </summary>
public static LayerMask AddToMask(this LayerMask original, params string[] layerNames)
{
return original | NamesToMask(layerNames);
}
/// <summary>
/// Removes a number of layer names from an existing LayerMask.
/// </summary>
public static LayerMask RemoveFromMask(this LayerMask original, params string[] layerNames)
{
LayerMask invertedOriginal = ~original;
return ~(invertedOriginal | NamesToMask(layerNames));
}
/// <summary>
/// Returns a string array of layer names from a LayerMask.
/// </summary>
public static string[] MaskToNames(this LayerMask original)
{
var output = new List<string>();
for (int i = 0; i < 32; ++i)
{
int shifted = 1 << i;
if ((original & shifted) == shifted)
{
string layerName = LayerMask.LayerToName(i);
if (!string.IsNullOrEmpty(layerName))
{
output.Add(layerName);
}
}
}
return output.ToArray();
}
/// <summary>
/// Returns an array of layer indexes from a LayerMask.
/// </summary>
public static int[] MaskToNumbers(this LayerMask original)
{
var output = new List<int>();
for (int i = 0; i < 32; ++i)
{
int shifted = 1 << i;
if ((original & shifted) == shifted)
{
output.Add(i);
}
}
return output.ToArray();
}
/// <summary>
/// Parses a LayerMask to a string.
/// </summary>
public static string MaskToString(this LayerMask original)
{
return MaskToString(original, ", ");
}
/// <summary>
/// Parses a LayerMask to a string using the specified delimiter.
/// </summary>
public static string MaskToString(this LayerMask original, string delimiter)
{
return string.Join(delimiter, MaskToNames(original));
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 9bbb833ec06b64849830a149f1e7b7e6
timeCreated: 1435745915
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,40 @@
using UnityEngine;
namespace RootMotion
{
/// <summary>
/// Auto-instantiated singleton base class.
/// </summary>
public abstract class LazySingleton<T> : MonoBehaviour where T : LazySingleton<T>
{
private static T sInstance = null;
public static bool hasInstance
{
get
{
return sInstance != null;
}
}
public static T instance
{
get
{
if (sInstance == null)
{
string name = typeof(T).ToString();
sInstance = new GameObject(name).AddComponent<T>();
}
return sInstance;
}
}
protected virtual void Awake()
{
sInstance = (T)this;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 38a253ce1de8036419e9f6c792893d7e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,218 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace RootMotion.Demos
{
// Custom navigator for Unity Navigation.
[System.Serializable]
public class Navigator
{
public enum State
{
Idle,
Seeking,
OnPath,
}
[Tooltip("Should this Navigator be actively seeking a path.")]
public bool activeTargetSeeking;
[Tooltip("Increase this value if the character starts running in a circle, not able to reach the corner because of a too large turning radius.")]
public float cornerRadius = 0.5f;
[Tooltip("Recalculate path if target position has moved by this distance from the position it was at when the path was originally calculated")]
public float recalculateOnPathDistance = 1f;
//public float recalculateBadTargetDistance = 1f;
[Tooltip("Sample within this distance from sourcePosition.")]
public float maxSampleDistance = 5f;
[Tooltip("Interval of updating the path")]
public float nextPathInterval = 3f;
/// <summary>
/// Get the move direction vector (normalized). If nowhere to go or path finished, will return Vector3.zero.
/// </summary>
public Vector3 normalizedDeltaPosition { get; private set; }
/// <summary>
/// Get the current state of this Navigator (Idle, Seeking, OnPath).
/// </summary>
public State state { get; private set; }
private Transform transform;
private int cornerIndex;
private Vector3[] corners = new Vector3[0];
private UnityEngine.AI.NavMeshPath path;
private Vector3 lastTargetPosition;
private bool initiated;
private float nextPathTime;
public void Initiate(Transform transform)
{
this.transform = transform;
path = new UnityEngine.AI.NavMeshPath();
initiated = true;
cornerIndex = 0;
corners = new Vector3[0];
state = State.Idle;
lastTargetPosition = new Vector3(Mathf.Infinity, Mathf.Infinity, Mathf.Infinity);
}
public void Update(Vector3 targetPosition)
{
if (!initiated)
{
Debug.LogError("Trying to update an uninitiated Navigator.");
return;
}
switch (state)
{
// When seeking path
case State.Seeking:
normalizedDeltaPosition = Vector3.zero;
if (path.status == UnityEngine.AI.NavMeshPathStatus.PathComplete)
{
corners = path.corners;
cornerIndex = 0;
if (corners.Length == 0)
{
Debug.LogWarning("Zero Corner Path", transform);
Stop();
}
else
{
state = State.OnPath;
}
}
if (path.status == UnityEngine.AI.NavMeshPathStatus.PathPartial)
{
Debug.LogWarning("Path Partial", transform);
}
if (path.status == UnityEngine.AI.NavMeshPathStatus.PathInvalid)
{
Debug.LogWarning("Path Invalid", transform);
}
break;
// When already on path
case State.OnPath:
if (activeTargetSeeking && Time.time > nextPathTime && HorDistance(targetPosition, lastTargetPosition) > recalculateOnPathDistance)
{
CalculatePath(targetPosition);
break;
}
if (cornerIndex < corners.Length)
{
Vector3 d = corners[cornerIndex] - transform.position;
d.y = 0f;
float mag = d.magnitude;
if (mag > 0f) normalizedDeltaPosition = (d / d.magnitude);
else normalizedDeltaPosition = Vector3.zero;
if (mag < cornerRadius)
{
cornerIndex++;
if (cornerIndex >= corners.Length) Stop();
}
}
break;
// Not on path, not seeking
case State.Idle:
if (activeTargetSeeking && Time.time > nextPathTime) CalculatePath(targetPosition);
break;
}
}
private void CalculatePath(Vector3 targetPosition)
{
if (Find(targetPosition))
{
lastTargetPosition = targetPosition;
state = State.Seeking;
}
else
{
Stop();
}
nextPathTime = Time.time + nextPathInterval;
}
private bool Find(Vector3 targetPosition)
{
if (HorDistance(transform.position, targetPosition) < cornerRadius * 2f) return false;
//if (HorDistance(targetPosition, lastTargetPosition) < recalculateBadTargetDistance) return false;
if (UnityEngine.AI.NavMesh.CalculatePath(transform.position, targetPosition, UnityEngine.AI.NavMesh.AllAreas, path))
{
return true;
}
else
{
UnityEngine.AI.NavMeshHit hit = new UnityEngine.AI.NavMeshHit();
if (UnityEngine.AI.NavMesh.SamplePosition(targetPosition, out hit, maxSampleDistance, UnityEngine.AI.NavMesh.AllAreas))
{
if (UnityEngine.AI.NavMesh.CalculatePath(transform.position, hit.position, UnityEngine.AI.NavMesh.AllAreas, path))
{
return true;
}
}
}
return false;
}
private void Stop()
{
state = State.Idle;
normalizedDeltaPosition = Vector3.zero;
}
private float HorDistance(Vector3 p1, Vector3 p2)
{
return Vector2.Distance(new Vector2(p1.x, p1.z), new Vector2(p2.x, p2.z));
}
public void Visualize()
{
if (state == State.Idle) Gizmos.color = Color.gray;
if (state == State.Seeking) Gizmos.color = Color.red;
if (state == State.OnPath) Gizmos.color = Color.green;
if (corners.Length > 0 && state == State.OnPath && cornerIndex == 0)
{
Gizmos.DrawLine(transform.position, corners[0]);
}
for (int i = 0; i < corners.Length; i++)
{
Gizmos.DrawSphere(corners[i], 0.1f);
}
if (corners.Length > 1)
{
for (int i = 0; i < corners.Length - 1; i++)
{
Gizmos.DrawLine(corners[i], corners[i + 1]);
}
}
Gizmos.color = Color.white;
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 733f93bd37122dd4889c53b5fa05a147
timeCreated: 1536568016
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,89 @@
using UnityEngine;
using System;
using System.Collections.Generic;
namespace RootMotion
{
public enum ShowIfMode
{
Disabled = 0,
Hidden = 1
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)]
public class ShowIfAttribute : PropertyAttribute
{
public string propName { get; protected set; }
public object propValue { get; protected set; }
public object otherPropValue { get; protected set; }
public bool indent { get; private set; }
public ShowIfMode mode { get; protected set; }
public ShowIfAttribute(string propertyName, object propertyValue = null, object otherPropertyValue = null, bool indent = false, ShowIfMode mode = ShowIfMode.Hidden)
{
this.propName = propertyName;
this.propValue = propertyValue;
this.otherPropValue = otherPropertyValue;
this.indent = indent;
this.mode = mode;
}
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)]
public class ShowRangeIfAttribute : ShowIfAttribute
{
public float min { get; private set; }
public float max { get; private set; }
public ShowRangeIfAttribute(float min, float max, string propertyName, object propertyValue = null, object otherPropertyValue = null, bool indent = false, ShowIfMode mode = ShowIfMode.Hidden) : base (propertyName, propertyValue, otherPropertyValue, indent, mode)
{
this.min = min;
this.max = max;
}
}
/// <summary>
/// Large header attribute for Editor.
/// </summary>
public class ShowLargeHeaderIf : ShowIfAttribute
{
public string name;
public string color = "white";
public ShowLargeHeaderIf(string name, string propertyName, object propertyValue = null, object otherPropertyValue = null, bool indent = false, ShowIfMode mode = ShowIfMode.Hidden) : base(propertyName, propertyValue, otherPropertyValue, indent, mode)
{
this.name = name;
this.color = "white";
}
public ShowLargeHeaderIf(string name, string color, string propertyName, object propertyValue = null, object otherPropertyValue = null, bool indent = false, ShowIfMode mode = ShowIfMode.Hidden) : base(propertyName, propertyValue, otherPropertyValue, indent, mode)
{
this.name = name;
this.color = color;
}
}
/// <summary>
/// Large header attribute for Editor.
/// </summary>
public class LargeHeader : PropertyAttribute
{
public string name;
public string color = "white";
public LargeHeader(string name)
{
this.name = name;
this.color = "white";
}
public LargeHeader(string name, string color)
{
this.name = name;
this.color = color;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 075dde4e7ae31b440915c8bd52e13197
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,303 @@
using UnityEngine;
using System.Collections;
namespace RootMotion {
/// <summary>
/// Helper methods for dealing with Quaternions.
/// </summary>
public static class QuaTools {
/// <summary>
/// Returns yaw angle (-180 - 180) of 'forward' vector relative to rotation space defined by spaceForward and spaceUp axes.
/// </summary>
public static float GetYaw(Quaternion space, Vector3 forward)
{
Vector3 dirLocal = Quaternion.Inverse(space) * forward;
if (dirLocal.x == 0f && dirLocal.z == 0f) return 0f;
if (float.IsInfinity(dirLocal.x) || float.IsInfinity(dirLocal.z)) return 0;
return Mathf.Atan2(dirLocal.x, dirLocal.z) * Mathf.Rad2Deg;
}
/// <summary>
/// Returns pitch angle (-90 - 90) of 'forward' vector relative to rotation space defined by spaceForward and spaceUp axes.
/// </summary>
public static float GetPitch(Quaternion space, Vector3 forward)
{
forward = forward.normalized;
Vector3 dirLocal = Quaternion.Inverse(space) * forward;
if (Mathf.Abs(dirLocal.y) > 1f) dirLocal.Normalize();
return -Mathf.Asin(dirLocal.y) * Mathf.Rad2Deg;
}
/// <summary>
/// Returns bank angle (-180 - 180) of 'forward' and 'up' vectors relative to rotation space defined by spaceForward and spaceUp axes.
/// </summary>
public static float GetBank(Quaternion space, Vector3 forward, Vector3 up)
{
Vector3 spaceUp = space * Vector3.up;
Quaternion invSpace = Quaternion.Inverse(space);
forward = invSpace * forward;
up = invSpace * up;
Quaternion q = Quaternion.Inverse(Quaternion.LookRotation(spaceUp, forward));
up = q * up;
float result = Mathf.Atan2(up.x, up.z) * Mathf.Rad2Deg;
return Mathf.Clamp(result, -180f, 180f);
}
/// <summary>
/// Returns yaw angle (-180 - 180) of 'forward' vector relative to rotation space defined by spaceForward and spaceUp axes.
/// </summary>
public static float GetYaw(Quaternion space, Quaternion rotation)
{
Vector3 dirLocal = Quaternion.Inverse(space) * (rotation * Vector3.forward);
if (dirLocal.x == 0f && dirLocal.z == 0f) return 0f;
if (float.IsInfinity(dirLocal.x) || float.IsInfinity(dirLocal.z)) return 0;
return Mathf.Atan2(dirLocal.x, dirLocal.z) * Mathf.Rad2Deg;
}
/// <summary>
/// Returns pitch angle (-90 - 90) of 'forward' vector relative to rotation space defined by spaceForward and spaceUp axes.
/// </summary>
public static float GetPitch(Quaternion space, Quaternion rotation)
{
Vector3 dirLocal = Quaternion.Inverse(space) * (rotation * Vector3.forward);
if (Mathf.Abs(dirLocal.y) > 1f) dirLocal.Normalize();
return -Mathf.Asin(dirLocal.y) * Mathf.Rad2Deg;
}
/// <summary>
/// Returns bank angle (-180 - 180) of 'forward' and 'up' vectors relative to rotation space defined by spaceForward and spaceUp axes.
/// </summary>
public static float GetBank(Quaternion space, Quaternion rotation)
{
Vector3 spaceUp = space * Vector3.up;
Quaternion invSpace = Quaternion.Inverse(space);
Vector3 forward = invSpace * (rotation * Vector3.forward);
Vector3 up = invSpace * (rotation * Vector3.up);
Quaternion q = Quaternion.Inverse(Quaternion.LookRotation(spaceUp, forward));
up = q * up;
float result = Mathf.Atan2(up.x, up.z) * Mathf.Rad2Deg;
return Mathf.Clamp(result, -180f, 180f);
}
/// <summary>
/// Optimized Quaternion.Lerp
/// </summary>
public static Quaternion Lerp(Quaternion fromRotation, Quaternion toRotation, float weight) {
if (weight <= 0f) return fromRotation;
if (weight >= 1f) return toRotation;
return Quaternion.Lerp(fromRotation, toRotation, weight);
}
/// <summary>
/// Optimized Quaternion.Slerp
/// </summary>
public static Quaternion Slerp(Quaternion fromRotation, Quaternion toRotation, float weight) {
if (weight <= 0f) return fromRotation;
if (weight >= 1f) return toRotation;
return Quaternion.Slerp(fromRotation, toRotation, weight);
}
/// <summary>
/// Returns the rotation from identity Quaternion to "q", interpolated linearily by "weight".
/// </summary>
public static Quaternion LinearBlend(Quaternion q, float weight) {
if (weight <= 0f) return Quaternion.identity;
if (weight >= 1f) return q;
return Quaternion.Lerp(Quaternion.identity, q, weight);
}
/// <summary>
/// Returns the rotation from identity Quaternion to "q", interpolated spherically by "weight".
/// </summary>
public static Quaternion SphericalBlend(Quaternion q, float weight) {
if (weight <= 0f) return Quaternion.identity;
if (weight >= 1f) return q;
return Quaternion.Slerp(Quaternion.identity, q, weight);
}
/// <summary>
/// Creates a FromToRotation, but makes sure its axis remains fixed near to the Quaternion singularity point.
/// </summary>
/// <returns>
/// The from to rotation around an axis.
/// </returns>
/// <param name='fromDirection'>
/// From direction.
/// </param>
/// <param name='toDirection'>
/// To direction.
/// </param>
/// <param name='axis'>
/// Axis. Should be normalized before passing into this method.
/// </param>
public static Quaternion FromToAroundAxis(Vector3 fromDirection, Vector3 toDirection, Vector3 axis) {
Quaternion fromTo = Quaternion.FromToRotation(fromDirection, toDirection);
float angle = 0;
Vector3 freeAxis = Vector3.zero;
fromTo.ToAngleAxis(out angle, out freeAxis);
float dot = Vector3.Dot(freeAxis, axis);
if (dot < 0) angle = -angle;
return Quaternion.AngleAxis(angle, axis);
}
/// <summary>
/// Gets the rotation that can be used to convert a rotation from one axis space to another.
/// </summary>
public static Quaternion RotationToLocalSpace(Quaternion space, Quaternion rotation) {
return Quaternion.Inverse(Quaternion.Inverse(space) * rotation);
}
/// <summary>
/// Gets the Quaternion from rotation "from" to rotation "to".
/// </summary>
public static Quaternion FromToRotation(Quaternion from, Quaternion to) {
if (to == from) return Quaternion.identity;
return to * Quaternion.Inverse(from);
}
/// <summary>
/// Gets the closest direction axis to a vector. Input vector must be normalized!
/// </summary>
public static Vector3 GetAxis(Vector3 v) {
Vector3 closest = Vector3.right;
bool neg = false;
float x = Vector3.Dot(v, Vector3.right);
float maxAbsDot = Mathf.Abs(x);
if (x < 0f) neg = true;
float y = Vector3.Dot(v, Vector3.up);
float absDot = Mathf.Abs(y);
if (absDot > maxAbsDot) {
maxAbsDot = absDot;
closest = Vector3.up;
neg = y < 0f;
}
float z = Vector3.Dot(v, Vector3.forward);
absDot = Mathf.Abs(z);
if (absDot > maxAbsDot) {
closest = Vector3.forward;
neg = z < 0f;
}
if (neg) closest = -closest;
return closest;
}
/// <summary>
/// Clamps the rotation similar to V3Tools.ClampDirection.
/// </summary>
public static Quaternion ClampRotation(Quaternion rotation, float clampWeight, int clampSmoothing) {
if (clampWeight >= 1f) return Quaternion.identity;
if (clampWeight <= 0f) return rotation;
float angle = Quaternion.Angle(Quaternion.identity, rotation);
float dot = 1f - (angle / 180f);
float targetClampMlp = Mathf.Clamp(1f - ((clampWeight - dot) / (1f - dot)), 0f, 1f);
float clampMlp = Mathf.Clamp(dot / clampWeight, 0f, 1f);
// Sine smoothing iterations
for (int i = 0; i < clampSmoothing; i++) {
float sinF = clampMlp * Mathf.PI * 0.5f;
clampMlp = Mathf.Sin(sinF);
}
return Quaternion.Slerp(Quaternion.identity, rotation, clampMlp * targetClampMlp);
}
/// <summary>
/// Clamps an angular value.
/// </summary>
public static float ClampAngle(float angle, float clampWeight, int clampSmoothing) {
if (clampWeight >= 1f) return 0f;
if (clampWeight <= 0f) return angle;
float dot = 1f - (Mathf.Abs(angle) / 180f);
float targetClampMlp = Mathf.Clamp(1f - ((clampWeight - dot) / (1f - dot)), 0f, 1f);
float clampMlp = Mathf.Clamp(dot / clampWeight, 0f, 1f);
// Sine smoothing iterations
for (int i = 0; i < clampSmoothing; i++) {
float sinF = clampMlp * Mathf.PI * 0.5f;
clampMlp = Mathf.Sin(sinF);
}
return Mathf.Lerp(0f, angle, clampMlp * targetClampMlp);
}
/// <summary>
/// Used for matching the rotations of objects that have different orientations.
/// </summary>
public static Quaternion MatchRotation(Quaternion targetRotation, Vector3 targetAxis1, Vector3 targetAxis2, Vector3 axis1, Vector3 axis2) {
Quaternion f = Quaternion.LookRotation(axis1, axis2);
Quaternion fTarget = Quaternion.LookRotation(targetAxis1, targetAxis2);
Quaternion d = targetRotation * fTarget;
return d * Quaternion.Inverse(f);
}
/// <summary>
/// Converts an Euler rotation from 0 to 360 representation to -180 to 180.
/// </summary>
public static Vector3 ToBiPolar(Vector3 euler)
{
return new Vector3(ToBiPolar(euler.x), ToBiPolar(euler.y), ToBiPolar(euler.z));
}
/// <summary>
/// Converts an angular value from 0 to 360 representation to -180 to 180.
/// </summary>
public static float ToBiPolar(float angle)
{
angle = angle % 360f;
if (angle >= 180f) return angle - 360f;
if (angle <= -180f) return angle + 360f;
return angle;
}
/// <summary>
/// Mirrors a Quaternion on the YZ plane in provided rotation space.
/// </summary>
public static Quaternion MirrorYZ(Quaternion r, Quaternion space)
{
r = Quaternion.Inverse(space) * r;
Vector3 forward = r * Vector3.forward;
Vector3 up = r * Vector3.up;
forward.x *= -1;
up.x *= -1;
return space * Quaternion.LookRotation(forward, up);
}
/// <summary>
/// Mirrors a Quaternion on the world space YZ plane.
/// </summary>
public static Quaternion MirrorYZ(Quaternion r)
{
Vector3 forward = r * Vector3.forward;
Vector3 up = r * Vector3.up;
forward.x *= -1;
up.x *= -1;
return Quaternion.LookRotation(forward, up);
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: a5860b124f5b94f3fb04cf4180b7ad67
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,35 @@
using UnityEngine;
using System.Collections;
namespace RootMotion
{
/// <summary>
/// The base abstract Singleton class.
/// </summary>
public abstract class Singleton<T> : MonoBehaviour where T : Singleton<T>
{
private static T sInstance = null;
public static T instance
{
get
{
return sInstance;
}
}
public static void Clear()
{
sInstance = null;
}
protected virtual void Awake()
{
if (sInstance != null) Debug.LogError(name + "error: already initialized", this);
sInstance = (T)this;
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: e1c8f0459a8434e80acd2f821a30412d
timeCreated: 1435745822
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,127 @@
using UnityEngine;
using System.Collections;
namespace RootMotion {
/// <summary>
/// Manages solver initiation and updating
/// </summary>
public class SolverManager: MonoBehaviour {
#region Main Interface
/// <summary>
/// If true, will fix all the Transforms used by the solver to their initial state in each Update. This prevents potential problems with unanimated bones and animator culling with a small cost of performance. Not recommended for CCD and FABRIK solvers.
/// </summary>
[Tooltip("If true, will fix all the Transforms used by the solver to their initial state in each Update. This prevents potential problems with unanimated bones and animator culling with a small cost of performance. Not recommended for CCD and FABRIK solvers.")]
public bool fixTransforms = true;
/// <summary>
/// [DEPRECATED] Use "enabled = false" instead.
/// </summary>
public void Disable() {
Debug.Log("IK.Disable() is deprecated. Use enabled = false instead", transform);
enabled = false;
}
#endregion Main
protected virtual void InitiateSolver() {}
protected virtual void UpdateSolver() {}
protected virtual void FixTransforms() {}
private Animator animator;
private Animation legacy;
private bool updateFrame;
private bool componentInitiated;
void OnDisable() {
if (!Application.isPlaying) return;
Initiate();
}
void Start() {
Initiate();
}
private bool animatePhysics {
get {
if (animator != null) return animator.updateMode == AnimatorUpdateMode.AnimatePhysics;
if (legacy != null) return legacy.animatePhysics;
return false;
}
}
private void Initiate() {
if (componentInitiated) return;
FindAnimatorRecursive(transform, true);
InitiateSolver();
componentInitiated = true;
}
void Update() {
if (skipSolverUpdate) return;
if (animatePhysics) return;
if (fixTransforms) FixTransforms();
}
// Finds the first Animator/Animation up the hierarchy
private void FindAnimatorRecursive(Transform t, bool findInChildren) {
if (isAnimated) return;
animator = t.GetComponent<Animator>();
legacy = t.GetComponent<Animation>();
if (isAnimated) return;
if (animator == null && findInChildren) animator = t.GetComponentInChildren<Animator>();
if (legacy == null && findInChildren) legacy = t.GetComponentInChildren<Animation>();
if (!isAnimated && t.parent != null) FindAnimatorRecursive(t.parent, false);
}
private bool isAnimated {
get {
return animator != null || legacy != null;
}
}
// Workaround hack for the solver to work with animatePhysics
void FixedUpdate() {
if (skipSolverUpdate) {
skipSolverUpdate = false;
}
updateFrame = true;
if (animatePhysics && fixTransforms) FixTransforms();
}
// Updating
void LateUpdate() {
if (skipSolverUpdate) return;
// Check if either animatePhysics is false or FixedUpdate has been called
if (!animatePhysics) updateFrame = true;
if (!updateFrame) return;
updateFrame = false;
UpdateSolver();
}
// This enables other scripts to update the Animator on in FixedUpdate and the solvers with it
private bool skipSolverUpdate;
public void UpdateSolverExternal() {
if (!enabled) return;
skipSolverUpdate = true;
UpdateSolver();
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 24f5c8273305e4aed8dd09bf3a1a379a
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,25 @@
using UnityEngine;
using System.Collections;
namespace RootMotion {
/// <summary>
/// Forwards collider OnTrigger.. events.
/// </summary>
public class TriggerEventBroadcaster : MonoBehaviour {
public GameObject target;
void OnTriggerEnter(Collider collider) {
if (target != null) target.SendMessage("OnTriggerEnter", collider, SendMessageOptions.DontRequireReceiver);
}
void OnTriggerStay(Collider collider) {
if (target != null) target.SendMessage("OnTriggerStay", collider, SendMessageOptions.DontRequireReceiver);
}
void OnTriggerExit(Collider collider) {
if (target != null) target.SendMessage("OnTriggerExit", collider, SendMessageOptions.DontRequireReceiver);
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: ee28b2c81966748468e9371d12c933a2
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,82 @@
using UnityEngine;
using System.Collections;
namespace RootMotion
{
/// <summary>
/// Helper methods for dealing with 2-dimensional vectors.
/// </summary>
public static class V2Tools
{
/// <summary>
/// Converts Vector3 to Vector2 on the XZ plane
/// </summary>
public static Vector2 XZ(Vector3 v)
{
return new Vector2(v.x, v.z);
}
/// <summary>
/// Returns delta angle from 'dir1' to 'dir2' in degrees.
/// </summary>
public static float DeltaAngle(Vector2 dir1, Vector2 dir2)
{
float angle1 = Mathf.Atan2(dir1.x, dir1.y) * Mathf.Rad2Deg;
float angle2 = Mathf.Atan2(dir2.x, dir2.y) * Mathf.Rad2Deg;
return Mathf.DeltaAngle(angle1, angle2);
}
/// <summary>
/// Returns delta angle from Vector3 'dir1' to Vector3 'dir2' on the XZ plane in degrees.
/// </summary>
public static float DeltaAngleXZ(Vector3 dir1, Vector3 dir2)
{
float angle1 = Mathf.Atan2(dir1.x, dir1.z) * Mathf.Rad2Deg;
float angle2 = Mathf.Atan2(dir2.x, dir2.z) * Mathf.Rad2Deg;
return Mathf.DeltaAngle(angle1, angle2);
}
/// <summary>
/// Returns true if a line from 'p1' to 'p2' intersects a circle with position 'c' and radius 'r'.
/// </summary>
public static bool LineCircleIntersect(Vector2 p1, Vector2 p2, Vector2 c, float r)
{
Vector2 d = p2 - p1;
Vector2 f = c - p1;
float a = Vector2.Dot(d, d);
float b = 2f * Vector2.Dot(f, d);
float z = Vector2.Dot(f, f) - r * r;
float discr = b * b - 4f * a * z;
if (discr < 0f) return false;
discr = Mathf.Sqrt(discr);
float a2 = 2f * a;
float t1 = (b - discr) / a2;
float t2 = (b + discr) / a2;
if (t1 >= 0f && t1 <= 1f) return true;
if (t2 >= 0f && t2 <= 1f) return true;
return false;
}
/// <summary>
/// Returns true if an infinite ray 'dir' from position 'p1' intersects a circle with position 'c' and radius 'r'.
/// </summary>
public static bool RayCircleIntersect(Vector2 p1, Vector2 dir, Vector2 c, float r)
{
Vector2 p2 = p1 + dir;
p1 -= c;
p2 -= c;
float dx = p2.x - p1.x;
float dy = p2.y - p1.y;
float dr = Mathf.Sqrt(Mathf.Pow(dx, 2f) + Mathf.Pow(dy, 2f));
float D = p1.x * p2.y - p2.x * p1.y;
float discr = Mathf.Pow(r, 2f) * Mathf.Pow(dr, 2f) - Mathf.Pow(D, 2f);
return discr >= 0f;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 28fecd3bd2d91b84bb40145282fa8bf5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,305 @@
using UnityEngine;
using System.Collections;
namespace RootMotion {
/// <summary>
/// Helper methods for dealing with 3-dimensional vectors.
/// </summary>
public static class V3Tools {
/// <summary>
/// Returns yaw angle (-180 - 180) of 'forward' vector.
/// </summary>
public static float GetYaw(Vector3 forward)
{
if (forward.x == 0f && forward.z == 0f) return 0f;
if (float.IsInfinity(forward.x) || float.IsInfinity(forward.z)) return 0;
return Mathf.Atan2(forward.x, forward.z) * Mathf.Rad2Deg;
}
/// <summary>
/// Returns pitch angle (-90 - 90) of 'forward' vector.
/// </summary>
public static float GetPitch(Vector3 forward)
{
forward = forward.normalized; // Asin range -1 - 1
return -Mathf.Asin(forward.y) * Mathf.Rad2Deg;
}
/// <summary>
/// Returns bank angle (-180 - 180) of 'forward' and 'up' vectors.
/// </summary>
public static float GetBank(Vector3 forward, Vector3 up)
{
Quaternion q = Quaternion.Inverse(Quaternion.LookRotation(Vector3.up, forward));
up = q * up;
float result = Mathf.Atan2(up.x, up.z) * Mathf.Rad2Deg;
return Mathf.Clamp(result, -180f, 180f);
}
/// <summary>
/// Returns yaw angle (-180 - 180) of 'forward' vector relative to rotation space defined by spaceForward and spaceUp axes.
/// </summary>
public static float GetYaw(Vector3 spaceForward, Vector3 spaceUp, Vector3 forward)
{
Quaternion space = Quaternion.Inverse(Quaternion.LookRotation(spaceForward, spaceUp));
Vector3 dirLocal = space * forward;
if (dirLocal.x == 0f && dirLocal.z == 0f) return 0f;
if (float.IsInfinity(dirLocal.x) || float.IsInfinity(dirLocal.z)) return 0;
return Mathf.Atan2(dirLocal.x, dirLocal.z) * Mathf.Rad2Deg;
}
/// <summary>
/// Returns pitch angle (-90 - 90) of 'forward' vector relative to rotation space defined by spaceForward and spaceUp axes.
/// </summary>
public static float GetPitch(Vector3 spaceForward, Vector3 spaceUp, Vector3 forward)
{
Quaternion space = Quaternion.Inverse(Quaternion.LookRotation(spaceForward, spaceUp));
Vector3 dirLocal = space * forward;
forward.Normalize();
return -Mathf.Asin(dirLocal.y) * Mathf.Rad2Deg;
}
/// <summary>
/// Returns bank angle (-180 - 180) of 'forward' and 'up' vectors relative to rotation space defined by spaceForward and spaceUp axes.
/// </summary>
public static float GetBank(Vector3 spaceForward, Vector3 spaceUp, Vector3 forward, Vector3 up)
{
Quaternion space = Quaternion.Inverse(Quaternion.LookRotation(spaceForward, spaceUp));
forward = space * forward;
up = space * up;
Quaternion q = Quaternion.Inverse(Quaternion.LookRotation(spaceUp, forward));
up = q * up;
float result = Mathf.Atan2(up.x, up.z) * Mathf.Rad2Deg;
return Mathf.Clamp(result, -180f, 180f);
}
/// <summary>
/// Optimized Vector3.Lerp
/// </summary>
public static Vector3 Lerp(Vector3 fromVector, Vector3 toVector, float weight) {
if (weight <= 0f) return fromVector;
if (weight >= 1f) return toVector;
return Vector3.Lerp(fromVector, toVector, weight);
}
/// <summary>
/// Optimized Vector3.Slerp
/// </summary>
public static Vector3 Slerp(Vector3 fromVector, Vector3 toVector, float weight) {
if (weight <= 0f) return fromVector;
if (weight >= 1f) return toVector;
return Vector3.Slerp(fromVector, toVector, weight);
}
/// <summary>
/// Returns vector projection on axis multiplied by weight.
/// </summary>
public static Vector3 ExtractVertical(Vector3 v, Vector3 verticalAxis, float weight)
{
if (weight <= 0f) return Vector3.zero;
if (verticalAxis == Vector3.up) return Vector3.up * v.y * weight;
return Vector3.Project(v, verticalAxis) * weight;
}
/// <summary>
/// Returns vector projected to a plane and multiplied by weight.
/// </summary>
public static Vector3 ExtractHorizontal(Vector3 v, Vector3 normal, float weight)
{
if (weight <= 0f) return Vector3.zero;
if (normal == Vector3.up) return new Vector3(v.x, 0f, v.z) * weight;
Vector3 tangent = v;
Vector3.OrthoNormalize(ref normal, ref tangent);
return Vector3.Project(v, tangent) * weight;
}
/// <summary>
/// Flattens a vector on a plane defined by 'normal'.
/// </summary>
public static Vector3 Flatten(Vector3 v, Vector3 normal)
{
if (normal == Vector3.up) return new Vector3(v.x, 0f, v.z);
return v - Vector3.Project(v, normal);
}
/// <summary>
/// Clamps the direction to clampWeight from normalDirection, clampSmoothing is the number of sine smoothing iterations applied on the result.
/// </summary>
public static Vector3 ClampDirection(Vector3 direction, Vector3 normalDirection, float clampWeight, int clampSmoothing)
{
if (clampWeight <= 0) return direction;
if (clampWeight >= 1f) return normalDirection;
// Getting the angle between direction and normalDirection
float angle = Vector3.Angle(normalDirection, direction);
float dot = 1f - (angle / 180f);
if (dot > clampWeight) return direction;
// Clamping the target
float targetClampMlp = clampWeight > 0 ? Mathf.Clamp(1f - ((clampWeight - dot) / (1f - dot)), 0f, 1f) : 1f;
// Calculating the clamp multiplier
float clampMlp = clampWeight > 0 ? Mathf.Clamp(dot / clampWeight, 0f, 1f) : 1f;
// Sine smoothing iterations
for (int i = 0; i < clampSmoothing; i++)
{
float sinF = clampMlp * Mathf.PI * 0.5f;
clampMlp = Mathf.Sin(sinF);
}
// Slerping the direction (don't use Lerp here, it breaks it)
return Vector3.Slerp(normalDirection, direction, clampMlp * targetClampMlp);
}
/// <summary>
/// Clamps the direction to clampWeight from normalDirection, clampSmoothing is the number of sine smoothing iterations applied on the result.
/// </summary>
public static Vector3 ClampDirection(Vector3 direction, Vector3 normalDirection, float clampWeight, int clampSmoothing, out bool changed) {
changed = false;
if (clampWeight <= 0) return direction;
if (clampWeight >= 1f) {
changed = true;
return normalDirection;
}
// Getting the angle between direction and normalDirection
float angle = Vector3.Angle(normalDirection, direction);
float dot = 1f - (angle / 180f);
if (dot > clampWeight) return direction;
changed = true;
// Clamping the target
float targetClampMlp = clampWeight > 0? Mathf.Clamp(1f - ((clampWeight - dot) / (1f - dot)), 0f, 1f): 1f;
// Calculating the clamp multiplier
float clampMlp = clampWeight > 0? Mathf.Clamp(dot / clampWeight, 0f, 1f): 1f;
// Sine smoothing iterations
for (int i = 0; i < clampSmoothing; i++) {
float sinF = clampMlp * Mathf.PI * 0.5f;
clampMlp = Mathf.Sin(sinF);
}
// Slerping the direction (don't use Lerp here, it breaks it)
return Vector3.Slerp(normalDirection, direction, clampMlp * targetClampMlp);
}
/// <summary>
/// Clamps the direction to clampWeight from normalDirection, clampSmoothing is the number of sine smoothing iterations applied on the result.
/// </summary>
public static Vector3 ClampDirection(Vector3 direction, Vector3 normalDirection, float clampWeight, int clampSmoothing, out float clampValue) {
clampValue = 1f;
if (clampWeight <= 0) return direction;
if (clampWeight >= 1f) {
return normalDirection;
}
// Getting the angle between direction and normalDirection
float angle = Vector3.Angle(normalDirection, direction);
float dot = 1f - (angle / 180f);
if (dot > clampWeight) {
clampValue = 0f;
return direction;
}
// Clamping the target
float targetClampMlp = clampWeight > 0? Mathf.Clamp(1f - ((clampWeight - dot) / (1f - dot)), 0f, 1f): 1f;
// Calculating the clamp multiplier
float clampMlp = clampWeight > 0? Mathf.Clamp(dot / clampWeight, 0f, 1f): 1f;
// Sine smoothing iterations
for (int i = 0; i < clampSmoothing; i++) {
float sinF = clampMlp * Mathf.PI * 0.5f;
clampMlp = Mathf.Sin(sinF);
}
// Slerping the direction (don't use Lerp here, it breaks it)
float slerp = clampMlp * targetClampMlp;
clampValue = 1f - slerp;
return Vector3.Slerp(normalDirection, direction, slerp);
}
/// <summary>
/// Get the intersection point of line and plane
/// </summary>
public static Vector3 LineToPlane(Vector3 origin, Vector3 direction, Vector3 planeNormal, Vector3 planePoint) {
float dot = Vector3.Dot(planePoint - origin, planeNormal);
float normalDot = Vector3.Dot(direction, planeNormal);
if (normalDot == 0.0f) return Vector3.zero;
float dist = dot / normalDot;
return origin + direction.normalized * dist;
}
/// <summary>
/// Projects a point to a plane.
/// </summary>
public static Vector3 PointToPlane(Vector3 point, Vector3 planePosition, Vector3 planeNormal) {
if (planeNormal == Vector3.up) {
return new Vector3(point.x, planePosition.y, point.z);
}
Vector3 tangent = point - planePosition;
Vector3 normal = planeNormal;
Vector3.OrthoNormalize(ref normal, ref tangent);
return planePosition + Vector3.Project(point - planePosition, tangent);
}
/// <summary>
/// Same as Transform.TransformPoint(), but not using scale.
/// </summary>
public static Vector3 TransformPointUnscaled(Transform t, Vector3 point)
{
return t.position + t.rotation * point;
}
/// <summary>
/// Same as Transform.InverseTransformPoint(), but not using scale.
/// </summary>
public static Vector3 InverseTransformPointUnscaled(Transform t, Vector3 point)
{
return Quaternion.Inverse(t.rotation) * (point - t.position);
}
/// <summary>
/// Same as Transform.InverseTransformPoint();
/// </summary>
public static Vector3 InverseTransformPoint(Vector3 tPos, Quaternion tRot, Vector3 tScale, Vector3 point)
{
return Div(Quaternion.Inverse(tRot) * (point - tPos), tScale);
}
/// <summary>
/// Same as Transform.TransformPoint()
/// </summary>
public static Vector3 TransformPoint(Vector3 tPos, Quaternion tRot, Vector3 tScale, Vector3 point)
{
return tPos + Vector3.Scale(tRot * point, tScale);
}
/// <summary>
/// Divides the values of v1 by the values of v2.
/// </summary>
public static Vector3 Div(Vector3 v1, Vector3 v2)
{
return new Vector3(v1.x / v2.x, v1.y / v2.y, v1.z / v2.z);
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 9a422d0570aa54fb39eb5ace0e12a4ed
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,29 @@
using UnityEngine;
using System.Collections;
namespace RootMotion {
/// <summary>
/// Manages warning messages.
/// </summary>
public static class Warning {
public static bool logged;
public delegate void Logger(string message);
public static void Log(string message, Logger logger, bool logInEditMode = false) {
if (!logInEditMode && !Application.isPlaying) return;
if (logged) return;
if (logger != null) logger(message);
logged = true;
}
public static void Log(string message, Transform context, bool logInEditMode = false) {
if (!logInEditMode && !Application.isPlaying) return;
if (logged) return;
Debug.LogWarning(message, context);
logged = true;
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: c1a06ee7b207c4743876a042752f325b
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: