How to change the registration point in AS3

Here is something I think everyone would love to know how to do in Actionscript: dynamically change the registration point of a Movieclip or Sprite at runtime. I have certainly wanted to know how to do it for a long time, and I have been kind of less than satisfied with some of the solutions I've seen online. I should say, though, that they did give me valuable clues on developing my own solution, so I certainly don't mean to knock anyone here. One of the most popular solutions you'll find out there is a class that lets you link a library symbol to it. After you do, you get properties like rotation2, x2, y2, scaleX2, scaleY2--you get the idea.

However, this is not what I wanted....

I don't want to use new properties with a "2" at the end, as this just seems not natural. Instead, I wanted a function I could call to change the registration point, then just use the normal MovieClip properties: rotation, x, y, scaleX and scaleY. If the registration point of the MovieClip can be moved permanently in the first place, then all following transformations can just start using that new registration point. Also I wanted something I could apply to an existing MovieClip instance without the necessity of first linking the MovieClip to a custom class.

I also didn't want to have to deal with the Matrix class if I didn't have to. Not that there's anything wrong with that, but if a simpler solution exists, I'll obviously prefer it.

In my online reading, I was often struck by the assertion of many people that the registration point of a MovieClip cannot be moved (and technically, this is true!). And yet, I also saw many online examples where people had managed to do it. So just HOW do you move a registration point? I just had to know! And I've seen many posts on many forums where frustrated people are just dying to know the answer to this as well. In fact, for something so fundamental as changing the registration point, you would think that a solution would already be built into the language, but it's not.

It's a technicality

Like I said earlier, it's true that you can't move a registration point. The location of the registration point is always (0, 0) for any Sprite or MovieClip. However, this is a technicality. It turns out that we're all just asking the wrong question. If you can't move a registration point, then it doesn't make any sense to ask how to do it. However, while you technically can't move a registration point, you CAN move everything else in relation to it. And that is the key to the answer! You can't move a registration point, but you can effectively move one! By moving everything else!

This is analogous to how you change the registration point in the Flash authoring tool. You never move the point itself, as that's impossible. Instead, you move all the graphical content in relation to that point. Let's say you have a movie clip instance on the stage, with its registration point in the upper left hand corner, and you want the registration point in the middle instead. You would edit the clip: once inside the clip, select all its graphics and center them on the registration point. Then, back in scene 1, when you see that the object on the stage appears to have shifted from its former position because you've changed the insides of it, you would adjust its x and y location to compensate for the changes you just made. Perhaps you've done this before. The steps are something like this:

  1. (inside the clip, in edit mode) Shift every item of graphical content inside the clip up and to the left.
  2. (then, back in Scene 1) Shift the MovieClip itself down and to the right the same amount.

Notice that you didn't move the registration point itself, but you moved all of the movie clip's display children up and to the left, and finally, back in scene 1, you moved the movie clip itself down and to the right to compensate.

So if we want to change the registration point dynamically in our program, we basically have to do this same process, only with code. I won't keep you in suspense any longer. I have spent a good portion of a whole day on this, and when I was done, my function was just 11 lines long (if you don't count the comments)! But it worked, and it worked great! And I'm happy to share it with you!

Oh yeah, I almost forgot. I also wanted a solution that worked regardless of where the registration point was originally set, and I managed that, too! This function is very easy to use. The first parameter is the object you want to affect. This object must be a display object container, so it can be any Sprite, MovieClip or even Loader instance. The second and third parameters are simply the x and y of the new registration point you want, in relation to the upper left corner of the graphical content of the clip. And, like I said, the code works regardless of where the registration point is currently. Here is the function:

function setRegPoint(obj:DisplayObjectContainer, newX:Number, newY:Number):void {
	//get the bounds of the object and the location 
	//of the current registration point in relation
	//to the upper left corner of the graphical content
	//note: this is a PSEUDO currentRegX and currentRegY, as the 
	//registration point of a display object is ALWAYS (0, 0):
	var bounds:Rectangle = obj.getBounds(obj.parent);
	var currentRegX:Number = obj.x - bounds.left;
	var currentRegY:Number = obj.y - bounds.top;
	
	var xOffset:Number = newX - currentRegX;
	var yOffset:Number = newY - currentRegY;
	//shift the object to its new location--
	//this will put it back in the same position
	//where it started (that is, VISUALLY anyway):
	obj.x += xOffset;
	obj.y += yOffset;
	
	//shift all the children the same amount,
	//but in the opposite direction
	for(var i:int = 0; i < obj.numChildren; i++) {
		obj.getChildAt(i).x -= xOffset;
		obj.getChildAt(i).y -= yOffset;
	}
}

The comments explain pretty much what is going on, but here's a little fuller explanation: The getBounds() function is used to get the "bounding box" of the clip. The clip's x and y properties give the location of the registration point currently. Let's consider just the x direction for a moment: The bounds.left value is the leftmost location of the graphical content. The clip's x property gives the x location of the registration point. Both of these values are from the parent's perspective (coordinate space). The difference between these two numbers gives the current location of the registration point in relation to the leftmost graphical content. This value is then subtracted from the new x value that we want to use, which is then stored in the variable xOffset. All that remains is to shift all the children to the left by this amount, then finally shift the movie clip itself to the right by that amount.  The same process holds true for the y direction.

