Merge branch 'Add-Calculated-Transition-Speed-to-TimelineController' into First-Project

This commit is contained in:
max 2020-01-14 17:52:02 +01:00
commit af72fdc566
5 changed files with 202 additions and 79 deletions

View file

@ -8,13 +8,21 @@ namespace cylvester
public class TimelineController : MonoBehaviour public class TimelineController : MonoBehaviour
{ {
[SerializeField] private PlayableDirector playableDirector; [SerializeField] private PlayableDirector playableDirector;
[SerializeField] private StateManager stateManager; //[SerializeField] private StateManager stateManager;
[SerializeField] private float initTransitionFactor = 1f; [SerializeField] private float initTransitionFactor = 1f;
private IList<QlistMarker> qlistMarkers_; private IList<QlistMarker> qlistMarkers_;
private Boundary boundary_; private Boundary boundary_;
private float speed_; private float speed_;
private float transitionTargetRealtime_;
private float previousMarkerTime_; //marker at which transition ends (if reversed)
private float nextMarkerTime_; //marker at which transition ends (if forward)
private bool transitionDirectionReverse_;
private bool instantTriggerMode;
private string lastStateName = "";
public void Start() public void Start()
{ {
@ -26,12 +34,13 @@ namespace cylvester
playableDirector.time = 0; playableDirector.time = 0;
boundary_ = new Boundary(null, null); boundary_ = new Boundary(null, null);
UpdateSpeed(); ResetSpeed();
} }
public void OnStateChanged(IStateReader stateManager) public void OnStateChanged(IStateReader stateManager)
{ {
var stateName = stateManager.CurrentState.Title; var stateName = stateManager.CurrentState.Title;
lastStateName = stateName;
var numMarkers = qlistMarkers_.Count; var numMarkers = qlistMarkers_.Count;
for (var i = 0; i < numMarkers; ++i) for (var i = 0; i < numMarkers; ++i)
{ {
@ -39,38 +48,133 @@ namespace cylvester
continue; continue;
playableDirector.time = qlistMarkers_[i].time; playableDirector.time = qlistMarkers_[i].time;
var previousMarkerTime = i > 0 ? (double?) qlistMarkers_[i - 1].time : null; var previousMarkerTime = i > 0 ? (double?)qlistMarkers_[i - 1].time : 0;
var nextMarkerTime = i < numMarkers - 1 ? (double?) qlistMarkers_[i + 1].time : null; var nextMarkerTime = i < numMarkers - 1 ? (double?)qlistMarkers_[i + 1].time : qlistMarkers_[numMarkers - 1].time;
previousMarkerTime_ = (float)previousMarkerTime;
nextMarkerTime_ = (float)nextMarkerTime;
boundary_ = new Boundary(previousMarkerTime, nextMarkerTime); boundary_ = new Boundary(previousMarkerTime, nextMarkerTime);
playableDirector.Play(); playableDirector.Play();
Debug.Log("prev=" + (i - 1) + " next=" + (i + 1));
break; break;
} }
} }
public double getStartMarkerTime() //Return time of marker where the current running animation started
{
var numMarkers = qlistMarkers_.Count;
for (var i = 0; i < numMarkers; ++i)
{
if (qlistMarkers_[i].id != lastStateName)
continue;
return qlistMarkers_[i].time;
}
return 0;
}
private void Update() private void Update()
{ {
if (playableDirector.state == PlayState.Paused) if (playableDirector.state == PlayState.Paused)
return; return;
if (!(Time.fixedUnscaledTime >= transitionTargetRealtime_)) // check if Transition has not finished yet
{
if (!transitionDirectionReverse_) // if transition direction forward, speed becomes positive
{
if (!instantTriggerMode)
{
speed_ = CalculateTransitionSpeed(nextMarkerTime_);
}
}
else // if transition direction backward, speed becomes negative
{
if (!instantTriggerMode)
{
speed_ = CalculateTransitionSpeed(previousMarkerTime_);
}
}
}
var deltaTime = Time.deltaTime; var deltaTime = Time.deltaTime;
var expectedTimeIncrement = speed_ * deltaTime; var expectedTimeIncrement = speed_ * deltaTime;
var expectedTimeInTimeline = playableDirector.time + expectedTimeIncrement; var expectedTimeInTimeline = playableDirector.time + expectedTimeIncrement;
if (boundary_.IsInside(expectedTimeInTimeline)) if (boundary_.IsInside(expectedTimeInTimeline)) // check if we are in between next and previous marker
{ {
playableDirector.time = expectedTimeInTimeline; playableDirector.time = expectedTimeInTimeline;
playableDirector.Evaluate(); playableDirector.Evaluate();
} }
else else
{ {
//sets playhead precisly to marker position at the end of transition.
if (!transitionDirectionReverse_) //forward
{
playableDirector.time = nextMarkerTime_;
}
else //backward
{
playableDirector.time = previousMarkerTime_;
}
playableDirector.Pause(); playableDirector.Pause();
UpdateSpeed(); ResetSpeed();
} }
} }
private void UpdateSpeed() private void ResetSpeed()
{ {
speed_ = initTransitionFactor; speed_ = initTransitionFactor;
} }
public void UpdateTransitionTargetRealTime(float restTime, bool reverse)
{
instantTriggerMode = false; //update speed continuous
transitionDirectionReverse_ = reverse;
transitionTargetRealtime_ = Time.fixedUnscaledTime + restTime;
}
private float CalculateTransitionSpeed(float targetMarkerTime)
{
var transitionSpeed = (targetMarkerTime - playableDirector.time) / (transitionTargetRealtime_ - Time.fixedUnscaledTime);
return (float)transitionSpeed;
}
public bool animationPaused()
{
return playableDirector.state == PlayState.Paused;
}
public void abortAnimation()
{
if (animationPaused()) //safe check
{
return;
}
if (!transitionDirectionReverse_) //forward
{
transitionDirectionReverse_ = !transitionDirectionReverse_; //invert direction
previousMarkerTime_ = (float)getStartMarkerTime();
}
else //backward
{
transitionDirectionReverse_ = !transitionDirectionReverse_; //invert direction
nextMarkerTime_ = (float)getStartMarkerTime();
}
boundary_ = new Boundary(previousMarkerTime_, nextMarkerTime_); //Update Boundary to stop animation when target reached
}
public void instantTrigger(float speed)
{
speed_ = speed;
instantTriggerMode = true; //keep speed_ constant
transitionDirectionReverse_ = speed < 0;
playableDirector.Play();
}
} }
} }

