Swift Equivalent of Unity3D Coroutines

Swift equivalent of Unity3d Coroutines?

No, Swift do not currently support Unity/C# style coroutines (i.e., yield return style constructs). Such feature is pretty cool by the way ;-)

Having said that, you might want to take a look in the Async framework for a somewhat valid alternative (if you are really looking for async/await abstractions):

Syntactic sugar in Swift for asynchronous dispatches in Grand Central Dispatch

As for Swift native support, we might need to wait for Swift ≥ 5 for something like that to come along:

Actors, async/await, atomicity, memory model, and related topics. This area is highly desired by everyone, as it will open the door for all sorts of new things on the client, server and more. We plan to start formal discussions about this in Phase 2, but it is unfortunately crystal clear that a new concurrency model won’t be done in time for the Swift 4 release. This is simply because it will take more than a 12 months to design and build, and we want to make sure to take time to do it right. It also makes sense for the memory ownership model to be better understood before taking this on.

Refer to one of many coroutines in Unity3D?

The type that you are looking for is a delegate. Delegates are similar to function pointers, and are not specific to Unity3D.

public class Example : MonoBehaviour
{
private delegate IEnumerator CoroutineDelegate();

private IEnumerator CoroutineA()
{
}

private IEnumerator CoroutineB()
{
}

public void Start()
{
CoroutineDelegate crt = CoroutineA;
StartCoroutine(crt());
}
}

How to create a generator in Swift?

Understanding how generators work (and why they are less important in swift) is at first difficult coming from Python.

Up to Swift v2.1 there was a protocol called GeneratorType. This was renamed to IteratorProtocol in Swift v3.0+. You can conform to this protocol to make your own objects that do just-in-time computations similar to what can be done in Python.

More information can be found in the Apple Documentation: IteratorProtocol

A simple example from IteratorProtocol page:

struct CountdownIterator: IteratorProtocol {
let countdown: Countdown
var times = 0

init(_ countdown: Countdown) {
self.countdown = countdown
}

mutating func next() -> Int? {
let nextNumber = countdown.start - times
guard nextNumber > 0
else { return nil }

times += 1
return nextNumber
}
}

let threeTwoOne = Countdown(start: 3)
for count in threeTwoOne {
print("\(count)...")
}
// Prints "3..."
// Prints "2..."
// Prints "1..."

However, you need to think about why you are using a generator:

Swift automatically does something "called copy on write." This means that many of the cases that use a Python generator to avoid the large copying cost of collections of objects (arrays, lists, dictionaries, etc) are unnecessary in Swift. You get this for free by using one of the types that use copy on write.

Which value types in Swift supports copy-on-write?

It is also possible to use a wrapper to force almost any object to be copy on write, even if it is not part of a collection:

How can I make a container with copy-on-write semantics?

The optimizations in swift usually mean that you do not not have to write generators. If you really do need to (usually because of data heavy, scientific calculations) it is possible as above.

C#/Unity - Returning the retrieved string from a UnityWebRequest coroutine

Since coroutines / web requests run asynchronously, you can't get a return value from them normally (because the rest of the program does not stop until they are done, so by the time there is a return value your program can be anywhere in your code). You can have them update some shared variable and then check every frame in Update() whether they are done or not, and use that share variable when done.

But the better and cleaner way to get variables from coroutines is using callbacks. That means you pass a paramater to the coroutine which is a method that you want to run with the result, and the coroutine will call that method as soon as it is complete.

See this example on how to use a callback with a web request coroutine:

    private void Start() {
StartCoroutine(DoWebRequest("http://www.stackoverflow.com", ParseWebResult));
}

private void ParseWebResult(string resultText) {
Debug.Log("Result of web request: " + resultText);
}

private IEnumerator DoWebRequest(string url, System.Action<string> callback) {
using (UnityWebRequest wr = UnityWebRequest.Get(url)) {
// Request and wait for the desired page.
yield return wr.SendWebRequest();

isWrDone = true;
if (wr.isNetworkError) {
Debug.LogError("Error getting www data: " + wr.error);
} else {
callback(wr.downloadHandler.text);
}
}
}

In this example you pass two parameters to the coroutine - the URL and a method that will handle the result. Then in Start() I start this coroutine, and pass it a url as a parameter, and also pass it the ParseWebResult() method that will handle the result.

