I'm stuck at Unity scripting. I got 3 files : Scene.cs , Player.cs and NetworkUtil.cs . I can't compile my code, as I don't know how to pass Coroutine response back to Scene.
In Scene.cs ( MonoBehavior class ) :
void OnGUI() {
if(GUI.Button(new Rect(0, 0, 100, 100), "Register")) {
StartCoroutine(registerPlayer());
}
}
IEnumerator registerPlayer() {
return Player.NewPlayer("Raptor"); // since Player is not IEnumerator, this line causes error.
}
In Player.cs ( Object class ) :
public static Player NewPlayer(string name) {
Player p = new Player();
result = p.tryRegister();
if(result) {
return p;
} else {
return null;
}
}
private bool tryRegister() {
Dictionary<string, string> data = new Dictionary<string, string>();
data.Add("some_key", "some_value");
NetworkUtil.ExecuteAPI(data);
}
In NetworkUtil.cs ( Object class ) :
public static IEnumerator ExecuteAPI(Dictionary<string, string> data) {
WWWForm form = new WWWForm();
form.AddField("mode", request);
foreach(KeyValuePair<string, string> entry in data) {
form.AddField(entry.Key, entry.Value);
}
WWW handler = new WWW(API_URL, form);
yield return handler;
if(handler.error != null) {
Debug.Log("Error occurred: " + handler.error); // Server Error
} else {
Debug.Log("Response: " + handler.text);
}
}
How can I change the codes to make the flow complete?
Note: In Object class, StartCoroutine() does not exist.
EDIT
I have changed my codes to use Delegates & Events:
// DelegatesAndEvents.cs
public class DelegatesAndEvents : MonoBehaviour {
public delegate void RegisterEventHandler(Player player);
public static event RegisterEventHandler onPlayerRegister;
public static void NewPlayerRegistered(Player player) {
if(onPlayerRegister != null) {
onPlayerRegister(player);
}
}
}
Then in Scene.cs:
void Start() {
DelegatesAndEvents.onPlayerRegister += this.userRegistered;
}
public void userRegistered(Player player) {
Debug.Log("User registered.");
}
How should I put the trigger in NetworkUtil.cs & Player.cs ?
There are several ways of doing that, because you not only need to return an IEnumerator, but it's Unity itself that will iterate on it, so you don't have the possibility to catch the return value.
Technically is possible to do the following:
IEnumerator registerPlayer() {
yield return Player.NewPlayer("Raptor"); // this is legal, player will be the Current value of the IEnumerator
}
If you want to do do something like the code above, you need to wrap the coroutine inside another, and iterate on it by yourself (I did something similar implementing behavior trees):
IEnumerator Wrap(IEnumerator playerRegister)
{
while(playerRegister.MoveNext())
{
Player p = playerRegister.Current as Player; //this can be done if you know what you are doing
yield return null;
}
}
Another way is not return anything and pass a delegate to the coroutine, that will be called passing back to the caller the required parameter.
Something like:
IEnumerator DoSomething(Action<Response> whenDone)
{
while (doingSomething)
yield return null;
whenDone(response);
}
EDIT
The other problem with your code is that you are calling another coroutine (ExecuteAPI)from inside Player's constructor.
So, making registerPlayer a coroutine is pointless since you are not yielding anything.
In your case the simplest way to go is to pass a delegate to ExecuteAPI that will be called when you receive the response from the server.
Related
I have a class Program which is a list of Nodes that contain a method IEnumerator Invoke() and the Program class iterates through each Node invoking it. I want to be able to provide methods to Start, Pause, Resume, and Stop execution. Starting would cause the invocation to start at the top of the list, Pausing would effectively 'Stop' the execution and allow Resume to be able to pick up wherever execution was when Pause was called, and Stop would cease all function and would require Start to be called to begin again. With Unity's built-in Coroutines is this even possible, and if it is how do I Pause/Resume a coroutine?
EDIT
what I'm looking for is how to essentially pause an instance of Program and be able to resume it at the same step.
If I understand one of the comments correctly the suggestion it makes would be something similar to this?
public abstract class Node {
public abstract IEnumerator Invoke(ProgramCaller caller);
}
public class Program : Node {
private List<Node> nodes;
public override IEnumerator Invoke(ProgramCaller caller) {
int index = 0;
while(index < nodes.Count) {
if(caller.Paused) {
yield return null;
}
else {
yield return nodes[index].Invoke(caller);
index++;
}
}
}
}
So from what I read is you have e.g.
public class Node
{
public IEnumerator Invoke()
{
yield return null;
}
}
Then a Unity Coroutine is basically using the IEnumerator and invoking MoveNext on certain intervals (Update by default except using the special ones like e.g. WaitForFixedUpdate etc).
So you could simply make Program implement that like e.g.
public class Program : IEnumerator
{
public Node[] nodes;
private int index = -1;
private IEnumerator currentNode;
public bool MoveNext()
{
if (nodes == null || nodes.Length == 0)
{
return false;
}
while (currentNode == null)
{
index++;
if (index >= nodes.Length)
{
return false;
}
currentNode = nodes[index]?.Invoke();
}
if (currentNode.MoveNext())
{
return true;
}
currentNode = null;
return true;
}
public void Reset()
{
index = -1;
currentNode = null;
}
public object Current => null;
}
and then you can link this up to a Coroutine from a MonoBehaviour like e.g.
public class Example : MonoBehaviour
{
public Program program;
private Coroutine currentRoutine;
// just a name alias
public void StartProgram() => RestartProgram();
public void RestartProgram()
{
StopProgram();
ResumeProgram();
}
public void ResumeProgram()
{
currentRoutine = StartCoroutine(program);
}
public void PauseProgram()
{
if (currentRoutine != null)
{
StopCoroutine(currentRoutine);
}
}
public void StopProgram()
{
PauseProgram();
program.Reset();
}
}
as you see the only difference between Start/Stop and Pause/Resume is resetting or not resetting the Program.
Alternatively and maybe even more simple: A Coroutine is paused automatically when disabling according MonoBehaviour and resumed when enabling it again.
=> If it is an option for you to have a dedicated runner component for each program then all you need really is the resetting part and you could simply do
public class Program
{
public Node[] nodes;
public IEnumerator Run()
{
foreach (var node in nodes)
{
yield return node.Invoke();
}
}
}
This way you can run them all as a single IEnumerator and then
public class Example : MonoBehaviour
{
public Program program;
private Coroutine currentRoutine;
// just a name alias
public void StartProgram() => RestartProgram();
public void RestartProgram()
{
StopProgram();
currentRoutine = StartCoroutine(program.Run());
}
public void ResumeProgram()
{
enabled = true;
}
public void PauseProgram()
{
enabled = false;
}
public void StopProgram()
{
if (currentRoutine != null)
{
StopCoroutine(currentRoutine);
}
}
}
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.
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);
}
I'm making an event that does the failLevel stuff when it fires off. For that I have made a delegate
public delegate Coroutine FailGame(IEnumerator function);
public static event FailGame gameFailedEvent;
like so and I subscribed the appropriate function to it
void Start ()
{
gameFailedEvent += StartCoroutine;
}
It works when it is called from the same script like so:
gameFailedEvent(WaitThenFailLevel());
when this WaitThenFailLevel() looks like this:
IEnumerator WaitThenFailLevel()
{
CharacterController2D.playerDied = true;
if (CharacterController2D.animState != CharacterController2D.CharAnimStates.fall)
{
CharacterController2D.currentImageIndex = 0;
CharacterController2D.animState = CharacterController2D.CharAnimStates.fall;
}
CharacterController2D.movementDisabled = true;
yield return new WaitForSeconds(0.2f);
StartCoroutine(ScaleTime(1.0f, 0.0f, 1.2f));
}
It works fine here. Now, I have another object that can kill the player (dangerous times I know) and I don't want to copy paste everything again, I just want it to fire off the static event made in the script above.
I DID try making the WaitThenFailGame function
public static
and make static all my other ienumerators but I got an error named "An object reference is required for non-static field..."
Hence I tried the event stuff.
All well and fine, but I can't call the event from the other script because I can't pass it the function from the script mentioned.
What to do now?
Here is the example code:
EventContainor.cs
public class EventContainer : MonoBehaviour
{
public static event Action<string> OnGameFailedEvent;
void Update()
{
if (Input.GetKey(KeyCode.R))
{
// fire the game failed event when user press R.
if(OnGameFailedEvent = null)
OnGameFailedEvent("some value");
}
}
}
Listener.cs
public class Listener : MonoBehaviour
{
void Awake()
{
EventContainer.OnGameFailedEvent += EventContainer_OnGameFailedEvent;
}
void EventContainer_OnGameFailedEvent (string value)
{
StartCoroutine(MyCoroutine(value));
}
IEnumerator MyCoroutine(string someParam)
{
yield return new WaitForSeconds(5);
Debug.Log(someParam);
}
}
using UnityEngine;
public class ScriptA : MonoBehaviour
{
public ScriptB anyName;
void Update()
{
anyName.DoSomething();
}
}
using UnityEngine;
public class ScriptB : MonoBehaviour
{
public void DoSomething()
{
Debug.Log("Hi there");
}
}
This is linking functions between scripts , Copied from Here, maybe coroutines are the same, Then you need to start the coroutine in void Start() {} , You may find this useful as well.
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.