How to keep program linear and synchronous while getting user input?
I'm trying to convert my Java code for a Monopoly game into a Unity game. The game logic follows a very linear sequence of events that lends itself to synchronous execution.
The main obstacle I'm running into is with user input. Whenever I needed user input in Java, I'd simply use a Scanner object that would halt the execution of the program until an input is given. I'm not sure how to accomplish this in Unity, at least the halting of the code part. I tried making a communication script between the Unity UI and my program logic. I've been successful in using the script to display the UI, and when a button is clicked, I can call a method on the script. The problem is that I don't know how to make the script wait to return the input until after the button method is called.
Here's an example of some code where I need the user's input:
public override void landOn(Player player){
bool buyIt = uI.askIfBuyProperty();
if (buyIt == true) buyProperty();
...etc.
}
Here is the code in my UI communication script. Unfortunately the while loop causes the program to freeze:
public class UIControllerScript : MonoBehaviour {
public GameObject popupTemplate;
private bool answer;
private bool replied = false;
public bool askIfBuyProperty()
{
popupTemplate.SetActive(true);
while (!replied) {}
return answer;
}
// method called when button is clicked
public void setAnswer(int ans)
{
replied = true;
answer = ans;
popupTemplate.SetActive(false);
}
}
I've considered having having my UI object call methods in my logic code whenever an input is given, but this would require me to restructure my entire project. I would need a new listener method for every UI input. Also, I'd need to somehow keep track of when a player's turn is over, who goes next, etc., because there is no set linear flow through the steps of the game. I really hope there is an easier way.
Answer by MaxGuernseyIII · Nov 07, 2017 at 02:10 AM
The Observer pattern (which .NET handily supports in the form on events) and the State pattern are how I manage this kind of thing in pretty much every domain - games or otherwise - but you've already excluded refactoring to recognize those patterns in your problem. It seems like, since you've considered it once, I'd implore you to consider it one more time.
If not, I guess I'd try to contort a coroutine into doing what you want. That would still involve some refactoring.
Your code would end up looking something like this:
public override void landOn(Player player)
{
StartCoroutine(LandOnCoroutine(player));
}
IEnumerable LandOnCoroutine(Player player)
{
YesNo buyIt = uI.askIfBuyProperty();
while (!buyIt.Answered) yield return null;
if (buyIt.Value == true)
yield return StartCoroutine(buyProperty());
...etc.
}
You have to write YesNo yourself. You can get fancier by making your "answer receipt" object generic rather than being coupled to a return type of bool and by making it inherit CustomYieldInstruction to integrate more smoothly into a coroutine's cooperative yield mechanism.
Thanks for your answer. I was also considering the coroutine option, but it seems like I'd need to make just about everything into a coroutine, right? Which becomes pretty un-managable. I'm thinking about using the observer pattern, as you recommended. In that case, if my understanding is correct, the Subject object would act as a sort of facade between user input and the game state, and it would contain methods that are triggered by button clicks and such, right? $$anonymous$$y player objects (which keep track of the internal state of things) would then act as observers, and change their states based on notifications from the Subject? I haven't used this pattern before, so I want to make sure that I'm implementing it correctly.
Yes. That's right. For example, you might have an interface like this:
public interface PurchasingUI
{
event Action OnPurchase;
void PurchasingIsPossible(Property p, int price);
void PurchasingIsNotpossible();
}
As you land on different cells, change players, or complete the sale of properties, you would keep the purchasing UI up to date on whether or not it was possible to purchase a property. Your $$anonymous$$onoBehaviour would translate that into the visibility and/or enablement of various buttons (e.g.). The appropriate UI artifacts, when interacted with, would then cause the OnPurchase method to be raised, which would cause your game's state to update.