AS3: How can I implement a perfect ROLL_OVER(or MOUSE_OVER) event listener on curved movieclips - actionscript-3

I've a problem with ROLL_OVER event listener. When I enter the empty area withing the movieclip with mouse cursor, ROLL_OVER event triggers. But I want that event trigger only when mouse cursor is on the colored area.
To Make it more clear: Think about " O " letter, when mouse cursor is between the empty area of O letter (inside of O) , event shouldn't trigger. It should trigger only when mouse curser is on the black area.
How can I implement this?
Thanks
-Ozan
PROBLEM IS SOLVED THANKS TO #Ethan Kennerly
I just want to add a few things to help people have problem same as me. In my situation I tried to make continents glow when my mouse is over them. I used the ROLL_OVER/MOUSE_OVER eventlistener to check if my mouse is over them or not. But with the data given by Ethan Kennerly I produced another way.
In Ethan Kennerly's solution, if your mouse enters the area of continent from a transparent area , it doesn't get blur effect because ROLL_OVER and MOUSE_OVER event listeners only trigger once per enters so I used MOUSE_MOVE event listener on each continent movieclips.
And for this statement:
if (isPixelTransparent(DisplayObject(event.currentTarget), new Point(stage.mouseX, stage.mouseY)) {
return;
}
add whatever is in the "ROLL_OUT or MOUSE_OUT" eventlistener function, add all of them inside this statement. But don't remove ROLL_OUT or MOUSE_OUT functions.

It sounds like the movie clip contains a shape that has transparent pixels. Transparent pixels respond to mouse over and roll over. If you could draw vector graphics that have no shapes with transparent pixels, the mouse would ignore the empty space in the movie clip's bounding box.
Yet it sounds like you need to use transparent pixels and you want the mouse to ignore them, so you could guard, like this:
private function onRollOver(event:MouseEvent):void
{
if (isPixelTransparent(DisplayObject(event.currentTarget), new Point(stage.mouseX, stage.mouseY)) {
return;
}
// respond to roll over.
}
To detect transparency, Miguel Santirso rendered the pixels and translated the coordinate space here: http://sourcecookbook.com/en/recipes/97/check-if-a-pixel-is-transparent-in-a-displayobject (Except line 38 looks on my computer like "rect" got rendered as "ct"). You could optimize that code by only drawing the pixel in question, instead of the whole image, and checking if that pixel value (getPixel32) is 0, instead of calling a hitTest. I would optimize Miguel's code like this:
public static function isPixelTransparent(objectOnStage:DisplayObject, globalPoint:Point):Boolean
{
var local:Point = objectOnStage.globalToLocal(globalPoint);
var matrix:Matrix = new Matrix();
matrix.translate(-local.x, -local.y);
var data:BitmapData = new BitmapData(1, 1, true, 0x00000000);
data.draw(object, matrix);
return 0x00000000 == data.getPixel32(0, 0);
}
By the way, if all your movie clips would have the same hit test shape, you could create a separate transparent shape that listens to the roll over. I use a transparent shape to define a custom hit test shape that is a consistent and simple shape (like a circle) when the image is a more complicate shape (like an X or an O with nothing in the middle). The custom hit test shape is a Sprite with a transparent shape. The sprite listens to the roll over. A separate mouse listener shape is also useful if your movie clip, on later frames, creates new shapes that alter the silhouette of the movie clip.

The easiest solution would be using the Interactive PNG class by Moses.
http://blog.mosessupposes.com/?p=40
Normally the clear areas of a PNG are treated as solid, which can be especially frustrating when dealing with a lot of images that overlap each other because they tend to block mouse interactions on the clips below them.
This utility fixes that so that mouse events don't occur until you
bump against a solid pixel, or a pixel of any transparency value
besides totally clear. InteractivePNG lets you set an alphaTolerance
level to determine what transparency level will register as a hit.

Related

Actionscript 3 - Making one MovieClip the hitArea of another

Suppose I have a rectangle whose instance name is
rectangle
and suppose I have a square right beside the rectangle and the squares instance name is
square
. Now, I want the rectangle to hitArea to be it's own area as well as the square's area. So, if I have these three event listeners:
rectangle.addEventListener(MouseEvent.CLICK, rectangleClick);
rectangle.addEventListener(MouseEvent.MOUSE_OVER, rectangleHover);
rectangle.addEventListener(MouseEvent.MOUSE_OUT, rectangleOut);
How do I make it so that when I hover over, hover out of and click the rectangle OR square, the rectangle event listeners get called?
Note: the event listeners have
event.target
and
event.currentTarget
so I cannot do a simple
square.addEventListener(MouseEvent.CLICK, rectangleClick);
since I need the rectangle to be the
event.target
.
Since your listeners are referencing the Rectangle only, just identify it by its instance name and NOT event.target. Now you can apply the same addEventListner() method to the Square, and the Rectangle will be acted upon, in either case, by the listener function.

Custom cursor distorts a little bit when rolling over movie clips

I have a custom cursor in my flash project. That cursor consists of several parts (i.e. several movie clips inside of the cursor movie clip). And when the cursor rolls over different movie clips in my project, the parts of the cursor distort a little bit as though moving 1 pixel in relation to each other. And therefore the look of the whole cursor distorts a little. This happens every time the cursor crosses the boundary between the movie clips of the project (buttons, design pieces and so on). How can I make my cursor always retain one and the same look?
Thanks in advance
I'm guessing you're doing something like this to position your custom cursor:
stage.addEventListener(MouseEvent.MOUSE_MOVE, moved);
function moved(e:MouseEvent):void {
customCursor.x = e.stageX;
customCursor.y = e.stageY;
}
If so, when you move the mouse over a MovieClip or other element, your listener is receiving the event from that DisplayObject rather than the Stage. For some reason, DisplayObjects positioned at sub pixel values produce e.stageX and e.stageY values which aren't exactly the same as stage.mouseX and stage.mouseY, so your custom cursor elements are jumping slightly as the pixel values round differently.
Try using the Stage mouse position directly instead:
stage.addEventListener(MouseEvent.MOUSE_MOVE, moved);
function moved(e:MouseEvent):void {
customCursor.x = stage.mouseX;
customCursor.y = stage.mouseY;
}

How to set mouse click to do not hit transparent pixels

Imagine a painting program. I have two imagens overlaping each other. I must be able to click on the image behind if I click on a transparent part of the above one.
I add an event listener on each image. So I must prevent the first one to dispatch click event in order to the behind one dispatch it.
(I mean, I already check for transparent pixels, but i can't cancel that event to the other imagem dispatch it.)
There is some dirty solution. Say your "images" are encapsulated in Sprites, otherwise you can't attach a listener to a Bitmap object or a Shape object. MovieClips - well, should still work, although checking alpha is a lot harder. You have your listeners attached to those sprites. First, that sprite in itself can check transparency, and if not transparent, proceed the event. To get what's behind, you can call stage.getObjectsUnderPoint(), this will return an Array of DisplayObjects, with foreground being on top. So you might have one single listener that would call this to enumerate what's under the point, then do as Roman Trofimov advises to determine that object's transparency. Once you'll find a non-transparent object, you do:
// "b" is a non-transparent DisplayObject found beforehand
while (b && (!(b is InteractiveObject))) b = b.parent;
if (b) {
var p:Point = new Point(e.stageX, e.stageY);
p = b.globalToLocal(p);
b.dispatchEvent(new MouseEvent('click', true, true, p.x, p.y));
}
This will dispatch a click event to the object that's able to receive events, and is opaque enough to obscure the background.
The MovieClip property 'mouseEnabled' can prevent click events from triggering on objects in front of other objects, for example, take two movie clips of the same size and same position:
mc1.addEventListener(MouseEvent.CLICK, click1);
function click1(e:MouseEvent) : void
{
trace("1");
}
mc2.addEventListener(MouseEvent.CLICK, click2);
function click2(e:MouseEvent) : void
{
trace("2");
}
mc1.mouseEnabled = false;
Output would be "2" as the click would essentially go 'through' mc1.
In flex you can add mouseEnabledWhereTransparent="false" to components. Not fully sure if that variable is available to you though
Generally, you will always get first event from first sprite (if it was not capture phase ot priority change). But as I understand, you need to check was it transparent and if not - stop bubbling this event.
To stop event - use event.stopImmediatePropagation() in event handler.
To determine transparency:
1) If it is just only the one bitmap, you can check its .bitmapData with method .getPixel32, it returns you alpha value.
2) If it is combined sprite (vector and bitmap), you need to manually render it to Bitmap using BitmapData.draw and then use solution from second case
3) To understand if it was absolutelly miss click, you can try this method: Check transparency