View file

@ -38,7 +38,7 @@ RenderSettings:
m_ReflectionIntensity: 1 m_ReflectionIntensity: 1
m_CustomReflection: {fileID: 0} m_CustomReflection: {fileID: 0}
m_Sun: {fileID: 0} m_Sun: {fileID: 0}
m_IndirectSpecularColor: {r: 0.0013490503, g: 0.0011814049, b: 0.0008122615, a: 1} m_IndirectSpecularColor: {r: 0.0013421604, g: 0.0011744275, b: 0.0008066663, a: 1}
m_UseRadianceAmbientProbe: 0 m_UseRadianceAmbientProbe: 0
--- !u!157 &3 --- !u!157 &3
LightmapSettings: LightmapSettings:
@ -193,6 +193,7 @@ MonoBehaviour:
m_Name: m_Name:
m_EditorClassIdentifier: m_EditorClassIdentifier:
playableDirector: {fileID: 65620552} playableDirector: {fileID: 65620552}
timelineController: {fileID: 65620555}
channel: 3 channel: 3
stateManager: {fileID: 850208555} stateManager: {fileID: 850208555}
instaTransitionSpeed: 10 instaTransitionSpeed: 10
@ -209,7 +210,6 @@ MonoBehaviour:
m_Name: m_Name:
m_EditorClassIdentifier: m_EditorClassIdentifier:
playableDirector: {fileID: 65620552} playableDirector: {fileID: 65620552}
stateManager: {fileID: 850208555}
initTransitionFactor: 8 initTransitionFactor: 8
--- !u!1 &155821760 --- !u!1 &155821760
GameObject: GameObject:

View file

