How do you share a variable between scripts using the MovieClip variable? - actionscript-3

I'm currently trying to code an interactive timeline for my Uni project (keep in mind im a new coder) and we go over basic actionscript stuff. I was taught to communicate between scripts using a movieclip variable and declaring this.parent.
I have 3 scripts, one that controls the button that is used to move forward in the timeline, one is main, and the other controls the text box which displays the timeline. I placed a number variable in main, initialised at 0(timeCount). In the button script, i have it linked to main using refToMain, my movieclip variable. Within the button script, if the user clicks on the button, it rises the number variable from main using refToMain(refToMain.timeCount). It was my ambition to have the text box script track the number and each number has a different bit of the timeline on. However, when I trace timeCount in the button script, the number seems fine and raises accordingly, however it doesnt change the number in any other script. How can I fix this using basic as3 code?
In Main:
var timeCount:Number = 0;
In Button:
public function mDown (mDown:MouseEvent){
refToMain.timeCount += 1;
if(refToMain.timeCount >= 10){
refToMain.timeCount = 10;
}
trace(refToMain.timeCount);
In timeline:
if(refToMain.timeCount == 0){
timelineText.text = "welcome"
}
if(refToMain.timeCount == 1){
timelineText.text = "hello"
}

Are you expecting the code in your timeline to run continuously instead of just once? A frame script will only run once each time the timeline reaches that frame. And if you only have one frame, the timeline won't advance at all. If that's the case, a simple fix would be to add another frame to your timeline with F5, and then your timeline will alternate between your two frames forever so that your script on frame 1 will execute every other frame.
A better option would be to call the script that updates the timeline text directly every time the button is clicked. So you would move the code from your timeline script to your button script like this:
public function mDown (mDown:MouseEvent) {
refToMain.timeCount += 1;
if(refToMain.timeCount >= 10) {
refToMain.timeCount = 10;
}
trace(refToMain.timeCount);
if(refToMain.timeCount == 0) {
MovieClip(root).timelineText.text = "welcome";
}
if(refToMain.timeCount == 1) {
MovieClip(root).timelineText.text = "hello";
}
}

There are several ways and approaches to access objects and variables across your application.
1) Traversing. The (probably) older and the most straightforward one is fully understanding and controlling the display list tree. If you understand where your current script is and where your target script is, you just traverse this tree with root to go straight to the top, parent to go level up and getChildByName or [] or dot notation to go level down.
Pros: it's simple. Contras: The weak point of this approach is its inflexibility. Once you change the structure of display list tree, the access would presumably be broken. Also, this way you might not be able to access things that are not on the display list. Also, there are cases the dot notation would not work, and there are cases getChildByName would not work. Not that simple, after all.
2) Bubbling events. These are events that bubble from the depths of display list to the root. Mouse events are bubbling: you can catch it anywhere from the deepest object that had some mouse event then all its parents right up to the stage. You can read about them here. So, you can send bubbles from whatever depth you want then intercept them at the any parent of the event target:
// *** TextEvent.as class file *** //
package
{
import flash.events.Event;
public class TextEvent extends Event
{
static public const TEXT_EVENT:String = "text_event";
public var text:String;
// Although it is not a very good practice to leave the basic Event
// parameters out of it, but it will do for this example.
public function TextEvent(value:String)
{
// Set type = "text_event" and bubbles = true.
super(TEXT_EVENT, true);
text = value;
}
}
}
// *** Button script *** //
import TextEvent;
// Dispatch the event.
dispatchEvent(new TextEvent("welcome"));
// *** Main timeline *** //
import TextEvent;
// Subscribe to catch events.
addEventListener(TextEvent.TEXT_EVENT, onText);
function onText(e:TextEvent):void
{
// Extract the passed text value.
timelineText.text = e.text;
}
Pros: it is good in an app architecture terms. Contras: you cannot catch the bubbling event at the point that is not parent of event source.
3) Static class members. Or singleton pattern, its basically the same. You can devise a class that shares certain values and references over the whole application:
// *** SharedData.as class file *** //
package
{
import flash.display.MovieClip;
public class SharedData
{
static public var MainTimeline:MovieClip;
}
}
// *** Main timeline *** //
import SharedData;
// Make root accessible from anywhere.
SharedData.MainTimeline = this;
// *** Button script *** //
import SharedData;
// You can access main timeline via shared reference.
SharedData.MainTimeline.timelineText.text = "welcome";
Pros: you are not limited by display list structure any more, you can also share non-visual instances this way, anything. Contras: careful with timelines, they tend to destroy and create timeline instances as playhead moves, so it is not impossible to end up with a reference to a removed object while timeline holds a new instance that is no longer shared.

