Using numeric values for state control

Something occurred to me one of these days. There’s something I’ve been doing for quite a while, but I’ve never discussed it with anybody else. I’m sure it all makes sense, somehow, or that there’s some better way to describe it – perhaps it has some fancy design pattern name – but for the sake of clarity and future linking, I decided to write about it. I also haven’t been talking about actual code for a while, so here it goes. It’s a big long-winded.

When creating custom objects with ActionScript, it’s pretty common to add some state variables to their classes. For example, suppose we have a FancyButton class, and we want to give developers the ability to disable or enable the button. Normally, we’d create the interface like this:

// Enables the button
myFancyButton.enabled = true;

// Disables the button
myFancyButton.enabled = false;

Then, inside the class, we’d just have a property, something like this:

class FancyButton extends Sprite {

	public var enabled:Boolean;

	function FancyButton() {
		enabled = true; // Enabled by default
	}

	(...other code...)

}

Then, when needed, the code inside the button would check for the value of enabled, and react accordingly – say, allowing the user to click it or not.

Of course, when we’re feeling fancy, we might as well transform it into a getter/setter property, and add functionality to when the property is changed. Like so:

class FancyButton extends Sprite {

	protected var _enabled:Boolean; // Actual value

	function FancyButton() {
		enabled = true; // Enabled by default
	}

	(...other code...)

	public function get enabled(): Boolean {
		return _enabled;
	}

	public function set enabled(__value:Boolean): void {
		_enabled = __value;
		alpha = _enabled ? 1 : 0.5;
	}

}

This allows the class to run whatever code we want once the value is changed – like maybe making the button semi-transparent when disabled, or fully visible otherwise (this is what the code above is doing). This is nothing fancy, basic ActionScript/OOP coding, but sets the context of my post.

Let me open another topic here first. I’ve always played a first-person shooter game called QuakeWorld, a multiplayer-only version of the classical game Quake. QuakeWorld is known for having many options you can change with the console, making the game pretty customizable, at least when compared to most first-person shooters. For example: when playing the game, you view your own weapon on screen, but if you want, you can make it invisible with the console command r_drawviewmodel 0 (so you get more screen space). Setting it to 1 will make it visible, as default. This value is a boolean, represented by a number.

Some years ago, I started using FuhQuake as my main QuakeWorld client (there are several different QuakeWorld clients). It turns out that in FuhQuake, many of the “boolean” console configuration variables (cvars) can be represented as numbers, instead of booleans. This means that, for r_drawviewmodel, 0 is still invisible, and 1 is still visible, but 0.5 means 50% opacity.

At the time, the idea struck me as pretty odd but quite ingenious – after all, I actually prefer to have my weapon on screen (so I could know which weapon was selected) but I also wanted to see what was beneath it, as the weapons tend to occupy some pretty large portion of the screen. As such, I use a r_drawviewmodel value of 0.3 on my QuakeWorld config file.

Almost without thinking, I adopted this same idea in most of the code I write. This means that, instead of my FancyButton have an enabled property which is a Boolean, it’s actually a Number that can go from 0 to 1 – 0 meaning disabled, 1 meaning enabled, and all values in between meaning whatever I want them to mean – say, disabled. The functionality, internally, looks the same.

class FancyButton extends Sprite {

	protected var _enabled:Number; // Actual value

	function FancyButton() {
		enabled = 1; // Enabled by default
	}

	(...other code...)

	public function get enabled(): Number {
		return _enabled;
	}

	public function set enabled(__value:Number): void {
		_enabled = __value;
		alpha = _enabled == 1 ? 1 : 0;
	}

}

What changes the most is when the Class code checks for whether the button’s enabled or not – and, of course, references to the enabled property of the instance would be slightly different, like so:

// Enables the button
myFancyButton.enabled = 1;

// Disables the button
myFancyButton.enabled = 0;

Well, why bother with this change then? It turns out that when I need to work with Flash, it’s pretty common that I have to smoothly change the state of an object – like enabling and disabling a button. With a numeric property, I can make the state value be the transition, and then have more control over a button state – either changing it with an animation, or changing it immediately when needed. For example, to make sure the enabled property changes the visible state of my button smoothly, the correct class’ property’s set function would be something like this:

class FancyButton extends Sprite {

	(...)

	public function set enabled(__value:Number): void {
		_enabled = __value;
		// Alpha goes from 0.5 to 1 as _enabled goes from 0 to 1
		alpha = 0.5 + _enabled * 0.5;
	}

}

(This is not something that would be used every time, of course. In many cases, having have a hard, Boolean property is fine, then you can do the animation transition inside the set function as necessary, and actually avoid the Class user’s the hassle of tweening anything. But this is an specific case that applies to the enabled state of a button – which is just a simpler example I used to illustrate my point – so please bear with me while I continue).

As it turns out, there are a number of advantages in using numbers to control the state of an object, instead of using Booleans.

Take, for instance, the rollover state of a button. Consider a simple, semi-transparent button, that becomes fully visible during rollover. It’s quite common that such functionality will look like this:

class FancyButton extends Sprite {

	function FancyButton() {
		addEventListener(MouseEvent.ROLL_OVER, onRollOver);
		addEventListener(MouseEvent.ROLL_OUT, onRollOut);
		alpha = 0.5;
	}

	public function onRollOver(e:MouseEvent): void {
		alpha = 1;
	}

	public function onRollOut(e:MouseEvent): void {
		alpha = 0.5;
	}

}

Or, of course, you can use some tween for that, so it’ll have a nifty animation when rolled over (some pseudo-code):

import com.somepackage.SomeTweenEngine;

class FancyButton extends Sprite {

	function FancyButton() {
		addEventListener(MouseEvent.ROLL_OVER, onRollOver);
		addEventListener(MouseEvent.ROLL_OUT, onRollOut);
		alpha = 0.5;
	}

	public function onRollOver(e:MouseEvent): void {
		SomeTweenEngine.doTween(this, {alpha:1, time:0.4});
	}

	public function onRollOut(e:MouseEvent): void {
		SomeTweenEngine.doTween(this, {alpha:0.5, time:0.4});
	}

}

All fine and dandy. Suppose, however, you want to add more features to your button. Say, for instance, your non-focused button will be a bit blurred, and have a little glow when rolled over. It could look like this:

import com.somepackage.SomeTweenEngine;

class FancyButton extends Sprite {

	function FancyButton() {
		addEventListener(MouseEvent.ROLL_OVER, onRollOver);
		addEventListener(MouseEvent.ROLL_OUT, onRollOut);
		alpha = 0.5;
	}

	public function onRollOver(e:MouseEvent): void {
		SomeTweenEngine.doTween(this, {alpha:1, time:0.4});
		SomeTweenEngine.doTween(this, {glow:3, color:0xff0000, time:0.4});
		SomeTweenEngine.doTween(this, {blur:0, time:0.4});
	}

	public function onRollOut(e:MouseEvent): void {
		SomeTweenEngine.doTween(this, {alpha:0.5, time:0.4});
		SomeTweenEngine.doTween(this, {glow:0, color:0xff0000, time:0.4});
		SomeTweenEngine.doTween(this, {blur:4, time:0.4});
	}

}

That is, considering your tweening engine already supports “shortcuts” for filter tweening, and that they’re initialized (if needed).

Still, this code poses a problem. How do you set the initial state of the Button as unfocused? You’d either need to duplicate the tweenings functionality, call onRollOut (it would animate the object to the desired state however), or create a function that moves the object between states with a customizable time. This is something I’ve been doing previously, and it looks like so:

import com.somepackage.SomeTweenEngine;

class FancyButton extends Sprite {

	function FancyButton() {
		addEventListener(MouseEvent.ROLL_OVER, onRollOver);
		addEventListener(MouseEvent.ROLL_OUT, onRollOut);
		setStateNormal();
	}

	public function get onRollOver(e:MouseEvent): void {
		setStateFocused(0.4);
	}

	public function set onRollOut(e:MouseEvent): void {
		setStateNormal(0.4);
	}

	protected function setStateNormal(__time:Number = 0): void {
		SomeTweenEngine.doTween(this, {alpha:0.5, time:__time});
		SomeTweenEngine.doTween(this, {glow:0, color:0xff0000, time:__time});
		SomeTweenEngine.doTween(this, {blur:4, time:__time});
	}

