[AS3] getTileColor 메소드를 이용한 이미지

Programming/ActionScript 3.0 2008. 1. 28. 19:11
BitmapData의 일정한 간격의 pixel 컬러를 추축하는 메소드를 작성하고 이미지를 뽑아 보았다. 땡굴이 형님도 비슷한 작업을 진행중인 듯 싶다. 원본 이미지의 사이즈와 상관없이 16x16픽셀 아이콘도 이러한 확대한 결과물을 만들 수 있어서 여러가지로 사용할 수 있을 듯 싶다.

랜덤한 형태에서 좀더 관련이 있는 부분들을 조합하면 불규칙적이지만 규칙이 있는 재질을 만들어 낼 수 있을 듯 싶다.









사용자 삽입 이미지

    

설정

트랙백

댓글

Flickr API UML - as3flickrlib

Programming/ActionScript 3.0 2008. 1. 28. 14:57

Project Page: http://code.google.com/p/as3flickrlib/
Project Group: http://groups.google.com/group/as3flickrlib

위 구글 코드에서 오픈되어 있는 flickr API adobe 소스를 UML로 변환해 보았다. 필요하신 분들은 참고하시면 좋을 듯 싶다. 기본적인 상속과 합성관계만 형성해 놓은 상태, 대부분 합성으로 페키지가 구성되어 있다.

참고. 새벽에 작성한 관계로 틀린 부분이 있을 수 있음.










사용자 삽입 이미지

    

설정

트랙백

댓글

Wise를 확장한 SimpleFanwise, SimpleArchwise 클래스 응용

Programming/ActionScript 3.0 2008. 1. 23. 13:53
예전에 만들었던 호 그리는 클래스를 응용하여 부채꼴, 활꼴 모양을 그리는 Shape를 만들고 응용해보았다.
아래 CREATE 버튼을 클릭하면 왼쪽은 SimpleFanwise를 이용한 것이고 오른쪽은 SimpleArchwise를 이용하여 20개 오브젝트 생성후 사라지는 모션을 적용해 봤다.














    

설정

트랙백

댓글

[AS3] private, protected, internal, public을 어떻게 결정하면 좋은가

Programming/ActionScript 3.0 2007. 12. 31. 03:07
AS3의 private, protected, internal, public을 어떻게 결정하면 좋은가

일본의 ASer의 포스트을 보고 나를 포함한 많은 플래시 작업자들이 개념적으로는 이해 하고 있지만 실전에서 흐지부지 하게되는 것을 지양하기 위해서 그 분의 포스트을 토대로 접근자들을  정리해 본다.

각 접근자의 개념은 아래와 같다.

내부 이용자(자기 자신)               -> private
계승이용자(서브 클래스)             -> protected
외부 이용자(동일 패키지의 제삼자) -> internal
외부 이용자(다른 패키지의 제삼자) -> public
 




이하에 패키지 A_package의 클래스 A_class를 예로 들어 아래와 같을 경우, A_package 패키지 내에서는 모두 접근이 가능하다.
package A_package {
public class A_class {

private var p1:uint;
protected var p2:uint;
internal var p3:uint;
public var p4:uint;

public function A_class() {

p1 = 1; // OK
p2 = 2; // OK
p3 = 3; // OK
p4 = 4; // OK

}
}
}
동일 패키지의 상속 클래스는 private를 제외한 속성들에 접근이 가능하다.
package A_package {
public class B_class extends A_class {

public function B_class() {

v1 = 1; // error
v2 = 2; // OK
v3 = 3; // OK
v4 = 4; // OK

}
}
}
동일 패키지의 다른 클래스는 protected 속성에 액세스할 수 없지만 internal에는 액세스할 수 있다.
package A_package {
public class C_class {

public function C_class() {

var a:A_class = new A_class();

a.v1 = 1; // error
a.v2 = 2; // error
a.v3 = 3; // OK
a.v4 = 4; // OK

}
}
}
다른 패키지의 상속 클래스에서는 protected에는 액세스할 수 있지만 internal에는 액세스할 수 없다.
package B_package {

import A_package.A_class;

public class D_class extends A_class {

public function D_class() {

v1 = 1; // error
v2 = 2; // OK
v3 = 3; // error
v4 = 4; // OK

}
}
}
다른 패키지의 다른 클래스에서는 public밖에 액세스할 수 없다.
package B_package {

import A_package.A_class;

public class E_class {

public function E_class() {

var a:A_class = new A_class();

a.v1 = 1; // error
a.v2 = 2; // error
a.v3 = 3; // error
a.v4 = 4; // OK

}
}
}
예전에는 이런 구분 자체가 내 머리속을 어지럽게 하여 private와 public만을 사용했었다. AS3로 넘어오면서 상속, 합성, 오버라이드를 이용한 작업이 늘어나면서 유기적인 클래스들과의 관계 중심으로 구조를 설계하다보니 클래스 간에 접근 규정을 코멘트로 달아놓지 않는 한 스스로 혼란스럽게 되었다. 보통 private로 해두고 protected→internal→public 방향으로 작업을 하면 되겠으나 이것 또한 중간중간 에러체크를 감당해야 하는 부담을 지울 수가 없다.

확실히 private, protected, internal, public을 구분해서 작업 하는 것이 현재 작업자에게도 큰 도움이 된다. 가장 바람직한 방법으로는 작은 프로젝트라도 작업에 들어가기 전에 구조를 설계하고 노트에 UML 비스무리한 낙서를 하고 시작하는 것이 좋을 것 같다.

 

    

설정

트랙백

댓글

[AS3] TextField.htmlText 속성 문제

Programming/ActionScript 3.0 2007. 11. 12. 22:33
작업한 파일을 주말 동안 브라우저에 올려 놓고 몇 시간을 놔두었더니 CPU 점유율이 서서히 올라간다. 어디서 문제가 있는지 고민해 보았는데 이상하게도 메모리에는 이상이 없는 것 같은데 유독 CPU 점유율만 서서히 상승하는 것이다. 그래서 몇 개의 클래스를 두고 하나 하나 테스트를 해봤는데 결국 예전에 만들었던 DZeroNumber.as 클래스에서 문제가 있는 것으로 나타났다.

그런데 그 클래스 내에서도 특별히 문제가 될만한 곳을 발견하지 못하였는데 결국 생각지도 못한 곳에서 문제가 발생하고 있었다. DZeroNumber 클래스에서는 지정한 텍스트 필드에 00000 부터 지정한 숫자만큼 다이나믹하게 넘버링 하는 기능을 하고 있는데 그 클래스에서 000에 해당하는 부분과 실제 숫자에 해당하는 부분의 색을 달리하기 위해 TextField.htmlText 속성에 html 태그를 포함한 String을 넣고 있었다. 그런데 이 htmlText 속성이 문제가 있다.

TextField.text = "String" 을 넣을 때에는 CPU 점유율이 고정이지만 하나의 TextField에 htmlText속성을 통해서 html 태그를 포함한 String을 주기적으로 넣었을 때는 html 태그를 렌더링하는 과정에서 CPU 점유율이 점차적으로 상승한다. 그리고 이 상승하는 CPU 점유율은 swf파일이 종료되지 않는 이상 떨어지지 않는다.

TextField.htmlText = "<b> text </b>"

위와 같은 형태로 html 태그를 계속해서 TextField.htmlText에 넣게 되면 점차적으로 CPU 점유율이 100%를 향해 달려가니 주의가 필요하다.
    

설정

트랙백

댓글

Variable Notation for ActionScript

Programming/ActionScript 3.0 2007. 11. 7. 21:55

 플래시 액션스크립트 언어가 3.0으로 접어들면서 변수명 지정에 있어서 스스로 한계를 느껴서 변수명 표기법을 만들어 보았다. 기본적인 규칙은 헝가리안 표기법에 따랐으며 플래시의 특성에 맞는 타입들은 플래시 액션스크립트에서 권장하는 접미어를 사용 하였다.

 

사실 아래 이미지와 같이 표기법을 만들어 놓았지만 지금은 이와 같이 변수명을 지정하지 않고 있다. 이 테이블에서는 Scope 형태를 표기하기 위해서 g_, m_, l_, p_ 접두어를 사용하였는데, ActionScript 3.0으로 접어들면서 글로벌 변수의 개념이 불명확해 졌으며 접두어를 사용할 경우 변수명을 볼 때 한번 더 생각을 하게 만들기 때문에 읽기 어려운 점도 있는 듯하다. 그래서 이 테이블을 만들어 놓고도 사용하는 방법은 다음과 같다.

 

g_를 사용하는 글로벌 변수 표기는 제거 하고 클래스 속성변수(멤버변수)의 경우에는 _를 사용하며, 로컬 변수의 경우는 _를 없앴다. 그리고 파라미터로 넘겨받은 변수의 경우는 로컬변수에 포함하여 _가 없는 변수명을 하도록 했다. 따라서 이러한 표기법을 사용하면 아래와 같은 형태를 취한다.


