Promises with Unity - c#

I want a Character class, inheriting from MonoBehavior and exposing handfuls methods : Walk, Attack, ...
However, assuming that two components use these methods at the same time, I would like to queue the actions and have some way to notify the components that their actions have been executed.
In Javascript standard, I would have done something like this :
var Character = function ( ) {
this._deferred = new Deferred( );
};
Character.prototype.walk = function ( ) {
return this._deferred = this._deferred.then( function ( ) {
// Do the actual walk
} );
};
Character.prototype.attack = function ( ) {
return this._deferred = this._deferred.then( function ( ) {
// Do the actual attack
} );
};
var character = new Character( );
// Component A
character.walk( ).then( function ( ) {
// Executes when the walk is done
} );
// Component B
character.attack( ).then( function ( ) {
// Executes when the walk AND attack is done
} );
What is the right way to do so with Unity / C# ?

Preamble
For the answer I will use this "I would like to queue the actions" as a description of your problem.
There are lot of ways to solve this problem. And I don't pretend to be comprehensive.
Even in JS I would consider using Promises for queuing character commands as wrong choice.
I haven't ever compiled, run or tested the code that i present here :D
Promises in C#
There is some C# port of Promises on github. I haven't ever used it, but the code doesn't seem to contain anything that stops it from being used in Unity. Anyway, you can give it a try.
Using Queue<>
I would definitely use Queue<TCommand> of some TCommand for this problem. The only question is what to use as TCommand. I will give you 2 examples here. But, as usual, there are more options.
Some class
Something like this:
public enum CommandUpdateResult
{
Ongoing,
Finished
}
public interface ICommand
{
CommandUpdateResult Update();
}
public class RunCommand: ICommand
{
// Bla-bla-bla
}
public class AttackCommand: ICommand
{
// Bla-bla-bla
}
public class Character: MonoBehaviour
{
private Queue<ICommand> commandQueue;
public void Awake()
{
commandQueue = new Queue<ICommand>();
}
public void Update()
{
if (commandQueue.Count > 0 && commandQueue.Peek().Update() == CommandUpdateResult.Finished)
commandQueue.Dequeue();
}
public void EnqueueCommand(ICommand command)
{
commandQueue.Enqueue(command);
}
}
public class SomeClassThatUsesCharacter
{
private Character character;
public void SomeMethodThatUsesCharacter()
{
character.EnqueueCommand(new RunCommand(bla-bla-bla));
character.EnqueueCommand(new AttackCommand(bla-bla-bla));
}
}
IEnumerator
The simplest (but not very elegant) way to use IEnumerator is to use it with some infinite coroutine.
public class Character: MonoBehaviour
{
private Queue<IEnumerator> commandQueue;
private IEnumerator CommandQueueCoroutine()
{
while (true)
{
if (commandQueue.Count > 0)
yield return StartCoroutine(commandQueue.Peek());
else
yield return new WaitForFixedUpdate();
}
}
public void Awake()
{
commandQueue = new Queue<ICommand>();
StartCoroutine(CommandQueueCoroutine());
}
public void Update()
{
if (commandQueue.Count > 0 && commandQueue.Peek().Update() == CommandUpdateResult.Finished)
commandQueue.Dequeue();
}
public void Enqueue(IEnumerator command)
{
commandQueue.Enqueue(command);
}
IEnumerator RunCommand()
{
while (Jenny.Tells("Run"))
{
transform.position.x += 1;
yield return new WaitForFixedUpdate();
}
}
IEnumerator AttackCommand(BadGuy badGuy)
{
badGuy.Die();
yield break;
}
}
public class SomeClassThatUsesCharacter
{
private Character character;
public void SomeMethodThatUsesCharacter()
{
character.Enqueue(character.RunCommand());
character.Enqueue(character.AttackCommand(someBadGuy));
}
}

A very good Unity Promise library: https://github.com/Real-Serious-Games/C-Sharp-Promise and see related blog http://www.what-could-possibly-go-wrong.com/promises-for-game-development/
Alternatively I think you can also go down an RX approach with Observers that react to Walk and Attack events. See https://github.com/neuecc/UniRx

