AS3 - Tools that drag and rotate in flash TOOL MODE - actionscript-3

I'm making an app/program that allows you to design your own piece of art with two basic functions by clicking the buttons on the UI, We'll call the 'Modes' for now.
Tweezer mode: you can drag the selected set of objects about in this mode.
Rotate Mode: this allows you to rotate a certain set of movie clips on the stage.
Assigning it.
I want it so that when rotate mode or tweezer mode is active the other is disenaged (enabled = false) or to that effect. I have arrived at a fork in the road where I need group them under method rotate or method tweezer. When tweezer is clicked, you move stuff about (only), and when rotate mode is selected you can rotate the movie clips...(only) this works fine until after you come away from rotate mode back tweezer that you can still rotate the movie clip! Could anyone shed some light on this so when I leave this mode you still can rotate it? Could any suggest the best way to organize this sort of functionality?
Thanks for your help - I'm an AS3 newbie.
// UI btns TOOLS ---------------------
spinny_mc.addEventListener(Event.ENTER_FRAME, fl_RotateContinuously);
function fl_RotateContinuously(event:Event)
{
spinny_mc.rotation += 20;
}
rotate_btn.visible = true;
tweezer_btn.visible = false;
//----- rotate tool
rotate_btn.addEventListener(MouseEvent.CLICK, spinmode);
function spinmode(event:MouseEvent):void
{
Mouse.hide();
stage.addEventListener(MouseEvent.MOUSE_MOVE,followspin);
function followspin(evt:MouseEvent)
{
spinny_mc.x = mouseX;
spinny_mc.y = mouseY;
rotate_btn.visible = false;
tweezer_cur.visible = false;
tweezer_btn.visible = true;
rotate_btn.enabled = true;
tweezer_btn.enabled = false;
skullface_mc.addEventListener(MouseEvent.CLICK, turnerbone);
function turnerbone(event:MouseEvent):void
{
skullface_mc.rotation+=45;
}
}
}
// ------------------------ tweeze tool
Mouse.hide();
stage.addEventListener(MouseEvent.MOUSE_MOVE,follow);
function follow(evt:MouseEvent){
tweezer_cur.x = mouseX;
tweezer_cur.y = mouseY;
}
tweezer_btn.addEventListener(MouseEvent.CLICK, tweezer);
function tweezer(event:MouseEvent):void
{
Mouse.hide();
stage.addEventListener(MouseEvent.MOUSE_MOVE,tweezer);
function tweezer(evt:MouseEvent){
tweezer_cur.x = mouseX;
tweezer_cur.y = mouseY;
rotate_btn.visible = true;
tweezer_cur.visible = true;
tweezer_btn.visible = false;
spinny_mc.visible = false;
rotate_btn.enabled = false;
tweezer_btn.enabled = true;
}
}

The problem comes down to your event listeners. They don't go away after you leave the function, as they are essentially objects in and of themselves. You need to do that manually.
At the top of the spinmode() function, add the line
stage.removeEventListener(MouseEvent.MOUSE_MOVE,tweezer);
At the top of the tweezer() function, add the line
stage.removeEventListener(MouseEvent.MOUSE_MOVE,followspin);
WARNING: I cannot rightly recall if this will throw an error if the event listener has not yet been created. If so, you can get around that using a Try/Catch, or an if-statement that links to a variable indicating what mode the user is in.
You may also need to rearrange your code a bit. I do not recommend nesting functions that affect the stage, as they can make life kinda difficult. Define the functions separately, and then call the other functions from the one you want.
Keep in mind, ActionScript is Object-Oriented, not procedural (and NOT strictly top-down). I'm not sure if you're coming from another language, but I know that took a lot of getting used to for me, coming from a procedural top-down technique.
WRONG
function myFunction1()
{
function myFunction 2()
{
//foobar
}
}
RIGHT
function myFunction1()
{
myFunction2();
}
function myFunction2()
{
//foobar
}

Related

How can I make a symbol appear after clicking in two different buttons in actionscript 3.0?

