Check when a part of the MovieClip leaves the Stage - actionscript-3

I'm creating a Drag and Drop game using AS3, i want to check when a apart of a Movieclip is outside the screen to move the View behind and let the user choose where to drop it.
I cant' test if the MovieClip credentials are bigger that the stage (scaleMode = NO_SCALE) Width/Height, because there is a part of the stage that it's hidden behind the browser window.
It's the same aspect as MOUSE_LEAVE just this time it has to be for MovieClips, i tried to see the code behind MOUSE_LEAVE but i couldn't reach it.
Thank You.
MAIN CLASS
[SWF(width='800', height='800',backgroundColor='#CC99FF', frameRate='60')]
public class DragTest extends Sprite
{
public function DragTest()
{
addChild(new World(this));
this.stage.scaleMode = "noScale";
this.stage.align = "TL";
this.graphics.lineStyle(5,0x555555,0.5);
this.graphics.drawRect(0,0,800,800);
}
}
WORLD CLASS
public class World extends Container // Container from my SWC
{
private var _display:Sprite;
private var _dragPt:Point;
private var _dragedObject:MovieClip;
public function World(display:Sprite)
{
super();
_display = display;
myMC.addEventListener(MouseEvent.MOUSE_DOWN, onPickUp, false, 0, true );
display.stage.addEventListener(MouseEvent.MOUSE_UP, onDrop, false, 0, true );
display.stage.addEventListener(Event.MOUSE_LEAVE, onMouseLeave, false, 0, true );
}
protected function onMouseLeave(event:Event):void
{
trace("Mouse Is Leaving The Stage");
}
protected function onDrop(e:MouseEvent):void
{
_display.stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMoveObject);
}
private function onPickUp(e:MouseEvent)
{
_dragedObject = e.currentTarget as MovieClip;
_display.stage.addEventListener(MouseEvent.MOUSE_MOVE, onMoveObject, false, 0, true);
}
protected function onMoveObject(e:MouseEvent):void
{
var point:Point = new Point(_display.stage.mouseX, _display.stage.mouseY);
(_dragedObject as MovieClip).x = point.x;
(_dragedObject as MovieClip).y = point.y;
}
}
Here is an Example :
Simple Code

The easiest approach would probably be to use getBounds(stage) and compare with stageWidth and stageHeight:
var bounds:Rectangle = _draggedObject.getBounds(stage);
if (bounds.left < 0) {
// left part of object is off-screen
} else if (bounds.right > stage.stageWidth) {
// right part of object is off-screen
}
if (bounds.top < 0) {
// top part of object is offscreen
} else if (bounds.bottom > stage.stageHeight) {
// bottom part of object is off-screen
}
You could move the display in each of these cases.

You can try to create an invisible zone that's a little bit smaller than your stage.
So you can add the MOUSE_LEAVE event to the zone, and when your mouse leaves that zone, you can do what you want.
Check the example here.

In response to Aaron Beall's response:
For a more interesting effect, if you want to wait until the movie clip is completely off stage, you can swap the boundaries you check on the object
var bounds:Rectangle = object.getBounds(stage);
if (bounds.right < 0) {
// do thing
} else if (bounds.left > stage.stageWidth) {
// do thing
}
if (bounds.bottom < 0) {
// do thing
} else if (bounds.top > stage.stageHeight) {
// do thing
}
Make sure you have import flash.geom.Rectangle; imported if this is inside a class.

Related

remove or destroy child completely

