Issues with embedding objects using htmlloader - actionscript-3

I'm making an editable stage where the user can add images, videos, and embedded content. Currently all objects reside in a classfile called Space. All objects that you can add go inside of bounds_mc inside of space. Those objects are called SpaceObjects and are handled by the classfile SpaceObject...
Right now, when I tell my SpaceObject to add an embed it does it, but..there's a bit of an issue.
When I add the object I can't exactly interact with it. I am able to highlight it with the same mouse icon that you would see when highlighting text..but nothing else. I can move bounds_mc around just fine (the white space behind it) and have that object move accordingly..but nothing else.
Now if I add this object directly to the stage by passing the stage all the way down to my Space class and doing a stagee.addChild(htmlLoader) this issue is resolved.
At first I thought this was because it was inside a bunch of movieclips or some kind of order issue in the displaylist...but that was disproved by this testrun.
I have the HtmlLoader inside of three movieclips and I'm able to interract with the embedd just fine. I'm trying to figure out what could be causing this. I've tried switching where HtmlLoader goes from here to there but I still get this issue.
Here's the embedd
<iframe width="560" height="315" src="https://www.youtube.com/embed/Kzgjiuvpmsk" frameborder="0" allowfullscreen></iframe>
Here's a sample from my Space class that handles user input from the menu.
case "embeddedobject":
trace("Embeddedobject");
//For now, just testing a standard embedd from youtube. Dynamics later.
var so:SpaceObject = new SpaceObject("embeddedobject", '<iframe width="560" height="315" src="https://www.youtube.com/embed/Kzgjiuvpmsk" frameborder="0" allowfullscreen></iframe>', new Rectangle(rightMouseX, rightMouseY));
bounds_mc.addChild(so);
//Tested with and without the eventlistener..same results
so.addEventListener(MouseEvent.CLICK, contentClickHandler);
spaceObjects.push(so);
break;
This is an excerpt from SpaceObject. It handles the arguments passed from it's constructor. In this case its generating my embedd. The first argument is source. the second is actions (the string of the embedd)
public function SpaceObject(source:String, actions:String, bounds:Rectangle, rotation:int=0, matrixx:Matrix = null)
{
//...
if (source == "embeddedobject")
{
htmlLoader = new HTMLLoader();
htmlLoader.placeLoadStringContentInApplicationSandbox = true;
htmlLoader.loadString(actions);
htmlLoader.addEventListener(Event.COMPLETE, handleHtmlLoadComplete);
}
//...
}
private function handleHtmlLoadComplete(e:Event):void
{
trace("Html content load complete");
htmlLoader.removeEventListener(Event.COMPLETE, handleHtmlLoadComplete);
htmlLoader.width = htmlLoader.contentWidth;
htmlLoader.height = htmlLoader.contentHeight;
addChild(htmlLoader);
}
Here are the links to the classfiles involved that I've made on pastebin.. all space objects reside in Space. Space resides in SpaceContainer.
SpaceContainer.as
Space.as
SpaceObject.as
Edit 12/5/15: An interesting note. When clicked on, e.target is returned as a sprite. When other objects are clicked on (be it images or videos) they're returned as my SpaceObject. This happens both at times when I add it to the stage, and when I add it to bounds_mc.
e.target = [object Sprite] vs e.target = [object SpaceObject]
Edit 12/5/15: Looks like any alteration of the objects (aside from its x and y) disables any interactive objects inside the embed and turns it into some kind of bitmap.

Resizing.
It looks like when you resize or alter any displayobjects that are containing the embed (either before or after you place the object) it will disable the embed.
This line was in main.as and was constructing SpaceContainer.
var sc:SpaceContainer = new SpaceContainer(stage);
//resize(sc, stage.stageWidth, stage.stageHeight);
...
private function resize(mc:DisplayObject, maxW:Number, maxH:Number = 0, constrainProportions:Boolean = true):void
{
maxH = maxH == 0 ? maxW : maxH;
mc.width = maxW;
mc.height = maxH;
if (constrainProportions)
{
mc.scaleX < mc.scaleY ? mc.scaleY = mc.scaleX : mc.scaleX = mc.scaleY;
}
}

