private bool ApplyAnimation(Skeleton skeleton, AnimatorClipInfo info, AnimatorStateInfo stateInfo, int layerIndex, float layerWeight, MixBlend layerBlendMode, bool useClipWeight1 = false) { float weight = info.weight * layerWeight; if (weight == 0) { return(false); } var clip = GetAnimation(info.clip); if (clip == null) { return(false); } var time = AnimationTime(stateInfo.normalizedTime, info.clip.length, info.clip.isLooping, stateInfo.speed < 0); weight = useClipWeight1 ? layerWeight : weight; clip.Apply(skeleton, 0, time, info.clip.isLooping, null, weight, layerBlendMode, MixDirection.In); if (_OnClipApplied != null) { OnClipAppliedCallback(clip, stateInfo, layerIndex, time, info.clip.isLooping, weight); } return(true); }
private MixMode GetMixMode(int layer, MixBlend layerBlendMode) { if (useCustomMixMode) { MixMode mode = layerMixModes[layer]; // Note: at additive blending it makes no sense to use constant weight 1 at a fadeout anim add1 as // with override layers, so we use AlwaysMix instead to use the proper weights. // AlwaysMix leads to the expected result = lower_layer + lerp(add1, add2, transition_weight). if (layerBlendMode == MixBlend.Add && mode == MixMode.MixNext) { mode = MixMode.AlwaysMix; layerMixModes[layer] = mode; } return(mode); } else { return(layerBlendMode == MixBlend.Add ? MixMode.AlwaysMix : MixMode.MixNext); } }
public void Apply(Skeleton skeleton) { #if UNITY_EDITOR if (!Application.isPlaying) { GetLayerBlendModes(); } #endif if (layerMixModes.Length < animator.layerCount) { int oldSize = layerMixModes.Length; System.Array.Resize <MixMode>(ref layerMixModes, animator.layerCount); for (int layer = oldSize; layer < animator.layerCount; ++layer) { bool isAdditiveLayer = false; if (layer < layerBlendModes.Length) { isAdditiveLayer = layerBlendModes[layer] == MixBlend.Add; } layerMixModes[layer] = isAdditiveLayer ? MixMode.AlwaysMix : MixMode.MixNext; } } InitClipInfosForLayers(); for (int layer = 0, n = animator.layerCount; layer < n; layer++) { GetStateUpdatesFromAnimator(layer); } // Clear Previous if (autoReset) { var previousAnimations = this.previousAnimations; for (int i = 0, n = previousAnimations.Count; i < n; i++) { previousAnimations[i].SetKeyedItemsToSetupPose(skeleton); } previousAnimations.Clear(); for (int layer = 0, n = animator.layerCount; layer < n; layer++) { float layerWeight = (layer == 0) ? 1 : animator.GetLayerWeight(layer); // Animator.GetLayerWeight always returns 0 on the first layer. Should be interpreted as 1. if (layerWeight <= 0) { continue; } AnimatorStateInfo nextStateInfo = animator.GetNextAnimatorStateInfo(layer); bool hasNext = nextStateInfo.fullPathHash != 0; int clipInfoCount, nextClipInfoCount, interruptingClipInfoCount; IList <AnimatorClipInfo> clipInfo, nextClipInfo, interruptingClipInfo; bool isInterruptionActive, shallInterpolateWeightTo1; GetAnimatorClipInfos(layer, out isInterruptionActive, out clipInfoCount, out nextClipInfoCount, out interruptingClipInfoCount, out clipInfo, out nextClipInfo, out interruptingClipInfo, out shallInterpolateWeightTo1); for (int c = 0; c < clipInfoCount; c++) { var info = clipInfo[c]; float weight = info.weight * layerWeight; if (weight == 0) { continue; } var clip = GetAnimation(info.clip); if (clip != null) { previousAnimations.Add(clip); } } if (hasNext) { for (int c = 0; c < nextClipInfoCount; c++) { var info = nextClipInfo[c]; float weight = info.weight * layerWeight; if (weight == 0) { continue; } var clip = GetAnimation(info.clip); if (clip != null) { previousAnimations.Add(clip); } } } if (isInterruptionActive) { for (int c = 0; c < interruptingClipInfoCount; c++) { var info = interruptingClipInfo[c]; float clipWeight = shallInterpolateWeightTo1 ? (info.weight + 1.0f) * 0.5f : info.weight; float weight = clipWeight * layerWeight; if (weight == 0) { continue; } var clip = GetAnimation(info.clip); if (clip != null) { previousAnimations.Add(clip); } } } } } // Apply for (int layer = 0, n = animator.layerCount; layer < n; layer++) { float layerWeight = (layer == 0) ? 1 : animator.GetLayerWeight(layer); // Animator.GetLayerWeight always returns 0 on the first layer. Should be interpreted as 1. bool isInterruptionActive; AnimatorStateInfo stateInfo; AnimatorStateInfo nextStateInfo; AnimatorStateInfo interruptingStateInfo; float interruptingClipTimeAddition; GetAnimatorStateInfos(layer, out isInterruptionActive, out stateInfo, out nextStateInfo, out interruptingStateInfo, out interruptingClipTimeAddition); bool hasNext = nextStateInfo.fullPathHash != 0; int clipInfoCount, nextClipInfoCount, interruptingClipInfoCount; IList <AnimatorClipInfo> clipInfo, nextClipInfo, interruptingClipInfo; bool interpolateWeightTo1; GetAnimatorClipInfos(layer, out isInterruptionActive, out clipInfoCount, out nextClipInfoCount, out interruptingClipInfoCount, out clipInfo, out nextClipInfo, out interruptingClipInfo, out interpolateWeightTo1); MixBlend layerBlendMode = (layer < layerBlendModes.Length) ? layerBlendModes[layer] : MixBlend.Replace; MixMode mode = GetMixMode(layer, layerBlendMode); if (mode == MixMode.AlwaysMix) { // Always use Mix instead of Applying the first non-zero weighted clip. for (int c = 0; c < clipInfoCount; c++) { ApplyAnimation(skeleton, clipInfo[c], stateInfo, layer, layerWeight, layerBlendMode); } if (hasNext) { for (int c = 0; c < nextClipInfoCount; c++) { ApplyAnimation(skeleton, nextClipInfo[c], nextStateInfo, layer, layerWeight, layerBlendMode); } } if (isInterruptionActive) { for (int c = 0; c < interruptingClipInfoCount; c++) { ApplyInterruptionAnimation(skeleton, interpolateWeightTo1, interruptingClipInfo[c], interruptingStateInfo, layer, layerWeight, layerBlendMode, interruptingClipTimeAddition); } } } else // case MixNext || Hard // Apply first non-zero weighted clip { int c = 0; for (; c < clipInfoCount; c++) { if (!ApplyAnimation(skeleton, clipInfo[c], stateInfo, layer, layerWeight, layerBlendMode, useClipWeight1:true)) { continue; } ++c; break; } // Mix the rest for (; c < clipInfoCount; c++) { ApplyAnimation(skeleton, clipInfo[c], stateInfo, layer, layerWeight, layerBlendMode); } c = 0; if (hasNext) { // Apply next clip directly instead of mixing (ie: no crossfade, ignores mecanim transition weights) if (mode == MixMode.Hard) { for (; c < nextClipInfoCount; c++) { if (!ApplyAnimation(skeleton, nextClipInfo[c], nextStateInfo, layer, layerWeight, layerBlendMode, useClipWeight1:true)) { continue; } ++c; break; } } // Mix the rest for (; c < nextClipInfoCount; c++) { if (!ApplyAnimation(skeleton, nextClipInfo[c], nextStateInfo, layer, layerWeight, layerBlendMode)) { continue; } } } c = 0; if (isInterruptionActive) { // Apply next clip directly instead of mixing (ie: no crossfade, ignores mecanim transition weights) if (mode == MixMode.Hard) { for (; c < interruptingClipInfoCount; c++) { if (ApplyInterruptionAnimation(skeleton, interpolateWeightTo1, interruptingClipInfo[c], interruptingStateInfo, layer, layerWeight, layerBlendMode, interruptingClipTimeAddition, useClipWeight1:true)) { ++c; break; } } } // Mix the rest for (; c < interruptingClipInfoCount; c++) { ApplyInterruptionAnimation(skeleton, interpolateWeightTo1, interruptingClipInfo[c], interruptingStateInfo, layer, layerWeight, layerBlendMode, interruptingClipTimeAddition); } } } } }
public void Apply(Skeleton skeleton) { if (layerMixModes.Length < animator.layerCount) { System.Array.Resize <MixMode>(ref layerMixModes, animator.layerCount); layerMixModes[animator.layerCount - 1] = MixMode.MixNext; } #if UNITY_EDITOR if (!Application.isPlaying) { GetLayerBlendModes(); } #endif InitClipInfosForLayers(); for (int layer = 0, n = animator.layerCount; layer < n; layer++) { GetStateUpdatesFromAnimator(layer); } //skeleton.Update(Time.deltaTime); // Doesn't actually do anything, currently. (Spine 3.6). // Clear Previous if (autoReset) { var previousAnimations = this.previousAnimations; for (int i = 0, n = previousAnimations.Count; i < n; i++) { previousAnimations[i].SetKeyedItemsToSetupPose(skeleton); } previousAnimations.Clear(); for (int layer = 0, n = animator.layerCount; layer < n; layer++) { float layerWeight = (layer == 0) ? 1 : animator.GetLayerWeight(layer); // Animator.GetLayerWeight always returns 0 on the first layer. Should be interpreted as 1. if (layerWeight <= 0) { continue; } AnimatorStateInfo nextStateInfo = animator.GetNextAnimatorStateInfo(layer); bool hasNext = nextStateInfo.fullPathHash != 0; int clipInfoCount, nextClipInfoCount, interruptingClipInfoCount; IList <AnimatorClipInfo> clipInfo, nextClipInfo, interruptingClipInfo; bool isInterruptionActive, shallInterpolateWeightTo1; GetAnimatorClipInfos(layer, out isInterruptionActive, out clipInfoCount, out nextClipInfoCount, out interruptingClipInfoCount, out clipInfo, out nextClipInfo, out interruptingClipInfo, out shallInterpolateWeightTo1); for (int c = 0; c < clipInfoCount; c++) { var info = clipInfo[c]; float weight = info.weight * layerWeight; if (weight == 0) { continue; } previousAnimations.Add(GetAnimation(info.clip)); } if (hasNext) { for (int c = 0; c < nextClipInfoCount; c++) { var info = nextClipInfo[c]; float weight = info.weight * layerWeight; if (weight == 0) { continue; } previousAnimations.Add(GetAnimation(info.clip)); } } if (isInterruptionActive) { for (int c = 0; c < interruptingClipInfoCount; c++) { var info = interruptingClipInfo[c]; float clipWeight = shallInterpolateWeightTo1 ? (info.weight + 1.0f) * 0.5f : info.weight; float weight = clipWeight * layerWeight; if (weight == 0) { continue; } previousAnimations.Add(GetAnimation(info.clip)); } } } } // Apply for (int layer = 0, n = animator.layerCount; layer < n; layer++) { float layerWeight = (layer == 0) ? 1 : animator.GetLayerWeight(layer); // Animator.GetLayerWeight always returns 0 on the first layer. Should be interpreted as 1. bool isInterruptionActive; AnimatorStateInfo stateInfo; AnimatorStateInfo nextStateInfo; AnimatorStateInfo interruptingStateInfo; float interruptingClipTimeAddition; GetAnimatorStateInfos(layer, out isInterruptionActive, out stateInfo, out nextStateInfo, out interruptingStateInfo, out interruptingClipTimeAddition); bool hasNext = nextStateInfo.fullPathHash != 0; int clipInfoCount, nextClipInfoCount, interruptingClipInfoCount; IList <AnimatorClipInfo> clipInfo, nextClipInfo, interruptingClipInfo; bool shallInterpolateWeightTo1; GetAnimatorClipInfos(layer, out isInterruptionActive, out clipInfoCount, out nextClipInfoCount, out interruptingClipInfoCount, out clipInfo, out nextClipInfo, out interruptingClipInfo, out shallInterpolateWeightTo1); MixMode mode = layerMixModes[layer]; MixBlend layerBlendMode = (layer < layerBlendModes.Length) ? layerBlendModes[layer] : MixBlend.Replace; if (mode == MixMode.AlwaysMix) { // Always use Mix instead of Applying the first non-zero weighted clip. for (int c = 0; c < clipInfoCount; c++) { var info = clipInfo[c]; float weight = info.weight * layerWeight; if (weight == 0) { continue; } GetAnimation(info.clip).Apply(skeleton, 0, AnimationTime(stateInfo.normalizedTime, info.clip.length, stateInfo.loop, stateInfo.speed < 0), stateInfo.loop, null, weight, layerBlendMode, MixDirection.In); } if (hasNext) { for (int c = 0; c < nextClipInfoCount; c++) { var info = nextClipInfo[c]; float weight = info.weight * layerWeight; if (weight == 0) { continue; } GetAnimation(info.clip).Apply(skeleton, 0, AnimationTime(nextStateInfo.normalizedTime, info.clip.length, nextStateInfo.speed < 0), nextStateInfo.loop, null, weight, layerBlendMode, MixDirection.In); } } if (isInterruptionActive) { for (int c = 0; c < interruptingClipInfoCount; c++) { var info = interruptingClipInfo[c]; float clipWeight = shallInterpolateWeightTo1 ? (info.weight + 1.0f) * 0.5f : info.weight; float weight = clipWeight * layerWeight; if (weight == 0) { continue; } GetAnimation(info.clip).Apply(skeleton, 0, AnimationTime(interruptingStateInfo.normalizedTime + interruptingClipTimeAddition, info.clip.length, interruptingStateInfo.speed < 0), interruptingStateInfo.loop, null, weight, layerBlendMode, MixDirection.In); } } } else // case MixNext || Hard // Apply first non-zero weighted clip { int c = 0; for (; c < clipInfoCount; c++) { var info = clipInfo[c]; float weight = info.weight * layerWeight; if (weight == 0) { continue; } GetAnimation(info.clip).Apply(skeleton, 0, AnimationTime(stateInfo.normalizedTime, info.clip.length, stateInfo.loop, stateInfo.speed < 0), stateInfo.loop, null, 1f, layerBlendMode, MixDirection.In); break; } // Mix the rest for (; c < clipInfoCount; c++) { var info = clipInfo[c]; float weight = info.weight * layerWeight; if (weight == 0) { continue; } GetAnimation(info.clip).Apply(skeleton, 0, AnimationTime(stateInfo.normalizedTime, info.clip.length, stateInfo.loop, stateInfo.speed < 0), stateInfo.loop, null, weight, layerBlendMode, MixDirection.In); } c = 0; if (hasNext) { // Apply next clip directly instead of mixing (ie: no crossfade, ignores mecanim transition weights) if (mode == MixMode.Hard) { for (; c < nextClipInfoCount; c++) { var info = nextClipInfo[c]; float weight = info.weight * layerWeight; if (weight == 0) { continue; } GetAnimation(info.clip).Apply(skeleton, 0, AnimationTime(nextStateInfo.normalizedTime, info.clip.length, nextStateInfo.speed < 0), nextStateInfo.loop, null, 1f, layerBlendMode, MixDirection.In); break; } } // Mix the rest for (; c < nextClipInfoCount; c++) { var info = nextClipInfo[c]; float weight = info.weight * layerWeight; if (weight == 0) { continue; } GetAnimation(info.clip).Apply(skeleton, 0, AnimationTime(nextStateInfo.normalizedTime, info.clip.length, nextStateInfo.speed < 0), nextStateInfo.loop, null, weight, layerBlendMode, MixDirection.In); } } c = 0; if (isInterruptionActive) { // Apply next clip directly instead of mixing (ie: no crossfade, ignores mecanim transition weights) if (mode == MixMode.Hard) { for (; c < interruptingClipInfoCount; c++) { var info = interruptingClipInfo[c]; float clipWeight = shallInterpolateWeightTo1 ? (info.weight + 1.0f) * 0.5f : info.weight; float weight = clipWeight * layerWeight; if (weight == 0) { continue; } GetAnimation(info.clip).Apply(skeleton, 0, AnimationTime(interruptingStateInfo.normalizedTime + interruptingClipTimeAddition, info.clip.length, interruptingStateInfo.speed < 0), interruptingStateInfo.loop, null, 1f, layerBlendMode, MixDirection.In); break; } } // Mix the rest for (; c < interruptingClipInfoCount; c++) { var info = interruptingClipInfo[c]; float clipWeight = shallInterpolateWeightTo1 ? (info.weight + 1.0f) * 0.5f : info.weight; float weight = clipWeight * layerWeight; if (weight == 0) { continue; } GetAnimation(info.clip).Apply(skeleton, 0, AnimationTime(interruptingStateInfo.normalizedTime + interruptingClipTimeAddition, info.clip.length, interruptingStateInfo.speed < 0), interruptingStateInfo.loop, null, weight, layerBlendMode, MixDirection.In); } } } } }
private bool ApplyInterruptionAnimation(Skeleton skeleton, bool interpolateWeightTo1, AnimatorClipInfo info, AnimatorStateInfo stateInfo, int layerIndex, float layerWeight, MixBlend layerBlendMode, float interruptingClipTimeAddition, bool useClipWeight1 = false) { float clipWeight = interpolateWeightTo1 ? (info.weight + 1.0f) * 0.5f : info.weight; float weight = clipWeight * layerWeight; if (weight < WeightEpsilon) { return(false); } var clip = GetAnimation(info.clip); if (clip == null) { return(false); } var time = AnimationTime(stateInfo.normalizedTime + interruptingClipTimeAddition, info.clip.length, stateInfo.speed < 0); weight = useClipWeight1 ? layerWeight : weight; clip.Apply(skeleton, 0, time, info.clip.isLooping, null, weight, layerBlendMode, MixDirection.In); if (_OnClipApplied != null) { OnClipAppliedCallback(clip, stateInfo, layerIndex, time, info.clip.isLooping, weight); } return(true); }