ArticyFlowPlayer with time delays

Thu 15. Aug 2019, 20:41

Hi, guys, I have a problem understanding how I should implement delays the best way using Articy Unity Importer.

I think I already know how the player works, but there are some questions. As I understand, OnFlowPlayerPaused() method is invoked somewhere after Start() method, right?
I am trying to make a simple text adventure, and I already set up the logic of traversing the flow and making choices in unity. In my game each text block appears and then gives you some time to read it (text length * k * text speed setting). I struggled so hard with this, but I finally managed to make it using Coroutines. I may understand them bad, so I think my solution is not very elegant.

Code: Select all
public void OnFlowPlayerPaused(IFlowObject aObject)
    {
        _current = GetComponent<ArticyFlowPlayer>().PausedOn as PhraseDialogueFragment;
        _currentSpeed = (float)textSpeed * 0.02f;

        if (aObject is PhraseDialogueFragment df) // my custom DF here
        {
            _spawner.SpawnPhrase(df.Text);
        }
    }
    public void OnBranchesUpdated(IList<Branch> aBranches)
    {
        List<Branch> candidates = new List<Branch>();
        foreach (Branch branch in aBranches)
        {
            if (branch.IsValid)
                candidates.Add(branch);
        }

        StartCoroutine(WaitTimeAndCheckForDelay(candidates, _current.Text.Length * _currentSpeed));
    }


I start coroutine at the end of the second method, so that nothing happens at the same time with the coroutine content. Which is:

Code: Select all
private IEnumerator WaitTimeAndCheckForDelay(List<Branch> candidates, float time)
    {
        yield return new WaitForSeconds(time); // here we are waiting time before next event happens

        float delay = _current.Template.PhraseFeature.delay;
        if (delay > 0)
        {
            StartCoroutine(PlayWithDelay(candidates, delay));
        }
        else
            Play(candidates);
    }


Let me stop here and explain the delay feature. I made it in articy:draft to signal the game when to perform a long wait event. Like... really long sometimes, up to 8 hours (the narrative is simulating realtime)
So at the moment we're waiting till the player is told Play(). So there are two ways: if no delay - continue right now. If there is - wait time. So another coroutine at the end of the method. Nothing is happening at the same time.
I know it's stupid, I'm trying to find a better way so hard! So let's move on:

Code: Select all
private void Play(List<Branch> candidates)
    {
        if (candidates.Count == 0)
            Debug.LogError("No candidates found.");
        else if (candidates.Count == 1)
        {
            _player.Play(candidates[0]);
        } else
            _spawner.SpawnChoice(candidates); // here we're simply making a button for every valid branch, and set its handler to Play() on specific branch
    }


And another way - we play after waiting:

Code: Select all
private IEnumerator PlayWithDelay(List<Branch> candidates, float delay)
    {
        DateTime endTime = DateTime.Now.AddMinutes(delay);
        while (DateTime.Now <= endTime)
        {
            yield return null;
        }
        Play(candidates);
    }


Well it's working good, because every event is after each other while the whole coroutines point is violated. Still, this weird method is working. But then I realized, that I need to save player's progress. I already implemented my save system and it's not the subject of this question.
Let's just assume I am keeping track of these:

1) The DF I should execute, when the game starts
2) the time remaining, which I should wait before executing it.

So here's the problem. This is the Start() method of my save system and the entry point to the entire game:

Code: Select all
private void Start()
    {
        if (!SaveFileExists())
        {
            CreateNewSaveFile();
        }
        else
        {
            LoadGame(); // this is where we restore the global variables and set the StartOn property
        }
    }


Okay, in LoadGame() we found out that the DF to execute is, for instance, "Df_1", and we should execute it in 30 mins. Here I should go to some loop and wait for the time to come, but after LoadGame()OnFlowPlayerPaused() happens in my flow controller class and we start on the "Df_1" immediately. And i can't prevent flow player from playing, like, set StartOn to null or something...
I'm asking you what is the best and the rightest way to do this or how to change everything and write it properly? Is the player designed to work with delays? I assume it is. If you could show me some code and tell anything I need in detail, I'd appreciate it a lot! If it's hard - okay, no matter! If it's easy - the better!

Thank you for taking time to read my question! Cheers from Russia from your big fan!
Last edited by thepunkoff on Fri 16. Aug 2019, 21:59, edited 1 time in total.
thepunkoff
 
Posts: 6
Joined: Thu 15. Aug 2019, 19:30

Re: ArticyFlowPlayer with time delays

Fri 16. Aug 2019, 09:02

Hi thepunkoff,

Interesting idea you have there! And first of all, i don't see an issue with your approach. If you look into our maniac manfred demo project, i delay the spoken text to make it "fade" into screen letter by letter. The flow player
doesn't care when you are continue, it just pauses and takes (almost) no processing.

Let me see if i can help you with your issue:

So you said

i can't prevent flow player from playing, like, set StartOn to null or something...


My first question would be; Why set it then? Store somewhere that you need to set it and set it at the appropriate time. FlowPlayer will only work if you actually set something and then it will traverse until it has to notify anything. If nothing is
set, the flow player won't start. (It is wort pointing out, that it is not starting but initializing to be precise. If startOn is set than that's where you want to start, and most often your startOn is also a of a pauseOn type, thats why its pausing directly).
Looking through the source code of the plugin i noticed a small bug, it is indeed not working when you try to set StartOnto null. Until its fixed, you could just do

Code: Select all
// notice the lowecase s of startOn
startOn.id = 0;


But if you try that on runtime, it will still still start on the node set in the inspector at edit time, so you should just clear that beforehand. (Click in startOn field in the inspector and hit DEL)

If for some reason those aren't options, the simplest solution would be to just skip the first OnFlowPlayerPaused.

Code: Select all
// after loading
flowPlayer.StartOn = nodeIDontWantToSee;
skipFirstPause = true;


Code: Select all
public void OnFlowPlayerPaused(IFlowObject aObject)
{
   if(skipFirstPause)
   {   
      skipFirstPause = false;
      return;
   }
   
   // ...
}


Hope that helps!

Best regards

Nico
Nico Probst
Senior Software Engineer | Articy | LinkedIn
User avatar
[Articy] Nico Probst
Articy Staff
Articy Staff
 
Posts: 217
Joined: Wed 23. Nov 2011, 09:45
Location: Bochum

Re: ArticyFlowPlayer with time delays

Fri 16. Aug 2019, 21:58

Thank you for your answer! Actually, the last snippet helped me the most. I made a little wrapper around coroutines to make them fire events. Then I made a StandBy flag in the controller and checked for this flag in both OnFlowPlayerPaused() and OnBranchesUpdated(). Now when the coroutine finishes, the event handler switches the flag and sets the StartOn property. Thanks!
thepunkoff
 
Posts: 6
Joined: Thu 15. Aug 2019, 19:30

Return to articy:draft Unity Importer

Who is online

Users browsing this forum: No registered users and 13 guests

Who We Are
Contact Us
Social Links