Related

How to load a swf and interact with it?

I have tried SWFLoader, but the problem is the loaded content is MovieClip and I don't know how to interact with it, and the MovieClip#numChildren is zero.
And by the way, I can't pass the flashvars to the swf.
Firstly, you should know that there is no exact answer to your question as it depends on your loaded SWF (you know it or not, its display list, ...) but I'll put a simple example to explain things and you have to adapt it to your case.
For this example, let's say that we have a very simple SWF (the loaded SWF) which contain a TextField (called txt_url) and a button (a MovieClip, called btn_go).
The btn_go button will open the URL entered in the txt_url TextField.
For our second SWF (the loader), we will use a Loader object to load our first one (which is in this case will be the Loader.content) and then we will set the URL (the txt_url text) and trigger the click event on the btn_go button.
So here is an example of the code of our loader.swf :
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, on_SWFLoad);
loader.load(new URLRequest('loaded.swf'));
addChild(loader);
function on_SWFLoad(e:Event): void
{
// get our loaded SWF
var loaded_swf:DisplayObjectContainer = DisplayObjectContainer(loader.content);
// because we know our target objects, we can use "getChildByName()"
// set the URL
TextField(loaded_swf.getChildByName('txt_url')).text = 'http://www.example.com';
// open the URL in the browser by triggering the click event on the "btn_go" button
MovieClip(loaded_swf.getChildByName('btn_go')).dispatchEvent(new MouseEvent(MouseEvent.CLICK));
}
This example will directly set and open the URL in the browser after loading the SWF, of course we can execute that action after clicking a button or something else but it's just a simple example to show you how you can do ...
Now, the problem is when we don't know anything about the loaded SWF and its children (names, depths, ...), in this case we should do more effort to do what we want : we should traverse the entire display list of the loaded SWF to identify the target objects.
Returning to our example and let's say that we only know that there are a TextField and a button in the stage, so our code can be like this for example :
function on_SWFLoad(e:Event): void
{
var loaded_swf:DisplayObjectContainer = DisplayObjectContainer(loader.content);
var num_children:int = loaded_swf.numChildren;
for(var i:int = 0; i < num_children; i++)
{
var child:DisplayObject = loaded_swf.getChildAt(i);
if(child is TextField)
{
trace(child.name); // gives : txt_url
TextField(child).text = 'http://www.example.com';
}
else
{
if(child.hasEventListener(MouseEvent.CLICK))
{
trace(child.name); // gives : btn_go
child.dispatchEvent(new MouseEvent(MouseEvent.CLICK));
}
}
}
}
Again, it's a very simple example just to show how we can proceed ...
...
Then about passing values (params) between SWFs, take a look on my answer of this question where you have a little example for that.
For more about Display programming (display list, display object, display object container, ...) take a look here.
Hope that can help.

Using a Numeric Stepper from one Flash file to affect gamespeed in an external Flash file? Actionscript 3.0

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.

I can't seem to access automatically named objects (instance##) placed on the stage in AS3, am I missing something?

I have a movieclip in the library that is added to the stage dynamically in the document class's actionscript. This movieclip contains many many child images that were imported directly from photoshop at their original positions (which must be preserved).
I do not want to manually name every single image instance, as there are dozens upon dozens.
I have already gone through and manually converted the images to symbols, as apparently flash won't recognize the "bitmap" objects as children of a parent movieclip in AS3 (numChildren doesn't see the bitmaps, but it sees the symbols).
I have an array filled with references to the dozens of children, and I loop through it, checking if each one is under the mouse when clicked. However, somehow, it is not detecting when I click over the items unless I manually name the child symbols (I tested by manually naming a few of them -- those ones became click-sensitive.)
I have already done trace() debugging all throughout the code, verifying that my array is full of data, that the data is, in fact, the names of the instances (automatically named, IE instance45, instance46, instance47, etc.), verifying that the function is running on click, verifying that the code works properly if I manually name the symbols.
Can any one see what's going wrong, or what aspect of flash I am failing to understand?
Here is the code:
//check each animal to see if it was clicked on
private function check_animal_hits():void
{
var i:int = 0;
var animal:Object = this.animal_container;
for (i=0; i<animal.mussels.length; i++)
{
if (this.instance_under_cursor(animal.mussels[i].name))
{
var animal_data = new Object();
animal_data.animal = "mussel";
this.send_data(animal_data);
}
}
}
Here is the code for the instance_under_cursor() method:
// Used for finding out if a certain instance is underneath the cursor the instance name is a string
private function instance_under_cursor(instance_name)
{
var i:Number;
var pt:Point = new Point(mouseX,mouseY);
var objects:Array = stage.getObjectsUnderPoint(pt);
var buttons:Array = new Array ;
var o:DisplayObject;
var myMovieClip:MovieClip;
// add items under mouseclick to an array
for (i = 0; i < objects.length; i++)
{
o = objects[i];
while (! o.parent is MovieClip)
{
o = o.parent;
}
myMovieClip = o.parent as MovieClip;
buttons.push(myMovieClip.name);
}
if (buttons.indexOf(instance_name) >= 0)
{
return true;
}
return false;
}
Update:
I believe I have narrowed it down to a problem with getObjectsUnderPoint() not detecting the objects unless they are named manually.
That is the most bizarre way to find objects under mouse pointer... There is a built-in function that does exactly that. But, that aside, you shouldn't probably rely on instance names as they are irrelevant / can be changed / kept solely for historical reasons. The code that makes use of this property is a subject to refactoring.
However, what you have observed might be this: when you put images on the scene in Flash CS, Flash will try to optimize it by reducing them all to a shape with a bitmap fill. Once you convert them to symbols, it won't be able to do it (as it assumes you want to use them later), but it will create Bitmpas instead - Bitmap is not an interactive object - i.e. it doesn't register mouse events - no point in adding it into what's returned from getObjectsUnderPoint(). Obviously, what you want to do, is to make them something interactive - like Sprite for example. Thus, your testing for parent being a MovieClip misses the point - as the parent needs not be MovieClip (could be Sprite or SimpleButton or Loader).
But, if you could explain what did you need the instance_under_cursor function for, there may be a better way to do what it was meant to do.

How can I load a Papervision/Flex application (SWF) as a material on a Papervision plane?