@ -10,12 +10,13 @@ namespace cylvester
{ {
OneBarLoopButton = 94, OneBarLoopButton = 94,
FourBarLoopButton = 86, FourBarLoopButton = 86,
NextSelectedScene = 18, NextSelectedTransition = 18,
CurrentSelectedScene = 17, CurrentSelectedAnimation = 17,
InstantTrigger = 2, InstantTrigger = 2,
} }
[SerializeField] private PlayableDirector playableDirector; [SerializeField] private PlayableDirector playableDirector;
[SerializeField] private TimelineController timelineController;
[SerializeField, Range(1, 16)] private int channel = 1; [SerializeField, Range(1, 16)] private int channel = 1;
[SerializeField] private StateManager stateManager; [SerializeField] private StateManager stateManager;
[SerializeField] private float instaTransitionSpeed = 10; [SerializeField] private float instaTransitionSpeed = 10;
@ -26,16 +27,20 @@ namespace cylvester
private ScheduledAction scheduledAction_; private ScheduledAction scheduledAction_;
private int currentTick_; private int currentTick_;
private int currentSelectedScene_ ; private int currentSelectedAnimation_;
private int nextSelectedScene_; private int nextSelectedMarker_;
private void Start() private void Start()
{ {
scheduledAction_ = new ScheduledAction(() => scheduledAction_ = new ScheduledAction(() =>
{ {
stateManager.SelectedState = currentSelectedScene_; stateManager.SelectedState = currentSelectedAnimation_;
playableDirector.playableGraph.GetRootPlayable(0).SetSpeed(instaTransitionSpeed); //playableDirector.playableGraph.GetRootPlayable(0).SetSpeed(instaTransitionSpeed);
timelineController.instantTrigger(instaTransitionSpeed);
}); });
currentSelectedAnimation_ = 0;
nextSelectedMarker_ = 1;
} }
public void OnSyncReceived(MidiSync midiSync, int counter) public void OnSyncReceived(MidiSync midiSync, int counter)
@ -50,60 +55,63 @@ namespace cylvester
var command = (CylCommand)mes.Data1; var command = (CylCommand)mes.Data1;
switch (command) switch (command)
{ {
case CylCommand.NextSelectedScene: case CylCommand.NextSelectedTransition:
{
nextSelectedScene_ = mes.Data2; nextSelectedMarker_ = mes.Data2; //next marker only used for direction
Debug.Log("command nextSelectedMarker_=" + nextSelectedMarker_);
break; break;
}
case CylCommand.InstantTrigger: case CylCommand.InstantTrigger:
{
scheduledAction_.Ready(); scheduledAction_.Ready();
break; break;
}
case CylCommand.CurrentSelectedScene: case CylCommand.CurrentSelectedAnimation:
{
currentSelectedScene_ = mes.Data2; currentSelectedAnimation_ = mes.Data2;
Debug.Log("command currentSelectedAnimation_=" + currentSelectedAnimation_);
scheduledAction_.Go(); scheduledAction_.Go();
break; break;
}
case CylCommand.FourBarLoopButton: case CylCommand.FourBarLoopButton:
{
var restTime = UpdateRestTime(FourBarTrigger - currentTick_ % FourBarTrigger);
if (nextSelectedScene_ > currentSelectedScene_)
{
UpdateTimelinePlaybackSpeed(1f, restTime);
stateManager.SelectedState = nextSelectedScene_;
}
else
{
UpdateTimelinePlaybackSpeed(-1f, restTime);
stateManager.SelectedState = nextSelectedScene_ + 2;
}
break;
}
case CylCommand.OneBarLoopButton: case CylCommand.OneBarLoopButton:
{ float restTime=0;
var restTime = UpdateRestTime(OneBarTrigger - currentTick_ % OneBarTrigger); switch(command)
UpdateTimelinePlaybackSpeed(1f, restTime); {
stateManager.SelectedState = nextSelectedScene_; case CylCommand.FourBarLoopButton:
break; restTime = CalculateRestTime(FourBarTrigger - currentTick_ % FourBarTrigger);
} break;
default: case CylCommand.OneBarLoopButton:
throw new Exception("Unexpected CYL command"); restTime = CalculateRestTime(OneBarTrigger - currentTick_ % OneBarTrigger);
break;
}
Debug.Log("currentSelectedAnimation_=" + currentSelectedAnimation_);
Debug.Log("nextSelectedScene_=" + nextSelectedMarker_);
bool _reverse = nextSelectedMarker_ <= currentSelectedAnimation_; //nextSelectedMarker_ used to calculate direction
if (timelineController.animationPaused())
{
timelineController.UpdateTransitionTargetRealTime(restTime, _reverse);
stateManager.SelectedState = currentSelectedAnimation_ + 1; //marker at which transition starts
}
else //trigger pressed while animation running
{
timelineController.abortAnimation();
}
break;
default:
Debug.Log("Unexpected Command: " + mes.Data1);
throw new Exception("Unexpected CYL command");
} }
} }
private float UpdateRestTime(int restTicks) private float CalculateRestTime(int restTicks)
{ {
return restTicks / 24f / stateManager.CurrentState.Bpm * 60f; return restTicks / 24f / stateManager.CurrentState.Bpm * 60f;
} }
private void UpdateTimelinePlaybackSpeed(float speed, float restTime)
{
var timelinePlaybackSpeed = TransitionLength / Mathf.Clamp(restTime, 0.001f, TransitionLength);
playableDirector.playableGraph.GetRootPlayable(0).SetSpeed(timelinePlaybackSpeed * speed);
}
} }
} }