I've got a child named "viseur" that appears on a particular scene and replace my actual cursor.
When the event "removeViseur" is called, I would like to completely remove the child "viseur" (and I'll use my previous cursor).
I did that :
stage.addEventListener("functionCalled", removeViseur, false, 0, true);
public function removeViseur(e:Event):void{
trace("removeViseur");
viseur.visible = false;
viseur.parent.removeChild(viseur);
viseur = null;
}
The child "viseur" is no longer here, but if do Alt+tab or changing window and came back, the child "viseur" come back...
Do you know how I can destroy it completely ? (I don't need it after this function is called)
Thx,
EDIT
Here is more of my code.
public static var viseur:Viseur;
var lookCocoDessous:Boolean = thisBack == "cocotierDessous";
if (lookCocoDessous) {
viseur = new Viseur(stage);
stage.addChild(viseur);
viseur.visible = true;
stage.addEventListener("functionCalled", removeViseur, false, 0, true);
public function removeViseur(e:Event):void{
trace("removeViseur");
viseur.visible = false;
viseur.parent.removeChild(viseur);
viseur = null;
}
And "viseur" has is own class like that
viseur.as :
public class Viseur extends MovieClip
{
private var engine:Engine;
private var stageRef:Stage;
private var p:Point = new Point();
public function Viseur(stageRef:Stage)
{
Mouse.hide(); //make the mouse disappear
mouseEnabled = false; //don't let our cursor block anything
mouseChildren = false;
this.stageRef = stageRef;
x = stageRef.mouseX;
y = stageRef.mouseY;
stageRef.addEventListener(MouseEvent.MOUSE_MOVE, updateMouse, false, 0, true);
stageRef.addEventListener(Event.MOUSE_LEAVE, mouseLeaveHandler, false, 0, true);
stageRef.addEventListener(Event.ADDED, updateStack, false, 0, true);
}
private function updateStack(e:Event) : void
{
stageRef.addChild(this);
}
private function mouseLeaveHandler(e:Event) : void
{
visible = false;
Mouse.show(); //in case of right click
stageRef.addEventListener(MouseEvent.MOUSE_MOVE, mouseReturnHandler, false, 0, true);
}
private function mouseReturnHandler(e:Event) : void
{
visible = true;
Mouse.hide(); //in case of right click
removeEventListener(MouseEvent.MOUSE_MOVE, mouseReturnHandler);
}
private function updateMouse(e:MouseEvent) : void
{
x = stageRef.mouseX;
y = stageRef.mouseY;
e.updateAfterEvent();
}
}
}
}
Viseur is adding itself to the stage whenever Event.ADDED fires. This fires whenever anything is added to the stage (not just Viseur). You've also made Viseur a static property, thus ensuring it doesn't disappear from memory.
To be succinct: add Viseur only once, and from outside of itself (thereby removing the need to pass a stage reference). Be aware that once a DisplayObject is parented, it automatically gains stage & root properties which you can reference for mouseX and mouseY.
That said, if you're trying to replace the mouse cursor, I highly recommend taking a different tact: Native cursors.
See http://inflagrantedelicto.memoryspiral.com/2012/07/as3-quickie-native-mouse-cursors/

Stage becomes null

I'm creating a game with AS3 in Flash Professional CS5.5.
In this game I have an "again" button, so that the player can reset the level and start from new.
My problem now is:
After the clicked "again" the stage becomes null.
All I do in the "ResetLevel" method is, that I set the x and y positions of some elements back to 0, remove some items from the movieclip, but I don't remove ALL items from the display list. So the background, the hud, the plane isn't removed from the movieclip. Here a sketch of my displaylist. The removeable items are sometimes zero, sometimes they are 30 or more items (depends on playtime, and so on)
Displaylist:
stage
|-- Game movieclip
|--LevelBackground
|--Removeable item
|--Removeable item
|--Removeable item
|--Plane
|--HUD
But after removing the "removeable items" and setting the position coordinates of levelbackground and plane the stage is null.
Maybe someone can help me to point me to a solution for this problem.
EDIT:
The "ResetLevel"-method will be called inside the "game movieclip" and the stage will be accessed from the "game movieclip", too. So I don't remove the "game movieclip" from the displaylist when I reset the level. I only remove some elements, that the game movieclip contains from the movieclip.
Here some pseudocode from the "game movieclip class" (GameMC):
public class GameMC extends Sprite {
//Some properties here
public function GameMC() {
//Some code here
//--Events--
this.addEventListener(Event.ADDED_TO_STAGE, Init, false, 0, true);
this.addEventListener(Event.REMOVED_FROM_STAGE, Removed, false, 0, true);
}
private function Init(e:Event) {
this.removeEventListener(Event.ADDED_TO_STAGE, Init);
//Some Code here
}
private function ResetLevel() {
//Some Code here, too
if(removeItemArray.length > 0) {
for(i = 0; i < removeItemArray.length; i++) {
currentRemoveableItem = removeItemArray[i];
this.removeChild(currentRemoveableItem );
removeItemArray.splice(i, 1);
}
}
level.x = 0;
level.y = 0;
trace(stage); //Will output null
}
}
When a DisplayObject is removed from the DisplayList, it does not hold any reference to the stage anymore. So, whatever you need to set/calculate, do it in a valid state. Event.ADDED, Event.ADDED_TO_STAGE, Event.REMOVED and Event.REMOVED_FROM_STAGE help to verify the DisplayObject's state is valid.
Now I used to store the stage into a property and access this:
public class GameMC extends Sprite {
//Some properties here
private var stagevar:Stage;
public function GameMC() {
//Some code here
//--Events--
this.addEventListener(Event.ADDED_TO_STAGE, Init, false, 0, true);
this.addEventListener(Event.REMOVED_FROM_STAGE, Removed, false, 0, true);
}
private function Init(e:Event) {
this.removeEventListener(Event.ADDED_TO_STAGE, Init);
this.stagevar = stage;
//Some Code here
}
private function ResetLevel() {
//Some Code here, too
if(removeItemArray.length > 0) {
for(i = 0; i < removeItemArray.length; i++) {
currentRemoveableItem = removeItemArray[i];
this.removeChild(currentRemoveableItem );
removeItemArray.splice(i, 1);
}
}
level.x = 0;
level.y = 0;
trace(stagevar); //Will output [Object Stage]
}
}

HitTest for objects not yet on Stage

I need to add a MovieClip to stage, the limitation being that it should only be added to an empty area on the stage. The stage itself either contains complex shapes or is manipulable by the user i.e. he can drag/move objects to change the empty area. The hitTest and hitTestObject methods need DisplayObject already available on the stage. What is the right way to go - the only solution I can imagine is having added my object on the stage and then repeatedly doing hit tests?
[Imagine it to something like adding sprites in a video game - they must spawn in empty regions; if they pop out from inside of each other, then it'll look really odd.]
Well, when you create a new class, just turn it off with a variable and set the visibility to false, then loop until there is no hitTest.
A silly example:
public class someClass extends Sprite
{
private var objectsOnStage:Array;
public function someClass(objectsArray:Array) {
objectsOnStage = objectsArray;
visible = false;
addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event){
removeEventListener(Event.ADDED_TO_STAGE, init);
addEventListener(Event.ENTER_FRAME, SEARCH);
}
private function SEARCH(e:Event) {
var doesHit:Boolean = false;
x = Math.round(Math.random() * (550 - 0)) + 0;
y = Math.round(Math.random() * (400 - 0)) + 0;
for (var i:int = 0; i < objectsOnStage; i++) {
if (doesHit) break;
if (this.hitTestObject(objectsOnStage[i])) {
doesHit = true;
}
}
if (doesHit) return;
placedInit();
}
private function placedInit() {
visible = true;
removeEventListener(Event.ENTER_FRAME, SEARCH);
//now init the stuff you want.
}
}
You just check if bounding boxes of both clips overlaps. Like this:
import flash.geom.Rectangle;
import flash.display.MovieClip;
// create simple movie clips that has a rectangle shape inside
var sym1 : MovieClip = new Sym1();
var sym2 : MovieClip = new Sym2();
// get a rectanle of both clipt
var boundingBox1 : Rectangle = sym1.getBounds(this);
var boundingBox2 : Rectangle = sym2.getBounds(this);
// check if bounding boxes of both movie clips overlaps
// so it works like hitTestObject() method
trace( boundingBox1.intersects( boundingBox2) )
I know this post is super old, but in case it helps anybody --
If you need to do a hit test on a movieclip that isn't on the stage. A workaround is to rasterize it to a bitmap first.
var bitmapData:BitmapData = new BitmapData(mc.width, mc.height, true, 0x0000000);
bitmapData.draw(mc);
if (bitmapData.getPixel32(x, y) > 0) {
// Hit true.
}

AS3 draws bigger Sprite than i have set (padding/margin ?)

i created my own Button which simply extends from Sprite.
Its able to show 3 different text fading in and out using a timer.
In my draw function i first draw a rectangle with the Sprites graphics object representing the background of the button.
I added a function to set the button width. This property is used to draw the rectangle. I know that the sprites's size is updated after the rectangle drawing. But for some reason the sprite's width is always more than what i have set via my "setButtonWidth" function.
Now i have a simple sprite acting as a button having a graphics.drawRectangle part drawing a simple rect. with lets say 500px width. But when i trace the width of that button it is always about 10% more. Where are these 10% coming from?
I read about calling validateNow(). But this is only for Labels, or Checkboxes. For some reason i cannot access the library for Label. This must work somehow with TextFields. But how?
// this function is part of MyButtonClass.as file
function drawButton()
{
this.graphics.clear();
this.graphics.beginFill( currentBackColor);
this.graphics.drawRoundRect(0, 0, int(this.buttonWidth)+20, 30, 10, 10);
this.graphics.endFill();
}
// this code is in main action code frame
// the buttonWidth is set this way
stage.addEventListener( Event.RESIZE, updateBtn);
function updateBtn( event:Event)
{
// access the container in which the buttons are in like ...
for( var i = 0; i < buttonContainer.numChildren; i++) {
var btn = buttonContainer.getChildAt( i) as MyButtonClass;
// MyButtonClass is the class which extends from Sprite drawing my button
// and using buttonWidth to draw the backgrounds width.
btn.setButtonWidth( (stage.stageWidth / 2) - 20);
// i set half of stageWidth because i have two columns with a list of buttons
// after setButtonWidth is called in MyButtonClass, the draw function is called
// which does the graphics stuff above.
}
}
this.buttonWidth is set to 500. There is nothing special done on that sprite. No children to be added, only this drawing stuff. But this sets the sprite's width to 550 or something.
Define Button like this
package {
import flash.display.Sprite;
public class Button extends Sprite {
private var _buttonWith: int = 0;
private var _buttonHeight: int = 0;
public function Button() {
// constructor code
}
public function set buttonWidth(w: int): void {
_buttonWith = w;
updateButton();
}
public function get buttonWidth(): int {
return _buttonWith;
}
public function set buttonHeight(h: int): void {
_buttonHeight = h;
updateButton();
}
public function get buttonHeight(): int {
return _buttonHeight;
}
protected function updateButton() {
graphics.clear();
graphics.beginFill(0xff00ff);
graphics.drawRoundRect(0, 0, buttonWidth, buttonHeight, 10, 10);
graphics.endFill();
}
}
}
And create instance like this
var button:Button = new Button();
addChild(button);
button.buttonWidth = 100;
button.buttonHeight = 30;

mouseX/Y confusion when dragging a child of a container

I've got a grid of images which are added to a imagecontainer(Sprite) in a class.
When I click a button, the imagecontainer gets tweened(scaled) to 0.2
Now I would like to start dragging around the images. on mouse-down I add an enterFrame Event:
function onEnterFrame(e:Event):void
{
imagecontainer.image.x = this.mouseX;
imagecontainer.image.y = this.mouseY;
}
Unfortunately the image is never on the mouseposition but has a increasing/decreasing offset to the mouse pointer.
The alternative, startDrag/stopDrag works perfectly, but I still need the mouseX/mouseY for placing the image on a grid…
I tried also parent.mouseX, no luck with that.
Why is this happening? I thought mouseX/mouseY is always depending on the stage-dimension.
have you tried:
function onEnterFrame(e:Event):void
{
imagecontainer.image.x = stage.mouseX;
imagecontainer.image.y = stage.mouseY;
}
If you want to get the mouseX and mouseY relative to the mouse position on the stage then why don't you use:
stage.mouseX,
stage.mouseY
Also, if you scale something up (let's say to a total size of 200%), then 50 pixels across on the stage is actually 25 pixels across in the container that has been scaled.
Use this as a document class to see what I mean:
package
{
import flash.display.MovieClip;
import flash.events.Event;
public class Main extends MovieClip
{
private var _box:MovieClip;
public function Main()
{
addEventListener(Event.ENTER_FRAME, _move);
_box = new MovieClip();
_box.scaleX = _box.scaleY = 2;
addChild(_box);
}
private function _move(e:Event):void
{
trace("stage: " + stage.mouseX + ", " + stage.mouseY);
trace("box: " + _box.mouseX + ", " + _box.mouseY);
}
}
}
If you are still looking for this maybe you can use startDrag(); and stopDrag();
for dragging images.
like this:
image.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDownMC);
image.addEventListener(MouseEvent.MOUSE_UP, onMouseUpMC);
function onMouseDownMC(e:MouseEvent):void
{
e.currentTarget.startDrag(true);
}
function onMouseUpMC(e:MouseEvent):void
{
e.currentTarget.stopDrag();
}
Update : You can set ur function like this:
image.addEventListener(MouseEvent.MOUSE_DOWN, onMouseMC);
image.addEventListener(MouseEvent.MOUSE_UP, onMouseMC);
function onMouseMC(e:MouseEvent):void
{
var type = e.type;
if(type == MouseEvent.MOUSE_DOWN) /// u can use "mouseDown" accept MouseEvent.MOUSE_DOWN
{
e.currentTarget.startDrag(true);// if you set true its gonna drag the obj from center
}
else if(type == MouseEvent.MOUSE_UP)
{
e.currentTarget.stopDrag(); //u can use "mouseUp" accept MouseEvent.MOUSE_UP
}
}