Buttons using a Design Pattern

flash에서 구현되는 대부분의 사용자와의 Interaction 은 버튼을 통해 이루어 진다.
즉, 버튼을 통해 이벤트가 발생되고 그것을 통해 필요한 반응들을 처리하게 된다.
일반적으로 flash 에 버튼을 설정할때는 아래와 같이 직접 이벤트 핸들러 함수를 등록하여 사용한다.

button.onRelease=function(){  //code….}

하지만 이런 방법은 무비클립이나 버튼 자체에 코드가 종속되어 버리게 되어, 제어해야할 버튼이 많을 경우 이곳저곳에 이벤트 핸들러 함수를 작성해야하므로 체계적인 이벤트 플로우를 관리하기 위해서 좀 부족한 감이 든다.
또한 버튼은 단독으로 한개가 있는것보다는 여러개의 버튼이 컨텐츠의 네비게이션으로서 작용하는 경우가 대부분이다. 따라서, 버튼을 릴리즈 했을 경우 활성/비활성되는 부분도 고려해야하는 문제가 있다.

여기에서 소개하는 방법은 Design pattern 중에 Template method 패턴을 활용하여 버튼 로직을 구현하는 부분을 구현하고 실제적인 기능을 담당하는 부분은 추상클래스의 상속을 통해 구현하는 방법을 사용하였다.

기본적인 개념은 추상클래스 타입을 확장하여 각각의 구상클래스로서 버튼클래스를 제작한다.
그리고 추상클래스 타입을 이용해 작성된  ButtonManager class 에 버튼 클래스를 등록한다.
여기서 ButtonManager class 는 버튼 등록, 활성/비활성 체크 같은 전체적인 버튼을 관리하는 역할을 담당하게 된다.
(* 이곳에 사용된 startEvent 메소드는 기본적으로 제공하는 Dispatcher 클래스의 dispatchEvent 메소드를 재정의 한것이다. )

<AbstractSimpleButton Class>
버튼 추상 클래스로써 생성하는 모든 버튼에 공통적으로 상속되는 클래스이다.
이곳에 선언된 추상메소드를 구상클래스에서 오버라이드(override)하여 실제적인 기능을 구현한다.

import com.dstrict.UB.events.Dispatcher;
import com.dstrict.UB.events.Delegate;
 
class com.dstrict.UB.ui.button.AbstractSimpleButton extends Dispatcher {
 
private var _mc:MovieClip;
 
public function setController(inMc : MovieClip) : Void {
_mc=inMc;
}
public function getController() : MovieClip {
return _mc;
}
/**---------------------------------------------------------------------------
* Constructor function
* @param inMc controller movieClip
*---------------------------------------------------------------------------*/
public function AbstractSimpleButton(inMc:MovieClip) {
setController(inMc);
setButton();
}
public function setButton():Void{
getController().onRollOver=Delegate.create(this,onRollOver);
getController().onRollOut=Delegate.create(this,onRollOut);
getController().onPress=Delegate.create(this,onPress);
getController().onRelease=Delegate.create(this,onRelease);
getController().onReleaseOutside=Delegate.create(this,onReleaseOutside);
}
public function onRollOver() : Void {
// override in sub-class
}
public function onRollOut() : Void {
// override in sub-class
}
public function onPress() : Void {
// override in sub-class
}
public function onRelease() : Void {
// override in sub-class
}
public function onReleaseOutside() : Void {
// override in sub-class
}
public function enabled(mode:Boolean):Void {
// override in sub-class
}
}

<ButtonManager Class>

추상클래스인 AbstractSimpleButton 클래스 타입을 이용하여 버튼의 등록및 이벤트 위임을 처리하고 있다.
어떠한 구상클래스(concrete class)도 사용되지 않았기 때문에 실제 구현과 완전히 분리되어 코드를 처리할 수 있다.

import com.dstrict.UB.ui.button.AbstractSimpleButton;
import com.dstrict.UB.events.ButtonEvent;
import com.dstrict.UB.events.Dispatcher;
class com.dstrict.UB.ui.button.ButtonManager extends Dispatcher{
 
private var _buttonSet:Array;
private var _selectButton:AbstractSimpleButton;
 
/**---------------------------------------------------------------------------
* Constructor function
*----------------------------------------------------------------------------*/
public function ButtonManager() {
initialize();
}
 
//==================================================================
///////////public method //////////////////////////////////////////////
//==================================================================
public function addItem(item:AbstractSimpleButton):Void {
_buttonSet.push(item);
registerListener(item);
killListener(item);
}
public function getItemAt(index:Number):AbstractSimpleButton{
return AbstractSimpleButton(_buttonSet[index]);
}
public function kill():Void {
for(var i in _buttonSet){
AbstractSimpleButton(_buttonSet[i]).enabled(false);
}
}
public function live():Void {
for(var i in _buttonSet){
AbstractSimpleButton(_buttonSet[i]).enabled(true);
}
}
 
//==================================================================
///////////event handler //////////////////////////////////////////////
//==================================================================
 
public function onPress(evt:ButtonEvent):Void {
startEvent(new ButtonEvent(ButtonEvent.ON_PRESS,evt.target,evt.currentMovieClip));
}
public function onRelease(evt:ButtonEvent):Void {
enabled(getSelectButton(),true);
enabled(AbstractSimpleButton(evt.target),false);
setSelectButton(AbstractSimpleButton(evt.target));
startEvent(new ButtonEvent(ButtonEvent.ON_RELEASE,evt.target,evt.currentMovieClip));
}
public function onRollOver(evt:ButtonEvent):Void {
startEvent(new ButtonEvent(ButtonEvent.ON_ROLL_OVER,evt.target,evt.currentMovieClip));
}
public function onRollOut(evt:ButtonEvent):Void {
startEvent(new ButtonEvent(ButtonEvent.ON_ROLL_OVER,evt.target,evt.currentMovieClip));
}
 
//==================================================================
///////////private method //////////////////////////////////////////////
//==================================================================
private function initialize():Void {
_buttonSet=new Array();
}
private function setSelectButton(button:AbstractSimpleButton):Void {
_selectButton=button;
}
private function getSelectButton():AbstractSimpleButton{
return _selectButton;
}
private function registerListener(item:AbstractSimpleButton):Void {
item.addEventListener(ButtonEvent.ON_ROLL_OVER,this);
item.addEventListener(ButtonEvent.ON_ROLL_OUT,this);
item.addEventListener(ButtonEvent.ON_PRESS,this);
item.addEventListener(ButtonEvent.ON_RELEASE,this);
}
private function killListener(item:AbstractSimpleButton):Void {
var owner:ButtonManager=this;
item.getController().onUnload=function(){
item.removeEventListener(ButtonEvent.ON_ROLL_OVER,this);
item.removeEventListener(ButtonEvent.ON_ROLL_OUT,this);
item.removeEventListener(ButtonEvent.ON_PRESS,this);
item.removeEventListener(ButtonEvent.ON_RELEASE,this);
};
}
private function enabled(button:AbstractSimpleButton,mode:Boolean):Void {
button.enabled(mode);
}
 
}

event handler 함수를 보면 등록되어 있는 추상클래스 버튼에서 발생한 이벤트를 받아서 다시 최종적으로 ButtonManager 에 등록되어있는 이벤트 리스너 함수로 넘겨준다. 이 과정에서 필요한 버튼 이벤트 오브젝만 넘겨준다.

<ButtonEvent Class>
버튼에 대한 이벤트 정보를 담고 있는 이벤트 오브젝트.

class com.dstrict.UB.events.ButtonEvent extends com.dstrict.UB.events.Event {
 
public static var ON_ROLL_OVER:String = "onRollOver";
public static var ON_ROLL_OUT:String = "onRollOut";
public static var ON_PRESS:String = "onPress";
public static var ON_RELEASE:String = "onRelease";
public static var ON_RELEASE_OUTSIDE:String = "onReleaseOutside";
 
public var currentMovieClip:MovieClip;
 
/**--------------------------------------------------------------------------
*constructor function
*@param Type : String, 이벤트 핸들러 메서드
*---------------------------------------------------------------------------*/
public function ButtonEvent(Type : String,Source : Object,curMovieClip:MovieClip) {
super(Type,Source);
currentMovieClip=curMovieClip;
}
}

<SampleConcreteButton  Class>

실제 버튼 기능을 구현하기 위한 구상 클래스. 단순히 버튼 롤오버 반응과 릴리즈시 활성체크를 구현하였다.
생성하려는 버튼의 기능에 따라 이 클래스는 다르게 구성된다. 물론 추상메소드로서 작용하는
AbstractSimpleButton 클래스의 onRollOver,onRollOut,onRelease,onReleaseOutside, enabled
는 오버라이드(override) 하여 구현하여야 한다.

import com.dstrict.UB.ui.button.AbstractSimpleButton;
import com.dstrict.UB.events.ButtonEvent;
class com.dstrict.UB.ui.button.SampleConcreteButton extends AbstractSimpleButton {
 
public var buttonIndex:Number;
 
public function SampleConcreteButton(inMc : MovieClip,inButtonIndex) {
super(inMc);
buttonIndex=inButtonIndex;
}
 
public function onRollOver() : Void {
getController().gotoAndStop(2);
startEvent(new ButtonEvent(ButtonEvent.ON_ROLL_OVER,this,getController()));
}
 
public function onRollOut() : Void {
getController().gotoAndStop(1);
startEvent(new ButtonEvent(ButtonEvent.ON_ROLL_OUT,this,getController()));
}
 
public function onRelease() : Void {
startEvent(new ButtonEvent(ButtonEvent.ON_RELEASE,this,getController()));
}
 
public function onReleaseOutside() : Void {
startEvent(new ButtonEvent(ButtonEvent.ON_RELEASE,this,getController()));
}
 
public function enabled(mode:Boolean):Void {
getController().enabled=mode;
if(mode){
getController().gotoAndStop(1);
}else{
getController().gotoAndStop(2);
}
}
 
}

<sample code>
위에서 제작한 클래스를 활용하여 버튼 클래스를 생성하고 등록하는 코드
간단한 4개의 버튼을 구현하고 있다. 사용자는 SampleConcreteButton 를 만들어 버튼을 등록하기만 하면 모든 이벤트를 한꺼번에 관리할 수 있다.

import com.dstrict.UB.ui.button.ButtonManager;
import com.dstrict.UB.ui.button.AbstractSimpleButton;
import com.dstrict.UB.ui.button.SampleConcreteButton;
import com.dstrict.UB.events.ButtonEvent;
 
var buttonMgr:ButtonManager=new ButtonManager();
buttonMgr.addEventListener(ButtonEvent.ON_RELEASE,this);
 
buttonMgr.addItem(new SampleConcreteButton(number1_mc,1));
buttonMgr.addItem(new SampleConcreteButton(number2_mc,2));
buttonMgr.addItem(new SampleConcreteButton(number3_mc,3));
buttonMgr.addItem(new SampleConcreteButton(number4_mc,4));
 
//event handler
function onRelease(evt:ButtonEvent):Void{
result_txt.text="onRelease event : "+evt.currentMovieClip+"-----&gt;"+evt.target.buttonIndex;
}
 
//Release event
var numberButton:AbstractSimpleButton=buttonMgr.getItemAt(0);
numberButton.onRelease(new ButtonEvent(ButtonEvent.ON_RELEASE,numberButton));

위 코드에는 ButtonManager에 등록되어있는 리스너가 onRelease 이벤트 밖에 없기 때문에
릴리즈 이벤트만 최종적으로 처리한다. 다른 버튼 이벤트를 처리하고 싶으면 리스너에 이벤트를
추가 하여 이벤트 핸들러 함수만 구현하면 된다.

등록된 모든 이벤트는  ButtonManager 의 이벤트 핸들러 함수를 통해 전달된다. 따라서 버튼에 대한 이벤트 플로우를 한곳에 집중할 수 있으며 기존에 코드에 종속되어있는 버튼에 비해 유연성이 크게 향상된다.

sample code 에서 맨 마직막 부분의 release event 부분은 사용자가 마우스를 통해 버튼을
클릭해서 release event 를 발생시키는 것이 아니라 직접 임의로 발생시킬 수 있다.

download sample

How to access to stage and root (AS3.0)

AS3.0 에서 사용되는 stage 와 root 의 개념은 AS2.0 버전과는 차이가 있다.

우선 stage 를 살펴보면 이전 버전에서는 stage 오브젝트는 static class 로서 주로 movie size 나 onResize 이벤트를 처리하는데 사용하였지만 새롭게 바뀐 AS3.0 에서는 플래시 무비에 존재하는 모든 displayObject 를 담을수 있는 최상위 컨테이너 개념이 첨가되었다.
이는 어떻게 생각하면 기존의 _root 와 같이 생각할 수도 있다. 하지만 바뀐 모델에 있어서 stage 속성은 인스턴스 속성이다. 다시말해, 전역적으로 접근할 수 없고 반드시 displayObject 의 인스턴스 속성으로서만 참조가 가능하다는 이야기이다. 또한 DisplayObject 가  스크린상의 timeline 이나 display list 에 등록하지 않는다면 null 값을 가지게 된다. (Document class 와 screen 상에 보여지는 오브젝트는 제외)
이런 점이 stage 속성을 사용하는데 있어 상당히 제약사항으로 작용하게 된다. 만약 DisplayObject 가 아닌 다른 Object 를 사용할때 stage 속성은 정의된 속성이 아니므로 constructor 에  stage 속성값을 parameter 로 넘겨주는 방식으로 사용할 수 밖에 없다.

<non-display object classes >

package {

import flash.display.Stage;public class CustomObject {

private var _stage:Stage;

public function CustomObject(stage:Stage) {
_stage= stage;
}
}
}

root 는 상황에 따라 다를 수 있지만 일반적으로 현재의 flash movie 에서 main timeline 을 참조할 수 있는 DisplayObject 속성이다. 다시말해 상대적인 최상위 DisplayObject 를 참조한다.
이전의 _root 속성은 전역속성으로서 가장 최상의 컨테이너를 참조하였기 때문에 로더를 통해 불러왔을경우 그 갯수에 따라 _root 의 참조값이 변하였다. 하지만 root 속성은 현재의 flash movie 를 기준으로 참조값을 얻어오기 때문에 각 swf 에 해당하는 main timeline 의 참조값이다. 즉, 이전버전에서 _lockroot 를 적용한 것 같이 root 속성을 상대적으로 참조할 수 있다. 모든 displayObject 가 각각 timeline 의 참조값을 가질 수 있다는 의미이다.
하지만 root 도 stage 속성과 마찬가지로 인스턴스 속성이다. 따라서 stage 의 제한사항을 그대로 가지고 있다.

따라서 이런 문제점들을 해결하기 위해서는 위와같이 각각의 참조값을 parameter 로 넘겨주는 방법과 초기화 함수를 실행하여 addChild 이후에 참조할 수 있도록 처리해야한다.
또다른 방법으로는 Document class 를 활용하여 전역속성으로 만드는 것이다.

Document class 에서는 Stage 에 자동으로 추가되는 main timeline 으로 인해 생성되자마자 stage 속성과 root 속성을 참조할 수 있다.

<static 속성으로 참조할 수 있는 MainStage class>

package {

import flash.display.DisplayObject;
import flash.display.*;

public class MainStage extends MovieClip {
public static var stage:Stage;
public static var root:DisplayObject;

public function MainStage() {
MainStage.stage = this.stage;
MainStage.root = this.root;
}
}
}

<Document class>

package {

public class DocumentClass extends MainStage {

public function DocumentClass() {

}
}
}

위와 같이 document class 에서 MainStage class 을 상속받아 사용하면 어느 오브젝트에서라도  전역적으로 stage를 참조 할 수 있다.
하지만 이는 전역속성으로서 의미가 있는 stage 에만 유용할 뿐 각기 다른 root 속성을 참조하기에는 문제가 있다.
root 속성 참조 대한 문제는 ADD_TO_STAGE 이벤트를  이용하여 오브젝트마다 일일이 체크하는 방법이나 아니면 초기화 함수를 실행하여 처리하는 방법밖에 없는것 같다.(어떤 다른 방법이 있을까?????)

timeline 으로 부터 종속되지 않도록 stage 속성과 root 속성을 추가한것은 참 좋은 방법인것 같다. 하지만 그로 인해 개발자들이 일일이 신경쓰지 않으면 안되는 것들이 정말 많이 생긴것 같다.

timeline 과 class……….플래시의 특성상 종속적일 수 밖에 없는데……
어떤 것이 더 나은 방법일까?….의구심이 든다.

…(rest) parameter (AS3.0)

가변적인 메소드의 parameter 갯수를 이용하려면 AS2.0 버전까지는 arguments 오브젝트를 사용하여 임의의 parameter 에 접근할 수 있었다. 하지만 AS3.0 에서는 컴파일러의 좀더 철저한 데이타 타입의 체크와 parameter 관리에 의해 기존의 방법을 쓸 수가 없게 되었다.

<AS2.0>
function parameterTest():Void{
trace(arguments.length)  //parameter 갯수를 알수 있음
}

parameterTest(1,2,3);
result–> 3

<AS3.0>
function parameterTest():void{
trace(arguments.length)
}

parameterTest(1,2,3);
result–> compiler error  : Incorrect number of arguments.

AS3.0 에서는 메소드 정의시 parameter 갯수와 호출시 parameter 갯수가 일치해야만 한다.
그렇지 않으면 위와 같이 arguments 갯수에 오류가 있다고 에러를 발생시키게 된다.
이를 위해 …(rest) paremeter 구문을 새롭게 제공하였는데 사용법은 단순히 … 뒤에 사용자가 원하는 parameter array 이름을 써주면 된다.

function parameterTest(…param):void{
trace(param.length) // param 은 배열 타입이다.
}

다른 parameter 와 같이 사용하려면
function parameterTest(str1:String,str2:String,…param):void{
//…..statements
}
마지막에 …(rest) parameter 를 써주면 된다.

주의할 점은 위 방법을 사용하면서 arguments 오브젝트를 동시에 사용할 수 없다는 점이다.

Area of a triangle via the cross product

막상 3D 상의 임의의 세 점으로 구성된 삼각형의 넓이를 구하려고 하면 좀처럼 마땅한 방법이 떠오르지 않는다. 여기에 비교적 간단한 방법으로 공간상의 삼각형 넓이를 구하는 방법을 소개한다

공간상의 두 벡터 P,Q 가 주어졌을때, 외적(cross product) 은 다음의 식을 만족한다.

1195641316

위식의 우변을 잘 살펴보면 외적 PxQ 의 크기가 P와 Q에 의해 만들어지는 평행사변형의 넓이와 같음을 알수 있다. 이를 이용하면 꼭지점이 P,Q,R 인 임의의 삼각형의 넓이A를 다음과 같은 식에 의해 계산할 수 있다.

1358145576

예를 들어 P(4, 2, -1), Q(-1, 4, 2), R(2, 1, -4) 같이 세점으로 구성된 삼각형 넓이를 구해보면,

PQ
= [-1 - 4]i + [4- 2]j + [2- (-1)]k = -5i +2j + 3k.

PR = [2 - 4]i + [1 - 2]j + [-4 - (-1)]k = -2ij – 3k.

PQ X PR = <2*(-3)-3*(-1) , 3*(-2)-(-5)*(-3) , (-5)*(-1)-2*(-2)>
=  <-3,-21,-1>

A=1/2 * sqrt( (-3)*(-3) + (-21)*(-21) + (-1)*(-1) ) = 10.618

Template method pattern

Template method 패턴은 디자인 패턴 중에서도 가장 기본이 되는 패턴중에 하나다.
템플릿이란 단어에서 유추할 수 있듯이 어떤 일정한 틀을 기본으로 클래스를 구성하는 방식이다.
추상클래스(Abstract class) 에 정의되어있는 Abstract method 를 기반으로 알고리즘을 구성하는 template method 를 만들어 동작시키는 것이다. 다시말해 알고리즘의 캡슐화(encapsulation) 라고 할수 있다.

추상클래스(Abstract class) 에서는 단지 알고리즘만 기술하는 역할만 한다. 실제 구현은 서브클래스 (Concrete class) 에서 기술한다. 따라서 Template method 를 이용하면 알고리즘의 구조는 유지하면서 서브클래스에서 추상메소드를 오버라이드하여 재정의 할 수 있다.
서브클래스에서 행동을 지정할 수 있게 하면서도 코드의 재사용이 가능한 특징으로  Template method 패턴은 framework 를 제작하는데 자주 등장한다.

플래시에서는 abstract 키워드가 지원되지 않지만 개념상 큰차이없이 패턴을 사용할 수 있다.

<Abstract class>

class AbstractButton {
 
 private var _mc:MovieClip;
 
 public function setController(inMc : MovieClip) : Void {
  _mc=inMc;
 }
 public function getController() : MovieClip {
  return _mc;
 }
 
 //Constructor function
 function AbstractButton(inMc:MovieClip){
  setController(inMc);
  setButton();
 }
 
 //Template method
 public function setButton():Void{
     getController().onRollOver=Delegate.create(this,onRollOver);
     getController().onRollOut=Delegate.create(this,onRollOut);
     getController().onRelease=Delegate.create(this,onRelease);
     getController().onReleaseOutside=Delegate.create(this,onReleaseOutside);
 
 }
 
 public function onRollOver() : Void{
  // override in sub-class
  throw new Error("Abstract method!!!!!");
 }
  public function onRollOut() : Void{
   // override in sub-class
  throw new Error("Abstract method!!!!!");
 }
  public function onRelease() : Void{
  // override in sub-class
  throw new Error("Abstract method!!!!!");
 }
  public function onReleaseOutside() : Void{
   // override in sub-class
  throw new Error("Abstract method!!!!!");
 }
 
  public function enabled(mode:Boolean):Void{
  // override in sub-class
  throw new Error("Abstract method!!!!!");
 }
 
}

<Concrete class>

class ConcreteButton extends AbstractButton{
 
function ConcreteButton(inMc:MovieClip){
super(inMc);
}
 
public function onRollOver() : Void{
getController().gotoAndPlay("over");
}
 
public function onRollOut() : Void{
getController().gotoAndPlay("out");
}
 
public function onRelease() : Void{
getController().gotoAndPlay("release");
}
 
public function onReleaseOutside() : Void{
 
}
 
public function enabled(mode:Boolean):Void{
getController().enabled=mode;
}
 
}

위 sample 은 버튼을 구성하는 간단한 클래스이다.
플래시에서 Interaction 을 구현할때 거의 대부분버튼을 구현하여 접근한다. 일반적으로 버튼 액션을 구현하려면 버튼 인스턴스나 무비클립 인스턴스를 통해 버튼 이벤트를 주어서 사용하지만 그럴경우 제어해야할 버튼이 많아지면 코드길이가 늘어날 뿐더러 코드 유연성이 떨어지게 된다.

실제 버튼을 구현하려면 추상클래스를 확장하여 추상메소드를 구현하기만 하면 된다.
추상클래스의 추상메소드의 정의는 개발자가 신중히 결정해서 정해야한다. 추상메소드가 너무 많게 되면 서브클래스에서 구현해야할 코드가 많아져 불편한점이 있을 수 있다. 반대로 너무 추상메소드 자체를 큰 덩어리로 나누게되면 유연성을 떨어뜨리게 된다.

ColorMatrixFilter TweenEngine

ColorMatrixFilter 클래스를 이용하면  이미지의 각 픽셀의 RGBA 색상 및 알파 값에 4 x 5 행렬 변환을 적용하여 saturation 또는 contrast 또는  brightness 를 적용할수 있다. 물론 각각의 ColorMatrixFilter 에 적용되는 matrix 속성값을 다르게 설정하여야 한다.

이 예제에서 활용한 ColorMatrix 는 Mario Klingemann(http://www.quasimondo.com) 가 제작한 소스를 이용하여 트윈클래스를 제작하였다.
기본적인 클래스 구조는 이전의 트윈클래스와 같다. 객체속성을 이용하여 for~in 구문으로 트윈을 적용하였다. 다른 객체에 비해 적용되는 속성의 갯수(matrix 속성)가 20개로써 많기 때문에 이미지를 크게 하거나 트윈길이를 너무 길게할 경우 느려질 수가 있다. 또한 트윈이후 이전 트윈클래스와 같이 콜백함수를 호출할 수 있다.

download sample

//==================================================================
//@class name  :  ColorMatrixTween.as
//@author          : vkimone. KimKiJeung  (http://kimkijeung.com)
//@last update   : 2007. 03. 19
//@version         : V1.0
//==================================================================
/**
 @description
* 무비클립의  ColorMatrixFilter 트윈 클래스 : 이징함수 설정으로 조절 
 
* @example
*     <code>
*     ColorMatrixTween.tween(targetMc,0,ColorMatrixTween.SATURATION,Regular.easeOut,30,
             {{func: callBackFunction,obj: functionScope, param: [파라미터 배열로 들어감]}}
*     </code>
*/
 
import flash.filters.ColorMatrixFilter;
import com.dstrict.UB.util.filters.ColorMatrix;
class com.dstrict.UB.util.transitions.tween.ColorMatrixTween{
  public static var SATURATION:String="saturation";
  public static var CONTRAST:String="contrast";
  public static var BRIGHTNESS:String="brightness";
 
 /**---------------------------------------------------------------------
  *@description  saturation, contrast, brightness 트랜지션
    @param mc : MovieClip, 적용무비클립
    @param value : Number , 적용 percentage
    @param mode :String , saturation or contrast or brightness mode
    @param func : Function, easing function
   @param durationFrame : Number, 지속프레임
*----------------------------------------------------------------------*/
public static function tween(mc:MovieClip,value:Number,mode:String,func:Function,durationFrame:Number):Void{
var time:Number=1;
var beginning:Array=new Array();
var change:Array=new Array();
 var mat:ColorMatrix = new ColorMatrix();
 switch(mode){
   case SATURATION :
     mat.adjustSaturation(value/100);
     break;
 
   case CONTRAST :
     mat.adjustContrast(value/100);
     break;
 
   case BRIGHTNESS :
     mat.adjustBrightness(255*value/100);
     break;
  }
  var cm:ColorMatrixFilter = new ColorMatrixFilter(mat.matrix);
  var startMatrix:Array=
                       (mc.filters[0].matrix==undefined) ? ColorMatrix.IDENTITY : mc.filters[0].matrix;
  var targetMatrix:Array=mat.matrix;
 
for(var i in targetMatrix){
  beginning.push(startMatrix[i]);
  change.push(targetMatrix[i]-startMatrix[i]);
}
       var type=(typeof(arguments[5])=="object")? true : false;
       if(type){
        var referObj=arguments[5];
       }else{
        var p:Number=arguments[5];
        var referObj=arguments[6];
       }
  mc.onEnterFrame=function(){
  var objIdx:Number=0;
  for(var i in targetMatrix){
   targetMatrix[i]=func(time,beginning[objIdx],change[objIdx],durationFrame,p);
   objIdx++;
   }
   targetColMatrixFilter.matrix=targetMatrix;
   mc.filters=[targetColMatrixFilter];
   time++;
  if(time&gt;durationFrame){
   delete this.onEnterFrame;
   if(referObj!=undefined){
    referObj.func.apply(referObj.obj,referObj.param);
   }
 
  }
};
}
}
 
import  com.dstrict.UB.util.transitions.tween.FilterTween;
import  mx.transitions.easing.*;
import com.dstrict.UB.util.filters.ColorMatrix;
//SATURATION (from 0  to 100)
image.onRollOver=function(){
 ColorMatrixTween.tween(image,0,ColorMatrixTween.SATURATION,Regular.easeOut,10);
}
image.onRollOut=function(){
 ColorMatrixTween.tween(image,100,ColorMatrixTween.SATURATION,Regular.easeOut,10);
}
//CONTRAST (from 0  to 100)
image2.onRollOver=function(){
 ColorMatrixTween.tween(image2,100,ColorMatrixTween.CONTRAST,Regular.easeOut,10);
}
image2.onRollOut=function(){
 ColorMatrixTween.tween(image2,0,ColorMatrixTween.CONTRAST,Regular.easeOut,10);
}
//BRIGHTNESS (from -100  to 100)
image3.onRollOver=function(){
ColorMatrixTween.tween(image3,100,ColorMatrixTween.BRIGHTNESS,Regular.easeOut,10);
}
image3.onRollOut=function(){
ColorMatrixTween.tween(image3,0,ColorMatrixTween.BRIGHTNESS,Regular.easeOut,10);
}

Filter TweenEngine

무비클립 속성에 비해 다루기가 까다로워 필터 트원은 좀처럼 사용하지는 않았다.
특히 프레임으로 모션트윈은 어느정도 사용했지만 스크립트를 이용한 조절은 거의……

전반적으로 필터에 대해 다시 살펴보면서 이번기회에 필터트윈 클래스를 만들어 보았다.
새로 만들긴 했지만 기존에 만들었던 무비클립 Tween 클래스와 기본 구조는 같다.
어짜피 필터 트원이라는것이 필터속성을 시간단위로 변화를 주는 것이기 때문에 무비클립의 그것과 다르지 않았다.

무비클립 트윈클래스와 다른점은 인자값으로 적용할 필터오브젝트를 생성해서 넘겨주는 것이다.
그리고 그 필터속성의 변화값을 무비클립 filters 속성에 적용해 주는 것이다.
그리고 다중필터 지원은 동시에 여러가지 필터를 적용하는 경우가 많지 않을뿐더러 쓸데없이 파라미터가 길어질 염려가 있어 적용하지 않았다.

필터 적용범위는 필터속성의 데이타 타입이 Number 일경우에만 적용해야 한다. 따라서 ColorMatrixFilter와 같이 속성값이 matrix 일 경우 이 클래스로는 트윈이 적용되지 않는다. 그 이외의 모든 필터의 속성에 대해서는 트윈이 가능하다.
또한 필터 속성마다 해당하는 속성 범위가 존재한다. 이점을 유의해서 적용해야한다.

download sample

//==================================================================
//@class name  :  FilterTween.as
//@author          : vkimone. KimKiJeung  (http://kimkijeung.com)
//@last update   : 2007. 03. 07
//@version         : V1.0
//==================================================================
/**
 @description
* 무비클립의 필터 속성  트윈 클래스 : 이징함수 설정으로 조절
* 트원할 필터속성의 갯수에 관계없이 오브젝트로 적용 가능
*
* @caution
*  -반드시 트원할 필터 속성의 데이타 타입이 Number 일경우에만 적용할수 있다.
    String,Boolean,Array 는 적용할 수 없다.
   -다중필터 적용은 지원하지 않는다.
*
* @example
*     <code>
*        Filtertween.tween(targetMc,{blurX:36,blurY:36},Regular.easeOut,30,
       {{func: callBackFunction,obj: functionScope, param: [파라미터 배열로 들어감]}}
*     </code>
*/
import flash.filters.BitmapFilter;
class com.dstrict.UB.util.transitions.tween.FilterTween{
/**------------------------------------------------------------------------
 * @param mc : MovieClip, 적용무비클립
 * @param filter : BitmapFilter , 적용할 필터 객체
 * @param obj : Object, 단일 필터속성 (필터 속성 data type 이 Number 일 경우)
                                                     ex. {blurX:32,blurY:32}
 * @param func : Function, easing function
 * @param durationFrame : Number, 지속프레임
 * @param referObj :[option] reference object(caution--&gt; 파라미터값 반드시 배열요소로 입력)
*----------------------------------------------------------------------*/
public static function tween(mc:MovieClip,filter:BitmapFilter,obj:Object,func:Function,durationFrame:Number):Void{
var time:Number=1;
var beginning:Array=new Array();
var change:Array=new Array();
 
for(var i in obj){
  beginning.push(filter[i]);
  change.push(obj[i]-filter[i]);
}
       var type=(typeof(arguments[5])=="object")? true : false;
       if(type){
        var referObj=arguments[5];
       }else{
        var p:Number=arguments[5];
        var referObj=arguments[6];
       }
  mc.onEnterFrame=function(){
  var objIdx:Number=0;
  for(var i in obj){
   filter[i]=func(time,beginning[objIdx],change[objIdx],durationFrame,p);
   mc.filters=[filter]; //필터 적용부분
   objIdx++;
   }
   time++;
  if(time&gt;durationFrame){
   delete this.onEnterFrame;
   if(referObj!=undefined){
    referObj.func.apply(referObj.obj,referObj.param);
   }
 
  }
};
}
}
 
import  com.dstrict.UB.util.transitions.tween.FilterTween;
import  mx.transitions.easing.*;
import flash.filters.*;
var blurFilter:BlurFilter=new BlurFilter(0,0,1);
var dropShadowFilter:DropShadowFilter=new DropShadowFilter(0,120,0x000000,1);
var glowFilter:GlowFilter=new GlowFilter(0x6E7D74,1,16,16,2,2);
//BlurFilter
image.onRollOver=function(){
 FilterTween.tween(image,blurFilter,{blurX:8,blurY:8},Regular.easeOut,15,
        {func:onFilterTweenFinished,obj:_root,param:["blur"]});
}
image.onRollOut=function(){
 FilterTween.tween(image,blurFilter,{blurX:0,blurY:0},Regular.easeOut,15);
}
//DropShadowFilter
image2.onRollOver=function(){
 FilterTween.tween(image2,dropShadowFilter,{distance:4,blurX:4,blurY:4},Regular.easeOut,15,
        {func:onFilterTweenFinished,obj:_root,param:["dropShadow"]});
}
image2.onRollOut=function(){
 FilterTween.tween(image2,dropShadowFilter,{distance:0,blurX:4,blurY:0},Regular.easeOut,15);
}
//GlowFilter
image3.onRollOver=function(){
 FilterTween.tween(image3,glowFilter,{alpha:1,blurX:36,blurY:36},Regular.easeOut,15,
        {func:onFilterTweenFinished,obj:_root,param:["glow"]});
}
image3.onRollOut=function(){
 FilterTween.tween(image3,glowFilter,{alpha:0,blurX:0,blurY:0},Regular.easeOut,15);
}
function onFilterTweenFinished(evt):Void{
 trace("filterTween finished....."+evt);
}

Converting to a Cartesian coordinates

동적으로 여러개의 MovieClip 을 원형으로 배열할때 직교좌표계 사용하는 것보다 극좌표계를 사용하면 훨씬 다루기가 쉽다. 또한 x,y 와 같은 점을 다룰경우 Point 클래스를 이용하면 각 점간의 수학적인 연산에 있어 기본적으로 Point  클래스의 메서드를 이용하여 쉽게 처리할 수 있다.

극좌표를 직교좌표로 바꿀때도 기본으로 제공해주는 메서드를 사용하면 된다.

polar (Point.polar method)
public static polar(len: Number, angle: Number) : Point

Converts a pair of polar coordinates to a Cartesian point coordinate.

Parameters
len: Number – The length coordinate of the polar pair.
angle: Number – The angle, in radians, of the polar pair.

Returns
Point – The Cartesian point.

1167762589

import flash.geom.Point;
var len:Number = 125;
var angleInRadians:Number ;
var cartesianPoint:Point
var unitAngle:Number=15;
for(var i=1 ; i&lt;=24 ; i++){
var mc:MovieClip=this.attachMovie("M_thumb"+i,"M_thumb"+i,i);
angleInRadians = Math.PI*unitAngle/180*(i-1);
cartesianPoint = Point.polar(len, angleInRadians); // 극 좌표를 직교좌표로 전환
 
var offsetX:Number=Math.round(cartesianPoint.x);
var offsetY:Number=Math.round(cartesianPoint.y);
mc._x=Stage.width/2+offsetX;
mc._y=Stage.height/2+offsetY;
}

굳이 point 클래스를 사용하지 않고서도 극좌표계를 직교좌표계로 간단한 삼각함수를 사용하여 다음과 같이도 쉽게 바꿀수 있다.

x1=x0+(Math.cos(angle)*radius);
y1=y0+(Math.sin(angle)*radius);

하지만 Point 클래스를 사용하여 좌표를 구성한다면 코드의 일관성에 있어서나 간결성에 있어 상당한 도움이 될것이다.

Book-Actionscript 3 with Design Patterns

오래전에 사놓고 프로젝트 때문에 읽어보지 못한 책들을 이제서야 한두권씩 찾아서 읽기 시작했다.
1년 전까지만해도 왜 플래시로는 design pattern 책이 나오질 않을까?….우리나라는 그렇다 치더라도 외국에는 충분히 나왔어야할 내용의 책인데…하고 생각했던 적이 있다. 물론 이전에도 패턴에 대해 언급했던 책은 있었지만 전문적으로 플래시를 이용하여 적용한 패턴 책은 찾아 볼 수 없었다.

이제 AS3.0 나 발표된지도 어느정도 지나서인지 관련책들이 많이 보이는것 같다. 안타깝게도 해외에서만 말이다. 처음 이책을 발견하고 그자리에서 구매해버렸다. 자그마치 $50 달러….

1036283543아직 자세히 살펴보지는 못했지만 내가 기대하고 있던 내용의 책이였다. AS3.0 으로 바뀌면서 문법자체가 자바랑 상당부분 비슷하게 바뀌었기 때문에 문법적으로 자바 디자인 패턴책과 엄청난 차이가 있는것은 아니다. 하지만 플래시만의 고유의 영역에 있어서 자바나 다른 언어와는 다른 구조적인 이유로 인해 발생하는 패턴 적용의 이슈들을 어느정도 다뤄줬기 때문에 상당히 유용할것 같다.

많은 플래시 개발자들이 아직까지 플래시로 무슨 디자인패턴까지 쓰냐고 …. 아직까지 인터페이스의 개념조차도 상당수의 플래시 개발자들에게 생소한 마당에 디자인 패턴이라니…. 하면서 효용성에 있어 의문을 제기한다.

물론 나도 이점에 있어서 어느정도 동의한다. 아직까지 플래시 개발에 있어서 기존 응용프로그램 개발만큼의 큰 규모의 프로젝트가 적었기도 했고 대부분 소규모 웹사이트 개발이 전부였을 것이다.
그래서 그런지 플래시 개발을 여러사람이 공동으로 진행했다라는 소리는 좀처럼 들어보지 못한것 같다. 또한 플래시 툴의 특성상 디자인 베이스의 사용자들이 개발자로 전향했던 경우가 많아서 정통 개발자들의 관심인 여러가지 문제에 있어 플래시 개발자들에겐 논외의 대상일 수도 있다.

그렇지만 앞으로의 actionscript 는 그런 문제에 있어 먼저 개발자들이 인지하고 좀더 구조적인 프로그래밍 스킬을 쌓아가지 않으면 안되는 모양새인것 같다.
해외에서는 이미 상당수의 개발자들이 플래시 오픈소스 프로젝트를 진행하고 있다.
혼자서 하는것이 아닌 여러사람이 공동으로 개발에 참여하고 결과물을 내놓는 것이다. 이들이 이렇게 까지 할수 있는 이유 중에 하나는 바로 의사소통을 할수 있는 도구…..그들 나름대로의 프레임워크가  존재하기 때문이다. 이런 프레임워크 정의는 어느정도 구조적인 개발의 전제하에 진행되어야할 것이다.

플래시….참 어떻게 보면 쉬운 툴이고 어떻게 보면 너무 어려운 툴이다.
요즘들어 적어도 Interaction Designer 에게는 플래시 그 이외의 배워야 할것이 너무 많다는걸 느낀다. 뭔가 새로운걸 만들어야한다는 생각과….계속 발전하는 기술…..새로운 기술을 익혀 적용하기에도 바쁜데 남들과 다른 그 어떤 새로운 것을 만들어야 한다는 생각이 들때면 가끔 슬퍼질때가 있다….

예술은 아는 만큼 보이고 아는만큼 느낄수 있다고 한다.
플래시도 알면 알수록 내가 가야할 길이 너무 멀다라는걸 새삼 느낀다.

Table of Contents:

Part I – Successful Projects
1. How to Design Applications
2. Programming to Interfaces

Part II – Patterns
3. MVC
4. Singleton
5. Factory (Abstract Factory and Factory Method)
6. Proxy
7. Iterator
8. Composite
9. Decorator
10. Command
11. Memento
12. State

Part III – Advanced ActionScript Topics
13. Working with Events
14. Sending and Loading Data
15. E4X (XML)
16. Regular Expressions

책정보보기

Debugging Tool – X-ray

2006 FlashFoward 에서 finalist 에 오른 플래시 실시간 디버깅 툴이다.
플래시의 가장 큰 단점중에 하나가 기타 프로그램언어에 비해 디버깅 기능이 빈약하다는 것이다.
물론 플래시내에서 디버깅 기능을 지원하지만 직관적이지 못한 구조로 대부분 오류코드를 직접 작성해 trace 로 찍어보는 방법이 가장 빠를것이다.
기존에 써오던 Alcon 이라는 디버깅 툴보다도 훨씬 강력한 기능을 가지고 있는 것 같다.

이 툴은 틀별한 과정없이 컴포넌트를 설치하고 타임라인 상에 드래그하여 export 하면 Interface window 를 통해 모든 오브젝트를 runtime 으로 확인하고 수정해볼 수도 있다.

이런 디버깅 툴이 가장 필요할때는 아마 웹상에 올려져있는 플래시 파일을 디버깅할때 일것이다.
이런툴을 사용하지 않고 원하는 데이타가 제대로 들어오는지, 웹상에 올려져 있는 플래시 파일을 디버깅 하려면 자바스크립트로 alert 창을 띄우는 방법밖에 없을것이다.

특히 필터효과 같은 경우 사용자들이 원하는 효과를 얻으려면 많은 테스트를 해보고 그 값 적용하는데 이 툴에서는 필터효과를 실시간으로 적용할수 있고 그 코드값을 복사해서 그대로 사용할수도 있다.

-Features

  • View physical parent/child relationships of all your objects/movieclips in the treeview
  • View all properties/methods associated with an object/movieclip
  • Drill down through any objects/movieclips/arrays in the Datagrid
  • Execute actionscript at runtime. Call any of your methods from the interface at runtime!
  • Control at runtime:
    • MovieClips – Edit all properties, including: Rotate, scale, move, opacity, properties, play, gotoAndPlay/Stop/labelName
    • TextFields – same as movieclips, as well as edit text, HTML text
    • Buttons – same as movieclips
    • video objects (NetStream) – play, pause, stop, view properties in realtime as video plays
    • Sound objects – play, stop, set loops, view ID3 information, all sound properties.
  • Use the _global.tt() to send any object/property to the output panel. Xray.trace() will recurse any object/array and display in the output panel a tabbed relational view of the object/array.
  • Use the search tool to search the output returned.
  • History info – use property settings saved in the history in your FLA or to reset your application to a specific state
  • Filters Panel – use Flash8 filters at run time and copy/paste the code for use in your FLA

    관련사이트
    http://osflash.org/xray