package{
        public class VariableNotation extends Sprite{
                // ----------------- properties ---------------//
private var _strMsg:String; private const COUNT:int = 12;    // 상수는 대문자 표기
   // ----------------- Methods ----------------// public function VariableNotation():void{ _strMsg = “멤버변수 표기법”; initStart(); // 메소드는 대문자로 단어를 구분 } private function initStart ():void{ var strMsg:String = “로컬변수 표기법”; trace(“_strMsg : ”+_strMsg, “strMsg : ”+strMsg); } } }

표기법은 개인의 취향에 따라 다양한 형태를 나타낼 수 있지만 그때 그때 다른 표기법은 자신은 물론 다른 사람이 코드를 분석할 때도 어려움을 줄 수 있을 것 같다. 딱히 표기법이 없는 분들에게 권유해 본다.

사용자 삽입 이미지


    

설정

트랙백

댓글

콜린무크의 「지금부터 시작한다 ActionScript 3.0 - WORLD WIDE TOUR 」

Programming/Etc 2007. 11. 7. 00:54
일본 adobe에서는 세계적으로 저명한 콜린무크의 ActionScript 3.0을 위한 1일 집중 트레이닝(무료)에 참가자를 모집하고 있다고 한다. 이 1일간의 트레이닝에서는 무크의 베스트셀러인 ssential ActionScript 3.0(O'Reilly, 2007)에 근거하고 Flash Player및 Adobe AIR의 프로그래밍에 필요한 기본적인 스킬을 습득한다고 한다.
토픽은 객체지향프로그래밍, 클래스, 오브젝트, 변수, 메소드, 패키지, 조건 제어, 루프, 연산자, 함수, 이벤트 핸들링, 화면상에서의 표시, 프로그램의 컴파일 및 실행 등과 관련된 내용이다.

학습 내용
    * 오브젝트 프로그래밍의 모든 주요 개념
    * 클래스 및 오브젝트의 이해
    * ActionScript 3.0프로그램의 구축 및 기술 방법
    * Flex Builder 2의 개발 환경
    * ActionScript3.0주요 개념의 리뷰

일정 :
2008년1월15일(화)     9:30개장 10:00-19:00     300명
사용자 삽입 이미지

MAX 레퍼런스가 한국에서 개최되지 않았던 것도 아쉬운 마당에 이런 군침 도는 기회조차 일본에 빼앗긴 것 같아서 아쉽다. 하루 동안에 얼마나 많은 지식을 얻을 수 있겠냐고 이야기 할 수도있지만 그 느낌만으로도 충분한 가치가 있는 자리가 아닐까 싶다.

일본에 계시거나 일본에 가는 분들은 신청을 해보시길….

http://www.event-web.net/as3/
    

설정

트랙백

댓글

「Flash Performance Tips Part 1」의 한국어 번역

Programming/ActionScript 3.0 2007. 11. 4. 20:15
「BIG SPACESHIP LABS / Flash Performance Tips Part I」 에서 Flash의 최적화에 대한 이야기를 하고 있어서 포스팅한다. 내용은 번역 과정에서 잘 못된 부분이 있을 수 있으므로 이해가 필요하다. Flash사이트를 최적화를 위해 부드럽게 재생시키려면 93%의 노력과6%의 지식과4%의 땀과2%정도의 butterscotch(버터를 넣은 캔디)가 필요합니다.(영화 「Charlie & the Chocolate Factory」의 대사로부터의 인용 했다고 합니다)
농담은 접어두고, 많은 사람들이Flash Player의 퍼포먼스에 고민을 하고 있지만 사이트를 부드럽게 재생시키기 위해서 당신이 할 수 있는 것이 있습니다. 부드러운 재생을 위해서 우리(BIG SPACESHIP스탭)이 매일 사용하고 있는 수법을 몇 개 소개하겠습니다.

마스크는 안된다
모든 마스크를 사용해서는 안된다는 것은 아닙니다. 마스크가 매우 도움이 되는 것은 이미 아시는 바지요. 그렇지만, 마스크는 재생 퍼포먼스를 떨어뜨리는 원인의 주범입니다. 어떤 대상을 마스크 했을 때 Flash player는 매 프레임 마다 무엇을 표시 하고 무엇을 숨기는지를 계산하고 있습니다. 마스크와 능숙하게 친해지려면 어떻게 하면 좋을까요? 거기에는 조금의 인내와 트릭인 층 구조(예를 들면 표시시키고 싶은 영역 사이즈로 구멍을 뚫은 상위 층을 올려놓고 그것을 아래의 층에 씌운다든가)를 사용하면 퍼포먼스 저하를 막으면서 같은 표시 결과를 얻을 수 있겠지요.

알파 채널 첨부PNG와 비디오에 대해
마스크와 같이 이것도 사용하지 않을 수 없을 때도 있지만 이 때도Flash Player는 알파 아래 부분의 표시 계산 처리를 강요당하고 있습니다. 경우에 따라서 우리는 절반 사이즈의 알파 비디오를 Flash위에서 확대해 사용하기도 합니다.
그리고 알파 첨부의 동영상에 대해입니다만 알파 비디오 대신에 연속된 번호의PNG를 사용하는 방법도 시험하는 가치가 있을 거라고 생각합니다. 대부분의 경우는 비디오 형식이 약간 퍼포먼스가 좋은 듯 하지만 그 때 마다 실험해 볼 가치는 있습니다.

frame rate
여러 가지 논의가 있습니다만 세상에 말하는 「매직 frame rate」같은 것은 없습니다. 우리는25, 30fps로 만들고 있습니다.(내가 알기로는) 그것이 베스트라고 생각하고 있습니다. 우리는 그 때마다 어느 쪽이 보다 좋은가를 테스트해서 결정을 하고 있습니다. 그렇지만 일반적으로 말하면 frame rate가 사이트의 재생 스피드가 저하하는 주된 원인은 되지 않을 것입니다. 아무튼 통상의 사용에서는30fps이상은 별로 추천하지 않습니다. 그렇게 아주 대단한 스피드로 렌더링 시켜도…

cacheAsBitmap 과 BitmapData
cacheAsBitmap을 이용한 벡터의 아이템의 rasterize화는 상황을 선택해 사용하도록 해야 한다. 분명히Flash는 심볼을draw하는 것은 한 번으로  끝납니다. 하지만 심볼을 확대 축소나 회전시킬 때는 결코cacheAsBitmap하지 말아야 한다. 이 때 Flash는 매프레임 마다 렌더링과 캡쳐를 반복하므로 처리가 빨라야할 어딘가가 늦어져 버립니다.
Da Vinci CodeNike Air 의 사이트에서 우리는 보이는 부분의 screen shot를 찍어 그것을 하나의 무비 클립에draw시켜서 screen shot을 애니메이션 시키는 처리를 하고 있습니다. 이것은 많은 엘리먼트를 개별적으로 움직이거나 하는 것보다도 상당히 빠릅니다.이 수법은 꽤 추천합니다.

alpha보다 visible로 하는 편이 좋아

alpha = 0과 visible = false는 실은 전혀 별개입니다. alpha는 대상이 되는 클립의 투명도를 결정하는 것입니다. Visible는 FlashPlayer위에서 실제로 그 클립을 표시 할까, 하지 않을까를 결정하는 것입니다. 만약, 무언가를 비 표시로 설정하고 싶으면 visible프롭퍼티를 사용하도록 합시다.

onEnterFrame 과 setInterval
이러한 처리를 사용했을 때는 각각 onEnterFrame = null;이나 clearInterval(myInterval); 하고, 메모리로부터 클리어 해 두도록 합시다.그것을 하지 않는 채 방치하는 것은 전화의 통화 후에 수화기를 올린 채로 떠나는 것입니다.

수학 연산은 사전에 미리 해둡시다.
표시 직전의 단계에서 싸인 웨이브(※ -1~1를 취하는 파형)을 연산하고 있지 않습니까? 그것은 매회 같은 싸인 커브는 아닙니까? 그럴 때는 수치를 배열 안에 하드 코드 해 둡시다. 사전에 수학 연산을 해 두는 것으로, 직전에 복잡한 처리를 하지 않는 것이 좋습니다. 나는 트윈 시키고 싶을 때에 사전에 배열에 모든 트윈치를 배열에 넣어두고 nextFrame()으로 매 프레임 마다 이동시킨 적도 있습니다.

무음 사운드
우리도 마지막 수단으로서 사용하고 있습니다만 기억해 두는 가치가 있는 수법이라고 생각합니다. 타임 라인상의 독립된 층에 무음의 사운드 파일을 「스트리밍 재생•루프」로 설정해 놓아두면 FlashPlayer는 사운드에 동기 시키려고 하고 자동적으로 프레임을 솎아내 조정해 줍니다.

//////////////////////////////////////////////////////////////////////////////////

위 내용은 「BIG SPACESHIP LABS / Flash Performance Tips Part I」의 내용이다. 이 밖에서 작업자 입장에서 FlashPlayer를 웃을 수 있게 해주는 방법은 여러 가지가 있을 듯 싶다. 현재 생각나는 몇 가지만 추가하고 그 이후 자신이 알고 있는 방법이 있으면 코멘트로 소통했으면 좋겠다.

1.    위에서 visible = false; 로 하는 것은 화면상의 표시 오브젝트에서 제외 되지만 그 안에서 연산하고 있는 처리는 그래도 FlashPlayer의 몫이다. 따라서 visible = false를 적용한 오브젝트 안에서 이루어지는 연산도 멈춰 주는 것이 바람직하다.

2.    for(var i:int=0;i<m_arr.length;i++) 와 같이 for문의 비교연산에서 직접 m_arr.length 값을 취득하는 것 보다 상위에 var l_intLen:int = m_arr.length; 변수를 미리 정의하여 사용하는 것 이 좋다. 이는 for문이 비교연산을 처리할 때 매번 m_arr 배열의 개수를 취득하기 위해서 연산처리를 시도하기 때문이다.

3.    _mcParent._mcChild1._mcChild2; 와 같은 참조를 여러 번 사용할 때는 상위에 var _mcChild2:MovieClip = _mcParent._mcChild1._mcChild2; 변수를 미리 선언하여 _mcChild2를 사용하는 것이 좋다. 이는 .연산자를 통해서 매번 depth 탐색이 이루어 지기 때문이다.

4.    _mcParent[“_mcChild1”]와 같은 참조 보다는 _mcParent._mcChild1과 같은 참조가 빠르다. _mcParent[“_mcChild1”]와 같은 형태로 참조할 경우에는 해시테이블 탐색을 통해서 string의 값을 비교 연산을 하기 때문이다.

5.    onEnterFrame or Timer and Interval과 같은 빠른 속도로 처리되는 function 안에서는 특정 기능을 하기 위한 코드들을 외부 function을 호출하는 형태로 분리 하는 것 보다는 직접 function 안에 쓰는 것이 좋다.(이는 유지 보수와 재 사용상의 문제가 있을 수 있음)

6.    for문을 사용하여 연속된 연산처리를 수행하는 것 보다는 절차지향으로 라인으로 풀어 쓰는 것이 빠르다.(onEnterFrame이나 interval과 같은 function 안에서 for문을 사용하는 것은 치명적이다. 이럴 때는 라인에 풀어 쓰는 것이 좋다)

이 밖에도 많은 테스트들이 있었을 것으로 생각한다. 각자 가지고 있는 노하우가 있으면 댓글을 달아 주길 바란다.

    

설정

트랙백

댓글

ActionScript는 프로그래밍 언어가 아니다.

Programming/Etc 2007. 11. 4. 19:21
인터렉션 UI개발과 그 목적을 위해서 사용하고 있는 ActionScript는 프로그래밍 언어가 아니다. 내 천직으로 생각하고 지금 하고 있는 업무가 그것이지만 ActionScript를 프로그래밍 언어적 차원에서 접근 하는 것에는 한계가 있다고 생각한다.

플래시라는 것은 기술적인 환경이나 배경지식 보다는 아이디어가 중요하다. 이러한 아이디어는 다양한 결과물을 만들어 낼 수 있다. 물론 그 아이디어는 어느 정도의 배경 지식이 있어야 하겠지만 없어도 무방하다. 가령 내가 어떤 비주얼을 보고 머리 속에서 그린 인터렉션을 구현하기 위해 작업에 착수 했다고 하자, 내가 필요로 하는 편리한 기능이 플래시 안에 내제되어 있지 않더라도 자신이 가지고 있는 지식 범위 안에서 찾을 수 있는 길은 있을 것이다. 이것이 플래시를 즐기는 사람들의 즐거움이자 정석이다.

플래시로 작업을 하다 보면 자신도 이 방법이 정석인지 아닌지 알 수 없는 경우가 있다. 그래서 작업이 끝나고도 뒤 끝이 어수선한 경우가 종종 발생하게 되는데, 어찌보면 플래시 작업에서의 정석은 없을 수도 있다. 이미 플래시에 탑재 되어 있는 내장 클래스를 모르더라도 그와 유사한 기능을 하는 것을 만들어 낼 수도 있고 전혀 다른 방법으로 문제를 풀어 낼 수도 있다.

그 과정에서 파생된 또 다른 아이디어는 다시 싹을 틔우고 꽃이 피어나는 결과로 진행 될 수도 있다. 하지만 플래시를 프로그래밍 언어적 개발 마인드로 접근을 하다 보면 정석만이 있으며 그것이 유일한 방법이라는 확신에 사로잡혀서 크리에이티브한 창의를 발휘할 수 있는 여지를 묵살하게 된다.

나는 작업을 하면서 엉뚱하며, 디자인 표현에 맞지 않는 결과물들도 많이 만들어 낸다. 남들 보다 같은 결과물을 얻기 까지 시간이 많이 걸리지만 작업 과정에서 떼 묻은 코드들은 또 다른 나의 아이디어로 언젠가는 도움이 되는 것 같다.

현재 플래시를 공부하고 관심을 갖고 있는 분들은 이러한 즐거움을 느꼈으면 좋겠다. 설계, 개발이라는 단어보다는 디자인이라는 단어가 어울리는 플래시는 프로그래밍 언어가 아니라 크리에이티브 언어이다.

    

설정

트랙백

댓글

[AS3] 스테이지에 올려놓은 TextField의 autoSize 설정

Programming/ActionScript 3.0 2007. 11. 3. 18:49
회사 작업으로 만들었던 클래스를 이리저리 가지고 놀다가 플래시 툴에서 stage에 만들어 놓은 TextField의 autoSize 설정이 제대로 먹히지 않는 경우가 발생하여 한참을 고생했다. new TextField()로 생성한 TextField의 경우에는 제대로 먹히는데 stage에 직업 올려놓은 TextField의 설정이 처음에는 되었다가 어느 순간부터 먹히지 않거나 중간에 사이즈가 늘어나다 끊기는 현상이 발생했다.

문제의 이유는 확인하지 못하였는데 여러가지 테스트를 해보다가 결국은 기존에 있던 텍스트 필드를 삭제하고 다시 텍스트 필드를 만들어서 같은 속성으로 적용을 해보니 이번에는 제대로 먹는다...

stage에 있는 TextField의 폰트를 임베드 시키고 폰트를 다른 폰트로 바꾸게 되면 그때부터 autoSize 속성이 적용이 되지 않는다. 텍스트필드에 filter를 적용해도 autoSize 속성이 먹통이 되버린다...

저 처럼 stage에 올려놓은 텍스트필드의 autoSize가 먹히지 않을 때에는 주저하지 말고 기존에 있던 텍스트 필드를 지우고 새롭게 텍스트 필드를 만들어서 테스트 해보길 바란다.



    

설정

트랙백

댓글

[에디터] Eclipse 플러그인 FDT 3.0

Programming/Etc 2007. 10. 17. 00:14
Flash 5.0 이전에는 특별히 코딩을 위해 따로 에디터를 사용할 필요성을 느끼지 못했다. 하지만 7.0으로 넘어오면서 ActionScript가 강화되었고, 코딩하는데 있어서 플래시 툴 자체에서 지원하는 에디터 화면으로는 불편한 점이 많았다. 그래서 예전에는 플래시 액션스크립트를 코딩 할 때, 주로 sepy를 사용하다가 요즘은 FlashDevelop 프로그램을 사용하고 있다. 둘 모두 무료 소프트웨어 이기 때문에 사용하는 것도 있지만 플래시툴에서 지원하는 에디터보다 사용성에 있어서 편리한 점이 많기 때문이다.

오늘 블로그를 돌아다니다가 Powerflasher.com에서 FDT 3.0 이라는 아주 훌륭한 상용 Eclipse 플러그인이 3.0으로 릴리즈 되었다는 정보를 알게 되었다. FDT는 예전에 땡굴이님이 배타테스터로 블로그에 포스팅 한 글을 읽어보긴 했지만 사용성에 있어서 얼마나 많은 편의를 제공하는지는 잘 알지 못했다.

아래 경로로 접근하면 FDT 사용방법에 대한 동영상을 볼 수 있다.

FDT 3.0 Basic
http://fdt.powerflasher.com/uploads/Media/liveCodeGeneration.htm

FDT 3.0 Professional
http://fdt.powerflasher.com/uploads/Media/formatter.htm

사용자 삽입 이미지 사용자 삽입 이미지 사용자 삽입 이미지



    

설정

트랙백

댓글

Flash Player 10 코드네임 Astro

Programming/Etc 2007. 10. 2. 18:28
Astro 코드네임으로 불리고 있는 Flash Player 10의 신기능 중에서 몇 개가 미국 시카고에서 개최한 MAX 컴퍼런스에서 공개되었다는 소개글이 있어서 포스팅한다. 공개된 내용은 아래와 같다.

1.새로운 텍스트 레이아웃 엔진
Astro에서는 향상된 새로운 기능의 텍스트 표현 엔진이 탑재될 예정이라고 한다. 이를 통해서 복수 컬럼의 레이아웃이나 이미지를 자동적으로 감싸는 레이아웃, 그리고 테이블 형식의 레이아웃 등이 가능하게 될 것 이라고 한다.

2. 3D 효과
Flash의 무비 클립을 3D 공간 내에서 취급할 수 있는 기능이 제공될 예정이라고 한다. PaperVision3D나 Away3D등과 같은 엔진이 탑재 되는 것은 아닌 것 같지만 표현의 폭이 다양해짐에 따라서 추가적인 3D엔진에 대한 기능 개선이 있을 것으로 기대되는 대목이다.

3.custom 필터, 브랜드, 효과
Flash Player 8 버전부터 추가되었던 필터기능에서 추가적으로 스스로 작성한 필터나 효과를 사용할 수 있게 될 예정이라고 한다. 필터등의 작성에는 Adobe Image Foundation (AIF) 툴킷을 사용하게 되는데 AIF 툴킷은 프리뷰판을 다운로드할 수 있게 되어 있다. 관심이 있는 분은 아래 경로에서 다운 받아서 사용해 보길 바란다.

(Adobe Image Foundation (AIF) Toolkit@Labs )



Flash Player 10의 릴리즈 시기는 아직 미정이라고 한다.


    

설정

트랙백

댓글

[스크린세이버] 바람과 데스크탑

Programming/Etc 2007. 9. 19. 04:16
웹 서핑중 재미있는 스크린세이버가 있어서 소개한다. 사내 프로젝트 일환으로 작업 했다고 하는데 발상이 재미있다. 플래시 AIR을 이용하여 데스크탑의 이미지를 비트맵데이타로 크롭하여 모션 효과를 준 듯하다. AIR을 이용하면 다양하고 기발한 결과물들을 만들 수 있지 않을까 싶다. 3.0에 어느정도 익숙해 지면 취미로 AIR도 접해봐야 겠다...

이 스크린세이버는 재미있기는 하지만 컴퓨터가 쉬어야할 시간에 너무 많은 일을 하고 있다. 화면 보호도 중요하지만 CPU가 너무 바쁜거 아닌지...

스크린세이버 제작자
프로그램 전반:키타무라 케이태
디자인/플래시:하나무라 타로
원안:사카이회리가
디렉션:나카무라 이사무오

프로젝트 임시 홈페이지 : http://scr.sc

트라이얼 버전 프로그램 :



    

설정

트랙백

댓글

[AS3] 호(arc)를 그리는 클래스

Programming/ActionScript 3.0 2007. 9. 15. 03:08
호를 그리는 클래스를 만들어 봤다. 여러가지 자료들을 급조하여 기본적인 호를 그리는 형태를 제작한 관계로 -값이나 예상치 않은 입력 값에 따른 예외는 처리하지 않은 상태다. 사용하실 분들은 개인적으로 자료 정리를 위해서 패키지 설정을 해 놓은 상태이니 원하는 폴더 구조 및 패키지를 설정하여 사용하거나 패키지(jasu.display)를 제거하고 fla와 같은 폴더에 복사 후 사용 하면 된다.









    

설정

트랙백

댓글

[AS3] Pen 클래스 테스트

Programming/ActionScript 3.0 2007. 9. 14. 12:52
Pen 클래스를 이용해서 드로잉 테스트겸 만들어 봤다. Graphics 객체는 드로잉을 하면 할수록 CPU를 많이 잡아먹는디 Graphics 객체 안에서 어떤 일이 벌어지길에 이런 현상이 발생하는지 모르겠다... 그리는 과정에서 비트맵데이타로 처리해버리면 될거 같은디 귀찮스러워서 그냥 올려 놓는다. 테스트 하시는 분들은 너무 많이 그리지 마셔용...쿠쿠











 
 
    

설정

트랙백

댓글

[AS3] UIScrollBar 동적으로 스킨 적용하기

Programming/ActionScript 3.0 2007. 9. 12. 06:43
UIComponent에 해당하는 컴포넌트들의 스킨을 적용하는 방법에 대해서 FlashCS3의 레퍼런스에서는 컴포넌트를 라이브러리에 등록하고 등록된 컴포넌트를 스테이지에 끌어다 놓고 무비클립을 직접 수정하는 방법 이외에는 이렇다 할 설명을 하지 않고 있는 듯 하다.

개인적으로 웹상에 올려지는 스크립트들을 이쁘게(?!) 보이도록 만들어 보려고 AScodeViewer를 작업하고 있는데, 외부에 있는 Style xml 데이터를 로드하여 사용자가 작성한 xml에 따라서 UIScrollBar와 기타 화면상의 DisplayObject들의 스타일을 적용하는 과정에서 컴포넌트의 스킨을 동적으로 적용을 해야 했다. 웹상에서 찾아보았지만 그와 관련된 자료를 찾지 못하여 임의로 적용해 보게 되었다.

일단은 컴포넌트의 스킨을 동적으로 적용하기 위해서는 기존의 컴포넌트들이 어떠한 형태로 스킨을 적용하고 있는지, 구조를 파악할 필요가 있다. 컴포넌트를 스테이지에 올려놓고 안으로 들어가보면 해당 컴포넌트에서 사용되는 기능별 무비클립들을 볼 수 있는데 이러한 무비클립들은 2 프레임에 위치하고 있기 때문에 외부에서 직접 해당 무비클립에 접근 할 수는 없다.

라이브러리에 있는 Component Assets -> ScrollBarSkins 폴더에 보면 사용되는 무비클립들을 볼 수 있는데 이들 무비클립들에는 각각 내부 클래스가 지정되어 있다. 이 클래스들이 각각의 무비클립을 대변한다고 볼 수 있다.

그럼 각각의 무비클립의 색을 변경하기 위해서는 어떻게 해야 할까… 여러가지로 실험도 해보고 고민을 해보았는데 일단은 아래 방법은 개인적으로 작업하는 과정에서 만들어 낸 결과이기 때문에 옳은 방법이 아닐 수 있음을 밝혀둔다.

UIScrollBar 컴포넌트의 상속 관계에서 setStyle 메소드를 발견할 수 있는데 이 메소드는 스타일 적용에 필요한 default 클래스를 custom 클래스로 대체할 수 있도록 해준다. 따라서 setStyle 메소드를 통해서 기존에 등록되어 있는 각 무비클립의 클래스를 자신이 원하는 기능을 하는 클래스로 대체할 수 있다는 이야기다. 아래는 디폴트로 지정되어 있는 클래스들이다.

ScrollArrowDown_disabledSkin
ScrollArrowDown_downSkin
ScrollArrowDown_overSkin
ScrollArrowDown_upSkin
ScrollThumb_downSkin
ScrollBar_thumbIcon
ScrollThumb_overSkin
ScrollThumb_upSkin
ScrollTrack_Skin
ScrollArrowUp_disabledSkin
ScrollArrowUp_downSkin
ScrollArrowUp_overSkin
ScrollArrowUp_upSkin

그리고 아래는 디폴트 클래스와 매칭되는 문자열들이다.

downArrowDisabledSkin
downArrowDownSkin
downArrowOverSkin
downArrowUpSkin
thumbDownSkin
thumbIcon
thumbOverSkin
thumbUpSkin
trackSkin
upArrowDisabledSkin
upArrowDownSkin
upArrowOverSkin
upArrowUpSkin

일단 적용한 형태를 보면 아래와 같다.
private function setScrollBarStyle(s:UIScrollBar):void{
s.setStyle("downArrowDisabledSkin", DScrollArrowDown_disabledSkin);
s.setStyle("downArrowDownSkin", DScrollArrowDown_downSkin);
s.setStyle("downArrowOverSkin", DScrollArrowDown_overSkin);
s.setStyle("downArrowUpSkin", DScrollArrowDown_upSkin);

s.setStyle("upArrowDisabledSkin", DScrollArrowUp_disabledSkin);
s.setStyle("upArrowDownSkin", DScrollArrowUp_downSkin);
s.setStyle("upArrowOverSkin", DScrollArrowUp_overSkin);
s.setStyle("upArrowUpSkin", DScrollArrowUp_upSkin);

s.setStyle("thumbDownSkin", DScrollThumb_downSkin);
s.setStyle("thumbIcon", DScrollBar_thumbIcon);
s.setStyle("thumbOverSkin", DScrollThumb_overSkin);
s.setStyle("thumbUpSkin", DScrollThumb_upSkin);
s.setStyle("trackSkin", DScrollTrack_Skin);
}

위 코드에서는 적용할 UIScrollBar를 전달 받아서 setStyle 메소드를 통해서 각 무비클립의 클래스들을 새로운 클래스(D로 시작하는 클래스들)들로 대체했다. 새로 대체한 클래스에서는 각각 디폴트로 지정되어 있는 클래스들을 extends 시켰다. 이렇게 한 이유는 라이브러리에 있는 컴포넌트 무비클립들을 그대로 사용하기 위함이다.

이렇게 작성하게 되면 컴포넌트에서 사용하는 각각의 무비클립에서 원하는 기능을 수행할 수 있는 새로운 클래스들로 대체했기 때문에 새로 등록된 클래스 안에서 무슨 짓을 하더라도 플래시가 용서해 준다…쿠쿠

각 클래스 안에서는 자신의 무비클립의 색을 변경하는 클래스를 통해서 색과 알파값등을 세팅하도록 하였고 셋팅하는 방법으로는 중앙에 Singleton 클래스를 만들어 놓고 xml에서 전달받은 스타일을 각 무비클립에 입히는 방법으로 사용했다. 참고로 색 변경에 사용한 간단한 클래스도 올려 놓는다.
package viewer.skins{
import flash.display.DisplayObject;
import flash.geom.Transform;
import flash.geom.ColorTransform;

public class TintColor{
public function TintColor():void{}
public static function setTintColor(d:DisplayObject, c:uint, a:Number):void{
var resultColorTransform:ColorTransform = new ColorTransform();
resultColorTransform.alphaMultiplier = a;
resultColorTransform.color = c;

var transformation:Transform = d.transform;
transformation.colorTransform = resultColorTransform;
}
}
}

이 과정중에서 버그를 발견하게 되었는데 다른 것들은 위에서 언급한 문자열을 통해서 클래스를 대체할 수 있었으나 유독 trackSkin(스크롤바의 기본 바탕이 되는 배경)의 경우 클래스를 대체하지 못하는 문제가 있다. 이 문제로 검색을 해 보니 adobe에 버그로 등록되어 있는 것 같은데 그 해결책은 찾지 못하였다.. 그렇다고 그냥 지나칠 수 있낭…자존심이 허락하는 범위 안에서 꼼수를 부렸다.

적용한 방법은 쿠쿠 웃기는 이야기이긴 하지만 컴포넌트 스킨에서 스크롤바의 배경이 되는 무비클립을 투명으로 처리하고 스크롤바 아래에 무비클립을 두어 스테이지가 변경될 때 스크롤바의 크기와 위치에 따라 이동하는 배경 무비클립을 따로 만들어 놓고 그 무비클립의 색을 변경하는 방법으로 적용했다…쿠쿠 약간 찜찜하지만 trackSkin 버그 문제가 해결되기 전까지 임시방편으로 적용해 두기로 했다.

스테이지 상에서 무비클립들을 직접 수정하여 사용할 경우 일단 사용된 최초 컴포넌트만을 사용하기 때문에 서로 다른 스킨을 적용하기가 어렵지만 이러한 방법을 사용하면 컴포넌트 각 인스턴스에 개별적인 스킨을 적용할 수 있다.

AScodeViewer는 거의 85% 정도 완성된 것 같다. FlashDevelop의 as 코드 스킨과 스크립트 창의 배경까지 직접 xml을 편집하여 사용할 수 있도록 하였기 때문에 자신의 FlashDevelop 환경과 거의 비슷한 화면을 웹상에 올려놓을 수 있을 듯 싶다. FlashDevelop에서 사용하는 xml데이터를 그대로 사용하다보니 AS 코드 뿐만이 아니라 Java나 기타 언어에서도 사용 할 수 있을 듯 싶다. 중요 포인트는 풀스크린 모드 기능을 지원한다는 것이 아닐까 싶다....
    

설정

트랙백

댓글

[FlashCS3] Auto Format 기능 주의...

Programming/ActionScript 3.0 2007. 9. 6. 03:16
코드 100줄 이상 넘어가는 것들은 대부분 FlashDevelop에서 작업을 하지만 그보다 적은 코딩 테스트를 할 경우에는 따로 열기 귀찮아서 FlashCS3에 내장된 스크립트 창을 사용하는데, Flash CS3의 Auto format 기능이 여러 가지로 문제가 좀 있다. CS3에서 ActionScript 3.0으로 버전업 하면서 스크립트 자체 기능은 강화되었다지만 FlashCS3 툴은 완성도는 다소 떨어지는 느낌이다. Sepy나 FlashDevelop 처럼 안되더라도 그에 부합하는 기능 보강은 필요할 듯싶다.

아래 코드의 경우 예상되는 결과값은 9가 되어야 하지만 스크립트 창에 있는 Auto Format 기능을 실행해서 코드를 정렬하고 퍼블리시를 해보면 결과는 엉뚱하게 7이 나온다.



trace(getResult());
function getResult(a:int=1, b:int=2, c:int=3):Number {
return (a + b) * c;
}

Auto Format 기능을 실행하게 되면 return 이후에 나타나는 ()는 리턴값으로 간주하여 ()를 제거해 버린다. 이 때문에 결과로 내보낼 사칙연산 순서가 b*c + a로 되어버린다. 실제로 Auto Format 기능을 실행하면 아래처럼 코드상에서 ()가 제거된 것을 확인 할 수 있다.
trace(getResult());
function getResult(a:int=1, b:int=2, c:int=3):Number {
return a + b * c;
}

간단한 코드의 경우에는 문제가 되는 부분을 찾을 수 있겠지만 덩치가 큰 코드의 경우에는 Auto Format 기능 한방으로 예상치 못한 에러 아닌 에러를 내는 곳을 찾기란 쉽지 않다. 그나저나 FlashDevelop은 왜 Auto Format 기능 안 넣는겨….


    

설정

트랙백

댓글

[AS3] BitmapData.lock and unlock 기능

Programming/ActionScript 3.0 2007. 9. 3. 09:45
BitmapData에 있는 lock/unlock 메소드는 레퍼런스에 기능이 명시 되어 있지만 짧게 설명되어 있어서 지나치기 쉬운 것 같다. 아래는 레퍼런스에 나와 있는 내용이다.

public function lock():void
언어 버전 :     ActionScript 3.0
Player 버전 :     Flash Player 9
이 BitmapData 객체를 변경할 때 BitmapData 객체를 참조하는 어떤 객체(예: Bitmap 객체)도 업데이트되지 않도록 이미지를 잠급니다. 성능을 높이려면 setPixel() 또는 setPixel32() 메서드를 여러 차례 호출하기 전후에 이 메서드를 unlock() 메서드와 함께 사용합니다.

레퍼런스의 내용만으로는 정확히 어떠한 역할을 하는지 알기 쉽지 않다. lock 메소드는 BitmapData의 정보를 화면에 draw하는 처리과정을 일시적으로 사용하지 않도록 하여 불필요한 처리과정을 제거함으로써 퍼포먼스를 향상시킬 수 있다. 예를 들면

보통 BitmapData를 처리하기 위해 addChild(new Bitmap(bitmap))와 같이 addChild한 이후에 setPixel() 또는 setPixel32() 메소드를 사용하는 경우가 많은데, 이때 setPixel() 또는 setPixel32()와 같은 메소드는 그 메소드 자체 처리만으로도 addChild 되어 있는 경우 화면에 draw하는 기능을 함께 처리해 버린다. 따라서 addChild되어 있는 BitmapData에 setPixel() 또는 setPixel32() 메소드를 여러 번 사용하여 복잡한 처리를 할 경우에 setPixel() 또는 setPixel32() 메소드를 호출될 때마다 화면에 draw 하기 때문에 불필요한 CPU 처리를 하게 되는 것이다.

물론 setPixel() 또는 setPixel32()메소드의 처리 과정을 화면에 표시해야 하는 경우라면 별로 효력이 없겠으나 setPixel() 또는 setPixel32() 메소드를 여러 번 처리한 이후 결과만을 화면에 표시할 경우는 lock 메소드를 통해서 화면에 표시하는 기능을 잠그고 일련의 처리를 완료한 이후에 unlock 메소드를 통해서 잠근 기능을 풀어주면 불필요한 처리를 하지 않으면서 화면에 처리한 결과를 표시할 수 있다.

아래 예제를 돌려보면 lock/unlock를 사용하지 않았을 때와 사용했을 때의 차이를 확인 할 수 있다.

lockTest(700, 700);

function lockTest(w:uint, h:uint) {
var bm:BitmapData = new BitmapData(w,h);
addChild(new Bitmap(bm));

trace("lock/Unlock 사용 안함 : "+drawBitmap(bm) + "ms");

bm.lock();
trace("lock/Unlock 사용 : "+drawBitmap(bm) + "ms");
bm.unlock();
}
function drawBitmap(bm:BitmapData):uint {
var start:uint = getTimer();
var w:uint=bm.width;
var h:uint=bm.height;

for (var x:uint=0; x < w; x++) {
for (var y:uint=0; y < h; y++) {
bm.setPixel(x,y,Math.random()*0xFFFFFF);
}
}
return getTimer() - start;
}

// output
lock/Unlock 사용 안함 : 214ms
lock/Unlock 사용 : 157ms

output 결과는 시스템에 따라 다소 차이가 있을 수 있는데 테스트한 컴퓨터 스펙은 아래와 같다.

소니 바이오 노트북 VGN-SZ18LP
Genuine Intel® CPU T2400 @ 1.83GHz
메모리 1.50GB RAM

    

설정

트랙백

댓글

[OOP] 디자인 원리 - Head First-OOAD

Programming/Design Patterns 2007. 8. 28. 07:35
『Head First Object-Oriented Analysis&Design』 책의 내용 중에 8장에서 다루고 있는 디자인 원리에 관한 좋은 내용이 있어서 소개한다.

여담 : 여러분들 마우스 옆에 핸드폰 놓지 맙시다. 자꾸 헷갈리네…쿠쿠

일단 와 닫는 짧은 문장 하나, “모방은 바보 같은 짓을 하지 않기 위한 가장 진지한 방안입니다.” 이 이야기는 새롭고 독창적인 방법이 좋을 수 있으나 그 방법은 기존에 같은 문제를 해결한 것이 없거나 자신의 독창적인 방법이 나은 결과를 가져왔을 때만 유효한 해결책이라는 설명이다. 이런 비슷한 내용은 디자인 패턴 관련 책에서 주로 다루고 있는데 이미 입증된 것이 항상 옳은 방법일 수는 없으나 가장 안전한 방법일 듯 싶다.

디자인 원리 : 디자인 원리는 코드를 좀 더 유지보수하기 쉽고, 유연하고, 확장하기 쉽게 만들기 위해, 코드의 작성이나 디자인에 적용되는 기본 도구 또는 기법이다.

객체지향 원리
1.    변하는 것을 캡슐화하라.
2.    구현에 의존하기 보다는 인터페이스에 의존하도록 코딩하라.
3.    각 클래스는 변경 요인이 오직 하나이어야 한다.
4.    클래스는 행동과 기능에 관한 것이다.

위와 같은 디자인 원리에 대한 구체적인 내용을 살펴보면 아래와 같다.

원리 1)
개방-폐쇄의 원리 OCP(Open-Closed Principle)

클래스는 확장에는 열려 있고, 수정에는 닫혀 있어야 한다는 것으로 보통 추상클래스의 확장과 override 개념에서 찾을 수 있을 듯싶다. 추상 클래스(상위 클래스) 자체는 하나의 독립되고 완전한 기능을 수행하며 수정에 닫혀 있고 그 추상 클래스를 확장하는 클래스에서 개별적으로 필요한 기능은 오버라이드를 통해서 고유의 기능을 수행할 수 있도록 하는 개념을 의미한다.

Java에서는 파라미터의 형태, 파라미터의 개수, 메소드의 접근성이 다를 경우에는 전혀 다른 메소드로 인식할 수 있다. 이 때문에 private로 선언된 함수를 사용하되 그 함수와 다른 기능을 수행하는 같은 메소드명의 public 메소드를 새로 만들어 외부에서 접근할 수 있도록 하는 경우도 이 OCP원리에 부합되는 예라고 할 수 있다.

원리 2)
반복 금지의 원리 DRY(Don’t Repeat Yourself)

공통되는 부분을 추출하여 추상화하고 한 곳에 두어 중복 코드를 피하라는 내용이다. 책에서는 2장에서 설명한 강아지 문(외국의 전원주택을 보면 애완견이 스스로 출입할 수 있도록 현관문 아래에 작은 문을 달아놓은 것을 프로그래밍한 내용)에 대한 이야기로 개념을 설명하고 있는데 리모컨을 눌렀을 때 열려있던 문은 닫히고 닫혀있던 문은 여는 기능을 수행한다. 또한 문은 리모컨 뿐만이 아니라 강아지가 짖는 소리에도 열린다. 그리고 열려있던 문은 일정한 시간이 지나면 자동으로 닫게 설계되어 있는데 이때 일정한 시간이 흐르면 자동으로 닫히는 기능을 리모컨 기능에도 넣고 강아지가 짖는 것을 판별하는 기능에도 넣었을 때 코드가 중복되는 것을 강아지 문 하나에 넣어 문이 open 되었을 때 일정 시간이 지나면 닫히게 할 수 있다는 것이다. 강아지 문을 여는 행위를 지시하는 곳은 두 곳이지만 이 두 곳 모두 일정 시간이 지나면 자동으로 닫히는 기능을 넣는 중복을 피하고 그 행위를 하는 강아지 문에 기능을 넣는 것이 좋다는 것이다. 하나의 요구사항은 한 곳에 두어 코드 중복으로 인한 유지보수의 어려움을 없애야 한다는 내용이다.

원리 3)
단일 책임의 원리 SRP(Single Responsibility Principle)

