Requiring User Input when very Deep inside the Game Process

I'm creating sequels of my old games and by doing so I'm going to port their engine from C++ to C# in Unity.

1 thing that bothers me is that I've been requesting user input from very deep in my game process. (likely up to 10 nested function calls)
To do so, I just opened a menu, and then manually ran the system process which in turn called the renderer, GUI system, etc.

In Unity, this is impossible to do like this.

To solve this issue, I see only 2 solutions : 1st one is to allow to exit & re-enter the process when user input is required ; I already did it once in the past, but it can get cumbersome, especially when it gets very deeply nested.

So now I'm thinking about multi-threading : game Update() would start a separate thread and wait for it to either end, or for it to request system process (ie: exit Update() and let Unity does its stuff).

So I was wondering if other devs faced this kind of issue ? And how they handled it ? If they used multithreading, were they satisfied with the result ? (ie: maintenance wise, was it ok ?)

Use Case (for people wanting to be sure I can't do it another way) :
my game is a tennis game where I process tournaments & players. According to the situation, the player controlled by the user may or may not do something (ie: enter the tournament he has selected) and then he has to take a new decision depending of this new context, and this is not known before processing the other players. And the consequences of his decision will influence the next processed players.

EDIT Extra explanation :
In term of coding graph, I guess you could see it as nested functions like this : A() -> B() -> C() -> D() -> E() -> F() with the user input needed in F() (dependent of F() processing) while E() requires F() result to go on, D() requires E() result, and so on.

So if you had to process a lot of non-Unity data using a lot of non-Unity nested functions, and required process-dependent user input in the middle of that giant mess, how did you do it ?

@frosted,
my example is about implementing the solution, not about the issue...

The key point is "likely up to 10 nested function calls" ; each function requiring the result of the called function right away ; if the function requiring user input is a coroutine, its caller won't get the result right away and thus the processing can't continue.

Using IEnumerator without coroutine can still potentially provide solution, since you can use this to essentially block until the process is complete without needing unity callbacks.

Depending on how exactly you're reading input or what you're operating on, you may have problems processing from background thread as unity tends to reject access to unity functions from non main thread.

Also, in your provided example spawning the thread achieves nothing. You could simply switch on state and achieve the same logical functionality without the complexity of additional thread.

The problem appears to revolve around needing to block a call until user input has completed. If the originating call is on unity's main thread anyway, this is not something you want to do as it will block internal process. If the call is occurring outside of main thread then you can simply sleep until your state is switched.

@frosted,
in my exposed solution, there's no actual input processing in the sub-thread, only data processing and opening menus, so the main thread can show this menu and gather user input.

I exposed the exact problem in "user case" above.
In term of coding graph, I guess you could see it as nested functions like this : A() -> B() -> C() -> D() -> E() -> F() with the user input needed in F() (dependent of F() processing) while E() requires F() result to go on, D() requires E() result, and so on.

And again, the example is the solution, not the issue. I'm trying to demonstrate the functionality achieved through multi-threading to make it more clear how my solution to the problem would work (ie: how the sub-thread & the main thread would wait for each other, which is the key point in this situation)

You should really just simplify the process. I'll never understand why people just have to make things more complicated than they need to be. Be creative and find another way to do it as simple as possible. It seems the way you're doing it now is just going to be a mess to manage and even create.

Like why do you even need to do Multi-Threading for user input? Seems like overkill. But perhaps if whatever it is you're trying to do requires Threading, then perhaps the new Unity Jobs system can work for you? Sense it's essentially Multi-Threading.

Oh and by the way, you might be wasting your time with pure Multi-Threading as Unity is (not) thread safe. You can run your own Methods in Threads, but not Update, etc.

EDIT: and what I mean by the very first sentence is.
For example: back when I first started Unity a few years ago. the FPS Controller, hundreds of lines of code, to do the same exact thing that I can now do in essentially 60 lines of code... Keep it simple, why make it harder than it needs to be.

@frosted,
blocking the main thread is how it works currently, so this behavior won't change.
Opening the menu doesn't invoke Unity function so there's no threading issue. If an Unity function was called, it'd be possible to create an custom command to avoid that by letting the main thread open the menu.

PS: I have 20 years of coding experience, it's not a noob question here. I'd like people with actual experience with this issue to answer me...

If your underlying call stack is on unity main thread, blocking it while relying on unity functionality to present menu will simply not work. Application freeze on your code causing essentially deadlock while main thread waits for update from main thread.

if the calling code is running on a background thread then you can simply while( state ) sleep; the returning call and block there with no impact on unity and you already have a separate thread running to notify unity main thread.

If the calling code is on unity main thread but your gui is on separate thread/process, then you don't need to spawn an intermediate thread to make the opening call.

To explain a little simpler:

If

Code (csharp):

CGui.OpenMenu("HeyUserTellMeWhatYouWantToDo");

blocks while a non unity menu is being shown then your secondary thread has no meaning - you can simply stick that in your update method and have same result.

If

Code (csharp):

CGui.OpenMenu("HeyUserTellMeWhatYouWantToDo");

doesn't block because it has it's own thread/process, then opening a new thread to just call this and wait while also blocking update also has no utility. You again, can simply call this from update on main thread while waiting for notification.

According to the code and context you presented, there is no argument for a thread to act while fully blocking the other one. Either your example is flawed, your context is flawed or your understanding of the process is flawed.

@frosted,
it's neither of both.
CGui.OpenMenu() sets the current shown menu, that will be then rendered by the classic GUI() call (it'd be same if it was handled by the new GUI system), thus the need to give back control to the main thread through the change of m_State.

After further reading, in C# it'd be done by WaitHandle.WaitOne() , which would replace the "while (m_State ...)" in the sample.

If you had actually understood my initial problem, you'd see that none of your answers so far are pertinent, and I'm afraid I don't know how to explain more clearly...

I'll try to rephrase it one last time : if you had to process a lot of non-Unity data using a lot of non-Unity nested functions, and required process-dependent user input in the middle of that giant mess, how did you do it ?

As long as you don't understand the actual issue, please don't answer anymore ; you'll just lose your time and mine...

I don't get it. It seems like a state machine might server you better?

State 1 - > Doing Stuff
State 2 - > Waiting for local users input
State 3 - > (Local user has provided input) Waiting for remote users input
Once all users have provided input, go back to state 1 (but doing different stuff obviously).

Am I not understanding right? Maybe I'm just tired, but it's not easily understand exactly what you're issue is, and looking at others responses, it doesn't appear that anyone else is comprehending this issue any better. This leads me to believe you might benefit from going over your original post and taking another crack at explaining everything. Try to look at the issue from all sides from the perspective of someone who has not dealt with it at all.

That's just my humble opinion on what you can do to try and get better feedback. Good luck!!

The problem here is that you need to find a way to switch from this procedural paradigm (such as an old DOS single threaded program would have used) to an event driven one (e.g., a windowing, GUI system or Unity).

In the case of the former the program can, of course, be held indefinitely in F() whilst the user enters details. But, as you suggest, that won't work in a framework like Unity where functions must yield results per frame.

To do this, you really need to arrange the code to fit the architecture of Unity rather than trying to hammer the thread-blocking pattern in.

For example, there will be ways to split the game into a sequence of scenes. Maybe you could have a player setup scene (e.g. enter player name, avatar, racket preference etc.). Then have a tournament setup scene (e.g. total number of players, number of sets per game etc.). Then a game prep scene (e.g. allow player to select a tournament, an opponent etc.). Then the game scene itself. Now you can control the navigation between those scenes based on required user input at the appropriate times.

Apologies if that's a bit vague but without knowing the specifics of your game flow logic, it's difficult to be more precise.

@frosted,
blocking the main thread is how it works currently, so this behavior won't change.
Opening the menu doesn't invoke Unity function so there's no threading issue. If an Unity function was called, it'd be possible to create an custom command to avoid that by letting the main thread open the menu.

PS: I have 20 years of coding experience, it's not a noob question here. I'd like people with actual experience with this issue to answer me...

Click to expand...

You had the Unity answer. Unity is not C# nor is it C++ at the tools level. it s a component based frame dependent architecture built for rapid application development with all sorts of beautiful Monobehaviours to make your life easy once you get a grip on how it actually works.. Trying to force your pure C++ or C# paradigm on it will cause you cascading woes.

This is just an ignorant programmer who is too proud to realize he needs to read some real introduction level stuff and is convinced he's a rocket scientist doing cutting edge research.

Click to expand...

I believe this is more a case of him not properly explaining the problem and trying to directly port an existing project rather than rebuild it around Unity.

For starters, let's touch on his explanation. He's constantly referring to user input but what he's clearly talking about is the main game loop. As you know we can't treat Update as a main method, start executing code, and only return once the program is done and ready to stop, but that's exactly how a non-engine based game would be designed.

By directly porting (converting from C# to C++) rather than rebuilding it around the design principles of Unity he's having to jump through hoops to allow Unity to handle the main game loop as it is designed to while still letting his game function the way it was originally designed to.

His game runs in its own thread while Unity runs in the main thread, Unity passes inputs through to the game, the game passes outputs through to Unity, and there is a very minor state system keeping them synced.

good lord guys, look at his code example and think through the problem. This is not remotely complex or difficult.

This is just an ignorant programmer who is too proud to realize he needs to read some real introduction level stuff and is convinced he's a rocket scientist doing cutting edge research.

Click to expand...

It would take me 10 to 20 minutes to set up and test given the requirements for each screen or menu or what have you to move on to the next step. The simplest method would be and Update loop in a Controller component turned on only for this use. It would have a bool for each function set to true when completed. Polling in the Update would immediately disable the previous component and set active the next or if it was purely function i would just fire off the next function in sequence after the current function's bool switch flipped to true. I am dealing with somewhat similar mindsets at my day job in that i am the lone pure Unity developer versus seven C#-ers with two having dabbled in Unity. It really ticks them off they cannot just use a new constructor to create instances of my components and that they have to use Instantiate and AddComponent. It ticks me off they tried to write a layer over top of the Unity toolset that I have to go through to use my tools and deprive me of the juicy bits of kit that hundreds of Unity technicians have put in place to make my job easier with Monobehaviours.

Once the fellow working directly with me on the lower level data structures that Unity parses and displays understood why i wanted things a certain way i got it and development went from moss growing rate to a rapid clip. The big light came on when I one day wanted the data structure to add one string to a List<> of properties to be able to extract a critical value at a matched index and could have finished that toolset in five minutes. He fussed with me that that was not the way to do it. I told him I could finish my work in five minutes if he would accommodate./ We went back and forth for 3 hours whilst he tried to convince me about the use of a windows business form paradigm but would cause the use of another parsing loop with cascading conditionals. It was one line of code on his end versus 20 on mine and a hit to the framerate. I am stubborn. He gave in. Five minutes later the toolset was on the repo completed. I reminded him of the time spent fussing about versus the reality of my claim. He said he liked the way the new data structure was working, I liked single line access to an unknown property's index with no conditionals at parsing time, so we were good to go all around. Since that day I have had no issues in getting what i need for the Unity toolkit to be used to it's greatest efficiency and most powerful and simplest elegance.

I believe this is more a case of him not properly explaining the problem and trying to directly port an existing project rather than rebuild it around Unity.

For starters, let's touch on his explanation. He's constantly referring to user input but what he's clearly talking about is the main game loop. As you know we can't treat Update as a main method, start executing code, and only return once the program is done and ready to stop, but that's exactly how a non-engine based game would be designed.

By directly porting (converting from C# to C++) rather than rebuilding it around the design principles of Unity he's having to jump through hoops to allow Unity to handle the main game loop as it is designed to while still letting his game function the way it was originally designed to.

His game runs in its own thread while Unity runs in the main thread, Unity passes inputs through to the game, the game passes outputs through to Unity, and there is a very minor state system keeping them synced.

Click to expand...

I am dealing with this misunderstanding of jargon and the way the Unity engine works and how Unity devs refer to things. I can say something that developers here on the forum would have no issue understanding what my question or statement was about. I use the same jargon with the C#-ers and often get an argument that has nothing to do with what i am speaking to. Or..I am writing a toolkit that has maybe 80 lines of code, no Update loops and has maybe five objects, all in an inactive gameobject. If I did not mention it nobody would know it was there unless they unfolded the Hierarchy and wondered what those objects did. I did mention it as I was giving them a visualization toolkit for relationships they have been fussing about how it should work for a bit.. I saw the solution immediately as far as the mechanic on the Unity level.. however they parse the data to get to me matters not. The toolkit will be able to handle it once I understand the parsing method. It was reacted to like i had put an active function in a C# class that would affect all functionality all the time unless it was put into a #define. I argued there was no need for that as Unity would not use any of the toolkit unless i activated the gameobject and called a function from a button. This actually led to an argument where I was hung up on and the fellow had to calm down before proceeding. I suppose it is human's perennial problem of two different movies playing in two different heads.Yanny vs Laurel. I see the application in this case as a Unity app and they see it as a C# app using Unity to display it.

I'll try to rephrase it one last time : if you had to process a lot of non-Unity data using a lot of non-Unity nested functions, and required process-dependant user input in the middle of that giant mess, how did you do it ?.

Click to expand...

I had the data formatted to properly use the Unity toolkit and Monobehaviours. The only functions at the data structuring level were ones that massaged the data for either use by the Unity tools or for storage on the cloud. All top level mechanics and functionality remained the domain of the Unity toolkit. Being process dependent I used what i monikered a switchboard pattern in a tools controller. I would understand the order of function firing and variable setting, constructing bools to switch to the next process in the sequence. So, one function would finish and set the bool to true and cause the firing of the next process in the same frame with no delays. It would finish the process, set it's bool to true which would then trigger the next process.

@Ryiah got it mostly right, with the exception that I'd be running into that issue even if I had started from scratch, because the exit & re-enter code needed to follow Unity implementation is actually awful and maintenance wise it's a mess.

I think the sub-thread solution is very elegant and easy to implement, but I'd have been happy to get some head up from someone who already did that, especially as I have at least 3 months of work to port the engine, so I won't be able to test anything for a while and thus knowing I picked a sound solution would ease the urge to see it in action...

Anyway, writing down my concept above helped me to decide it was the best course of action.

As it raised a lot of incomprehension in the answers, I guess it means it's not as much a common issue as I thought it was. And actually thinking about it now, for me, it only happened in my tennis Tour engine ; I never got a similar issue in all other games & stuff I have been coding...

I'm in a case where I cannot get user input early (because I don't know what input I'll need before I'm inside the process), I cannot delay the decision (because the rest of process depends on the user input) and I can't easily get out of the current nested calls : I can't just make a little state to handle the exit & re-entry of the different processing sequences, nor make everything more flat (ie: less nested calls).

@Doug_B,
what you describe is a classic menu flow ; fortunately, even if I'm an ignorant programmer, it's still something that I don't have issue to handle...

@gilley033,
yes, a state machine could do, but the issue here is the extreme nested function calls which makes the exit & re-entry paths especially dirty to implement & maintain.

@ippdev,
it's really not about C# or C++ paradigm. I have a Tour handling that takes 80'000+ of packed, complex and inter-dependent lines of code, and they have and will have nothing to do about Unity.

@ippdev,
it's really not about C# or C++ paradigm. I have a Tour handling that takes 80'000+ of packed, complex and inter-dependent lines of code, and they have and will have nothing to do about Unity.

Click to expand...

Except you are using Unity to interact with and display the results of manipulation of those 80k+ lines of code. Does this irony escape you? You sound just like some of the folks at my work..Simply refusing to recognize the environment they are sandboxing in and according the tools the respect they deserve. Frankly..you shouldn't be using Unity. It will only get in your way with your not gonna compromise headspace.

I've been down this road multiple times with experienced developers coming into a new environment. My advice is relax and go with the flow. Give the new paradigm a chance and put aside what you think you know for the time being. Do that and a light will go off before too long. Fight it, and it won't end well.

@frosted,
my example is about implementing the solution, not about the issue...

The key point is "likely up to 10 nested function calls" ; each function requiring the result of the called function right away ; if the function requiring user input is a coroutine, its caller won't get the result right away and thus the processing can't continue.

Click to expand...

Your nested function call should happen within the coroutine.

The point of coroutine is that it can stop execution till the next frame and continue from where it left off without programmer going nuts. Meaning it won't block the caller.

@snacktime,
I moved to Unity paradigm years ago and I'm very happy with it. However, the current issue doesn't fit in a simple way in Unity paradigm, at least not without a ton of dirty ugly code in the middle. Or it can, but with the elegant & simple multi-threading solution exposed above.

@neginfinity,
coroutines don't work when their callers need their result right away ; ie: with nested functions processing data.

I already happily used them for GUI & Input stuff, though, where the process can be easily linearized.

@snacktime,
I moved to Unity paradigm years ago and I'm very happy with it. However, the current issue doesn't fit in a simple way in Unity paradigm, at least not without a ton of dirty ugly code in the middle. Or it can, but with the elegant & simple multi-threading solution exposed above.

@neginfinity,
coroutines don't work when their callers need their result right away ; ie: with nested functions processing data.

I already happily used them for GUI & Input stuff, though, where the process can be easily linearized.

Click to expand...

Replying to your reply to neginfinity, then in that case wouldn't you just hold those results as variables and then any other functions can grab the results at any time? Meaning you have a bunch of variables, and the coroutines manipulate those variables (as opposed to returning the data directly). For example, if you wanted a UI that can only be interacted with when it's done fading in, have a bool called isTransitioning. At the beginning of the coroutine it's set to true, and at the end it's set to false. Other code can then just check isTransitioning to determine whether or not it can do something. User input function OpenMenu then might do something like if(!isTransitioning)//do stuff. Same thing could apply to any other kind of game mechanic, like waiting for the player to press a button to hit a tennis ball.
Sorry if I'm misunderstanding anything!

The little jargon fest means nothing in context. It does not alter the fact that you are dealing with a hybrid in that Unity is a frame dependent component based architecture. You seem to think you have some kind of unique code situation nobody has ever had to deal with prior...i.e. cascading functions. You have a notion that plopping this cascading function set in another thread is gonna whiz bang yer stuff and yer good to go. Funny it doesn't look that way to the experienced in many languages developers who have chimed in here and backed up what they were saying with technical explanations and expertise. You are looking like you do not want the answer..you want the answer you want..which ain't happening.

At some point years of experience ceases to be a relevant metric: it seems everyone here has lots of years of experience, and making appeals to authority is probably not going to get us anywhere.

@manutoo you did a poor job of explaining your problem and its constraints and maybe you are just a little too set on thinking about the problem in a certain way. But if you are happy with your elegant solution then continuing to argue seems somewhat moot.

@JohnnyA,
I didn't come here to argue, but to ask for people to share their experience with such kind of issue. But no one who answered had that kind of issue. Everybody (except Ryiah) misinterpreted what I wrote, so I guess I didn't explain well enough. Unfortunately, I don't see how to explain it in another way, especially to people who seem to be there just to talk me down to show off their ego instead of actually trying to understand my issue. No one asked me to explain more in detail a point ; everybody assumed they totally understand my issue, thought it was super simple, despite me keep telling everybody they didn't get it and it wasn't that simple...

And I direly hope any coder with 20 years of experience is able to use a coroutine when he needs to, and to organize a menu flow when required, and wouldn't come to ask for help about this on the Unity forum...

@Jingle-Fett,
issue is not on the menu side, and has nothing to do with a transitioning variable...

Yes, it's exactly what I have written in my 1st post. I don't want someone finding a solution to my issue. I want to know what other people did when facing that issue. Maybe next time try to read & understand before trying to "help"...

@Everybody,

and if someone here is genuinely curious to understand what is the issue, here a last attempt to explain it with some pseudo code :

Code (CSharp):

class CTour

{

void Process()

{

//... other stuff comes here...

HandleDay();

//... other stuff comes here...

}

void HandleDay()

{

//... other stuff comes here...

if(NewWeek())

{

FillTournaments();

CreateTournamentDraws();

}

//... other stuff comes here...

}

void FillTournaments()

{

for(int i =0; i < m_NbPlayer; ++i)

{

ChooseTournament(m_PlayerSortedByRank[i]);

}

}

void PickTournament(CPlayer p)

{

if(p.IsControllerByUser)

{

if(p.SelectedTournament.IsFull)// this depends of previous player choices

{

CGui.WaitForMenu("AskUserIfHeWantsToGoToALowerTournament"); // <= this is blocking in my current engine, showing the menu & waiting for the user to answer the question

if(CGui.Result== EAnswer.e_Yes)

EnterTournament(p, p.NewlyChosenTournament);

}

else

EnterTournament(p, p.SelectedTournament);

}

else

{

// CPU Player chooses a tournament

}

}

}

Reminder : you can't put the player selection out of the loop, as his choice is influenced by and influences the CPU player choices.
Real case has much, much more nested functions.
Real case has many, many places like this where it's required to show a menu.
Real case will get new menus to show in the future when adding functionalities.

PS: at this point, it's not about me asking for sharing your experience, it's about me to see if I can make understand at least one person what the problem actually is...

From your more detailed pseudo code there, it turns out I did understand exactly what you are asking. I refer you again to my earlier response above which exactly answers your question and is in keeping with your new pseudo code.

What I do not understand is how you do not understand the answer I gave you (and has subsequently been followed up by a number of others).

As a summary of what has been discussed above, you have 2 ways to progress:

A developer of your skill will be able to shoe-horn your 80+k lines of single-threaded code into Unity. But it will be difficult and require jumping through a number of hoops to do so.

You could create a game architecture that fits the Unity framework. Then reuse subroutines and assets from your original game where appropriate / possible.

If you are looking for a 3rd option, then when you find it maybe you could come back and enlighten us about it. I, for one, wish you all the best in your endeavours.

@JohnnyA,
I didn't come here to argue, but to ask for people to share their experience with such kind of issue. But no one who answered had that kind of issue. Everybody (except Ryiah) misinterpreted what I wrote, so I guess I didn't explain well enough. Unfortunately, I don't see how to explain it in another way, especially to people who seem to be there just to talk me down to show off their ego instead of actually trying to understand my issue. No one asked me to explain more in detail a point ; everybody assumed they totally understand my issue, thought it was super simple, despite me keep telling everybody they didn't get it and it wasn't that simple...

And I direly hope any coder with 20 years of experience is able to use a coroutine when he needs to, and to organize a menu flow when required, and wouldn't come to ask for help about this on the Unity forum...

@Jingle-Fett,
issue is not on the menu side, and has nothing to do with a transitioning variable...

Yes, it's exactly what I have written in my 1st post. I don't want someone finding a solution to my issue. I want to know what other people did when facing that issue. Maybe next time try to read & understand before trying to "help"...

@Everybody,

and if someone here is genuinely curious to understand what is the issue, here a last attempt to explain it with some pseudo code :

Code (CSharp):

class CTour

{

void Process()

{

//... other stuff comes here...

HandleDay();

//... other stuff comes here...

}

void HandleDay()

{

//... other stuff comes here...

if(NewWeek())

{

FillTournaments();

CreateTournamentDraws();

}

//... other stuff comes here...

}

void FillTournaments()

{

for(int i =0; i < m_NbPlayer; ++i)

{

ChooseTournament(m_PlayerSortedByRank[i]);

}

}

void PickTournament(CPlayer p)

{

if(p.IsControllerByUser)

{

if(p.SelectedTournament.IsFull)// this depends of previous player choices

{

CGui.WaitForMenu("AskUserIfHeWantsToGoToALowerTournament"); // <= this is blocking in my current engine, showing the menu & waiting for the user to answer the question

if(CGui.Result== EAnswer.e_Yes)

EnterTournament(p, p.NewlyChosenTournament);

}

else

EnterTournament(p, p.SelectedTournament);

}

else

{

// CPU Player chooses a tournament

}

}

}

Reminder : you can't put the player selection out of the loop, as his choice is influenced by and influences the CPU player choices.
Real case has much, much more nested functions.
Real case has many, many places like this where it's required to show a menu.
Real case will get new menus to show in the future when adding functionalities.

PS: at this point, it's not about me asking for sharing your experience, it's about me to see if I can make understand at least one person what the problem actually is...

@JohnnyA,
I didn't come here to argue, but to ask for people to share their experience with such kind of issue. But no one who answered had that kind of issue. Everybody (except Ryiah) misinterpreted what I wrote, so I guess I didn't explain well enough. Unfortunately, I don't see how to explain it in another way, especially to people who seem to be there just to talk me down to show off their ego instead of actually trying to understand my issue. No one asked me to explain more in detail a point ; everybody assumed they totally understand my issue, thought it was super simple, despite me keep telling everybody they didn't get it and it wasn't that simple...

And I direly hope any coder with 20 years of experience is able to use a coroutine when he needs to, and to organize a menu flow when required, and wouldn't come to ask for help about this on the Unity forum...

@Jingle-Fett,
issue is not on the menu side, and has nothing to do with a transitioning variable...

Yes, it's exactly what I have written in my 1st post. I don't want someone finding a solution to my issue. I want to know what other people did when facing that issue. Maybe next time try to read & understand before trying to "help"...

@Everybody,

and if someone here is genuinely curious to understand what is the issue, here a last attempt to explain it with some pseudo code :

Code (CSharp):

class CTour

{

void Process()

{

//... other stuff comes here...

HandleDay();

//... other stuff comes here...

}

void HandleDay()

{

//... other stuff comes here...

if(NewWeek())

{

FillTournaments();

CreateTournamentDraws();

}

//... other stuff comes here...

}

void FillTournaments()

{

for(int i =0; i < m_NbPlayer; ++i)

{

ChooseTournament(m_PlayerSortedByRank[i]);

}

}

void PickTournament(CPlayer p)

{

if(p.IsControllerByUser)

{

if(p.SelectedTournament.IsFull)// this depends of previous player choices

{

CGui.WaitForMenu("AskUserIfHeWantsToGoToALowerTournament"); // <= this is blocking in my current engine, showing the menu & waiting for the user to answer the question

if(CGui.Result== EAnswer.e_Yes)

EnterTournament(p, p.NewlyChosenTournament);

}

else

EnterTournament(p, p.SelectedTournament);

}

else

{

// CPU Player chooses a tournament

}

}

}

Reminder : you can't put the player selection out of the loop, as his choice is influenced by and influences the CPU player choices.
Real case has much, much more nested functions.
Real case has many, many places like this where it's required to show a menu.
Real case will get new menus to show in the future when adding functionalities.

PS: at this point, it's not about me asking for sharing your experience, it's about me to see if I can make understand at least one person what the problem actually is...

Click to expand...

The example I gave in my post with the transition variable is about how you can make a coroutine process a variable, and then use that variable as a condition to decide whether to proceed or not. This solves the problem you mentioned of:

coroutines don't work when their callers need their result right away

Click to expand...

Here's a coroutine based pseudo code example of what you could do with your PickTournament function:

@Doug_B,
actually, the 1) is easy, it's the ~10 lines of code exposed in my solution in the 1st post. And done.
So now why should I break into thousand pieces a code that works decently well & that provides a logical flow easy to follow ?
And how future code would be easier to develop & maintain if broke into thousand of pieces instead of just adding my 10 lines of code that will work once and for all for existing & future code ?
It's especially true as I may not know in advance what parts will need to be broken into pieces, as I may think of new functionalities later on (eg: adding stuff from user feedback).

For me, it's now ultra obvious the sub-thread solution is a far superior solution. I wasn't totally convinced when I thought about it at 1st, so I can understand that a seasoned developer might need a little moment to wrap his head around it, but the total negation I met here is rather surprising...

@Jingle-Fett,
same than neginfinity but you even didn't understand how the pseudo code works at 1st... (actually, I'm not sure neginfinity got it, but I let him the benefice of the doubt )
And for the global picture, see my answer above to Doug_B .

Reminder : you can't put the player selection out of the loop, as his choice is influenced by and influences the CPU player choices.
Real case has much, much more nested functions.
Real case has many, many places like this where it's required to show a menu.
Real case will get new menus to show in the future when adding functionalities.

Click to expand...

Yes, you can move player selection anywhere.
Having more nested functions is not a problem. It is standard refactoring work.
Many, many, many more places where it is necessary to show a menu will be automatically transform into "ShowMenu()" coroutine/sub-coroutine. It will be actually quite elegant with coroutine approach. In any part of coroutine where you need to show a menu you'll simply make a call.
And I don't see how more menus will be a problem.

Code (csharp):

var playerMenu =new PlayerMenu("title", "Choice1", "Choice2");

yield return playerMenu.run();

if(playerMenu.choice== ....)

At this point I suspect you'll be happier hiring a freelancer to work on your exact specific scenario. IIRC, spawning separate thread can actually lead to trouble if you start messing with gameobjects with it, because many portions of api are not expected to be thread safe.

Unless I'm missing something the issue at-hand is there is some code running in a background thread that needs to wait for user input, and there is a desire to limit change to the already-written code.

I'm still relatively new to Unity, but in a non-Unity application I would typically use dependency injection to solve this problem. I look at the case of "I need user interaction" as a cross-cutting concern, and I typically use service location for cross-cutting concerns - right or wrong, this approach lets me easily separate concerns while compromising a little bit on visibility of dependencies:

Code (CSharp):

publicinterface IUserInteraction

{

string GetResponse(string question);

}

In my UI-dependent background thread I'd just get a handle to the "user interaction service" and use it - the calls would block (assuming this method is running on a background thread):

The "user interaction service" would be responsible for sleeping the background thread while waiting for a response on the main thread. This is where my lack-of-knowledge in Unity shows, but based on a little googling one suggested approach is to have some sort of state/flag that indicates when a co-routine should be started:

Code (CSharp):

publicstring GetResponse(string question)

{

if(this.MainThread== Thread.CurrentThread)

{

thrownew InvalidOperationException("Blocking methods must be called from a background thread!");

}

this.StartTime= DateTime.Now.Ticks;

this.Question= question;

//Background thread is blocked until response is received or it times out

The problem appears to revolve around needing to block a call until user input has completed. If the originating call is on unity's main thread anyway, this is not something you want to do as it will block internal process. If the call is occurring outside of main thread then you can simply sleep until your state is switched.

Click to expand...

I answered the problem here "if the call is occurring outside of the main thread then you can simply sleep until your state is switched".

if the calling code is running on a background thread then you can simply while( state ) sleep; the returning call and block there with no impact on unity and you already have a separate thread running to notify unity main thread.

Click to expand...

The problem was that @manutoo didn't understand "while( state ) sleep;" or other methods of thread notification.
This is why when he discovered WaitHandle.WaitOne() the question was more or less solved.

Code (csharp):

if(p.SelectedTournament.IsFull)// this depends of previous player choices

{

CGui.OpenMenuNonBlocking("AskUserIfHeWantsToGoToALowerTournament");

//----------------------

// SOLUTION IS THIS LINE

//----------------------

WaitForUserResponse.WaitOne();

//----------------------

// ALTERNATE SOLUTION IMPLEMENTATION

//----------------------

while( CGui.Result== EAnswer.e_Unspecified) Thread.sleep(1);

// END OF SOLUTIONS

if(CGui.Result== EAnswer.e_Yes)

EnterTournament(p, p.NewlyChosenTournament);

}

The problem in this thread is that I assumed that @manutoo was asking a more complex question than "how do I sleep a thread" and he apparently didn't understand my answers since they were overly brief (since I assumed his question had to be more complex than this).

In sum, when I said, "If the call is occurring outside of main thread then you can simply sleep until your state is switched." he should have said, "Oh this is what I want to do, how do I implement that?"

Instead he rambled about how skilled he was and assumed I didn't understand multithreading. It was an example of dunning krueger in action.

Finally, it's worth noting that if @manutoo is still creating an additional thread (when his calling code here is already running on it's own thread (which it must be in order to block while waiting for user input)) then he's still doing it wrong.

@Doug_B,
actually, the 1) is easy, it's the ~10 lines of code exposed in my solution in the 1st post. And done.
So now why should I break into thousand pieces a code that works decently well & that provides a logical flow easy to follow ?
And how future code would be easier to develop & maintain if broke into thousand of pieces instead of just adding my 10 lines of code that will work once and for all for existing & future code ?
It's especially true as I may not know in advance what parts will need to be broken into pieces, as I may think of new functionalities later on (eg: adding stuff from user feedback).

