rj-action-library/Runtime/Time/TimeLineRunner.cs

324 lines
7.4 KiB
C#

using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
using System;
using Godot;
namespace Rokojori
{
public class TimeLineRunner
{
public TimeLine timeLine;
public TimeLineManager manager;
public float lastPosition = 0;
public float position = 0;
public bool playing = false;
public float deltaScale = 1;
public float speed = 1;
List<TimeLineEvent> events = new List<TimeLineEvent>();
List<TimeLineSpan> spans = new List<TimeLineSpan>();
List<TimeLineCallback> callbacks = new List<TimeLineCallback>();
AnimationCurve modulatorCurve;
float modulatorTime;
Vector3 modulatorRandomization;
Action<bool> modulatorOnReady = null;
float _lastModulation = 1;
float _currentDelta = 1;
public float modulatedSpeed => (float)( speed * _lastModulation * deltaScale );
public float currentDelta => _currentDelta;
public TimeLineRunner( TimeLine timeLine, TimeLineManager manager )
{
this.timeLine = timeLine;
this.manager = manager;
playing = timeLine.autoStart;
}
public void Modulate( AnimationCurve curve, Action<bool> onReady )
{
if ( modulatorOnReady != null )
{
modulatorOnReady( false );
}
modulatorCurve = curve;
modulatorTime = 0;
modulatorRandomization = curve.Randomize();
modulatorOnReady = onReady;
}
public void UpdateTimeLine( float realtimeDelta )
{
if ( ! playing )
{
return;
}
var modulation = 1f;
if ( modulatorCurve != null )
{
modulation = modulatorCurve.Sample( modulatorTime, modulatorRandomization );
_lastModulation = modulation;
modulatorTime += realtimeDelta;
if ( modulatorTime >= modulatorCurve.GetRandomizedEndTime( modulatorRandomization ) )
{
var mr = modulatorOnReady;
modulatorOnReady = null;
modulatorCurve = null;
modulatorTime = 0;
modulatorRandomization = Vector3.Zero;
if ( mr != null )
{
mr( true );
}
}
}
else
{
_lastModulation = 1;
}
_currentDelta = realtimeDelta * deltaScale * speed * modulation;
lastPosition = position;
position += _currentDelta;
var isForward = speed >= 0;
if ( isForward )
{
ProcessForward();
}
}
void ProcessForward()
{
ProcessEvents();
ProcessSpans();
ProcessCallbacks();
}
List<TimeLineCallback> _callbackAdditions = new List<TimeLineCallback>();
void ProcessCallbacks()
{
if ( _callbackAdditions.Count > 0 )
{
callbacks.AddRange( _callbackAdditions );
_callbackAdditions.Clear();
}
callbacks.ForEach( c => c.callback( c ) );
}
void ProcessEvents()
{
if ( requestedRemovals.Count > 0 )
{
requestedRemovals.Sort();
Lists.RemoveIncreasingSortedIndices( events, requestedRemovals );
requestedRemovals.Clear();
}
List<int> eventRemovals = null;
for ( int i = 0; i < events.Count; i++ )
{
var eventPosition = events[ i ].position;
if ( events[ i ].looping )
{
var next = events[ i ].GetNextLoopPosition( lastPosition );
var previous = events[ i ].GetPreviousLoopPosition( position );
if ( next != previous )
{
continue;
}
eventPosition = next;
}
if ( ! RangeDouble.ContainsExclusiveMax( lastPosition, position, eventPosition ) )
{
if ( events[ i ].wasInside )
{
eventRemovals = AddRemoval( events[ i ].persistent, i, eventRemovals );
}
continue;
}
events[ i ].callback( events[ i ] );
events[ i ].wasInside = true;
}
if ( eventRemovals != null )
{
Lists.RemoveIncreasingSortedIndices( events, eventRemovals );
}
}
void ProcessSpans()
{
if ( spans.Count == 0 )
{
return;
}
List<int> spanRemovals = null;
var timelineSpan = new RangeDouble( lastPosition, position );
var span = new RangeDouble( 0, 1 );
var isForward = lastPosition < position;
for ( int i = 0; i < spans.Count; i++ )
{
span.min = spans[ i ].start;
span.max = spans[ i ].end;
var overlaps = timelineSpan.Overlaps( span );
if ( ! overlaps )
{
if ( spans[ i ].wasInside )
{
spanRemovals = AddRemoval( spans[ i ].persistent, i, spanRemovals );
}
continue;
}
var isStart = timelineSpan.Contains( spans[ i ].start );
var isEnd = timelineSpan.Contains( spans[ i ].end );
var spanType = TimeLineSpanUpdateType.InSpan;
if ( isStart && isEnd )
{
spanType = TimeLineSpanUpdateType.CompletelyInside;
}
else if ( isStart )
{
spanType = TimeLineSpanUpdateType.Start;
}
else if ( isEnd )
{
spanType = TimeLineSpanUpdateType.End;
}
spans[ i ].wasInside = true;
spans[ i ].callback( spans[ i ], spanType );
}
if ( spanRemovals != null )
{
Lists.RemoveIncreasingSortedIndices( spans, spanRemovals );
}
}
List<int> AddRemoval( bool isPersistent, int index, List<int> list )
{
if ( isPersistent )
{
return list;
}
if ( list == null )
{
list = new List<int>();
}
list.Add( index );
return list;
}
List<int> requestedRemovals = new List<int>();
public void RemoveEvent( int eventID )
{
requestedRemovals.Add( eventID );
}
public TimeLineCallback _ScheduleCallback( Action<TimeLineCallback> callback, int eventID )
{
var tle = new TimeLineCallback();
tle.id = eventID;
tle.callback = callback;
tle.timeLine = timeLine;
_callbackAdditions.Add( tle );
return tle;
}
public TimeLineEvent _ScheduleEvent( float position, int eventID, bool isPersistent, Action<TimeLineEvent> callback )
{
var tle = new TimeLineEvent();
tle.position = position;
tle.id = eventID;
tle.persistent = isPersistent;
tle.callback = callback;
tle.timeLine = timeLine;
events.Add( tle );
return tle;
}
public TimeLineEvent _ScheduleLoopEvent( float loopDuration, float loopOffset, int eventID, bool isPersistent, Action<TimeLineEvent> callback )
{
var tle = new TimeLineEvent();
tle.position = loopOffset;
tle.looping = true;
tle.loopDuration = loopDuration;
tle.id = eventID;
tle.persistent = isPersistent;
tle.callback = callback;
tle.timeLine = timeLine;
events.Add( tle );
return tle;
}
public TimeLineSpan _ScheduleSpan( float start, float end, int eventID, bool isPersistent, Action<TimeLineSpan, TimeLineSpanUpdateType> callback )
{
var tse = new TimeLineSpan();
tse.start = start;
tse.end = end;
tse.id = eventID;
tse.persistent = isPersistent;
tse.wasInside = false;
tse.callback = callback;
tse.timeLine = timeLine;
spans.Add( tse );
return tse;
}
}
}