The ParseWebResult method receives a string (the result of the web request) and prints it to the log. This is where your own code should go to handle the result properly.

You could also use inline lambda instead of defining a separate message to handle the result:

    private void Start() {
StartCoroutine(DoWebRequest("http://www.stackoverflow.com", (string result) => {
Debug.Log("Result of web request: " + resultText);
}));
}

In this example I use the lambda syntax to define the WWW handler inline. You can read more about the lambda here.

How do I handle async functions in a loop?

If you really want to do this sequentially, the easiest way is to perform your tasks recursively, actually invoking the next task in the completion handler of the prior one:

processNext(in: tableView.selectedRowIndexes) {
// do something when they're all done
}

Where:

func processNext(in rows: [Int], completion: @escaping () -> Void) {
guard let row = rows.first else {
completion()
return
}

myData.fetch(url: urlList[row]) { res in
self.anotherAsyncCall(res) { data in
//continue to deal with next row now

self.processNext(in: Array(rows.dropFirst()), completion: completion)
}
}
}

But I agree with GoodSp33d that the other approach is to wrap this asynchronous process in a custom, asynchronous, Operation subclass.


But this begs the question why you want to do these sequentially. You will pay a significant performance penalty because of the inherent network latency for each request. So the alternative is to let them run concurrently, and use dispatch group to know when they're done:

let group = DispatchGroup()

tableView.selectedRowIndexes.forEach { row in
group.enter()
myData.fetch(url: urlList[row]) { res in
self.anotherAsyncCall(res) { data in
//continue to deal with next row now
group.leave()
}
}
}

group.notify(queue: .main) {
// do something when they're all done
}

Whether you can run these concurrently (or to what degree) is a function of what you're doing inside various asynchronous methods. But I would suggest you think hard about making this work concurrently, as the performance is likely to be much better.

UNITY - How do I change color at a delay?

You're looking for something called Coroutines.

Update() is called 60 times per second. As you have ChangeColor() inside the Update() method, it will be called regardless of the yield statement within the ChangeColor() method, as yield only works from inside coroutines.

You will need to call the method like this:

StartCoroutine(ChangeColor()); 

or

StartCoroutine("ChangeColor"); 

Be aware that if you put that line inside the Update() method you will be starting a new coroutine 60 times a second. If you want ChangeColor() to continually run after being started you'll need to change its logic slightly and place it in your Start() method, or in a place where it's called once.

    function ChangeColor() {
while(true){
this.camera.backgroundColor = Color32(Random.Range(0, 255), Random.Range(0, 255), Random.Range(0, 255), 1);
yield WaitForSeconds(5);
}
}

You'll notice I added a while loop that is an infinite loop, and INSIDE the while loop I placed the yield statement. This will properly yield the method for 5 seconds before looping again and yielding forever.

Some additional notes:

  • Make sure you choose the correct version of StartCoroutine(...). The one that takes a string can be stopped by using StopCoroutine("NameOfMethod"). The other one that takes the method signature itself cannot be stopped after being started.

  • Coroutines live on the gameobject script that creates them. So if your camera had the script which calls the ChangeColor() method and it was destroyed, then the ChangeColor() coroutine is also stopped and destroyed.

Links to the other coroutine methods.

StartCoroutine

Move GameObject over time

gjttt1's answer is close but is missing important functions and the use of WaitForSeconds() for moving GameObject is unacceptable. You should use combination of Lerp, Coroutine and Time.deltaTime. You must understand these stuff to be able to do animation from Script in Unity.

public GameObject objectectA;
public GameObject objectectB;

void Start()
{
StartCoroutine(moveToX(objectectA.transform, objectectB.transform.position, 1.0f));
}

bool isMoving = false;

IEnumerator moveToX(Transform fromPosition, Vector3 toPosition, float duration)
{
//Make sure there is only one instance of this function running
if (isMoving)
{
yield break; ///exit if this is still running
}
isMoving = true;

float counter = 0;

//Get the current position of the object to be moved
Vector3 startPos = fromPosition.position;

while (counter < duration)
{
counter += Time.deltaTime;
fromPosition.position = Vector3.Lerp(startPos, toPosition, counter / duration);
yield return null;
}

isMoving = false;
}

Similar Question: SKAction.scaleXTo



Related Topics



Leave a reply



Submit