Animation scaling step 1

This commit is contained in:
Pascal Serrarens 2026-04-10 17:43:44 +02:00
parent 742fc3f323
commit 38391181af
29 changed files with 1009 additions and 143 deletions

View File

@ -0,0 +1,117 @@
using System.IO;
using UnityEditor;
using UnityEngine;
using System.Collections.Generic;
public class AnimationEditor {
[MenuItem("Assets/Scale Animation Transforms...", true)]
private static bool ValidateScaleMenu() {
// enable only when an AnimationClip is selected
return Selection.objects != null && Selection.objects.Length > 0 && System.Array.Exists(Selection.objects, o => o is AnimationClip);
}
[MenuItem("Assets/Scale Animation Transforms...")]
private static void ScaleSelectedClips() {
// default scale - you can change or prompt
float scale = 2.0f;
var clips = new List<AnimationClip>();
foreach (var o in Selection.objects)
if (o is AnimationClip clip)
clips.Add(clip);
if (clips.Count == 0) {
EditorUtility.DisplayDialog("Scale Animation", "No AnimationClip selected.", "OK");
return;
}
if (!EditorUtility.DisplayDialog("Scale Animation", $"Scale translations in {clips.Count} clip(s) by {scale}?", "Yes", "No"))
return;
foreach (AnimationClip clip in clips) {
ScaleClip(clip, scale);
}
EditorUtility.DisplayDialog("Scale Animation", "Done scaling selected clips.", "OK");
}
public static void ScaleClip(AnimationClip clip, float scale, string saveFolder) {
string origPath = AssetDatabase.GetAssetPath(clip);
string ext = Path.GetExtension(origPath);
string nameNoExt = Path.GetFileNameWithoutExtension(origPath);
string fileName = $"{nameNoExt}_scaled{ext}";
string savePath = Path.Combine(saveFolder, fileName).Replace("\\", "/");
if (AssetDatabase.LoadAssetAtPath<Object>(savePath) != null) {
if (!AssetDatabase.DeleteAsset(savePath)) {
Debug.LogError($"Failed to delete existing asset at {savePath}");
return;
}
Debug.Log($"Deleted old {savePath}");
AssetDatabase.Refresh();
}
if (!AssetDatabase.CopyAsset(origPath, savePath)) {
Debug.LogError($"Failed to duplicate asset: {origPath} -> {savePath}");
return;
}
AssetDatabase.ImportAsset(savePath);
AnimationClip newClip = AssetDatabase.LoadAssetAtPath<AnimationClip>(savePath);
if (newClip == null) {
Debug.LogError($"Failed to load duplicated clip at: {savePath}");
return;
}
ScaleClip(newClip, scale);
}
public static void ScaleClip(AnimationClip clip, float scale) {
// make editable copy if needed (if clip is in model import, make sure to duplicate or modify import settings)
Undo.RegisterCompleteObjectUndo(clip, "Scale Animation Translations");
// Collect curve bindings for position-like properties.
EditorCurveBinding[] bindings = AnimationUtility.GetCurveBindings(clip);
foreach (EditorCurveBinding binding in bindings) {
string prop = binding.propertyName;
if (string.IsNullOrEmpty(prop))
continue;
// Identify position/localPosition/property patterns to scale:
// common property names:
// "m_LocalPosition.x", "m_LocalPosition.y", "m_LocalPosition.z"
// "localPosition.x", etc. or custom paths that animate transform.localPosition
// We'll scale any curve with "position" or "LocalPosition" in propertyName (case-insensitive).
string lower = prop.ToLowerInvariant();
bool isPos =
lower.Contains("m_localposition") ||
lower.Contains("localposition") ||
lower.Contains(".position") ||
lower == "position.x" || lower == "position.y" || lower == "position.z";
if (!isPos)
continue;
AnimationCurve curve = AnimationUtility.GetEditorCurve(clip, binding);
if (curve == null) continue;
// scale each keyframe value
Keyframe[] keys = curve.keys;
for (int i = 0; i < keys.Length; i++) {
Keyframe k = keys[i];
k.value *= scale;
k.inTangent *= scale;
k.outTangent *= scale;
keys[i] = k;
}
curve.keys = keys;
AnimationUtility.SetEditorCurve(clip, binding, curve);
}
EditorUtility.SetDirty(clip);
AssetDatabase.SaveAssets();
}
}

View File

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

View File