I am trying to build a portfolio application similar to the used by Whitevoid. I am using Flex 4 and Papervision3D 2. I have everything working except for one issue. When I try to load an external SWF as a material on one of the planes, I can see any native Flex or Flash components in their correct positions, but the papervision objects are not being rendered properly. It looks like the viewport is not being set in the nested swf. I have posted my code for loading the swf below.
private function loadMovie(path:String=""):void
{
loader = new Loader();
request = new URLRequest(path);
loader.contentLoaderInfo.addEventListener(Event.INIT, addMaterial);
loader.load(request);
}
private function addMaterial(e:Event):void
{
movie = new MovieClip();
movie.addChild(e.target.content);
var width:Number = 0;
var height:Number = 0;
width = loader.contentLoaderInfo.width;
height = loader.contentLoaderInfo.height;
//calculate the aspect ratio of the swf
var matAR:Number = width/height;
if (matAR > aspectRatio)
{
plane.scaleY = aspectRatio / matAR;
}
else if (matAR < aspectRatio)
{
plane.scaleX = matAR / aspectRatio;
}
var mat:MovieMaterial = new MovieMaterial(movie, false, true, false, new Rectangle(0, 0, width, height));
mat.interactive = true;
mat.smooth = true;
plane.material = mat;
}
Below I have posted two pictures. The first is a shot of the application running by itself. The second is the application as a MovieMaterial on a Plane. You can see how the button created as a spark object in the mxml stays in the correct position, but papervision sphere (which is rotating) is in the wrong location. Is there something I am missing here?
Man. I haven't seen that site in a while. Still one of the cooler PV projects...
What do you mean by:
I cannot properly see the scene rendered in Papervision
You say you can see the components in their appropriate positions, as in: you have a plane with what looks like the intended file loading up? But I'm guessing that you can't interact with it.
As far as I know, and I've spent a reasonable amount of time trying to make something similar work, the MovieMaterial (which I assume you're using) draws a Bitmap of whatever contents exist in your MovieClip, and if you set it to animated=true, then it will render out a series of bitmaps - equating animation. What it's not doing, is displaying an actual MovieClip (or SWF) on the plane. So you may see your components, but this is how:
MovieMaterial.as line 137
// ______________________________________________________________________ CREATE BITMAP
/**
*
* #param asset
* #return
*/
protected function createBitmapFromSprite( asset:DisplayObject ):BitmapData
{
// Set the new movie reference
movie = asset;
// initialize the bitmap since it's new
initBitmap( movie );
// Draw
drawBitmap();
// Call super.createBitmap to centralize the bitmap specific code.
// Here only MovieClip specific code, all bitmap code (maxUVs, AUTO_MIP_MAP, correctBitmap) in BitmapMaterial.
bitmap = super.createBitmap( bitmap );
return bitmap;
}
Note in the WhiteVoid you never actually interact with a movie until it "lands" = he's very likely swapping in a Movie on top of the bitmap textured plane.
The part that you are interacting with is probably another plane that holds the "button" that simply becomes visible on mouseover.
I think PV1.0 had access to real swfs as a material but this changed in 2.0. Sadly. Hopefully Molehill will.
cheers

Load External SWF, assign MOUSE_DOWN event, and prevent empty space / sub stage from being clicked