시스템의 모든 객체는 하나의 책임만을 가지며, 객체가 제공하는 모든 서비스는 그 하나의 책임을 수행하는 데 집중되어 있어야 한다는 내용이다. 이는 얼핏 보면 반복 금지의 원리와 비슷하다고 볼 수 있는데, 반복금지의 원리는 SRP를 포괄하는 내용이라고 볼 수 있다. 반복금지의 원리는 하나의 기능을 한 곳에 두자는 내용으로 그 한 곳이 클래스가 될 수도 있고 코드가 될 수도 있다. 하지만 SRP는 클래스가 한 가지 일만 잘하게 하자는 내용이다.

책에서는 간단하면서도 재미있는 방법을 제한하고 있는데 아래와 같은 방법이다.

[*]이 자신을 [*]한다.를 한 줄에 하나씩 작성하고 첫 번째 빈칸에는 자신 클래스명을 기입하고 두 번째 빈칸에는 자신 클래스에서 사용하고 있는 메소드명을 기입하는 것이다. 이렇게 작성하여 한 줄씩 큰 소리로 읽어보면 ‘무엇이 자신을 무엇 한다’는 말이 맞지 않은 경우가 SRP를 위반할 가능성이 높다는 이야기다. 예를 들면 다음과 같다.

Automobile 이라는 클래스에 아래와 같은 메소드들이 있다고 가정하면
1.    start()
2.    stop()
3.    changeTires(Tire[*])
4.    drive()
5.    wash()
6.    checkOil()
7.    getOil():int

