Coroutines

Along with tweens, Otter also has support for running coroutines.  A coroutine usually is a method that can be suspended for any length of time by the program.  Coroutines have a bunch of amazing uses in games like running an enemy's AI, playing a cutscene, or going through the steps of a tutorial.  The ability to suspend a method and resume it later can be incredibly powerful.

In this simple example we'll just be taking a look at moving an entity around using a coroutine and also applying some cool effects to it.

using Otter;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CoroutineExample {
  class Program {
    static void Main(string[] args) {
      // Create a Game.
      var game = new Game();
      // Set the background color to something nice.
      game.Color = new Color(0.3f, 0.2f, 0.2f);
      // Start it up using the CoroutineScene.
      game.Start(new CoroutineScene());
    }
  }

  class CoroutineScene : Scene {
    // A box that will be moving around.
    public Image ImageBox = Image.CreateRectangle(50);

    // Fade the color of the box.
    public Color NextColor = Color.White;
    public Color CurrentColor = Color.White;

    public CoroutineScene() : base() {
      // Center that box.
      ImageBox.CenterOrigin();

      // Fancy it up
      ImageBox.OutlineColor = Color.Black;
      ImageBox.OutlineThickness = 3;

      // Gotta draw the box.
      AddGraphic(ImageBox);

      // Set the box position.
      ImageBox.X = 100;
      ImageBox.Y = 100;
    }

    public override void Begin() {
      base.Begin();

      // Start the coroutine, yo.
      Game.Coroutine.Start(MainRoutine());
    }

    // The main coroutine to execute.  This will move the box around and change its color.
    IEnumerator MainRoutine() {
      // Wait for 30 frames.
      yield return Coroutine.Instance.WaitForFrames(30);
      // Set the next color.
      NextColor = Color.Red;
      // Move the box to the top right.
      yield return MoveBoxTo(540, 100);

      // Wait for 30 frames.
      yield return Coroutine.Instance.WaitForFrames(30);
      // Set the next color.
      NextColor = Color.Yellow;
      // Move the box to the bottom right.
      yield return MoveBoxTo(540, 380);

      // Wait for 30 frames.
      yield return Coroutine.Instance.WaitForFrames(30);
      // Set the next color.
      NextColor = Color.Green;
      // Move the box to the bottom left.
      yield return MoveBoxTo(100, 380);

      // Wait for 30 frames.
      yield return Coroutine.Instance.WaitForFrames(30);
      // Set the next color.
      NextColor = Color.Cyan;
      // Move the box to the top left.
      yield return MoveBoxTo(100, 100);

      // Start a new coroutine.
      Game.Coroutine.Start(MainRoutine());
    }

    // Move the box toward an x and y destination.
    IEnumerator MoveBoxTo(float x, float y) {
      // Used to determine the completion.
      var initialDistance = Util.Distance(ImageBox.X, ImageBox.Y, x, y);

      float currentDistance = float.MaxValue;
      while (currentDistance > 1) {
        currentDistance = Util.Distance(ImageBox.X, ImageBox.Y, x, y);

        // Determine the completion of the movement from 0 to 1.
        var completion = Util.ScaleClamp(currentDistance, 0, initialDistance, 1, 0);

        // Lerp the color of the box based on the completion of the movement.
        ImageBox.Color = Util.LerpColor(CurrentColor, NextColor, completion);

        // Spin the box along with its movement because why not.
        ImageBox.Angle = Util.ScaleClamp(completion, 0, 1, 0, 360);

        // Actually move the box toward the destination.
        ImageBox.X = Util.Approach(ImageBox.X, x, 5);
        ImageBox.Y = Util.Approach(ImageBox.Y, y, 5);

        // Wait until next frame.
        // See how magical this is? We're in the middle of a while loop
        // and we can just wait until the next frame to resume it!
        yield return 0;
      }

      // Done moving.  Update the color.
      CurrentColor = NextColor;
    }
  }
}

With the above code you should end up with something like this:

Setting up and executing coroutines with Otter is a pretty simple process.  Beyond this there are many uses of coroutines at your disposal, and for even more control you can create your own Coroutine object to use.

Examples