rj-action-library/Runtime/Animation/Shake/Shake.cs

174 lines
5.0 KiB
C#

using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Text;
using Godot;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class Shake:SequenceAction, Animator
{
[Export]
public ShakeEffect shakeEffect;
[Export]
public Node3D[] targets;
List<TransformData> _targetValues;
int _actionID = -1;
int _currentSpanAnimationID = -1;
public void OnAnimatorStart(){}
public void OnAnimatorEnd(){}
public void OnAnimatorCancel(){}
protected override void _OnTrigger()
{
if ( _actionID != -1 )
{
CancelAction( _actionID );
}
_actionID = DispatchStart();
var random = LCG.Randomized();
var networkSeed = random.GetSeed();
var randomization = shakeEffect.shakeAmountCurve.Randomize( random );
var timeline = shakeEffect.timeline;
var duration = shakeEffect.shakeAmountCurve.GetRandomizedEndTime( randomization );
var curve = shakeEffect;
var start = TimeLineManager.GetPosition( shakeEffect.timeline );
var keyFrames = new List<KeyFrame<TransformData>>();
var elapsed = 0f;
while ( elapsed < duration )
{
var key = new KeyFrame<TransformData>();
key.data = shakeEffect.GetShakeData( elapsed, randomization, random );
key.time = elapsed;
keyFrames.Add( key );
elapsed += 1f / shakeEffect.GetShakeChangeFPSRandomized( elapsed, random );
}
var kfAnimation = new KeyFrameAnimation<TransformData>();
kfAnimation.keyFrames = keyFrames;
kfAnimation.FlagSorted();
KeyFrame<TransformData> lastKeyFrame = null;
AnimationManager.StartAnimation( this, targets, AnimationMember.Transform );
if ( _targetValues == null || _targetValues.Count == 0 )
{
_targetValues = Lists.Map( targets, t => TransformData.From( t, shakeEffect.globalPosition, shakeEffect.globalRotation ) );
}
_currentSpanAnimationID = TimeLineScheduler.ScheduleSpanIn( timeline, 0, duration,
( int spanAnimationID, TimeLineSpanUpdateType type )=>
{
if ( spanAnimationID != _currentSpanAnimationID )
{
return;
}
if ( ! AnimationManager.IsAnimating( this, targets, AnimationMember.Transform ) )
{
return;
}
if ( TimeLineSpanUpdateType.End == type )
{
AnimationManager.EndAnimation( this, targets, AnimationMember.Transform );
for ( int i = 0; i < targets.Length; i++ )
{
var o = _targetValues[ i ];
o.Set( targets[ i ], shakeEffect.globalPosition, shakeEffect.globalRotation );
}
_targetValues.Clear();
_actionID = -1;
_currentSpanAnimationID = -1;
return;
}
var offset = TimeLineManager.GetPosition( shakeEffect.timeline ) - start;
var keyFrame = kfAnimation.GetKeyFrameAt( offset );
if ( keyFrame == lastKeyFrame && ! shakeEffect.smooth )
{
return;
}
var offsetData = keyFrame.data;
var delta = shakeEffect.timeline.delta;
for ( int i = 0; i < targets.Length; i++ )
{
var combined = offsetData.Clone();
var o = _targetValues[ i ];
if ( shakeEffect.scaleShakeIsRelative )
{
combined.scale *= _targetValues[ i ].scale;
}
combined.rotation *= MathX.DegreesToRadians;
var beforeAdd = combined.Clone();
combined.Add( o );
// this.LogInfo( "Applying:", beforeAdd, " + ", o, "=", combined );
if ( shakeEffect.smooth )
{
var rawSmooth = Mathf.Clamp( shakeEffect.smoothingStrength, 0, 1 );
rawSmooth = Mathf.Pow( 1f - rawSmooth, 3 );
var smoothStrength = rawSmooth * 250f;
var current = TransformData.From( targets[ i ], shakeEffect.globalPosition, shakeEffect.globalRotation );
combined.position = Smoother.SmoothTimeInvariant( current.position, combined.position, delta, smoothStrength );
combined.rotation = Smoother.SmoothTimeInvariant( current.rotation, combined.rotation, delta, smoothStrength );
combined.scale = Smoother.SmoothTimeInvariant( current.scale, combined.scale, delta, smoothStrength );
}
combined.Set( targets[ i ], shakeEffect.globalPosition, shakeEffect.globalRotation );
}
lastKeyFrame = keyFrame;
}
);
}
public override void CancelAction( int actionID )
{
if ( actionID != _actionID )
{
return;
}
AnimationManager.EndAnimation( this, targets, AnimationMember.Transform );
_actionID = -1;
_currentSpanAnimationID = -1;
}
}
}