@ -1,7 +1,7 @@
using UnityEditor;
using UnityEditor.SceneManagement;
namespace Passer.CreatureControl {
namespace CreatureControl {
[CustomEditor(typeof(Creature), true)]
public class Creature_Editor : Editor {

View File

@ -1,13 +1,23 @@
using UnityEngine;
using UnityEditor;
namespace Passer.CreatureControl {
namespace CreatureControl {
[CustomEditor(typeof(InsectRig))]
public class InsectRigEditor : Editor {
SerializedProperty renderProp;
void OnEnable() {
InsectRig insectRig = (InsectRig) target;
renderProp = serializedObject.FindProperty("render");
insectRig.legLength = float.PositiveInfinity;
foreach (TargetLeg leg in insectRig.legs) {
float legLength = leg.bones.length;
if (legLength > 0 && legLength < insectRig.legLength)
insectRig.legLength = legLength;
}
}
public override void OnInspectorGUI() {
@ -40,4 +50,5 @@ namespace Passer.CreatureControl {
}
}
}

View File

@ -1,7 +1,9 @@
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEditor.Animations;
namespace Passer.CreatureControl {
namespace CreatureControl {
[CustomEditor(typeof(Insect), true)]
public class Insect_Editor : Creature_Editor {
@ -16,6 +18,46 @@ namespace Passer.CreatureControl {
anythingChanged |= insect.CheckTargetRig("InsectRig");
insect.insectRig.MatchTo(insect, ref anythingChanged);
float prefabLegLength = 0.003f;
float modelLegLength = insect.insectRig.legLength;
float scale = prefabLegLength / modelLegLength;
Animator animator = insect.insectRig.GetComponent<Animator>();
RuntimeAnimatorController rac = animator.runtimeAnimatorController;
if (rac == null) {
EditorUtility.DisplayDialog("No Controller", "Animator has no RuntimeAnimatorController assigned.", "OK");
return;
}
// Collect unique clips from controller, including BlendTree children
List<AnimationClip> clips = new();
foreach (AnimationClip c in rac.animationClips) {
if (c != null && !clips.Contains(c))
clips.Add(c);
}
// Some clips referenced via AnimatorController layers/states (BlendTrees) may already be in animationClips,
// but to be thorough (and to access BlendTree children) we inspect the controller asset when possible.
string path = AssetDatabase.GetAssetPath(rac);
AnimatorController ac = AssetDatabase.LoadAssetAtPath<AnimatorController>(path);
if (ac != null) {
foreach (AnimatorControllerLayer layer in ac.layers) {
CollectFromStateMachine(layer.stateMachine, clips);
}
}
if (clips.Count == 0) {
EditorUtility.DisplayDialog("No Clips", "No AnimationClips found on the controller.", "OK");
return;
}
if (insect.updateAnimations) {
AnimationEditor.ScaleClip(clips[0], scale, insect.animationsPath);
Debug.Log($"Scaled{clips[0].name} with {scale} and saved it to {insect.animationsPath}");
}
else {
Debug.Log($"Did not scale {clips[0].name} with scale {scale} and save it to {insect.animationsPath}");
}
if (anythingChanged) {
EditorUtility.SetDirty(creature);
@ -23,6 +65,33 @@ namespace Passer.CreatureControl {
}
}
static void CollectFromStateMachine(AnimatorStateMachine sm, List<AnimationClip> outList) {
foreach (var state in sm.states) {
var motion = state.state.motion;
if (motion is AnimationClip clip) {
if (!outList.Contains(clip)) outList.Add(clip);
}
else if (motion is BlendTree bt) {
CollectFromBlendTree(bt, outList);
}
}
foreach (var child in sm.stateMachines)
CollectFromStateMachine(child.stateMachine, outList);
}
static void CollectFromBlendTree(BlendTree tree, List<AnimationClip> outList) {
foreach (var child in tree.children) {
if (child.motion is AnimationClip clip) {
if (!outList.Contains(clip)) outList.Add(clip);
}
else if (child.motion is BlendTree bt) {
CollectFromBlendTree(bt, outList);
}
}
}
#region Inspector
public override void OnInspectorGUI() {
@ -38,6 +107,18 @@ namespace Passer.CreatureControl {
static bool showTargets;
private void TargetsInspector() {
bool configurationIncomplete = false;
if (!insect.leftFrontLeg.isConfigured ||
!insect.leftMiddleLeg.isConfigured ||
!insect.leftHindLeg.isConfigured ||
!insect.rightFrontLeg.isConfigured ||
!insect.rightMiddleLeg.isConfigured ||
!insect.rightHindLeg.isConfigured) {
showTargets = true;
configurationIncomplete = true;
}
GUIContent text = new(
"Targets",
"The target transforms controlling the body parts"
@ -46,7 +127,9 @@ namespace Passer.CreatureControl {
if (showTargets) {
EditorGUI.indentLevel++;
if (configurationIncomplete) {
EditorGUILayout.HelpBox("Not all legs are configured", MessageType.Warning);
}
SerializedProperty leftFrontLegProp = serializedObject.FindProperty(nameof(Insect.leftFrontLeg));
Leg_Editor.Inspector(leftFrontLegProp);
SerializedProperty leftMiddleLegProp = serializedObject.FindProperty(nameof(Insect.leftMiddleLeg));
@ -67,18 +150,25 @@ namespace Passer.CreatureControl {
private void AnimatorInspector() {
GUIContent text = new(
"Animator",
"Standard Unity Animator Controller for animating the character"
"Animator Controller",
"Unity Animator Controller for animating the character"
);
SerializedProperty targetRigProp = serializedObject.FindProperty(nameof(Insect.targetRig));
if (targetRigProp == null)
return;
SerializedObject targetRigObj = new(targetRigProp.objectReferenceValue);
TargetRig targetRig = targetRigProp.objectReferenceValue as TargetRig;
Animator animator = targetRig.GetComponent<Animator>();
if (animator == null)
return;
SerializedProperty animatorControllerProp = targetRigObj.FindProperty(nameof(InsectRig.animator));
animatorControllerProp.objectReferenceValue = (Animator)EditorGUILayout.ObjectField(text, animatorControllerProp.objectReferenceValue, typeof(Animator), true);
SerializedObject animatorObj = new(animator);
SerializedProperty animatorControllerProp = animatorObj.FindProperty("m_Controller");
animatorControllerProp.objectReferenceValue =
(RuntimeAnimatorController)EditorGUILayout.ObjectField(text, animatorControllerProp.objectReferenceValue, typeof(RuntimeAnimatorController), true);
animatorObj.ApplyModifiedProperties();
EditorGUI.indentLevel++;
ForwardSpeedInspector();
@ -93,7 +183,7 @@ namespace Passer.CreatureControl {
);
SerializedProperty forwardSpeedProp = serializedObject.FindProperty(nameof(Insect.forwardSpeed));
forwardSpeedProp.floatValue = EditorGUILayout.FloatField(text, forwardSpeedProp.floatValue);
forwardSpeedProp.floatValue = EditorGUILayout.FloatField(text, forwardSpeedProp.floatValue);
}
private void RotationSpeedInspector() {
@ -103,7 +193,7 @@ namespace Passer.CreatureControl {
);
SerializedProperty rotationSpeedProp = serializedObject.FindProperty(nameof(Insect.rotationSpeed));
rotationSpeedProp.floatValue = EditorGUILayout.FloatField(text, rotationSpeedProp.floatValue);
rotationSpeedProp.floatValue = EditorGUILayout.FloatField(text, rotationSpeedProp.floatValue);
}
#endregion Inspector

View File

@ -1,16 +1,12 @@
using UnityEditor;
using UnityEngine;
namespace Passer.CreatureControl {
namespace CreatureControl {
public class Leg_Editor {
//private string label = "";
private static bool showfield = false;
public void Enable() {
}
public static void Inspector(SerializedProperty legProp) {
GUIStyle foldoutStyle = new(EditorStyles.foldout) {
margin = EditorStyles.objectField.margin
@ -45,6 +41,7 @@ namespace Passer.CreatureControl {
EditorGUI.indentLevel++;
tibiaProp.objectReferenceValue = (Transform)EditorGUILayout.ObjectField("Lower Leg", tibiaProp.objectReferenceValue, typeof(Transform), true);
tarsusProp.objectReferenceValue = (Transform)EditorGUILayout.ObjectField("Foot", tarsusProp.objectReferenceValue, typeof(Transform), true);
// Need to check if anythingChanged and update the projection if it did
EditorGUI.indentLevel--;
}
}

View File

@ -0,0 +1,44 @@
using UnityEngine;
using UnityEditor;
namespace CreatureControl {
[CustomEditor(typeof(TargetLeg))]
public class TargetLeg_Editor : Editor {
protected TargetLeg targetLeg;
private void OnEnable() {
targetLeg = (TargetLeg)target;
CheckLegTarget(targetLeg);
}
void CheckLegTarget(TargetLeg targetLeg) {
LegTarget legTarget = targetLeg.target.GetComponent<LegTarget>();
if (legTarget == null)
legTarget = targetLeg.target.gameObject.AddComponent<LegTarget>();
legTarget.leg = targetLeg;
}
public override void OnInspectorGUI() {
base.OnInspectorGUI();
EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.FloatField("Femur Length", targetLeg.bones.femurLength);
EditorGUI.EndDisabledGroup();
}
public float PrefabFemurLength() {
Object prefabSource = PrefabUtility.GetCorrespondingObjectFromSource(serializedObject.targetObject);
if (prefabSource == null)
return 0;
SerializedObject targetLegPrefabObj = new(prefabSource);
SerializedProperty targetBonesPrefabProp = targetLegPrefabObj.FindProperty(nameof(TargetLeg.bones));
return 0;
}
}
}

View File

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

View File

@ -1,6 +1,6 @@
using UnityEngine;
namespace Passer.CreatureControl {
namespace CreatureControl {
public class Creature : MonoBehaviour {
/// <summary>
@ -24,6 +24,7 @@ namespace Passer.CreatureControl {
public Quaternion targetToModelRotation;
public Animator animator;
public string animationsPath = "Assets";
/// <summary>
/// The maximum height of objects from the ground which do not stop the creature

View File

@ -1,5 +1,8 @@
namespace Passer.CreatureControl {
namespace CreatureControl {
/// <summary>
/// A creature with six legs
/// </summary>
public class Insect : Creature {
public InsectRig insectRig;
@ -13,7 +16,9 @@ namespace Passer.CreatureControl {
public Leg rightFrontLeg;
public Leg rightMiddleLeg;
public Leg rightHindLeg;
public Leg rightHindLeg;
public bool updateAnimations = false;
#region Init

View File

@ -1,19 +1,58 @@
using System.Collections.Generic;
using UnityEngine;
namespace Passer.CreatureControl {
namespace CreatureControl {
// An insect target rig....
/// <summary>
/// A rig or skeleton for a six-legged insect
/// </summary>
public class InsectRig : TargetRig {
/// <summary>
/// The left front leg
/// </summary>
public TargetLeg leftFrontLeg;
/// <summary>
/// The left middle leg
/// </summary>
public TargetLeg leftMiddleLeg;
/// <summary>
/// The left hind leg
/// </summary>
public TargetLeg leftBackLeg;
/// <summary>
/// The right front leg
/// </summary>
public TargetLeg rightFrontLeg;
/// <summary>
/// The right middle leg
/// </summary>
public TargetLeg rightMiddleLeg;
/// <summary>
/// The right hindLeg
/// </summary>
public TargetLeg rightBackLeg;
private TargetLeg[] _legs;
public TargetLeg[] legs {
get {
if (_legs == null) {
_legs = new TargetLeg[6];
_legs[0] = leftFrontLeg;
_legs[1] = leftMiddleLeg;
_legs[2] = leftBackLeg;
_legs[3] = rightFrontLeg;
_legs[4] = rightMiddleLeg;
_legs[5] = rightBackLeg;
}
return _legs;
}
}
public bool render;
public float legLength; // smalled leg length
public override void Pose() {
this.leftBackLeg.PoseLimb();
this.leftMiddleLeg.PoseLimb();
@ -43,7 +82,6 @@ namespace Passer.CreatureControl {
this.rightBackLeg.MatchTo(insect.rightHindLeg);
}
// Public helper you can call from editor or runtime
public void ApplyRenderToChildren(bool value) {
// Find all renderers under this GameObject (including inactive)
var renderers = GetComponentsInChildren<Renderer>(true);

View File

@ -1,8 +1,44 @@
using UnityEngine;
[System.Serializable]
public class Leg {
public Transform femur; // UpperLeg, Thigh
public Transform tibia; // LowerLeg, Shank
public Transform tarsus; // Foot
namespace CreatureControl {
/// <summary>
/// A leg of a creature
/// </summary>
[System.Serializable]
public class Leg {
/// <summary>
/// The upper leg or thigh bone
/// </summary>
public Transform femur;
/// <summary>
/// The lower leg or shank bone
/// </summary>
public Transform tibia;
/// <summary>
/// The foot bone
/// </summary>
public Transform tarsus;
[SerializeField]
private float _femurLength;
public float femurLength {
get {
if (_femurLength <= 0)
_femurLength = Vector3.Distance(this.femur.position, this.tibia.position);
return _femurLength;
}
}
// A bit inefficient is this is used a lot...
public float tibiaLength => Vector3.Distance(this.tibia.position, this.tarsus.position);
public float length => femurLength + tibiaLength;
/// <summary>
/// Check if all bones of the legs have been configured
/// </summary>
/// <returns>True when all bones are configured</returns>
public bool isConfigured => femur != null && tibia != null && tarsus != null;
}
}

View File

@ -1,6 +1,6 @@
using UnityEngine;
namespace Passer.CreatureControl {
namespace CreatureControl {
public class LegTarget : MonoBehaviour {
public TargetLeg leg;

View File

@ -1,83 +1,60 @@
using UnityEngine;
namespace Passer.CreatureControl {
namespace CreatureControl {
/// <summary>
/// A leg in a TargetRig which is used to control a leg in a model
/// </summary>
[System.Serializable]
public class TargetLeg : MonoBehaviour {
public Transform femurTarget; // UpperLeg, Thigh
public Transform tibiaTarget; // LowerLeg, Shank
public Transform tarsusTarget; // Foot
public Leg bones;
public Transform target; // for the tarsus
protected LegTarget legTarget;
public Quaternion targetToBoneFemur;
public Quaternion targetToBoneTibia;
public float femurLength;
public float tibiaLength;
public float length;
/// <summary>
/// Update the lenghts of the leg bones
/// </summary>
private void CalculateLengths() {
this.femurLength = Vector3.Distance(this.femurTarget.position, this.tibiaTarget.position);
this.tibiaLength = Vector3.Distance(this.tibiaTarget.position, this.tarsusTarget.position);
this.length = femurLength + tibiaLength;
}
public void MatchTo(Leg leg) {
this.femurTarget.position = leg.femur.position;
this.tibiaTarget.position = leg.tibia.position;
this.tarsusTarget.position = leg.tarsus.position;
if (this.bones.femur == null || this.bones.tibia == null || this.bones.tarsus == null)
return;
if (leg.femur == null || leg.tibia == null || leg.tarsus == null)
return;
this.bones.femur.position = leg.femur.position;
this.bones.tibia.position = leg.tibia.position;
this.bones.tarsus.position = leg.tarsus.position;
targetToBoneFemur = TargetRig.TargetToBoneRotation(leg.femur, leg.tibia);
targetToBoneTibia = TargetRig.TargetToBoneRotation(leg.tibia, leg.tarsus);
CalculateLengths();
float modelLegLength = leg.length;
float targetLegLength = this.bones.length;
Debug.Log($"model: {modelLegLength} rig: {targetLegLength}");
// Put the end-effector target for IK in a sensible place
Vector3 legDirection = (this.tarsusTarget.position - this.femurTarget.position).normalized;
Vector3 targetPosition = this.femurTarget.position + 0.7f * this.length * legDirection.normalized;
Vector3 legDirection = (this.bones.tarsus.position - this.bones.femur.position).normalized;
Vector3 targetPosition = this.bones.femur.position + 0.7f * this.bones.length * legDirection.normalized;
Quaternion targetRotation = Quaternion.LookRotation(legDirection);
this.target.SetPositionAndRotation(targetPosition, targetRotation);
this.target.localPosition = new(this.target.localPosition.x, 0, this.target.localPosition.z);
}
public virtual void OnDrawGizmosSelected() {
if (this.enabled == false)
return;
if (target != null && legTarget == null) {
legTarget = target.GetComponent<LegTarget>();
if (legTarget == null)
legTarget = target.gameObject.AddComponent<LegTarget>();
legTarget.leg = this;
}
Gizmos.color = Color.white;
if (this.femurTarget != null && this.tibiaTarget != null)
Gizmos.DrawLine(this.femurTarget.position, this.tibiaTarget.position);
if (tibiaTarget != null && this.tarsusTarget != null)
Gizmos.DrawLine(this.tibiaTarget.position, this.tarsusTarget.position);
PoseLimb();
}
/// <summary>
/// Pose the target limb
/// </summary>
public void PoseLimb() {
if (target == null)
return;
if (bones.femur == null || bones.tibia == null || bones.tarsus == null)
return;
Quaternion femurOrientation = FemurRotation(target.position);
Quaternion tibiaOrientation = TibiaRotation(target.position);
Quaternion tarsusOrientation = TarsusRotation(target.rotation);
femurTarget.rotation = femurOrientation;
tibiaTarget.rotation = tibiaOrientation;
tarsusTarget.rotation = tarsusOrientation;
bones.femur.rotation = femurOrientation;
bones.tibia.rotation = tibiaOrientation;
bones.tarsus.rotation = tarsusOrientation;
}
public void UpdateBones(Leg leg) {
@ -86,14 +63,14 @@ namespace Passer.CreatureControl {
}
protected Quaternion FemurRotation(Vector3 targetPosition) {
if (this.femurTarget == null || this.tibiaTarget == null || this.tarsusTarget == null)
if (this.bones.femur == null || this.bones.tibia == null || this.bones.tarsus == null)
return Quaternion.identity;
Vector3 toTarget = targetPosition - this.femurTarget.position;
Vector3 toTarget = targetPosition - this.bones.femur.position;
// Debug.DrawRay(femur.position, toTarget, Color.magenta);
float targetDistance = toTarget.magnitude;
float femurLength = Vector3.Distance(this.femurTarget.position, this.tibiaTarget.position);
float tibiaLength = Vector3.Distance(this.tibiaTarget.position, this.tarsusTarget.position);
float femurLength = Vector3.Distance(this.bones.femur.position, this.bones.tibia.position);
float tibiaLength = Vector3.Distance(this.bones.tibia.position, this.bones.tarsus.position);
float hipAngle = CosineRule(targetDistance, femurLength, tibiaLength);
// NaN happens when the distance to the footTarget is longer than the length of the leg
@ -110,10 +87,10 @@ namespace Passer.CreatureControl {
}
protected Quaternion TibiaRotation(Vector3 targetPosition) {
if (this.tibiaTarget == null)
if (this.bones.tibia == null)
return Quaternion.identity;
Vector3 directionToTarget = targetPosition - this.tibiaTarget.position;
Vector3 directionToTarget = targetPosition - this.bones.tibia.position;
Quaternion tibiaOrientation = Quaternion.LookRotation(directionToTarget, Vector3.up); // femur.up);
return tibiaOrientation; // In world space
@ -124,17 +101,17 @@ namespace Passer.CreatureControl {
}
public void UpdateFemur(Transform femurBone) {
if (femurBone == null || this.femurTarget == null)
if (femurBone == null || this.bones.femur == null)
return;
femurBone.rotation = this.femurTarget.rotation * targetToBoneFemur;
femurBone.rotation = this.bones.femur.rotation * targetToBoneFemur;
}
public void UpdateTibia(Transform tibiaBone) {
if (tibiaBone == null || this.tibiaTarget == null)
if (tibiaBone == null || this.bones.tibia == null)
return;
tibiaBone.rotation = this.tibiaTarget.rotation * targetToBoneTibia;
tibiaBone.rotation = this.bones.tibia.rotation * targetToBoneTibia;
}
#region Math
@ -151,6 +128,23 @@ namespace Passer.CreatureControl {
}
#endregion Math
#region Scene
public virtual void OnDrawGizmosSelected() {
if (this.enabled == false)
return;
Gizmos.color = Color.white;
if (this.bones.femur != null && this.bones.tibia != null)
Gizmos.DrawLine(this.bones.femur.position, this.bones.tibia.position);
if (bones.tibia != null && this.bones.tarsus != null)
Gizmos.DrawLine(this.bones.tibia.position, this.bones.tarsus.position);
PoseLimb();
}
#endregion Scene
}
}

View File

@ -1,14 +1,11 @@
using UnityEngine;
namespace Passer.CreatureControl {
namespace CreatureControl {
/// <summary>
/// A target rig for a creature
/// A target rig or skeleton for a creature
/// </summary>
public class TargetRig : MonoBehaviour {
public Animator animator;
/// <summary>
/// Pose the target rig using the IK targets
/// </summary>

View File

@ -1,9 +1,37 @@
Animations
==========
The NanoBrain Ant comes with walking animations and an animator. These can be used with any hexapod model but as the rigs of these hexapods can be very different<sup>[1](#notes-1)</sup> our software provides an animation projection to solve this. H<sup>2</sup>O
The NanoBrain [Ant](#CreatureControl.Ant) comes with walking animations and an animator. These can be used with any insect model but as the rigs of these hexapods can be very different<sup>[1](#notes-1)</sup> our software provides an animation projection to solve this.
Notes
=====
The animations are designed to work with the InsectTargetRig found in `Runtime/Resources`. When this is used with any other insect model, you will probably see only root motion and no leg movement. This is because there is no mapping from the animation to the bones in the rig of that model.
If you want to use the animations on a different insect model, you should place the
[Insect component](#CreatureControl.Insect)<sup>[2](#notes-2)</sup>
on that model and assign the animator contoller to the `Animator Controller` parameter. *Do not* assign the animator to the Controller parameter of the models Animator component! Note that you probably also need to [configure the leg bones of the model](Models.md#custom-models).
Animators
---------
The package provides an animator In `Samples/Animation` called AntAnimator3. This provides a full 3 Degrees-of-Freedom walking with the following parameters:
- Forward: Forward/backward walking
- Rotate: Rotate left/right
- Strafe: Move sideward left/right
The corresponding animations will be blended together in a resulting walking animation.
The [Ant component](#CreatureControl.Ant) only uses the Forward and Rotate parameters to move the ant.
Animations
----------
The package provides different walking animations, found in `Samples/Animation`:
- AntIdle: No movement
- AntWalkForward: Forward walking, can be reversed to backward walking
- AntWalkLeft: Left sideward walking
- AntWalkRight: Right sideward walking
- AntRotateLeft: Anti-clockwise turning on the spot
- AntRotateRight: Clockwise turning on the spot
These animations are blended by the AntAnimator3 to enable all possible walking movements.
### Notes
1. <a id="notes-1"></a>
The animation retargeting of Unity's Mecanim only works for Humanoid models.
2. <a id="notes-2"></a> bla bla
2. <a id="notes-2">or any other component derived from [Insect](#CreatureControl.Insect)</a>

View File

@ -3,4 +3,14 @@ Models
The package comes with a number of models which can be found in the [Samples][1] folder. These models are rigged and can be used in combination with the [Animations](Animations.md).
[1]: Installation.md (How to install the Samples)
Custom Models
-------------
To use custom insect models, the [Insect component](#CreatureControl.Insect)<sup>[2](#notes-2)</sup> should be added to the model.
Additionally, the leg bones of the insect should be configured as the package is not able to find these automatically.
In most cases, only the upper leg bone (the femur) need to be configures, the lower leg (tibia) and foot (tarsus) bones are then assumed to be the descendant bones of the femur if those bones have only one child bone each. If this approach does not work, it is always possible to override the identified bones by manually replacing them by the correct bone.
#### Notes
[1]: Installation.md (How to install the Samples)
2. <a id="notes-2">or any other component derived from the Insect component</a>

View File

@ -1,7 +1,7 @@
using UnityEditor;
using UnityEngine;
namespace Passer.CreatureControl {
namespace CreatureControl {
[CustomEditor(typeof(Ant))]
public class Ant_Editor : Insect_Editor {

View File

@ -77,15 +77,14 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: f3394e8da3685a263901c13e2a231279, type: 3}
m_Name:
m_EditorClassIdentifier:
femurTarget: {fileID: 2087731612450819825}
tibiaTarget: {fileID: 7566832081299524197}
tarsusTarget: {fileID: 7011186323915432702}
bones:
femur: {fileID: 2087731612450819825}
tibia: {fileID: 7566832081299524197}
tarsus: {fileID: 7011186323915432702}
_femurLength: 0.004816547
target: {fileID: 8947192729555298471}
targetToBoneFemur: {x: 0, y: 0, z: 0, w: 0}
targetToBoneTibia: {x: 0, y: 0, z: 0, w: 0}
femurLength: 0
tibiaLength: 0
length: 0
--- !u!1 &714113386832462525
GameObject:
m_ObjectHideFlags: 0
@ -138,7 +137,6 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: a25e8575da1d1cba48dc2b8da7a2b0ab, type: 3}
m_Name:
m_EditorClassIdentifier:
animator: {fileID: 5843436816833865534}
leftFrontLeg: {fileID: 5655676289343650514}
leftMiddleLeg: {fileID: 6327345715726789984}
leftBackLeg: {fileID: 3124099709297356894}
@ -146,6 +144,7 @@ MonoBehaviour:
rightMiddleLeg: {fileID: 4196034226083389076}
rightBackLeg: {fileID: 8323677930838830493}
render: 0
legLength: 0.009770508
--- !u!95 &5843436816833865534
Animator:
serializedVersion: 5
@ -156,7 +155,7 @@ Animator:
m_GameObject: {fileID: 714113386832462525}
m_Enabled: 1
m_Avatar: {fileID: 0}
m_Controller: {fileID: 9100000, guid: d4b9f32bef604abd5953647ad53ca0f7, type: 2}
m_Controller: {fileID: 9100000, guid: ba169e741a830f910bdde9e04f7c88f4, type: 2}
m_CullingMode: 0
m_UpdateMode: 0
m_ApplyRootMotion: 1
@ -243,15 +242,14 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: f3394e8da3685a263901c13e2a231279, type: 3}
m_Name:
m_EditorClassIdentifier:
femurTarget: {fileID: 976560727556484423}
tibiaTarget: {fileID: 3873135409852464941}
tarsusTarget: {fileID: 3355004770557811387}
bones:
femur: {fileID: 976560727556484423}
tibia: {fileID: 3873135409852464941}
tarsus: {fileID: 3355004770557811387}
_femurLength: 0.003806679
target: {fileID: 2041764967651498327}
targetToBoneFemur: {x: 0, y: 0, z: 0, w: 0}
targetToBoneTibia: {x: 0, y: 0, z: 0, w: 0}
femurLength: 0
tibiaLength: 0
length: 0
--- !u!1 &1274558016778403244
GameObject:
m_ObjectHideFlags: 0
@ -512,15 +510,14 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: f3394e8da3685a263901c13e2a231279, type: 3}
m_Name:
m_EditorClassIdentifier:
femurTarget: {fileID: 7879963364189984297}
tibiaTarget: {fileID: 8557148899078362646}
tarsusTarget: {fileID: 2520372565419969361}
bones:
femur: {fileID: 7879963364189984297}
tibia: {fileID: 8557148899078362646}
tarsus: {fileID: 2520372565419969361}
_femurLength: 0.006008031
target: {fileID: 5692380185316106944}
targetToBoneFemur: {x: 0, y: 0, z: 0, w: 0}
targetToBoneTibia: {x: 0, y: 0, z: 0, w: 0}
femurLength: 0
tibiaLength: 0
length: 0
--- !u!1 &1713014906338526394
GameObject:
m_ObjectHideFlags: 0
@ -1198,15 +1195,14 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: f3394e8da3685a263901c13e2a231279, type: 3}
m_Name:
m_EditorClassIdentifier:
femurTarget: {fileID: 1138231466888029713}
tibiaTarget: {fileID: 6229249450306020558}
tarsusTarget: {fileID: 3473835942814004862}
bones:
femur: {fileID: 1138231466888029713}
tibia: {fileID: 6229249450306020558}
tarsus: {fileID: 3473835942814004862}
_femurLength: 0.0031432603
target: {fileID: 2714894253296331867}
targetToBoneFemur: {x: 0, y: 0, z: 0, w: 0}
targetToBoneTibia: {x: 0, y: 0, z: 0, w: 0}
femurLength: 0
tibiaLength: 0
length: 0
--- !u!1 &6227205784071755847
GameObject:
m_ObjectHideFlags: 0
@ -1316,15 +1312,14 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: f3394e8da3685a263901c13e2a231279, type: 3}
m_Name:
m_EditorClassIdentifier:
femurTarget: {fileID: 5158943779127512027}
tibiaTarget: {fileID: 5102382635668602733}
tarsusTarget: {fileID: 5832494694235077857}
bones:
femur: {fileID: 5158943779127512027}
tibia: {fileID: 5102382635668602733}
tarsus: {fileID: 5832494694235077857}
_femurLength: 0.004816545
target: {fileID: 8660658904332009209}
targetToBoneFemur: {x: 0, y: 0, z: 0, w: 0}
targetToBoneTibia: {x: 0, y: 0, z: 0, w: 0}
femurLength: 0
tibiaLength: 0
length: 0
--- !u!1 &6934656281848074513
GameObject:
m_ObjectHideFlags: 0
@ -1885,15 +1880,14 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: f3394e8da3685a263901c13e2a231279, type: 3}
m_Name:
m_EditorClassIdentifier:
femurTarget: {fileID: 7827425323228862897}
tibiaTarget: {fileID: 8607306932134183899}
tarsusTarget: {fileID: 5350519918537018444}
bones:
femur: {fileID: 7827425323228862897}
tibia: {fileID: 8607306932134183899}
tarsus: {fileID: 5350519918537018444}
_femurLength: 0.00380668
target: {fileID: 5063380583403966759}
targetToBoneFemur: {x: 0, y: 0, z: 0, w: 0}
targetToBoneTibia: {x: 0, y: 0, z: 0, w: 0}
femurLength: 0
tibiaLength: 0
length: 0
--- !u!1 &9173392299445808517
GameObject:
m_ObjectHideFlags: 0

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using UnityEngine;
using NanoBrain;
namespace Passer.CreatureControl {
namespace CreatureControl {
/// <summary>
/// Simulated ant using a NanoBrain
@ -58,8 +58,9 @@ namespace Passer.CreatureControl {
protected override void Awake() {
base.Awake();
if (this.targetRig != null)
this.animator = this.targetRig.animator;
this.animator = this.targetRig.GetComponent<Animator>();
this.nanoBrain = GetComponentInChildren<Brain>();
}

View File

@ -1,7 +1,7 @@
using System.Collections;
using UnityEngine;
namespace Passer.CreatureControl {
namespace CreatureControl {
public class AntsNest : Odorant {
public Ant antPrefab;

View File

@ -1,6 +1,6 @@
using UnityEngine;
namespace Passer.CreatureControl {
namespace CreatureControl {
public class Food : Odorant
{

View File

@ -2,7 +2,7 @@ using System.Collections.Generic;
using UnityEngine;
using NanoBrain;
namespace Passer.CreatureControl {
namespace CreatureControl {
public class Mouth : MonoBehaviour {
public GameObject foodPrefab;

View File

@ -1,6 +1,6 @@
using UnityEngine;
namespace Passer.CreatureControl {
namespace CreatureControl {
public class Odorant : MonoBehaviour {
public float strength = 1;

View File

@ -1,6 +1,6 @@
using UnityEngine;
namespace Passer.CreatureControl {
namespace CreatureControl {
public class Pheromone : Odorant {
public float duration = 30; // seconds

View File

@ -0,0 +1,473 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!74 &7400000
AnimationClip:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: AntWalkForward 1
serializedVersion: 7
m_Legacy: 0
m_Compressed: 0
m_UseHighQualityCurve: 1
m_RotationCurves: []
m_CompressedRotationCurves: []
m_EulerCurves: []
m_PositionCurves:
- curve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: {x: 0, y: 0, z: 0}
inSlope: {x: 0, y: 0, z: -0.0096}
outSlope: {x: 0, y: 0, z: -0.0096}
tangentMode: 0
weightedMode: 0
inWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334}
outWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334}
- serializedVersion: 3
time: 0.41666666
value: {x: 0, y: 0, z: -0.004}
inSlope: {x: 0, y: -0, z: -0.0096}
outSlope: {x: 0, y: 0.0096, z: 0.0096}
tangentMode: 0
weightedMode: 0
inWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334}
outWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334}
- serializedVersion: 3
time: 0.8333333
value: {x: 0, y: 0.004, z: -4.656613e-10}
inSlope: {x: 0, y: 0, z: 0.009599999}
outSlope: {x: 0, y: 0, z: 0.009599999}
tangentMode: 0
weightedMode: 0
inWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334}
outWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334}
- serializedVersion: 3
time: 1.25
value: {x: 0, y: 0, z: 0.004}
inSlope: {x: 0, y: 0, z: 0.0096}
outSlope: {x: 0, y: 0, z: -0.009600001}
tangentMode: 0
weightedMode: 0
inWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334}
outWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334}
- serializedVersion: 3
time: 1.6666666
value: {x: 0, y: 0, z: 0}
inSlope: {x: 0, y: 0, z: -0.009600001}
outSlope: {x: 0, y: 0, z: -0.009600001}
tangentMode: 0
weightedMode: 0
inWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334}
outWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334}
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
path: Body/LeftFeetTarget
- curve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: {x: 0, y: 0.004, z: 0}
inSlope: {x: 0, y: 0, z: 0.0096}
outSlope: {x: 0, y: 0, z: 0.0096}
tangentMode: 0
weightedMode: 0
inWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334}
outWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334}
- serializedVersion: 3
time: 0.41666666
value: {x: 0, y: 0, z: 0.004}
inSlope: {x: 0, y: -0.0096, z: 0.0096}
outSlope: {x: 0, y: 0, z: -0.0096}
tangentMode: 0
weightedMode: 0
inWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334}
outWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334}
- serializedVersion: 3
time: 1.25
value: {x: 0, y: 0, z: -0.004}
inSlope: {x: 0, y: -0, z: -0.0096}
outSlope: {x: 0, y: 0.009600001, z: 0.009600001}
tangentMode: 0
weightedMode: 0
inWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334}
outWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334}
- serializedVersion: 3
time: 1.6666666
value: {x: 0, y: 0.004, z: 0}
inSlope: {x: 0, y: 0, z: 0.009600001}
outSlope: {x: 0, y: 0, z: 0.009600001}
tangentMode: 0
weightedMode: 0
inWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334}
outWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334}
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
path: Body/RightFeetTarget
- curve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: {x: 0, y: 0, z: 0}
inSlope: {x: 0, y: 0, z: 0.0096}
outSlope: {x: 0, y: 0, z: 0.0096}
tangentMode: 0
weightedMode: 0
inWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334}
outWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334}
- serializedVersion: 3
time: 1.6666666
value: {x: 0, y: 0, z: 0.016}
inSlope: {x: 0, y: 0, z: 0.0096}
outSlope: {x: 0, y: 0, z: 0.0096}
tangentMode: 0
weightedMode: 0
inWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334}
outWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334}
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
path:
m_ScaleCurves: []
m_FloatCurves: []
m_PPtrCurves: []
m_SampleRate: 24
m_WrapMode: 0
m_Bounds:
m_Center: {x: 0, y: 0, z: 0}
m_Extent: {x: 0, y: 0, z: 0}
m_ClipBindingConstant:
genericBindings:
- serializedVersion: 2
path: 683997697
attribute: 1
script: {fileID: 0}
typeID: 4
customType: 0
isPPtrCurve: 0
isIntCurve: 0
isSerializeReferenceCurve: 0
- serializedVersion: 2
path: 1250470035
attribute: 1
script: {fileID: 0}
typeID: 4
customType: 0
isPPtrCurve: 0
isIntCurve: 0
isSerializeReferenceCurve: 0
- serializedVersion: 2
path: 0
attribute: 1
script: {fileID: 0}
typeID: 4
customType: 0
isPPtrCurve: 0
isIntCurve: 0
isSerializeReferenceCurve: 0
pptrCurveMapping: []
m_AnimationClipSettings:
serializedVersion: 2
m_AdditiveReferencePoseClip: {fileID: 0}
m_AdditiveReferencePoseTime: 0
m_StartTime: 0
m_StopTime: 1.6666666
m_OrientationOffsetY: 0
m_Level: 0
m_CycleOffset: 0
m_HasAdditiveReferencePose: 0
m_LoopTime: 1
m_LoopBlend: 1
m_LoopBlendOrientation: 0
m_LoopBlendPositionY: 0
m_LoopBlendPositionXZ: 0
m_KeepOriginalOrientation: 0
m_KeepOriginalPositionY: 1
m_KeepOriginalPositionXZ: 0
m_HeightFromFeet: 0
m_Mirror: 0
m_EditorCurves:
- serializedVersion: 2
curve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 0
tangentMode: 34
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
- serializedVersion: 3
time: 0.41666666
value: 0
inSlope: -0
outSlope: 0.0096
tangentMode: 69
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
- serializedVersion: 3
time: 0.8333333
value: 0.004
inSlope: 0
outSlope: 0
tangentMode: 34
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
- serializedVersion: 3
time: 1.25
value: 0
inSlope: 0
outSlope: 0
tangentMode: 65
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
- serializedVersion: 3
time: 1.6666666
value: 0
inSlope: 0
outSlope: 0
tangentMode: 34
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
attribute: m_LocalPosition.y
path: Body/LeftFeetTarget
classID: 4
script: {fileID: 0}
flags: 0
- serializedVersion: 2
curve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: -0.0096
outSlope: -0.0096
tangentMode: 34
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
- serializedVersion: 3
time: 0.41666666
value: -0.004
inSlope: -0.0096
outSlope: 0.0096
tangentMode: 69
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
- serializedVersion: 3
time: 1.25
value: 0.004
inSlope: 0.0096
outSlope: -0.009600001
tangentMode: 69
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
- serializedVersion: 3
time: 1.6666666
value: 0
inSlope: -0.009600001
outSlope: -0.009600001
tangentMode: 34
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
attribute: m_LocalPosition.z
path: Body/LeftFeetTarget
classID: 4
script: {fileID: 0}
flags: 0
- serializedVersion: 2
curve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0.004
inSlope: 0
outSlope: 0
tangentMode: 136
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
- serializedVersion: 3
time: 0.41666666
value: 0
inSlope: -0.0096
outSlope: 0
tangentMode: 69
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
- serializedVersion: 3
time: 1.25
value: 0
inSlope: -0
outSlope: 0.009600001
tangentMode: 69
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
- serializedVersion: 3
time: 1.6666666
value: 0.004
inSlope: 0
outSlope: 0
tangentMode: 136
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
attribute: m_LocalPosition.y
path: Body/RightFeetTarget
classID: 4
script: {fileID: 0}
flags: 0
- serializedVersion: 2
curve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0.0096
outSlope: 0.0096
tangentMode: 34
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
- serializedVersion: 3
time: 0.41666666
value: 0.004
inSlope: 0.0096
outSlope: -0.0096
tangentMode: 69
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
- serializedVersion: 3
time: 1.25
value: -0.004
inSlope: -0.0096
outSlope: 0.009600001
tangentMode: 69
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
- serializedVersion: 3
time: 1.6666666
value: 0
inSlope: 0.009600001
outSlope: 0.009600001
tangentMode: 34
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
attribute: m_LocalPosition.z
path: Body/RightFeetTarget
classID: 4
script: {fileID: 0}
flags: 0
- serializedVersion: 2
curve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0.0096
outSlope: 0.0096
tangentMode: 34
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
- serializedVersion: 3
time: 1.6666666
value: 0.016
inSlope: 0.0096
outSlope: 0.0096
tangentMode: 34
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
attribute: m_LocalPosition.z
path:
classID: 4
script: {fileID: 0}
flags: 0
- serializedVersion: 2
curve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 0
tangentMode: 136
weightedMode: 0
inWeight: 0
outWeight: 0
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
attribute: m_LocalPosition.x
path:
classID: 4
script: {fileID: 0}
flags: 8
- serializedVersion: 2
curve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 0
tangentMode: 136
weightedMode: 0
inWeight: 0
outWeight: 0
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
attribute: m_LocalPosition.y
path:
classID: 4
script: {fileID: 0}
flags: 8
m_EulerEditorCurves: []
m_HasGenericRootTransform: 1
m_HasMotionFloatCurves: 0
m_Events: []

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c216474961242cb2ca482c52ec68c790
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 7400000
userData:
assetBundleName:
assetBundleVariant: