Using localToGlobal to switch image positions - actionscript-3

I am trying to convert from coordinates of a parent container to global stage coordinates. This is the object and the container:
imgSprite.addChild(imgInstance); //imgInstance is imported from class
imageContainer.addChild(last);
imageContainer.addChild(imgSprite); // imgSprite and last are to be replaced
Basically, I want to switch positions of two images in imageContainer.
I applied the localToGlobal() function, but that hasn't had any effect. What did I do wrong?
This is my function that replaces them. Note: this code belongs to class main, parent of container is stage and every imgSprite is just added to imgContainer and moved via obj.x, obj.y (not in the code).
function click(e:MouseEvent):void {
var p1:Point = e.currentTarget.localToGlobal(newPoint(e.currentTarget.x, e.currentTarget.y));
var p2:Point = last.localToGlobal(new Point(last.x, last.y));
e.currentTarget.x = globalToLocal(p2).x;
e.currentTarget.y = globalToLocal(p2).y;
last.x = globalToLocal(p1).x;
last.y = globalToLocal(p1).y;
}
As I understand it, every object has local and global positions. So, here I convert a local position to a global, and then again when moving the other object to this position, I convert it to local, as they belong to the same parent it should result in matching coordinates, but my images start flying around in all directions and I start pulling my hair.

var p2:Point = last.localToGlobal(new Point(last.x, last.y));
should be
var p2:Point = last.localToGlobal(new Point(0, 0));
The Point argument within localToGlobal() is the point within the DisplayObject that you want to globalize. So if last is located at (50,50) within its parent container, your code is really asking for the global coordinate of (50,50) within last. In this case, (0,0) is (last.x, last.y).

Related

Moving Object to another Objects position

Hey everyone so I am having some trouble trying to get this to work correctly. I have a MC Object called character and another called "points". I have a container object called planetContainer I add the character to the planetContainer the character is rotating around the planets that are also added to the container. The main issue I am having is when the points power up is activated I want the points to move off the other planets and to the charactercenter position. It was working perfect but had to update some code and remove the Points out of the planetContainer and attach them to the planets instead. I know I might have to use localToGlobal but not too sure.
Here is how I setup the character:
private function newCounterClockWise():void
{
planetContainer.addChild(character);
character.rotation = (Math.atan2(character.y - planetHit.y, character.x - planetHit.x) * 180 / Math.PI);
}
How the points are added to the Planets:
private function addPoints():void
{
points = new mcPoints();
var planetPosition:Point = planetContainer.parent.localToGlobal(new Point(0, 0));
points.x = planetPosition.x;
points.y = planetPosition.y;
outerPlanets.addChild(points);
aPointsArray.push(points);
}
Now this is the main function that handles the points to move to the character but it is not working correctly. The points move but they move off the screen or cause the game to kinda tweak out and do different things. Also the "magnetHandler(); is in my EnterFRame Event:
private function magnetHandler():void
{
for (var i:int = 0; i < aPointsArray.length; i++)
{
var currentPoints:mcPoints = aPointsArray[i];
var characterPosition:Point = planetContainer.parent.globalToLocal(new Point(character.x, character.y));
if (currentPoints.hitTestObject(playScreen.mcPointsHit))
{
trace("POINTS MID STAGE");
currentPoints.x -= (currentPoints.x - characterPosition.x);
currentPoints.y -= (currentPoints.y - characterPosition.y);
//currentPoints.x = character.x;
//currentPoints.y = character.y;
//TweenMax.to(currentPoints, 0.5, {x:characterGlobalPosition.x, y:characterGlobalPosition.y , ease:Power1.easeInOut } );
}
}
}
Can anyone see what I am doing wrong?
It's a hard to understand your question fully (or to understand why you're putting things that relate to each other in separate containers), but likely this line is where it's falling down:
var characterPosition:Point = planetContainer.parent.globalToLocal(new Point(character.x, character.y));
What you want to do, is get the characters x/y coordinates in the currentPoints parent space. To do that, you would do something like this:
//first, find the global position of character:
var globalCharacterPoint:Point = character.localToGlobal(new Point());
//then, convert that to the currentPoints parent local space:
var localCharacterPoint:Point = currentPoints.parent.globalToLocal(globalCharacterPoint);
Also, in this code of yours:
points = new mcPoints();
var planetPosition:Point = planetContainer.parent.localToGlobal(new Point(0, 0));
points.x = planetPosition.x;
points.y = planetPosition.y;
You are getting the global space of the planetContainer's parent, which is probably NOT what you want. You likely want:
planetContainer.localToGlobal(new Point()); //this gives you the global location of the planet container's top left corner
And, since you're adding the points object to outerPlanets, you probably want to convert to its local space (unless it's positioned at 0,0 globally - then it doesn't especially matter).
var outerPoint:Point = outerPlanets.globalToLocal(planetPosition);
points.x = outerPoint.x;
points.y = outerPoint.y;
Needless to say, for games it's best to have everything in the global coordinate space unless it's truly encapsulated assets (like smoke on a rocket etc.)