XY restrictions for custom mouse cursor in Actionscript3

I have this interactive 5 seconds animated intro for a website. the preloader and one item are animating and i made the second animation follow the mouse cursor but it has to stay within a certain part of the stage to work with the other animation happening on screen.
I have this code on the movie clip
Mouse.hide();
potistiri.addEventListener(Event.ENTER_FRAME, newCursor);
function newCursor(event:Event): void { potistiri.x = mouseX;
potistiri.y = mouseY; }
and i like i said i just want it to stay in the area i want...
i found this code which gives me errors for not putting the staments if and else if correctly or that it needs a rightparen when i input my numbers in...
if(this._x>Stage.width){
this._x=Stage.width;
}else if(this._x<0){
this._x=0; }
but i cant get it to work...
i need it to move between x 208-656 and y 140-336 and when it gets out of that area the object stay there doing its loop and you see the normal mouse cursor moving in the rest of the screen.
thanks a lot in advance...im leaving my it to the experts in here to pls help me ouy!
The logic you're using in your if/else is fine for clamping the movie clip to a specific area, what exactly do your errors say?
In regards to seeing the normal mouse cursor again you could try using the same if/else checks to determine whether the mouse should or should not be hidden ie if the mouse is outside the area and is hidden, call Mouse.show(), else if it is inside the area and shown, call Mouse.hide().

