Flash: get absolute position of e.currentTarget - actionscript-3

Okay, wasn't sure about the title.
But here's what I am trying to achieve:
Basically i am trying to do something like a quiz, where you can drag and drop the answers into a field. And if they are correkt it should snap the answer field position.
It should be something like
if(myobject.hitTestObject(targetField) && isCorrectAnswer()) {
myobject.x = targetField.x;
myobject.y = targetField.y;
}
But it's not really working.
So here is what I have:
/**
* Generating dragable answer fields based on an array.
**/
function generateAnswer():void {
// creating text format
var myFormat:TextFormat = new TextFormat();
myFormat.color = 0x0066FF;
myFormat.size = 24;
myFormat.align = TextFormatAlign.CENTER
// reference array to store all textfields
var referenceArray:Array = new Array();
// iterate through all answers in vocabListItems and generate textfields
var i:int;
for (i = 0; i < vocabListItems.length; i++) {
var answerField:TextField = new TextField();
// Setting text to current answer
answerField.text = vocabListItems[i];
answerField.width = 140;
answerField.height = 40;
answerField.x = 60+ i*150;
answerField.y = 410;
answerField.background = true;
answerField.backgroundColor = 0xffffff;
answerField.setTextFormat(myFormat);
answerField.selectable = false;
answerField.type = TextFieldType.DYNAMIC
// store the textfield in a container so drag and drop
// will work
var textContainer:Sprite = new Sprite();
textContainer.addChild(answerField);
addChild(textContainer);
referenceArray.push(textContainer);
}
for each (var item in referenceArray) {
item.addEventListener(MouseEvent.MOUSE_DOWN, startDragging);
item.addEventListener(MouseEvent.MOUSE_UP, stopDragging);
}
}
And then I start my drag
var start_x:Number;
var start_y:Number;
function startDragging(e: MouseEvent):void {
var object = e.currentTarget;
start_x = e.currentTarget.x;
start_y = e.currentTarget.y;
object.startDrag();
}
And my stop dragging
function stopDragging(e:MouseEvent):void {
e.currentTarget.stopDrag();
if (e.currentTarget.hitTestObject(targetField)) {
e.currentTarget.x = targetField.x;
e.currentTarget.y = targetField.y;
} else {
e.currentTarget.x = start_x;
e.currentTarget.y = start_y;
}
}
So the problem however is, that e.currentTarget.x is starting from 0. And not from the absolute position on the screen. Its always a relative value, so if i drag it to the targetField its x and y is something like -100, -40
If I set it to the targetField x and y it disappears somewhere in the nirvana of the screen.
targetField is in this case just a rectangle drawn on the stage with a x and y of 160
How can I position it to the absolute x and y?
Here's a screenshot
So the top field is the targetField which is only a rectangle with x 161 and y 191.
The field on the bottom are the dragable fields which are the e.currentTarget. But currentTarget.x is always 0.

EDIT
Your e.currentTarget is going to be the textContainer, which you haven't set an x/y on so it will naturally be 0.
It would seem to make more sense to move the container, and not the actual text field when you create it, like so:
for (i = 0; i < vocabListItems.length; i++) {
var answerField:TextField = new TextField();
// Setting text to current answer
answerField.text = vocabListItems[i];
answerField.width = 140;
answerField.height = 40;
//answerField.x = 60+ i*150; //don't move the text field, move the container later
//answerField.y = 410;
answerField.background = true;
answerField.backgroundColor = 0xffffff;
answerField.setTextFormat(myFormat);
answerField.selectable = false;
answerField.type = TextFieldType.DYNAMIC
// store the textfield in a container so drag and drop
// will work
var textContainer:Sprite = new Sprite();
textContainer.x = 60+ i*150;
textContainer.y = 410;
textContainer.addChild(answerField);
addChild(textContainer);
referenceArray.push(textContainer);
//Also, as an aside, there is no reason to loop through the array after this, just add the listeners here
textContainer.addEventListener(MouseEvent.MOUSE_DOWN, startDragging);
textContainer.addEventListener(MouseEvent.MOUSE_UP, stopDragging);
}
End Edit
To translate the coordinates from one object to another, you can use the localToGlobal and globalToLocal methods of a display object.
Something like this:
var globalPoint:Point = targetField.localToGlobal(new Point());
var destinationLocalPoint:Point = e.currentTarget.parent.globalToLocal(globalPoint);
e.currentTarget.x = destinationLocalPoint.x;
e.currentTarget.y = destinationLocalPoint.y;
What I'm doing here, is first, getting the global coordinates of the targetField. So it's taking a point (at 0,0) relative to targetField and translating that relative to the stage.
Then I'm making a new Point object that takes that global coordnate set, and translate that to the parent of e.currentTarget.

