Tutorial 2: Managing XML/SWF payloads easily

by Tom Byrne 13. May 2010 08:48

This is part two of a three part tutorial.

  1. Using Multiple XML sources
  2. Loading classes from other SWFs
  3. Download Source

1. Using Multiple XML sources

If your app is big enough, you won’t want your users to download all XML content when it starts, you’ll want to break down content into chucks which are loaded only when the app needs them. This is easy in SiteStream by linking your XML sources together.

This example expands on the example in Part 1 by including a second XML file (myXML2.xml), this is done by adding a ‘url’ attribute to the requested item (root/item2 in myXML.xml), which is recognised by SiteStream as a special attribute. This second XML file contains the fully populated version of the object (as opposed to the empty ‘stub’ version in the myXML.xml file). Notice that the code hasn’t changed at all, SiteStream will realise that the requested item references another XML source and will automatically load in the additional content. This would also be the case if the code requested any object deeper within the myXML2.xml file (i.e. “root/item2/childObject”).

 

myXML.xml:

<Array id="root"
myPackage="myPackage.*">
<myPackage:MyClass id="item1"/>
<myPackage:MyClass id="item2" url="myXML2.xml"/>
<myPackage:MyClass id="item3"/>
<myPackage:MyClass id="item4"/>
<myPackage:MyClass id="item5"/>
</Array>


myXML2.xml:

<myPackage:MyClass id="item2" colour="0xff0000"
myPackage="myPackage.*">
<myPackage:MyClass id="childObject"/>
</myPackage:MyClass>

 

SiteStreamTest.as:

package
{
import org.farmcode.siteStream.SiteStream;
import flash.display.Sprite;
import myPackage.MyClass;
public class SiteStreamTest extends Sprite
{
public function SiteStreamTest(){
var includeClass:Class = MyClass;
var siteStream:SiteStream = new SiteStream();
siteStream.rootURL = "myXML.xml";
siteStream.getObject("root/item2",onSuccess);
}
private function onSuccess(e:MyClass):void{
trace("Item 2 retrieved");
}
}
}

 

The SiteStream object has a property 'baseDataURL', which allows the setting of base URL which will be prepended to the beginning of the XML URLs included in your data. This property helps with multiple environments where the relative path to XML files might change. This value is not used for the root XML load.

2. Loading classes from other SWFs

When you’re app becomes large, you may wish to break down the SWF into several parts (‘Modules’ in Flash Builder), generally the issue with this is loading these SWFs in at the right time. SiteStream makes this very easy by allowing SWFs to be referenced in the package/namespace declarations of the root node.

Note, the only change I have made to SiteStreamTest.as is removing the reference to MyClass (to avoid MyClass being included in the main SWF).

The following example builds on example in Part 1, notice the MyClass Class has been moved from the SiteStreamTest SWF into the CodeLibrary SWF, which is then referenced in the XML package declaration.

myXML.xml:

<Array id="root"
myPackage="CodeLibrary.swf::myPackage.*">
<myPackage:MyClass id="item1"/>
<myPackage:MyClass id="item2"/>
<myPackage:MyClass id="item3"/>
<myPackage:MyClass id="item4"/>
<myPackage:MyClass id="item5"/>
</Array>

SiteStreamTest.as:

package
{
import org.farmcode.siteStream.SiteStream;
import flash.display.Sprite;
public class SiteStreamTest extends Sprite
{
public function SiteStreamTest(){
var siteStream:SiteStream = new SiteStream();
siteStream.rootURL = "myXML.xml";
siteStream.getObject("root/item2",onSuccess);
}
// Removed reference to MyClass to avoid inclusion
private function onSuccess(e:*):void{
trace("Item 2 retrieved");
}
}
}

 

CodeLibrary.as:

package
{
import myPackage.MyClass;
public class CodeLibrary extends Sprite
{
public function CodeLibrary(){
var includeClass:Class = MyClass;
}
}
}

The SiteStream object has a property 'baseClassURL', which allows the setting of base URL which will be prepended to the beginning of the SWF URLs included in your data. This property helps with multiple environments where the relative path to SWF files might change.

 

3. Download Source

 

< Goto Tutorial 1 | Goto Tutorial 3 >

Tags:
Categories:

Tutorial 3: Node Referencing & Alternative Parsers

by Tom Byrne 13. May 2010 08:47

This is part three of a three part tutorial.

  1. Node References
  2. Configuring SiteStream URLs
  3. Download Source

1. Node References

SiteStream XML allows you to have references between objects that will be resolved when parent item is resolved.

