How to convert a two-file preloader and game swf with Document class into single swf? - actionscript-3

I have a game that I've made that runs in two files, one the preloader and one the swf. When I use these two files together they work fine, however some flash portals require only one SWF, so I'm trying to find the shortest-path way to convert it to use only one.
The game swf uses a Document Class that includes the package, class constructor, and tons of functions, and runs fine. I have lots of movieclips on the stage that I refer to in my code. The code is something like this (abridged):
package {
import flash.display.MovieClip;
import flash.events.MouseEvent;
...
public class Game extends MovieClip {
var EnemyArray:Array;
var HeroArray:Array;
...
public function Game() { // class constructor
EnemyArray = new Array();
addEventListener(Event.ENTER_FRAME,onEnterFrame);
mainRestartButton.addEventListener(MouseEvent.CLICK, RestartLevel);
...
}
public function KeyPressed(event:KeyboardEvent):void
{
if (event.keyCode == Keyboard.SPACE)
{
attack();
...
}
} // End of class
} // End of package
I also have another swf, my preloader, which has the following code in frame 1:
import flash.display.Loader;
import flash.net.URLRequest;
import flash.events.ProgressEvent;
import flash.events.Event;
var myLoader:Loader = new Loader();
myLoader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, onLoading);
var myURL:URLRequest = new URLRequest("Game.swf");
myLoader.load(myURL);
myLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
function onLoading(event:ProgressEvent):void {
var loaded:Number = event.bytesLoaded / event.bytesTotal;
percent_txt.text = "Loading: " + (loaded*100).toFixed(0) + "%";
}
function onComplete(event:Event):void {
myLoader.contentLoaderInfo.removeEventListener(ProgressEvent.PROGRESS, onLoading);
myLoader.contentLoaderInfo.removeEventListener(Event.COMPLETE, onComplete);
this.addChild(myLoader);
}
I've been through the gamut of solutions online and none of them seem to work with the way that my code is set up, such as: How to create Preloader in AS3
I tried his option 1 by taking all the code in my Document Class other than the package and constructor and pasted it into frame 2. However it returns "Error #1009: Cannot access a property or method of a null object reference." for any references to items on the stage, such as:
mainRestartButton.addEventListener(MouseEvent.CLICK, RestartLevel);
As he mentions, "This works well if you organized your project in a way that the most of the assets (images, etc) are in the Flash IDE Library and are not loaded on the first frame (you can check that in each library item's properties)." My project is NOT organized in such a way.
What is the easiest way for me to reorganize my FLA/code so they can have a preloader in a single swf?
Thanks so much for your time and help!

I use FlashDevelop (no FLAs) so I'm not sure if it'll work for you but I use the solution described here.

Related

Adobe Flash Builder: How to make a SWF with a progress bar for loading itself?