View file

@ -85,6 +85,14 @@ namespace cylvester
set set
{ {
sceneSelection = value; sceneSelection = value;
if (value > States.Length - 1)
{ //if index out of bounds
sceneSelection = States.Length - 1; //set to last index
}
else if (value < 0)
{
sceneSelection = 0;
}
onStateChanged.Invoke(this); onStateChanged.Invoke(this);
} }
get get
@ -104,10 +112,12 @@ namespace cylvester
case Operation.Previous: case Operation.Previous:
if (sceneSelection == 0) return; if (sceneSelection == 0) return;
sceneSelection--; sceneSelection--;
sceneSelection = Math.Max(sceneSelection, 0); //constrain minimum
break; break;
case Operation.Next: case Operation.Next:
if (sceneSelection >= States.Length - 1) return; if (sceneSelection >= States.Length - 1) return;
sceneSelection++; sceneSelection++;
sceneSelection = Math.Min(sceneSelection, States.Length - 1); //constrain maximum
break; break;
default: default:
return; return;
@ -127,10 +137,12 @@ namespace cylvester
case Operation.Previous: case Operation.Previous:
if (sceneSelection == 0) return; if (sceneSelection == 0) return;
sceneSelection--; sceneSelection--;
sceneSelection = Math.Max(sceneSelection, 0); //constrain minimum
break; break;
case Operation.Next: case Operation.Next:
if (sceneSelection >= States.Length - 1) return; if (sceneSelection >= States.Length - 1) return;
sceneSelection++; sceneSelection++;
sceneSelection = Math.Min(sceneSelection, States.Length - 1); //constrain maximum
break; break;
default: default:
return; return;

View file