	protected function setStateFocused(__time:Number = 0): void {
		SomeTweenEngine.doTween(this, {alpha:1, time:__time});
		SomeTweenEngine.doTween(this, {glow:3, color:0xff0000, time:__time});
		SomeTweenEngine.doTween(this, {blur:0, time:__time});
	}
}

This works great. However, there are two questions still raised – what if I want to control my state from outside, and what if I want to do something that isn’t normally supported by the tweening engine? The former would require all my external code to “know” what properties are changed, or for the setState* methods to be public; the latter would get me stuck.

Enter state variables. With state variables, you can control the tween the actual state the button is, and then just apply the state to the object. The above class would work like so:

class FancyButton extends Sprite {

	protected var _mouseFocused:Number; // Actual value

	function FancyButton() {
		addEventListener(MouseEvent.ROLL_OVER, onRollOver);
		addEventListener(MouseEvent.ROLL_OUT, onRollOut);
		mouseFocused = 0; // Normal by default
	}

	public function onRollOver(e:MouseEvent): void {
		SomeTweenEngine.doTween(this, {mouseFocused:1, time:0.4});
	}

	public function onRollOut(e:MouseEvent): void {
		SomeTweenEngine.doTween(this, {mouseFocused:0, time:0.4});
	}

	// Getter/setters

	public function get mouseFocused(): Number {
		return _mouseFocused;
	}

	public function set mouseFocused(__value:Number): void {
		_mouseFocused = __value;
		// Applies the focus value
		alpha = 0.5 + _mouseFocused* 0.5;
		filters = [
			new BlurFilter((1-_mouseFocused) * 4, (1-_mouseFocused) * 4),
			new GlowFilter(0xff0000, 1, _mouseFocused * 3, _mouseFocused * 3)
		];
	}

}

“But Zeh”, I can hear people say, “this looks a lot more complicated. Why would I even want to use this crap?”

If you just said or thought so, then you’re right that it looks more complicated. However, I personally like to think that this approach is the cleanest in the sense that it allows me to set the state of an object to be whatever I want it to be, from the class itself or from outside of it, without having to deal with the actual property values; I don’t need the tweening class to actually support anything other than basic tweening, or to learn a new “shortcut” API in addition to standard DisplayObject features like filters, avoiding redundancy; and additionally, one of the most important aspects of this approach is that you can stack state properties and make the object react accordingly. Suppose, for example, that our FancyButton can also be disabled in addition to being focused; this would cut its opacity in half. Doing this with normal alpha tweening would be a big problem, because you’d need to check all the Boolean states of an object before determining what the target value should be. With numeric states, this would look like so:

class FancyButton extends Sprite {

	protected var _mouseFocused:Number;
	protected var _enabled:Number;

	function FancyButton() {
		addEventListener(MouseEvent.ROLL_OVER, onRollOver);
		addEventListener(MouseEvent.ROLL_OUT, onRollOut);
		mouseFocused = 0;
		enabled = 1;
	}

	// State redrawing

	public function redrawState(): void {
		// Applies the focus value
		alpha = ((0.5 + _mouseFocused * 0.5) + (0.5 + _enabled * 0.5))/2;
		filters = [
			new BlurFilter((1-_mouseFocused) * 4, (1-_mouseFocused) * 4),
			new GlowFilter(0xff0000, 1, _mouseFocused * 3, _mouseFocused * 3)
		];
	}

	// Events

	public function onRollOver(e:MouseEvent): void {
		SomeTweenEngine.doTween(this, {mouseFocused:1, time:0.4});
	}

	public function onRollOut(e:MouseEvent): void {
		SomeTweenEngine.doTween(this, {mouseFocused:0, time:0.4});
	}

	// Getter/setters

	public function get mouseFocused(): Number {
		return _mouseFocused;
	}

	public function set mouseFocused(__value:Number): void {
		_mouseFocused = __value;
		redrawState();
	}

	public function get enabled(): Number {
		return _enabled;
	}

	public function set enabled(__value:Number): void {
		_enabled = __value;
		redrawState();
	}

}