Well, at the moment Unity already allows you to use:
TaskCompletionSource as a promise;
Task as a future;
but if Promise itself is more suitable for your project give it a try by using: C-Sharp-Cancellable-Promise library
This is a fork of Real-Serious-Games Promise lib updated to the latest .NET and with Promises cancellation support.

Related

How to repeatedly execute a method until said otherwise

I have an EventSystem for managing my turn-based game in Unity.
public class EventSystem : MonoBehaviour
{
private static List<Action> _commandsQueue = new List<Action>();
private bool _canExecuteCommand = true;
public void AddToQueue(Action command)
{
_commandsQueue.Add(command);
}
private void StartCommandExecution()
{
_commandsQueue[0]();
_canExecuteCommand = false;
}
public void CommandExecutionComplete()
{
_canExecuteCommand = true;
}
public void PlayFirstCommandFromQueue()
{
if (_commandsQueue.Any() && _canExecuteCommand)
{
StartCommandExecution();
}
else
{
Debug.LogError("No Command In Queue");
}
}
}
How do I put a method in Update() until _canExecuteCommand is true again but only for some methods?
It is quite broad what you are trying to do but in general you would use an endless loop within a Coroutine.
You can create a generic routine which invokes any Action you pass in as parameter once a frame like e.g.
private IEnumerator InvokeEveryFrame(Action action)
{
// This looks strange but is okey in a Coroutine as long as you yield somewhere within
while(true)
{
action?.Invoke();
// This tells Unity to "pause" this routine here
// render the current frame and continue from here in the next frame
yield return null;
}
}
So all that's left is starting the routine using MonoBehaviour.StartCoroutine like e.g.
Coroutine routine = StartCoroutine(SomeParameterlessMethod);
or if you need parameters
Coroutine routine = StartCoroutine(() => SomeMethod(x, y, z));
and then at some point later stop it using MonoBehaviour.StopCoroutine and the stored Coroutine reference like e.g.
StopCoroutine(routine);
how exactly you store that reference is up to you of course up to you.

How to debug the fact that StartCoroutine wont start the coroutine with no error?