I am a little bit new in this programming stuff and I need help to program in ActionScript 3.0 (Adobe Animate CC). I want to make a symbol (graph) visible but only after clicking in two different buttons (button1 and button 2). I can make that with just one button, but I can't make it with two buttons... Can anyone help me? I tried this code but it isn't working as it should:
button1.addEventListener (MouseEvent.CLICK, fl_MouseClickHandler_1);
button2.addEventListener (MouseEvent.CLICK, fl_MouseClickHandler_1);
function fl_MouseClickHandler_1(event:MouseEvent):void
{
graph.visible = true;
}
Tom
Simplest way would be to do this, although there may be other ways to do it as well:
var isButton1Clicked:Boolean = false;
var isButton2Clicked:Boolean = false;
button1.addEventListener (MouseEvent.CLICK, fl_MouseClickHandler_1);
button2.addEventListener (MouseEvent.CLICK, fl_MouseClickHandler_1);
function fl_MouseClickHandler_1(event:MouseEvent):void
{
if (event.currentTarget == button2)
isButton2Clicked = true;
else if (event.currentTarget == button1)
isButton1Clicked = true;
if (isButton1Clicked && isButton2Clicked)
{
graph.visible = true;
isButton1Clicked = isButton2Clicked = false;
}
}
Note that I reset both the Boolean values to false, once the graph is visible so that it works like a reset.
On a side note, I would recommend to use better names for your buttons and your event handlers. Just best practice.
Hope this helps. Cheers.

Problems with an object appearing on a frame I don't want it to appear on AS3

I am a total noob at AS3, roughly 1 year experience so please be lenient with me :)
I currently am making an endless runner game and I'm making the obstacles spawn using this method
var therespawn:RespawnObject;
var thecone:trafficcone;
var started:Boolean = false;
var dx:Number = 10;
var dy:Number = 10;
stage.addEventListener(Event.ENTER_FRAME, startGame);
addEventListener(Event.ENTER_FRAME, collision);
addEventListener(Event.ENTER_FRAME, coneCollision);
function startGame(evt:Event):void {
if (started == false) {
spawnHazard();
}
}
function spawnHazard() {
started = true;
therespawn = new RespawnObject();
addChild(therespawn);
thecone = new trafficcone();
addChild(thecone);
therespawn.x = -50;
therespawn.y = 310;
thecone.x = 600;
thecone.y = 310;
}
function collision(evt:Event):void {
thecone.x -= 15;
if(thecone.hitTestObject(therespawn)) {
thecone.x = 600;
}
}
Now the only way to finish the game or end it is to get hit by an obstacle which ive shown down below:
function coneCollision(evt:Event):void {
if(MainChar.hitTestObject(thecone)) {
gotoAndStop("frameFive");
}
}
Everytime the highscore frame appears the cone is still spawning and despawning, why is that?
I haven't declared them as global?
Any help appreciated, thanks!
You can fix your problem by setting started to false:
function coneCollision(evt:Event):void {
if(MainChar.hitTestObject(thecone)) {
started = false;
gotoAndStop("frameFive");
}
}
The flash timeline really only has to with the way MovieClips are visually displayed as children of the stage. Removing an object from the timeline doesn't just suddenly nullify all the code associated with that object. In other words, your ENTER_FRAME method still runs in the background even if the object is no longer a child of the Stage, regardless of the frame number for the MovieClip. If you're serious about coding you might consider investigating in Classes and Object Oriented AS3. Classes are much nicer to work with than the Flash timeline.

Swapping the image used for my character, to a seperate image on button click

So first off i'd like to state that i am not very good at AS3, i'm completely self taught so i am sure that there are many things i've done badly, inefficiently or plain wrong and i'm happy for any comments on these if there is things i can improve.
Okay so, this seems like a really simple problem but i don't know the exact code for it and i can't find an example anywhere
what i want to do is to change the image which i am using for my character to a separate image on a button click, then after a certain amount of time for him to go back to the original.
My image is set as a movie clip and i am calling it like so :
public var Smallclock_hero = new Smallclock;
it is worth noting that smallclock has it's own separate class.
is there a way that i can change this image on a mouse click event ?
A simple way would be:
Add a new image on the second frame of the Smallclock movieclip. Have this new image span several frames of the timeline for the duration you want it to appear for.
Place a stop(); action on the first frame of the Smallclock movieclip.
btn.addEventListener(MouseEvent.CLICK, btnChangeCharacterFrame);
function btnChangeCharacterFrame(e:MouseEvent)
{
Smallclock_hero.gotoAndPlay(2);
// Or use a frame label
// Smallclock_hero.gotoAndPlay("jump");
}
This can be achieved relatively simply using the visible property and setTimeout. When you click on your MovieClip you'll want to make the old image invisible and the new image visible:
oldImage.visible = false;
newImage.visible = true;
Then you'll want to use setTimeout to change the visible properties back to:
oldImage.visible = true;
newImage.visible = false;
setTimeout can run a function after a specified number of milliseconds. Here is an example of this technique on wonderfl : http://wonderfl.net/c/acxl
private var imageA:ImageA = new ImageA();
private var imageB:ImageB = new ImageB();
private var revertId:int;
public function Main() {
imageB.visible = false;
addChild(imageA);
addChild(imageB);
addEventListener(MouseEvent.CLICK, onClick);
}
private function onClick(e:MouseEvent):void{
clearTimeout(revertId);
revertId = setTimeout(revert, 1000);
imageB.visible = true;
imageA.visible = false;
}
private function revert():void{
imageB.visible = false;
imageA.visible = true;
}

