212 lines
8.6 KiB
C#
212 lines
8.6 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEditor;
|
|
using UnityEditor.Animations;
|
|
|
|
public static class SetupPlayerAnimatorLayers
|
|
{
|
|
private const string ControllerPath = "Assets/mainhero/PlayerAnimator.controller";
|
|
private const string UpperMaskPath = "Assets/Kevin Iglesias/Human Animations/Models/Avatar Masks/Human Body Upper Mask.mask";
|
|
private const string IdleFbxPath = "Assets/Kevin Iglesias/Human Animations/Animations/Male/Idles/HumanM@Idle01.fbx";
|
|
private const string WalkFbxPath = "Assets/Kevin Iglesias/Human Animations/Animations/Male/Movement/Walk/HumanM@Walk01_Forward.fbx";
|
|
private const string SprintFbxPath = "Assets/Kevin Iglesias/Human Animations/Animations/Male/Movement/Sprint/HumanM@Sprint01_Forward.fbx";
|
|
private const string AttackFbxPath = "Assets/Brawler Animations/Animations/Atk_P_1.fbx";
|
|
private const string GunAimFbxPath = "Assets/Kevin Iglesias/Human Animations/Animations/Male/Combat/Gun/HumanM@Gun_Aim01.fbx";
|
|
private const string GunShootFbxPath = "Assets/Kevin Iglesias/Human Animations/Animations/Male/Combat/Gun/HumanM@Gun_Aim01_Shoot01.fbx";
|
|
private const string GunReloadFbxPath = "Assets/Kevin Iglesias/Human Animations/Animations/Male/Combat/Gun/HumanM@Gun_Reload01.fbx";
|
|
|
|
public static AnimationClip FindClip(string fbxPath)
|
|
{
|
|
var all = AssetDatabase.LoadAllAssetsAtPath(fbxPath);
|
|
foreach (var a in all)
|
|
{
|
|
if (a is AnimationClip c && !c.name.StartsWith("__preview"))
|
|
{
|
|
return c;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
[MenuItem("Tools/Rebuild Player Animator Controller")]
|
|
public static string Rebuild()
|
|
{
|
|
var controller = AssetDatabase.LoadAssetAtPath<AnimatorController>(ControllerPath);
|
|
if (controller == null)
|
|
{
|
|
controller = AnimatorController.CreateAnimatorControllerAtPath(ControllerPath);
|
|
}
|
|
|
|
// 1. Параметры
|
|
AddOrUpdateParam(controller, "Speed", AnimatorControllerParameterType.Float, 0f);
|
|
AddOrUpdateParam(controller, "IsMoving", AnimatorControllerParameterType.Bool, false);
|
|
AddOrUpdateParam(controller, "Attack", AnimatorControllerParameterType.Trigger);
|
|
AddOrUpdateParam(controller, "HasGun", AnimatorControllerParameterType.Bool, false);
|
|
AddOrUpdateParam(controller, "Shoot", AnimatorControllerParameterType.Trigger);
|
|
AddOrUpdateParam(controller, "Reload", AnimatorControllerParameterType.Trigger);
|
|
|
|
// 2. Base Layer
|
|
var baseSm = controller.layers[0].stateMachine;
|
|
// Очищаем существующие состояния
|
|
for (int i = baseSm.states.Length - 1; i >= 0; i--)
|
|
{
|
|
baseSm.RemoveState(baseSm.states[i].state);
|
|
}
|
|
for (int i = baseSm.anyStateTransitions.Length - 1; i >= 0; i--)
|
|
{
|
|
baseSm.RemoveAnyStateTransition(baseSm.anyStateTransitions[i]);
|
|
}
|
|
|
|
// Создаем BlendTree Locomotion
|
|
var locomotionState = baseSm.AddState("Locomotion", new Vector3(250, 50, 0));
|
|
var blendTree = new BlendTree
|
|
{
|
|
name = "Locomotion",
|
|
blendType = BlendTreeType.Simple1D,
|
|
blendParameter = "Speed",
|
|
useAutomaticThresholds = false
|
|
};
|
|
AssetDatabase.AddObjectToAsset(blendTree, controller);
|
|
|
|
var idleClip = FindClip(IdleFbxPath);
|
|
var walkClip = FindClip(WalkFbxPath);
|
|
var sprintClip = FindClip(SprintFbxPath);
|
|
|
|
blendTree.AddChild(idleClip, 0f);
|
|
blendTree.AddChild(walkClip, 1.5f);
|
|
blendTree.AddChild(walkClip, 3.0f);
|
|
blendTree.AddChild(sprintClip, 6.0f);
|
|
|
|
locomotionState.motion = blendTree;
|
|
baseSm.defaultState = locomotionState;
|
|
|
|
// Создаем Melee Attack State
|
|
var attackClip = FindClip(AttackFbxPath);
|
|
if (attackClip != null)
|
|
{
|
|
var attackState = baseSm.AddState("Attack", new Vector3(250, 150, 0));
|
|
attackState.motion = attackClip;
|
|
attackState.speed = 1.15f;
|
|
|
|
var toAttack = baseSm.AddAnyStateTransition(attackState);
|
|
toAttack.AddCondition(AnimatorConditionMode.If, 0f, "Attack");
|
|
toAttack.duration = 0.08f;
|
|
toAttack.canTransitionToSelf = false;
|
|
|
|
var fromAttack = attackState.AddTransition(locomotionState);
|
|
fromAttack.hasExitTime = true;
|
|
fromAttack.exitTime = 0.85f;
|
|
fromAttack.duration = 0.15f;
|
|
}
|
|
|
|
// 3. UpperBody Layer (для удержания оружия, выстрела, перезарядки)
|
|
var upperMask = AssetDatabase.LoadAssetAtPath<AvatarMask>(UpperMaskPath);
|
|
if (upperMask == null)
|
|
{
|
|
Debug.LogError("[SetupPlayerAnimatorLayers] Upper body mask not found!");
|
|
}
|
|
|
|
// Удаляем слой UpperBody если уже существовал
|
|
for (int i = controller.layers.Length - 1; i >= 1; i--)
|
|
{
|
|
controller.RemoveLayer(i);
|
|
}
|
|
|
|
controller.AddLayer("UpperBody Layer");
|
|
var layers = controller.layers;
|
|
var upperLayer = layers[1];
|
|
upperLayer.defaultWeight = 0f; // Управляется PlayerGun скриптом
|
|
upperLayer.blendingMode = AnimatorLayerBlendingMode.Override;
|
|
upperLayer.avatarMask = upperMask;
|
|
|
|
var upperSm = upperLayer.stateMachine;
|
|
var emptyState = upperSm.AddState("Empty", new Vector3(250, 50, 0));
|
|
emptyState.motion = null;
|
|
upperSm.defaultState = emptyState;
|
|
|
|
var gunAimClip = FindClip(GunAimFbxPath);
|
|
var gunShootClip = FindClip(GunShootFbxPath);
|
|
var gunReloadClip = FindClip(GunReloadFbxPath);
|
|
|
|
if (gunAimClip != null)
|
|
{
|
|
var gunAimState = upperSm.AddState("Gun_Aim", new Vector3(250, 150, 0));
|
|
gunAimState.motion = gunAimClip;
|
|
|
|
// Empty <-> Gun_Aim
|
|
var toAim = emptyState.AddTransition(gunAimState);
|
|
toAim.AddCondition(AnimatorConditionMode.If, 0f, "HasGun");
|
|
toAim.duration = 0.15f;
|
|
toAim.hasExitTime = false;
|
|
|
|
var toEmpty = gunAimState.AddTransition(emptyState);
|
|
toEmpty.AddCondition(AnimatorConditionMode.IfNot, 0f, "HasGun");
|
|
toEmpty.duration = 0.15f;
|
|
toEmpty.hasExitTime = false;
|
|
|
|
// Gun_Aim <-> Shoot
|
|
if (gunShootClip != null)
|
|
{
|
|
var shootState = upperSm.AddState("Shoot", new Vector3(500, 100, 0));
|
|
shootState.motion = gunShootClip;
|
|
|
|
var toShoot = gunAimState.AddTransition(shootState);
|
|
toShoot.AddCondition(AnimatorConditionMode.If, 0f, "Shoot");
|
|
toShoot.duration = 0.05f;
|
|
toShoot.hasExitTime = false;
|
|
|
|
var fromShoot = shootState.AddTransition(gunAimState);
|
|
fromShoot.hasExitTime = true;
|
|
fromShoot.exitTime = 0.8f;
|
|
fromShoot.duration = 0.1f;
|
|
}
|
|
|
|
// Gun_Aim <-> Reload
|
|
if (gunReloadClip != null)
|
|
{
|
|
var reloadState = upperSm.AddState("Reload", new Vector3(500, 200, 0));
|
|
reloadState.motion = gunReloadClip;
|
|
|
|
var toReload = gunAimState.AddTransition(reloadState);
|
|
toReload.AddCondition(AnimatorConditionMode.If, 0f, "Reload");
|
|
toReload.duration = 0.1f;
|
|
toReload.hasExitTime = false;
|
|
|
|
var fromReload = reloadState.AddTransition(gunAimState);
|
|
fromReload.hasExitTime = true;
|
|
fromReload.exitTime = 0.9f;
|
|
fromReload.duration = 0.15f;
|
|
}
|
|
}
|
|
|
|
controller.layers = layers;
|
|
|
|
EditorUtility.SetDirty(controller);
|
|
AssetDatabase.SaveAssets();
|
|
|
|
Debug.Log("[SetupPlayerAnimatorLayers] Controller successfully rebuilt with Base and UpperBody layers!");
|
|
return "SUCCESS";
|
|
}
|
|
|
|
private static void AddOrUpdateParam(AnimatorController ctrl, string name, AnimatorControllerParameterType type, object defVal = null)
|
|
{
|
|
foreach (var p in ctrl.parameters)
|
|
{
|
|
if (p.name == name) return;
|
|
}
|
|
ctrl.AddParameter(name, type);
|
|
if (defVal != null)
|
|
{
|
|
for (int i = 0; i < ctrl.parameters.Length; i++)
|
|
{
|
|
if (ctrl.parameters[i].name == name)
|
|
{
|
|
if (defVal is float f) ctrl.parameters[i].defaultFloat = f;
|
|
else if (defVal is bool b) ctrl.parameters[i].defaultBool = b;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|