The Class Gettext starts a Unitywebrewuest to google.com but after the console pints "index1" nothing happens and no error are displayed. How could I solve / debug this ( in Visual Studio I can't start the debugger).
Thank you a lot for the time!
public class Gettext : UnityEngine.MonoBehaviour
{
private string m_fileContentWebGl = "aaa";
public Gettext()
{
callGettext(m_fileContent => {
});
}
public void callGettext(Action<string> onTextResult)
{
UnityEngine.Debug.Log("stage1");
StartCoroutine(this.GetText(onTextResult));
UnityEngine.Debug.Log("stage3");
}
public IEnumerator GetText(Action<string> onResult)
{
UnityEngine.Debug.Log("here");
UnityWebRequest www = UnityWebRequest.Get("http://google.com");
yield return www.SendWebRequest();
}
}
I think its pretty odd that you are using a constructor for a monobehaviour derived class. You're not supposed to use them, that may be affecting the coroutine, I dunno.
Try changing
public Gettext()
{
callGettext(m_fileContent => {
});
}
to
void Start
{
callGettext(m_fileContent => {
});
}

Better method then long if statements in unity c#

Im working on a game in which I have a canon that fires at difrent amounts of time. Instead of writing a long if statement like this:
If (time == 1) {
"Do something "
} else If (time == 2) {
"Do something else "
} else If (time == 3) {
" You probaly get the point"
} else {
" My real problem is much longer than this. This is just.
an.
example"
}
So if there is an easier and prettier way to do this in c# that would be great. If not let me know also. Thanks in advance
As mentioned in the comments
Using a Switch Statement is probably what you're looking for.
private static void FireCanons(int time)
{
switch (time)
{
case 1:
// "do something";
break;
case 2:
// do something else
break;
case 3:
// you probably get the point"
break;
default:
// My real problem is much longer than this. This is just. an. example"
break;
}
}
Update:
Just looking at your comments, a Dictionary may be better suited.Here is a quick example I have made.
private static void FireCanons(int time)
{
// A Dictionary That Will Hold Our Results.
var myList = new Dictionary<int, string>
{
{ 1, "do something" },
{ 2, "do something else" },
{ 3, "you probably get the point" },
{ 4, "My real problem is much longer than this. This is just. an. example" }
};
// Attempt To Get The Value.
if (myList.TryGetValue(time, out string result))
{
// Display Results.
Console.WriteLine(result);
}
}
Dictionary is another approach. Create Dictionary where you can register different actions for every value of time
void DoSomething()
{
// Do something
}
void DoSomethingElse()
{
// Do else
}
void YouProbalGetThePoint()
{
// You probaly get the point
}
Register actions
var actions = new Dictionary<int, Action>
{
{ 1, DoSomething },
{ 2, DoSomethingElse },
{ 3, YouProbalGetThePoint },
{ 4, DoSomething },
}
Then usage can be one liner:ish
var action = actions.GetValueOrDefault(time, () => {});
action();
Such approach provide possibility to change behaviour of your code without changing the code in the function - Open-Close Principle ;)
The current answers are fine ... to answer your question if time actually is an int value.
This is just a guess but usually when you spak about time you have a float value as e.g. from Time.time.
For this your approach using ==, a switch and a dictionary as well would be completely useless since the odds that an exact time of e.g. 2.0 is matched in a certain frame is very close to 0. (not even speaking about the single floating point precision here...)
For this you rather might want to have a look at Coroutines and WaitForSeconds
You can create an Extension method
public static class MonoBehaviourExtensions
{
public static void Invoke(this MonoBehaviour me, Action theDelegate, float time)
{
me.StartCoroutine(ExecuteAfterTime(theDelegate, time));
}
private static IEnumerator ExecuteAfterTime(Action theDelegate, float delay)
{
yield return new WaitForSeconds(delay);
theDelegate?.Invoke();
}
}
and than simply use e.g.
private void Start()
{
this.Invoke(DoSomething, 1);
this.Invoke(DoSomethingElse, 2);
this.Invoke(YouGetThePoint, 3);
}
// will be executed 1 second after start
private void DoSomething()
{ ... }
// will be executed 2 seconds after start
private void DoSomethingElse()
{ ... }
// will be executed 3 seconds after start
private void YouGetThePoint()
{ ... }
If you than want those to be executed again after a ceratin delay you could simply add a new call at the end like e.g.
private void DoSomething()
{
...
this.Invoke(DoSomething, 1);
}
or alternatively add another extension method to do so:
public static class MonoBehaviourExtensions
{
public static void Invoke(this MonoBehaviour me, Action theDelegate, float time)
{ ... }
private static IEnumerator ExecuteAfterTime(Action theDelegate, float delay)
{ ... }
public static void InvokeRepeated(this MonoBehaviour me, Action theDelegate, float timeInterval)
{
StartCoroutine(ExecuteInIntervals(theDelegate, interval));
}
private static IEnumerator ExecuteInIntervals(Action theDelegate, float interval)
{
while(true)
{
yield return new WaitForSeconds(interval);
theDelegate?.Invoke();
}
}
}
and than use instead
private void Start()
{
this.InvokeRepeated(DoSomething, 1);
this.InvokeRepeated(DoSomethingElse, 2);
this.InvokeRepeated(YouGetThePoint, 3);
}
so DoSomething is called every second, DoSomethingElse is called every 2 seconds and YouGetThePoint is called every 3 seconds.

C# Custom Invoke method to invoke various other methods

