This commit is contained in:
SoulliesOfficial
2026-01-03 18:19:39 -05:00
parent 3bcd7c1cf8
commit 33b1795c1f
7387 changed files with 2762819 additions and 716926 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4e65e8868feca6d4aa33663566b71707
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Sirenix.OdinInspector;
using SLSFramework.General;
using SLSUtilities.FunctionalAnimation;
using UnityEngine;
@@ -12,6 +13,8 @@ namespace Cielonos.MainGame.Characters
public partial class AnimationSubcontrollerBase : SubcontrollerBase<CharacterBase>
{
public Animator animator;
public AnimatorStateMapper mapper;
public Dictionary<DisruptionType, bool> disruptionStatus;
@@ -34,6 +37,8 @@ namespace Cielonos.MainGame.Characters
defaultAnimationGroup?.SetUp(this);
disruptionStatus = new Dictionary<DisruptionType, bool>()
{
{ DisruptionType.ForcedAction , false},
{ DisruptionType.ForcedExternal , false},
{ DisruptionType.NormalExternal, false },
{ DisruptionType.NormalAction, false },
{ DisruptionType.Movement, false }
@@ -77,6 +82,22 @@ namespace Cielonos.MainGame.Characters
disruptionStatus[DisruptionType.NormalExternal] = currentIntervals.Any(interval => interval.intervalType == IntervalType.ExternalDisruption);
disruptionStatus[DisruptionType.NormalAction] = currentIntervals.Any(interval => interval.intervalType == IntervalType.ActionDisruption);
disruptionStatus[DisruptionType.Movement] = currentIntervals.Any(interval => interval.intervalType == IntervalType.MovementDisruption);
if (fullBodyFuncAnimSm.currentRuntimeFuncAnim.HasIntervalType(IntervalType.ForcedActionDisruption))
{
disruptionStatus[DisruptionType.ForcedAction] = currentIntervals.Any(interval => interval.intervalType == IntervalType.ForcedActionDisruption);
}
else
{
disruptionStatus[DisruptionType.ForcedAction] = true;
}
if (fullBodyFuncAnimSm.currentRuntimeFuncAnim.HasIntervalType(IntervalType.ForcedExternalDisruption))
{
disruptionStatus[DisruptionType.ForcedExternal] = currentIntervals.Any(interval => interval.intervalType == IntervalType.ForcedExternalDisruption);
}
else
{
disruptionStatus[DisruptionType.ForcedExternal] = true;
}
isDuringRootMotion = currentIntervals.Any(interval => interval.intervalType == IntervalType.RootMotion);
}
}
@@ -245,25 +266,26 @@ namespace Cielonos.MainGame.Characters
if (direction == default || !animator.HasState(fullBodyActionIndex, Animator.StringToHash(getHitAnim)))
{
animator.CrossFade(getHitFrontAnim, normalizedTransitionDuration, fullBodyActionIndex, 0);
animDuration = mapper.GetClip(getHitFrontAnim).length;
}
else
{
animator.CrossFade(getHitAnim, normalizedTransitionDuration, fullBodyActionIndex, 0);
animDuration = mapper.GetClip(getHitAnim).length;
}
animDuration = animator.GetCurrentAnimatorStateInfo(fullBodyActionIndex).length;
}
else if (animator.HasState(fullBodyActionIndex, Animator.StringToHash(getHitAnimPrefix)))
{
getHitAnim = getHitAnimPrefix;
animator.CrossFade(getHitAnim, normalizedTransitionDuration, fullBodyActionIndex, 0);
animDuration = animator.GetCurrentAnimatorStateInfo(fullBodyActionIndex).length;
animDuration = mapper.GetClip(getHitAnim).length;
}
else
{
if (animator.HasState(fullBodyActionIndex, Animator.StringToHash("GetHit")))
{
animator.CrossFade("GetHit", normalizedTransitionDuration, fullBodyActionIndex, 0);
animDuration = animator.GetCurrentAnimatorStateInfo(fullBodyActionIndex).length;
animDuration = mapper.GetClip("GetHit").length;
}
else
{

View File

@@ -0,0 +1,160 @@
using UnityEngine;
using System.Collections.Generic;
using Sirenix.OdinInspector; // 引入 Odin 命名空间
#if UNITY_EDITOR
using UnityEditor.Animations; // 仅编辑器下引用用于解析Controller
#endif
namespace Cielonos.MainGame.Characters
{
public class AnimatorStateMapper
{
// --- 配置区域 ---
[Title("Bake Settings")]
[SerializeField, LabelText("Target Animator")]
private Animator _targetAnimatorForBake;
[TableList(ShowIndexLabels = true), Searchable] // Odin: 列表显示为表格,且支持搜索
[SerializeField]
private List<StateClipPair> _mappings = new List<StateClipPair>();
// --- 运行时缓存 ---
private Dictionary<string, AnimationClip> _clipDict;
private bool _isInitialized = false;
// --- 简单的数据结构 ---
[System.Serializable]
public struct StateClipPair
{
[ReadOnly] // 防止手动误改建议通过Bake生成
public string stateName;
public AnimationClip clip;
}
public AnimatorStateMapper()
{
_mappings = new List<StateClipPair>();
}
// ========================================================================
// Runtime API (供宿主调用)
// ========================================================================
/// <summary>
/// 必须在宿主的 Awake 中调用此方法构建索引
/// </summary>
public void Initialize()
{
if (_isInitialized) return;
_clipDict = new Dictionary<string, AnimationClip>(_mappings.Count);
foreach (var pair in _mappings)
{
if (!string.IsNullOrEmpty(pair.stateName) && pair.clip != null)
{
// 防止重复Key报错以后面的覆盖前面的或者你可以选择忽略
_clipDict[pair.stateName] = pair.clip;
}
}
_isInitialized = true;
}
public AnimationClip GetClip(string stateName)
{
if (!_isInitialized) Initialize();
if (_clipDict.TryGetValue(stateName, out var clip))
{
return clip;
}
Debug.LogWarning($"[AnimatorStateMapper] 未找到 State: '{stateName}' 对应的 Clip。请检查是否已 Bake 或 State 名字是否正确。");
return null;
}
public float GetClipLength(string stateName)
{
var clip = GetClip(stateName);
return clip != null ? clip.length : 0f;
}
// ========================================================================
// Editor Baking Logic (Odin Button)
// ========================================================================
#if UNITY_EDITOR
public void Bake(Animator animator)
{
_targetAnimatorForBake = animator;
var controller = _targetAnimatorForBake.runtimeAnimatorController as AnimatorController;
// 处理 Override Controller 的情况
if (controller == null && _targetAnimatorForBake.runtimeAnimatorController is AnimatorOverrideController overrideCtrl)
{
controller = overrideCtrl.runtimeAnimatorController as AnimatorController;
}
if (controller == null)
{
Debug.LogError("Animator 上未找到有效的 AnimatorController (或 AnimatorOverrideController) 资源!");
return;
}
_mappings.Clear();
int count = 0;
// 递归遍历所有的 Layer 和 SubStateMachine
foreach (var layer in controller.layers)
{
count += RecursiveProcessStateMachine(layer.stateMachine);
}
Debug.Log($"<color=green>Bake 完成!共提取了 {count} 个 State-Clip 映射。</color>");
}
private int RecursiveProcessStateMachine(AnimatorStateMachine stateMachine)
{
int count = 0;
// 1. 遍历当前层级的 State
foreach (var childState in stateMachine.states)
{
var state = childState.state;
var motion = state.motion;
// 仅处理直接引用 AnimationClip 的情况
if (motion is AnimationClip clip)
{
_mappings.Add(new StateClipPair { stateName = state.name, clip = clip });
count++;
}
// TODO: 如果需要支持 BlendTree可以在这里扩展逻辑
// else if (motion is BlendTree tree) { ... }
}
// 2. 递归遍历子状态机 (Sub-State Machines)
foreach (var childMachine in stateMachine.stateMachines)
{
count += RecursiveProcessStateMachine(childMachine.stateMachine);
}
return count;
}
#endif
}
#if UNITY_EDITOR
public partial class AnimationSubcontrollerBase
{
[Button]
private void MapperBake()
{
mapper ??= new AnimatorStateMapper();
mapper.Bake(animator);
}
}
#endif
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 0ea4943f9bb92174fbf6dc844cadf580

View File

@@ -23,6 +23,9 @@ namespace Cielonos.MainGame.Characters
[Title("Custom Parts")]
public Dictionary<string, Transform> customBodyParts;
[Title("Attachments")]
public Dictionary<string, GameObject> attachments;
}
public partial class BodyPartsSubcontroller
@@ -48,4 +51,14 @@ namespace Cielonos.MainGame.Characters
return customBodyParts.GetValueOrDefault(partName);
}
}
public partial class BodyPartsSubcontroller
{
public AuxiliaryDrone AuxiliaryDrone => GetAttachment("AuxiliaryDrone")?.GetComponent<AuxiliaryDrone>();
public GameObject GetAttachment(string attachmentName)
{
return attachments.GetValueOrDefault(attachmentName);
}
}
}