Related

How to get index of a Bitmap Image from bitmapdata(from an Array)?

I am wondering if i have an Array that push content that is Bitmap, how do i get index of a specific image when clicked. I tried to use indexOf but no luck, my codes are below.
Thanks for your time!
Code:
//First Part is where i add the URLRequest and add the image into contentHolder then onto Stage
function loadImage():void {
for(var i:int = 5; i < somedata.length; i++){
if(somedata[i]){
var loader:Loader = new Loader();
loader.load(new URLRequest("http://www.rentaid.info/rent/"+somedata[i]));
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onImageLoaded);
}
}
}
function onImageLoaded(e:Event):void {
loadedArray.push(e.target.content as Bitmap);
for(var i:int = 0; i < loadedArray.length; i++){
var currentY1:int = 200;
e.currentTarget.loader.content.height =200;
e.currentTarget.loader.content.y += currentY1;
currentY1 += e.currentTarget.loader.content.height +300;
_contentHolder.mouseChildren = false; // ignore children mouseEvents
_contentHolder.mouseEnabled = true; // enable mouse on the object - normally set to true by default
_contentHolder.useHandCursor = true; // add hand cursor on mouse over
_contentHolder.buttonMode = true;
_contentHolder.addChild(loadedArray[i]);
addChild(_contentHolder);
_contentHolder.addEventListener(MouseEvent.CLICK, gotoscene);
}
}
// then the part where i try to get the index
function gotoscene(e:MouseEvent):void {
var index:Number;
index = loadedArray.indexOf(e.target);
trace(index);
}
Edit:
var viewport:Viewport = new Viewport();
viewport.y = 0;
viewport.addChild(_contentHolder);
Your first question has very simple answer:
var image:Bitmap = new Bitmap();
var images:Array = new Array(image);
for (var i:uint = 0; i < images.length; i++) {
// images[i].bitmapData is the original image in your array
// image.bitmapData is searched one
if (images[i].bitmapData == image.bitmapData) {
// found
}
}
But your problem is bigger than this. I see you keep wandering around..
You should add listener to each child, not the content holder as one. I usually don't use Loaders, but get their Bitmaps and wrap them in Sprites or something, that I add into the scene. You should store either this Sprite or your Loader into that array, not the Bitmap. Then add listener to each of them (Sprite or Loader, not Bitmap) and get the target. Depending on what you've stored in the array, you can easily get it as:
function gotoscene(e:MouseEvent):void {
var index:uint = loadedArray(indexOf(e.target));
}
But it's important to store one specific type that will actually be clickable. Don't think about the Bitmap - it's only a graphic representation, and doesn't do much in the code.
**EDIT:
Okay I'm adding the code you need but it's important to understand what you are doing and not just rely on someone else's answer :)
function onImageLoaded(e:Event):void {
var bitmap:Bitmap = e.target.content as Bitmap; // get the Bitmap
var image:Sprite = new Sprite();
image.addChild(bitmap); // wrap it inside new Sprite
// add listener to Sprite!
image.addEventListener(MouseEvent.CLICK, gotoscene);
// gets url of current image (http://website.com/images/image1.jpg)
var url:String = e.target.loaderURL;
// get only the number from that url by replacing or by some other way
// this removes the first part and results in "1.jpg"
var name:String = url.replace("http://website.com/images/image", "");
// this removes the extension and results in number only - 1, 2, 3
// it's important to change this depending on your naming convention
name = name.replace(".jpg", "");
image.name = "button" + name; // results in "button1", "button2", "button3"
// store object, name, or whatever (not really needed in your case, but commonly used)
loadedArray.push(image.name);
image.x = counter * 100; // position so you can see them, at 100, 200, 300, etc.
_contentHolder.addChild(image); // add newly created Sprite to content
}
function gotoscene(e:MouseEvent):void {
var name:String = e.target.name;
// strips down "button" from "button1", and only the number remains,
// which is 1, 2, 3, etc. the number of the scene :)
var scene:uint = name.replace("button", "");
// you're the man now :)
}

