AS3 Clicking in "zones" of a picture - actionscript-3

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.

Related

as3 add multiple MovieClips into random MovieClips

I'm trying to to do some dynamic MovieClips placement here. I've been trying for a couple'o days and I can't find the right way to do the trick. It's the continuation for this. I didn't manage to properly make my MC's appear in the triangle, so I've made a pyramid of rectangles (suits my case better, beacuse I can change each of'em, to better fit my desired shape - a not-so-regular triangle).
Here's the code with some comments:
import flash.events.MouseEvent;
import flash.display.MovieClip;
btn_toys_2.confirm.addEventListener(MouseEvent.MOUSE_UP, confirmToys);
var toysPlc:Array = new Array(); //an array for a bunch of rectangles
var toAdd:int = 100 //this is supposed to be dynamic, user defined
var toy:MovieClip = new shar_001; //an MC from a library
for (var j:int=0; j<33; j++){
toysPlc.push("tPl_" + j); //here I add that bunch of rects into an array
}
function confirmToys(e:MouseEvent):void{
for (var k:int=0; k<toAdd; k++){ //supposed to add an "toAdd" amount of "toys"
var p:int = Math.random()*toysPlc.length; //^do so in a random rect
toysPlc[p].addChild(toy); //supposed to place a toy in a random rect
toy.x = Math.random()*toysPlc[p].width; //positioning
toy.y = Math.random()*toysPlc[p].height; //positioning
}
}
The error I get is: TypeError: Error #1006: value is not a function.
What I DID manage is to place a single toy in a random of these rects, tho I don't remember how :)
Thanks in advance!
EDIT: null asked me to clarify the case, so here's the whole picture:
I've got:
- triangle-like MC (since a triangle MC is a rectangle for flash anyway, I've solved this by creating a pyramid of 33 rectangles, layered on each other);
- toys (multiple frames to change whenever I need to);
- text field (to input the quantity of desired toys);
- confirm button (to make the magic happen once clicked);
Now I need a user to input a 3-digit number in the input field, press "confirm", and 0.4 of that quantity is supposed to appear in the "triangle-like MC".
For example: user inputs a number: 600, the end value is 600*0.4=240. Now 240 "toys" are randomly spreaded between my 33 rectangles, within their width (which is different for every single one).
Hope that explains a bit more.
A simplified sample of what I need here.
Is there a way to fill an array with MovieClips rather than String values? That would be THE answer to this question here.
There is in fact more than one way:
Place all the instance names into the Array when you create it:
var rectangles:Array = [instanceName1, instanceName2];
there are no quotation marks, which create string literals. Just the names.
This approach quickly becomes impractical for large numbers of objects.
Given that the instance names have a number component, iterate through the names in conjunction with getChildByName(). I assume that this is what you were trying with in your question:
var rectangles:Array = []; // initialise empty array
for (var j:int=0; j<33; j++){
rectangles.push(getChildByName("tPl_" + j));
}
original answer
toysPlc.push("tPl_" + j); //here I add that bunch of rects into an array
No you don't. You are filling the Array with String objects. It's totally unrelated to any rectangle whatsoever.
Now this next line, tries to call a function on each String, which fails.
toysPlc[p].addChild(toy);
The above is equivalent to
"tPl_0".addChild(toy);
"tPl_1".addChild(toy);
// etc.
A String doesn't have that method addChild.

Loop an image in Flash

I want to have a scene where an image which is 5000 pixels high is moving up 5 pixels each frame-refresh. When the image is all up, I'd like to see the top of the image connected to the bottom of the image. This should be done untill the level is 'done'. How can I 'loop' such an Image?
You can create a copy of that image which you keep hidden/above and the trick is to update the position and loop accordingly so when one image goes bellow the screen it goes back on top and repeats.
Here's a basic snippet to illustrate the idea using the DisplayObject class and the scrollRect property:
//ignore this, you have your content already
var dummyContent:BitmapData = new BitmapData(100,100,false);
dummyContent.perlinNoise(10,10,8,12,true,true);
//important stuff starts here
var container:Sprite = addChild(new Sprite()) as Sprite;//make a container
container.scrollRect = new Rectangle(0,0,dummyContent.width,dummyContent.height);//set a scrollRect/'mask'
var part1:DisplayObject = container.addChild(new Bitmap(dummyContent));//add two copies
var part2:DisplayObject = container.addChild(new Bitmap(dummyContent));//of the same content
part2.y -= part2.height;//set the 2nd at the top of the 1st
addEventListener(Event.ENTER_FRAME,update);
function update(e:Event):void{
//move both
part1.y += 5;
part2.y += 5;
//check if any reach the bottom so they can be moved back up
if(part1.y >= part1.height) part1.y = -part1.height;
if(part2.y >= part2.height) part2.y = -part2.height;
//the above can also be nicely placed in a loop if you plan on using more seamless looping clips/images
}
Obviously you will have different content, but the principle is the same.
If you're working with images, you can simply use BitmapData's copyPixels method:
var s:int = 5;//scroll speed
//make some content
var w:int = 100;
var h:int = 100;
var dummyContent:BitmapData = new BitmapData(w,h,false);
dummyContent.perlinNoise(10,10,8,12,true,true);
//prepare for stiching
var renderPos:Point = new Point();//position to render the current image to
var prenderPos:Point = new Point();//position to render the previous image (the 'hidden' copy above)
var render:BitmapData = new BitmapData(w,h,false);//create a bitmap data instance to render updated pixels int
addChild(new Bitmap(render));//and add it to the stage
addEventListener(Event.ENTER_FRAME,update);
function update(e:Event):void{
renderPos.y = (renderPos.y+s)%h;//update the scroll position for the 1st part, % is used to loop back to 0 when the position gets to the content height
prenderPos.y = renderPos.y - h;//update the scroll position for the 2nd part (above)
render.lock();//freeze pixel updates
render.copyPixels(dummyContent,dummyContent.rect,renderPos);//copy pixels from the scroll position to the bottom
render.copyPixels(dummyContent,dummyContent.rect,prenderPos);//copy pixels from the top to the scroll position
render.unlock();//unfreeze/update ALL THE PIXELS
}
You can try to use a Rectangle object which changes height (height-scrollPosition) so you potentially access less pixels each time or you can manually work out single for loops using BitmapData's getVector method, but that's something to look into if performance is actually an issue for such a simple task and it's worth checking what's faster ( copy full bitmap rect vs copy partial bitmap rect vs manually copy values using vector )
Be warned, Flash cannot load an image greater than 16,769,025 pixels (or 4095x4095). The height of 5000 pixels will work as long as the width is not greater than 3353.
That said, I'd loop the image by keeping two copies of the image onstage, move both at the same time with a parent object, and reset to origin once your loop point is met.
Consider the following stage setup:
Stage ¬
0: MainTimeline:MovieClip ¬
0: Container:MovieClip ¬
0: img1:Bitmap
1: img2:Bitmap
Now moving container up, you'd just need to check that the looping second image reaches the origin point of the first image.
function onEnterFrame(e:Event):void {
Container.y = Container.y - 5;
if (Container.y < -5000) {
Container.y = -5;
}
}

How can I randomize a picture under a cover in actionscript 3.0?

I'm making a flash game for practice and I have my stage set up so there are 9 boxes. When the game is started, one of the boxes is randomized as the one with the start underneath, if you pick the box with the star underneath, you win.
The randomizing code is
var star = 1 + Math.Round(Math.Random()*8.0)//generate a number between 1 and 9
What i dont know is how to attach this code so that it assigns the star to one of my 9 boxes made as buttons. How can I hide the star underneath the box as a cover.
Thanks for your time
I'm picturing one of those games where you but a ball under one of three cups and swap the cups, then guess which one has the ball.
The simplest way to hide one object under another is to just add it to the stage before the object covering it. So add your star to the stage, then add all your boxes. BUT since you don't have to have an unseen object actually be on the stage, I recommend not adding the star to the stage until it is revealed, and remove it when it gets hidden again.
You can create layers to make sure objects are always above/below what they need to be above/below. Create sprite objects, and call them layers. Add them in order from bottom to top. Add other sprites to these layer sprites to control their display order.
var layer1:Sprite = new Sprite(); // Bottom / background
var layer2:Sprite = new Sprite(); // Top / foreground
stage.addChild(layer1);
stage.addChild(layer2);
layer2.addChild(someObject1);
layer1.addChild(someObject2); // someObject2 will be below someObject1
That deals with covering the star with the boxes.
You can put your boxes in an array. You'll want a number between 0 and 8, then just use that as the index in the array to get the box you want.
var whichBox:int = (int)(Math.random() * 9);
var boxesArray:Array = new Array();
for (var i:int = 0; i < 9; i++) {
boxesArray.push(new Box()); // Or whatever your boxes are
}
var boxWithStar:Box = boxesArray[whichBox];
You can then move the star to the same location as its box...
star.x = boxWithStar.x;
star.y = boxWithStar.y;
This is a pretty handy function you can use:
function randRange(start:Number, end:Number) : Number
{
return Math.floor(start +(Math.random() * (end - start)));
}
example (any number between 0 - 9) :
var random:int = randRange(0,9);
remember to make it an int or you may end up with a float.

Setting multiple depth layers in AS3

I get how to set depth in as3 - but with as2 i could begin multiple 'depth points' using numbers - where in as3 all i can seem to do is set this object to a higher/lower depth than that object. The problem is (when dealing with a stack of isometric boxes, which can be placed by the user on a grid in any order) i don't want to deal with the added complexity of having every element know where every other element is, then adjust appropriately.
What I'm trying to do is set up 6 total depth numbers/positions, one for each column in a 6 x 6 grid. So anything in column 1 will begin it's depth placement at say 500, anything in column 2 will begin its depth at 1000, column 3 would be 1500 and so on.
That way, the second i place an object on a particular column, it would tuck itself under, or place itself above all surrounding items in other columns, this to me is much much easier than somehow figuring out where 15 different sized boxes are, how they relate to one another, then figure out what depth order they need to go in.
Any ideas? as3 seems to have removed the ability to set a depth to a specific number :p
The approach can be simplified. You basically want to create 3 'container' clips and add them in order. The last one added is the top-most.
Bonus: if you want to rearrange, you can call addChild() on any clip (even already added ones) and that one will go to the top.
//// IMPORTANT STUFF ////
import flash.display.Sprite;
var top:Sprite = new Sprite;
var mid:Sprite = new Sprite;
var bot:Sprite = new Sprite;
addChild(bot);
addChild(mid);
addChild(top);
//// END IMPORTANT STUFF ////
// Move Stuff so we can visualize how this works.
// Then add some boxes so we can see what's going on.
mid.x = 20;
mid.y = 20;
bot.x = 40;
bot.y = 40;
// Add Top box
var t:Sprite = new Sprite;
t.graphics.beginFill(0xFF0000);
t.graphics.drawRect(0,0,100,100);
top.addChild(t);
// Add Middle box
var m:Sprite = new Sprite;
m.graphics.beginFill(0x00FF00);
m.graphics.drawRect(0,0,100,100);
mid.addChild(m);
// Add Bottom box
var b:Sprite = new Sprite;
b.graphics.beginFill(0x0000FF);
b.graphics.drawRect(0,0,100,100);
bot.addChild(b);

Actionscript 3.0 problem - Why is it giving an error even when I've re-checked the code?

I'm using Adobe Flash CS4 professional for this Actionscript 3.0 project
(http://tutorials.flashmymind.com/2009/02/rotating-menu-via-actionscript-3/)
I even tried following the suggestions in the comments as well but this error always shows up:
TypeError: Error #1010: A term is undefined and has no properties.
at rotating_menu_fla::MainTimeline/frame1()
(for complete details, kindly click the link - http://i429.photobucket.com/albums/qq19/tsujzpie/screenshot_03.jpg)
I've been following every step of the tutorial but I'm stumped over the coding...
Here's the code by the way...
//Save the center coordinates of the stage
var centerX:Number=stage.stageWidth/2;
var centerY:Number=stage.stageHeight/2;
//The number of items we will have (feel free to change!)
var NUMBER_OF_ITEMS:uint=5;
//Radius of the menu circle (horizontal and vertical)
var radiusX:Number=200;
var radiusY:Number=100;
//Angle difference between the items (in radians)
var angleDifference:Number = Math.PI * (360 / NUMBER_OF_ITEMS) / 180;
//How fast a single circle moves (we calculate the speed
//according to the mouse position later on...)
var angleSpeed:Number=0;
//Scaling speed of a single circle
var scaleSpeed:Number=0.0002;
//This vector holds all the items
//(this could also be an array...)
var itemVector:Array = new Array ('1', '2', '3', '4','5');
//This loop creates the items and positions them
//on the stage
for (var i:uint = 0; i < NUMBER_OF_ITEMS; i++) {
//Create a new menu item
var item:Item = new Item();
//Get the angle for the item (we space the items evenly)
var startingAngle:Number=angleDifference*i;
//Set the x and y coordinates
item.x=centerX+radiusX*Math.cos(startingAngle);
item.y=centerY+radiusY*Math.sin(startingAngle);
//Save the starting angle of the item.
//(We have declared the Item class to be dynamic. Therefore,
//we can create new properties dynamically.)
item.angle=startingAngle;
//Add an item number to the item's text field
item.itemText.text=i.toString();
//Allow no mouse children
item.mouseChildren=false;
//Add the item to the vector
itemVector.push(item);
//Add the item to the stage
addChild(item);
}
//We use ENTER_FRAME to animate the items
addEventListener(Event.ENTER_FRAME, enterFrameHandler);
//This function is called in each frame
function enterFrameHandler(e:Event):void {
//Calculate the angle speed according to mouse position
angleSpeed = -(mouseX - centerX) / 5000;
//Loop through the vector
for (var i:uint = 0; i < NUMBER_OF_ITEMS; i++) {
//Save the item to a local variable
var item:Item=itemVector[i];
//Update the angle
item.angle+=angleSpeed;
//Set the new coordinates
item.x=centerX+radiusX*Math.cos(item.angle);
item.y=centerY+radiusY*Math.sin(item.angle);
//Calculate the vertical distance from centerY to the item
var dy:Number=centerY-item.y;
//Scale the item according to vertical distance
item.scaleY = (dy / radiusY)+2;
//Set the x scale to be the same as y scale
item.scaleX=item.scaleY;
//Adjust the alpha according to y scale
item.alpha=item.scaleY+1.1;
}
}
I find it odd - it may be that the code is right but I don't know if these steps has messed up the project...
3 - Convert the circle into a movie clip...
4 - In the “Item” movie clip, create a dynamic text field in the center of the circle (in a new layer).
5 - Set the text to align center. Type some number in the text field. Give the text field an instance name of “itemText”. Embed numerals...
6 - Remove the Item movie clip from the stage. We will create all the items dynamically via ActionScript 3.
I could've given more screenshots but since I'm a new user, I'm only allowed a max of two - and contrary to this tag, I'm not doing an Android app.
(I'll give you added info once any of you would reply to this question...)
I have to admit that the steps 3 to 6 is confusing and didn't made sense to me - especially with step six, when you have to remove the movie clip from the stage. For me, if I were to do that, what would the script work on then?
Any idea what I did wrong?
EDIT:
Thanks, I realized my mistake - thanks for the tip :-)
But now, I tried to modify the code of this tutorial a bit to have words appear inside the circles (like "Home", "About", etc....) like what I've typed in the line in the screenschot - http://i429.photobucket.com/albums/qq19/tsujzpie/modifiedlineincode_00.jpg
But inspite of what I believe are the appropriate changes I've done, an error showed up still - (kindly see here - http://i429.photobucket.com/albums/qq19/tsujzpie/newerrorincode_00.jpg)
Why is that happening? Before I forget, in what section of the code must I insert a line that will make a clicked button display the info corresponding to it? (that is, if I click on the "Contact" or any of the buttons a window will appear beneath the menu on the stage...)
That errors means ActionScript has no clue what an Item is. Make sure you've ticked Export for ActionScript on your Item MovieClip in library and that the class is named Item also.
Update:
The tutorial you're following mentions itemVector is a Vector of Item instances, not an array of Strings like you're code suggests. This why you're getting the error displayed in your screenshot. It means ActionScript can not convert an Item to a String.
An easy fix is to create another array for the menu item labels:
var itemVector:Array = [];// = new Array ('1', '2', '3', '4','5');
var itemLabels:Array = ["Home","About","Contact","Gallery"];
NUMBER_OF_ITEMS = itemLabels.length;
and in the for loop swap this item.itemText.text=i.toString(); for this tem.itemText.text=itemLabels[i];
It seems that you don't fully grasp the difference between variable types. I would suggest getting familiar with the basics of as3 before moving forward. Also paying more attention to your code and fully understanding code you use written by others will keep you out of trouble. However you will still encounter errors. You might find explanations for those errors on the Flash Error Database.
Regarding the Carosel tutorial, also have a look at this video. It might help explain things better.
The compiler can not find the Item class.
Go into your library and open up the properties for the "Item" MovieClip.
Verify that is is set for export to actionscript.