|
|
Labs.byHook |
Flash Scripts, Tools & Methods Developed at Hook |
Let me guess, it goes a bit like this:
“Click + Drag, crap grabbed the wrong one, slower click, still the wrong item, lock, lock, lock, lock, click, finally. Double Click, crap.. locked..unlock, unlock, unlock, double click, f@&^k!”
Managing the seemingly simple act of selecting objects on stage quickly becomes a nightmare for any flash designer. The solution? JSFL! (or so we thought)
This is the tale of an Outliner style utility that helps manage the normally arduous process of selecting the right objects.
So we set out to build something with similar features to Autodesk Maya’s Outliner. This would allow a simplified selection mechanism and a bit more organized way of viewing things. The wish list ended up being something like this:
In this posted version we managed to get the really “required” features in, but we still have more to go. However we wanted to get this out there as it has already eased some of the pain around the office. So we decided to roll it out in its current beta state.
JSFL: The Forgotten Road
The journey leading into trying to build a flash extension seemed so very bright and full of optimism, as if we were headed into a gingerbread town with roads paved using delicious candies. The possibilities were limitless, or so it seemed.
As with most things we started out doing a bit of research on how to build a panel and get it to interact with items on the stage. This is when the first wisps of oddity showed up. There seemed to be very little written on JSFL after flash 8. In all honesty there didn’t seem to be much written at all even for versions of flash before 8. These candy roads seemed a bit lonely, but hey, its still candy right?
After a bit of fumbling through the “Extending Flash” section of the docs, we managed to get a panel open with buttons and everything! However the buttons didn’t do much. As it turns out the link between the panel (a swf) and the flash IDE is a bit flimsy. Basically you call javascript formatted functions that feed results to the IDE. This on the surface sounds just fine, until you realize that any results you get from functions written in JSFL thare are returned to the panel are returned as pure strings. Meaning no real object references and no direct control. Suddenly the amazing candy roads were revealed as being paved with crappy powder dusted Necco Wafers.
So, how to click on an object in the panel, and have it actually select in the IDE. Our solution was two fold.
//Instance only props if(curObj.elementType == "instance") {//instance returnString += ("<prop propName=\"libraryName\" value=\"" + curObj.libraryItem.name + "\"/>"); //check for persitant data if(curObj.hasPersistentData("uniqueID")) {//use old data returnString += ("<prop propName=\"uniqueID\" value=\"" + curObj.getPersistentData("uniqueID") + "\"/>"); }//use old data else {//make new curObj.setPersistentData("uniqueID", "string", createUUID()); returnString += ("<prop propName=\"uniqueID\" value=\"" + curObj.getPersistentData("uniqueID") + "\"/>"); }//make new }//instance else {//not an instance returnString += ("<prop propName=\"libraryName\" value=\"null\"/>"); returnString += ("<prop propName=\"uniqueID\" value=\"null\"/>"); }//not an instance
The first solution was to use an odd method for IDE elements of type “instance” called setPersistentData(). This method will allow you to store some data with an object in the .fla that is saved with the .fla. Soooo, that means we can make our own ID for the object, and pass that ID back to the panel. When something is selected in the panel, our custom ID is passed to the JSFL function, and we can then search through the DOM to find our object. Be aware that every time you have an object on a different keyframe in the IDE, that is technically a new instance, which means it will get its own ID, so there is no continuity there.
var propObj = {}; propObj["x"] = curObj["x"]; propObj["y"] = curObj["y"]; propObj["scaleX"] = curObj["scaleX"]; propObj["scaleY"] = curObj["scaleY"]; propObj["rotation"] = curObj["rotation"]; propObj["Layer"] = curObj["layer"]; //loop through objects and match all recorded properties for(var i = 0; i < objList.length; i++) {//check each object var curObj = objList[i]; for (var prop in propObj) {//check each prop desc object var propsMatch = true; //var propObj = propListObj[j]; if(prop == "Layer") {//layer if(propObj[prop] != curObj['layer']['name']) {//no match propsMatch = false; break; }//no match }//layer else if(propObj[prop] != curObj[prop]) {//no match.. fail propsMatch = false; break; }//no match.. fail }//check each prop desc object if(propsMatch) {//found one selectionObj = curObj; return curObj; }//found one }//check each object
That first solution is fine unless you want to try to select a shape or something that isn’t a symbol or bitmap. So we needed a fallback method of selecting items that couldn’t have persistent data stored on them. The resulting second solution was to basically “guess” based on matching properties. So in true brute force style, we loop through each object in the IDE that we care about and compare its properties to a property object that is sent from the panel to JSFL. “But Wait!”, I hear you cry. “You said we can’t pass objects, only strings!”. That is still correct, so we build an object from a string:
eval('[{"name":"_testMC","id":"ADEC39E38EDB492F81DDD20836ABD7E5","type":"instance"}]');
Please note that is just an example, more properties are in that string than the 3 listed. And yes we know its awful, but it does in fact work.
So far we’ve sort of glossed over one very important problem, how to get information about an object from JSFL back to the panel. The answer we came up with was our old friend XML.
function serialize(list) {//serialize var returnString = "<objects>"; for(var i = 0; i < list.length; i++) {//list returnString += ("<object>"); var curObj = list[i]; for( var prop in curObj) {//each prop //fl.trace("Prop: " + prop); if(prop == "brightness" || prop == "tintColor" || prop == "tintPercent" || prop == "actionScript") {//clean returnString += ("<prop propName=\"" + prop + "\" value=\"" + ("null") + "\"/>"); }//clean else {//normal prop returnString += ("<prop propName=\"" + prop + "\" value=\"" + (curObj[prop]) + "\"/>"); }//normal prop }//each prop //Layer props returnString += ("<prop propName=\"layerLocked\" value=\"" + curObj.layer.locked + "\"/>"); returnString += ("<prop propName=\"layerName\" value=\"" + curObj.layer.name + "\"/>"); //Instance only props if(curObj.elementType == "instance") {// returnString += ("<prop propName=\"libraryName\" value=\"" + curObj.libraryItem.name + "\"/>"); //check for persitant data if(curObj.hasPersistentData("uniqueID")) {//use old data returnString += ("<prop propName=\"uniqueID\" value=\"" + curObj.getPersistentData("uniqueID") + "\"/>"); }//use old data else {//make new curObj.setPersistentData("uniqueID", "string", createUUID()); returnString += ("<prop propName=\"uniqueID\" value=\"" + curObj.getPersistentData("uniqueID") + "\"/>"); }//make new }// else {//not an instance returnString += ("<prop propName=\"libraryName\" value=\"null\"/>"); returnString += ("<prop propName=\"uniqueID\" value=\"null\"/>"); }//not an instance returnString += "</object>"; }//list returnString += ("</objects>"); //fl.trace("ObjString: " + returnString); return returnString; }//serialize
What we do is walk through each property on the object and build one long XML string from it manually. Additionally we add any custom properties that we need such as the ID mentioned earlier. Its at this point that the persistent data is set and retrieved, if available.
Pitfalls, ATARI Style
This is a random list of issues that we have run into with JSFL thus far:
We have emerged from the depths, a little older, and a little jaded (mostly because of the flying monkeys), but fairly successful. So please let us know what you think of this panel and if its managed to help in your every day production!
Instructions
To install, save the .mxp file from the link below, and either double click on the .mxp or select File -> Install Extension in the Adobe Extension Manager. Finally restart Flash if you had it open during the install.
To run the panel, in the Flash IDE go to Window -> Other Panels -> HookOutliner
To use select your filter method on the left (Objects On Frame drop down list). This will populate the grid below with information about the objects from that filter. You can click on an object in that grid to select it, shift select for multiple.
The Select All and Select None buttons will select all things that are not locked in the grid, and the select none button will deselect everything.
The Auto Unlock On Pick will automatically unlock the layer and select whatever you select from the grid.
The Lock Other Layers button will lock all layers that are not selected. This can be useful to ensure that you are only manipulating the objects you want.
The Edit Clip button will take you into a selected instance.
The top row of buttons will change the size of the grid.
The Auto Refresh and Force Refresh buttons change how often the grid is updated. If Auto Refresh is checked, it will update the grid every half second or so. You can click the Force Refresh button to update the grid immediately.
This panel is still very beta and the known issues are as follows:
If you find a bug, please let us know in the comments below. Thanks!
This product is licensed under GPLv3.
Oh my goodness! an amazing article. Thank you!
North American Hockey League (NHL) 4 finals in the second end of Vancouver Rogers Stadium. Vancouver Canucks first goal in extra time, so that rivals the Boston Bruins ‘sudden death’, which ended 3-2 victory and 2-0 total points in the Stanley Cup finals to stay ahead. Winning goal by striker Abel Ross added 11 seconds left in overtime when hit. At that time, he received a pass from his teammates quickly advancing from the left wing, escaped Bruins goalie Tim Thomas of the block.
[...] What are you waiting for go get it here? [...]
[...] seeing how some of this flash panel stuff is done, please check out our other posts on the subject: Hook Outliner and Hook [...]