导入角色动画,和增加角色控制

This commit is contained in:
2025-12-11 19:30:20 +08:00
parent a60a92e7ba
commit 7775fa30bb
1452 changed files with 592217 additions and 42573 deletions

View File

@@ -0,0 +1,29 @@
using KINEMATION.KAnimationCore.Runtime.Rig;
using UnityEditor;
using UnityEngine;
namespace KINEMATION.KAnimationCore.Editor.Rig
{
[CustomEditor(typeof(KRigComponent), true)]
public class KRigComponentEditor : UnityEditor.Editor
{
private KRigComponent _rigComponent;
private int _boneCount = 0;
private void OnEnable()
{
_rigComponent = (KRigComponent) target;
_boneCount = _rigComponent.GetRigTransforms().Length;
}
public override void OnInspectorGUI()
{
EditorGUILayout.LabelField("Total bones: " + _boneCount);
if (GUILayout.Button("Refresh Hierarchy"))
{
_rigComponent.RefreshHierarchy();
_boneCount = _rigComponent.GetRigTransforms().Length;
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 69ff5379d20945a39a83cf6611fd2fa3
timeCreated: 1704612600

View File

@@ -0,0 +1,97 @@
using KINEMATION.KAnimationCore.Editor.Misc;
using KINEMATION.KAnimationCore.Editor.Tools;
using KINEMATION.KAnimationCore.Runtime.Input;
using KINEMATION.KAnimationCore.Runtime.Rig;
using UnityEditor;
using UnityEngine;
namespace KINEMATION.KAnimationCore.Editor.Rig
{
[CustomEditor(typeof(KRig), true)]
public class KRigEditor : UnityEditor.Editor
{
private KRig _rigAsset;
private KRigComponent _rigComponent;
private SerializedProperty _rigElementChains;
private SerializedProperty _rigCurves;
private KToolbarWidget _kToolbarWidget;
private RigTreeWidget _rigTreeWidget;
private void RenderHierarchy()
{
_rigTreeWidget.Render();
}
private void RenderElementChains()
{
EditorGUILayout.PropertyField(_rigElementChains);
}
private void RenderCurves()
{
EditorGUILayout.PropertyField(_rigCurves);
}
private void OnEnable()
{
_rigAsset = (KRig) target;
_rigElementChains = serializedObject.FindProperty("rigElementChains");
_rigCurves = serializedObject.FindProperty("rigCurves");
_kToolbarWidget = new KToolbarWidget(new KToolbarTab[]
{
new KToolbarTab()
{
name = "Hierarchy",
onTabRendered = RenderHierarchy
},
new KToolbarTab()
{
name = "Element Chains",
onTabRendered = RenderElementChains
},
new KToolbarTab()
{
name = "Curves",
onTabRendered = RenderCurves
}
});
_rigTreeWidget = new RigTreeWidget();
_rigTreeWidget.Refresh(_rigAsset.GetHierarchy());
}
private void ImportRig()
{
_rigAsset.ImportRig(_rigComponent);
}
public override void OnInspectorGUI()
{
_rigComponent = (KRigComponent) EditorGUILayout.ObjectField("Rig Component",
_rigComponent, typeof(KRigComponent), true);
_rigAsset.targetAnimator = (RuntimeAnimatorController) EditorGUILayout.ObjectField("Animator",
_rigAsset.targetAnimator, typeof(RuntimeAnimatorController), true);
_rigAsset.inputConfig = (UserInputConfig) EditorGUILayout.ObjectField("Input Config",
_rigAsset.inputConfig, typeof(UserInputConfig), true);
if (_rigComponent == null)
{
EditorGUILayout.HelpBox("Rig Component not specified", MessageType.Warning);
}
else if (GUILayout.Button("Import Rig"))
{
ImportRig();
_rigTreeWidget.Refresh(_rigAsset.GetHierarchy());
}
_kToolbarWidget.Render();
serializedObject.ApplyModifiedProperties();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: af8d20a2cb744d08ba69726161fe4a40
timeCreated: 1704100975

View File

@@ -0,0 +1,208 @@
// Designed by KINEMATION, 2024.
using KINEMATION.KAnimationCore.Runtime.Rig;
using System.Collections.Generic;
using KINEMATION.KAnimationCore.Editor.Misc;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
namespace KINEMATION.KAnimationCore.Editor.Rig
{
public class StringListWidget
{
public List<string> list = new List<string>();
private ReorderableList _reorderableOptionsList;
public void Initialize(string listName)
{
_reorderableOptionsList = new ReorderableList(list, typeof(string), true,
true, true, true);
_reorderableOptionsList.drawHeaderCallback = (Rect rect) =>
{
EditorGUI.LabelField(rect, listName);
};
_reorderableOptionsList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
{
rect.height = EditorGUIUtility.singleLineHeight;
list[index] = EditorGUI.TextField(rect, list[index]);
};
_reorderableOptionsList.onAddCallback = (ReorderableList list) =>
{
this.list.Add("");
};
_reorderableOptionsList.onRemoveCallback = (ReorderableList list) =>
{
ReorderableList.defaultBehaviours.DoRemoveButton(list);
};
}
public void Render()
{
_reorderableOptionsList.DoLayoutList();
}
}
public class RigMappingWindow : EditorWindow
{
private KRig _rigAsset;
private GameObject _root;
private StringListWidget _entriesToAvoid;
public static RigMappingWindow CreateWindow()
{
var window = GetWindow<RigMappingWindow>(false, "Rig Mapping", true);
return window;
}
private void TraverseHierarchy(Transform root, ref KRigElementChain chain, KRig rig)
{
KRigElement element = rig.rigHierarchy.Find(item => item.name.Equals(root.name));
chain.elementChain.Add(element);
// Filter child bones from corrections, twists and IKs.
List<int> fkBoneIndexes = new List<int>();
for (int i = 0; i < root.childCount; i++)
{
string childName = root.GetChild(i).name.ToLower();
bool bSkipIteration = false;
foreach (var entry in _entriesToAvoid.list)
{
if(string.IsNullOrEmpty(entry)) continue;
if (childName.Contains(entry.ToLower()))
{
bSkipIteration = true;
break;
}
}
if (bSkipIteration)
{
continue;
}
fkBoneIndexes.Add(i);
}
// If no extra branches, traverse the old chain.
if (fkBoneIndexes.Count == 1)
{
TraverseHierarchy(root.GetChild(fkBoneIndexes[0]), ref chain, rig);
return;
}
// If we have branches, create new chains and start traversing them.
foreach (var boneIndex in fkBoneIndexes)
{
string chainName = root.GetChild(boneIndex).name;
KRigElementChain newChain = new KRigElementChain()
{
chainName = chainName,
elementChain = new List<KRigElement>()
};
rig.rigElementChains.Add(newChain);
TraverseHierarchy(root.GetChild(boneIndex), ref newChain, rig);
}
}
private void MapRigChains(GameObject root)
{
if (root == null)
{
Debug.LogWarning("RigMappingWindow: Selected GameObject is NULL!");
return;
}
KRigComponent rigComponent = root.GetComponent<KRigComponent>();
if (rigComponent == null)
{
rigComponent = root.AddComponent<KRigComponent>();
rigComponent.RefreshHierarchy();
}
if (_rigAsset == null)
{
_rigAsset = ScriptableObject.CreateInstance<KRig>();
}
else
{
_rigAsset.rigElementChains.Clear();
}
_rigAsset.ImportRig(rigComponent);
KRigElementChain newChain = new KRigElementChain()
{
chainName = root.name,
elementChain = new List<KRigElement>()
};
_rigAsset.rigElementChains.Add(newChain);
TraverseHierarchy(root.transform, ref newChain, _rigAsset);
if (EditorUtility.IsPersistent(_rigAsset))
{
EditorUtility.SetDirty(_rigAsset);
AssetDatabase.SaveAssetIfDirty(_rigAsset);
return;
}
Undo.RegisterCreatedObjectUndo(_rigAsset, "Create Rig Asset");
string path = $"{KEditorUtility.GetProjectActiveFolder()}/Rig_{root.transform.root.name}.asset";
AssetDatabase.CreateAsset(_rigAsset, AssetDatabase.GenerateUniqueAssetPath(path));
}
private void OnEnable()
{
_entriesToAvoid = new StringListWidget();
_entriesToAvoid.Initialize("Bone Names to Avoid");
_entriesToAvoid.list.Add("twist");
_entriesToAvoid.list.Add("correct");
_root = Selection.activeObject as GameObject;
}
public void OnGUI()
{
EditorGUILayout.HelpBox("If empty, a new asset will be created.", MessageType.Info);
_rigAsset = (KRig) EditorGUILayout.ObjectField("Rig Asset", _rigAsset, typeof(KRig),
false);
_root = (GameObject) EditorGUILayout.ObjectField("Root Bone", _root, typeof(GameObject),
true);
_entriesToAvoid.Render();
if (GUILayout.Button("Create Rig Mapping"))
{
MapRigChains(_root);
}
}
}
public class RigMappingMenu
{
private const string RigItemName = "GameObject/Auto Rig Mapping";
[MenuItem(RigItemName, true)]
private static bool ValidateCreateRigMapping()
{
return Selection.activeObject is GameObject;
}
[MenuItem(RigItemName)]
private static void CreateRigMapping()
{
var window = RigMappingWindow.CreateWindow();
window.Show();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 34bf35b5a2cd4145b620c133c2a54346
timeCreated: 1721131260

View File

@@ -0,0 +1,200 @@
// Designed by KINEMATION, 2024.
using KINEMATION.KAnimationCore.Editor.Misc;
using KINEMATION.KAnimationCore.Runtime.Rig;
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
using UnityEngine;
namespace KINEMATION.KAnimationCore.Editor.Rig
{
public delegate void OnItemClicked(KRigElement selection);
public delegate void OnSelectionChanged(KRigElement[] selectedItems);
public class RigTreeView : TreeView
{
public OnItemClicked onItemClicked;
public float singleRowHeight = 0f;
public bool useToggle;
private List<TreeViewItem> _treeItems;
private KRigElement[] _originalItems;
private bool[] _selectedItems;
public RigTreeView(TreeViewState state) : base(state)
{
_treeItems = new List<TreeViewItem>();
Reload();
}
public KRigElement[] GetToggledItems()
{
List<KRigElement> toggledItems = new List<KRigElement>();
int index = 0;
foreach (var element in _originalItems)
{
if (_selectedItems[index])
{
var newElement = element;
newElement.index = index;
toggledItems.Add(newElement);
}
index++;
}
return toggledItems.ToArray();
}
public void InitializeTreeItems(KRigElement[] hierarchy)
{
_treeItems.Clear();
int count = hierarchy.Length;
_originalItems = new KRigElement[count];
int depthOffset = useToggle ? 1 : 0;
for (int i = 0; i < count; i++)
{
_treeItems.Add(new TreeViewItem(i + 1, hierarchy[i].depth + depthOffset, hierarchy[i].name));
}
hierarchy.CopyTo(_originalItems, 0);
_selectedItems = new bool[count];
var selection = GetSelection();
foreach (var index in selection)
{
_selectedItems[index - 1] = true;
}
}
public void Filter(string query)
{
int depthOffset = useToggle ? 1 : 0;
_treeItems.Clear();
query = query.ToLower().Trim();
int count = _originalItems.Length;
for (int i = 0; i < count; i++)
{
if (string.IsNullOrEmpty(query))
{
_treeItems.Add(new TreeViewItem(i + 1, _originalItems[i].depth + depthOffset,
_originalItems[i].name));
continue;
}
if (!_originalItems[i].name.ToLower().Trim().Contains(query)) continue;
_treeItems.Add(new TreeViewItem(i + 1, depthOffset, _originalItems[i].name));
}
Reload();
}
protected override TreeViewItem BuildRoot()
{
// 0 is the root ID, -1 means the root has no parent
var root = new TreeViewItem { id = 0, depth = -1, displayName = "Root" };
// Utility method to setup the parent/children relationship
SetupParentsAndChildrenFromDepths(root, _treeItems);
return root;
}
protected override void RowGUI(RowGUIArgs args)
{
Color darkGrey = new Color(0.2f, 0.2f, 0.2f);
Color lightGrey = new Color(0.25f, 0.25f, 0.25f);
Color blue = new Color(115f / 255f, 147f / 255f, 179f / 255f, 0.25f);
bool isSelected = args.selected;
if (args.rowRect.Contains(Event.current.mousePosition)) isSelected = true;
var color = isSelected ? blue : args.row % 2 == 0 ? lightGrey : darkGrey;
EditorGUI.DrawRect(args.rowRect, color);
if (useToggle)
{
var rect = args.rowRect;
rect.width = rect.height;
bool prevToggle = _selectedItems[args.item.id - 1];
bool toggle = EditorGUI.Toggle(rect, prevToggle);
if (toggle != prevToggle)
{
// If this item is a part of a larger selection, update the status globally.
if (IsSelected(args.item.id))
{
var selection = GetSelection();
foreach (var selectedId in selection) _selectedItems[selectedId - 1] = toggle;
} // Otherwise, change this toggle only.
else _selectedItems[args.item.id - 1] = toggle;
}
}
singleRowHeight = rowHeight;
if (!useToggle)
{
Rect buttonRect = args.rowRect;
float indent = GetContentIndent(args.item);
buttonRect.x += indent;
if (GUI.Button(buttonRect, args.item.displayName, EditorStyles.label))
{
var element = _originalItems[args.item.id - 1];
element.index = args.item.id - 1;
onItemClicked?.Invoke(element);
}
return;
}
base.RowGUI(args);
}
}
public class RigTreeWidget
{
public RigTreeView rigTreeView = new RigTreeView(new TreeViewState());
public void Refresh(KRigElement[] hierarchy)
{
rigTreeView.InitializeTreeItems(hierarchy);
rigTreeView.Reload();
rigTreeView.ExpandAll();
}
public void Render()
{
float maxHeight = rigTreeView.singleRowHeight + rigTreeView.totalHeight;
float height = Mathf.Max(rigTreeView.singleRowHeight * 2f, maxHeight);
EditorGUILayout.BeginHorizontal();
Rect parentRect = GUILayoutUtility.GetRect(0f, 0f, 0f, height);
EditorGUILayout.EndHorizontal();
float padding = 7f;
GUI.Box(parentRect, "", EditorStyles.helpBox);
parentRect.x += padding;
parentRect.y += padding;
parentRect.width -= 2f * padding;
parentRect.height -= 2f * padding;
rigTreeView.OnGUI(parentRect);
}
}
}

View File

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

View File

@@ -0,0 +1,69 @@
using System.Collections.Generic;
using KINEMATION.KAnimationCore.Runtime.Rig;
using UnityEditor;
using UnityEngine;
namespace KINEMATION.KAnimationCore.Editor.Rig
{
public class RigWindow : EditorWindow
{
private OnItemClicked _onClicked;
private OnSelectionChanged _onSelectionChanged;
private Vector2 _scrollPosition;
private string _searchEntry = string.Empty;
private RigTreeWidget _rigTreeWidget;
private bool _useSelection = false;
public static void ShowWindow(KRigElement[] hierarchy, OnItemClicked onClicked,
OnSelectionChanged onSelectionChanged, bool useSelection, List<int> selection = null, string title = "Selection")
{
RigWindow window = CreateInstance<RigWindow>();
window._useSelection = useSelection;
window._onClicked = onClicked;
window._onSelectionChanged = onSelectionChanged;
window.titleContent = new GUIContent(title);
window._rigTreeWidget = new RigTreeWidget
{
rigTreeView =
{
useToggle = useSelection,
onItemClicked = window.OnItemClicked
}
};
if (selection != null)
{
window._rigTreeWidget.rigTreeView.SetSelection(selection);
}
window._rigTreeWidget.Refresh(hierarchy);
window.minSize = new Vector2(450f, 550f);
window.ShowAuxWindow();
}
private void OnItemClicked(KRigElement selection)
{
_onClicked.Invoke(selection);
Close();
}
private void OnGUI()
{
EditorGUILayout.BeginHorizontal(GUI.skin.FindStyle("Toolbar"));
_searchEntry = EditorGUILayout.TextField(_searchEntry, EditorStyles.toolbarSearchField);
EditorGUILayout.EndHorizontal();
_rigTreeWidget.rigTreeView.Filter(_searchEntry);
_rigTreeWidget.Render();
}
private void OnDisable()
{
if (_useSelection) _onSelectionChanged?.Invoke(_rigTreeWidget.rigTreeView.GetToggledItems());
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ebe01bfda1354888806c73d35b2e8af0
timeCreated: 1704267364