Actionscript 3 drag and drop on multiple specific targes and change alpa for the dropped objects as well as stack targets

I have been trying to achieve three things in the project without success. I am new at this and have relied on tutorials to get this far. Here we go!!
a. I want to be able to drop label_3 and label_4 on either or targetlabel_3 and targetlabel_4 but not effect the other labels and targets.
b. I want to be able to drop label_2 on top of label_1 once it has been dropped. I am finding that when label_1 has been dropped, it hides the targetlabel_2 and label_2 can't find it's target.
c. I want to change the Alpa of each of labels _1, _2, _3, _4 and _5 to zero when they are dropped on their targets and change the Apha for labels _11, _21, _31, _41 and _51 to 100. (I have changed the Apha to 25 on these for the sake of making it easier for someone to see what I am trying to do).
I have been mucking around for days on this and have hit a brick wall.
Can anyone help please?
import flash.display.DisplayObject;
import flash.geom.Rectangle;
/* Drag and Drop
Makes the specified symbol instance moveable with drag and drop.
*/
var startX:Number;
var startY:Number;
var counter = 0;
var attempts = 0;
var rect:Rectangle;
rect=new Rectangle(100,100,700,500);
correct_txt.text=counter;
attempts_txt.text=attempts;
label_1.addEventListener(MouseEvent.MOUSE_DOWN,Drag);
label_1.addEventListener(MouseEvent.MOUSE_UP,Drop);
label_2.addEventListener(MouseEvent.MOUSE_DOWN,Drag);
label_2.addEventListener(MouseEvent.MOUSE_UP,Drop);
label_3.addEventListener(MouseEvent.MOUSE_DOWN,Drag);
label_3.addEventListener(MouseEvent.MOUSE_UP,Drop);
label_4.addEventListener(MouseEvent.MOUSE_DOWN,Drag);
label_4.addEventListener(MouseEvent.MOUSE_UP,Drop);
label_5.addEventListener(MouseEvent.MOUSE_DOWN,Drag);
label_5.addEventListener(MouseEvent.MOUSE_UP,Drop);
label_1.buttonMode = true;
label_2.buttonMode = true;
label_3.buttonMode = true;
label_4.buttonMode = true;
label_5.buttonMode = true;
function Drag(event:MouseEvent):void
{
event.target.startDrag(true,rect);
feedback_txt.text="";
event.target.parent.addChild(event.target);
startX=event.target.x;
startY=event.target.y;
}
function Drop(event:MouseEvent):void
{
event.target.stopDrag();
var myTargetName:String="target" + event.target.name;
var myTarget:DisplayObject=getChildByName(myTargetName);
if (event.target.dropTarget!=null&&event.target.dropTarget.parent==myTarget){
feedback_txt.text="Well done! You have selcted the correct label and placed it in the recommended position on the package.";
feedback_txt.textColor = 0xCC0000
event.target.removeEventListener(MouseEvent.MOUSE_UP,Drop);
event.target.removeEventListener(MouseEvent.MOUSE_DOWN,Drag);
event.target.buttonMode = false;
event.target.x=myTarget.x;
event.target.y=myTarget.y;
counter++;
correct_txt.text=counter;
correct_txt.textColor = 0x0000ff
attempts++;
attempts_txt.text=attempts;
attempts_txt.textColor = 0x0000ff
}else{
feedback_txt.text="Your attempt is not quite correct. You have either selected the incorrect label or placed it in the wrong position. Please try again.";
event.target.x = startX;
event.target.y = startY;
attempts++;
attempts_txt.text = attempts;
}
if (counter==5){
feedback_txt.text="Well done! You have correctly placed all 5 labels";
percentage_txt.text ="Based on your attempts, you have scored "+Math.round ((counter/attempts) *100)+" %";
percentage_txt.textColor = 0x0000ff
}
}
The easiest way to detect when a label is on another label is by using hittest in an enter frame event listener.
stage.addEventListener(Event.ENTER_FRAME, hit_test);
function hit_test(e:Event):void{
if (label_1.hitTestObject(targetLabel_1)) {
trace("Label_1 is hitting targetlabel_1");
label_hit();
}
if (label_2.hitTestObject(targetLabel_2)) {
trace("Label_2 is hitting targetlabel_2");
label_hit();
}
}
When the hittest is activated, the trace text is shown and the function is called. To change the alphas of the labels, use the function being called by the hittest. For example:
function label_hit()
{
label_1.alpha = 0;
label_2.alpha = 0;
label_3.alpha = 0;
}
If you are trying to have conditions to when things can be dragged, seen, or hit tested, that function is also where you can take care of them. For example, If you don't want a label to be visible until the hittest, you have the alpha set to 0 until the function sets it to 100. If you don't want a label to be drageable until then, you create the listener inside the function instead of earlier.
function label_hit()
{
label_1.alpha = 100;
label_1.addEventListener(MouseEvent.MOUSE_DOWN,Drag);
label_1.addEventListener(MouseEvent.MOUSE_UP,Drop);
}
If you want hittests to occur only after other hittests have already occured, place them in conditions and have the conditions met in the functions.
stage.addEventListener(Event.ENTER_FRAME, hit_test);
function hit_test(e:Event):void{
if (label_1.hitTestObject(targetLabel_1)) {
trace("Label_1 is hitting targetlabel_1");
label_hit();
}
if(condition)
{
if (label_2.hitTestObject(targetLabel_2)) {
trace("Label_2 is hitting targetlabel_2");
label_hit();
}
}
function label_hit()
{
var condition = true;
}

Simulate Mouse Click in AS3

I am working on an AS3 project and I am struggling with one particularly fragile part which will need a lot of refactoring in the near future. Just unit testing separate classes in isolation does not catch all issues we are running into. For example, we might forget to disable mouse events on a transparent overlay and thereby block all clicks on a button. Therefore, I am trying to write a test that simulates real user input.
I have tried to manually send a MouseEvent to the stage at the correct position:
stage.dispatchEvent(new MouseEvent(MouseEvent.CLICK, true, true, 380, 490, stage));
Since the stage has no click event handler, I expected the event to propagate through the hierarchy to the button that will actually handle it (as it does when I physically click the mouse). However, it doesn't.
I know that I could just dispatch the event on the button, but that will not detect if the object is somehow obstructed. Is there some way to simulate mouse events, such that they will properly propagate through the hierarchy?
Edit:
I managed to do it by re-implementing the propagation behavior of Flash:
Edit 2:
My previous solution didn't work if there was a partly transparent overlay with a click handler, like a Sprite with a few Shapes in it. The problem is that the hitTestPoint method returns true even if the object in question is completely transparent at that point. Therefore, I modified it to check the actual pixel value:
private function clickObject(obj:DisplayObject) : void
{
var relPos:Point = new Point(obj.width / 2, obj.height / 2);
var globalPos:Point = obj.localToGlobal(relPos);
simulateClick(obj.stage, globalPos);
}
private function simulateClick(obj:InteractiveObject, globalPos:Point) : Boolean
{
// first, check if we have any children that would rather handle the event
var container:DisplayObjectContainer = obj as DisplayObjectContainer;
if (container != null)
{
if (container.mouseChildren)
{
for (var i:int = 0; i < container.numChildren; ++i)
{
var child:DisplayObject = container.getChildAt(i);
var interactive:InteractiveObject = child as InteractiveObject;
if (interactive != null)
{
if (simulateClick(interactive, globalPos))
{
// if we have found a handler in the children, we are done
return true;
}
}
}
}
}
if (!obj.mouseEnabled) {
return false;
}
if (obj.hitTestPoint(globalPos.x, globalPos.y))
{
var localPos:Point = obj.globalToLocal(globalPos);
// check if object is visible at the clicked location
var pixel:BitmapData = new BitmapData(1, 1);
pixel.draw(obj, new Matrix(1, 0, 0, 1, -localPos.x, -localPos.y));
var color:uint = pixel.getPixel32(0, 0);
if ((pixel.getPixel32(0, 0) & 0xff000000) != 0)
{
// if yes, dispatch the click event
var e:MouseEvent = new MouseEvent(MouseEvent.CLICK, true, true, localPos.x, localPos.y, obj);
obj.dispatchEvent(e);
return true;
}
}
return false;
}
Unfortunately, there is still at least one case not covered: If the object is a mask for another object. I have no idea how to check for this, since it could be mask anywhere in the display hierarchy. I would have to traverse the whole tree and check every single display object to find this out.
So, my question remains: Isn't there an easier way to do this?
I've had issues with events in AS3 as well. I've found that the best way is to have the eventListeners added to the same object that's dispatching the events. In your case, adding the .addEventListener to the stage and sending the function as a function on a child clip. eg:
stage.addEventListener(MouseEvent.CLICK, object.object.clicked);
I hope this may help. I've used this method with success in the past.
You can use stage.getObjectsUnderPoint(new Point(pointerX , pointerY )); function , that will return You array with objects . Than remove overlay object and last instance in array should be deepest DisplayObject.
note: last instance can be graphic or such thing , so You should loop through parent objects to find nearest InteractiveObject .
Also , dont forget that parent objects can have mouseChildren = false or mouseEnabled = false