I have various methods that work fine, but I want to call them only after a delay. To avoid writing a different method for all of them I figured it's more beneficial to Invoke them somehow. I made the methods so that they exclude Unity's timeScale, so they always wait for Real seconds, using a custom built short function.
The WaitForRealSeconds:
public class WaitForRealSecondsClass
{
#region Wait for real seconds
public Coroutine WaitForRealSeconds(float aTime, MonoBehaviour mono)
{
return mono.StartCoroutine(_WaitForRealSeconds(aTime));
}
private IEnumerator _WaitForRealSeconds(float aTime)
{
while (aTime > 0.0f)
{
aTime -= Mathf.Clamp(Time.unscaledDeltaTime, 0, 0.2f);
yield return null;
}
}
#endregion
}
The way I wish to Invoke a Move function of mine:
public void InvokeAnim(float timeBeforeStart, Action<MonoBehaviour> MoveFunction, MonoBehaviour mono)
{
if (moveRoutine != null)
{
mono.StopCoroutine(moveRoutine);
}
moveRoutine = _InvokeAnim(timeBeforeStart, MoveFunction, mono);
}
IEnumerator _InvokeAnim(float timeBeforeStart, Action<MonoBehaviour> MoveFunction, MonoBehaviour mono)
{
yield return new WaitForRealSecondsClass().WaitForRealSeconds(timeBeforeStart, mono);
MoveFunction(mono);
}
And the Move(MonoBehaviour mono) itself:
public void Move(MonoBehaviour mono)
{
if (moveRoutine != null)
{
mono.StopCoroutine(moveRoutine);
}
moveRoutine = _Move(from, to, overTime, mono);
mono.StartCoroutine(moveRoutine);
}
What I tested and worked is the Move itself, the WaitForRealSeconds I used in another project for UI waiting when the game was stopped, it was fine then.
As I said I have many methods to invoke, all of them return void and have a parameter MonoBehaviour. Currently it doesn't do anything and I have no idea why.
Sod it, I was dump enough to forget actually Starting that coroutine.
In my Invoke:
public void InvokeAnim(float timeBeforeStart, Action<MonoBehaviour> MoveFunction,
MonoBehaviour mono)
{
if (moveRoutine != null)
{
mono.StopCoroutine(moveRoutine);
}
moveRoutine = _InvokeAnim(timeBeforeStart, MoveFunction, mono);
mono.StartCoroutine(moveRoutine); //one line was missing
}
IEnumerator _InvokeAnim(float timeBeforeStart, Action<MonoBehaviour> MoveFunction,
MonoBehaviour mono)
{
yield return new WaitForRealSecondsClass().WaitForRealSeconds(timeBeforeStart, mono);
MoveFunction(mono);
}

Java Equivalent to C#'s Delegates/Events: Changing Event Invoked Code Dynamically

I have a simple Screen class in C# that has a bunch of events (with corresponding delegates) like the FadeOutEvent.
I want to port my library to Java, and I find that the mechanism for events/delegates is really cludgey. Specifically, I cannot easily write code like:
if (someVar == someVal) {
this.FadeOutComplete += () => {
this.ShowScreen(new SomeScreen());
};
} else {
this.FadeOutComplete += () => {
this.ShowScreen(new SomeOtherScreen());
};
}
For all you Java-only guys, essentially, what I'm whinging about is the inability to reassign the event-handling method in the current class to something else dynamically, without creating new classes; it seems that if I use interfaces, the current class must implement the interface, and I can't change the code called later.
In C#, it's common that you have code that:
In a constructor / early on, assign some event handler code to an event
Later during execution, remove that code completely
Often, change that original handler to different handler code
Strategy pattern can solve this (and does), albeit that I need extra classes and interfaces to do it; in C#, it's just a delcarative event/delegate and I'm done.
Is there a way to do this without inner/anonymous classes?
Edit: I just saw this SO question, which might help.
Most of the time, it's done the other way round:
this.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (someVar == someVal) {
showSomeScreen();
}
else {
showSomeOtherScreen();
}
}
});
But you could do something similar to your C# code by delegating to two other objects:
private Runnable delegate;
// ...
this.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
delegate.run();
}
});
// ...
if (someVar == someVal) {
this.delegate = new Runnable() {
#Override
public void run() {
showSomeScreen();
}
};
}
else {
this.delegate = new Runnable() {
#Override
public void run() {
showSomeOtherScreen();
}
};
}
Delegates were proposed by Microsoft for Java a long long time ago, and were refused by Sun. I don't remember if anonymous inner classes already existed at that time or if they were chosen as the alternative.
With lambdas in JDK 1.8 / Java 8:
private Runnable delegate;
public void delegateTest() {
// ...
this.addActionListener(e -> delegate.run());
// ...
if (someVar == someVal) {
this.delegate = () -> showSomeScreen();
}
else {
// or like this:
this.delegate = this::showSomeOtherScreen;
}
}
private void showSomeOtherScreen() {
}
private void showSomeScreen() {
}

Categories

Resources