I dont know how to make a command run then the timer stops
here is the code :
using UnityEngine;
using System.Collections;
using UnityEngine.SceneManagement;
using System.Threading;
using System;
public class introtogamescript : MonoBehaviour
{
public class Example
{
public static void Main()
{
// Create an instance of the Example class, and start two
// timers.
Example ex = new Example();
ex.StartTimer(7000);
}
public void StartTimer(int dueTime)
{
Timer t = new Timer(new TimerCallback(TimerProc));
t.Change(dueTime, 0);
}
private void TimerProc(object state)
{
// The state object is the Timer object.
Timer t = (Timer)state;
t.Dispose();
SceneManager.LoadScene(2);
}
}
}
Thanks for the help.
You can use coroutines like this:
public void WaitAndExecute(float timer, Action callBack = null)
{
StartCoroutine(WaitAndExecute_CR(timer,callBack));
}
IEnumerator WaitAndExecute_CR(float timer, Action callBack = null)
{
yield return new WaitForSeconds(timer);
if (callBack != null)
{
callBack();
}
}
void Start()
{
WaitAndExecute(5,() => {Debug.Log("This will be printed after 5 seconds");});
WaitAndExecute(10,JustAnotherMethod);
}
void JustAnotherMethod()
{
Debug.Log("This is another way to use callback methods");
}
When you start the timer use lambdas or anonymous delegate - this way you will have the timer as closure.
You can also use Invoke(string methodName, float callDelay)
Example:
void SomeMethod()
{
Invoke("OtherMethod",3.5f);
}
//It should be called 3.5 seconds after SomeMethod
void OtherMethod()
{
print(something);
}
And you can not invoke any parameter
Related
I have 2 C# classes one of them has a string delegate and the other subscribes an function to that delegate.
My question is I want to combine the two called string functions from the delegate instead of choosing the return value between them randomly
delgatesystem.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class delegatesystem : MonoBehaviour {
public delegate string MyDelegate();
public static event MyDelegate MyEvent;
string GameObjectsNames = "";
void Update ()
{
if (Input.GetKeyDown(KeyCode.Space))
{
if (MyEvent != null)
{
GameObjectsNames += MyEvent();
}
}
}
}
delegatesave.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class delegatesave : MonoBehaviour {
void Start ()
{
delegatesystem.MyEvent += DelegateFunction;
}
string DelegateFunction()
{
return gameObject.name;
}
}
note: the delgatesave.cs is attached to 2 gameobjects.
First of all, creating events with non-void delegates is an antipattern. Events are typically used with potentially more subscribers.
If a non-void multicast delegate is invoked with multiple subscribers, always the return value of the last subscribed method is returned.
But after all you can do something like this:
string[] objectNames = MyEvent.GetInvocationList().Cast<MyDelegate>().Select(del => del()).ToArray();
However, a better solution would be to use more conventional events:
public class PopulateNamesEventArgs : EventArgs
{
private List<string> names = new List<string>();
public string[] Names => names.ToArray();
public void AddName(string name) => names.Add(name);
}
And then in your class:
public event EventHandler<PopulateNamesEventArgs> MyEvent;
protected virtual void OnMyEvent(PopulateNamesEventArgs e) => MyEvent?.Invoke(this, e);
Invokation:
var e = new PopulateNamesEventArgs();
OnMyEvent(e);
string[] objectNames = e.Names; // the result is now populated by the subscribers
Subscription:
void Start()
{
delegatesystem.MyEvent += DelegateFunction;
}
void DelegateFunction(object sender, PopulateNamesEventArgs e)
{
e.AddName(gameObject.name);
}
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'm a Chinese developers, English is not very good, please predecessors read it for editing, thanks in advance.
In Unity, i write one script to listener an Event and in delegate function, i add another script the same listener in previous Event, but why the same listener be called immediately?
The following is add another script the same listener script:
using System.Collections.Generic;
using UnityEngine;
public class SceneManager : MonoBehaviour
{
private static SceneManager m_Instance;
public static SceneManager Instance
{
get
{
if (m_Instance == null)
{
m_Instance = (SceneManager)FindObjectOfType(typeof(SceneManager));
}
if (!m_Instance)
{
Debug.LogError("SceneManager could not find himself!");
}
return m_Instance;
}
}
[HideInInspector]
public List<EventDelegate> EventDelegetDo = new List<EventDelegate>();
public void RegisterBackBtnDo()
{
EventDelegate.Add(EventDelegetDo, Do);
}
public void UnRegisterBackBtnDo()
{
EventDelegate.Remove(EventDelegetDo, Do);
}
private void Start()
{
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.T))
{
EventDelegate.Execute(EventDelegetDo);
}
}
private void Do()
{
Debug.Log("===============ZJDebug=============== From SceneManager");
}
}
The following is initial register listener script:
using UnityEngine;
public class TDelegate : MonoBehaviour
{
public void RegisterBackBtnDo()
{
EventDelegate.Add(SceneManager.Instance.EventDelegetDo, Do);
}
public void UnRegisterBackBtnDo()
{
EventDelegate.Remove(SceneManager.Instance.EventDelegetDo, Do);
}
private void Start()
{
RegisterBackBtnDo();
}
private void Update()
{
}
private void Do()
{
Debug.Log("===============ZJDebug=============== From TDelegate");
SceneManager.Instance.RegisterBackBtnDo();
//Invoke("WaitForTest", float.NegativeInfinity);
}
private void WaitForTest()
{
SceneManager.Instance.RegisterBackBtnDo();
}
}
I have tried multiple variations of this, if invoke float.NegativeInfinity second register it not be called Immediately, can someone tell me why? in addition to delay call There is no better way called by after? English is too bad please forgive me thanks!
I have used Observer Pattern for my application.
I have a subject which has one System.Timers.Timer object in it named 'tmr'. The tick event of this timer fires after every 60 seconds. On this tick event I will notify all my observers that are attached to my subject. I have used a for-loop to iterate throught my Observers List & then fire the Observers Update method.
Assume i have 10 observers attached to my subject.
Each observer takes 10 seconds to complete its processing.
Now notification being done in a for-loop causes the last Observer's Update method to be called after 90seconds. i.e. Next Observer Update method is called only after previous one has completed its processing.
But this is not what i wanted in my application. I need all my observers Update method to be fired instantly when the timer tick occurs. So that no observer has to wait. I hope this can be done by Threading.
So, I modified code to,
// Fires the updates instantly
public void Notify()
{
foreach (Observer o in _observers)
{
Threading.Thread oThread = new Threading.Thread(o.Update);
oThread.Name = o.GetType().Name;
oThread.Start();
}
}
But I have two doubts in my mind,
If there are 10 observers
And my timer interval is 60 seconds
Then the statement new Thread() will fire 600 times.
Is it efficient and recommended to create new threads on every timer tick ?
What if my observers are taking too much time to complete their update logic i.e. goes more than 60seconds. Means the timer tick occurs before the observers are updated. How can i control this ?
I can post sample code.. if required...
The code i used..
using System;
using System.Collections.Generic;
using System.Timers;
using System.Text;
using Threading = System.Threading;
using System.ComponentModel;
namespace singletimers
{
class Program
{
static void Main(string[] args)
{
DataPullerSubject.Instance.Attach(Observer1.Instance);
DataPullerSubject.Instance.Attach(Observer2.Instance);
Console.ReadKey();
}
}
public sealed class DataPullerSubject
{
private static volatile DataPullerSubject instance;
private static object syncRoot = new Object();
public static DataPullerSubject Instance
{
get
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
instance = new DataPullerSubject();
}
}
return instance;
}
}
int interval = 10 * 1000;
Timer tmr;
private List<Observer> _observers = new List<Observer>();
DataPullerSubject()
{
tmr = new Timer();
tmr.Interval = 1; // first time to call instantly
tmr.Elapsed += new ElapsedEventHandler(tmr_Elapsed);
tmr.Start();
}
public void Attach(Observer observer)
{
_observers.Add(observer);
}
public void Detach(Observer observer)
{
_observers.Remove(observer);
}
// Fires the updates instantly
public void Notify()
{
foreach (Observer o in _observers)
{
Threading.Thread oThread = new Threading.Thread(o.Update);
oThread.Name = o.GetType().Name;
oThread.Start();
}
}
private void tmr_Elapsed(object source, ElapsedEventArgs e)
{
tmr.Interval = interval;
tmr.Stop(); // stop the timer until all notification triggered
this.Notify();
tmr.Start();//start again
}
}
public abstract class Observer
{
string data;
public abstract void Update();
public virtual void GetDataFromDBAndSetToDataSet(string param)
{
Console.WriteLine("Processing for: " + param);
data = param + new Random().Next(1, 2000);
Threading.Thread.Sleep(10 * 1000);//long work
Console.WriteLine("Data set for: " + param);
}
}
public sealed class Observer1 : Observer
{
private static volatile Observer1 instance;
private static object syncRoot = new Object();
public static Observer1 Instance
{
get
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
instance = new Observer1();
}
}
return instance;
}
}
Observer1()
{
}
public override void Update()
{
base.GetDataFromDBAndSetToDataSet("Observer1");
}
}
public sealed class Observer2 : Observer
{
private static volatile Observer2 instance;
private static object syncRoot = new Object();
public static Observer2 Instance
{
get
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
instance = new Observer2();
}
}
return instance;
}
}
Observer2()
{
}
public override void Update()
{
base.GetDataFromDBAndSetToDataSet("Observer2");
}
}
}
Thanks & kind regards.
Using new Thread is discouraged. Use Task or Task<T>
Your best attempt at creating an Observable pattern framework will probably only come close to Rx. Use that which solves the problems you mentioned (i.e. if processing takes too much time). Rx will give you huge flexibility in defining your observable scenarios.
1) You can use Threads from the ThreadPool via ThreadPool.QueueUserWorkItem or you can use Tasks
2) You have to synchronize your Methods.
Alternatively, the observers could implement Update in a non-blocking way.
That is, Update always returns immediately. Then it is the responsibility of the Observer objects to perform their work in a new thread if necessary.
I'm not sure if this helps in your scenario - I don't know what your 'Observers' are, but then maybe you don't know either?
This is what I want to do:
Have a timer with some interval
In the timer callback code, if some condition is met, another thread should be run
I’ve put my code in a class which is instantiated by the main form and the code is executed upon method call (‘StartSync()’, se sample code).
The problem is that the code runs for a couple of seconds but then terminates. I suppose I’m doing something stupid but I really can’t see what it is. Thankful for any help with regards to this.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Diagnostics;
namespace WindowsFormsApplication1
{
class Syncer
{
static bool SYNC_IN_PROGRESS;
public void StartSync()
{
SYNC_IN_PROGRESS = false;
Timer timer = new Timer(timerCallback, null, 0, 1000);
}
public void timerCallback(Object stateInfo)
{
Debug.WriteLine("Sync?");
if (!SYNC_IN_PROGRESS)
{
SYNC_IN_PROGRESS = true;
Thread thSync = new Thread(new ThreadStart(sync));
thSync.Start();
}
}
void sync()
{
Debug.WriteLine("Syncing...");
SYNC_IN_PROGRESS = false;
}
}
}
At a guess, the Timer is only held in a method variable; it sounds to me like the Timer is getting garbage collected and finalized, hence terminated. I suspect you should hold onto that reference in a field to prevent collection.
As an aside - I doubt it is the cause here, but when dealing with threading you should be religiously aware of access to shared state from multiple threads; for example:
using Monitor (aka lock)
appropriate use of volatile
Interlocked when it fits
Your current access to the static bool will probably work OK, but...
Try this cleaner approach
static volatile bool SYNC_IN_PROGRESS;
static thread syncPoll;
public void StartSync()
{
SYNC_IN_PROGRESS = false;
syncPoll = new Thread(sync);
syncPoll.Start();
}
void sync()
{
while (true)
{
Debug.WriteLine("Sync?");
if (SYNC_IN_PROGRESS) Debug.WriteLine("Syncing...");
Thread.Sleep(1000);
}
}
It does the same you try to do with your current code :) but doesn't use a timer
So here is what I did and it seems to work just fine
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
StartSync();
}
static bool SYNC_IN_PROGRESS;
public void StartSync()
{
SYNC_IN_PROGRESS = false;
System.Threading.Timer timer = new System.Threading.Timer(timerCallback, SYNC_IN_PROGRESS, 0, 1000);
}
public void timerCallback(Object stateInfo)
{
Debug.WriteLine("Sync?");
if (!(bool)stateInfo)
{
SYNC_IN_PROGRESS = true;
Thread thSync = new Thread(new ThreadStart(sync));
thSync.Start();
}
}
void sync()
{
Debug.WriteLine("Syncing...");
SYNC_IN_PROGRESS = false;
}
}