Multiple sprites added in loop, but only last sprite clickable

I have multiple bitmap images added into sprites(each image added into 1 sprite) in a loop, then all the sprites added to 1 _contentHolder(Sprite) then that is added to a viewport.
What the problem is, the multiple sprites that are added inside the loop, everything displays with no problem but only the last sprite added is clickable. None of the sprite added before it is clickable. Wondering what the problem is, they are not overlapping and when i hover the mouse over the top of all the sprites, it turns into the mouse clicker but it just won't click.
Thanks for your time!
My code:
function onImageLoaded(e:Event):void {
loadedArray.push(e.target.content as Bitmap);
for(var i:int = 0; i < loadedArray.length; i++){
var currentY1:int = 200;
var image: Sprite= new Sprite;
e.currentTarget.loader.content.height =200;
e.currentTarget.loader.content.y += currentY1;
image.mouseChildren = true; // ignore children mouseEvents
image.mouseEnabled = true; // enable mouse on the object - normally set to true by default
image.useHandCursor = true; // add hand cursor on mouse over
image.buttonMode = true;
image.addChild(loadedArray[i]);
_contentHolder.addChild(image);
}
newArray.push(image);
var viewport:Viewport = new Viewport();
viewport.y = 0;
viewport.addChild(_contentHolder);
var scroller:TouchScroller = new TouchScroller();
scroller.width = 300;
scroller.height = 265;
scroller.x = 10;
scroller.y = 100;
scroller.viewport = viewport;
addChild(scroller);
image.addEventListener(MouseEvent.CLICK, gotoscene);
}
loadImage();
Edit:
function gotoscene(e: MouseEvent):void{
var index:Number;
index = newArray.indexOf(e.target);
trace(index);
blackBox.graphics.beginFill(0x000000);
blackBox.graphics.drawRect( -1, -1, stage.width, stage.height);
blackBox.alpha = 0.7;
addChild(blackBox);
var originalBitmap : BitmapData = loadedArray[index].bitmapData;
var duplicate:Bitmap = new Bitmap(originalBitmap);
duplicate.width = stage.width;
_contentHolder1.addChild(duplicate);
// Use counter here to only add _contentHolder1 once
//Assuming that `samedata` is a class member (I can't see the rest of your code)
addChild(_contentHolder1);
}
Edit2:
private var image:Array = new Array;
//In the For loop
image[i] = new Sprite();
image[i].addChild(loadedArray[i]);
image[i].addEventListener(MouseEvent.CLICK, gotoscene);
function gotoscene(e:MouseEvent):void{
index = image.indexOf(e.target);
trace(index);
}
You should move image.addEventListener(MouseEvent.CLICK, gotoscene); statement into the loop where you add child sprites. Once you do, the listener will be added to all of the sprites, not just the last one that's currently stored in image variable, and is the only one that responds to your clicks.
for(var i:int = 0; i < loadedArray.length; i++){
var currentY1:int = 200;
var image: Sprite= new Sprite;
e.currentTarget.loader.content.height =200;
e.currentTarget.loader.content.y += currentY1;
image.mouseChildren = true; // ignore children mouseEvents
image.mouseEnabled = true; // enable mouse on the object - normally set to true by default
image.useHandCursor = true; // add hand cursor on mouse over
image.buttonMode = true;
image.addEventListener(MouseEvent.CLICK, gotoscene); // <-- THIS
image.addChild(loadedArray[i]);
_contentHolder.addChild(image);
}
And for all that is holy, learn to indent your code, so that you will be able to visually find the start and end of your loops and see if a certain statement is within the loop or not.
I did work for a few years in AS3 and this was a weird an usual problem. I used to solve it with a function that adds the event to each clip:
function someFunction():void {
for (...) {
var image:Sprite = new Sprite();
addSceneListener(image);
}
}
function addSceneListener(mc:Sprite):void {
mc.addEventListener(MouseEvent.CLICK, gogoscene);
}