View File

@@ -11,6 +11,8 @@ namespace Cielonos.MainGame.Characters
private void Update()
{
if (feedbacks == null) return;
foreach (var feedbackUnit in feedbacks.Values)
{
float timeScaleMultiplier = owner.selfTimeSm.TimeScale;

View File

@@ -56,9 +56,7 @@ namespace Cielonos.MainGame.Characters
if (owner is Player player)
{
dashRotation.y = player.viewSc.lockTargetModule.isUsingLockTargetCamera
? player.viewSc.cameraRotationSm.cinemachineEndLockYaw + angle
: player.viewSc.cameraRotationSm.cinemachineTargetYaw + angle;
dashRotation.y = player.viewSc.playerCamera.transform.eulerAngles.y + angle;
}
else
{

View File

@@ -5,7 +5,7 @@ using UnityEngine;
namespace Cielonos.MainGame.Characters
{
public class ReactionSubcontroller : SubcontrollerBase<CharacterBase>
public partial class ReactionSubcontroller : SubcontrollerBase<CharacterBase>
{
public Dictionary<BreakthroughType, bool> originalBreakthroughResistances;
public Dictionary<BreakthroughType, bool> breakthroughResistances;
@@ -20,8 +20,8 @@ namespace Cielonos.MainGame.Characters
{
{ BreakthroughType.None, true },
{ BreakthroughType.Weak, true },
{ BreakthroughType.Medium, true },
{ BreakthroughType.Heavy, true },
{ BreakthroughType.Medium, false },
{ BreakthroughType.Heavy, false },
{ BreakthroughType.Disruption, false },
{ BreakthroughType.Forced, false },
{ BreakthroughType.Unstoppable, false },
@@ -31,8 +31,8 @@ namespace Cielonos.MainGame.Characters
{
{ BreakthroughType.None, true },
{ BreakthroughType.Weak, true },
{ BreakthroughType.Medium, true },
{ BreakthroughType.Heavy, true },
{ BreakthroughType.Medium, false },
{ BreakthroughType.Heavy, false },
{ BreakthroughType.Disruption, false },
{ BreakthroughType.Forced, false },
{ BreakthroughType.Unstoppable, false },
@@ -61,4 +61,18 @@ namespace Cielonos.MainGame.Characters
}
}
}
public partial class ReactionSubcontroller
{
public void InitializeResistances(EnemyRank enemyRank)
{
if (enemyRank == EnemyRank.Nexus || enemyRank == EnemyRank.Core)
{
originalBreakthroughResistances[BreakthroughType.Medium] = true;
originalBreakthroughResistances[BreakthroughType.Heavy] = true;
breakthroughResistances[BreakthroughType.Medium] = true;
breakthroughResistances[BreakthroughType.Heavy] = true;
}
}
}
}