您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 

253 行
10 KiB

using System;
using System.Collections.Generic;
using Unity.Entities;
using UnityEngine;
using UnityEngine.Animations;
using UnityEngine.Playables;
[CreateAssetMenu(fileName = "StateSelector", menuName = "FPS Sample/Animation/AnimGraph/StateSelector")]
public class AnimGraph_StateSelector : AnimGraphAsset
{
// TODO remove this and use character locomotion state
public enum CharacterAnimationState
{
Stand,
Run,
Jump,
InAir,
Dead,
NumStates
}
[Serializable]
public struct TransitionDefinition
{
public CharacterAnimationState sourceState;
public float transtionTime;
}
[Serializable]
public struct ControllerDefinition
{
public CharacterAnimationState animationState;
public AnimGraphAsset template;
[Tooltip("Default transition time from any other state (unless overwritten)")]
public float transitionTime;
[Tooltip("Custom transition times from specific states")]
public TransitionDefinition[] customTransitions;
}
public ControllerDefinition[] controllers;
public override IAnimGraphInstance Instatiate(EntityManager entityManager, Entity owner, PlayableGraph graph)
{
return new Instance(entityManager, owner, graph, this);
}
class Instance : IAnimGraphInstance, IGraphLogic
{
public Instance(EntityManager entityManager, Entity owner, PlayableGraph graph, AnimGraph_StateSelector settings)
{
m_settings = settings;
m_graph = graph;
m_EntityManager = entityManager;
m_Owner = owner;
animStateMixer = AnimationMixerPlayable.Create(m_graph, 0, true);
m_RootPlayable = animStateMixer;
// Animation states
animStates = new AnimationControllerEntry[(int)CharacterAnimationState.NumStates];
// Instantiate controllers. We only create one of each type even though it might be used in multiple animation states
var controllers = new Dictionary<AnimGraphAsset, IAnimGraphInstance>();
var controllerPorts = new Dictionary<IAnimGraphInstance, int>();
var stateTransitionPorts = new List<int>();
var transitionTimes = new Dictionary<IAnimGraphInstance, float[]>();
foreach (var controllderDef in m_settings.controllers)
{
if (controllderDef.template == null)
continue;
if (controllers.ContainsKey(controllderDef.template))
continue;
var controller = controllderDef.template.Instatiate(entityManager, owner, m_graph);
controllers.Add(controllderDef.template, controller);
var outputPlayable = Playable.Null;
var outputPort = 0;
controller.GetPlayableOutput(0, ref outputPlayable, ref outputPort);
var port = animStateMixer.AddInput(outputPlayable, outputPort);
controllerPorts.Add(controller, port);
stateTransitionPorts.Add(port);
var times = new float[(int)CharacterAnimationState.NumStates];
for (var i = 0; i < (int)CharacterAnimationState.NumStates; i++)
{
times[i] = controllderDef.transitionTime;
}
for (var i = 0; i < controllderDef.customTransitions.Length; i++)
{
var sourceStateIndex = (int)controllderDef.customTransitions[i].sourceState;
var time = controllderDef.customTransitions[i].transtionTime;
times[sourceStateIndex] = time;
}
transitionTimes.Add(controller, times);
}
// Setup states specifically defined
foreach (var controllderDef in m_settings.controllers)
{
var animState = controllderDef.animationState;
if (animStates[(int)animState].controller != null)
{
GameDebug.LogWarning("Animation state already registered");
continue;
}
var controller = controllers[controllderDef.template];
animStates[(int)animState].controller = controller;
animStates[(int)animState].animStateUpdater = controller as IGraphState;
animStates[(int)animState].port = controllerPorts[controller];
animStates[(int)animState].transitionTimes = transitionTimes[controller];
}
m_StateTranstion = new SimpleTranstion<AnimationMixerPlayable>(animStateMixer, stateTransitionPorts.ToArray());
}
public void Shutdown()
{
for (var i = 0; i < animStates.Length; i++)
{
animStates[i].controller.Shutdown();
}
}
public void SetPlayableInput(int index, Playable playable, int playablePort)
{
}
public void GetPlayableOutput(int index, ref Playable playable, ref int playablePort)
{
playable = m_RootPlayable;
playablePort = 0;
}
public void UpdateGraphLogic(GameTime time, float deltaTime)
{
var state = m_EntityManager.GetComponentData<CharAnimState>(m_Owner);
var animState = GetAnimState(ref state);
var firstUpdate = animState != m_lastAnimState;
m_lastAnimState = animState;
if(animStates[(int)animState].animStateUpdater != null)
animStates[(int)animState].animStateUpdater.UpdatePresentationState(firstUpdate, time, deltaTime);
}
public void ApplyPresentationState(GameTime time, float deltaTime)
{
var state = m_EntityManager.GetComponentData<CharAnimState>(m_Owner);
var animState = GetAnimState(ref state);
// If animation state has changed the new state needs to be started with current state duration to syncronize with server
if (animState != currentAnimationState || state.charLocoTick != currentAnimationStateTick)
{
var previousState = currentAnimationState;
var prevController = (int)currentAnimationState < animStates.Length ? animStates[(int) previousState].controller : null;
currentAnimationState = animState;
currentAnimationStateTick = state.charLocoTick;
var newController = animStates[(int)currentAnimationState].controller;
// Reset controller and update previous animation state if it has changed
if (newController != prevController)
{
previousAnimationState = previousAnimationState == CharacterAnimationState.NumStates ? currentAnimationState : previousState;
}
}
// Blend to current state
// We dont replicate blend values for each animAction as we assume just blending to current anim state will be close enough
var interpolationDuration = animStates[(int)currentAnimationState].transitionTimes[(int)previousAnimationState];
var blendVel = interpolationDuration > 0 ? 1.0f / interpolationDuration : 1.0f / deltaTime;
m_StateTranstion.Update(animStates[(int)currentAnimationState].port, blendVel, deltaTime);
// Update any networks that have weight
for (var i = 0; i < (int)CharacterAnimationState.NumStates; i++)
{
if (animStates[i].controller != null && animStateMixer.GetInputWeight(animStates[i].port) > 0f)
{
animStates[i].controller.ApplyPresentationState(time, deltaTime);
}
}
}
CharacterAnimationState GetAnimState(ref CharAnimState presentationState)
{
// Set animation state
var animState = CharacterAnimationState.Stand;
switch (presentationState.charLocoState)
{
case CharacterPredictedState.StateData.LocoState.Stand:
animState = CharacterAnimationState.Stand;
break;
case CharacterPredictedState.StateData.LocoState.GroundMove:
animState = CharacterAnimationState.Run;
break;
case CharacterPredictedState.StateData.LocoState.Jump:
animState = CharacterAnimationState.Jump;
break;
case CharacterPredictedState.StateData.LocoState.DoubleJump:
animState = CharacterAnimationState.InAir;
break;
case CharacterPredictedState.StateData.LocoState.InAir:
animState = CharacterAnimationState.InAir;
break;
case CharacterPredictedState.StateData.LocoState.Dead:
animState = CharacterAnimationState.Dead;
break;
}
return animState;
}
struct AnimationControllerEntry
{
public IAnimGraphInstance controller;
public IGraphState animStateUpdater;
public int port;
public float[] transitionTimes;
}
AnimGraph_StateSelector m_settings;
EntityManager m_EntityManager;
Entity m_Owner;
PlayableGraph m_graph;
AnimationMixerPlayable m_RootPlayable;
CharacterAnimationState m_lastAnimState = CharacterAnimationState.NumStates;
CharacterAnimationState currentAnimationState = CharacterAnimationState.NumStates;
CharacterAnimationState previousAnimationState = CharacterAnimationState.NumStates;
int currentAnimationStateTick;
AnimationControllerEntry[] animStates;
AnimationMixerPlayable animStateMixer;
SimpleTranstion<AnimationMixerPlayable> m_StateTranstion;
}
}