1.    [Automobile]이(가) 자신을 [start](출발한다).
2.    [Automobile]이(가) 자신을 [stop](멈춘다).
3.    [Automobile]이(가) 자신을 [changeTires](타이어를 간다).
4.    [Automobile]이(가) 자신을 [drive](운전한다).
5.    [Automobile]이(가) 자신을 [wash](닦는다).
6.    [Automobile]이(가) 자신을 [checkOil](오일을 점검한다).
7.    [Automobile]이(가) 자신을 [getOil](기름을 얻는다).

위와 같이 썼을 때 책에서는 3개의 메소드는 SRP를 따르고 있고 나머지 4개의 경우에는 SRP를 따르지 않고 있다고 이야기 하고 있다. 다시 말해서 3개의 메소드는 Automobile 클래스에 있어야 하지만 나머지 4개의 메소드는 따로 빼내어 다른 클래스로 만들어야 단일 책임의 원리를 따를 수 있다는 이야기다.

답은 며칠 후에 이 포스트에 추가로 올려놓도록 할 테니 여러분들도 한번 고민해 보고 어떤 것이 단일 책임의 원리를 위반하고 있다고 생각하는지 댓글을 남겨보면 좋겠다. 책에서는 SRP.는 단지 가이드라인일 뿐이지 꼭 이 방법을 따라야 한다는 것은 아니라고 설명하고 있다. 클래스의 기능과 프로젝트에 대한 상식, 그리고 경험을 통해서 디테일한 부분을 스스로 결정해야 한다고 설명하고 있으니 부담 없이 참여해 보시길…