Remove randomly generated sprites with Mouse.Event

I am making a game for my class where different chemical elements are generated as sprites at the top of the screen and then fall down. Different types are made and I want students to mouse over specific types depending on where they are in the game.
My question is how to write the function to remove them when they are correctly selected? I've tried a lot of different ways but am having a lot of trouble. An example of the code that I wrote to make each element is below and then I have a separate function to move down all of the sprites created.
var spriteArray:Array = new Array();
var halogenArray:Array = new Array("F", "Cl", "Br", "I");
var rndnum:Number = Math.random();
//Halogens
if (rndnum < 0.05)
{
var halo:Sprite = new Sprite();
halo.graphics.beginFill(0x00FF00, 1);
halo.graphics.drawCircle(7.5, 7.5, 15);
halo.graphics.endFill();
halo.addEventListener(MouseEvent.MOUSE_OVER, removeElement);
halo.x = Math.random()*500 + 50;
halo.y = -18;
var textField = new TextField();
textField.text = halogenArray[int(Math.random()*4)];
textField.width = 30;
textField.height = 30;
textField.x = (15 - textField.textWidth)/2; // center it horizontally
textField.y = (15 - textField.textHeight)/2; // center it vertically
halo.addChild(textField);
spriteArray.push(halo);
addChild(halo);
}
At what point are you struggling?
I am assuming it is in determining the types of the halogens.
In your remove function I assume you have the desired type already figured out, you would then compare it to
element.getChildAt(0).text
and you would get the element by either looping across every element in the spriteArray, or using the mouseEvent's target
My suggestion is to use a halogen Class to contain the grapics & textfield, and a vector to hold the objects. It would then be easier to get the type rather than searching the anonymous children of the sprite.
I believe you are looking for something like this:
//give your textfields a name, it isn't totally necessary as we can do getChildAt(0)
//but it's more readable, and if you decide to add more children before you
//add the text field, then this will still work
var textField = new TextField();
textField.text = halogenArray[int(Math.random()*4)];
textField.width = 30;
...
textField.name = "haloTx"; //for tracking later
//assuming you have some variable set to the correct answer
var correctAnswer:String = "F";
function removeElement( e:MouseEvent ):void {
var element:TextField = ( e.target as Sprite ).getChildByName( "haloTx" );
//if we have the correct element, remove from parent and list
if ( element && element.text == correctAnswer ) {
var index:int = spriteArray.indexOf( e.target as Sprite );
removeChild( spriteArray.splice( index, 1 )[0] );
}
}
Although #VBCPP is right, doing that in a separate class is definitely the best way organizationally. Which might look something like:
class ElementSprite extends Sprite {
public var textField:TextField;
//pass shapeArgs as string, so say a circle at x=7.5, y=7.5, and radius=15 -- shapeArgs = "7.5, 7.5, 15"
public function ElementSprite( element:String, drawShape:String="Circle", shapeArgs:String="7.5, 7.5, 15", fillColor:uint=0x00FF00 ) {
//set textfield properties etc. or textFormat
textField = new TextField();
textField.text = element;
addChild( textField );
//if you passed some arguments to draw our shape
if ( shapeArgs != "" ) {
graphics.beginFill( fillColor );
graphics[ "draw" + drawShape ].apply( this, shapeArgs.split( "," ) );
}
}
public function get currentElement():String { return textField.text }
}
Then you would use it like so in your if statement if (rndnum < 0.05):
var elementSprite:ElementSprite = new ElementSprite( "A" );
//elementSprite.x = set your x;
//elementSprite.y = set your y;
addChild(elementSprite);
That would be replacing all your current code in that if statement. This is all a working example, if you have an questions feel free to comment.