I have an AS3 project that has all its assets embedded using the [Embed] metadata tag because I would like the resulting SWF to be completely self-contained for the sake of portability on the internet.
Problem:
The file size is rather large, and I would like a progress bar to be displayed as it loads itself instead of a blank screen until it's completely finished. I can already achieve this with Adobe Animate (Flash Professional) by having a timeline with a light frame 1 and a heavy frame 2 which has a MovieClip that embeds the bulk of the assets.
I'm trying to switch over to Adobe Flash Builder, which has no IDE timeline, but I'm at a loss on how to do the same thing as the Flash IDE. Does anyone have any ideas on how to accomplish this?
Option №1. The one I'd picked, because it's easier to comprehend: external loader. A lightweight SWF with the only purpose of displaying some preloading info like % or progress while loading the heavyweight main module.
Option №2. There's a certain metatag that allows you to emulate that frame 1 preloader behavior. Keep in mind that is is not supported by ASC2.0 compiler (AIR SDK, I assume) but only by ASC1.0 compiler (Flex SDK). Flash Builder is a descendant of Flex Builder, so it's fine, I guess, but if it does not work for you, the first thing you should check is the compiler version your Flash Builder is packed with.
So, your main (the one you set as the document class in the settings) class should have that one metatag:
package
{
import flash.events.Event;
import flash.display.Sprite;
// Brace for the magic impact.
[Frame(factoryClass="Preloader")]
public class Main extends Sprite
{
public function Main()
{
// This is important, because at the moment of creation
// the instance is not attached to the stage.
if (stage) onStage(null);
else addEventListener(flash.events.Event.ADDED_TO_STAGE, onStage);
}
private function onStage(e:Event):void
{
removeEventListener(flash.events.Event.ADDED_TO_STAGE, onStage);
// This is the entry point of your actual application.
// The rest of the class goes normally from this point on.
Then, the mentioned preloader class. Its name should qualify exactly as mentioned in the metatag above.
package
{
import flash.display.DisplayObject;
import flash.display.MovieClip;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.ProgressEvent;
import flash.utils.getDefinitionByName;
// This class represents the multi-framed main timeline
// thus it should subclass the basic MovieClip.
public class Preloader extends MovieClip
{
public function Preloader()
{
// Subscribe to all necessary points to monitor the loading.
addEventListener(Event.ENTER_FRAME, onFrame);
loaderInfo.addEventListener(IOErrorEvent.IO_ERROR, ioError);
loaderInfo.addEventListener(ProgressEvent.PROGRESS, onProgress);
}
private function ioError(e:IOErrorEvent):void
{
// Handle loading errors here.
}
private function onProgress(e:ProgressEvent):void
{
// Display loading progress here.
// Use e.bytesLoaded and e.bytesTotal values
// to calculate the % loaded and the overall loading progress.
}
private function onFrame(e:Event):void
{
// When the loading is finished, the main timeline,
// represented by Preloader class moves to the frame 2.
if (currentFrame == totalFrames)
{
stop();
onComplete();
}
}
// This method concludes the loading,
// cleans up the preloader itself
// and instantiates the Main class.
private function onComplete():void
{
removeEventListener(Event.ENTER_FRAME, onFrame);
loaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, ioError);
loaderInfo.removeEventListener(ProgressEvent.PROGRESS, onProgress);
// So, here's the thing. You don't import the Main class
// because if you import it, then it will be embedded into
// the Preloader, then it must be loaded before Preloader
// can be initialized, which kind of fails the whole idea.
// Thus, you don't import the Main class but obtain it
// via the other means, like the "getDefinitionByName" method.
// Again, the fully qualified class name is to be provided.
var aMain:Class = getDefinitionByName("Main") as Class;
stage.addChild(new aMain as DisplayObject);
// Remove this instance as it no longer needed for anything.
parent.removeChild(this);
}
}
I found a solution that works with ASC2 / AIR SDK. Although his example preloader extends Sprite, and I believe you need to extend MovieClip to make it work, since you need a frame 2. And you need a gotoAndStop(2) once it has finished loading itself. Additional information here. Man, it's not a good sign when all your reference links go through web.archive.org!

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());

AS3 URLRequest Local File on multiple OS/Browsers

Greatings!
I have yet another question concerning the loading of a .swf inside a existing one.
In my game. I have a introduction screen in which I load another .swf (which is a movie).
This all works fine and the URLRequest is this:
request = new URLRequest("Movie.swf");
As I said, this works fine. However when I copy my game.swf and movie.swf to a USB stick.
(I put them in the same directory to prevent other issues).
It doesn't seem to find the movie.swf.
Now I know that it has to do with the path given in the URLRequest and/or the publish settings. But I do not know how to make this so that it searches in the same directory
as the game.swf is in.
I hope you guys have an answer for this issue.
Thanks in advance,
Matti.
Matti, I believe Lukasz's comment is correct about it being a security error.
You can avoid this security error by embedding Movie.swf instead of using a Loader. If you do this, then at compile-time the Movie.swf needs to sit next to the Game.as file, and it will be included in the Game.swf (no need to deliver both files, just Game.swf).
The syntax is:
package
{
import flash.display.Sprite;
public class Game extends Sprite
{
[Embed(source="MyMovie.swf")]
private var myMovieClass:Class;
private var myMovie:DisplayObject;
public function Game():void
{
myMovie = new myMovieClass();
// Technically myMovie is now a Loader, and once
// it's loaded, it'll have .content that's a
// MovieClipLoaderAsset, and .content.getChildAt(0)
// will be your MyMovie.swf main timeline.
}
}
}
Alternately, if you embed it as mimeType="application/octet-stream", you can get the bytes of the SWF and use it in your existing Loader's .loadBytes() method:
package
{
import flash.display.Sprite;
import flash.utils.ByteArray;
public class Game extends Sprite
{
[Embed(source="MyMovie.swf", mimeType="application/octet-stream")]
private var movieBytes:Class;
private var myMovie:DisplayObject;
public function Game():void
{
// Treat this loader the same as your current loader,
// but don't call .load(url), call loadbytes():
var l:Loader = new Loader();
l.loadBytes(new movieBytes() as ByteArray);
}
}
}

Accessing variables of of loaded swf

I'm loading swf externally. I need to access it's methods and variables. This is the code I'm using:
import flash.display.Loader;
import flash.net.URLRequest;
import flash.events.Event;
import flash.display.MovieClip;
var loader:Loader;
var req:URLRequest = new URLRequest("aaa.swf");
var mc:MovieClip;
createLoader();
function createLoader():void {
loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.INIT, onSWFLoad);
loader.load(req);
}
function onSWFLoad(e:Event):void {
mc = MovieClip(loader.content);
addChild(mc);
trace(loader.content)
}
EM is loaded SWF's document class. If questioned if variable gameEnded is declared publicly, the answer is yes. The game loads, but there is an error when calling the variable :
ReferenceError: Error #1069: Property gameEnded not found on EM__Preloader__ and there is no default value.
at main_fla::MainTimeline/onSWFLoad()
Check your preloader setting in the ActionScript settings panel of your game.fla - EM__Preloader__ looks to me like an auto-generated class (and not your main EM class).
Also, your EM class should implement a simple interface that you can cast your loader.content to (instead of MovieClip). This way, you can be sure that the methods you need are available, and your code is type-safe. Embed this interface into your preloader.fla instead of the EM class to prevent unnecessary bloating of your SWFs.
It turned out that I had to embed all the code in SWF (I was using some TLF in it)

flash-as3 code execution order: accessing a flash pro instance after gotoAndStop()

this is a follow up question to this question
i have never actually got the flash-actionscript code execution order.
in flash pro i have an instance of a moveiclip on stage in frame one named tree1 and on frame 3 i have on the stage tree3.
in the document class i have this code:
stop();
var scaleFactor:Number = tree1.scaleX;
gotoAndStop(3);
tree3.scaleX = scaleFactor;
while this works when testing on the desktop, this app will go mobile at the end
is this the correct way to go or should i register for a frameComplete event before accessing instances on a certain frame
So, just dont use the Document Class cause you will need to declare all the scenes from the beginning and will be complicate to manage each scene.
I suggest you to instance a simple MovieClip linked with his own class like the example SceneTree, put it on each Keyframe. You will have more control when you are entering or exiting each frame.
package {
import flash.display.MovieClip;
import flash.events.Event;
public class SceneTree extends MovieClip {
public function SceneTree() {
super();
this.addEventListener(Event.ADDED_TO_STAGE, Init);
this.addEventListener(Event.REMOVED_FROM_STAGE, removed);
}
protected function Init (event:Event):void{
trace("added")
}
protected function removed (event:Event):void{
trace("removed")
}
}
}
waiting for Event.FRAME_CONSTRUCTED is the correct way whenever accessing assets on a timeline
it insures all assets have been created