How to get position of nested object after the rotation of parent object?

I have a big MovieClip object composed of many small MovieClip objects. After the rotation of parent object, the coordinates of nested objects are changed. But the x and y properties of nested objects remains the same it was before rotation of parent. How to get updated coordinates?
For example, there is an object composed of two circles. Local coordinates of small circle is -34 0. After rotation the coordinates of the small circle is still -34 0, but global coordinates was obviously changed. Is there a way to obtain new global coordinates?
Yes, it's possible. The most straightforward way is to use localToGLobal function
In you example as3 can looks like:
//converts local (0, 0) of innerMc to global (stage) coordinates system
var point:Point = innerMc.localToGlobal(new Point(0, 0));
//another way (for better understanding) - converts innerMc
//coordinates in outerMc system (in your case it's (-34, 0)
var point2:Point = outerMc.localToGlobal(new Point(innerMc.x, innerMc.y));
Both options will give you the same result.

AS3 Clicking in "zones" of a picture

I have searched and simply cannot find what I need (if it exists).
A window will have a large picture.
The picture will be divided into zones (such as border lines that separate states on a map).
When a person clicks within a zone, then I will raise the appropriate event.
I've used AS3 with MXML to create a database program. All is working great except for this last step. I cannot figure out how the user is within a particular area of the picture when he clicks or when he touches.
I've read and tried to come up with an approach, and there must be (hopefully so) an easier way than the muddled nonsense I'm coming up with.
Thanks
VL
Are you drawing it in flash professional CS6? If so then why can't you just have the picture as a symbol and then just self divide the lines and make those divided areas into symbols that are children of the picture symbol. You could keep the individual state symbols right where they so that they stay true to the overall picture.
A first thought would be to make an instance of this picture symbol through the code, and then loop though all the children of that picture and add a click event to each one.
var picture:Picture=new Picture();
for(var i:int=0; i<picture.numChildren-1; i++){
picture.getChildAt(i).addEventListener(MouseEvent.CLICK, mouseEventHandler);
}
Please comment if I am missing something, or this does not work.
EDIT
Well, if you know the dimensions of the image, you could divide its width and height by 3(your number of rows and columns) and this is your zone dimensions. You then could take the mouse's click point relative to the top left of your picture and then divide its width by the zone with, and its height by the zone height, and then get its integer floor value, you could get which region it is. Code it below:
//This is all for a constat region list (like a window, or floor tiles, not things irregular)
import flash.display.Sprite;
var regionsX:int = 3; //Your number of windows across the row
var regionsY:int = 3; // across the column
var regions:Array = new Array(); // an array to hold the values that you will get from where the user clicks
// All of this used a 2D array method
for(var x:int = 0; x < regionX; x++) {
regions[regionsX] = new Array();
for(var y:int = 0; y < regionY; y++) {
regions[regionsX][regionsY] = "region(".concat(x).concat(",").concat(y);
// Here you make this equal to anything you want to get a value of,
//once the correct region is found (I just have a string version here for an example)
}
}
... // other stuff..
var picture:Picture = new Picture(); // your window picture
var regionWidth:Number = picture.width / regionsX; // Gets each region's width
var regionHeight:Number = picture.height / regionsY; // Get each regoin's height
...
picture.addEventListener(MouseEvent.CLICK, mouseEventListener); // add a click listener to the picture
function mouseEventListener(event:MouseEvent):void{
var mouseX:Number = picture.globalToLocal(event.stageX); // gets where the user clicked, and then converts it
//to the picture's cordinate space. ( 50,100 acording to the stage, could be (25,200) to the picture)
var mouseY:Number = picture.globalToLocal(event.stageY); // same for the Y
var regionIntX:Number = Math.floor(mouseX / regionWidth); // Dives the point by each region's width, and then
// converts it to a while integer. (For instance, if a region's width is 100 and you click at 288, then if you do the
// math, you clicked in the 3rd region, but it returns 2... why? (becaue the array counter starts at 0, so 0 is the 1st
// region, 1 is the second and so on...
var regionIntY:Number = Math.floor(mouseY / regionHeight); // Same for Y
var yourValue:String = regions[regionIntX][regionIntY]; // This returns that you initialy put into your 2d array
// by using the regionIntX and regionIntY for the array values. You have to decide what is stored in this array...
}
The simplest solution would be to add an event listener for MouseEvent.CLICK to the picture and in the handler check properties mouseX and mouseY of the picture. Define the bounds of each area in an XML or similar and check against current mouseX/Y to see which area has been clicked.

