I've just managed to finish my very first Silverlight project. Using Silverlight2 I was tasked with making a simple Flash application into a Silverlight application. Was just a 1:1 conversion.
So I thought I'd share some experiences with you.
The tools I used were Microsoft Expression Blend and Visual Studio 2008. I'd feel utterly lost if I didn't have both programs to complement each other. There are for example no codehinting whatsoever in Blend. And there are little to no design area thats working properly in VS2008. Therefore I was more or less forced to use Blend for the designing of buttons, colors etc, and VS2008 for coding in the code-behind files.
It is however quite obvious that these two programs are intended to complement each other, and do so nicely. Making events etc are amazingly easy. You pretty much get a list with available events such as Click, OnLeftMouseDown, KeyUp for each element you select.
Another thing I would like to write a little about is the Templates idea thats implemented in Blend. The idea is simply to be able to create a "skin" which you can add to similar elements, for example a button. With a template you can set color, mouseover-color, hit-color or if you'd like you can create some animations into the templates by adding storyboards(which works pretty much exactly like the Flash-timeline). Thats sweet. Almost. When editing a button, you can easily change the Text-property to whatever text you'd like your button to read. When adding a template however, you overwrite the button completely. Meaning the Text property in the button isn't viewable. This might be me not realising something, like some setting somewhere to set it so it displays the text on top, but in my opinion this should be a standard behavior. If you have a button, and you have a Text property, you most likely want it to show on your button. Even if you change the color of the button.
So my experiences coming from a Flash background with Silverlight2? It's pretty straightforward, and everything is made pretty logical. Without any Visual Studio experience. The Storyboard concept is easy to grasp, and you have no problems creating and manipulating the timelines. Creating events I dare say are way easier in Blend. You simply type in the name you want for your event in the box(that says what you want it to fire on) and you get launched into VS2008 where it creates the event for you, and you just have to code in what you want to happen next. It can be pretty hard to at first realise you need to keep track of both the xaml-code and the c#-code tho. Like if you remove your event from the C#-file, your silverlight application will not send you an error, but will stand at the loading-screen at 100% indefinetly, and never actually launching. This means you forgot to remove the event form your xaml-code. Good thing to keep in mind! Without any error-message atleast I got a bit confused.
I ended up two times in this short project stuck in things that didnt work, and I couldn't figure out what was wrong, did the same thing again, and it strangely worked. The first time I thought I probably missed some step, but the third time it happened, I carefully reviewed what I had done and redid it. And it worked. Might be something with the events created by Blend or something like that.
So to sum it up, using Microsoft Expression Blend and Visual Studio 2008 to create Silverlight2 applications are as easy as pie. Sometimes too easy. With too much help from Blend, you can easily end up with code you don't actually realise you have. The Templates section really needs some work in my opinion. And it sometimes doesnt feel completely finished. But I see alot of potential in Silverlight for the future.
2008-10-29
2008-10-03
Mazegame solution
To day I was facing a classical problem. I wanted to build a classic maze game. The players task is to guide a charachter out of the maze with help of the arrowkeys on the keyboard .
The classic way to do this is to create MovieClips of all the walls in the level, add them to an array, then loop through the array to find if the charachter hits a wall with the hitTest function. This is a bit clumsy solution if the maze is complex with multiple walls or if I want to make many levels. I can't hitTest one single movieclip with the whole maze graphics in it, because the hitTest function tests the graphics boundingrectangle.
The solution I came up with was to check the colors around the charachter instead. So I just loaded a png image with transparent background and black lines as walls. Then I extracted the bitmapdata with bitmapdata.draw(mazeImage). After that I scanned the charachters boundingrectangle outlines for colors others then white with help of getPixel(x, y). To get the pixels around the charachter I made four loops, one loop for each side, like this one:
for(i=0; i<(player.width+1)/tolerance; i++)
{
xScan = player.x + (i*tolerance);
yScan = player.y;
controlHit(bitmapData.getPixel(xScan, yScan), xScan, yScan);
}
Then depending on where the pixel is found i bouncedback the charachter in the opposite direction.
To check if the charachter has reached the goal I just made the goal in a specifik color.
The classic way to do this is to create MovieClips of all the walls in the level, add them to an array, then loop through the array to find if the charachter hits a wall with the hitTest function. This is a bit clumsy solution if the maze is complex with multiple walls or if I want to make many levels. I can't hitTest one single movieclip with the whole maze graphics in it, because the hitTest function tests the graphics boundingrectangle.
The solution I came up with was to check the colors around the charachter instead. So I just loaded a png image with transparent background and black lines as walls. Then I extracted the bitmapdata with bitmapdata.draw(mazeImage). After that I scanned the charachters boundingrectangle outlines for colors others then white with help of getPixel(x, y). To get the pixels around the charachter I made four loops, one loop for each side, like this one:
for(i=0; i<(player.width+1)/tolerance; i++)
{
xScan = player.x + (i*tolerance);
yScan = player.y;
controlHit(bitmapData.getPixel(xScan, yScan), xScan, yScan);
}
Then depending on where the pixel is found i bouncedback the charachter in the opposite direction.
To check if the charachter has reached the goal I just made the goal in a specifik color.
2008-09-08
Papervision 3D hand cursor
viewport:Viewport3D's property buttonMode is what control if a hand cursor should show on ALL or NON 3D objects. If buttonMode=true the hand cursor will show whether or not the materials are interactive, or if you are listening for InteractiveScene3DEvents.
So if you need hand cursor to show all time, set viewport.buttonMode=true; and if you do not want a hand cursor anytime, set viewport.buttonMode=false;
If you need to show a hand cursor on specific target objects, you should set viewport.buttonMode=false; and then have each target object to set listeners. In those callback functions you must use CursorManager.setCursor(id:Class) | CursorManager.remove...
So if you need hand cursor to show all time, set viewport.buttonMode=true; and if you do not want a hand cursor anytime, set viewport.buttonMode=false;
If you need to show a hand cursor on specific target objects, you should set viewport.buttonMode=false; and then have each target object to set listeners. In those callback functions you must use CursorManager.setCursor(id:Class) | CursorManager.remove...
Labels:
3D,
cursor,
CursorManager,
Flex,
hand,
Papervision,
useHandCursor,
viewport
2008-09-02
Font Embed
I want to share my experience with working with Flex and font embedding. Today I noticed that an embedded asset including a symbol with text that embedded the same Font as the Flex compiler did - It seemed to result in a conflict of what scope (or sandbox?) the font should belong to.
1) Flex can embed fonts from a flash-8.swf by the @font-face {} tag.
2) Flex can embed fonts in an instantiated AS3 model to a Class type using the meta Embed tag.
3) Flex can also embed or dynamically load an asset.swf that includes embedded fonts, and then have those exposed to any scope (or sandbox?) that wish to register them. And the other way around - the loaded asset.swf may register the fonts embedded by Flex.
Later I will show how. I haven't yet figured out the reason for the above mentioned problem, but let me start by sharing a simple workaround. In this case the asset.swf had some symbols in library with non-dynamic textfields in it. They where imported or pasted into Flash from an Illustrator design. When embedding/loading these symbols, all other text in the applications other scopes did not render. The solution to this was simply to redo the import from Illustrator to Flash first after I made the Illustrator textfields to outlines.
1) Flex can embed fonts from a flash-8.swf by the @font-face {} tag.
2) Flex can embed fonts in an instantiated AS3 model to a Class type using the meta Embed tag.
3) Flex can also embed or dynamically load an asset.swf that includes embedded fonts, and then have those exposed to any scope (or sandbox?) that wish to register them. And the other way around - the loaded asset.swf may register the fonts embedded by Flex.
Later I will show how. I haven't yet figured out the reason for the above mentioned problem, but let me start by sharing a simple workaround. In this case the asset.swf had some symbols in library with non-dynamic textfields in it. They where imported or pasted into Flash from an Illustrator design. When embedding/loading these symbols, all other text in the applications other scopes did not render. The solution to this was simply to redo the import from Illustrator to Flash first after I made the Illustrator textfields to outlines.
Maintain thumbsize in a scrollbarcomponent.
I found a great tutorial how to set a constant thumbsize in a scrollbarcomponent.
http://npacemo.com/wordpress/2008/05/20/flex-3-designer-scrollbar-fixed-size-scrollthumb/
http://npacemo.com/wordpress/2008/05/20/flex-3-designer-scrollbar-fixed-size-scrollthumb/
2008-09-01
Problem removing blankspaces with split(" ").join("");
Yesterday I was playing around with Adobe Air and flex. My goal was to parse a productprice from an HTML-site by splitting the pricetag in to ”kronor” and ”oren” (pounds and cents) My only problem was that ”kronor” below 10 just contained one number, before that number was a blankspace to balance the textalign on the site.
So I built a function that was suppose to remove the blankspaces with split(” ”).join(””); , but no success. After some testing I tryed the string.charCodeAt(index:Number = 0);. This function returns a “charCode” , the numeric Unicode character code of the character ,at a specified index, this gave the result 160. After some googleing on unicode 160 I found this site http://bytes.com/forum/post1462660-5.html. This sort of blankspace-characters are called a ”non breaking space” http://en.wikipedia.org/wiki/Non-breaking_space. I also found that there are some more types of whitespace - characters that may be used by unicode but not removed by the split(” ”).join(””) – function. So to solve this problem I some how had to force the special character in to the splitjoin-function. After some reading about strings on ”livedocs” ,http://livedocs.adobe.com/flex/3/langref/String.html ,I found the String.fromCharCode(), that simply translates a list of commaseparated Unicodenumbers to a string. So instead of using split(“ “).join(“”) to remove the non breaking space I used split(String.fromCharCode(160)).join("").
So I built a function that was suppose to remove the blankspaces with split(” ”).join(””); , but no success. After some testing I tryed the string.charCodeAt(index:Number = 0);. This function returns a “charCode” , the numeric Unicode character code of the character ,at a specified index, this gave the result 160. After some googleing on unicode 160 I found this site http://bytes.com/forum/post1462660-5.html. This sort of blankspace-characters are called a ”non breaking space” http://en.wikipedia.org/wiki/Non-breaking_space. I also found that there are some more types of whitespace - characters that may be used by unicode but not removed by the split(” ”).join(””) – function. So to solve this problem I some how had to force the special character in to the splitjoin-function. After some reading about strings on ”livedocs” ,http://livedocs.adobe.com/flex/3/langref/String.html ,I found the String.fromCharCode(), that simply translates a list of commaseparated Unicodenumbers to a string. So instead of using split(“ “).join(“”) to remove the non breaking space I used split(String.fromCharCode(160)).join("").
2008-08-31
Papervision 3D Framerate
Let us share some constructive hands-on tips or trix on how to increase framerates. There's a lot of design issues from modeling 3D-objects to bind them to value-/business objects (classes). However, you should also take the rendering principals of the Flashplayer and your 3D-API in consideration! The gems in this topics may be, in no particular order, under clever headlines, so feel free to contribute by comments.
1) Override Flex default framerate of 24 by setting the desired framerate Application property in *.mxml (haven't got it to work from CSS). Try framerate 60, and hope that the Flashplayer may reach the fps of 40 once in a while.
2) Ask your self when and why you need to run onEnterFrame()! Even for a design built with constant movement in mind, it is a good strategy to provide for situations where a 3D world is allowed to freeze for a couple of seconds. If you can make it freeze without the user notice it - good for you. You will need that time to load additional assets or resources.
IMPORTANT!!! Try NOT to use onEnterFrame() at all in your 3D viewport rendering engine! Try the Timer intervall instead. And above all - try NOT to use two or more onEnterFrame()-callbacks in parallell, that will probably sink you application framerate below 10 fps.
In PV3D all DisplayObject3D have a pointer to the scene or context they belong to. Therefor you may choose to use the Scene object as the eventdispatcher for such events as PLAY and STOP to control when to stop or run the onEnterFrame.
3) If you know, or if the 3D object or face itself know that it should be invisible - set its visibility to false. In that way the the culling process does not even need to calculate.
4) Z-depth and culling calculus are process intensive. One finding I had was with a cube, or rather six individual planes, with no awareness of eachother, in a group forming a cubelike shape. I wanted to keep the cube in constant movement of a Perlin noiced sine function in each degree of freedom (DOF). The cube was rotated with in a slow wobbeling motion showing the same three faces to the camera, two sides and the rooftop. I found that the framerate decreased when one face - the rooftop - had it's normal almost perpendicular to the camera. The more the rooftop pointed it's normal direction towards the camera the framerate increased.
Thus, a large surface with a tiny projection to the view plane is more computationally heavy than when the same side projects more of its surface to the view plane! By rotating the default angle of my cube slightly, the culling did not kick in, and I increased the framerate by two times.
5) Over all for FlashPlayer rendering, check how Bitmap Cache Policy can be used in your project..
1) Override Flex default framerate of 24 by setting the desired framerate Application property in *.mxml (haven't got it to work from CSS). Try framerate 60, and hope that the Flashplayer may reach the fps of 40 once in a while.
2) Ask your self when and why you need to run onEnterFrame()! Even for a design built with constant movement in mind, it is a good strategy to provide for situations where a 3D world is allowed to freeze for a couple of seconds. If you can make it freeze without the user notice it - good for you. You will need that time to load additional assets or resources.
IMPORTANT!!! Try NOT to use onEnterFrame() at all in your 3D viewport rendering engine! Try the Timer intervall instead. And above all - try NOT to use two or more onEnterFrame()-callbacks in parallell, that will probably sink you application framerate below 10 fps.
In PV3D all DisplayObject3D have a pointer to the scene or context they belong to. Therefor you may choose to use the Scene object as the eventdispatcher for such events as PLAY and STOP to control when to stop or run the onEnterFrame.
3) If you know, or if the 3D object or face itself know that it should be invisible - set its visibility to false. In that way the the culling process does not even need to calculate.
4) Z-depth and culling calculus are process intensive. One finding I had was with a cube, or rather six individual planes, with no awareness of eachother, in a group forming a cubelike shape. I wanted to keep the cube in constant movement of a Perlin noiced sine function in each degree of freedom (DOF). The cube was rotated with in a slow wobbeling motion showing the same three faces to the camera, two sides and the rooftop. I found that the framerate decreased when one face - the rooftop - had it's normal almost perpendicular to the camera. The more the rooftop pointed it's normal direction towards the camera the framerate increased.
Thus, a large surface with a tiny projection to the view plane is more computationally heavy than when the same side projects more of its surface to the view plane! By rotating the default angle of my cube slightly, the culling did not kick in, and I increased the framerate by two times.
5) Over all for FlashPlayer rendering, check how Bitmap Cache Policy can be used in your project..
2008-08-30
3D General
Let's talk about how to optimize 3D for Flex. Keep it low poly, and simple texturing to allow more cpu and framerate. You will need the power for animation in both 3D viewport and 2D. There are several 3D API's and their offsprings for Flex/Flash developers out there. I tried Sandy, but got stucked with Papervision - in the summer of 2008 the branch GreatWhite Effects. Whether they are called GreatWhite, Away3D or anything else I believe in general that it really doesn't matter which one you choose. You should try them all, but you can't be an expert on all of them, and you should choose the one targeted to solve your specific problems or design needs.
3D on the web is not to be compared to modern 3D technology for film effects, computer games, or game consols! The open source community mentioned aboved are doing a tremendous job putting effort into making the API's both stable and effectfull, but as long as it is all about software rendering we must consider following when starting a new project:
a) From a technical point of view, keep it simple shading, low poly, and light-weight texturing!
b) From a design point of view - are you sure 3D is going to add to user experience at all?
These are two mayor considerations when first meeting your client. You don't want to end up with a disapointed client stating that you did not live up to their expectations!
... to be continued
3D on the web is not to be compared to modern 3D technology for film effects, computer games, or game consols! The open source community mentioned aboved are doing a tremendous job putting effort into making the API's both stable and effectfull, but as long as it is all about software rendering we must consider following when starting a new project:
a) From a technical point of view, keep it simple shading, low poly, and light-weight texturing!
b) From a design point of view - are you sure 3D is going to add to user experience at all?
These are two mayor considerations when first meeting your client. You don't want to end up with a disapointed client stating that you did not live up to their expectations!
... to be continued
AIR Garbage Collection and Memory Leaks
Summary: Following insights came to me when debugging a new AIR application that was built to be used as a printing service, polling a database for printjobs, setting a timeout and then repeatedly poll again. Using Flex Builder Profiler showed first an obvious memory leak as several of the project classes increased in number of instances over time. This resulted in a crash of the Air Debug Launcher (adl.exe). However, after fixing the obvious by best practise as described below, and verifying that the Profilers memory profile was kept constant, I found that the total memory usage given by the Profilers analysis was NOT the same as the trace of flash.system.System.totalMemory!
Best practise in programming AS3, whether you are hacking *.as classes or *.mxml, should always use a destuctor function. Let us use the same convention as for the good example of the BitmapData class, and call it dispose():void. That will also make good sens to you .Net hackers for the same reason. The BitmapData.dispose() function frees memory for garbage collection.
You will have to make sure the dispose functions are called when instantiated objects no longer are in used and need to be referenced. Creating a new Array() of objects, running ArrayCollection.removeAll(), or setting each object to null does not guarantee garbage collection. References within the objects still may be held by listeners and/or other classes.
For *.mxml you will most likely use protected functions as onCreationComplete():void and onAddedToStage():void to do some initialization, bindings, and listening - just as with the class constuctor. Just make sure you also declare a public function dispose():void that eather can be called internally by a remove event, or externally by any controller or command. The function may look something like this:
Being consequent and thorough will also give you the possibillity to force garbage collection once in a while between two frames by doing the following:
Thank you Grant skinner. Read more:
www.flexdeveloper.eu
blogs.warwick.ac.uk
www.gskinner.com
Best practise in programming AS3, whether you are hacking *.as classes or *.mxml, should always use a destuctor function. Let us use the same convention as for the good example of the BitmapData class, and call it dispose():void. That will also make good sens to you .Net hackers for the same reason. The BitmapData.dispose() function frees memory for garbage collection.
You will have to make sure the dispose functions are called when instantiated objects no longer are in used and need to be referenced. Creating a new Array() of objects, running ArrayCollection.removeAll(), or setting each object to null does not guarantee garbage collection. References within the objects still may be held by listeners and/or other classes.
For *.mxml you will most likely use protected functions as onCreationComplete():void and onAddedToStage():void to do some initialization, bindings, and listening - just as with the class constuctor. Just make sure you also declare a public function dispose():void that eather can be called internally by a remove event, or externally by any controller or command. The function may look something like this:
public function dispose():void {
bitmapData.dispose();
swfLoader.loaderInfo.loader.unload();
removeListener("type", callbackfunction);
CairngormEventdispatcher.getInstance().removeListener(
"type", callbackfunction);
file.close();
... and so forth
}
Being consequent and thorough will also give you the possibillity to force garbage collection once in a while between two frames by doing the following:
private var gcCount:int;
private function startGCCycle():void{
gcCount = 0;
addEventListener(Event.ENTER_FRAME, doGC);
}
private function doGC(evt:Event):void{
flash.system.System.gc();
if(++gcCount > 1){
removeEventListener(Event.ENTER_FRAME, doGC);
clearTimeout(timeOut);
timeOut = setTimeout(lastGC, 50);
}
}
private function lastGC():void{
clearTimeout(timeOut);
flash.system.System.gc();
}
Thank you Grant skinner. Read more:
www.flexdeveloper.eu
blogs.warwick.ac.uk
www.gskinner.com
2008-08-28
AIR and the pitfalls of the Security Sandbox (and eternal damnation of Singletons)
For a few days now I've been working on an AIR application to generate XML for a product called Sound Manager. Sound Manager, which we built for a client of ours - DinahMoe, is a pretty nifty system which allows them to hand over a couple of as-files to their clients through which the end client's developers can dispatch events describing what's going on in the Flash app. DinahMoe can then orchestrate sounds for the app by linking the events to actions playing, stopping, pausing, transforming, jumping and so forth, in sounds.
In order to lower the complexity for the end client, we put all the code that actually does anything into the sound libraries that are compiled by the guys at DinahMoe (or is it just guy, Johan?). The objects in the sound libraries then listen for the Sound Managers events and do what has to be done... and what has to be done is off course described in XML.
So the AIR admin lets DinahMoe build the XML through a rich interface where actions can be categorized in folders, copied, edited and so on. Then it was time for the testing. In short, I wanted the AIR app to instanciate the SoundManager, tell it to load the XML and the sound libs and then fire the events I wanted to test. Here's where everything goes pear shaped. The AIR app lives in the Application Sandbox, the loaded swf:s liv in the Loca file Sandbox. So they each instanciate their own singletons and all communication is lost.
So I built a swf which instanciates the SoundManager, and let the AIR app load the SWF... but as it was placed inside the apps application directory, it was too run in the Application Sandbox. So I copied it to the same directory as the Sound libraries that were to be loaded... and now they all ended up in the same Sandbox... but still they could not communicate. And here's where I lost hope. So now I return handles through the SoundLibraries and call each and everyone of them with each event. Not pretty but It's the best I could com up with.
So what is the deal? Are SWF:s in the Local File Sandbox prohibited from talking to each other? Is there a solution? Anyone?
In order to lower the complexity for the end client, we put all the code that actually does anything into the sound libraries that are compiled by the guys at DinahMoe (or is it just guy, Johan?). The objects in the sound libraries then listen for the Sound Managers events and do what has to be done... and what has to be done is off course described in XML.
So the AIR admin lets DinahMoe build the XML through a rich interface where actions can be categorized in folders, copied, edited and so on. Then it was time for the testing. In short, I wanted the AIR app to instanciate the SoundManager, tell it to load the XML and the sound libs and then fire the events I wanted to test. Here's where everything goes pear shaped. The AIR app lives in the Application Sandbox, the loaded swf:s liv in the Loca file Sandbox. So they each instanciate their own singletons and all communication is lost.
So I built a swf which instanciates the SoundManager, and let the AIR app load the SWF... but as it was placed inside the apps application directory, it was too run in the Application Sandbox. So I copied it to the same directory as the Sound libraries that were to be loaded... and now they all ended up in the same Sandbox... but still they could not communicate. And here's where I lost hope. So now I return handles through the SoundLibraries and call each and everyone of them with each event. Not pretty but It's the best I could com up with.
So what is the deal? Are SWF:s in the Local File Sandbox prohibited from talking to each other? Is there a solution? Anyone?
All Your Base Are Belong To Flex, Flash, AIR, .NET and such
Here we go. This is the official All Your Base blog for sharing our wisdom and experience in developing RIA:s with Flash, Flex, AIR, .NET and all other things cool and nerdy.
Subscribe to:
Posts (Atom)