@ -1,4 +1,4 @@
#N canvas 179 93 1690 536 10; #N canvas -3090 415 1690 536 10;
#X declare -lib timbreID/timbreIDLib; #X declare -lib timbreID/timbreIDLib;
#X obj 484 442 adc~ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16; #X obj 484 442 adc~ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16;
#X obj 414 483 dac~, f 6; #X obj 414 483 dac~, f 6;
@ -290,8 +290,8 @@ Roses_Front.wav \; TimbreID_Test.wav \;;
#X text 416 223 176... control channel 1; #X text 416 223 176... control channel 1;
#X obj 6 194 midirealtimein; #X obj 6 194 midirealtimein;
#X obj 242 41 netreceive -u 54345; #X obj 242 41 netreceive -u 54345;
#X obj 598 90 cnv 15 500 60 empty current Forces 20 12 0 45 -204786 #X obj 598 90 cnv 15 500 60 empty current PositionCloud 20 12 0 45
-66577 0; -204786 -66577 0;
#X obj 786 234 bng 50 250 50 0 empty empty next 15 25 0 10 -204786 #X obj 786 234 bng 50 250 50 0 empty empty next 15 25 0 10 -204786
-1 -1; -1 -1;
#X obj 596 227 bng 50 250 50 0 empty empty rewind 7 25 0 10 -261234 #X obj 596 227 bng 50 250 50 0 empty empty rewind 7 25 0 10 -261234
@ -302,10 +302,10 @@ Roses_Front.wav \; TimbreID_Test.wav \;;
-1 -1; -1 -1;
#X msg 692 303 send 176 127 1; #X msg 692 303 send 176 127 1;
#X msg 791 304 send 176 127 2; #X msg 791 304 send 176 127 2;
#X obj 594 46 cnv 12 500 30 empty previous PositionCloud 20 12 0 25 #X obj 594 46 cnv 12 500 30 empty previous PontCloud 20 12 0 25 -262130
-262130 -66577 0;
#X obj 596 152 cnv 12 500 30 empty next ShowAll 20 12 0 25 -262130
-66577 0; -66577 0;
#X obj 596 152 cnv 12 500 30 empty next Forces 20 12 0 25 -262130 -66577
0;
#X obj 879 467 declare -lib timbreID/timbreIDLib; #X obj 879 467 declare -lib timbreID/timbreIDLib;
#N canvas 28 541 1273 735 timbreID_example 0; #N canvas 28 541 1273 735 timbreID_example 0;
#X obj 77 657 nbx 3 28 -1e+037 1e+037 0 0 empty empty empty 0 -8 0 #X obj 77 657 nbx 3 28 -1e+037 1e+037 0 0 empty empty empty 0 -8 0
@ -420,7 +420,6 @@ samples/kick.wav kick \, read -resize samples/closed.wav closed;
#X connect 5 0 4 0; #X connect 5 0 4 0;
#X connect 6 0 7 0; #X connect 6 0 7 0;
#X restore 405 71 pd shmem; #X restore 405 71 pd shmem;
#X text 701 378 <--- this is only for reference;
#X obj 1167 19 cnv 15 400 500 empty empty CYL_Transition_Sim 20 12 #X obj 1167 19 cnv 15 400 500 empty empty CYL_Transition_Sim 20 12
0 14 -204800 -66577 0; 0 14 -204800 -66577 0;
#X obj 1383 196 bng 15 250 50 0 4bar_trig_sim empty 4_Bar_trigger_Sim #X obj 1383 196 bng 15 250 50 0 4bar_trig_sim empty 4_Bar_trigger_Sim
@ -594,13 +593,13 @@ samples/kick.wav kick \, read -resize samples/closed.wav closed;
#X floatatom 1472 98 5 0 0 1 BPM - midi_sim_tempo; #X floatatom 1472 98 5 0 0 1 BPM - midi_sim_tempo;
#X text 1374 100 Clock Sim.:; #X text 1374 100 Clock Sim.:;
#X obj 1451 98 tgl 15 0 clock_sim_start_stop empty empty 17 7 0 10 #X obj 1451 98 tgl 15 0 clock_sim_start_stop empty empty 17 7 0 10
-204786 -1 -1 0 1; -204786 -1 -1 1 1;
#X text 1198 40 Simulate the Press of a 1_Bar or 4_Bar Transition trigger. #X text 1198 40 Simulate the Press of a 1_Bar or 4_Bar Transition trigger.
; ;
#N canvas 0 50 1167 867 Midi_Clock_Sim 0; #N canvas 0 50 1167 867 Midi_Clock_Sim 0;
#X obj 21 155 outlet; #X obj 21 155 outlet;
#X obj 155 14 tgl 15 0 empty clock_sim_start_stop mock 17 7 0 10 -262144 #X obj 155 14 tgl 15 0 empty clock_sim_start_stop mock 17 7 0 10 -262144
-1 -1 0 1; -1 -1 1 1;
#X msg 110 128 248, f 4; #X msg 110 128 248, f 4;
#X text 29 -56 MIDI Clock Simulator; #X text 29 -56 MIDI Clock Simulator;
#X text 8 10 start; #X text 8 10 start;
@ -885,10 +884,10 @@ samples/kick.wav kick \, read -resize samples/closed.wav closed;
#X connect 30 0 11 0; #X connect 30 0 11 0;
#X connect 31 0 11 0; #X connect 31 0 11 0;
#X connect 36 0 11 0; #X connect 36 0 11 0;
#X connect 42 0 43 0; #X connect 41 0 42 0;
#X connect 43 0 54 0; #X connect 42 0 53 0;
#X connect 44 0 74 0; #X connect 43 0 73 0;
#X connect 58 0 44 0; #X connect 57 0 43 0;
#X connect 58 0 63 0; #X connect 57 0 62 0;
#X connect 64 0 11 0; #X connect 63 0 11 0;
#X coords 0 0 2 2 0 0 0; #X coords 0 0 2 2 0 0 0;