Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

A simple system for running nested coroutines in C#.

License

NotificationsYou must be signed in to change notification settings

ChevyRay/Coroutines

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 

Repository files navigation

A simple system for running nested coroutines in C#. Just dropCoroutines.cs into your project and you're ready to go.

What is a coroutine?

C# has a feature called "enumerators" which are functions that can besuspended during their execution. They do this by usingyield return, rather than regularreturn. When you useyield, you effectivelypause the function, and at any time you canresume it and it will continue after the most recentyield.

In this context, a "coroutine" is basically one of these functions, but wrapped up in a nice little package and a few extra features:

  • A container that will automatically update several coroutines for you
  • A simple syntax for nesting coroutines so they can yield to others
  • The ability to yield a floating point number, which will pause the routine for a desired amount of time
  • Handles to routines are provided to make tracking them easier

Example usage

To use the system, you need to create aCoroutineRunner and update it at regular intervals (eg. in a game loop). You also need to tell it a time interval.

CoroutineRunnerrunner=newCoroutineRunner();voidUpdateGame(floatdeltaTime){runner.Update(deltaTime);}

Now you can run coroutines by callingRun(). Here's a simple coroutine that counts:

IEnumeratorCountTo(intnum,floatdelay){for(inti=1;i<=num;++i){yieldreturndelay;Console.WriteLine(i);}}voidStartGame(){//Count to 10, pausing 1 second between each numberrunner.Run(CountTo(10,1.0f));}

When you yield a floating-point number, it will pause the coroutine for that many seconds.

You can also nest coroutines by yielding to them. Here we will have a parent routine that will run several sub-routines:

IEnumeratorDoSomeCounting(){Console.WriteLine("Counting to 3 slowly...");yieldreturnCountTo(3,2.0f);Console.WriteLine("Done!");Console.WriteLine("Counting to 5 normally...");yieldreturnCountTo(5,1.0f);Console.WriteLine("Done!");Console.WriteLine("Counting to 99 quickly...");yieldreturnCountTo(99,0.1f);Console.WriteLine("Done!");}voidStartGame(){runner.Run(DoSomeCounting());}

You can also stop any running routines:

//Stop all running routinesrunner.StopAll();//Start a routine and store a handle to itCoroutineHandlemyRoutine=runner.Run(SomeRoutine());//Stop a specific routinemyRoutine.Stop();

Other tips and tricks

A coroutine can run infinitely as well by using a loop. You can also tell the routine to "wait for the next update" by yieldingnull:

IEnumeratorRunThisForever(){while(true){yieldreturnnull;}}

Coroutines are very handy for games, especially for sequenced behavior and animations, acting sort of likebehavior trees. For example, a simple enemy's AI routine might look like this:

IEnumeratorEnemyBehavior(){while(enemyIsAlive){yieldreturnPatrolForPlayer();yieldreturnSpeak("I found you!");yieldreturnChaseAfterPlayer();yieldreturnSpeak("Wait... where did you go!?");yieldreturnReturnToPatrol();}}

Sometimes you might want to run multiple routines in parallel, and have a parent routine wait for them both to finish. For this you can use the return handle fromRun():

IEnumeratorGatherNPCs(VectorgatheringPoint){//Make three NPCs walk to the gathering point at the same timevarmove1=runner.Run(npc1.WalkTo(gatheringPoint));varmove2=runner.Run(npc2.WalkTo(gatheringPoint));varmove3=runner.Run(npc3.WalkTo(gatheringPoint));//We don't know how long they'll take, so just wait until all three have finishedwhile(move1.IsPlaying||move2.IsPlaying||move3.IsPlaying)yieldreturnnull;//Now they've all gathered!}

Here is a more complicated example where I show how you can use coroutines in conjunction with asynchronous functions (in this case, to download a batch of files and wait until they've finished):

IEnumeratorDownloadFile(stringurl,stringtoFile){//I actually don't know how to download files in C# so I just guessed this, but you get the pointbooldone=false;varclient=newWebClient();client.DownloadFileCompleted+=(e,b,o)=>done=true;client.DownloadFileAsync(newUri(url),toFile);while(!done)yieldreturnnull;}//Download the files one-by-one in syncIEnumeratorDownloadOneAtATime(){yieldreturnDownloadFile("http://site.com/file1.png","file1.png");yieldreturnDownloadFile("http://site.com/file2.png","file2.png");yieldreturnDownloadFile("http://site.com/file3.png","file3.png");yieldreturnDownloadFile("http://site.com/file4.png","file4.png");yieldreturnDownloadFile("http://site.com/file5.png","file5.png");}//Download the files all at once asynchronouslyIEnumeratorDownloadAllAtOnce(){//Start multiple async downloads and store their handlesvardownloads=newList<CoroutineHandle>();downloads.Add(runner.Run(DownloadFile("http://site.com/file1.png","file1.png")));downloads.Add(runner.Run(DownloadFile("http://site.com/file2.png","file2.png")));downloads.Add(runner.Run(DownloadFile("http://site.com/file3.png","file3.png")));downloads.Add(runner.Run(DownloadFile("http://site.com/file4.png","file4.png")));downloads.Add(runner.Run(DownloadFile("http://site.com/file5.png","file5.png")));//Wait until all downloads are donewhile(downloads.Count>0){yieldreturnnull;for(inti=0;i<downloads.Count;++i)if(!downloads[i].IsRunning)downloads.RemoveAt(i--);}}

Why coroutines?

I use coroutines a lot in my games, as I find them great for organizing actor behavior and animations. As opposed to an async callback-based system, coroutines allow you to write your behaviors line-by-line, like how you would naturally write code, and result in very clean and easy to understand sequences.

There are good and bad times to use them, and you will get better at distinguishing this as you use them more. For many of my games, coroutines have been completely priceless, and have helped me organize and maintain very large and complicated systems that behave exactly in the order I wish them to.

NOTE: Not all languages have built-in support for coroutine systems like this. If you plan on porting your code to other languages, it may not be worth the pain of porting if your target language does not have a reliable means of implementing coroutines.

About

A simple system for running nested coroutines in C#.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages


[8]ページ先頭

©2009-2025 Movatter.jp