change var initialized on root time line from class in as3 - actionscript-3

hi im new on as3 and its make me so frustated since so many changes from as2. i have problem when i want to change variable value from class file, here is the detail.
on the root timeline frame 2, i initialized var like below
stop();
var gameStat;
gameStat = "stop";
then i attached penguin movie clip to the stage,
and i make class file called "penguin.as"
//untuk hero penguin
package {
import flash.display.MovieClip;
import flash.events.Event;
public class penguin extends MovieClip {
var rootref:Object = root;//mendefinisikan root dengan rootref
public function penguin() {
rootref.gameStat = "play";//change var value to play
addEventListener(Event.ENTER_FRAME,on_enter_frame);//tmbhkn enterFrame function
}//eof penguin()
private function on_enter_frame(e:Event) {
trace(rootref.gameStat);//<- test the variable here
//trace(this.parent.parent.parent.gameStat);
}
}//eof class
}
i trace gameStat var in on_enter_frame function, but it still give me "stop" result not "play".
any help how to change the "gameStat" value??

Classes should start with the first letter capitalized.
Also why make a reference to root if it can be accessed anyway. You should also trace in the constructor whether you set the value.

Related

AS3 || Using same function with different variables

I'm very new to AS3 and I'm trying to learn by experimenting in flash, by making a simple 2D farming game with very simple code.
I've made one crop field out of 6 that works, which is a movieclip with different frames for each fruit growing. For example, frame 1-5 is a strawberry growing where frame 5 is when it's ready to be picked, and then 6-10 is of carrots, etc
Is there a way for me to make it so that I don't have to write the exact same code for the next crop field, and instead change the variables in this code depending on which crop field you click on?
Here's an example of the code
if (field1.currentFrame == 1)
{
field1.nextFrame();
infoText.text = "You've watered the crop. Let's wait and see how it turns out!";
function plantStrawberry():void
{
field1.nextFrame();
if (field1.currentFrame == 5)
{
clearInterval(strawberryInterval);
}
}
var strawberryInterval = setInterval(plantStrawberry,5000);
}
pls no judgerino, as said, I'm very new to AS3, lol.
There are a few ways to go about being DRY (don't repeat yourself) with your code in this scenario. The best way, would be to learn to use classes. Classes are blueprints, and are made for these very scenarios.
Here is an example of a simple class to do what you'd like. In Flash/Animate, go to file, then new, then 'ActionScript 3.0 Class' - give it a name of Crop.
In the document that comes up, there should be some basic boilerplate code. Everything should wrapped in a package. The package tells flash where to find this class - so this example, leave it as is (just package {) and save this file in the same folder as your .fla. All functions need to be wrapped in a class declaration, this should be generated for you based off the name you put in (Crop). Next you'll see one function, that has the same name as the class. This is called a constructor, and this function runs whenever you create a new instance of this class. Since classes are blueprints, you create instances of them that are objects - those objects then get all the code you put in this class.
So, to start, you should have this:
package {
public class Crop {
public function Crop() {
// constructor code
}
}
}
Let's go ahead and put your code in. See the code comments for details:
package {
//imports should go here
import flash.display.MovieClip;
import flash.events.Event;
import flash.events.TimerEvent;
import flash.utils.Timer;
//lets make this class extend MovieClip - that means it will be a MovieClip in addition to everything else you add below
public class Crop extends MovieClip {
//instead of setInterval, use a timer - it's easier to manage and cleanup
//in class files, variables and functions have access modifiers, that's what the public and private words are about
//private means only this class can ever use the var/function
private var timer:Timer;
public function Crop() {
//initialize the timer - have it tick every 5 seconds, and repeat 4 times (to move you from frame 1 - 5)
timer = new Timer(5000, 4);
//listen for the TIMER event (which is the tick) and call the function 'grow' when the timer ticks
timer.addEventListener(TimerEvent.TIMER, grow);
}
//a function that starts the timer ticking
public function startGrowing():void {
timer.start();
}
//this function is called every timer tick.
private function grow(e:Event):void {
this.nextFrame(); //go to the next frame of your crop
}
}
}
Save the file. Now that you have this class, you need to attach it to your library assets so they all get this functionality.
In the library panel, for each of your crop objects, right click (or ctrl+click on Mac) and go to properties. In the properties, click advanced, and give it a unique class name (for instance Strawberry). Then in the base class field, put Crop (the class we just made). Repeat for the others.
Now on your timeline, when you want a crop to start growing, you can do:
field1.startGrowing(); //assuming your instance `field1` is one of the crops that you assigned the base class `Crop` to
Hopefully this gives an entry point into the power of classes. You can add more functionality into this one and it automatically apply to all the crops you attached it to.
Although BFAT's tutorial is absolutely correct, it is not the only way to do things, moreover, if you ever move from Flash and AS3 to something else, or even try Starling (a framework that allows to build fast and non-laggy mobile applications in Flash/AS3), you'll find that concept not applicable. It is very Flash-y and I applause to it though.
Instead of making each field subclass the abstract (means, it is never instantiated by itself) Crop class, you can make the Crop class take 1 of these 6 fields as an argument on creation (or later). Basically, you tell "I want to make crop field with wheat graphics". So, let me redo that class a bit.
package
{
// Imports.
import flash.display.Sprite;
import flash.display.MovieClip;
import flash.utils.Timer;
import flash.events.Event;
import flash.events.TimerEvent;
public class Crop extends Sprite
{
// I agree with the use of Timer.
private var timer:Timer;
// Visuals go here.
private var field:MovieClip;
// Class constructor.
public function Crop(FieldClass:Class)
{
// With "new" keyword you can omit ()
// if there are no mandatory arguments.
field = new FieldClass;
field.stop();
addChild(field);
}
// A function that starts the timer ticking.
public function startGrowing():void
{
timer = new Timer(5000, 4);
timer.addEventListener(TimerEvent.TIMER, grow);
timer.start();
}
// This function is called every timer tick.
private function grow(e:Event):void
{
// Command the graphics to go to the next frame.
field.nextFrame();
}
}
}
Then, the usage. When you create fields, you need to set AS3 classes to them to have access, leaving base class as is, Flash will automatically set it to non-specific MovieClip. Lessay, you have crops.Wheat field and crops.Barley field.
import Crop;
import crops.Wheat;
import crops.Barley;
var W:Crop = new Crop(Wheat);
var B:Crop = new Crop(Barley);
addChild(W);
addChild(B);
B.x = 100;
W.startGrowing();
B.startGrowing();

Actionscript 3 Multiple sounds playing at the same time when accessing different movieclips

I am at the moment trying to create an interactive movie, structured so that each keyframe in the timeline contains a movieclip navigated to using buttons inside the movieclips, with the appropriate code inside the main timeline.
The problem right now is that when you access the movieclip inside frame 3, the sound from frame 2 also plays simultaneously. After doing some research i found that this appears to be a bug with flash itself, and most of the time is dealt with using SoundMixer.stopAll();. Sadly, i have no idea how to use it to kill the sound from frame 2 when only frame 3 is accessed.
I know that when accessing frame 2 instead, only frame 2 is played, which should mean that flash basically goes through all frames on the way to the frame you are supposed to go to.
This is the limited code i am using at the moment:
Frame 1:
import flash.events.MouseEvent;
import flash.display.SimpleButton;
import flash.media.SoundMixer;
stop();
var soundVar:int = 0;
var Choice1F:SimpleButton;
var Choice1R:SimpleButton;
this.Val1.Choice1F.addEventListener(MouseEvent.CLICK, function(me:MouseEvent):void{buttonHandler(me, 2)});
this.Val1.Choice1R.addEventListener(MouseEvent.CLICK, function(me:MouseEvent):void{buttonHandler(me, 3)});
function buttonHandler(e:MouseEvent, Value:int): void{
SoundMixer.stopAll();
soundVar = Value;
this.gotoAndPlay(Value);
}
Frame 2:
import flash.media.SoundMixer;
stop();
if(soundVar == 3){
SoundMixer.stopAll();
}
Frame 3 simply contains a stop(); statement. The code in frame 2 was a futile attempt from me to make it kill the sound on its way to frame 3. Hopefully, you guys can think of a better solution, if one even exists.
The correct structure of the project assumes that you control the playback of music and sounds with a special instance of custom class. And timeline you use only gave him command when and what to do.
One SoundChannel and couple of Sound's will do the trick.
You could use this one
package src.utils{
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.net.URLRequest;
public dynamic class BackgroundMusicPlayer extends Object{
public var playlist;
public var sndChannel;
private var ID;
public function BackgroundMusicPlayer(srcList:Array){
playlist=[];
for(var i=0;i<srcList.length;i++){
var src= new URLRequest(srcList[i]);
var newSound = new Sound(src);
playlist.push(newSound);
}
}
public function playMusic(id){
if (sndChannel!=undefined) {
sndChannel.stop();
}
sndChannel = playlist[id].play();
ID=id;
sndChannel.addEventListener("soundComplete",replayListener);
}
public function replayListener(e){
sndChannel = playlist[ID].play();
}
}
}
import class to you timeline, create instance passing him files list
var musicPlayer = new BackgroundMusicPlayer("music1.mp3","music2.mp3");
And then you want start some sound, call
musicPlayer.playMusic(0);
If you want use imported to project sounds, just share them to actionscript, give them class names and slightly modify given class constructor
public function BackgroundMusicPlayer(srcList:Array){
playlist=[];
for(var i=0;i<srcList.length;i++){
playlist.push(srcList[i]);
}
}
So your instance creation now should be
var musicPlayer = new BackgroundMusicPlayer(new MySound1(),new MySound2());

Actionscript 3 passing variable from parent to child

I am still having problems so I made a very basic main.swf that loads sub.swf.
Sub.swf has 2 rectangle movieclips (red and blue) that have their alpha = 0.
Both swfs compile fine and no errors but the red alpha does not get turned on so the value of "spanish" from main.swf variable must not be getting recognised in sub.swf. Any ideas?
Main.swf actionscript:
import flash.display.Loader;
import flash.display.MovieClip;
import flash.events.Event;
var child_loader:Loader = new Loader();
var req:URLRequest = new URLRequest("sub.swf");
child_loader.load(req);
child_loader.contentLoaderInfo.addEventListener(Ev ent.COMPLETE, done);
function done(e:Event):void{
addChild(child_loader);
MovieClip(child_loader.content).language = "spanish";
}
sub.swf actionscript:
import flash.display.Loader;
import flash.display.MovieClip;
import flash.events.Event;
function callFunction(_txt:String):void{
var sublanguage = (MovieClip(parent.parent).language)
if (sublanguage == "spanish"){
red.alpha = 1;
}else if (sublanguage == "english"){
blue.alpha = 1;
}
}
The problem is that AS3 is not dynamic in the way AS2 was, so the child document needs to have a property or method that the parent knows is there.
One way to do this is relatively simple:
package {
public class ChildClass extends MovieClip {
protected var _property:String;
public function get property():String {
return _property;
}
public function set property(value:String):void {
if (value != _property) {
_property = value;
//do something, because now the property has been set
}
}
}
}
You'd just apply that as the Document Class to the swf you're loading, and then in your onLoaded function, you'd do something like:
//cast to ChildClass, so you will know you have the property available
var childClass:ChildClass = Loader(event.currentTarget).contentLoaderInfo.content as ChildClass;
//if the variable content is null, the swf had a different Document Class
if (childClass) {
//now you can set your variable, you're good
childClass.property = 'foo';
}
Like most things in Actionscript, you can do it the easy way (shown above), or you can do it the right way. The right way is to use an Interface. What an Interface is is the "blueprint" for a Class. In this case, it would need to say that any Class that implements that Interface would always have a property getter and a property setter. You can't just use a variable, because Interfaces only allow functions.
But at any rate, the advantage of using the Interface, especially in your case, is that you will not have to compile in the specific Class associated with the swf that you're loading. Instead, you'd just need to compile in the "contract" for the Class, which is much lighter weight. But because the player won't cast the contentLoaderInfo.content to ISomeInterface unless it fulfills that contract, you can confidently set that property once you have done the casting.
You should be able to set up a function in your child movie that can be called from your parent. You could pass anything you want as an argument.

What I might be doing wrong in this code?

I am relatively new to flash. I am trying to create a square grid and add it to the movie. When I open the actionscript panel by pressing F9 and when I type the following code,
var square:SquareClip = new SquareClip();
addChild(square);
Things are working fine (the squareclip is appearing in the movie).
Instead when I do this however, I deleted the above code and just create a new instance of Main,
new Main
and inside Main.as
package{
//----
//IMPORT
//
import flash.display.*;
import flash.events.*;
import flash.text.*;
//Class creation
public class Main extends MovieClip {
//PROPERTIES
public function Main():void {
layout_in_grid();
}
private function layout_in_grid():void{
trace("layout_in_grid");
//create a new Square
var square:SquareClip = new SquareClip();
addChild(square);
trace("Square added");
}
}
}
And when I run the code, my square is not coming. I am doing something wrong very basically. Please help me.
You need to add Main to the displaylist:
var myMain : Main = new Main();
addChild(myMain);
You could also set Main as your document class.
#Mattias is correct. But you should set this as the Document Class as he suggested - When you've selected the stage, in the properties there will be an input box allowing you to enter the name of the class.
If your file is in the same location as the FLA and called 'Main.as' you enter in the box:
Main
If the file is within a folder structure e.g. com/company/projects/Main.as - enter:
com.company.projects.Main
--
Kudos on learning the OOP way!

accessing GUI-created elements in a class definition file in AS3 (Flash CS4)

I've used the GUI to create a rectangle that I turned into a button symbol (SimpleButton). I then edited the button so that it had 4 different button states and a text layer on top. I then created a class definition file for this object so that I could dynamically change the label text (the text layer) when adding instances of this button to the stage.
I was able to create and link a class file (DynamicButton.as) just fine, but when I try to access the text field that I created on the button, I get the error:
"Access of possibly undefined property btnLabel through a reference with static type com.examples:DynamicButton."
when i couldn't get that to work, I decided I'd try adding the TextField directly within the class definition file, using the following code:
package com.examples
{
import flash.display.Sprite;
import flash.display.SimpleButton;
import flash.text.TextField;
public class DynamicButton extends SimpleButton
{
public function DynamicButton(btnText:String="Click Me")
{
placeText();
labelText.text = btnText;
}
//property variables
//create a text box to hold the button label
private var labelText:TextField = new TextField();
//create a displayObject to hold the text
private var labelSprite:Sprite = new Sprite();
private function placeText():void {
labelText.width = this.width;
labelText.height = this.height;
labelText.x = this.x;
labelText.y = this.y;
labelText.visible = true;
labelSprite.addChild(labelText);
this.parent.addChild(labelSprite);
}
}
}
The problem is that I can't seem to add the TextField to the SimpleButton, as it's not a display object. So, I tried adding it to the parent of the simple button (and I figured, I'd just place it exactly above the button). But then I get a "null object reference." error.
So, I have two questions
is there a way to access GUI-created elements from w/i a class definition file?
How would I add the TextField to the button using only AS3 inside of a my class definition file?
OK, it took a few days and some introspection, but it seems that the problem I've having stems from the fact that you cannot add children to a SimpleButton. I changed my class definition to extend a MovieClip, then created a function named "listen()" that I called when constructing the object that added listeners for the "over","down", and "out" mouse states, so as to imitate a simple button. I also had to put a "stop()" command in the constructor, so that each button wouldn't keep cyclying through all the states. The final class definition looks like this:
package com.examples
{
import flash.display.MovieClip;
import flash.events.MouseEvent;
public class DynamicButton extends MovieClip
{
public function DynamicButton(btnText:String="Click Me")
{
stop();
this.btnText_btn.text = btnText;
listen();
}//constructor
private function listen():void {
this.addEventListener(MouseEvent.MOUSE_OVER,function(){
gotoAndStop(2);
}//anon mouseover fcn
);
this.addEventListener(MouseEvent.MOUSE_DOWN,function(){
gotoAndStop(3);
}//anon mousedown fcn
);
this.addEventListener(MouseEvent.MOUSE_OUT,function(){
gotoAndStop(1);
}//anon mouseout fcn
);
}
}//class definition
}//package