원리 4)
리스코프 치환 원리 LSP(Liskov Substitution Principle)

자식 타입들은 부모 타입들이 사용되는 곳에 대체될 수 있어야 한다는 내용이다. 이는 잘 디자인된 상속에 관한 내용으로 부모 클래스를 상속할 때, 부모 클래스가 사용되는 곳은 아무 문제없이 자식 클래스도 사용할 수 있어야 한다는 것이다. 그렇지 않으면 상속을 잘못 사용하고 있다는 것.

예를 들어 Board라는 클래스는 대부분의 메소드에서 x와 y의 인자를 사용하지만 3DBoard 클래스의 경우는 x,y,z 인자를 사용한다. 이때 3DBoard 클래스가 Board 클래스를 상속하였을 때 3DBoard 클래스에서는 상속된 Board 클래스의 기능이 전혀 필요 없음에도 상속을 하는 경우이다.

이렇게 되었을 때는 상속을 통해서 얻는 것보다 잃는 것이 많아지는데 이는 불필요한 메소드와 구조상 가독성이 떨어지기 때문이다. 불필요한 상속을 피하고 LSP 따를 수 있는 방법으로는 아래와 같은 방법이 있을 수 있다고 설명한다.

1.    상속 보다는 연관을 사용한다
이것은 3DBoard에 Board 인스턴스를 가지고 있으며 Board를 사용하되 zpos 값을 통해서 3DBoard를 표현하는 방법이다.

2.    구성(Composition)을 사용하여 다른 클래스들의 행동을 조합한다.
이 방법은 하나의 인터페이스를 따르는 클래스들의 묶을 배열 형태로 저장하여 필요한 클래스를 동적으로 적용할 수 있는 방법이다. 구성의 경우 구성을 가지고 있는 클래스가 소멸했을 때는 그 클래스에 포함된 구성 요소들도 함께 소멸한다.

3.    집합(Aggregation)을 상용하여 구성에서 구성 요소들이 사라지지 않게 한다.
이 방법은 구성과 비슷하지만 구성 요소들이 외부에서도 사용할 수 있도록 하여 집합을 가지고 있는 인스턴스가 소멸했을 때에도 집합 요소들은 사라지지 않도록 하는 방법이다.

책에서는 LSP위한 방법으로 아래와 같이 정리하고 있다.

1.    위임(Delegation)
클래스의 행동을 변경하고 싶지 않고 그 행동을 스스로 구현하는 것이 그 클래스의 책임이 아닌 경우에는 그 행동을 다른 클래스에 위임(Delegation)한다.

2.    구성(Composition)
구성을 사용하여 하나 또는 여러 개의 클래스, 특히 비슷한 종류의 여러 클래스들로부터 행동을 재사용할 수 있다. 여러분의 객체가 다른 객체를 완전히 소유하고 있는 형태이며, 구성 관계로 연결된 객체는 여러분 객체의 외부에 독립적으로 존재할 수 없다.

3.    집합(Aggregation)
구성 관계의 이점을 바라지만 여러분 객체의 외부에서도 연결된 객체의 행동이 사용되는 경우, 집합을 사용한다.

덧붙여 책에서는 위임과 구성 그리고 집합을 상속보다 선호하면, 대개의 경우 소프트웨어는 더 유연하고, 유지보수성, 확장성, 그리고 재사용성이 좀더 좋아진다고 이야기 하고 있다. 꼭 그런 것은 아니겠지만 LSP를 따르지 않는 상속은 구조를 복잡하게 하고 재사용성이 떨어질 수 있기 때문이라고 할 수 있다. LSP를 따르는 상속의 경우에는 상속을 사용하는 것이 바람직할 것으로 생각된다.


    

설정

트랙백

댓글

플래시 게임 Budapest defenders

Programming/Etc 2007. 8. 26. 19:15
게임 장르를 잘모르지만 이런 장르를 전략시물레이션 게임이라고 하지 않나 싶다. 간단한 스토리지만 플래시 전략 보드게임으로서는 매력적이다.













http://alt.tnt.tv/tntoriginals/thecompany/budapestdefenders/index.htm

사용자 삽입 이미지





    

설정

트랙백

댓글

[AS3] Head First - OOAD AS3로 변환 두번째

Programming/ActionScript 3.0 2007. 8. 26. 12:11
이전에 포스팅한 기타 검색 프로그램의 두 번째로 객체지향 원리를 살린 cohesive버전을 AS3로 변환하였다. 변환 과정에서 Java에서 제공하고 있는 HashMap의 경우는 AS3로 기본적인 기능만을 하는 HashMap class를 만들었다.

메인 document class에서 inventory. addInstrument() 메소드를 실행할 때, 마지막 파라미터 값으로 new InstrumentSpec(HashMap)을 전달하게 되는데, 이때 HashMap 클래스의 경우 Array을 이용하여 데이터를 테이블 형태로 저장하고 있기 때문에 put과 remove과정에서 하나의 Array를 참조하므로 마지막 데이터만을 저장하게 되어 검색이 제대로 되지 않는다.

이는 addInstrument 메소드를 실행하는 시점에서 참조 형태로 HashMap 데이터 Array를 전달하기 때문에 발생하는 문제다. 이를 해결하기 위해 HashMap에 있는 Array를 복제(clone)할 필요가 생겼다. 그래서 HashMap 클래스 내에 clone() 메소드를 만들어 사용되고 있는 Array를 byteArray로 변환하여 복제하였다.

