I've been creating a magnifier class using a standard displacementMapfilter similar to Adobe's and numerous other examples: http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/WS5b3ccc516d4fbf351e63e3d118a9b90204-7da2.html
The filter is being used at the application level so it filters everything, and there's a large number of image elements that can be dragged, rotated, and scaled around the screen, and when passed below this filter it magnifies them. I also intend to add some more functionality to allow dragging the filter around.
The filter works as intended, magnifying everything that passes by it. The weird thing is whenever an image is being dragged outside of the top or left edge of the screen, the filter floats in that direction the distance it takes before the image is entirely offscreen (ie: the filter floats 500 pixels left if a picture 500 pixels wide is pulled offscreen to the left.)
I'm using an enterFrame listener to constantly update its position, and it works as follows:
private function onEnterFrame(e:Event):void {
dPoint.x = stage.stageWidth - radius;
dPoint.y = stage.stageHeight - radius;
dFilter = new DisplacementMapFilter(map, dPoint, BitmapDataChannel.RED, BitmapDataChannel.BLUE, 100, 100, DisplacementMapFilterMode.IGNORE, 0x000000, 0);
// The application is a displayObject itself, so just apply filters to "this"
this.filters = [dFilter];
}
So this code should anchor the filter at the bottom right of the screen, yet somehow whenever an image is dragged off, the filter drifts with it. Is there any reason a filter would do that, and any way I could stop it?
Dragging an image off the stage changes the size and shape of the rectangle the filter is being applied to (picture the filter as if it's taking a snapshot of everything on the stage). When the image moves off the top left, it means that (0, 0) on the filter is actually at the top left corner of the image.
If you check the bounds of the stage (in the stage's own coordinate space), you should see top and left become negative numbers when you drag an image off:
stage.getBounds(stage).top;
stage.getBounds(stage).left;
Cancelling out any negative bounds should keep your filter in the correct position:
var stageBounds:Rectangle = stage.getBounds(stage);
if (stageBounds.left < 0) {
dPoint.x -= stageBounds.left;
}
if (stageBounds.top < 0) {
dPoint.y -= stageBounds.top;
}
Related
Here's the scenario.
A user selects an image, video, or other type of display object. It gets loaded inside a custom class known as EditableObject. The goal of EditableObject is re-sizing and re-positioning. EditableObject has 4 movieclips.
center_mc : Handles drag and drop and contains the display object.
rightside_mc : Handles re-sizing of width on mousedown
bottomside_mc : Handles re-sizing of height on mousedown
bottomright_mc : Handles scaling of object on mousedown
So far I can scale all parts of these objects. The bugs are what's causing trouble.
When I click down on the bottom or right side of the display object, the display object increases size from whichever side it's handling before I move the mouse. I get the amount to move based on the difference of the stageX and stageY positions of the mouse passed on MOUSE_DOWN and at the end of every MOUSE_MOVE. This isn't a problem, it just looks bad and currently solves the second bug..
When I handle bottomright_mc, I can only scale the object down. When trying to scale out, I have to position the mouse outside of EditableObject to get a difference greater than bottomright_mc's current position. This doesn't work because any attempts to exit the bounds of EditableObject trigger MOUSE_UP, even if I add the event listener using this.parent..
I want to be able to re-position and scale anything inside EditableObject using two sides and one corner of EditableObject when a mouse is held down and moved. Currently I can only move the object, scale the object less than its current size, and use the two sides to adjust the objects width and height but without having the mouse match the position of the side it's currently handling on initialization.
Here's an excerpt for the MOUSE_MOVE handler.
private function handleMouseMove(e:MouseEvent):void
{
switch(dragType)
{
case "right":
trace("Right triggered.");
this.width = e.stageX - this.x;
break;
case "bottom":
trace("Bottom triggered");
this.height = e.stageY - this.y;
break;
case "bottomright":
resizeMe(this, e.stageX - this.x, this.height);
break;
}
prevMouseX = e.stageX;
prevMouseY = e.stageY;
}
The full code for the class can be found here on pastebin.
Here's Editable Object in Adobe Flash CC
Here's EditableObject currently containing and handling an image inside the editor.
Based on this answer on stackoverflow provided by TOMATO in the comments, I found a great API that easily lets you handle DisplayObjects with just a few lines of code.
The one I have chosen is senocular's transform tool. A demonstration of his work can be found here. Broken down, this is how to use it.
//Constructor
var editTool:TransformTool = new TransformTool();
//Add to stage (This will not be visible until you give your TransformTool a target)
addChild(editTool);
desiredDisplayObject.addEventListener(MouseEvent.CLICK, handleMouseClick)
function handleMouseClick(e:MouseEvent):void
{
//Set the object clicked as the target for the TransformTool
editTool.target = e.target as Sprite;
//Swap the positioning in the display list so the TransformTool appears in front
editTool.parent.setChildIndex(editTool, editTool.parent.numChildren - 1);
}
You can do this a number of ways, but the basics of it is to simply add the transform tool to the stage and assign whatever display object you would like to the TransformTool.
Using the first photo below, let's say:
The red outline is the stage bounds
The gray box is a Sprite on the stage.
The green box is a child of the gray box and has a rotation set.
both display object are anchored at the top-left corner (0,0).
I'd like to rotate, scale, and position the gray box, so the green box fills the stage bounds (the green box and stage have the same aspect ratio).
I can negate the rotation easily enough
parent.rotation = -child.rotation
But the scale and position are proving tricky (because of the rotation). I could use some assistance with the Math involved to calculate the scale and position.
This is what I had tried but didn't produce the results I expected:
gray.scaleX = stage.stageWidth / green.width;
gray.scaleY = gray.scaleX;
gray.x = -green.x;
gray.y = -green.y;
gray.rotation = -green.rotation;
I'm not terribly experienced with Transformation matrices but assume I will need to go that route.
Here is an .fla sample what I'm working with:
SampleFile
You can use this answer: https://stackoverflow.com/a/15789937/1627055 to get some basics. First, you are in need to rotate around the top left corner of the green rectangle, so you use green.x and green.y as center point coordinates. But in between you also need to scale the gray rectangle so that the green rectangle's dimensions get equal to stage. With uniform scaling you don't have to worry about distortion, because if a gray rectangle is scaled uniformly, then a green rectangle will remain a rectangle. If the green rectangle's aspect ratio will be different than what you want it to be, you'd better scale the green rectangle prior to performing this trick. So, you need to first transpose the matrix to offset the center point, then you need to add rotation and scale, then you need to transpose it away. Try this set of code:
var green:Sprite; // your green rect. The code is executed within gray rect
var gr:Number=green.rotation*Math.PI/180; // radians
var gs:Number=stage.stageWidth/green.width; // get scale ratio
var alreadyTurned:Boolean; // if we have already applied the rotation+scale
function turn():void {
if (alreadyTurned) return;
var mat:flash.geom.Matrix=this.transform.matrix;
mat.scale(gs,gs);
mat.translate(-gs*green.x,-gs*green.y);
mat.rotate(-1*gr);
this.transform.matrix=mat;
alreadyTurned=true;
}
Sorry, didn't have time to test, so errors might exist. If yes, try swapping scale, translate and rotate, you pretty much need this set of operations to make it work.
For posterity, here is what I ended up using. I create a sprite/movieClip inside the child (green) box and gave it an instance name of "innerObj" (making it the actually content).
var tmpRectangle:Rectangle = new Rectangle(greenChild.x, greenChild.y, greenChild.innerObj.width * greenChild.scaleX, greenChild.innerObj.height * greenChild.scaleY);
//temporary reset
grayParent.transform.matrix = new Matrix();
var gs:Number=stage.stageHeight/(tmpRectangle.height); // get scale ratio
var mat:Matrix=grayParent.transform.matrix;
mat.scale(gs,gs);
mat.translate(-gs * tmpRectangle.x, -gs * tmpRectangle.y);
mat.rotate( -greenChild.rotation * Math.PI / 180);
grayParent.transform.matrix = mat;
If the registration point of the green box is at one of it's corners (let's say top left), and in order to be displayed this way it has a rotation increased, then the solution is very simple: apply this rotation with negative sign to the parent (if it's 56, add -56 to parent's). This way the child will be with rotation 0 and parent -> -56;
But if there is no rotation applied to the green box, there is almost no solution to your problem, because of wrong registration point. There is no true way to actually determine if the box is somehow rotated or not. And this is why - imagine you have rotated the green box at 90 degrees, but changed it's registration point and thus it has no property for rotation. How could the script understand that this is not it's normal position, but it's flipped? Even if you get the bounds, you will see that it's a regular rectangle, but nobody know which side is it's regular positioned one.
So the short answer is - make the registration point properly, and use rotation in order to display it like in the first image. Then add negative rotation to the parent, and its all good :)
Edit:
I'm uploading an image so I can explain my idea better:
As you can see, I've created a green object inside the grey one, and the graphics INSIDE are rotated. The green object itself, has rotation of 0, and origin point - top left.
#Vesper - I don't think that the matrix will fix anything in this situation (remember that the green object has rotation of 0).
Otherwise I agree, that the matrix will do a pretty job, but there are many ways to do it :)
I am writing now a flash game and I run into a an issue. I have a map for the game which is defined as a 2-D array, where each element represents a component of the map. The player is always in the center of the map.
The problem is when the player reaches one end of the map. Now it is empty space. I want that the player instead of seeing the empty space, to see another end of the map and in this way, the map will loo like it goes around.
So for example if the player goes to right he will eventually start seeing the the left side of the map and the world will look continuous.
Does anyone knows how to implement this functionality?
You could make the array 2 times and put the first one behind the second one again and than the second one behind the first etc etc..
It's done here with 2 pictures, just use the arrays instead:
//The speed of the scroll movement.
var scrollSpeed:uint = 2;
//This adds two instances of the movie clip onto the stage.
var s1:ScrollBg = new ScrollBg();
var s2:ScrollBg = new ScrollBg();
addChild(s1);
addChild(s2);
//This positions the second movieclip next to the first one.
s1.x = 0;
s2.x = s1.width;
//Adds an event listener to the stage.
stage.addEventListener(Event.ENTER_FRAME, moveScroll);
//This function moves both the images to left. If the first and second
//images goes pass the left stage boundary then it gets moved to
//the other side of the stage.
function moveScroll(e:Event):void{
s1.x -= scrollSpeed;
s2.x -= scrollSpeed;
if(s1.x < -s1.width){
s1.x = s1.width;
}else if(s2.x < -s2.width){
s2.x = s2.width;
}
}
You simply check if your player is about to get off the "right" or "left" edge of the map, and position him at the other edge. To draw a circular map, you can use the following technique: if you are about to draw a column of a number that exceeds the map's width, decrease that number by width and draw the column at resultant index; and if you are about to draw a column at index below zero, add width and draw the column at resultant index. If you are in troubles of making a hitcheck at continuous map's borders, you can employ the same trick to find neighbors. (The "circular array" is a pretty basic algorithmic problem, and is resolved in many ways already)
You have a few options here. You can do the pac-man style of just making your character pop up on the other side of the screen, but that would require you to abandon the cool bit of the character being in the middle at all times.
On to the real suggestions:
If you're not implementing your array as one solid object (i.e. making it draw individual collumns/rows at a time) then this is a no-brainer. Just have a function that returns the index of the next collumn/row, within certain bounds. Like, if your array is 40 elements wide, when it tries to draw element 41, subtract the size of the array, and make it draw element 1 instead.
If your array is one solid object (like if you drew it onto a stage object and are just manipulating that) and it's not very big, you could probably get away with drawing a total of four of them, and just having a new one cover up any whitespace that's about to appear. Like, as you approach the right edge of the first array, the second array moves to the right of it for a lawless transition.
If your array is a solid object and is very big, perhaps you could make eight buffer objects (one per edge and one per corner) that hold approximately half a screen's worth of the array. That way as you approach the right edge, you see the left edge, but then when you cross into the buffer zone, you could teleport the player to the corresponding position on the left of the array, which has the buffer for the right size. To the player, nothing has changed, but now they're on the other side of the world.
I'm using Flash Builder to build an flash-web application, and whenever I try to add objects to the stage, they appear near the center even if their coordinates are set at 0,0. I've tried track the mouse movement to see what the coordinates of the upper left corner are, and they are always a negative number.
Strangely, the stage coordinates and position of the children change based on how large the flash object is. For example, if the flash object is 500 pixels wide, the top left corner of the stage is at about -130 and if the flash object is 800 pixels wide, its at about -200. Why is the coordinate system wrong? Do I have to alter an anchor point or something like that?
Here is a sample of an object I'm trying to position at 0,0:
var square:Sprite = new Sprite();
addChild(square);
square.graphics.lineStyle(3,0x00ff00);
square.graphics.beginFill(0x0000FF);
square.graphics.drawRect(0,0,100,100);
square.graphics.endFill();
square.x = 0;
square.y = 0;
This shows up near the center and I can't figure out why.
Is the object you are using a MovieClip or Sprite? If so then your registration point could be in an incorrect position.
Usually registration points are placed right in the middle of the object instead of lets say the top left as you would want it. So the co-ordinates would be different to the stage.
I fixed this by adding
stage.align = StageAlign.TOP_LEFT;
to the main function.
I cannot figure out how to do this. I was translating the character and background at the same time, but if there's any hiccup, the character position slides out of the viewable area of the canvas, and I need the canvas translation to be based off the position of the player (hero.x, hero.y).
Currently I have
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = 640;
canvas.height = 480;
//then in my update function
if (38 in keysDown && hero.y > 0){ //UP KEY PRESSED KEY
ctx.translate(0,12); //translate background position +12y
hero.y -= hero.speed * modifier; //move player up on the background image
else if (40 in keysDown && hero.y < 3750-64){ //DOWN KEY PRESSED
ctx.translate(0, -12);
hero.y += hero.speed * modifier;
}
}
That moves the player and the canvas but not guaranteed together...if it freezes at all, the player is off center or even off screen.
The viewable canvas area is 640x480, but the background image you can navigate on is 5,000 x 3750.
On the web browser, when it doesn't freeze, it works how I want, moving the player and background at the same pace as the character.
However, that same rate on the phone puts the player much faster than the screen translates which means the player walks right out of the viewable area even though it still moves the background.
If I do ctx.translate(hero.x, hero.y) and use the hero.x, hero.y coordinates of the player, or some variation of it minus an offset, it moves the background BY that measurement each time I press the key instead of moving it TO that position.
How can I make everything conditional on the players position to move both the player and background, but together, or automatically adjust next update() to center on the player....?????
How can I make everything conditional on the players position to move both the player and background, but together, or automatically adjust next update() to center on the player
Well, the easy way would be to actually always draw the player in the center! In other words, never ever change or translate his or her coordinates. Instead worrying about translating everything else in relation to that.
Since you want the player to always be in the center, you should always draw the player at the center of the canvas (640/2 x 480/2 for you)
Then you'll want to do is keep a canvas offset for X and Y and draw everything (Background, etc) using that offset, then reset the transformation and draw the hero in the plain old center.
So your draw function might look something like this:
function draw() {
ctx.clearRect(0,0,500,500);
// Save the default transformation matrix
ctx.save();
// Translate by the background's offset
ctx.translate(xoffset, yoffset);
// Draw the background (and maybe other things) translated
ctx.fillStyle = backgroundGradient;
ctx.fillRect(0,0,500,500);
// We restore to the default transformation
// This will draw the hero not translated at all
// That means we can always draw the hero in the center!
ctx.restore();
// hero is a black rectangle
ctx.fillRect(240, 240, 20, 20);
}
Here is a live example that works with mouse down and up. There is a big "sun" for the background that moves, while the "stays" rectangle stays still because it is literally always drawn in the same place:
http://jsfiddle.net/3CfFE/