Remove sprite from stage in as3

I have this code for a message to appear on stage when player finishes drag and drop. I would like this sprite to be removed when a button is clicked for the next frame. Can someone help me with the code?
stage.addEventListener(Event.ENTER_FRAME, EntFrame);
function EntFrame (e:Event):void
{
if (CntP1+CntP2+CntP3+CntP4+CntP5+CntP6+CntP7+CntP8 == 40)
{
var w:int = 400, h:int = 200;
var win:Sprite = new Sprite();
win.name = "Mywin";
addChild(win);
// draw rounded rect with subtle vertical linear gradient fill and blue stroke
win.graphics.lineStyle(4,0x0077ff);
var mat:Matrix = new Matrix();
mat.createGradientBox(w, h, 90 * (Math.PI / 180));
win.graphics.beginGradientFill(GradientType.LINEAR,[0xffffff,0xeeeeee],[1.00,1.00],[0,255],mat);
win.graphics.drawRoundRect(0,0,w,h,15,15);
// show center "YOU WIN!" text
var tf:TextField = new TextField();
tf.autoSize = TextFieldAutoSize.LEFT;
tf.antiAliasType = AntiAliasType.ADVANCED;
tf.defaultTextFormat = new TextFormat("Arial, Verdana",36,0x454545,true);
tf.text = "Κέρδισες!";
tf.selectable = false;
win.addChild(tf);
tf.x = w/2 - tf.width/2;
tf.y = h/2 - tf.height/2;
// add a drop shadow
var dropShadow:DropShadowFilter = new DropShadowFilter(3,45,0,.35,8,8,1,3);
win.filters = [dropShadow];
// center the graphic
win.x = stage.stageWidth/2 - win.width/2;
win.y = stage.stageHeight/2 - win.height/2;
}
}
Your code isn't written well and needs rewriting to ensure reuse or scalability of your project, but here's a quick way out.
make a holder Sprite, something like
var messageHolder:Sprite = new Sprite();
addChild(messageHolder);
add all the messages to that holder in any fashion you like. When you need to erase the contents of that holder, call following method:
function clearHolderContents(holder:DisplayObjectContainer):void
{
if (holder.numChildren < 1)
return; // no need to continue this method if the target is empty
for (var i:int = holder.numChildren - 1; i >= 0; i--)
removeChild(holder.getChildAt(i));
}
This method can clear contents of any DisplayObjectContainer => use it for your messageHolder:
clearHolderContents(messageHolder);
Hope that helps!

AS3 Setting width and height of a Sprite Container

