Files
JJBB/Assets/Project/Script/Common/Utilities/Editor/MecanimTransitionSet.cs
2024-08-23 15:49:34 +08:00

356 lines
19 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Author: Blastom
// 按照Animation.txt表重置动画机全部动画转换效果
// 新版动画系统将会完全使用动画机内部参数进行转换
using Games.AnimationModule;
using Games.LogicObj;
using Games.SkillModle;
using GCGame.Table;
using System;
using System.Collections.Generic;
using Module.Log;
using UnityEditor;
using UnityEditor.Animations;
using UnityEngine;
public class MecanimTransitionSet
{
private readonly List<Tab_Animation> _animationData;
// 连段普攻动画记录
private readonly List<AttackComboData> _comboList;
private readonly string[] _controllers;
private readonly List<string> _errorList;
private string _controllerPath;
private int _index;
private MecanimTransitionSet()
{
// 获得需要处理的动画机
var paths = EditorCommonUtility.GetPathsFromSelectByExtension(".controller");
if (paths.Count > 0)
{
_controllers = paths.ToArray();
_animationData = EditorTableManager.GetTable<Tab_Animation>();
// 记录所有可以跳过前摇的普攻连段配置 - 普攻将额外配置一条带Offset的Transition
var skillExData = EditorTableManager.GetTable<Tab_SkillEx>();
var skillBaseData = EditorTableManager.GetTable<Tab_SkillBase>();
_comboList = new List<AttackComboData>();
for (var i = 0; i < skillExData.Count; i++)
{
var skillEx = skillExData[i];
var skillBase = skillBaseData.Find(a => a.Id == skillEx.BaseId);
if (skillBase != null && skillBase.SkillClass.ContainFlag((int) SKILLCLASS.SIMPLEATTACK))
{
if (_comboList.Find(a => a.Contains(skillEx)) == null)
{
var combo = new AttackComboData(skillEx, skillExData, _animationData);
if (combo.attackComboList.Count > 1 && combo.NoEmptyAnimation())
_comboList.Add(combo);
}
}
}
_errorList = new List<string>();
}
}
// 丢失动画的状态,会试图修复状态;动画名称不对应动画状态,会试图修改动画名称;
[MenuItem("Assets/Animation/配置Mecanim动画机转换")]
public static void RemovAnimatoreTransitions()
{
var animatorFix = new MecanimTransitionSet();
animatorFix.Start();
}
private void Start()
{
if (_controllers.Length > 0 && _animationData != null)
EditorApplication.update = Update;
}
private void Stop()
{
EditorUtility.ClearProgressBar();
EditorApplication.update = null;
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
EditorUtility.UnloadUnusedAssetsImmediate(true);
for (var i = 0; i < _errorList.Count; i++)
LogModule.ErrorLog(_errorList[i]);
}
private void Update()
{
var cancel = false;
// 每一帧处理一个State使移动后的路径能够正常赋值
if (_index > _controllers.Length - 1)
{
cancel = true;
LogModule.WarningLog("******** 动画机转换全部完成 ********");
}
else
_controllerPath = _controllers[_index];
_index++;
if (!cancel)
cancel = EditorUtility.DisplayCancelableProgressBar("自动修改动画机转换", _controllerPath,
_index / (float) _controllers.Length);
if (cancel)
Stop();
else
{
var animatorController = AssetDatabase.LoadAssetAtPath<AnimatorController>(_controllerPath);
if (animatorController != null)
{
// 清空当前Controller的Parameters然后重建
animatorController.parameters = new AnimatorControllerParameter[0];
for (var i = 0; i < animatorController.layers.Length; i++)
{
// AnyState跳转到新状态的功能
var anyTransitionList = new List<AnimatorStateTransition>();
// 状态机的状态列表
var layer = animatorController.layers[i];
var states = layer.stateMachine.states;
if (layer.stateMachine.behaviours.Find(a => a is AnimationStateEvent) == null)
layer.stateMachine.AddStateMachineBehaviour<AnimationStateEvent>();
// 构造各状态的特征
for (var j = 0; j < states.Length; j++)
{
var state = states[j];
if (state.state.motion == null)
_errorList.Add(string.Format("动画机{0}上状态{1}没有动画!", _controllerPath, state.state.name));
else
{
// 动画机状态统一使用小写格式
state.state.name = state.state.name.ToLower();
var currentAnim = _animationData.Find(a =>
string.Equals(a.AnimName, state.state.name, StringComparison.CurrentCultureIgnoreCase));
if (currentAnim != null)
{
state.state.transitions = new AnimatorStateTransition[0];
//var stateTransitionList = new List<AnimatorStateTransition>();
// 如果当前状态能够在连段列表中被找到,就试图创建额外的连段转换
var nextAttack = GetNextAnim(state.state.name);
if (nextAttack != null)
{
var nextState = states.Find(a => string.Equals(a.state.name,
nextAttack.second.AnimName,
StringComparison.CurrentCultureIgnoreCase));
if (nextState.state != null)
{
// 动画系统一律使用小写传递,防止出现错误
var nextTransitionName =
AnimationLogic.GetTransitionName(currentAnim, nextAttack.second);
animatorController.AddParameter(nextTransitionName,
AnimatorControllerParameterType.Trigger);
var animClip = nextState.state.motion as AnimationClip;
var duration = animClip == null ? nextState.state.motion.averageDuration : animClip.length;
var attackTransition = new AnimatorStateTransition
{
destinationState = nextState.state,
interruptionSource = TransitionInterruptionSource.Destination,
duration = 0f,//currentAnim.SwitchFromTime,
canTransitionToSelf = false,
exitTime = 1f,
hasExitTime = false,
orderedInterruption = false,
offset = nextAttack.first.CradleTime.ToClientTime() /
duration
};
attackTransition.AddCondition(AnimatorConditionMode.If, default(float),
nextTransitionName);
anyTransitionList.Add(attackTransition);
}
else
nextAttack = null;
}
if (nextAttack == null)
state.state.transitions = new AnimatorStateTransition[0];
// 建立任意状态到当前状态的转换
var anyTransitionName = state.state.name.ToLower();
animatorController.AddParameter(anyTransitionName,
AnimatorControllerParameterType.Trigger);
var anyTransistion = new AnimatorStateTransition
{
destinationState = state.state,
// 统一允许重置 - 出现问题可以正常清掉Trigger
canTransitionToSelf = true,
hasExitTime = false,
orderedInterruption = false,
interruptionSource = TransitionInterruptionSource.Destination,
duration = currentAnim.SwitchToTime,
};
anyTransistion.AddCondition(AnimatorConditionMode.If, 0f, anyTransitionName);
anyTransitionList.Add(anyTransistion);
// 建立自己到ExitTime动画的连接
if (currentAnim.NextAnimID > -1)
{
var nextAnim = _animationData.Find(a => a.AnimID == currentAnim.NextAnimID);
if (nextAnim != null)
{
var nextState = states.Find(a => string.Equals(a.state.name, nextAnim.AnimName,
StringComparison.CurrentCultureIgnoreCase));
// 特殊如果目标动画不存在回滚到Idle动画
if (nextState.state == null)
{
nextAnim = _animationData.Find(a => a.AnimID == Obj_Character.idleAnimId);
nextState = states.Find(a => string.Equals(a.state.name, nextAnim.AnimName,
StringComparison.CurrentCultureIgnoreCase));
}
if (nextState.state == null)
_errorList.Add(string.Format("动画机{0}中没有默认id={1}的动画!", _controllerPath,
Obj_Character.idleAnimId));
else
{
var transition = state.state.AddTransition(nextState.state);
transition.interruptionSource = TransitionInterruptionSource.Destination;
transition.duration = currentAnim.SwitchFromTime;
transition.canTransitionToSelf = true;
transition.hasExitTime = true;
transition.orderedInterruption = true;
transition.exitTime = 1f - currentAnim.SwitchFromTime;
//var nextTransition = new AnimatorStateTransition
//{
// destinationState = nextState.state,
// interruptionSource = TransitionInterruptionSource.Destination,
// duration = currentAnim.SwitchFromTime,
// canTransitionToSelf = true,
// hasExitTime = true,
// orderedInterruption = true,
// exitTime = 1f - currentAnim.SwitchFromTime
//};
//stateTransitionList.Add(nextTransition);
}
}
else
_errorList.Add(string.Format("动画数据 {0}关联了不存在的NextAnimId {1}",
currentAnim.AnimID,
currentAnim.NextAnimID));
}
// 特殊处理尸体动画
if (currentAnim.AnimID == Obj_Character.corpseAnimId)
{
animatorController.AddParameter(Obj_Character.corpseTransition,
AnimatorControllerParameterType.Trigger);
var corpseTransition = new AnimatorStateTransition
{
destinationState = state.state,
interruptionSource = TransitionInterruptionSource.None,
duration = 0f,
offset = 1f,
canTransitionToSelf = true,
hasExitTime = false,
orderedInterruption = false
};
corpseTransition.AddCondition(AnimatorConditionMode.If, 0f,
Obj_Character.corpseTransition);
anyTransitionList.Add(corpseTransition);
}
//var oldStateTransitions = state.state.transitions;
//for (var t = 0; t < oldStateTransitions.Length; t++)
// state.state.RemoveTransition(oldStateTransitions[i]);
//state.state.transitions = new AnimatorStateTransition[0];
//for (var t = 0; t < stateTransitionList.Count; t++)
// state.state.AddTransition(stateTransitionList[i]);
}
else
_errorList.Add(string.Format("动画机{0}中名为{1}的动画状态无法找到对应动画数据!", _controllerPath,
state.state.name));
}
//var oldAnyTransitions = layer.stateMachine.anyStateTransitions;
//for (var t = 0; t < oldAnyTransitions.Length; t++)
// layer.stateMachine.RemoveAnyStateTransition(oldAnyTransitions[t]);
layer.stateMachine.anyStateTransitions = new AnimatorStateTransition[0];
for (var t = 0; t < anyTransitionList.Count; t++)
{
var anyTransition = layer.stateMachine.AddAnyStateTransition(anyTransitionList[t].destinationState);
anyTransition.canTransitionToSelf = anyTransitionList[t].canTransitionToSelf;
anyTransition.duration = anyTransitionList[t].duration;
anyTransition.hasExitTime = anyTransitionList[t].hasExitTime;
anyTransition.orderedInterruption = anyTransitionList[t].orderedInterruption;
anyTransition.interruptionSource = anyTransitionList[t].interruptionSource;
anyTransition.offset = anyTransitionList[t].offset;
anyTransition.exitTime = anyTransitionList[t].exitTime;
anyTransition.conditions = anyTransitionList[t].conditions;
}
}
}
//EditorUtility.SetDirty(animatorController);
}
}
}
private MyTuple<Tab_SkillEx, Tab_Animation> GetNextAnim(string motionName)
{
MyTuple<Tab_SkillEx, Tab_Animation> result = null;
for (var i = 0; i < _comboList.Count; i++)
{
result = _comboList[i].GetNextAnim(motionName);
if (result != null)
break;
}
return result;
}
/// <summary>
/// 连段普攻数据
/// </summary>
private class AttackComboData
{
public readonly List<MyTuple<Tab_SkillEx, Tab_Animation>> attackComboList;
public AttackComboData(Tab_SkillEx firstAttack, List<Tab_SkillEx> skillList, List<Tab_Animation> animList)
{
attackComboList = new List<MyTuple<Tab_SkillEx, Tab_Animation>>();
var current = firstAttack;
// 获得firstAttack之前的连段
while (current != null)
{
attackComboList.Insert(0, new MyTuple<Tab_SkillEx, Tab_Animation>{ first = current });
current = skillList.Find(a => a.NextSkillId == current.SkillExID);
}
// 获得firstAttack之后的连段
current = firstAttack;
while (current != null)
{
current = skillList.Find(a => a.SkillExID == current.NextSkillId);
if (current != null)
attackComboList.Add(new MyTuple<Tab_SkillEx, Tab_Animation>{ first = current });
}
for (var i = 0; i < attackComboList.Count; i++)
attackComboList[i].second = animList.Find(a => a.AnimID == attackComboList[i].first.SatrtMotionId);
}
public bool NoEmptyAnimation()
{
var result = true;
for (var i = 0; i < attackComboList.Count; i++)
if (attackComboList[i].second == null)
{
result = false;
// 过滤掉对id小于等于0情况的报错。
if (attackComboList[i].first.SatrtMotionId > 0)
LogModule.WarningLog(string.Format("技能{0}没有对应的动画id={1}的配置!", attackComboList[i].first.SkillExID, attackComboList[i].first.SatrtMotionId));
}
return result;
}
public bool Contains(Tab_SkillEx skillEx)
{
var result = false;
for (var i = 0; i < attackComboList.Count; i++)
if (skillEx.SkillExID == attackComboList[i].first.SkillExID)
{
result = true;
break;
}
return result;
}
public MyTuple<Tab_SkillEx, Tab_Animation> GetNextAnim(string motionName)
{
MyTuple<Tab_SkillEx, Tab_Animation> result = null;
var index = attackComboList.FindIndex(a => string.Equals(a.second.AnimName, motionName, StringComparison.CurrentCultureIgnoreCase));
if (index >= 0 && index < attackComboList.Count - 1)
result = attackComboList[index + 1];
return result;
}
}
}