In the sample file, there is a movie clip instance on the stage called "book." The book movie clip purposely starts off with its registration point NOT in the upper left corner, nor is it in the center. The setRegPoint() function then sets the registration point to the center. This only requires one simple function call. When the book is clicked, the scaleX property is multiplied by -1. This is a well-known technique for doing a horizontal flip. Since the function call has set the reg point in the center, the scaling is done using that new registration point. The book movie clip even has a couple of shapes inside of it too, to demonstrate that all of the contents are shifted. Here's all the additional code it took to make that happen (combined with the function code above, this is the whole code contained in the demo file):

book.addEventListener(MouseEvent.CLICK, bookClicked);
function bookClicked(event:MouseEvent):void {
	book.scaleX *= -1;
}

//set the registration point for the book object in the middle instead:
setRegPoint(book, book.width / 2, book.height / 2);

And here's a live demo:

Note: The attached file is in CS4 format. It demonstrates how easy it is to use the function presented above. A movie clip on the stage has its registration point dynamically altered. After that, any transformations that are done to it use this new point instead of the original one. If you are using CS3, this file won't work for you. But it's not as though there's a lot to it.  I've given the complete code above, so just copy and paste it into a file of your own, and apply it to one of your own movie clip instances. Enjoy!

Producing a string with randomly colored characters

OMG! I loved the simplicity of the setRegPoint function.
The program below works, but is calling for such simplicity.
Its objective is simply to randomly format(in this case, color) the individual characters of a given string.

Here goes....

import flash.display.*;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
import flash.text.TextFormatAlign;
/*
The objective is to format individual characters of a given string.
*/
//Definition of the retangular space where the string will be placed
var wdth:uint=200;
var hght:uint=200;
//In the example, the string will be center aligned
var regX:uint=wdth/2;
var regY:uint=hght/2;
var X:int = 0;
var Y:int = 0;
//Handling variables
var string:String; //The string whose characters will be randomly colored
var chrsTextFields:Array = new Array();//Eack string character is put in a TexField for formating
var colorname:MovieClip; //This movie the holder of the colored characters
//Color handling variables
var colors:Array = [0xFF0000,0x00FF00,0x0000FF]; //Selected color set
var kolors:Array = [0xFF0000,0x00FF00,0x0000FF];
var color:Number = 0xFF0000;
var lastColor:Number = 0x000000;
//General purpose variables
var j:int=0;
var totalCardWidths:int

//Program

colorname = new MovieClip();
addChild(colorname);

string = "Rhinoceros"; //The string whose characters will be randomly colored
//Create an empty TextField for each string character and store them in an array
for(var i:int=0;i

unfinished!

Hi!

Sadly, your code is not finished! Could you include it completed?

Merci!!!

Jody Hall's picture

That might be my fault

Cleostem,

First of all, I thought you meant it was MY code that was incomplete. It took me a long time to realize you were referring to the previous comment!

Hosting a Drupal website has been quite an adventure. It's so geeky! Anyway, one of my goals is to make it easier for users of the site to post code. The way I do it in my tutorials is kind of arcane, involving some markup code. I would like to have a simple toobar button on a rich text editor which you could just click to tag your highlighted code. I haven't found that way, yet. Sigh.

Anyway, meanwhile, Mudzidzisi: If you write to me using the site's contact form, we can discuss arranging for me to take a look at your files.

Scale issue !

Hey Jody,

First of all, thank you very much for the flashconnection.com site ! the posts really explains everything of the code, and why you made this or that way, this is very good for learning purposes, instead of just copy and see if it worked.

The point is, I was using import com.greensock*; and made a scale at the MovieClip. So I usedsetRegPoint() to place the registration point in the middle and after used greensock to change the scale and I used setRegPoint() again to return the registration point to (0,0), and then the object just moved !

I think that somehow getBounds() don't look to the scaleX or scaleY properties and then when we use the Offset the number is incorrect.

I don't know if you could take a look, i had to return my MovieClip to scaleX =1 and scaleY =1 before return the registration point to (0,0).

Jody Hall's picture

Sorry I missed your comment

Thank you for writing to me. I apologize for my lack of attention to your posted comment, I must have missed it the first time around. However, I appreciate all comments and I really try to answer them all, but I'm also only too human, so all I can do is apologize.

The article I wrote was intended to describe the nature of the registration point: How it's not a physical thing that can be moved, but rather how it's a fixed thing, and you can only "change" it by moving all the graphical content in relation to it. The setRegPoint() method was written solely for the purpose of demonstrating this principle. It was NOT intended to be an all-around tool for changing registration points. It was really just an experiment of mine.

Greensock (Jack Doyle) actually has his own tools for stuff like this. Specifically there is a plugin called transformAroundPoint, which allows you to tween things using a custom point as the registration point, but you will have to become a club greensock member to get access to it.

Thank you!

I want to really thank you for sharing your code and knowledge. I found the class that you mention, and as happened to you, it did not convince me completely. Your solution is very smart! I've got only a question. I tried to apply this function directly to a Sprite and seemed it didn't work (for testing, I applied a simple scale tween to it). But after adding this Sprite to a MovieClip and then tweening the MC, everything worked fine. Is it always necessary to include the objects into Containers? Thanks in advance.