For me, it's now ultra obvious the sub-thread solution is a far superior solution. I wasn't totally convinced when I thought about it at 1st, so I can understand that a seasoned developer might need a little moment to wrap his head around it, but the total negation I met here is rather surprising...

@Jingle-Fett,
same than neginfinity but you even didn't understand how the pseudo code works at 1st... (actually, I'm not sure neginfinity got it, but I let him the benefice of the doubt )
And for the global picture, see my answer above to Doug_B .

Click to expand...

No, I understood your pseudocode. But this isn't the scripting section of the forum. The example I gave was to show the general idea of the solution, its your job to figure out how you can adapt the solution your specific code. Your original question was how others deal with nested functions that depend on the result of the next one. Coroutines are a way to do that and I showed it in the example I gave.

EDIT Extra explanation :
In term of coding graph, I guess you could see it as nested functions like this : A() -> B() -> C() -> D() -> E() -> F() with the user input needed in F() (dependent of F() processing) while E() requires F() result to go on, D() requires E() result, and so on.

So if you had to process a lot of non-Unity data using a lot of non-Unity nested functions, and required process-dependent user input in the middle of that giant mess, how did you do it ?

I have over 30 years coding experience. The first thing I do when building a solution is try to make it simple. Both simple to build and simple to maintain. Anytime code feels like the "middle of that giant mess", it is time to refactor the code in a more simple way.