이 프로그램을 변환하면서 다시 한번 느끼는 건 단순해 보이지만 단순하지 않은, 간결하고 함축적인, 사용자가 사용하기 편리한 것과 개발자의 정신분열 증상은 항상 반비례한다는 것이다. 
Java의 버전업을 통해 지원하고 있는 클래스들을 분석하는 것은 충분히 가치가 있는 일이다. 이미 입증된 알고리즘으로 제작되었기 때문에 AS3로 변환하면 충분히 사용성에서 좀더 나을 방법을 찾을 수 있을 듯싶다.

사용자 삽입 이미지

// FindInstrumentTester.as
package{
import flash.display.Sprite;
public class FindInstrumentTester extends Sprite{

public function FindInstrumentTester():void{
var inventory:Inventory = new Inventory();
initializeInventory(inventory);

var properties:HashMap = new HashMap();
properties.put("builder", Builder.GIBSON);
properties.put("backWood", Wood.MAPLE);
var whatBryanLikes:InstrumentSpec = new InstrumentSpec(properties);

var matchingInstruments:Array = inventory.search(whatBryanLikes);
if(matchingInstruments.length != 0){
trace("Bryan, you might like these instruments:");
var ilen:int = matchingInstruments.length;
for(var i:int=0;i<ilen;i++){
var instrument:Instrument = matchingInstruments[i];
var spec:InstrumentSpec = instrument.getSpec();

trace("We have a " + spec.getProperty("instrumentType") +
" with the following properties:");

var hashMap:HashMap = spec.getProperties();
var jlen:int = hashMap.length;
for(var j:int=0;j<jlen;j++){
var propertyName:String =
String(hashMap.getHashTable()[j].key);
trace(" " + propertyName + ": "
+spec.getProperty(propertyName));
}
trace(" You can have this " +
spec.getProperty("instrumentType") +
" for $" + instrument.getPrice() + "\n---");
}
}else{
trace("Sorry, Bryan, we have nothing for you.");
}
}
private function initializeInventory(inventory:Inventory):void {
var properties:HashMap = new HashMap();
properties.put("instrumentType", InstrumentType.GUITAR);
properties.put("builder", Builder.COLLINGS);
properties.put("model", "CJ");
properties.put("type", Type.ACOUSTIC);
properties.put("numStrings", 6);
properties.put("topWood", Wood.INDIAN_ROSEWOOD);
properties.put("backWood", Wood.SITKA);
inventory.addInstrument("11277", 3999.95,
new InstrumentSpec(properties.clone()));

properties.put("builder", Builder.MARTIN);
properties.put("model", "D-18");
properties.put("topWood", Wood.MAHOGANY);
properties.put("backWood", Wood.ADIRONDACK);
inventory.addInstrument("122784", 5495.95,
new InstrumentSpec(properties.clone()));

properties.put("builder", Builder.FENDER);
properties.put("model", "Stratocastor");
properties.put("type", Type.ELECTRIC);
properties.put("topWood", Wood.ALDER);
properties.put("backWood", Wood.ALDER);
inventory.addInstrument("V95693", 1499.95,
new InstrumentSpec(properties.clone()));
inventory.addInstrument("V9512", 1549.95,
new InstrumentSpec(properties.clone()));

properties.put("builder", Builder.GIBSON);
properties.put("model", "Les Paul");
properties.put("topWood", Wood.MAPLE);
properties.put("backWood", Wood.MAPLE);
inventory.addInstrument("70108276", 2295.95,
new InstrumentSpec(properties.clone()));

properties.put("model", "SG '61 Reissue");
properties.put("topWood", Wood.MAHOGANY);
properties.put("backWood", Wood.MAHOGANY);
inventory.addInstrument("82765501", 1890.95,
new InstrumentSpec(properties.clone()));

properties.put("instrumentType", InstrumentType.MANDOLIN);
properties.put("type", Type.ACOUSTIC);
properties.put("model", "F-5G");
properties.put("backWood", Wood.MAPLE);
properties.put("topWood", Wood.MAPLE);
properties.remove("numStrings");
inventory.addInstrument("9019920", 5495.99,
new InstrumentSpec(properties.clone()));

properties.put("instrumentType", InstrumentType.BANJO);
properties.put("model", "RB-3 Wreath");
properties.remove("topWood");
properties.put("numStrings", 5);
inventory.addInstrument("8900231", 2945.95,
new InstrumentSpec(properties.clone()));
}
}
}
//  Inventory.as
package{
        public class Inventory{
                private var inventory:Array;
                public function Inventory():void{
                        inventory = new Array();
                }
                public function addInstrument(serialNumber:String,
                price:Number,
                spec:InstrumentSpec):void{
                        var instrument:Instrument = new Instrument(serialNumber,
                        price,
                        spec);
                        inventory.push(instrument);
                }
                public function get(serialNumber:String):Instrument{
                        var len:int = inventory.length;
                        for(var i:int=0;i<len;i++){
                                if(inventory[i].getSerialNumber() == serialNumber){
                                        return inventory[i];
                                }
                        }
                        return null;
                }
                public function search(searchSpec:InstrumentSpec):Array{
                        var matchingInstruments = new Array();
                        var len:int = inventory.length;
                        for(var i:int=0;i<len;i++){
                                if(inventory[i].getSpec().matches(searchSpec)){
                                        matchingInstruments.push(inventory[i]);
                                }
                        }
                        return matchingInstruments;
                }
        }

}
// Instrument.as
package{
        public class Instrument{
                private var serialNumber:String;
                private var price:Number;
                private var spec:InstrumentSpec;
                public function Instrument(serialNumber:String,
                price:Number,
                spec:InstrumentSpec) {
                        this.serialNumber = serialNumber;
                        this.price = price;
                        this.spec = spec;
                }
                public function getSerialNumber():String {
                        return serialNumber;
                }
                public function getPrice():Number{
                        return price;
                }
                public function setPrice(newPrice:Number):void{
                        price = newPrice;
                }
                public function getSpec():InstrumentSpec{
                        return spec;
                }
        }
}
// InstrumentSpec.as
package{
        public class InstrumentSpec{
                private var properties:HashMap;
                public function InstrumentSpec(properties:HashMap=null):void{
                        if (properties == null) {
                                this.properties = new HashMap();
                        } else {
                                this.properties = new HashMap(properties);
                        }
                }
                public function getProperty(propertyName:String):*{
                        return properties.getValue(propertyName);
                }
                public function getProperties():HashMap{
                        return properties;
                }
                public function matches(otherSpec:InstrumentSpec):Boolean{
                        var otherHashMap:HashMap = otherSpec.getProperties();
                        var len:int = otherHashMap.length;

                        var hashTable:Array = otherHashMap.getHashTable();

                        for (var i:int=0; i<len; i++ ) {
                                var propertyName:String = String(hashTable[i].key);

                                if (String(properties.getValue(propertyName)) !=
                                String(otherHashMap.getValue(propertyName))) {
                                        return false;
                                }
                        }
                        return true;
                }
        }
}
// HashMap.as
package{
        import flash.utils.ByteArray;
        public class HashMap{
                private var hashTable:Array;
                public function HashMap(hm:HashMap=null):void{
                        if (hm == null) {
                                hashTable = new Array();
                        } else {
                                hashTable = hm.getHashTable();
                        }
                }
                public function put(k:*, v:*):*{
                        var len:int = length;
                        for(var i:int=0;i<len;i++){
                                if(hashTable[i].key == k){
                                        hashTable[i].key = k;
                                        hashTable[i].value = v;
                                        return v;
                                }
                        }
                        hashTable.push({key:k, value:v});
                        return v;
                }
                public function remove(k:*):Boolean{
                        var len:int = length;
                        for(var i:int=0;i<len;i++){
                                if(hashTable[i].key == k){
                                        hashTable.splice(i,1);
                                        return true;
                                }
                        }
                        return false;
                }
                public function getValue(k:*):*{
                        var len:int = length;
                        for(var i:int=0;i<len;i++){
                                if(hashTable[i].key == k){
                                        return hashTable[i].value;
                                }
                        }
                        return null;
                }
                // this function for test
                public function allTrace():void{
                        var len:int = length;
                        for(var i:int=0;i<len;i++){
                                trace(String(hashTable[i].key)+" : "+String(hashTable[i].value));
                        }
                }
                public function getHashTable():Array{
                        return hashTable;
                }
                public function setHashTable(hashTable:Array):void{
                        this.hashTable = hashTable;
                }
                public function get length():int{
                        return hashTable.length;
                }
                public function clone():HashMap{
                        var hm:HashMap = new HashMap();
                        var hashTableBA:ByteArray = new ByteArray();
                        hashTableBA.writeObject(hashTable);
                        hashTableBA.position = 0;
                        var ary:Array = hashTableBA.readObject() as Array;
                        hm.setHashTable(ary);
                        return hm;
                }
        }
}
// Builder.as
package{
        public class Builder{
                public static const FENDER = "Fender";
                public static const MARTIN = "Martin";
                public static const GIBSON = "Gibson";
                public static const COLLINGS = "Collings";
                public static const OLSON = "Olson";
                public static const RYAN = "Ryan";
                public static const PRS = "PRS";
        }
}

// InstrumentType.as

package{
        public class InstrumentType{
                public static const GUITAR = "Guitar";
                public static const BANJO = "Banjo";
                public static const DOBRO = "Dobro";
                public static const FIDDLE = "Fiddle";
                public static const BASS = "Bass";
                public static const MANDOLIN = "Mandolin";
        }
}

// Type.as

package{
        public class Type{
                public static const ACOUSTIC = "acoustic";
                public static const ELECTRIC = "Belectric";
        }
}

// Wood.as

package{
        public class Wood{
                public static const INDIAN_ROSEWOOD = "Indian Rosewood";
                public static const BRAZILIAN_ROSEWOOD = "Brazilian Rosewood";
                public static const MAHOGANY = "Mahogany";
                public static const MAPLE = "Maple";
                public static const COCOBOLO = "Cocobolo";
                public static const CEDAR = "Cedar";
                public static const ADIRONDACK = "Adirondack";
                public static const ALDER = "Alder";
                public static const SITKA = "Sitka";
        }
}



 

    

설정

트랙백

댓글

[FlashCS3] AIR for FlashCS3 업데이트

Programming/ActionScript 3.0 2007. 8. 25. 23:44
AIR이 공식적으로 FlashCS3에 서포트 되었다. 예전 포스트 grant skinner의 Flash CS3용 Adobe AIR extension 에서 개인이 만든 AIR extension 소개한 적이 있는데 adobe에서 공식적으로 AIR을 FlashCS3에서 생성할 수 있도록 지원하는 update 파일을 배포했다. 버전은 현재 AIR 1.0이다.  업데이트 파일은 아래 링크에서 다운로드 할 수 있다.

http://labs.adobe.com/wiki/index.php/AIR:Flash_CS3_Professional_Update

air파일 정보 세팅과 air파일 생성은 Commands 메뉴에 추가된 AIR-Application and Package Settings 와 AIR-Pacakge AIR File 명령을 통해서 세팅 및 생성할 수 있다.





사용자 삽입 이미지






    

설정

트랙백

댓글

[AS3] Head First - OOAD AS3로 변환

