Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>I think I've figured it out in a simple test program</p> <p>Firstly, I've got a base class for the NPC's like this:</p> <p>EDIT: Updated NpcBase to use TaskCompletionSource:</p> <pre><code>public class NpcBase { // Derived classes to call this when starting an async operation public Task BeginTask() { // Task already running? if (_tcs!= null) { throw new InvalidOperationException("busy"); } _tcs = new TaskCompletionSource&lt;int&gt;(); return _tcs.Task; } TaskCompletionSource&lt;int&gt; _tcs; // Derived class calls this when async operation complete public void EndTask() { if (_tcs != null) { var temp = _tcs; _tcs = null; temp.SetResult(0); } } // Is this NPC currently busy? public bool IsBusy { get { return _tcs != null; } } } </code></pre> <p>For reference, here's the old version of NpcBase with custom IAsyncResult implementation instead of TaskCompletionSource:</p> <pre><code>// DONT USE THIS, OLD VERSION FOR REFERENCE ONLY public class NpcBase { // Derived classes to call this when starting an async operation public Task BeginTask() { // Task already running? if (_result != null) { throw new InvalidOperationException("busy"); } // Create the async Task return Task.Factory.FromAsync( // begin method (ac, o) =&gt; { return _result = new Result(ac, o); }, // End method (r) =&gt; { }, // State object null ); } // Derived class calls this when async operation complete public void EndTask() { if (_result != null) { var temp = _result; _result = null; temp.Finish(); } } // Is this NPC currently busy? public bool IsBusy { get { return _result != null; } } // Result object for the current task private Result _result; // Simple AsyncResult class that stores the callback and the state object class Result : IAsyncResult { public Result(AsyncCallback callback, object AsyncState) { _callback = callback; _state = AsyncState; } private AsyncCallback _callback; private object _state; public object AsyncState { get { return _state; ; } } public System.Threading.WaitHandle AsyncWaitHandle { get { throw new NotImplementedException(); } } public bool CompletedSynchronously { get { return false; } } public bool IsCompleted { get { return _finished; } } public void Finish() { _finished = true; if (_callback != null) _callback(this); } bool _finished; } } </code></pre> <p>Next, I've got a simple "NPC" that moves in one dimension. When a moveTo operation starts it calls BeginTask in the NpcBase. When arrived at the destination, it calls EndTask().</p> <pre><code>public class NpcTest : NpcBase { public NpcTest() { _position = 0; _target = 0; } // Async operation to count public Task MoveTo(int newPosition) { // Store new target _target = newPosition; return BeginTask(); } public int Position { get { return _position; } } public void onFrame() { if (_position == _target) { EndTask(); } else if (_position &lt; _target) { _position++; } else { _position--; } } private int _position; private int _target; } </code></pre> <p>And finally, a simple WinForms app to drive it. It consists of a button and two labels. Clicking the button starts both NPC and their position is displayed on the labels.</p> <pre><code>public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void onButtonClick(object sender, EventArgs e) { RunNpc1(); RunNpc2(); } public async void RunNpc1() { while (true) { await _npc1.MoveTo(20); await _npc1.MoveTo(10); } } public async void RunNpc2() { while (true) { await _npc2.MoveTo(80); await _npc2.MoveTo(70); } } NpcTest _npc1 = new NpcTest(); NpcTest _npc2 = new NpcTest(); private void timer1_Tick(object sender, EventArgs e) { _npc1.onFrame(); _npc2.onFrame(); label1.Text = _npc1.Position.ToString(); label2.Text = _npc2.Position.ToString(); } } </code></pre> <p>And it works, all seems to be running on the main UI thread... which is what I wanted.</p> <p>Of course it needs to be fixed to handle cancelling of operations, exceptions etc... but the basic idea is there.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload