Anatomy of a Rainbow

rainbow3The magic items and their corresponding visual effects ended up taking about a week to bring from concept to implementation, and working on them was one of the more iterative, productive, and fun periods of Color Sheep’s development cycle. Our primary goal was to introduce some forgiveness to the game — up until items were introduced, fumbling a color combo was pretty close to 100% fatal. This also presented us with an opportunity to add some visual flair to offset the fact that Woolson is immobile.

With this in mind, we started by brainstorming power-up mechanics and the “theme” for each within the Color Sheep setting (e.g. knockback = a gust of wind from the forest). Once we had nailed down a few that we knew we wanted to execute, Tom and I divided the list of items and began implementing the game logic for each and stubbing in placeholder art. Meanwhile, Eric began concepting the look for each effect.

FXMockup

Throughout the week Eric was able to ping-pong between Tom and myself to give feedback and artistic guidance to the progress we were making, and in this way we were able to fine tune and adjust each spell quickly.

Looking back on that week, I think there were a few key factors that enabled us to rapidly churn seven new items in five days (one item, referred to as “Slash” above, didn’t make the final cut). The foremost factor is Eric’s ability to envision and explain the little nuances of what makes a game effect cool and attractive. Secondly, Tom and I have a lot of experience doing gameplay iterations in Unity. That being said, here are some techniques that served us well.

RainbowBeamInspector

scene_view_rainbow

 

Overview

The rainbow beam is a prefab that exists in our singular level at the start of the level. Woolson’s ‘Player’ script has a reference to the root GameObject. We then toggle this object’s active state when the rainbow beam item is activated. The effect itself is comprised of three static sprites and two sprite animations. The rounded part near Woolson’s mouth is an inner AND outer curved base beam sprite. The inner one forms the bright portion in the middle and the outer one is the wider, more transparent section that undulates slightly in game while the beam is active. The part of the beam traveling right is a gradient strip which we stretch out to the edge of the screen.

The two sprite animations are for the wrapping electrical effect and for motion lines on the top and bottom of the inner beam. Finally, a cluster of three ParticleSystem objects emanate the streaks, stars, and crescent effects that fill the center of the beam. That’s about it!

rainbowExploded

 

Using Coroutines to Control FX Logic

All of the item effects are implemented as coroutines whose primary purpose is to handle toggling sprites and particle systems at various points during the spell. Intermediate Unity users are probably familiar and comfortable with coroutines, but I have always found them particularly useful for one-off scenarios where a degree of script control is required but a more general-case system would be overkill. To be clear, each Color Sheep item effect is its own script, which sits at the root of a prefab which contains all of the assets required to trigger the effect (sprites, particle systems, and sounds).

It’s significantly easier to manage logic that executes in a fixed-order fashion with a coroutine than it would be to manage a set of member variables in an Update loop, and I suspect that most people who have used coroutines before would agree. I also think it’s much easier to process someone else’s code if it’s written as a coroutine, as coroutines tend to localize all of the information that you need in order to understand what’s going on.

However, there are some issues and pitfalls to be aware of when using coroutines.

First, coroutines will silently exit execution if the GameObject from which StartCoroutine was called is set to be inactive. This can be troublesome if you use SetActive(false) to hierarchically disable segments of your game.

Second, coroutines can become difficult to manage if the coroutine is managing a large number of dynamic objects. In this case, you may want to break some logic off into a new MonoBehavior rather than managing many sets of data for your dynamic objects. Again, this may seem like common sense, but sometimes the temptation to just add one more dynamic object state variable can result in a complexity creep, and pretty soon you’re managing disparate elements with different activation periods in very tedious ways.

Finally (at least in so far as this post is concerned), coroutines can sometimes be difficult to abort cleanly unless you are very careful to record all of your reset-sensitive state in member variables. Moving variables out of the scope of the coroutine and into the class definition can be very repetitive.

 

Animation Curves

With stock Unity there are a few defacto standards for non-linear interpolation:

Animation component: Keyframe something moving in space and edit the curves to ensure that tangents in and out of keyframes are nice and smooth.

Mathf.Smoothstep: Yields a value that gentles accelerates out of the start value and decelerates into the end value, but your output values follow a fixed function).

Mathf.SmoothDamp: Approximates spring-like behaviour so that the rate at which you move towards the target value increases (up to an optional maximum speed) based on the distance to the target. This tends to look decent on cameras and the like, but I’ve never liked the management overhead or the imprecision of the timing (i.e. you can’t guarantee that with a smoothTime of 1.0f that you will reach the target in exactly 1 second).

And of course, you’re free to roll your own math functions for nice smooth interpolation. However, in working on our latest two releases, I’ve come to prefer a method based on the AnimationCurve class. AnimationCurve objects display a nice-ish custom editor when clicked. This editor is similar to the one used by ParticleSystems and the Animation editor. The advantage of an AnimationCurve is that you can have someone explain what the motion or interpolation they want to achieve looks like, and then quickly model that with curves. It’s also much easier to explain what’s happening in the latest version of the game if you can simply point at a graph and show them what the motion will look like. And finally, if the motion needs to change in the future, all you have to do is edit the curve in the inspector — no mussing about with different functions to get something approximately right.

Here’s an example in C#:

// Your f(t) function
public AnimationCurve yourAnimationCurve;

// How long the interpolation will take
public float duration;

// How much to scale f(t) by; this is optional, I prefer this method to having
// to set the values of all keyframes in the curve to the true value I want
public float scalingFactor;

// Wherever you intend to trigger the interpolation,
// for now, let's say this happens in Start()
void Start() { StartCoroutine(InterpolateValue()); }

IEnumerator InterpolateValue()
{
   float t = -Time.deltaTime;
   while(t < duration)
   {
      t += Time.deltaTime;
      float nrmT = Mathf.Clamp(t, 0.0f, duration) / duration;
      float y = yourAnimationCurve.Evaluate(nrmT);
      float finalFloatValue = y * scalingFactor;
      // Do something with the float!
      yield return null;
   }
}

One thing to note is that I prefer to use an initialization value of -Time.deltaTime. This is done to keep the while loop condensed and brief while still guaranteeing that t = 0.0f and t = 1.0f are evaluated. An alternative would be to simply set t = 0.0f at the start of the loop and, after the while loop, set your value one last time with a call to Evaluate(1.0f). All of this also assumes that getting the precise value at 0.0f and 1.0f is needed; this might be irrelevant depending on the application.