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.
Related
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.
My goal is:
define a subClass of Sprite called Ship
use an event at runtime to call a function within this new class
It seems that I've figured out how to create my Ship class using a package in a linked .as file. But I can't seem to access the function within that class. Can anyone see what I'm doing wrong?
var ShipMc:Ship = new Ship();
addChild(ShipMc);// This successfully adds an instance, so I know the class is working.
addEventListener(MouseEvent.CLICK, ShipMc.addShip);//But this doesn't seem to run the function
This code works fine for instantiating a Sprite, but the code in the Ship.as file, specifically the function, is not working. No runtime errors, but nothing traced to the output window, either.
package
{
import flash.display.Sprite
public class Ship extends Sprite
{
public function addShip():void
{
trace("running addShip function")
}
}
}
The last time a coded anything in flash it was AS2!
I'll just mention that I've tried using addShip():void and just addShip(). Same response with both. It should be with :void, right? Anyway, the fact that neither one throws, tells me that this section of code isn't even getting read, I think.
Any help is much appreciated! Pulling my hair out.
Your code is not working because it contains some problems, so let's see that.
You should know that you are attaching the MouseEvent.CLICK event listener to the main timeline which didn't contain any clickable object yet now (it's empty), so let's start by adding something to your Ship class to avoid that :
public class Ship extends Sprite
{
// the constructor of your class, called when you instantiate this class
public function Ship()
{
// this code will draw an orange square 100*100px at (0, 0)
graphics.beginFill(0xff9900);
graphics.drawRect(0, 0, 100, 100);
graphics.endFill();
}
public function addShip():void
{
trace("addShip function run");
}
}
N.B: You can attach the MouseEvent.CLICK event listener to the stage, which will work even if you have nothing in the stage.
Now, if you test your app, you'll get an clickable orange square at the top left corner of your stage, but the compiler will fire an error (ArgumentError) because it's waiting for a listener function (the Ship.addShip() function here) which accept an MouseEvent object.
So to avoid that error, your Ship.addShip() function can be like this for example :
public function addShip(e:MouseEvent):void
{
trace("addShip function run");
}
Then your code should work.
You can also simplify things by using another listener function in your main code which can call the Ship.addShip() function, like this for example :
var ShipMc:Ship = new Ship();
addChild(ShipMc);
addEventListener(MouseEvent.CLICK, onMouseClick);
function onMouseClick(e:MouseEvent): void
{
ShipMc.addShip();
}
For more about all that, you can take a look on AS3 fundamentals where you can find all what you need to know about AS3.
Hope that can help.
I'm making a quiz type animation for work where on clicking an answer it plays a short animation FLV file relating to what you picked. As everything I read points towards AS3 being OOP I decided to make a MovieClip containing an FLV player and linked it to an AS3 file called FLV_Player.as. That way I can create a new instance of the FLV_Player everytime I need to play a video. Here is the code in that file which seems to work fine:
package
{
import fl.video.VideoEvent;
import flash.events.VideoEvent;
import flash.display.MovieClip;
public class FLV_Player extends MovieClip
{
public function FLV_Player(NextVideo:String)
{
animation_player.source=(NextVideo);
animation_player.addEventListener(VideoEvent.COMPLETE, vcompleted);
}
private function vcompleted(e:VideoEvent):void
{
nextFrame();
}
}
}
Now in the DocumentClass.as file I have this code:
private function NewVideo(videoname:String)
{
var nextvideo:FLV_Player = new FLV_Player(videoname);
addChild(nextvideo);
nextvideo.x = 0;
nextvideo.y = 0;
}
So when you click a button, go to the next frame or whatever the prompt is, it calls the NewVideo function and passes the name of whatever video is to be played next.
NewVideo("Introduction.flv");
Now I'm sure I'm going to run in to other issues later down the line as I really have no idea whether anything I've done is how it should be done, but the only issue I seem to be having at this point in time is removing the video and going to the next (or previous) frame to answer another question. I tried:
nextFrame();
removeChild(newVideo);
But it didn't work. Well, it may have gone to the next frame but with the video taking up the whole window it's hard to see if it did or not.
So how do I remove the video I've created? The main issue seems to be that because I had to create a new instance of the FLV_Player class in a private function the child is defined locally "var", rather than "public" or "private" var so I can't reference it again. It tells me that you can only create a "private var" from within the document class but if I make it there it will create the class on load rather than from the function when I'm ready to pass the video name parameter to it. At load I don't know what video I need it to play?
removeChild() must be called from the same object in which it was added. In this case, your DocumentClass. What you're trying to do now is telling an FLV_Player to remove itself, which won't work due to several reasons and bugs in your code.
The correct way to do things would be to have the FLV_Player object dispatch a custom event that your DocumentClass listens for. You need to create a new class which inherits from Event to create your custom event. I'd call it "PlayerEvent". In DisplayClass function you'd do this:
nextVideo.addEventListener(PlayerEvent.PLAYBACK_FINISHED, onPlaybackFinished);
addChild(nextVideo);
Then you need to create the onPlaybackFinished method:
private function onPlaybackFinished(event:PlayerEvent):void {
nextVideo.removeEventListener(PlayerEvent.PLAYBACK_FINISHED, onPlaybackFinished);
removeChild(nextVideo);
}
Inside the FLV_Player class, the vcomplete function should change to:
dispatchEvent(new Event(PlayerEvent.PLAYBACK_FINISHED));
Alternately, you could pass a pointer of the DocumentClass to the FLV_Player object, but this is very messy, can cause serious problems and not at all in the spirit of OOP. But it's a quick fix if you want to be lazy.
Events are an extremely important part of Actionscript 3 and I recommend you read up on them. Here's some good references:
http://www.adobe.com/devnet/actionscript/articles/event_handling_as3.html
http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/WS5b3ccc516d4fbf351e63e3d118a9b90204-7fca.html
http://www.blog.mpcreation.pl/actionscript-3-0-basics-custom-events-part-1/
I think you're right that your first problem is simply how to reference the new video, so to expand on my comment a bit: You can declare a variable without also assigning a value, so you don't need to have var nextvideo within your NewVideo function. With a class level variable instead, you can then reference whatever you set nextvideo to when you want to remove the video:
public class DocumentClass {
private var nextvideo:FLV_Player;
private function NewVideo(videoname:String)
{
nextvideo = new FLV_Player(videoname);
addChild(nextvideo);
}
private function removeVideo():void
{
removeChild(nextvideo);
nextvideo = null;
}
}
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.
I am building your standard slideshow flash header for a web page.
There are three main parts:
The Slideshow class
A controller class that is used as the projects Document Class
Some linking timeline code.
The slideshow class has all the functionality, so I used the Document class to create a new instance of the slideshow and keep a property variable called slideshow that keeps a reference the Slideshow instance.
import flash.display.MovieClip;
import flash.events.Event;
public class Header extends MovieClip
{
public var slideshow:Slideshow;
public function CSYC_Header()
{
var picturesURL:String = "images/pictures.xml";
var picturesURLFVar:String = root.loaderInfo.parameters.pictures;
picturesURL = picturesURLFVar ? picturesURLFVar : picturesURL;
slideshow = new Slideshow(picturesURL, Slideshow.FADE);
slideshow.init();
addChild(slideshow);
}
public function hello():void{trace("Hello");}
}
My next step now is to use Adobe Flash Professional to draw some play and stop buttons, and then link thier click events to call slidshow.play()/.pause(). This code is just place in the timeline:
import flash.events.MouseEvent;
pause_control_btn.addEventListener(MouseEvent.CLICK, pauseClicked);
play_control_btn.addEventListener(MouseEvent.CLICK, playClicked);
addChild(pause_control_btn);
addChild(play_control_btn);
function pauseClicked(e:MouseEvent):void
{
//the play and pause buttons are on the stage and have the following names as
// thier instance names: pause_control_btn, play_control_btn
pause_control_btn.alpha = 0;
play_control_btn.alpha = 0.37;
slideshow.pause();
}
function playClicked(e:MouseEvent):void
{
pause_control_btn.alpha = 0.37;
play_control_btn.alpha = 0;
slideshow.play();
}
Despite me being able to call regular methods that are in the Doc Class from the timeline, I can not call properties without the following error, for example when i say slideshow.play():
1061: Call to a possibly undefined method play through a reference with static type com.example.test:Slideshow.
So am I missing something obvious, or will I have to make a method on my document class every time I want to wire an event to call an object in my Document Class?
There's no need to put that code for the buttons on the timeline; you can reference those objects by their instance names from within the Document Class. That'd be the easiest solution, avoiding the timeline altogether, I think.
Otherwise possibly a call to parent.slideshow, or root.slideshow (though I think root is AS2, I don't quite remember) would give you access to that instance from the timeline. The former option is probably still the better option, and keeps your code in one place.
Hope that helps.