DC251: Actionscript Tip – flickering custom mouse cursor
Several of the projects in the Casual Games class feature a replacement for the mouse cursor. Depending on how you implemented this, you might run into the dreaded "flickering mouse cursor" problem. It was a bit of a puzzler at first, but here's a quick walkthrough on how to solve the problem...
First, let's talk about how you might end up with the issue in the first place. First, you'll have the custom class that will be replacing the mouse cursor. The constructor for this class might look something like this:
public function CustomCursor() {
this.addEventListener(Event.ENTER_FRAME,enterFrame);
}
Thus, when you construct a new CustomCursor, it automatically gets an event listener that makes something happen every frame. Here's that enterFrame function that you might have:
private function enterFrame(e:Event):void {
this.x = this.mouseX;
this.y = this.mouseY;
}
That code is what is causing the flicker. The reason behind it is the multiple coordinate layers of Actionscript that we've talked about in class. There is first, the absolute, global level of coordinates (where, in pixels, an object is on the stage). Additionally, each object on the stage has it's own local level of coordinates--the upper left hand corner of a square on the stage is that square's LOCAL 0, 0 point, but that square might be at a GLOBAL 30, 50 point on the stage.
The problem above is that this.x (or y) refers to the CustomCursor's global position on the stage. The x, y there is where, globally, on the stage the CustomCursor's local 0, 0 point goes. With me so far? Here's the big problem: The mouseX/mouseY variables however, are on LOCAL coordinates. On the local coordinate scale, where is the mouse, compared to CustomCursor's LOCAL 0, 0 point. Here's an example to illustrate what happens:
A CustomCursor is on the stage at GLOBAL 100, 100.
The mouse is on the stage at GLOBAL 120, 120
This means that local to the CustomCursor, mouseX, mouseY is 20, 20, because it is 20 right and 20 down from the CustomCursor's 0, 0 point.
So the code runs, it enter's a new frame, and runs that function up above. That means it sets the CustomCursor's global x, y to 20, 20 (taking the local mouseX, mouseY from above). Here's the kicker though: When it does that, if the user has kept his mouse in the exact same spot (global 120, 120), what's the NEW local mouseX, mouseY? 100, 100-- because the mouse is 100 right and 100 down from where the CustomCursor has moved to (20, 20)! So now the CustomCursor moves back there... and we're back to the initial state, and the CustomCursor will continually bounce between those points as long as the user holds his mouse there, each frame bouncing back and forth.
The good news is, the good people at Adobe have provided us with an easy means for dealing with the power of multiple coordinate levels. Here is the adjusted, non-flickering, working code for that function:
private function enterFrame(e:Event):void {
var localPoint:Point = new Point(this.mouseX, this.mouseY);
var globalPoint:Point = localToGlobal(localPoint);
this.x = globalPoint.x;
this.y = globalPoint.y;
}
Note that that code requires an import statement at the top (import flash.geom.*; will get the job done). This code uses Actionscript's built in localToGlobal function to convert the local point to Global coordinates. Once we have that global point, we can assign the CustomCursor's global x, y to the mouse pointer's global x, y and all is well in the world. Note, in case you need it for something else, that there is also a GlobalToLocal function!
Tags: Actionscript, casual games, custom cursor, globalToLocal, localToGlobal