In the above code, the alpha of a fully focused and enabled button would be 1; a focused but not enabled button, as well as an enabled but not focused button, would have an alpha of 0.75; and a button that’s neither enabled nor focused would have an alpha of 0.5. That’s because they’re stacking – if I was simply changing the values themselves depending on the change of a property, I’d risk overwriting one animation with another, or ending up with the wrong value.

This may not sound like much, because, again, the focused/enabled button is a simple example. But if you take the idea and apply it to some of the most common visible objects you have to create in Flash, I believe you’ll find it quite useful. Or, well, at least I do.

Here’s another example that may illustrate why state control is better than direct property control. Suppose you have a box that you want to go from the left side of the screen to the right side of the screen in 3 seconds. Something like this:

myBox.x =  0;
SomeTweenEngine.doTween(myBox, {x:stage.stageWidth, time:0.6});

This will work. However, suppose the screen is resized during the animation, and you’re using a stage scaleMode of NO_SCALE (variable width). What to do?

Most people will say the normal solution is to interrupt the tween, setting a new goal. Or, maybe, if your tweening solution allows tweening references, you could just save the reference and change the target value. Something like this, in pseudo-code:

myBox.x =  0;
myTween = SomeTweenEngine.doTween(myBox, {x:stage.stageWidth, time:0.6});
stage.addEventListener(Event.RESIZE, onResizeScreen);

public function onResizeScreen(e:Event): void {
	if (Boolean(myTween) && myTween.isActive) {
		myTween.x = stage.stageWidth;
	}
}

This may work, but creating an exception to that opens up a big can of worms – every kind of animation that depends on a value that can change depending on the screen size will need a similar event. And that’s even considering you can save tweening references and change them later. For engines that don’t allow that – my own Tweener, for example, doesn’t unless yo hack its code – actually stopping and recreating tweenings is a much worse solution.

This (nice) animation is a good real-world example of that problem. It employs no resize detection during tweening – if you click to go to the next or previous slide, and resize your browser during the tweening, the final state won’t make a lot of sense – the slide will have the wrong position and scale, instead of being fully centered.

In cases like this, a solution would be to add an state property to the object – in the previous box example, an alignment property would probably make some sense, if you consider -1 to be full left alignment, and 1, full right alignment. The interface could work like this:

myBox.alignment = -1;
SomeTweenEngine.doTween(myBox, {alignment:1, time:0.6});

Then the code for myBox – which would render the x property useless, unless you extend and overwrite it to have some additional functionality – would look like this:

class MyFancyBox extends Sprite {

	protected var _alignment:Number = 0; // Default is centered

	(...class code...)
	// Getter/setters

	public function get alignment(): Number {
		return _alignment;
	}
	public function set alignment(__value:Number): Void {
		_alignment = __value;
		x = ((_alignment + 1)/2) * stage.stageWidth;
	}

}

Obviously, that’s some pseudo-code, with just the basic idea – to move the object to a new position but always based on the stage width. A final class could be properly constructed to, say, take the object’s width in consideration, allow x to work as an offset value (in addition to alignment), etc. The basic point, however, is that by adding a state property via a getter/setter, you don’t have to worry about exceptional behavior – like a window resize – anymore. Instead, you just take them into consideration when applying the state to the object.

And while I do use the focus/enabled functionality quite often, here’s a real, more useful example. I was creating an animation that should contain some text. This text must come into view with a fancy vertical blur effect (like a motion blur), and go away in the same way. I could have done it like this:

// Show
SomeTweenEngine.doTween(myText, {blurX:4, time:1});
SomeTweenEngine.doTween(myText, {alpha:1, time:1, onStart:function() { this.visible = true; }});

// Hide
SomeTweenEngine.doTween(myText, {blurX:0, time:1});
SomeTweenEngine.doTween(myText, {alpha:0, time:1, onComplete:function() { this.visible = false; }});

(And, of course, I could also employ some autoAlpha functionality, if the engine supported it, to automatically make it invisible)

Sure, it looks and works fine, but what if I want to use that on several different places, and what if I want to change the amount of blur it’s applied later? I need to hunt down everywhere I have the tweening start and change the code, or create a function to show and hide this object.

The solution I’ve chosen, instead, is to have a visibility property that applies directly to how I want my object to show and hide every time. Its interface works like so:

// Show
SomeTweenEngine.doTween(myText, {visibility:1, time:1});

// Hide
SomeTweenEngine.doTween(myText, {visibility:0, time:1});

And then, inside the code, of course, I have the appropriate getter/setter code, and state update function:

(...class code...)

// State redrawing

private function redraw(): Void {
	if (visibility > 0) {
		_alpha = _visibility;
		_visible = true;
		filters = _visibility == 1 ? [] : [new BlurFilter((1-_visibility) * 64, 1-_visibility, 2)];
	} else {
		_visible = false;
		filters = [];
	}
}

// Getter/setters

public function get visibility(): Number {
	return _visibility;
}

public function set visibility(__value:Number): Void {
	if (_visibility != __value) {
		_visibility = __value;
		redraw();
	}
}

(...class code...)

This is actual code used on some recent work I’ve done. And again, this might looks like more code, but I like to believe it’s actually less – you only write the functionality once, inside your class, then reuse when needed. You can then work on the object’s state whenever you please, and not worry about tweening support.

This may sound sort of crazy since I’ve gone out of my way to add a lot of (optional) shortcuts to Tweener. But the truth is, I haven’t been using those for a long, long time. While they may make your life easier when you need some quick functionality – say, autoAlpha and such – trying to use a tweening extension to control way too many features (specially features that are not directly available as a direct property) is a recipe for disaster. Nowadays, I like having a cleaner, tweening-independent solution, and honestly it has been working very well for even the most complex cases.

Finally, the above alternative is not a silver bullet, as it adds a few problems by itself – on the previous FancyButton class example, trying to tween both enabled and mouseFocused at the same time would cause redrawState() to be called twice per frame (see this for an explanation on the solution). But still, I think the advantages far outweight the disadvantages.

6 responses

  1. Because I know this may happen (since some people don’t like to read posts in its entirety, and instead just assume the author is talking about something and go post a comment): please do not use the post’s comments to talk about how you prefer one tweening engine over another, or how you like one feature over another. This is not an specific attack or defense on any tweening engine (if anything, it’s a criticism of an opinion *I* used to have a few years ago!) but rather an observation that some Flash functionality may benefit by shielding actual object properties with “state” variables – and that part of these benefits include not needing certain tweening features.

    Again, please keep comments (if any) neutral and on topic.

  2. Shouldn’t all the _variables (_enabled, _mouseFocused…) be private? Why do you need them public? I know it’s not the point or the article, but just wondered.

  3. Ouch, you’re right. They’re supposed to be protected, forgot to change them while writing.

    Fixed, thanks!

  4. Hi, while I agree in principle, I think if you’re doing this you should take one more step to make the class more understandable to other users, create static properties to represent these numbers. e.g you give the example of aligning, and -1 meaning left, 0 centre, 1 right, which kind of make sense but aren’t immeditaely intuitive. How about creating defined props for MyClass.alignLeft = -1, MyClass.alignRight = 0 etc, and use those instead? Internally you’re still tweaning numbers, but the functions become a little easier to use without understanding the internals.

    Also, slight typo in last paragraph (just to prove I did read it all ;)…
    “Finally, the above alternative is *now* a silver bullet,”…. should be *not*?

  5. public function onRollOver(e:MouseEvent): void {
    SomeTweenEngine.doTween(this, {alpha:1, time:0.4});
    }

    public function onRollOut(e:MouseEvent): void {
    SomeTweenEngine.doTween(this, {alpha:1, time:0.4});
    }
    I guess we should read alpha:0.5 on the onRollout method tween.

  6. Both right (and fixed), thanks.

    Adam: you’re right about adopting constants, and I’m sure there’s a lot of “but we could/should do <x>” in the whole thing, but I guess I just wanted to make things simple without having to potentially “introduce” and explain too many concepts. In this specific case, I’ve learned it’s simpler (as in, “shorter” and “works”) to explain to people that it goes from -1 to 1 then say it uses static constants such and such and why. Not to say it shouldn’t be taught, but that if I’m just writing a simple example, I don’t need to go that deep into making it the ultimate class and broaden the scope that much.

    In that sense, thanks for pointing it out in the comments, more information never hurts and hopefully people will run into it once they read the code above.

Comments are closed.