Programming/ActionScript 3.0 2007. 8. 24. 18:43
Head First 시리즈 중에 설계를 위한 객체지향 방법론에 관한 내용을 담고 있는 『Head First Object-Oriented Analysis&Design』 이라는 책이 있다. 책에서 사용한 언어는 Java언어인데 내용 중 기타 판매점의 기타검색 프로그램을 AS3로 수정하였다. 비교적 간단한 구조를 가지고 있지만 막상 하나의 문제를 해결하기 위해 필요한 기능을 구현하고 재사용성이 용이한 구조로 설계하려고 하면 어려움이 많이 발생하는 것 같다. AS3로 변환 과정에서 Java의 enum(열거형 타입 Builder, Type, Wood)는 각 클래스의 static const속성으로 변환하였다.

이 구조는 책의 2장까지 나와 있는 내용 중에서 final 버전이다. 책에서는 이 프로그램을 객체지향의 원리

(변하는 것을 캡슐화하라,
구현에 의존하기 보다는 인터페이스에 의존하도록 코딩하라,
각 클래스는 변경 요인이 오직 하나이어야 한다.)


에 입각해서 다시 5장에서 리펙토링하게 되는데 그 버전은 다음에 바꿔볼 생각이다. 어떤 문제를 풀 때 역행으로 분석해 보는 것도 상당히 도움이 된다.



사용자 삽입 이미지

// FindGuitarTester .as
package{
import flash.display.Sprite;
public class FindGuitarTester extends Sprite{
private var inventory:Inventory;
private var whatErinLikes:GuitarSpec;
private var matchingGuitars:Array;
public function FindGuitarTester():void{
inventory = new Inventory();
initialize();
}
private function initialize():void{
initializeInventory(inventory);
whatErinLikes = new GuitarSpec(Builder.FENDER, "Stratocastor", Type.ELECTRIC, 6, Wood.ALDER, Wood.ALDER);
matchingGuitars = inventory.search(whatErinLikes);
if (matchingGuitars != null) {
trace("Erin, you might like these guitars:");
var len:int = matchingGuitars.length;
for (var i:int=0;i<len;i++) {
var guitar:Guitar = matchingGuitars[i];
var spec:GuitarSpec = guitar.getSpec();
trace(" We have a " +
spec.getBuilder() + " " + spec.getModel() + " " +
spec.getType() + " guitar:\n " +
spec.getBackWood() + " back and sides,\n " +
spec.getTopWood() + " top.\n You can have it for only $" +
guitar.getPrice() + "!\n ----");
}
} else {
trace("Sorry, Erin, we have nothing for you.");
}
}
private function initializeInventory(inventory:Inventory):void {
inventory.addGuitar("11277", 3999.95, new GuitarSpec(Builder.COLLINGS, "CJ", Type.ACOUSTIC, 6, Wood.INDIAN_ROSEWOOD, Wood.SITKA));
inventory.addGuitar("V95693", 1499.95, new GuitarSpec(Builder.FENDER, "Stratocastor", Type.ELECTRIC, 6, Wood.ALDER, Wood.ALDER));
inventory.addGuitar("V9512", 1549.95, new GuitarSpec(Builder.FENDER, "Stratocastor", Type.ELECTRIC, 6, Wood.ALDER, Wood.ALDER));
inventory.addGuitar("122784", 5495.95, new GuitarSpec(Builder.MARTIN, "D-18", Type.ACOUSTIC, 6, Wood.MAHOGANY, Wood.ADIRONDACK));
inventory.addGuitar("76531", 6295.95, new GuitarSpec(Builder.MARTIN, "OM-28", Type.ACOUSTIC, 6, Wood.BRAZILIAN_ROSEWOOD, Wood.ADIRONDACK));
inventory.addGuitar("70108276", 2295.95, new GuitarSpec(Builder.GIBSON, "Les Paul", Type.ELECTRIC, 6, Wood.MAHOGANY, Wood.MAHOGANY));
inventory.addGuitar("82765501", 1890.95, new GuitarSpec(Builder.GIBSON, "SG '61 Reissue", Type.ELECTRIC, 6, Wood.MAHOGANY, Wood.MAHOGANY));
inventory.addGuitar("77023", 6275.95, new GuitarSpec(Builder.MARTIN, "D-28", Type.ACOUSTIC, 6, Wood.BRAZILIAN_ROSEWOOD, Wood.ADIRONDACK));
inventory.addGuitar("1092", 12995.95, new GuitarSpec(Builder.OLSON, "SJ", Type.ACOUSTIC, 12, Wood.INDIAN_ROSEWOOD, Wood.CEDAR));
inventory.addGuitar("566-62", 8999.95, new GuitarSpec(Builder.RYAN, "Cathedral", Type.ACOUSTIC, 12, Wood.COCOBOLO, Wood.CEDAR));
inventory.addGuitar("6 29584", 2100.95, new GuitarSpec(Builder.PRS, "Dave Navarro Signature", Type.ELECTRIC, 6, Wood.MAHOGANY, Wood.MAPLE));
}
}
}

// Inventory.as

package{
        public class Inventory{
                private var guitars:Array;
                public function Inventory():void{
                        guitars = new Array();
                }
                public function addGuitar(serialNumber:String, price:Number, spec:GuitarSpec):void{
                        var guitar:Guitar = new Guitar(serialNumber, price, spec);
                        guitars.push(guitar);
                }
                public function getGuitar(serialNumber:String):Guitar{
                        var len:int = guitars.length;
                        for(var i:int=0;i<len;i++){
                                if(guitars[i].getSerialNumber() == serialNumber){
                                        return guitars[i];
                                }
                        }
                        return null;
                }
                public function search(searchSpec:GuitarSpec):Array{
                        var matchingGuitars = new Array();
                        var len:int = guitars.length;
                        for(var i:int=0;i<len;i++){
                                if(guitars[i].getSpec().matches(searchSpec)){
                                        matchingGuitars.push(guitars[i]);
                                }
                        }
                        return matchingGuitars;
                }
        }

}


// GuitarSpec.as

package {
        public class GuitarSpec {
                private var builder:String;
                private var model:String;
                private var type:String;
                private var numStrings:int;
                private var backWood:String;
                private var topWood:String;

                public function GuitarSpec(builder:String, model:String, type:String, numStrings:int, backWood:String, topWood:String) {
                        this.builder = builder;
                        this.model = model;
                        this.type = type;
                        this.numStrings = numStrings;
                        this.backWood = backWood;
                        this.topWood = topWood;
                }

                public function getBuilder():String {
                        return builder;
                }

                public function getModel():String {
                        return model;
                }

                public function getType():String {
                        return type;
                }

                public function getNumStrings():int {
                        return numStrings;
                }

                public function getBackWood():String {
                        return backWood;
                }

                public function getTopWood():String {
                        return topWood;
                }

                public function matches(otherSpec:GuitarSpec):Boolean {
                        if (builder != otherSpec.getBuilder()) {
                                return false;
                        }
                        if ((model != null) && (model !="") && (model.toLowerCase() != (otherSpec.getModel().toLowerCase()))) {
                                return false;
                        }
                        if (type != otherSpec.getType()) {
                                return false;
                        }
                        if (numStrings != otherSpec.getNumStrings()) {
                                return false;
                        }
                        if (backWood != otherSpec.getBackWood()) {
                                return false;
                        }
                        if (topWood != otherSpec.getTopWood()) {
                                return false;
                        }
                        return true;
                }
        }
}

// Guitar.as
package{
        public class Guitar{
                private var serialNumber:String;
                private var price:Number;
                private var spec:GuitarSpec;
                public function Guitar(serialNumber:String, price:Number, spec:GuitarSpec):void{
                        this.serialNumber = serialNumber;
                        this.price = price;
                        this.spec = spec;
                }
                public function getSerialNumber():String{
                        return serialNumber;
                }
                public function getPrice():Number{
                        return price;
                }
                public function setPrice(newPrice:Number):void{
                        price = newPrice;
                }
                public function getSpec():GuitarSpec{
                        return spec;
                }
        }
}

// Builder.as
package{
        public class Builder{
                public static const FENDER = "Fender";
                public static const MARTIN = "Martin";
                public static const GIBSON = "Gibson";
                public static const COLLINGS = "Collings";
                public static const OLSON = "Olson";
                public static const RYAN = "Ryan";
                public static const PRS = "PRS";
        }
}
// Wood.as
package{
        public class Wood{
                public static const INDIAN_ROSEWOOD = "Indian Rosewood";
                public static const BRAZILIAN_ROSEWOOD = "Brazilian Rosewood";
                public static const MAHOGANY = "Mahogany";
                public static const MAPLE = "Maple";
                public static const COCOBOLO = "Cocobolo";
                public static const CEDAR = "Cedar";
                public static const ADIRONDACK = "Adirondack";
                public static const ALDER = "Alder";
                public static const SITKA = "Sitka";
        }
}
// Type.as
package{
        public class Type{
                public static const ACOUSTIC = "acoustic";
                public static const ELECTRIC = "Belectric";
        }
}
    

설정

트랙백

댓글

[FlashCS3] AlignAssist Extension

Programming/ActionScript 3.0 2007. 8. 23. 09:17
AlignAssist Extension은 스테이지에 있는 복수의 심볼을 원형으로 배치해 주는 Extension이다. 일본 사람이 만든 것으로 익스텐션을 설치하면 익스텐션 설명이 일본어로 되어 있는데 내용은 다음과 같다.

 ■AlignAssist에 대해
 선택한 심볼을 지정한 수치 간격으로 정렬합니다.
 본확장 기능은 Macromedia Flash MX 2004에서 사용할 수 있습니다.  다만 환상 정렬 기능에 대해서는 Flash CS3에서만 되니 주의해 주십시오.

 ■사용 방법
 도구모음의[Window]의[Other Panels]의 [AlignAssist]를 선택하세요.




왼쪽과 같이 불특정 좌표로 배치되어 있는 심볼들을 선택하고 익스텐션을 실행하면 오른쪽과 같이 정렬이 가능하다.
 



    

설정

트랙백

댓글

[AS3] APE : CollisionCircleParticle

Programming/Physics Engine 2007. 8. 18. 06:37
APE의 RectangleParticle 클래스를 이용해서 파티클을 생성하고 등록할 경우에는 자체 rotation (angle) 표현을 하지 못한다. 이는 CircleParticle의 경우도 같다. 하지만 CircleParticle을 상속하고 있는 WheelParticle의 경우에는 자체 angle값을 가지고 있어서 마찰과 각도에 따라서 자체 회전을 하게 된다. 아래 실험한 오브젝트의 경우도 WheelParticle을 통해서 생성한 오브젝트들이기 때문에 회전이 가능하다.

화면에서 마우스를 down한 상태에서 시간의 흐름에 따라 원형의 크기가 커지는데 Max 값은 반지름 80으로 한정하였다. 생성한 오브젝트를 MouseDown and Drag하면 힘의 방향을 바꿀 수 있으며 MouseUp을 하게 되면 마우스 포인트와의 SpringConstraint를 removeConstraint를 실행하여 연결을 끊어 주었다. 이러한 형태는 앙드레 미쉘이 만들고 있는 Revive의 엔진 자체에 있는 마우스 이벤트와 비슷하지만 구현 방법은 아마 다를 듯싶다.

오브젝트가 없는 화면에서 위에서 생성한 방법으로 생성할 수 있는 오브젝트의 개수는 10개로 한정하였다. 생성한 모든 오브젝트들은 서로 collision이 가능하다.(외벽 또한 그러함) 8개로 오브젝트를 한정한 이유는 오브젝트가 많으면 지누 컴이 뻑난다.(쿠쿠)