For input systems in Unity, I don't have a bunch of input code scattered throughout many different classes, because that tends to cause trouble once a project grows. I always set up an input class and then attach that class to an object in the scene. For example, I will often have a class called InputLayer.cs and that is the only class that accepts input. All of the other classes that react to input will have a reference to that class. My InputLayer class will have public variables exposed for all of the actions the input will trigger.

If I need a user customizable input scheme in a Unity project, I will use Rewired and then my InputLayer class will get input data from Rewired and then expose the actions through public variables. All of my other classes will still get input from my InputLayer class. None of my other classes will get input data directly.

Also, I do not attach my InputLayer class to my player controller object, because the player controller will get disabled during menus and transitions.

@AndersMalmgren,
it's a mess but it's maintainable, and I intend to keep it that way...

@Doug_B,
thanks, and I'll report the result in this topic once it's done in a few months, especially if I was wrong and something went horribly bad...

@neginfinity & @Jingle-Fett,
I don't need the general idea about how to use coroutines ; I already used them in the past and will do again every time they are the right tool for the situation.
But here the point would to show that using coroutine doesn't create horrible code hard to maintain. Which you didn't do because even with so short code, it's already inconvenient to use, so with a bit of imagination, it's not hard to conclude that with 80k+ lines of intricate code, it'd be way more nightmarish.