This can help in:

  • Reducing the amount of XML and processing by listing common objects once and referencing them multiple times.
  • Help create complex relationships between objects that would otherwise require lengthy coding.

The syntax used to reference other nodes is:

childObject="{root/item2}"

Where 'childObject' is the name of the property this reference will fill.

Or, if it is preferrable to use a node to reference:

<String id="childObject">{root/item2}</String>

References do not need to remain within their own XML file/source and can reference items anywhere in all of your linked XML sources.

To expand on the example in part 1, I will add a reference to item2 which will result in item2's 'childObject' object being pointed to item3.

myXML.xml:

<Array id="root"
myPackage="myPackage.*">
<myPackage:MyClass id="item1"/>
<myPackage:MyClass id="item2" childObject="{root/item3}"/>
<myPackage:MyClass id="item3"/>
<myPackage:MyClass id="item4"/>
<myPackage:MyClass id="item5"/>
</Array>

Currently SiteStream only allows for absolute paths in Node References, meaning that if an XML file's position relative to the root changes, so to must any references pointing into it (including those within the file itself).

 

2. Alternative Parsers

The rules and methods SiteStream uses to parse XML can be modified on a per item basis. This is done by having an item (created in your XML) implement the ISiteStreamItem interface, this will then allow full control over how this item (and it's descendants) are parsed.

Note that the item itself will be created with it's parents settings (or the default settings if no ancestor was an ISiteStreamItem), as SiteStream doesn't know if it is an ISiteStreamItem until after it is created. All subsequent properties and children will then be parsed with the newly created object.

This feature has most commonly been used to create collections of items which only get resolved when they are specifically requested (as opposed to their parent object), which can be very useful for creating libraries of objects.

 

3. Download Source

 

< Goto Tutorial 2

Tags:
Categories:

Why SiteStream?

by Tom Byrne 13. May 2010 07:14

SiteStream is an AS3 library for parsing XML into first-class objects, read a basic tutorial here.

SiteStream aims to solve certain inflexibilities of AS3. It is not a replacement for flashvars or Remoting/AJAX. It is better to consider it a non-compiled part of your application.

Consider these traits of AS3/Flash Player:

  • AS3 is strongly typed and pre-compiled, meaning that code dependancies will automatically get bundled together without special provisions to split them into seperate SWFs (like Flash Builder's 'Module's).
  • The Flash Player offers no control over the order in which parts of a SWF are loaded (I have a feeling they do this for server compatibility reasons).
  • Which of these SWFs to load, and in what order, enerally depends on which parts of your app the user visits.

These SWFs quickly become unmanagable, SiteStream provides a simple way of loading the appropriate SWF at the appropriate time.

The XML files themselves are also only loaded when needed by the app.

Whilst AS3 (and other C-based languages) are great for fine functional programming, XML based languages have long been recognised as stronger in broader architectural programming situations (e.g. MXML, XAML, etc). SiteStream allows the core displays, data-sources and visual assets of your app to be configured and assembled in XML.

The benefits of doing this in a non-compiled format (as opposed to MXML):

  • Implicit management of SWF loading.
  • Easily switch from XML files to dynamically generated XML output (from any number of existing CMSs).

After saying all of this, SiteStream doesn't impose any rules upon it's use in your applications, and if you want to use it purely for the loading of data structures you're perfectly free to do so.

Tags:
Categories: Flash | SiteStream

Sodality Simple Tutorial in Flash Builder (formerly Flex)

by Tom Byrne 16. July 2009 03:14

This tutorial goes through the building of a simple demo app with Sodality. If it’s moving too slow for you I’d recommend downloading the source and only going through the checkpoint steps of the tutorial (3, 5 & 7).

  1. Project Setup
  2. Basic Sodality interactions
  3. Checkpoint 1
  4. Inserting Asychronous behaviour
  5. Checkpoint 2
  6. Intercepting before and after
  7. Checkpoint 3
  8. Download Source

1. Project Setup

  • In Flash Builder, set up a new ActionScript project (i.e. NOT a Flex Project) named ‘SodalitySimpleDemo’.
  • Download the Sodality SWC and add it to your project, making sure you add it to your source (Project Properties > ActionScript Build Path > Library path > Add SWC).
  • Open the Compiler Settings (Project Properties > ActionScript Compiler) and uncheck ‘Copy non-embedded…’, uncheck ‘Generate HTML Wrapper…’, this is not required by the framework but will help keep things clean.
  • Add the compiler argument ‘-keep-as3-metadata+=Advice,Trigger,Property’ to the ‘Additional Compiler Arguments’ field (still in the Compiler Settings area). This tells Flash Builder to keep the metatags we use to make sodality work.

2. Basic Sodality interactions

First we must add a President to the project, the president executes the core logic of Sodality.

Within the SodalitySimpleDemo class (which flash builder created in the default package), add this private variable:

private var president:President;

Add this to the SodalitySimpleDemo constructor to instantiate the president:

president = new President(this);

Passing through the root object to the president tells it how much of the display hierarchy should be managed by this president (i.e. in this case the whole hierarchy).

Now we must create an Advice class, this class represents a certain action that is taking place within your application. For simplicity’s sake we will create an Advice class representing a mouse click and we’ll create it in the default package. The MouseClickAdvice class will contain a message which will be displayed later:

package
{

import org.farmcode.sodality.advice.Advice;

public class MouseClickAdvice extends Advice
{
[Property(toString="true",clonable="true")]
public var message:String;

public function MouseClickAdvice(message:String=null){
super();
this.message = message;
}
}
}

Consider this Advice class similar to an event, we’ll use it to run other code when something gets clicked on. You’ll notice the message property is annotated with the ‘Property’ metatag, this forces the value to be copied over when the MouseClickAdvice is cloned in the dispatching process, generally all public properties of Advice classes should be annotated in this way. You’ll also note that the constructor arguments must always be optional in Advice subclasses, this is again to allow the cloning behaviour that Advice undergoes.

Next, we’ll add a button to the SodalitySimpleDemo class which we'll use to dispatch our advice, add this to the constructor:

var button:Sprite = new Sprite();
button.graphics.beginFill(0xff0000);
button.graphics.drawRect(100,100,100,100);
button.addEventListener(MouseEvent.CLICK, onMouseClick);
button.buttonMode = true;
addChild(button);

And then we’ll add the event handler which will dispatch the MouseClickAdvice:

protected function onMouseClick(e:MouseEvent):void{
dispatchEvent(new MouseClickAdvice("Button Clicked"));
}

Now the MouseClickAdvice is being dispatched but nothing is listening for it. Before we do that we'll add a text field to the SodalitySimpleDemo class that we can display the message in. Add this private property to the class:

private var resultField:TextField;

And add this to the constructor:

resultField = new TextField();
addChild(resultField);

Now we'll add a function to the SodalitySimpleDemo class which will catch the advice (using the Sodality 'Trigger' Metadata), note that the method must be public for the President to find it:

[Trigger(triggerTiming="after")]
public function afterMouseClick(cause:MouseClickAdvice):void{
resultField.text = cause.message;
}

This is all fine except that Sodality won’t trigger functions within classes unless they’re tagged with the interface IAdvisor (this only works for DisplayObjects, more on that in Step 4).

So change the SodalitySimpleDemo class definition line to:

public class SodalitySimpleDemo extends Sprite implements IAdvisor

Back to top

3. Checkpoint 1

At this point your project should run happily and when you click on the red square, the text ‘Button Clicked’ should appear in the corner of the SWF.

Your classes should look like this:

SodalitySimpleDemo.as

package
{
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.text.TextField;

import org.farmcode.sodality.President;
import org.farmcode.sodality.advisors.IAdvisor;

public class SodalitySimpleDemo extends Sprite implements IAdvisor
{
private var president:President;
private var resultField:TextField;

public function SodalitySimpleDemo()
{
president = new President(this);

var button:Sprite = new Sprite();
button.graphics.beginFill(0xff0000);
button.graphics.drawRect(100,100,100,100);
button.addEventListener(MouseEvent.CLICK, onMouseClick);
button.buttonMode = true;
addChild(button);

resultField = new TextField();
addChild(resultField);
}
protected function onMouseClick(e:MouseEvent):void{
dispatchEvent(new MouseClickAdvice("Button Clicked"));
}
[Trigger(triggerTiming="after")]
public function afterMouseClick(cause:MouseClickAdvice):void{
resultField.text = cause.message;
}
}
}

MouseClickAdvice.as

package
{
import flash.display.DisplayObject;

import org.farmcode.sodality.advice.Advice;

public class MouseClickAdvice extends Advice
{
[Property(toString="true",clonable="true")]
public var message:String;

public function MouseClickAdvice(message:String=null){
super();
this.message = message;
}
}
}

Skip to Checkpoint 2

Back to top

4. Inserting Asychronous behaviour

You’ll be thinking to yourselves that this is just a convoluted event management system at this point, so now we’ll load a file in between when the button is clicked and when the handling function gets called (something that would be impossible with events).

We’ll change the demo so that when the red button is clicked another (non-visual) class loads a file before the ‘afterMouseClick’ handler function gets called.

First we'll create the file to load in, simply create a text file called 'file.txt' in the 'bin-debug' folder containing the words "File Loaded".

Now we’ll need to create a new interface representing an action (Advice) that needs to load a file. This represents all of the interactions that our file loading Advisor (which we’re about to build) will need from the advice:

package
{
import org.farmcode.sodality.advice.IAdvice;

public interface ILoadFileAdvice extends IAdvice
{
function get fileURL():String;
function set message(value:String):void;
}
}

Then we'll modify MouseClickAdvice to implement the new interface (and modify the constructor arguments to take a url):

package
{
import org.farmcode.sodality.advice.Advice;

public class MouseClickAdvice extends Advice implements ILoadFileAdvice
{
public function MouseClickAdvice(fileURL:String=null){
super();
this.fileURL = fileURL;
}

private var _message:String;
private var _fileURL:String;

[Property(toString="true",clonable="true")]
public function get fileURL():String{
return _fileURL
}
public function set fileURL(value:String):void{
_fileURL = value;
}
[Property(toString="true",clonable="true")]
public function get message():String{
return _message
}
public function set message(value:String):void{
_message = value;
}
}
}

Then we’ll need to create our new Advisor, whose responsibility it is to load a file whenever an action requires it:

package
{
import flash.events.Event;
import flash.net.URLLoader;
import flash.net.URLRequest;

import org.farmcode.sodality.advice.AsyncMethodAdvice;
import org.farmcode.sodality.advisors.DynamicAdvisor;

public class FileLoaderAdvisor extends DynamicAdvisor
{
private var lastCause:ILoadFileAdvice;
private var lastAdvice:AsyncMethodAdvice;

[Trigger(triggerTiming="before")]
public function beforeFileLoad(cause:ILoadFileAdvice,
advice:AsyncMethodAdvice, time:String):void{
lastCause = cause;
lastAdvice = advice;
var loader:URLLoader = new URLLoader();
loader.addEventListener(Event.COMPLETE, onLoadComplete);
loader.load(new URLRequest(cause.fileURL));
}
protected function onLoadComplete(e:Event):void{
lastCause.message = ((e.target as URLLoader).data as String);
lastAdvice.adviceContinue();
}
}
}

Much of this should be familiar, the class loads in a file and gets it's content. Have a look at the beforeFileLoad function’s parameters though, it is noteworthy that this class never references the MouseClickAdvice class, meaning that any other Advice class that implements ILoadFileAdvice will now be picked up by this class and load a file. Also of note is that the second parameter of the beforeFileLoad function is an AsyncMethodAdvice instance, this tells Sodality to wait until the ‘adviceContinue’ method is called on this instance before other listeners to this advice continue getting called.

The order in which the listeners get called is based on the ‘triggerTiming’ property within the ‘Trigger’ metatag. This is the reason that the 'afterMouseClick' method on the SodalitySimpleDemo will wait until after the file is loaded (and lastAdvice.adviceContinue() is called in 'onLoadComplete') before it gets called and sets the message into the TextField.

Before the new Advisor will do anything though, it must be added to the president. You can see it extends DyamicAdvisor instead of implementing IAdvisor (like SodalitySimpleDemo did), this is because non-visual Advisors need a little more logic to be added to the President. The first way a DynamicAdvisor can be added to the President is using the President's addAdvisor method, this is not the recommended way of doing it because it means references to the president must be passed around your application, tangling your logic. The preferrable way is to set the advisorDisplay property of the new Advisor to a DisplayObject which is added to the display hierarchy (beneath the original display you passed into the President when you created it).

Add this to the constructor of SodalitySimpleDemo to add the new Advisor:

var fileLoaderAdvisor:FileLoaderAdvisor = new FileLoaderAdvisor();
fileLoaderAdvisor.advisorDisplay = this;

The final touch is to modify the onMouseClick method to use the file's URL (as opposed to "Button Clicked"):

protected function onMouseClick(e:MouseEvent):void{
dispatchEvent(new MouseClickAdvice("file.txt"));
}

Back to top

5. Checkpoint 2

Now, when you run the app, click the red box, the file will be loaded in and it’s contents will be shown in the TextField.

The FileLoaderAdvisor class and the ILoadFileAdvice should be as shown in Step 4 above (they won’t change throughout the tutorial).

Your classes should look like this:

SodalitySimpleDemo.as

package
{
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.text.TextField;

import org.farmcode.sodality.President;
import org.farmcode.sodality.advisors.IAdvisor;

public class SodalitySimpleDemo extends Sprite implements IAdvisor
{
private var president:President;
private var resultField:TextField;

public function SodalitySimpleDemo()
{
president = new President(this);

var button:Sprite = new Sprite();
button.graphics.beginFill(0xff0000);
button.graphics.drawRect(100,100,100,100);
button.addEventListener(MouseEvent.CLICK, onMouseClick);
button.buttonMode = true;
addChild(button);

resultField = new TextField();
addChild(resultField);

var fileLoaderAdvisor:FileLoaderAdvisor = new FileLoaderAdvisor();
fileLoaderAdvisor.advisorDisplay = this;
}
protected function onMouseClick(e:MouseEvent):void{
dispatchEvent(new MouseClickAdvice("file.txt"));
}
[Trigger(triggerTiming="after")]
public function afterMouseClick(cause:MouseClickAdvice):void{
resultField.text = cause.message;
}
}
}

MouseClickAdvice.as

package
{
import org.farmcode.sodality.advice.Advice;

public class MouseClickAdvice extends Advice implements ILoadFileAdvice
{
public function MouseClickAdvice(fileURL:String=null){
super();
this.fileURL = fileURL;
}

private var _message:String;
private var _fileURL:String;

[Property(toString="true",clonable="true")]
public function get fileURL():String{
return _fileURL
}
public function set fileURL(value:String):void{
_fileURL = value;
}
[Property(toString="true",clonable="true")]
public function get message():String{
return _message
}
public function set message(value:String):void{
_message = value;
}
}
}

Skip to CheckPoint 3

Back to top

6. Intercepting before and after

For the final step, we build an (incredibly rudimentary) load monitor which outputs the time that outputs the time before and after the file loads.

Lets create our Load Monitor class:

package
{
import flash.display.Sprite;
import flash.text.TextField;
import flash.utils.getTimer;

import org.farmcode.sodality.advisors.IAdvisor;

public class LoadMonitor extends Sprite implements IAdvisor
{
private var textField:TextField;

public function LoadMonitor(){
textField = new TextField();
textField.y = 100;
addChild(textField);
}

[Trigger(triggerTiming="before")]
public function beforeFileLoad(cause:ILoadFileAdvice):void{
textField.text = "Load begun: "+getTimer();
}
[Trigger(triggerTiming="after")]
public function afterFileLoad(cause:ILoadFileAdvice):void{
textField.appendText("\nLoad finished: "+getTimer());
}
}
}

Notice that it has two triggered functions; one before the Advice is executed and one after the Advice executes. Also note that it implements the IAdvisor interface so that the President manages it.

Add these lines to the SodalitySimpleDemo constructor to add the LoadMonitor to our app:

var loadMonitor:LoadMonitor = new LoadMonitor();
addChild(loadMonitor);

Back to top

7. Checkpoint 3

Now when you run the app and click the red button, several things happen in this order:

  • The LoadMonitor outputs the time before any other Advisors see the advice.
  • The FileLoaderAdvisor begins loading the file, at this point the president waits for confirmation to continue.
  • The file finishes loading, the FileLoaderAdvisor calls the adviceContinue method, telling the President to continue.
  • The SodalitySimpleDemo sets the text in it’s TextField to the contents of the file.
  • The LoadMonitor outputs the time after the advice has been to all Advisors.

This process is done without the Advisors being aware of each other, allowing for a discreet relationship between code frameworks. The only thing tying the operations together is the Advice class which simply stores and transfers data.

NOTE: You may be wondering why the LoadMonitor’s beforeFileLoad function gets called before the FileLoaderAdvisor’s beforeFileLoad method, this is based on the order in which the Advisor’s are added. This is not an issue with the prebuilt Advisors and more control over ordering is coming in version 4 (see the roadmap in the repository).

Your main class should look like this (the MouseClickAdvice class didn’t change in this step):

SodalitySimpleDemo.as

package
{
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.text.TextField;

import org.farmcode.sodality.President;
import org.farmcode.sodality.advisors.IAdvisor;

public class SodalitySimpleDemo extends Sprite implements IAdvisor
{
private var president:President;
private var resultField:TextField;

public function SodalitySimpleDemo()
{
president = new President(this);

var button:Sprite = new Sprite();
button.graphics.beginFill(0xff0000);
button.graphics.drawRect(100,100,100,100);
button.addEventListener(MouseEvent.CLICK, onMouseClick);
addChild(button);

resultField = new TextField();
addChild(resultField);

var fileLoaderAdvisor:FileLoaderAdvisor = new FileLoaderAdvisor();
fileLoaderAdvisor.advisorDisplay = this;

var loadMonitor:LoadMonitor = new LoadMonitor();
addChild(loadMonitor);
}
protected function onMouseClick(e:MouseEvent):void{
dispatchEvent(new MouseClickAdvice("file.txt"));
}
[Trigger(triggerTiming="after")]
public function afterMouseClick(cause:MouseClickAdvice):void{
resultField.text = cause.message;
}
}
}

 

8. Download Source

Back to top

Tags:
Categories: Flash

Fast 2D Bezier Library for ActionScript 3

by Tom Byrne 6. July 2009 11:13

Recently we needed to do some animated curves in AS3, and after searching around for some time we found several libraries for easily drawing bezier paths. The problem was they were all very slow, this being a result of them using the lineTo method for drawing (resulting in a curve made up of many tiny straight lines).

The benefits of the lineTo method (over the curveTo method) are that the resulting drawing can be very accurate, and that the maths used is simpler, neither of these were necessary for what we were doing.

We wrote our library with some goals, firstly, that it used the curveTo for all of it’s drawing. We also wanted to be able to construct paths without specifying control points, just the main points that the path passed through and a “tension” to describe the curve that gets drawn between.

The code for the bezier library is part of our Open Source Library

Here’s an example of the kind of thing that can be done easily with the library.

 

 (Click anywhere to create a wave)

Please note that the library hasn’t undergone the rigorous optimisations that everyone is writing articles on, we’ll probably wait till we convert it to FP10.

Here's the source:

package 
{
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Point;

import org.farmcode.bezier.BezierPoint;
import org.farmcode.bezier.Path;

[SWF(backgroundColor="#dfdfdd", frameRate="25", width="700", height="400")]
public class WaveTest extends Sprite
{
//Math
private var theta:Number = 0;
private var buffer:Array = new Array();
private var renderBuffer:int = 0;
private var k1:Number;
private var k2:Number;
private var k3:Number;

private var path:Path;

//Common
private var spacing:int = 70;
private var waterSegsX:int;
private var waterSegsXreal:int;
private var waterXcenter:int;
private var isPause:Boolean = false;

public function WaveTest(){
path = new Path();
path.autoFillTension = 0.2;
//path.closed = true;

stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;

prepareMath();
populateBuffer();

addEventListener(Event.ENTER_FRAME, onEnterFrame);
stage.addEventListener(MouseEvent.CLICK, onMouseClick);

}
private function onMouseClick(e:Event):void{
makeSplash(((stage.stageHeight/2)-mouseY)/2, mouseX/stage.stageWidth);
}
public function makeSplash(impact:Number, fract:Number):void{
var index:int = Math.floor(fract*waterSegsXreal);
if(index==waterSegsXreal)index = waterSegsX;
buffer[renderBuffer][index].y += impact;
}
public function prepareMath():void{
waterSegsX = Math.round(stage.stageWidth/spacing);
waterSegsXreal = waterSegsX+1;
waterXcenter = waterSegsX/2;

var weight:Number = 10;
var t:Number = 0.3;
var speedPerFrequency:Number = 6;
var decreaseRate:Number = 0.1;

var f1:Number = speedPerFrequency*speedPerFrequency*t*t/(weight*weight);
var f2:Number = 1/(decreaseRate*t+2);

k1 = (4 - 8*f1)*f2;
k2 = (decreaseRate*t-2)*f2;
k3 = 2 * f1 * f2;
}

public function populateBuffer():void{
var x:int,y:int,z:int;
for(var i:int = 0 ; i < 2; i++){
var a:Array = new Array();
for(var k:int = 0 ; k < waterSegsXreal; k++)
a.push(new Point(k,0));

buffer[i] = a;
}
}

private function evaluate():void{
var crnt:Array = buffer[renderBuffer];
var prev:Array = buffer[1-renderBuffer];
for (var i:int = 0 ; i < waterSegsXreal; i++) {
var currentP:Point = (Point)(buffer[renderBuffer][i]);
var prevPoint:Point = prev[i] as Point;
var beforePoint:Point = crnt[i-1];
var afterPoint:Point = crnt[i+1];
prevPoint.y =
k1*((Point)(crnt[i])).y + k2*prevPoint.y +
k3*((afterPoint?afterPoint.y:0) + (beforePoint?beforePoint.y:0) + currentP.y);
}
renderBuffer = 1-renderBuffer;
}


private function onEnterFrame(event:Event):void{
if(!isPause){
theta += 0.05;
var verCounter:uint = 0;
graphics.clear();
graphics.beginFill(0x111120);

var points:Array = [];
graphics.moveTo(0,stage.stageHeight/2);

for(var x:int = 0 ; x < waterSegsXreal ; x++){
var y:Number = buffer[renderBuffer][x].y;
var point:BezierPoint = new BezierPoint((x/(waterSegsXreal-1))*stage.stageWidth,(stage.stageHeight/2)+y);
if(x==0){
point.backwardDistance = 0;
point.backwardAngle = 0;
}else if(x == waterSegsXreal-1){
point.forwardDistance = 0;
point.forwardAngle = 0;
}
points.push(point);
}

path.points = points;
path.drawInto(graphics);

graphics.lineTo(stage.stageWidth,stage.stageHeight);
graphics.lineTo(0,stage.stageHeight);
graphics.endFill();

evaluate();

}
}
}
}
Tags:
Categories: Flash

Introducing Sodality

by Tom Byrne 5. May 2009 05:38

Sodality is a runtime AOP (Aspect Oriented Programming) library for ActionScript 3. Simple implementations of Sodality use Metadata to call functions at specific times. Because of this, simple usage is only possible in IDEs that allow custom Metadata (e.g. Flex, FDT, FlashDevelop, etc), although it can be used in a more advanced manner in Flash (and others which don’t allow custom Metadata).

Simply put, Sodality is like an event system, allowing functions to get called in any part of your application when another piece of code dispatches an Advice object.

  1. Basic Example
  2. Basic Concepts
  3. Simple Usage
  4. A Real Example
  5. Further Flexibility
  6. Download Source

1. Basic Example

If somewhere within your app an Advisor class (more on that soon) dispatched a MouseClickAdvice object (for example), like this:

	dispatchEvent(new MouseClickAdvice(this, mouseX, mouseY));

Any other Advisor could catch the MouseClickAdvice like this (the bracketed metadata is what does the magic):

	[Trigger(timing="before")]
public function onMouseClickAdvice(cause:IMouseClickAdvice):void{
trace("Something was clicked at: "+cause.mouseX,cause.mouseY);
}

Or, it could catch the MouseClickAdvice asynchronously (allowing it to take some time before any other Advisors catch the advice) like this:

	private var advice:AsyncMethodAdvice;

[Trigger(timing="before")]
public function onMouseClickAdvice(cause:IMouseClickAdvice,
advice:AsyncMethodAdvice):void{
/* The advice has been caught here, I'm going
to wait 1 second before allowing it to continue.*/
this.advice = advice;
var timer:Timer = new Timer(1000,1);
timer.addEventListener(TimerEvent.TIMER, onTimer);
}
public function onTimer(event:TimerEvent):void{
trace("I'll now allow the advice to continue");
advice.adviceContinue();
}

2. Basic Concepts

The President

Every Sodality Application has (at least) one President, the President monitors and arranges all Sodality processes. Whilst the president doesn’t get used directly by your code to give Sodality the scope of it’s operations. The President must have a reference to some top level DisplayObject, this allows it to detect Advisors being added or removed from the app, usually this is set to the root of your app.

The Advisors

Any class can become an Advisor by implementing the IAdvisor interface, this allows it to specify Metadata above it’s functions telling Sodality when to call the function. In most apps, IAdvisor is only implemented by DisplayObjects which get added to the stage, non-visual advisors normally extend the DynamicAdvisor class, which allows them to be easily added to the President.

The Advice

Each Advice object represents an operation which will be executed, Advice objects are dispatched like events from any IAdvisor. Normally, each type of request will have an interface associated with it, the Advisors then will listen for the interface (rather than the class) which allows any Advice class to implement as many of these request types as required.

3. Simple Usage

Normally, Sodality is used for broad-scale communication between parts of an application. There are reusable DynamicAdvisors which specify several Advice interfaces allowing your app to instruct the DynamicAdvisor how to behave.

A good example of this would be the SoundAdvisor, which is used to play sounds, adjusting each sound’s volume appropriately and making sure the wrong sounds do not overlap. The SoundAdvisor specifies many Advice interfaces (e.g. IAddSoundAdvice, IRemoveSoundAdvice, IChangeVolumeAdvice, etc.), each of these correspond to a method within the SoundAdvisor. When an Advice object that implements one of these interfaces gets dispatched from another Advisor the SoundAdvisor’s corresponding method will be called and it will have an opportunity to make the required changes (e.g. begin playing a sound, adjust the volume etc.). These Advice interfaces can specify any functions or getters that the SoundAdvisor requires to complete the request, for example, the IChangeVolumeAdvice could look something like this:

package examplePackage
{
import org.farmcode.sodality.advice.IAdvice;

public interface IChangeVolumeAdvice extends IAdvice
{
function get volume(): Number;
function get muted(): Boolean;
}
}

This means that whenever an Advice object implements the IChangeVolumeAdvice interface, it must specify a volume and whether the SoundAdvisor should be muted, which is all the information that the SoundAdvisor needs to complete the request. The reason that interfaces are used instead of classes is so that each Advice object can implement many different interfaces, which will each execute different requests across different Advisors.

4. A Real Example

Stay tuned, we’ll be posting a real example with source code very soon.

5. Further Flexibility

Sodality has been built to be flexible, there are still unexplored pieces of flexibility within it’s architecture, we’d love to see how other people use Sodality and where it goes from here. These are some examples of under-explored areas in Sodality:

IAdviceTrigger

IAdviceTriggers are the object which tie pieces of advice together, they’re a powerful tool which is usually hidden from view, they can dramatically change the functionality of Sodality.

President Monitors

Because of the Asynchronous way that Sodality runs, classes which affect/monitor the running of Sodality can easily be plugged in. Here are some examples of tools we’ve built on to this:

  • A (faux) threading system, which allows different advice streams to take up different amounts of processing time.
  • A step-through debugger
  • A hot-key which traces out any currently pending advice.

Whilst we haven’t open-sourced these, they are good examples of the types of options that are available.

6. Download Source

 

Categories: Flash | Sodality

Tutorial 1: Introducing SiteStream

by Tom Byrne 1. May 2009 11:27

This is part one of a three part tutorial.

SiteStream is an AS3 library for parsing XML into first-class objects. All the information needed for the parsing process is in the XML syntax and there is no need to write any additional code.
XML data sources can reference each other, allowing data to load in piece by piece depending on what your flash app requires.
SiteStream also has the powerful feature of being able to specify other SWFs which contain the classes used, so that your class libraries are loaded invisibly only when required.

  1. Simple XML Syntax Example
  2. Retrieving an item
  3. Download Source

1. Simple XML Syntax Example

SiteStream XML allows you to write object structures in XML format. Each XML Node represents an Actionscript object.

Look at the first child tag in the XML below, it has the following characteristics:

  • It’s node name “Shape” is the name of the Class which will be used for this object
  • It’s namespace “displayPackage” is a package identifier, all package identifiers have a listing in the root tag which specifies the full package for the class.
  • It has an attribute “id” with the value “mask”, this specifies both the property of the parent object that this object should fill and the SiteStream name used in SiteStream paths.
  • It has an attribute “alpha” with a value “0.5”, this is how simple properties are set into objects.

This example creates a Sprite, it then creates a Shape and sets it as the Sprite's mask, then it creates a Rectangle (100x100) which it sets as the Shape's scrollRect.

<displayPackage:Sprite id="root"
displayPackage="flash.display.*"
geomPackage="flash.geom.*"
>
<displayPackage:Shape id="mask" alpha="0.5">
<geomPackage:Rectangle id="scrollRect" x="0" y="0" width="100" height="100"/>
</displayPackage:Shape>
</displayPackage:Sprite>

2. Retrieving an item

The following example has an XML data source which creates an array of MyClass objects (an arbitrary class as an example), the code shows how an item could be retrieved from the array. Notice that the class must be included in the code (more on that in Part 2). The MyClass Class can be seen at the bottom of this post.

myXML.xml:

<Array id="root"
myPackage="myPackage.*">
<myPackage:MyClass id="item1"/>
<myPackage:MyClass id="item2"/>
<myPackage:MyClass id="item3"/>
<myPackage:MyClass id="item4"/>
<myPackage:MyClass id="item5"/>
</Array>

SiteStreamTest.as:

package
{
import org.farmcode.siteStream.SiteStream;
import flash.display.Sprite;
import myPackage.MyClass;
public class SiteStreamTest extends Sprite
{
public function SiteStreamTest(){
var includeClass:Class = MyClass;
var siteStream:SiteStream = new SiteStream();
siteStream.rootURL = "myXML.xml";
siteStream.getObject("root/item2",onSuccess);
}
private function onSuccess(e:MyClass):void{
trace("Item 2 retrieved");
}
}
}


MyClass.as:

package myPackage
{ public class MyClass { public var colour:Number; public var childObject:MyClass; } }

 

3. Download Source


Goto Tutorial 2 >

Categories: Flash | SiteStream