이러한 형태를 어디에 쓸 수 있을 지는 생각해 봐야겠다. 원래 이런 형태로 테스트 하려고 했던 것은 아니었는데 하다 보니…

원래 하려고 했던 것은 위에서 언급했던 것과 같이 RectangleParticle의 경우 자체 angle이 지원되지 않아서 중력에 의해서 외부의 충격을 받았을 때 WheelParticle처럼 회전을 하지 못한다는 것을 고민하다가 APE 제작자 alec cove와 APE를 사용하는 kiosk(키오스크 작업을 하는지 이름이 그러한지 모르겠다.) 와의 대화를 보고 알게 되었다. 방법은 아래와 같다.

4면에 대한 충돌과 각도에 따라 회전이 가능한 사각형의 오브젝트를 만들 때는 4점이 되는 모서리 부분에 CircleParticle을 생성하고 그 모서리의 외각선을 따라 각각 4개의 SpringConstraint로 연결해 준다. 이렇게만 하면 4점이 모두 연결 되었으나 외각선에 따라 연결되었기 때문에 충격을 받으면 쓰러지듯이 퍼져버린다. 이를 방지하기 위해서 모서리 4개 중에서 대각선으로 마주보는 CircleParticle들을 연결하는 2개의 SpringConstraint로 연결하되, 그 연결은 non-collidable springs 이어야 한다는 점이다.

위와 같은 방법으로 Group에 추가하면 사각형이 생기고 그 사각형 안에 X자 모양의 두 개의 선이 대각선으로 연결된 모양이 된다. 이렇게 만들어지면 외부의 충격이나 중력에 의해서 다른 그룹에 있는 particle들과의 충돌에 의해서 각도와 좌표가 바뀌는 CustomRectangleParticle을 생성할 수 있다. 이런 방법으로 사각형뿐만 아니라 다각형들도 연결을 통해서 생성할 수가 있다.

그러데 하다 보니 문제가 좀 있다. 4개의 모서리에 있는 CircleParticle의 크기를 작게 할 경우 SpringConstraint도 함께 무게가 실리지 않아서 다른 오브젝트와 충돌했을 때 그 안쪽으로 collision된 오브젝트들이 뚫고 들어온다는 점이다. 엔진의 계산 값이 벗어 났을 때는 오류가 발생한다. 이는 좀더 고민해 봐야겠다.



    

설정

트랙백

댓글

[AS3] TextField 길이를 벗어나는 글자는 ...으로

Programming/ActionScript 3.0 2007. 8. 16. 03:13
이 클래스는 인수로 지정한 TextField의 width길이보다 text 내용이 길어질 경우에 뒤에 임의로 "..."을 append 하는 클래스다. static 메소드 형태로 만들려다가 동적으로 TextField의 width 값을 변경할 경우에도 편하게 사용할 수 있도록 만들었다. 실제적으로 동적으로 사이즈를 변경해야 하는 경우가 많지는 않겠지만 브라우저의 사이즈에 따라 유동적으로 TextField의 길이를 변경해야 할 경우에는 유용하게 사용할 수 있을 듯싶다.

아래는 위 클래스를 이용하여 동적으로 TextField의 사이즈를 변경하는 예이다.









아래는 클래스 소스 원본
package jasu.display{
        import flash.text.TextField;
        public class CutTextField {
                private var _tf:TextField;
                private var _t:String;
                public function CutTextField(tf:TextField):void {
                        _tf = tf;
                        _t = tf.text;
                        cutText();
                }
                public function cutText():void {
                        if (_tf.maxScrollH > 0) {
                                _tf.appendText("...");
                                while (_tf.maxScrollH > 0) {
                                        var str:Array=_tf.text.split("");
                                        var len:int = str.length;
                                        str.splice(len - 4,1);
                                        _tf.text=str.join("");
                                }
                        }
                }
                public function setTextField(tf:TextField):void {
                        _tf = tf;
                        _t = tf.text;
                }
                public function getTextField():TextField {
                        return _tf;
                }
                public function set width(w:int):void {
                        _tf.width = w;
                        _tf.text = _t;
                }
                public function get width():int {
                        return _tf.width;
                }
                public function set text(t:String):void {
                        _tf.text = _t = t;

                }
                public function get text():String {
                        return _t;
                }
        }
}
    

설정

트랙백

댓글

[AS3] 지원하는 신기능 Best 10

Programming/ActionScript 3.0 2007. 8. 14. 13:03
1. 복수의 변수의 출력이 편하게:trace
인수에 변수를 복수 건네주면 자동적으로 공백으로 구분하여 출력한다. 3.0 이전에도 굳이 필요하다면 trace([a,b])와 같은 방법으로 사용하였었는데 뭐 자체 지원이니…
var a:int = 0;
var b:String = 'hgoe';
trace(a, b); // Output: 0 hoge




2. 요소의 순회가 간단히:for each
이전에는 배열의 요소를 순회하는 것은 다음과 같이 하는 것이 정석이었다.
var len:Number = list.length;
for (var i:Number = 0; i < len; ++i) {
trace(list[i]);
}

하지만 3.0에서는 다음과 같이 사용하면 이런 귀찮은 일이 많이 줄어든다. list 안의 요소가 하나씩 element에 대입되어 loop된다.
for each (var element:Object in list) {
trace(element);
}

3. 솔직하게 쓸 수 있게 되었다:default 인수
다음과 같이 인수를 생략 했을 경우의 default값을 간단하게 지정할 수 있다.
function f(arg1:int, arg2:int = 2):void
{
trace(arg1, arg2);
}
f(3, 4); // Output: 3 4
f(3); // Output: 3 2

4. arguments는 필요없다:가변 인수
trace와 같이 얼마든지 인수를 잡는 함수에서는 다음과 같이 쓸 수 있다. 지정한 인수(여기에서는rest )에 나머지의 인수를 배열로서 받을 수가 있다.
function output(separator:String, ...rest:Array):void
{
trace(rest.join(separator));
}
output(':', 1, 2, 3, 4); // Output: 1:2:3:4
output('.', 'a', 'b', 'c'); // Output: a.b.c

5. 모든 형태를 나타낸다:*
어떤 형태가 대입되는지 모르지만 형태를 지정하고 싶을 경우 기존에는 Object가 이용되고 있었지만 AS3에서는 보다 그 의미를 명확하게 하기 위해서, 「* 」(asterisk)라고 하는 형태 지정을 할 수 있다.
var element:* = list[0];

6. 변경되지 않는 상수:const
한 번 값을 설정하면 실행 중에는 변경할 수 없는 이른바 「상수」를 정의할 수 있다.
public class SelectedEvent extends Event
{
public static const SELECTED:String = 'selected';
}

7. 복수의 루프를 단번에 빠진다:label
다중 루프를 단번에 break 하거나 continue 하거나 할 수가 있게 되었다. switch case 에서 친숙한 라벨을 쓰고 break나 continue를 할 때에 그것을 지정하면 그 라벨에 대해서 동작을 한다.
loop: for (var i:int = 0; i < 2; i++) {
for (var j:int = 0; j < 3; j++) {
if ((i == 1) && (j == 0)) {
break loop;
}
trace(i, j);
}
}
/* Output:
0 0
0 1
0 2
*/
설명:「loop 」라는 라벨을 지정하고 그 안에 상자가 된 루프가 있다. 루프는 최초로 i=0 으로부터 1씩 증가하며 한번 증가할 때마다 j루프를 돌게 되는데 if문을 만나는 순간, break에 의해서loop 라벨로부터 탈출(?!)하여 두 루프를 빠져 버리므로 Output과 같은 결과가 된다. 보통 이러한 형태는 인터프리터언어(순차적 언어)에서 주로 사용되었던 방식이었는데 그 복잡한 흐름 때문에 순차적 언어에서도 꺼려하는 방법이었다. 하지만 간단한 내부 알고리즘에서 활용하면 편할 것 같기는 하다.


8. 프롭퍼티의 존재를 간단하게 확인:in

「a in b 」와 같이 쓰면 b의 안에 프롭퍼티 a가 들어가 있으면 true, 그렇지 않으면 false 반환한다.이것에 이용하면 프롭퍼티 존재를 간단하게 조사할 수가 있다.
var obj:Object = {abc: 1, def: 2};
trace('abc' in obj); // Output: true
trace('ghi' in obj); // Output: false

9. 오브젝트가 특정의 형태인가 간단하게 확인:is
「a is b 」와 같이 쓰면 a가 b와 형태가 같거나 b의 서브 클래스의 인스턴스이면 true, 그렇지 않으면 false를 반환한다. 이것에 이용하면 어느 형태인가를 간단하게 조사할 수가 있다.
var s:Sprite = new Sprite();
trace(s is Sprite); // Output: true
trace(s is DisplayObject); // Output: true
trace(s is String); // Output: false

*instanceof 는 직접적인 인스턴스가 아니면 true를 반환하지 않기 때문에 주의해야 한다.
var s:Sprite = new Sprite();
trace(s instanceof Sprite); // Output: true
trace(s instanceof DisplayObject); // Output: false

10. 예외 없이 캐스트:as

보통 다음과 같이 캐스트 하려고 했을 때 형태가 맞지 않으면 예외가 슬로우 된다.
var s:Sprite = Sprite(new Array());
그러나 as를 사용해 캐스트 하면 형태가 맞지 않는 경우, 예외는 슬로우 되지 않고 그 형태의 디폴트값을 돌려준다.
var s:Sprite = new Array() as Sprite;
trace(s); // Output: null ← 형태 지정으로 null을 반환한다.
var n:Number = new Array() as Number;
trace(n); // Output: 0 ← Number의 디폴트값은 0

    

설정

트랙백

댓글

APE UML 다이어그램

Programming/Physics Engine 2007. 8. 12. 19:11
APE UML 자료를 찾아보려고 하였으나 공개된 UML자료가 없어서 클래스를 토대로 임의로 만들어 놓았다. 위 이미지는 APE의 클래스 명으로 표기한 기본적인 class diagram이다. 아직 APE의 클래스 관계가 제대로 파악되지 않아서 generalization과 realization  관계만을 표시해 놓았다. 위 이미지는  클래스명으로만 간략하게 표시해 놓았지만 아래 파일은  각 클래스에 있는 attribute와 operation도 포함해 놓았으니 ape를 분석하시는 분에게는 도움이 될 듯 싶다. exe파일로 퍼블리시 해 놓았는데 예전에 AS3 API 계층 구조도를 만들 때 사용했던 소스를 그대로 사용하였다.


사용자 삽입 이미지






    

설정

트랙백

댓글

Flash Player 9 실시간 인스톨 상황

Programming/ActionScript 3.0 2007. 8. 10. 10:52




[Flash] http://www.onflex.org/FP9Counter/FP9Counter.swf



현재 Flash Player 9버전 install 상황, 이 숫자의 개념이 머리 속에 들어오지 않는다.





    

설정

트랙백

댓글

2D 물리엔진을 이용한 벽돌깨기 게임

Programming/Physics Engine 2007. 8. 9. 18:29
물리엔진을 이용한 벽돌깨기 게임이 있어 소개한다. 쉐어웨어로 일정한 스테이지 까지는 게임이 가능하니 받아서 한번 해보길 바란다. 기본적인 벽돌깨기 게임과 비슷하지만 오브젝트들의 움직임이 예술이다.




사용자 삽입 이미지

    

설정

트랙백

댓글