@spiderpk,
as you have StartCoroutine() in the Update(), it means you have mixed up something and not achieve the needed functionality, which is to block the sub-thread (a Coroutine isn't blocking, it's its point )...

@frosted,
my question was not about the details of the implementation (I know how to find & read a tutorial ), but about the possible unforeseen consequences of this choice, especially maintenance wise. (3rd or 4th time I write this in this topic )

And I didn't assume you didn't understand multi-threading ; I just known you didn't understand what I was asking, which you just shown 1 more time...

@ShilohGames,
I do similar things as well as it's 101 of coding, so it's really not the issue in this topic.

About level of complexity, it's inevitable and I don't need a lecture from someone who has no idea what is in my code.

@Everybody,
little game : there's something very important (and very short) that is missing maintenance-wise in the pseudo-code solution in the 1st topic. Will you be able to find it..?

Just a side note, the service locator pattern is a anti pattern. Constructor injection is the way togo, for that we need to be able to control how objects are instanced in Unity, sadly I dont think its possible with Unity since they control when the objects are created.

It seems like it would be easier to just re-do the project in Unity if you really want to port the project to Unity, using a lot less line of codes and using it's built-in abilities and functions... along with porting over your existing assets. If it is originally 100's of lines of C++ coding, a lot of it can be simplified using Unity (not just the C# language). I don't think I fully understand either.

In your orginal code example from first post you had a Monobehaviour that requested a menu in the RequestUserInput() method. Now it seems its not a monobehaviour but the actual touring code?

If I understand this correctly CGui.WaitForMenu will land in CLR land and display a menu inside Unity, so if this is correct I would just make sure your Cpp Touring engine runs on a seperate thread. The CLR code that triggers when CGui.WaitForMenu triggers should look something like this (pseudo-code)

Code (CSharp):

publicclass CppToCLRInterop

{

private AutoResetEvent sync =new AutoResetEvent();

publicvoid OpenMenu()

{

DisplayUnityMenu(); //This need to trigger menu display on Unity thread, it can be done by putting the menu request on a Queue and in a Monobehaviour Update display the menu

edit: I want to point out that blocking code like CGui.WaitForMenu("AskUserIfHeWantsToGoToALowerTournament"); is a thing of the past. The modern way is async programming, but since OP does not want to change his code this is the only solution