issue with addChild's X and Y value

I have an Enemy class that deals with my monster moving and attacking. Within that class, I have another class called enemyMagic, which is a blank movieclip that serves as a masterclass to different movieclips that I will make.
So in the enemyMagic class, I add a movieclip called attack1
public var attack1:Attack1 = new Attack1;
public function EnemyMagic() {
////////////CREATE THE TIMER//////////
masterEnemyAttackTimer.addEventListener(TimerEvent.TIMER, mastertimer);
////////////ATTACKS/////////
//TIER 1//
addChild(attack1);
}
And in the enemy class, I add the enemyMagic when the enemy is attacking a certain position.
for (var k:int = 0; k < Main.tileset.length; k++)
{
if (! Main.tileset[k].tileMiddle.hitTestObject(this.enemyVisionPoint))
{
if (Main.tileset[k].tileHP !== 0)
{
attackoptions.push(Main.tileset[k]);
}
if (Main.tileset[k].tileMiddle.hitTestObject(Main.player.visionPoint))
{
addChild(enemymagic);
Main.tileset[k].outline.gotoAndStop("attack");
this.enemymagic.x = (Main.tileset[k].x);
this.enemymagic.y = (Main.tileset[k].y);
trace(enemymagic.x, enemymagic.y, Main.tileset[k].x, Main.tileset[k].y);
For some reason, the enemymagic is tracing the exact same number as the tile's x and y, but it isn't adding it on the tile. It adds it way off the screen. I think it might be because it starts on the enemy's x and y and then calculates?
So my question is how can I get the enemymagic movie clip to get exactly on the position of the tile?
You can do two things. First, when you do a plain addChild() the base coordinate system of the child is the one of its parent, which is your Enemy instance, which is of course at somewhere nonzero. And then you assign it the coordinates of Main.tileset[k] which has a different parent (most likely instance of Main). This creates the distance you speak of. So, in order to locate your magic over the exact tile, either use this.globalToLocal(Main.tileset[k].localToGlobal(PZERO)) where PZERO is a new Point() constant (or write new Point() instead of PZERO, but this will create another empty Point object and will quickly escalate), or do an addChild() directly to the tile you are attacking with unaltered coordinates.

make visual clone of displayObject that's nested within other displayObjects, and add the clone to the stage layer in the same location, rotation, etc

I want to be able to grab a copy of a DisplayObject that is nested within other transformed DisplayObjects (rotated, scaled, stretched objects), and be able to stamp it back into the same visual location, but on the stage layer. Essentially, being able to make a clone of a nested DisplayObject, but be able to add the clone to the stage layer, yet have it perfectly align (visually) with the original (same position, scale, rotation)
I have been working with something along the lines of:
// draw the pixels of a displayobject into a new bitmap object
var bitmapData:BitmapData = new BitmapData(nestedSprite.width, nestedSprite.height, true, 0xFFFFFF);
var bitmap:Bitmap = new Bitmap(bitmapData);
bitmapData.draw(nestedSprite);
// put the copy on the top most layer
stage.addChild(bitmap);
// position the copy to perfectly overlay the original, but on the top stage layer
var point:Point = nestedSprite.localToGlobal(new Point(0, 0));
bitmap.x = point.x;
bitmap.y = point.y;
But this only works well for displayObjects whose parents are not transformed; and for displayObjetcs that are perectly at the (0,0) origin. It falls apart for centered aligned objects or scaled parents, etc.
I am aware that I can add a matrix param to the .draw() method, as well as a clipping rectngle, and scale my bitmap afterwards, or setting the transform of one object to another, or use .transform.concatenatedMatrix, or use nestedObject.getBounds(null), or nestedSprite.getBounds(nestedSprite), etc. But I have unfortunately fallen into doing trial and error programming on this one, and with some many variables, this is never a good way to solve a programming problem.
I believe this function should work, the only extra step was offsetting the concatenated matrix so that the target would draw with its top left at (0, 0) on the Bitmap even if its origin was somewhere else. Hopefully the rest is self explanatory, but I can add more comments if anything doesn't make sense.
function createBitmapClone(target:DisplayObject):Bitmap {
var targetTransform:Matrix = target.transform.concatenatedMatrix;
var targetGlobalBounds:Rectangle = target.getBounds(target.stage);
var targetGlobalPos:Point = target.localToGlobal(new Point());
// Calculate difference between target origin and top left.
var targetOriginOffset:Point = new Point(targetGlobalPos.x - targetGlobalBounds.left, targetGlobalPos.y - targetGlobalBounds.top);
// Move transform matrix so that top left of target will be at (0, 0).
targetTransform.tx = targetOriginOffset.x;
targetTransform.ty = targetOriginOffset.y;
var cloneData:BitmapData = new BitmapData(targetGlobalBounds.width, targetGlobalBounds.height, true, 0x00000000);
cloneData.draw(target, targetTransform);
var clone:Bitmap = new Bitmap(cloneData);
// Move clone to target's global position, minus the origin offset.
clone.x = targetGlobalPos.x - targetOriginOffset.x;
clone.y = targetGlobalPos.y - targetOriginOffset.y;
return clone;
}
Unfortunately, pixelBounds seems to return an origin of (0, 0) if there are any filters on the DisplayObjects, which obviously breaks things.
Edit: Replaced target.transform.pixelBounds with target.getBounds(target.stage) as a slight improvement. This keeps the position correct if there are filters, but filters on parent DisplayObjects still won't be included, and filters on the target can overlap the edges of the Bitmap. I'm not sure if there's a simple way to work around that.
Update: Jimmi Heiserman spotted that this function is broken if the swf is scaled. Without stage.scaleMode = StageScaleMode.NO_SCALE; though, the stageWidth and stageHeight parameters seem to stay unchanged, so the only (rather hacky) workaround I've found is to add an "unscaled" test Sprite and use its concatenatedMatrix to adjust the clone's position and scale:
function createScaledBitmapClone(target:DisplayObject):Bitmap {
var targetTransform:Matrix = target.transform.concatenatedMatrix;
var targetGlobalBounds:Rectangle = target.getBounds(target.stage);
var targetGlobalPos:Point = target.localToGlobal(new Point());
// Calculate difference between target origin and top left.
var targetOriginOffset:Point = new Point(targetGlobalPos.x - targetGlobalBounds.left, targetGlobalPos.y - targetGlobalBounds.top);
// Create a test Sprite to check if the stage is scaled.
var testSprite:Sprite = new Sprite();
target.stage.addChild(testSprite);
var testMatrix:Matrix = testSprite.transform.concatenatedMatrix;
target.stage.removeChild(testSprite);
// Move transform matrix so that top left of target will be at (0, 0).
targetTransform.tx = targetOriginOffset.x * testMatrix.a;
targetTransform.ty = targetOriginOffset.y * testMatrix.d;
var cloneData:BitmapData = new BitmapData(targetGlobalBounds.width * testMatrix.a, targetGlobalBounds.height * testMatrix.d, true, 0x00000000);
cloneData.draw(target, targetTransform);
var clone:Bitmap = new Bitmap(cloneData);
// Move clone to target's global position, minus the origin offset, and cancel out stage scaling.
clone.x = targetGlobalPos.x - targetOriginOffset.x;
clone.y = targetGlobalPos.y - targetOriginOffset.y;
clone.scaleX = 1 / testMatrix.a;
clone.scaleY = 1 / testMatrix.d;
return clone;
}
Have you tried passing the parents transform into draw? draw takes a transform matrix as the second param.
If you have a handle on the parent you can use something like this
bitmapData.draw(nestedSprite, parent.transform.matrix);