I've been developing in Flash now for over 15 years, and recently started developing games in Flash Actionscript 3. I am having some difficulties, and need some help. I've spent days trying to find a solution, with no luck.
I have a main SWF, which loads sub SWF animations. One object can have 5 different animations / SWFs associated with it. So lets say I have a chicken, chicken01.swf, chicken02.swf, ...
I assign a MOUSE_DOWN event to the first loaded SWF, then based on the tool used while clicking on the object, it will load the other animations. My problem is that each SWF has empty space around it which becomes clickable. I only need the object clickable, and not the empty space, because some of the objects can overlap each other, which makes it hard to click on the object behind another object.
The Sub SWFs / animations are on a single timeline, and I played with Bitmap Tracing to remove the empty space around the imported PNG objects. This works if I reduce the Stage size to behind the Object, but then screws up the size of the loaded SWF due to the Stage size being smaller than the object. So when I assign a width and height to the object, with a smaller stage, the object is huge. If I constraint the stage size to the size of the object, even as a Traced bitmap image, the stage is still clickable. I tried to assign the MOUSE_DOWN event to the object on the sub SWF, from the Main SWF, but this gives errors.
My goal is to load a sub SWF, assign the MOUSE_DOWN event, and only have the object clickable, and not the Stage, or empty space around the object.
Is this possible? I also played around with creating an invisible button, but this makes it difficult to assign to 300 + objects of different shapes and sizes.
Below is some of the code I'm using.
var loadimage = foreground_list[i].imagelocation + foreground_list[i].image;
var loader:SWFLoader = new SWFLoader(loadimage,{container:tn_mc,x:current_tn_x,y:current_tn_y,name:current_name,alpha:1,width:current_tn_w,height:current_tn_h,rotation:0});
loader.load();
tn_mc.buttonMode = true;
tn_mc.addEventListener( MouseEvent.MOUSE_DOWN, tn_down );
tn_mc.addEventListener( MouseEvent.MOUSE_UP, tn_up );
addChild( tn_mc );
function tn_down(e:MouseEvent):void
{
switch (MovieClip(this.root).PointerTool)
{
case "move" :
stage.addEventListener(MouseEvent.MOUSE_UP, stage_up );
e.target.startDrag();
break;
case "play" :
var loader4:SWFLoader = new SWFLoader(foreground_list.imagelocation + foreground_list.playimage,{container:tn_mc,name:e.target.name,x:foreground_list.setx,y:foreground_list.sety,width:foreground_list.setw,height:foreground_list.seth,rotation:0});
tn_mc.removeChildAt(0);
tn_mc.addEventListener( MouseEvent.MOUSE_DOWN, tn_down );
tn_mc.addEventListener( MouseEvent.MOUSE_UP, tn_up );
loader4.load();
loader4.addEventListener(Event.COMPLETE, completeactionHandler);
break;
default :
//Some other animation
break;
}
}
Create a movieclip - a vector shape inside each swf, that is the same shape as your clickable area. Set the alpha to 0% on the vector's fill color. Give it and instance name of something like activeArea, and assign your event listener to that instead of the outer shell moveiclip.
Another approach that might work is to use hitTestObject() on a MOUSE_DOWN event, which would allow you to choose to ignore the transparency.
EDIT
Hard to tell exactly what you are trying to do without seeing it. I didn't actually compile this so I'm not sure if this will work just the way it is, but in theory it should be close. Its a slightly different approach than you are using. I used Loader() instead of SWFLoader, and cleaned up the idea a little bit. As a side note, you should avoid the use of root in as3.
var _swfLoader:Loader;
var loadimage = foreground_list[i].imagelocation + foreground_list[i].image;
var loader:SWFLoader = new SWFLoader(loadimage,{container:tn_mc,x:current_tn_x,y:current_tn_y,name:current_name,alpha:1,width:current_tn_w,height:current_tn_h,rotation:0});
loader.load();
tn_mc.buttonMode = true;
tn_mc.addEventListener( MouseEvent.MOUSE_DOWN, tn_down );
addChild( tn_mc );
function tn_down(e:MouseEvent):void
{
tn_mc.addEventListener( MouseEvent.MOUSE_UP, tn_up );
switch (MovieClip(this.root).PointerTool)
{
case "move" :
stage.addEventListener(MouseEvent.MOUSE_UP, stage_up );
e.target.startDrag();
break;
case "play" :
_swfLoader = new Loader();
var req:URLRequest = new URLRequest(foreground_list.imagelocation + foreground_list.playimage);
_swfLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, animationLoaded);
_swfLoader.load(req);
break;
default :
//Some other animation
break;
}
}
function tn_up(e:MouseEvent):void
{
tn_mc.removeEventListener( MouseEvent.MOUSE_UP, tn_up );
}
function animationLoaded(evt:Event):void
{
_swfLoader.contentLoaderInfo.removeEventListener(Event.COMPLETE, animationLoaded);
tn_mc.removeChildAt(0);
var loadedSwf = evt.target.content;
loadedSwf.x = foreground_list.setx;
loadedSwf.y = foreground_list.sety;
loadedSwf.width = foreground_list.setw;
loadedSwf.height = foreground_list.seth;
loadedSwf.rotation = 0;
loadedSwf.addEventListener(MouseEvent.MOUSE_DOWN, onAnimationStart);
// might wanna add theses to an array to keep track of them and run clean up later on
// now add to some display list
}
function onAnimationStart(evt:MouseEvent):void
{
loadedSwf.addEventListener(MouseEvent.MOUSE_UP, onAnimationStop);
// play your animation or whatever else
evt.target.play();
}
function onAnimationStop(evt:MouseEvent):void
{
loadedSwf.removeEventListener(MouseEvent.MOUSE_UP, onAnimationStop);
// stop your animation or whatever else
evt.target.stop();
}