AS3 How would I make a figure do different animations based on keyboard input?

I am using Action script 3, and CS5.5. I wish to make my character animate different ways based on keyboard input. Like say if I press the right arrow key I want my run animation to start, and if I press the left arrow key I want to reverse that same animation. Then When there is no input I want him to be just standing, and when the up arrow key is pushed I want the jump animation to work. What is the best way to do this in action script 3?
Use frame labels to programmatically control the movement of the playhead on the timeline.
MEDIA
The character clip should have this structure:
-3 layers on the timeline, each with three keyframes for nine in all
--a frame label layer, giving a frame label to each frame ("standing", "walkingRight", "walkingLeft")
--a stops layer, which make each frame separate, so movement between the frames is only controlled by code
--a sub-animations layer, with one animation in each frame, all the animation looping and having no stops in them
---walk left animation clip
---walk right animation clip
---standing animation clip
Just to be clear, each of these nested animations takes up one frame, but if you move the playhead over it, it looks like that animation is playing alone, and it stays that way until your code moves the playhead.
CODE IT
var character:MovieClip=new MyCharacterClass()
addChild(character);
character.gotoAndStop("standing")
stage.addEventListener(KeyboardEvent.KEY_UP,keyUp);
protected function keyUp(event:KeyEvent):void
{
switch (event.keyCode)
{
case Keyboard.LEFT:
character.gotoAndStop("walkingLeft")
break;
case Keyboard.RIGHT:
character.gotoAndStop("walkingRight")
break;
default:
character.gotoAndStop("standing")
break;
}
}
This is the simplest possible example--click the left arrow, and the animation shows the character walking left (albeit in one spot, you need to add that actual movement code). Click the right arrow and it walks right. Press any other key and the character stands.
First off, you use KeyboardEvent to check for arrow keys being pressed. Then what exactly you do in the event listener function depends on how exactly your animations were created. For example, if what you're doing is playing MovieClips that were drawn in Flash, then switch the MovieClip in the KeyboardEvent listener.