Related

Trying to make a button that target specific Movie Clip properties that is located within multiple layers of Movie Clips

I want to change the opacity of a specific MovieClip (named: Red_mc) within multiple layers of Movie Clips (example layer hierarchy : Character_mc > arm_mc > weapon_mc > Attribute_mc > Red_mc).
But I also have frame by frame animation within Character_mc (each containing and using the same MovieClip). I want the button to change the properties of all the Red_mc within each frame).
I've learn Adobe Animate for a while now but I've just started learning ActionScript recently,thus I'm very new in this language. Basically I'm just trying to make a somewhat "simple" character profile "page". I've tried a few method, but they have lots of limitation. Below is what I used for a single framed Movie clip
function fl_ClickToHide(event: MouseEvent): void {
Idle_mc.Idle_hw_mc.CrystalW_mc.Attribute_mc.IntelligenceW.visible = false;
Idle_mc.Idle_hw_mc.CrystalW_mc.Attribute_mc.AgilityW.visible = false;
Idle_mc.Idle_hw_mc.CrystalW_mc.Attribute_mc.StrengthW.visible = true;
}
button_7.addEventListener(MouseEvent.CLICK, fl_ClickToHide_2);
function fl_ClickToHide_2(event: MouseEvent): void {
Idle_mc.Idle_hw_mc.CrystalW_mc.Attribute_mc.StrengthW.visible = false;
Idle_mc.Idle_hw_mc.CrystalW_mc.Attribute_mc.IntelligenceW.visible = false;
Idle_mc.Idle_hw_mc.CrystalW_mc.Attribute_mc.AgilityW.visible = true;
}
button_8.addEventListener(MouseEvent.CLICK, fl_ClickToHide_3);
function fl_ClickToHide_3(event: MouseEvent): void {
Idle_mc.Idle_hw_mc.CrystalW_mc.Attribute_mc.StrengthW.visible = false;
Idle_mc.Idle_hw_mc.CrystalW_mc.Attribute_mc.AgilityW.visible = false;
Idle_mc.Idle_hw_mc.CrystalW_mc.Attribute_mc.IntelligenceW.visible = true;
}
This works btu there's lots of limitaion, eg. when there's multiple single framed within the movie clip then it wouldn't work.
My goal is to make a button that when clicked, it'll search for a specific MovieClip and then edit the the properties of the Movie Clips within it.(ie. Red, green and Blue).
TD;DR: So is there a way for the code to search for the a target specific Movie Clip across multiple layers of Movie Clips within a frames?
thanks hope what i said make sense.
Info №1. Objects that are not in the current frame do not exist at the moment (for your script, at least).
Info №2. Mixing scripts and frames is a brave thing to do. Because there's a lot of pain and suffering and misery lies ahead once you decide to go that way.
If I had a task of programming a lot of pieces spread across complicated hierarchy, I think I'd do the following.
First, I'd devise a shared data class that is available from any point of your application.
package
{
import flash.events.Event;
import flash.events.EventDispatcher;
public class AppData
{
static public const D:Object = new Object;
static public const E:Event = new Event(Event.CHANGE);
static public const I:EventDispatcher = new EventDispatcher;
static public function has(key:String):Boolean
{
return D.hasOwnProperty(key);
}
static public function read(key:String):*
{
return D[key];
}
static public function write(key:String, value:*):void
{
if (value === null)
{
delete D[key];
}
else
{
D[key] = value;
}
I.dispatchEvent(E);
}
}
}
Now, if you want certain clip to behave in a certain way, without actually knowing, where this clip is on your app hierarchy might be. For example, you want to control its alpha-transparency. On the first frame of this clip you do:
import AppData;
import flash.events.Event;
// The last argument is important, because timeline objects are
// auto-removed if their parent's timeline instructs so, thus
// you won't be able to locate them and unsubscribe, which,
// in turn, means they will hang in the memory forever.
// Still, if you subscribe them with useWeakReference
// set to true, they will be removed normally
// and unsubscribed automatically.
AppData.I.addEventListener(Event.CHANGE, onChange, false, 0, true);
// Call once in order to forcibly sync the object with the data.
onChange(null);
function onChange(e:Event):void
{
if (AppData.has("red.alpha"))
{
alpha = AppData.read("red.alpha");
}
else
{
alpha = 1;
}
}
Then, once you execute the following instruction, each and every object, watching the red.alpha setting will change its alpha:
import AppData;
AppData.write("red.alpha", 0.3);
The setup above is very primitive, and, probably, can be improved in a number of ways, but that greatly depends on understanding of what you are building there, which I don't have.

