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);
}
Related
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'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 need to call a function on Windows Store app side by checking a boolean from unity script. How to do that i used this code but it gives errors. I just need a simple solution in which i check a bool declared in unity script to call a function at ported windows store app end
using System;
using UnityEngine;
public class SphereScript : MonoBehaviour
{
private bool m_IsMoving = true;
private bool m_IsMovingLeft = false;
public Camera GameCamera;
public event Action<bool> SphereStateChanged;
public bool IsSphereMoving { get { return m_IsMoving; } }
void Start()
{
if (GameCamera == null)
{
throw new Exception("Camera is not attached to the sphere script!");
}
}
void FixedUpdate()
{
if (!m_IsMoving)
{
return;
}
if (m_IsMovingLeft)
{
transform.position -= new Vector3(0.2f, 0.0f);
if (GameCamera.WorldToScreenPoint(transform.position).x < 100.0f)
{
m_IsMovingLeft = false;
}
}
else
{
transform.position += new Vector3(0.2f, 0.0f);
if (GameCamera.WorldToScreenPoint(transform.position).x > Screen.width - 100.0f)
{
m_IsMovingLeft = true;
}
}
}
void OnGUI()
{
var buttonText = m_IsMoving ? "Stop sphere" : "Start sphere movement";
if (GUI.Button(new Rect(0, 0, Screen.width, 40), buttonText))
{
m_IsMoving = !m_IsMoving;
if (SphereStateChanged != null)
{
SphereStateChanged(m_IsMoving);
}
}
}
}
Complete code is here
The way I understand you question is that when something happens on the Unity side, you'd like to call a method on the Windows Store app side, right?
A nice way to do this is to have an event in your Unity code that your Win code can register for. For the sake of this example I'm going to call your event ButtonClicked.
First you need a static class that the event will be in. Create a new C# script in the Unity Assets folder and open it up. I called mine StaticInterop. Clear the code that was generated, and make it this:
public class StaticInterop
{
public static event EventHandler ButtonClicked;
public static void FireButtonClicked()
{
if (ButtonClicked != null)
{
ButtonClicked(null, null);
}
}
}
Now, in Unity whenever that thing happens (in this case when the button is clicked), do this line of code:
StaticInterop.FireButtonClicked();
That is everything on the Unity side done. So create a build, and open up the VS Windows Store app project it creates.
In the Win code you can now sign up for the event like this:
StaticInterop.ButtonClicked+= StaticInterop_ButtonClicked;
And declare this method for it to run:
void StaticInterop_ButtonClicked(object sender, EventArgs e)
{
// Do whatever you need here.
}
The important thing to note is that the StaticInterop static class is showing up in your Win code. This means that you can use it to transfer anything between the Unity and Win side. You could even have a method in the class called something like PauseGame(), and then run that from the Win side with StaticInterop.PauseGame().
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.
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.