I'm trying to set up communication between three swfs loaded into separate broswer windows (all opened by the initially opened page) using LocalConnection. I'm new to inter-swf communication, and am going by the tutorial found here.
I'm running these from a xampp server for testing.
Essentially, I have 3 swfs. I have one 'master' swf, and two 'slave' swfs. When the master swf is launched, it opens both other swfs in new windows. When a button is clicked in the master, I'm trying to just update some text in one of the slaves. More complicated stuff will happen later, I'm just trying to figure out LocalConnections first.
The code (on the timeline) in 'master.swf':
import flash.net.URLRequest;
import flash.events.Event;
//Used as a flag to open each slave window, one of the first frame, one on the second frame, as using navigateToURL twice in a row doesn't seem to work.
var frameCount = 0;
//This is what I'm attempting to send over the LocalConnection.
var messageText = "it worked!";
var slave1url:URLRequest = new URLRequest("slave1.html");
var slave2url:URLRequest = new URLRequest("slave2.html");
//Creates a one-way connection to communicate with JUST slave1
var slave1OutboundConnection:LocalConnection = new LocalConnection();
this.addEventListener(Event.ENTER_FRAME, Update);
slave1Button.addEventListener(MouseEvent.CLICK, SendMessageToSlave1);
slave1Button.buttonMode = true;
//Send a message to slave1 via the slave1OutboundConnection
function SendMessageToSlave1(e:Event){
slave1OutboundConnection.send("masterToSlave1Connection", "UpdateTextFromCommunication", messageText);
}
function Update(e:Event){
//Open the slave1 page on the first frame, and the slave2 page on the second frame.
if(frameCount == 0){
navigateToURL(slave1url, "_blank");
frameCount++;
} else if (frameCount == 1){
navigateToURL(slave2url, "_blank");
frameCount++;
}
}
In slave1:
var masterInboundConnection:LocalConnection = new LocalConnection();
masterInboundConnection.allowDomain("*");
masterInboundConnection.connect("masterToSlave1Connection");
function UpdateTextFromCommunication(receivedArguments){
displayText.text = receivedArguments;
}
I've also got a little bit of custom javascript in each html page to set page size and position, but I don't think that should matter:
<script>
//Get the width of the user's monitor
var resolutionWidth = window.screen.availWidth;
//Get the height of the user's monitor (minus the height of the task bar)
var resolutionHeight = window.screen.availHeight;
//First screen is 0, second is 1, third is 2, etc.
var screenNumber = 2;
//If this screen is not displayed on the primary monitor, add 40 pixels for the lack of the taskbar on other monitors
if(screenNumber > 0){
resolutionHeight += 40;
}
//Maximize the window
this.resizeTo(resolutionWidth, resolutionHeight);
//Move the window to the appropriate display
this.moveTo(resolutionWidth * screenNumber, 0);
</script>
When I click the button that's supposed to trigger the communication, nothing appears to happen.
I'm running the html pages containing these swfs in IE11 on a localhost xampp server on Windows 7. Swfs made in CS5.5.
In each of the inbound connections, I was missing a single line of code:
masterInboundConnection.client = this;
Now everything is working as expected.
Related
I am working on a project which is loading png(png1) on run-time in front of another png(png2) image(which is placed earlier)
I have done this and it is working properly, the problem is after some time png1 transparenting to the background even the png2 placed in middle of png1 and background, bellow i have attached screenshots of the issue and the code.
import flash.net.URLLoader;
import flash.net.URLRequest;
var fl_TimerInstance: Timer = new Timer(1000);
fl_TimerInstance.addEventListener(TimerEvent.TIMER, fl_TimerHandler);
fl_TimerInstance.start();
var fl_SecondsElapsed: Number = 1;
function fl_TimerHandler(event: TimerEvent): void {
var imageLoader: Loader = new Loader();
var image: URLRequest = new URLRequest("C:\\Users\\Public\\Pictures\\pic.png"); //png1 = pic.png
imageLoader.load(image);
Zerolocation.addChild(imageLoader);
}
ScreenShots:
Before Error - https://drive.google.com/file/d/19a0t2jEGfDoX2voQ96rap4XpvDlGMWBd/view?usp=sharing
Error - https://drive.google.com/file/d/1a--EIEXz2Qzt5SBfl8Y8SxDIAG3DkYZf/view?usp=sharing
Timeline - https://drive.google.com/file/d/1s2uPSpOYAcfEJqdNqD4QpDGla8Gvs5LC/view?usp=sharing
It would be much appreciated if anyone can give me a clue about what is wrong the with this.
You need to replace your png1 each time you load a new file. Best if you'd check if the file is still the same so not to download it again and again each second. The reason of why does the png2 stop being displayed is exactly because you have too many transparent layers in front of png2. I recall having 24 pictures with alpha channel on top of each other caused me to lose some background that would otherwise be visible. To solve your issue I say you add a Sprite container in front of your png2 layer wise, then you could just clear all the children of that container to remove obsolete imageLoaders, then add your newly downloaded picture. Something in line of this:
// Zerolocation has a child named "runtimePic" to place loaded pics
var didWeLoad:Boolean=true;
function fl_TimerHandler(event: TimerEvent): void {
if (!didWeLoad) return; // why downloading more while we haven't finished?
var imageLoader: Loader = new Loader();
var image: URLRequest = new URLRequest("C:\\Users\\Public\\Pictures\\pic.png"); //png1 = pic.png
imageLoader.load(image);
imageLoader.addEventListener(Event.COMPLETE,loaded);
didWeLoad=false;
}
function loaded(e:Event):void {
didWeLoad=true; // allowing more downloading
e.target.removeEventListener(Event.COMPLETE,loaded); // clean up, or else you'll leak memory
// remove old pic(s)
Zerolocation.runtimePic.removeChildren();
// and now add new pic
Zerolocation.runtimePic.addChild(e.target);
}
Be warned, this code does not handle downloading errors.
I have a slightly unusual situation with SharedObject:
The situation: where we have a SWF (browser) based application running on a single local machine. The SWF accesses local content and is reloaded every XX number of seconds/minutes/hours.
The state of application has to be stored within a single SharedObject (this is using the '/' parameter to force it to global) in a JSON style.
The SWF loads the SO before it makes any state updates and correctly calls flush() immediately after to save state.
The Problem: Everything runs fine BUT occasionally there is a situation where by 2 instances of the same SWF are existing at the same in different instances and both are accessing the same SharedObject.
The 2nd has to take control of the SO from the 1st, by each SWF instance setting it's instance state to an incremented number (SWF Idx) stored in the SO.
Both are loading the file before any update is made, version number checked, and will disable themselves if the saved SWF Idx is above it's own. Unfortunately the 1st SWF instance somehow isn't loading the latest version of the SharedObject because it traces out the original number (e.g. 22) instead of the now updated one (e.g. 23). While the 2nd SWF is tracing out 23 and the SO contains 23.
Is there any way that the browser could possibly be caching the SO?
Testing
I'm currently testing the situation by running one browser instance locally then launching a 2nd. Making copies of the SO before and after each state.
I've also run the 1st and 2nd via IntelliJ and can see that SharedObject.getLocal is being called and checked each time.
I've included the basics of the code I'm using below where:
__so = is the public SharedObject variable
_thisSWFIdx = is the private variable inside AS3 storing the current instances SWF Index
__so.data.activeSWFIdx = the latest SWF Index
The SO 'get' I'm using is:
SharedObject.getLocal(_SO_ID, "/");
The check i'm doing is:
if (_thisSWFIdx < __so.data.activeSWFIdx) {
__amActiveSWF = false;
}
The saving of the variable to SO:
__so.data.activeSWFIdx = _thisSWFIdx;
flush();
Additonal info:
This is running on both mac & windows
FlashPlayer 10.3
Pure AS3
compiled in IntelliJ
same issue in FF, Chrome and IE
primary machine Macbook
I can't find anything within the documentation or existing threads so any help will be much appreciated.
After a little test, I remarked the behavior that you've mentioned : after updating the SharedObject by a second SWF, the first one can't get the new value only after a reload.
To avoid that, I think, as a workaround, you can use a second SWF which will just work on the SharedObject by reading / writing data.
For that, take this example :
so.swf : the SWF which will read and write data.
var shared_object_name:String = 'so';
var shared_object:SharedObject = SharedObject.getLocal(shared_object_name, '/');
// get the id
function get_id(): int {
return shared_object.data.id ? shared_object.data.id : -1;
}
// set the id
function set_id(new_id:int): void {
shared_object.data.id = new_id;
shared_object.flush();
}
app.swf : your global app which will use the "so.swf" to do SharedObject's operations :
var init:Boolean = true;
var swf_id:int = 1;
var swf_disabled:Boolean = false;
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, on_SWFLoad);
loader.load(new URLRequest('so.swf'));
function on_SWFLoad(e:Event): void
{
var loaded_swf:MovieClip = MovieClip(loader.content);
var current_id:int = loaded_swf.get_id();
// if it's initial run
if(init){
init = false;
if(current_id > 0){
swf_id = current_id + 1;
}
loaded_swf.set_id(swf_id);
} else { // we are verifying if there is a new active SWF
if (swf_id <= current_id) {
swf_disabled = true;
}
}
}
function stage_onPress(e:MouseEvent){
// if this SWF is disabled, stop getting the "id"
if(swf_disabled){
stage.removeEventListener(MouseEvent.CLICK, stage_onPress);
return;
}
// otherwise, get the "id" to verify if there is another active SWF
loader.load(new URLRequest('so.swf'));
}
stage.addEventListener(MouseEvent.CLICK, stage_onPress);
I used MouseEvent.CLICK only for testing purposes, of course you have to use a Timer or something else to verify every n seconds (for example) that you have another active SWF to disable the current one...
Hope that can help.
Making a custom AS3 preloader, I noticed that when my SWF is executed locally (file:///) the preloader gets stuck in the loading screen when previewed in a web browser like Chrome.
When executed from a remote server or through the standalone Flash Player, then it works. I noticed other SWF that have preloaders do not have this issue. What I need to change?
this.loaderInfo.addEventListener(ProgressEvent.PROGRESS, preloaderProgress);
function preloaderProgress(event:ProgressEvent):void {
var loadedPercent:Number = event.bytesLoaded/event.bytesTotal*100;
if (loadedPercent == 100){
this.gotoAndStop(2);
}
}
Loading locally goes very fast. Not only you should check if the file is completely loaded using the Event.COMPLETE and not the ProgessEvent but you should also make sure to register your listeners before actually calling load or the file might end up completely loaded before you register your listeners.
Following the example at http://stephenscholtz.com/201110/single-movie-flash-preloading-as3
It seems that there was no ProgressEvent.COMPLETE, but there is a Event.COMPLETE instead, a bit confusing.
I changed my code to the following, (also including some tweaks for right click menu to prohibit the user from right clicking and pressing Play before movie has loaded and such)
//Initialize any variables
var loadedPercent:Number = 0;
//Remove all items from right click Flash Player menu (except Quality, and the mandatory About... & Settings...)
var cxMenu:ContextMenu = new ContextMenu();
cxMenu.hideBuiltInItems();
cxMenu.builtInItems.quality = true;
this.contextMenu = cxMenu;
/* or disable right click menu completely (Flash Player 11.2.202.228 and over) */
/* this.addEventListener(MouseEvent.RIGHT_CLICK, onMouseRightClick);
function onMouseRightClick(event:MouseEvent)
{
return false;
} */
//Disable shortcut keys and window menubar when played from desktop Standalone Flash Player app
if (Capabilities.playerType == "StandAlone" || Capabilities.playerType == "External") {
fscommand("showmenu", "false");
fscommand("trapallkeys", "true");
}
/* Preloader: begin */
//Update loading bar and percent text as SWF loads
function onProgress(e:Event) {
//Get the amount of bytes that have been loaded / bytes total to load
loadedPercent = this.loaderInfo.bytesLoaded/this.loaderInfo.bytesTotal*100;
//Update the text of _preloaderProgress movieclip
_preloaderProgress.text = int(loadedPercent)+"%";
//Update the _preloaderBar amount by scaling horizontally as it loads
_preloaderBar.scaleX = loadedPercent/100;
}
//Go to next frame after everything loads
function init(e:Event) {
gotoAndStop(2);
}
//Attach the events to the stage
this.loaderInfo.addEventListener(ProgressEvent.PROGRESS, onProgress);
this.loaderInfo.addEventListener(Event.COMPLETE, init);
/* Preloader: end */
//Stop to 1st frame and wait until it is loaded
stop();
This will allow the movie to play both remotely and locally with no problems.
Currently I have an intro screen to my flash file which has two objects.
A button which will load an external flash file using:
var myLoader:Loader = new Loader();
var url:URLRequest = new URLRequest("flashgame.swf");
The second thing is a Numeric Stepper, which will be from 1 to 10. If the user selects a number e.g. 3 then the game speed I have set in the flashgame.swf should be changed
Such as:
var gameSpeed:uint = 10 * numericStepper.value;
But I think my problem is coming into place because the stepper and gamespeed are from different files.
Anyone got any idea please?
I have also tried creating a stepper in the game file and used this code:
var gameLevel:NumericStepper = new NumericStepper();
gameLevel.maximum = 10;
gameLevel.minimum = 1;
addChild(gameLevel);
var gameSpeed:uint = 10 * gameLevel.value;
For some reason the stepper just flashes on the stage, no errors come up and the game doesn't work
When you execute you code, the stepper has no chance to wait for user input.
There is no time between theese two instructions.
addChild(gameLevel);
var gameSpeed:uint = 10 * gameLevel.value;
You should wait for user input in your NumericStepper, and then, on user event, set the game speed.
Edit: Yeah I know it's kinda sad to type out all this code (especially since some people wouldn't even be grateful enough to say thanks) but I think this question is important enough to justify the code as it may be helpful to others in future also.
Hi,
You were close. In your game file you could have put a var _setgameSpeed and then from Intro you could adjust it by flashgame._setgameSpeed = gameSpeed; It's a bit more complicated though since you also have to setup a reference to flashgame in the first place. Let me explain...
Ideally you want to put all your code in one place (an .as file would be best but...) if you would rather use timeline then you should create a new empty layer called "actions" and put all your code in the first frame of that.
Also change your button to a movieClip type and remove any code within it since everything will be controlled by the code in "actions" layer. In the example I have that movieclip on the stage with instance name of "btn_load_SWF"
Intro.swf (Parent SWF file)
var my_Game_Swf:MovieClip; //reference name when accessing "flashgame.swf"
var _SWF_is_loaded:Boolean = false; //initial value
var set_gameSpeed:int; //temp value holder for speed
var swf_loader:Loader = new Loader();
btn_load_SWF.buttonMode = true; //instance name of movieclip used as "load" button
btn_load_SWF.addEventListener(MouseEvent.CLICK, load_Game_SWF);
function load_Game_SWF (event:MouseEvent) : void
{
//set_gameSpeed = 10 * numericStepper.value;
set_gameSpeed = 100; //manual set cos I dont have the above numericStepper
if ( _SWF_is_loaded == true)
{
stage.removeChild(swf_loader);
swf_loader.load ( new URLRequest ("flashgame.swf") );
}
else
{ swf_loader.load ( new URLRequest ("flashgame.swf") ); }
swf_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, Game_SWF_ready);
}
function Game_SWF_ready (evt:Event) : void
{
swf_loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, Game_SWF_ready);
//Treat the Loader contents (flashgame.swf) as a MovieClip named "my_Game_Swf"
my_Game_Swf = swf_loader.content as MovieClip;
my_Game_Swf.gameSpeed = set_gameSpeed; //update gameSpeed variable in flashgame.swf
//also adjust SWF placement (.x and .y positions etc) here if necessary
stage.addChild(my_Game_Swf);
_SWF_is_loaded = true;
}
Now in you flashgame file make sure the there's also an actions layers and put in code like this below then compile it first before debugging the Intro/Parent file. When your Intro loads the flashgame.swf it should load an swf that already has the code below compiled.
flashgame.swf
var gameSpeed:int;
gameSpeed = 0; //initial value & will be changed by parent
addEventListener(Event.ADDED_TO_STAGE, onAdded_toStage);
function onAdded_toStage (e:Event):void
{
trace("trace gameSpeed is.." + String(gameSpeed)); //confirm speed in Debugger
//*** Example usage ***
var my_shape:Shape = new Shape();
my_shape.graphics.lineStyle(5, 0xFF0000, 5);
my_shape.graphics.moveTo(10, 50);
my_shape.graphics.lineTo(gameSpeed * 10, 50); //this line length is affected by gameSpeed as set in parent SWF
addChild(my_shape);
}
The key line in intro.swf is this: my_Game_Swf.gameSpeed = set_gameSpeed; as it updates a variable in flashgame.swf (referred as my_Game_Swf) with an amount that is taken from a variable in the Parent SWF.
This is just one way you can access information between two separate SWF files. Hope it helps out.
Im loading multiple SWF of the same file
Example:
var movies:Array = new Array ("Symbols/00.swf","Symbols/00.swf","Symbols/00.swf");
My loading method works fine, below im using:
var perc:int = (event.bytesLoaded / event.bytesTotal) * 100;
Loading.text = "loading " +perc + "%";
The Problem im having is the the loading text is at %100 after iv downloaded one item from the Movies:Array.
This would be happening because the remainder files are already in the catch.
The question is:
How would I be able to slow this loading text to determine if all the items are ready.Basically the problem is that the loading text saids complete but all my other files
are not ready yet...
First, why do you ever load one SWF multiple times? You can use the following trick: How to display multiple instances of loaded SWF. And second, you are using your Loader object to load one SWF at a time, and its progress will be related to that single SWF. So, you first load all unique SWFs and store their Loader references somewhere, in order to instantiate more of those if you need to, and second, you are to collect total percentage from progress event listener. The latter is slightly complicated, because you are most likely using a single progress event listener for all the loaders. You, for example, can do something like this:
public static var ALL_LOADERS:Object;
public static var PROGRESS_DATA:Object; // we need associative arrays here
public static var BYTES_LOADED:int=0;
public static var TOTAL_BYTES:int=0;
public static function RegisterLoader(path:String,loader:Loader):void {
if (!ALL_LOADERS) ALL_LOADERS=new Object();
if (!PROGRESS_DATA) PROGRESS_DATA=new Object();
ALL_LOADERS[path]=loader;
PROGRESS_DATA[loader]=new Object();
PROGRESS_DATA[loader].bytesLoaded=0;
PROGRESS_DATA[loader].bytesTotal=-1;
}
// now a listener
public static function progressListener(e:Event):void {
var l:Loader=e.target as Loader;
if (!l) return; // that wasn't a loader that sent the event. Uh oh
var plo:Object=PROGRESS_DATA[l];
if (!plo) return; // unregistered loader. Ignore
if (plo.bytesTotal<0) {
plo.bytesTotal=e.bytesTotal;
TOTAL_BYTES+=e.bytesTotal;
}
var pbl:int=plo.bytesLoaded;
plo.bytesLoaded=e.bytesLoaded;
LOADED_BYTES+=(e.bytesLoaded-pbl);
var perc:int = (LOADED_BYTES / TOTAL_BYTES) * 100;
Loading.text = "loading " +perc + "%";
}
Note, as the loading process is designed to be run only once, all initialization is made once, and this code does not have error handling - you can, however, write it yourself.