Ok, my mind is boggling over this issue im having. I know this might seem like such an easy issue and I can't understand why I cant figure it out but none the less I can't and I've just about given up. Here's the issue:
I have a sprite container which is supposed to hold a bunch of thumbnails to videos. I am able to populate the container with all the videos and the whole works but obviously if I add a bunch of videos its going to exceed the size of the flash document so I need to add a UIScrollBar (which I did) now the scrollbars target is set to the container but isnt allowing me to scroll and if im correct this is because the container doesnt have a set height. So Im trying to set the height of this container but the second I try setting the height or even width all my thumbnails I used to be able to see are gone! It's as if the size is being set to 0 when its not I've even tried to set it to a specified size just to test and nothing. Anyways heres my code if anyone can help out I'd really appreciate it! Thanks in advance!
import fl.controls.UIScrollBar;
var videoList:XMLList;
var numVideos:Number;
var current_Video:Number = 0;
var video_position:Number;
var video_paused:Boolean;
var xmlPlaylist:String;
//XML File setup
var playlist_xml:XML;
var myLoader:URLLoader = new URLLoader();
//Playlist setup
var thumb_width:Number;
var thumb_height:Number;
var thumbs_x:Number;
var thumbs_y:Number;
var main_container:Sprite;
var thumbs:Sprite;
var scrollbar:UIScrollBar;
//Loader Data
this.loaderInfo.addEventListener(Event.COMPLETE, loaderComplete);
function loaderComplete(e:Event):void
{
var myQueryStrings = this.loaderInfo.parameters;
xmlPlaylist = myQueryStrings.pList;
myLoader.load(new URLRequest(xmlPlaylist + "?uniq=" + new Date().getTime()));
}
myLoader.addEventListener(Event.COMPLETE, processXML);
function processXML(e:Event):void {
playlist_xml = new XML(e.target.data);
numVideos = playlist_xml.video.length();
videoList = playlist_xml.video;
thumb_width = playlist_xml.#thumb_width;
thumb_height = playlist_xml.#thumb_height;
thumbs_x = playlist_xml.#thumbs_x;
thumbs_y = playlist_xml.#thumbs_y;
current_Video = Math.round(Math.random()*(numVideos-1))+1;
current_Video--;
startPlayer();
}
function startPlayer()
{
makeContainers();
callThumbs();
setVideo(current_Video);
}
function makeContainers():void
{
main_container = new Sprite();
addChild(main_container);
thumbs = new Sprite();
thumbs.addEventListener(MouseEvent.CLICK, playVideo);
thumbs.addEventListener(MouseEvent.MOUSE_OVER, onOver);
thumbs.addEventListener(MouseEvent.MOUSE_OUT, onOut);
thumbs.x = thumbs_x;
thumbs.y = thumbs_y;
This is where the Issue is: (If i comment out this code it displays the thumbnails)
thumbs.width = thumb_width;
thumbs.height = (thumb_height + 11) * 3;
thumbs.buttonMode = true;
main_container.addChild(thumbs);
scrollbar = new UIScrollBar();
scrollbar.x = thumbs_x + thumb_width + 2;
scrollbar.y = thumbs_y;
scrollbar.setSize(25, (thumb_height + 11) * 3);
scrollbar.visible = true;
scrollbar.scrollTarget = thumbs;
main_container.addChild(scrollbar);
}
function callThumbs():void
{
for (var i:Number = 0; i (less than) numVideos; i++) //For some reason Stack Overflow isnt allowing me to put the symbol less than so i just typed it in...
{
var thumb_url = videoList[i].#thumb;
var thumb_loader = new Loader();
thumb_loader.name = i;
thumb_loader.load(new URLRequest(thumb_url));
thumb_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, thumbLoaded);
thumb_loader.y = (thumb_height + 11) * i;
}
}
function thumbLoaded(e:Event):void
{
var my_thumb:Loader = Loader(e.target.loader);
thumbs.addChild(my_thumb);
}
function playVideo(e:MouseEvent):void
{
setVideo(e.target.name);
}
function onOver (e:MouseEvent):void
{
var my_thumb:Loader = Loader(e.target);
my_thumb.alpha = 0.5;
}
function onOut (e:MouseEvent):void
{
var my_thumb:Loader = Loader (e.target);
my_thumb.alpha = 1;
}
function setVideo(current_Video)
{
var display:String = videoList[current_Video].#title;
var video:String = videoList[current_Video].#url;
txt_Display.text = display;
flvPlayer.source = video;
}
stop();
This is easy. You are creating Sprite, adding listeners, setting coordinates. The sprite is still empty. Then you set width and height, which are translating to scaleX and scaleY. On empty sprite this messes up transformation matrix and sprite will never show up. Set width, height, or scaleX/Y only on non-empty sprites.