Custom Event Listener not working

I am new to as3 and I recently saw creation of custom events in as3 in a tutorial and I wanted to incorporate in my game. When i did the tutorial, it all seemed well for that project. But it doesnt seem to work with the new project.
Here is my code :
package
{
import flash.display.MovieClip;
import flash.utils.Timer;
import flash.events.TimerEvent;
public class FashionFrenzy extends MovieClip
{
public var Buyer_mc:Buyer;
public var Buyers:Array;
public var gameTimer:Timer;
public function FashionFrenzy()
{
GameTimeController();
GenerateBuyers();
addEventListener(ReachMallDoorEvent.CHECK, OnReachMallDoor);
}
public function GameTimeController()
{
gameTimer = new Timer( 25 );
gameTimer.start();
}
public function GenerateBuyers()
{
Buyers = new Array ;
Buyer_mc = new Buyer(533.2,0) ;
addChild(Buyer_mc);
gameTimer.addEventListener( TimerEvent.TIMER, BuyerEnter );
if(Buyer_mc.y==377.25)
{
dispatchEvent( new ReachMallDoorEvent( ReachMallDoorEvent.CHECK ) );
}
}
public function BuyerEnter(event:TimerEvent)
{
Buyer_mc.Enter();
}
public function OnReachMallDoor(event:ReachMallDoorEvent)
{
trace("my timer starts now");
}
}
}
Here, OnReachMallDoor never seems to run because there is something wrong. I cant see the output saying "My timer starts now". But there is no error in the code and output doesnt show any runtime errors either. Where have I gone wrong? I want OnReachMallDoor function to run when my y coordinate is in desirable position and the event is dispatched.
The order of commands is wrong.
GenerateBuyers();
addEventListener(Rea...
The first line of these two is the one that could potentially cause the Event to be dispatched. But only after that will you start listening for it. That's simply too late. You have to start listening before the Event is dispatched.
The probability of the Event to be dispatched is very low.
Buyer_mc.y==377.25
Checking floating point values for equality is often not a good idea. It could easily be just slightly off due to rounding errors etc. If this .y property was controlled by the mouse for example, you'd have to position the mouse at exactly that position, which is very unlikely.
You only dispatch the Event at the beginning.
GenerateBuyers();
That function is only called once. The .y position is evaluated.
This only happens once and never again.
But the .y position is subject to change and the condition should be evaluated again, which doesn'T happen
The structure is not helpful.
It doesn't make much sense for an object to listen for its own Events. Simply call the function and be done with it.
Events are for communication between objects.
How this is supposed to be:
The point of the custom Event is to be notified about something.
You want to be notified when this condition
Buyer_mc.y==377.25
is true.
If you are evaluating that condition the way you do it now, then there's no point in receiving a notification about the result thereof. You have it already.
Instead, Buyer_mc should dispatch the Event. The condition should be checked in Buyer class.
What the code looks like
some snippets pointing out what the above means, code untested:
class Buyer
override public function set y(value:Number):void
{
if (value == 377.25)
dispatchEvent(new ReachMallDoorEvent(ReachMallDoorEvent.CHECK)); // I'm at the position, I send out the notification
super.y = value;
}
class FashionFrenzy
buyer = new Buyer(533.2, 0); // variables should start with small letter
buyer.addEventListener(ReachMallDoorEvent.CHECK, OnReachMallDoor);
If you now set the .y position to the value, the object will dispatch the Event. It will figure that out on its own.
Letting the object figure something out on its own and just receive a notification about it is the main reason to use custom events.

AS3 Accessing frame without using gotoAndStop

Is there a way to access the image data (children/transformation info) of a particular frame in a MovieClip without having to use the gotoAndStop methods.
These methods are part of a rendering pipeline, all I want is access to the data, not to start a chain of asynchronous events that were developed to render things on screen, call multiple event listeners and execute frame actions.
Not only can you not do that, but gotoAndStop() doesn't even immediately make that data available. The contents of a frame aren't code accessible until the FRAME_CONSTRUCTED Event is dispatched when that frame is reached, so what you would actually have to do is more like:
var lastFrame:int = currentFrame;
function ready(e:Event):void
{
if(currentFrame !== lastFrame)
{
// In this example, frame 15 is where some image
// data we want is.
if(currentFrame === 15)
{
// Get image data.
//
}
lastFrame = currentFrame;
}
}
addEventListener(Event.FRAME_CONSTRUCTED, ready);
Needless to say; storing data you need across frames is not a viable way to structure an application.

How can I give flash stage instances unique properties in Flash Professional to pass to AS3 script?

I've started building a rough game engine framework in Flash Professional and I'm curious how I can create objects in the Flash library that I'm able to drag onto the stage and assign properties that are accessible from AS3.
Example:
I want to create a switch object (e.g. a light switch), so that when the player interactes with it, it triggers something specific in code such as a light in the room turns on.
I understand that Flash has built in UI components that you can define properties within the Flash Professional environment (see image below), and I'm wondering if there's a way to create my own custom style components so that I can essentially have my level file open in flash (.fla) and then drag a switch component from my library, and type in some information such as what light it is controlling, and any other information I want.
(above is an example of the type of parameter control I'm looking for)
I've read a bit about extending the flash UIComponent class but I feel that that's not the right approach because it's overkill for what I want. All I want is to pass some basic parameters from a library stage instance into AS3. I do not want to pass data via the instance name because this seems very messy if I want to have more complex interaction.
Thanks!
I would create a "switch" movie clip and export it to actionscrip, same with a "light" movie clip. The in the main class .as file I would inset them into the stage, using addChild (clips) and then add a click listener to the "switch" movie clip to control the "light".
This can be easily done.
Component(s) are wrong approach in my opinion.
Firstly you would want to setup Actionscript linkage / label your Library item.
In Library Panel.
- Right Click on "yourMC" >> click "Properties".
- In Properties dialog Tick "Export for Action Script"
- Then Name your Class eg "yourMC_Class"
now MC is ready to be referenced in your code.
next you would want to Dynamically add your "yourMC" from library to stage.
which can be done like such.
// first reference library item
var yourMC_ref:yourMC_Class = new yourMC_Class();
// Then load dynamic mc item into var
var your_MC_OBJ = yourMC_ref;
// then add your MC to stage.
this.addChild(your_MC_OBJ);
your_MC_OBJ.x = 200;
your_MC_OBJ.y = 100;
in a nutshell that's how I add library items to stage.
Obviously thats the basic function / code.
In a project I would have all code in an external class, in which case you would just set vars as public vars
public var yourMC_ref:yourMC_Class = new yourMC_Class();
public var your_MC_OBJ = yourMC_ref;
and the last 3 lines of code into a public function
public function ADD_First_MC()
{
this.addChild(your_MC_OBJ);
your_MC_OBJ.x = 200;
your_MC_OBJ.y = 100;
}
Now 'your_MC_OBJ' can be used in more complex ways.
eg. to create a light switch there are many options depending on how you need to approch functionality.
eg. Apply a different MC library item to "your_MC_OBJ"
play specific frame within MCs.
However If it was me I would just use mouse function to switch light on or off using addChild removeChild.
eg.
public var LightON = 0;
public var yourMC_ref:yourMC_Class = new yourMC_Class();
public var your_MC_OBJ = yourMC_ref;
then create a public function that handles on / off events
public function LightON_OFF()
{
if(LightON == 1)
{
this.addChild(your_MC_OBJ);
your_MC_OBJ.x = 200;
your_MC_OBJ.y = 100;
}
if(LightON == 0)
{
this.removeChild(your_MC_OBJ);
}
}
Hope this helps.
So, for what you want, while it may not be the best way to do what you want, I understand it's your experience you are constructing.
Use components, yes...in the following way (the most simple one):
Create a Movie Clip
Right-click it in library
Click on "Component Definitions"
Add a property, set a name, a variable name (var test, for this matter) and a default value
Click OK
Open your movie clip
Open code for the first frame and declare the variable without an initial value (var test:String;)
Trace it's value ( trace( test ); )
Go back to the stage root
Drag and drop the item from library to stage
Test it (Cmd/Ctrl + Enter) (maybe it will print null, dunno why, it ignores the default value sometimes)
Select your component on stage
Open the properties panel (Windows > Properties)
Go to Component Parameters on this panel and change the property value
You should see the value traced on console
And, I think, like this you can use properties from components for what you want, like using a String and getting the controlled mc by its name.
Good luck
I think what people are trying to say is that you can have the whole thing is data driven, and so you can combine the IDE with the data to come up with your final game.
But consider this ... it might be what you want.
If you have, for instance, a BaseSwitch Class:
public Class BaseSwitch extends MovieClip {
private var _lightName:String;
private var _light:Light;
public function get lightName():String {
return lightName;
}
public function set lightName(value:String):void {
if (value != _lightName) {
_lightnName = value;
//Note I don't advocate having children reach into their parents like this,
//but you sound like you don't want the parent involved in the process, so
//this is one way you could do it.
if (parent.hasOwnProperty(lightName) && parent[lightName] is Light) {
_light = parent[lightName];
} else {
trace('Could not find light', _lightName);
}
}
}
//other code to listen for gestures and operate the light
}
Now, when you want a switch to operate a specific light name, create a library instance and set its base class to BaseSwitch. When you close the dialog where you set the base Class, you'll notice that it gives you a dialogue that it couldn't find the Class in the Class path and one will be generated. You're going to replace it with a Class that sets the lightName. Create a new AS3 Class in the root directory with the same name as your library instance. It should look something like this:
public class SpecificSwitch {
public function SpecificSwitch() {
super();
lightName = 'theSwitch';
}
}
Other possible choices involve having the parent Class match up instances of switch with instances of light based on name, so if it finds a light1 and a light1Switch, it either gives a reference to the light to the switch or it simply sets up a mapping in its own event listening system.

calling function in a runtime loaded swf

Using AS3 / Flash CS4
Alright thanks to everyone who is reading this. My problem specifically I am designing a user interface with controls on it. 5 buttons rotate left, rotate right, zoom in, zoom out, and auto rotate. The interface in itself works fine, I can trace out button clicks, toggle the auto rotate button etc...
My program reads an xml file. Loads some images, fills an array with links for each image, and when the image is clicked a loader loads a swf from a URL and displays it on screen. No problem.
Now I originally had the zoom controls user interface in the runtime_loaded.fla library, and the mouse listeners in the same linked document class. The interface works with the movieClip in runtime_loaded.swf when it is in the same code.
Now to practice good coding, I decided to remove the UI from the runtime_loaded.fla and add it to the Main.fla. This is essential because the main is going to handle possible 100's of images/objects that each have their own unique swf to link too. If I decide to change out the look of the interface but leave the function calls the same, I could essentially put in as many as I want into the main.fla instead of the runtime_loaded.fla which I would have to do for every single object.
THE FILE STRUCTURE
Main.fla <- interface in the library. 5 mouse event functions. Inside each function calls
a property of loaded_swf (loaded_swf.rotateLeft, loaded_swf.rotateRight) etc...
Runtime_loaded.fla <- links specificObject.as in properties (AS3/CS4)
specificObject.as <- has 5 public static functions{ rotateRight, rotateLeft, zoomIn, zoomOut, toggleAutoRotate }
THE CODE
//showFlashBox
function showFlashBox(temp_string:String):void {
if(!flash_box_on){
var temp_top:uint = numChildren;
addChildAt(FlashBoxContainer,temp_top);
newXButton.addEventListener(MouseEvent.CLICK, flashBoxXClick);
1. addChild(new_loader);
2. var url:URLRequest = new URLRequest(temp_string);
new_loader.x = 0;
new_loader.y = 0;
new_loader.load(url);
3. new_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, gotSwf);
flash_box_on = true;
}
}
function gotSwf(e:Event){
4. //loaded_swf = e.target.content;
trace(e.target.content);
5. new_zoom_controls.button_minus.addEventListener(MouseEvent.CLICK, zoomOutFunction);
new_zoom_controls.button_plus.addEventListener(MouseEvent.CLICK, zoomInFunction);
new_zoom_controls.button_left.addEventListener(MouseEvent.CLICK, rotateLeftFunction);
new_zoom_controls.button_right.addEventListener(MouseEvent.CLICK, rotateRightFunction);
new_zoom_controls.button_rotate.addEventListener(MouseEvent.CLICK, toggleRotateFunction);
function rotateRightFunction(e:MouseEvent){
6. //loaded_swf.rotateRight();
}
function rotateLeftFunction(e:MouseEvent){
//loaded_swf.rotateLeft();
}
function zoomInFunction(e:MouseEvent){
//loaded_swf.zoomIn();
}
function zoomOutFunction(e:MouseEvent){
//loaded_swf.zoomOut();
}
function toggleRotateFunction(e:MouseEvent){
//loaded_swf.toggleAutoRotate();
if(new_zoom_controls.button_rotate.currentFrame == 1){
new_zoom_controls.button_rotate.gotoAndStop(2);
}
else new_zoom_controls.button_rotate.gotoAndStop(1);
}
new_loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, gotSwf);
}
If you follow steps 1-6 you see my steps of loading the .swf, mouse event listeners and click handlers, then the object call of the var loaded_swf:Object;
public static function rotateLeft():void
{
object.yaw(animation_turning_speed);
}
public static function rotateRight():void
{
object.yaw(-animation_turning_speed);
}
if I run the main.fla and try to click the buttons. This happens.
ReferenceError: Error #1069: Property rotateRight not found on
ThreedsKU39web and there is no default value. at MethodInfo-82()
ReferenceError: Error #1069: Property rotateLeft not found on
ThreedsKU39web and there is no default value. at MethodInfo-83()
I actually stumbled upon the answer before I finished submitting this as I went through the code to copy it. But after spending a few hours of frustrating moments on this last night, I will post it to ensure the next guy doesn't meet the same demise.
The answer was in the function of the runtime-loaded swf class.
public static function rotateRight():void
{
object.yaw(-animation_turning_speed);
}
It turns out it only needs to be public function instead of public static.
Mainly human error as after the file was working, I attempted to copy the code over to all my other object files, and somehow static got back in there and messed it up. So for future reference when loading in the external swf, public function should do the trick. *Note that many of my variables were returning errors until being declared as public static though.