UDN
Search public documentation:

ScaleformTechnicalGuideKR
English Translation
日本語訳
中国翻译

Interested in the Unreal Engine?
Visit the Unreal Technology site.

Looking for jobs and company info?
Check out the Epic games site.

Questions about support via UDN?
Contact the UDN Staff

UE3 홈 > 유저 인터페이스와 HUD > 스케일폼 GFx > 스케일폼 테크니컬 가이드

스케일폼 테크니컬 가이드


문서 변경내역: Jeff Wilson 작성. 홍성진 번역.

개요


언리얼 엔진 3의 스케일폼 GFx 통합으로 어도비 플래시 프로페셔널에서 만든 인터페이스와 메뉴를 HUD(Heads-Up Display, 머리위 표시)나 메뉴에서 사용할 수 있게 되었습니다. 여기서는 스케일폼 GFx 시스템을 사용하는 프로그래머용 테크니컬 가이드입니다. HUD나 메뉴를 구현할 때 포함되는 주요 클래스를 다룰 것이며, 프로세스에서의 그 위치와 사용법도 설명해 드리도록 하겠습니다.

배제 조항:

Scaleform 및 GFx는 Scaleform Corporation 의 등록 상표입니다. Scaleform GFx ⓒ 2010 Scaleform Corporation. 모든 저작권은 Scaleform Corporation 에게 있습니다.

Adobe 및 Flash는 미국 및/또는 다른 나라의 Adobe Systems Incorporated에 등록된 상표 혹은 상호입니다.

GFxUI 컴포넌트 참고서


GFxUI 컴포넌트는 스케일폼 GFx 인터페이스 표시용 기본 시스템을 구성합니다. 전체 프로세스는 새로운 HUD 서브클래스로 시작됩니다. 이 HUD 클래스는 GFxMoviePlayer 구성을 담당하며, 차례로 GFxMovie 재생을 담당합니다. GFxMovie에는 정보 표시를 처리 및 플레이어와의 상호작용을 하는 GFxObject가 몇이든 담길 수 있습니다.

GFxMovie

전체 스케일폼 GFx 시스템은 GFxMovie를 중심으로 돌아갑니다. 이것이 플래시에서 내보내고 엔진으로 가져와서 시간선, ActionScript 코드, 이미지 리소스, CLIK 오브젝트 등을 포함하고 있는 실제 Swf 무비 오브젝트입니다. 플레이어가 상호작용하는 인터페이스는 이런 무비를 재생하고 조작하는 식으로 만들어 집니다. 그렇기에 이 문서의 대부분은 언리얼 엔진 3에 한한 이런 동작을 수행하는 법에 대해 설명하는 데 할애하도록 하겠습니다.

GFxMoviePlayer 클래스

스케일폼 GFx 무비의 초기화 및 재생을 담당하는 모든 클래스에 대한 베이스 클래스입니다. 이 클래스는 플레이어가 담당하는 개별 무비 고유의 특화된 함수성을 구현하기 위해 서브클래싱됩니다. 게임 인터페이스의 다양한 요소를 언제고 재생하기 위해, HUD 클래스는 이러한 참조를 몇이고 가질 수 있습니다.

+
GFxMoviePlayer Properties (click to view)

+
GFxMoviePlayer Functions (click to view)

외부 텍스처

ExternalTexture (외부 텍스처) 구조체는 무비의 이미지 리소스(무비 내 이미지 리소스 상의 "연계" 식별자)와 언리얼 텍스처 리소스 사이의 매핑을 저장합니다. 이를 통해 무비에서 사용된 이미지의 런타임 리매핑이 가능합니다. 연계 식별자를 가진 텍스처는 무엇이든 SetExternalTexture() (외부 텍스처 설정 함수)를 사용하여 런타임에 대체시킬 수 있습니다.

자세한 정보는 런타임에 이미지 맞바꾸기 부분을 참고하시기 바랍니다.

Members 멤버

  • Resource 리소스 - 텍스처의 연계 식별자입니다.
  • Texture 텍스처 - 연계 식별자로 매핑된 텍스처입니다.

사운드 테마 바인딩

SoundThemeBinding 구조체는 이 무비 내 오브젝트의 사운드 이벤트를 처리하기 위해, 사운드 테마명을 실제 UISoundTheme (UI 사운드 테마)로 바인딩합니다. 사운드 이벤트는 CLIK 위젯 또는 아티스트가 수동으로 발동시킬 수 있습니다. 각 이벤트에는 테마명과 재생할 이벤트가 담겨 있습니다. 이 매핑은 아티스트가 지정한 테마명을 UISoundTheme (UI 사운드 테마) 애셋으로 일치시키며, 그 후에 이벤트명을 다양한 사운드 큐나 액션에 바인딩합니다.

자세한 정보는 UI 사운드 테마 부분을 참고하십시오.

Members 멤버

  • ThemeName 테마명 - 사운드 테마의 이름으로, 무비에서 아티스트가 지정합니다.
  • Theme 테마 - ThemeName (테마명)에 대한 사운드 이벤트 처리를 위한 사운드 테마입니다.

위젯 바인딩

GFxWidgetBinding 구조체는 무비의 CLIK 위젯 인스턴스에 GFxObject의 특정 UnrealScript 서브클래스를 넣어주고, 여기다 위젯의 플래시명을 더하며, 클래스를 지정합니다. 이는 WidgetInitialized() (위젯 초기화됨 함수)의 GFxObject 파라미터가 적절한 서브클래스로 생성되도록 합니다.

자세한 정보는 위젯 초기화 및 바인딩 부분을 참고하십시오.

Members 멤버

  • WidgetName 위젯명 - 무비의 위젯 이름입니다.
  • WidgetClass 위젯 클래스 - WidgetName 으로 링크하기 위한 GFxObject 서브클래스입니다.

GFxObject 클래스

The GFxObject class represents an element in a GFxMovie. This can technically be anything from a variable object to a function object to a displayable object, such as graphics or CLIK components.

+
GFxObject Properties (click to view)

+
GFxObject Functions (click to view)

디스플레이 인포

ASDisplayInfo 구조체는 런타임 사용시의 쉽고 빠른 조작을 위해 디스플레이 오브젝트의 속성을 저장합니다. 어떤 GFxObject 에 대한 디스플레이 인포도 획득 및 변경한 후 위에 설명한 Object Interface (오브젝트 인터페이스) 함수를 사용하여 오브젝트에 재적용시킬 수도 있습니다.

디스플레이 인포 작업에 관련된 상세 정보는, 위젯 디스플레이 인포 작업하기 부분을 참고하시기 바랍니다.

Members 멤버

  • [X/Y/Z] - GFxObject 의 위치입니다.
  • Rotation 회전 - Z 축 주변으로의 GFxObject 회전입니다.
  • [X/Y]Rotation 회전 - 가로 세로 (X Y) 축 주변으로의 GFxObject 회전입니다.
  • [X/Y/Z]Scale 스케일 - GFxObject 의 스케일입니다.
  • Alpha 알파 - GFxObject 의 불투명도로, [0, 1] 범위입니다.
  • Visible 표시여부 - 참이면 GFxObject 는 표시되며, 아니면 숨깁니다.
  • has[X/Y/Z] [X/Y/Z] 있는지 - GFxObject 가 각각 X, Y, Z 위치 정보를 갖고 있으면 참입니다.
  • hasRotation 회전 있는지 - GFxObject 가 회전 정보를 갖고 있으면 참입니다.
  • has[X/Y]Rotation [X/Y] 회전 있는지 - GFxObject 가 각각 X, Y 회전 정보를 갖고 있으면 참입니다.
  • has[X/Y/Z]Scale [X/Y/Z] 스케일 있는지 - GFxObject 가 각각 X, Y, Z 스케일 정보를 갖고 있으면 참입니다.
  • hasAlpha 알파 있는지 - GFxObject 가 알파 정보를 갖고 있으면 참입니다.
  • hasVisible 표시여부 있는지 - GFxObject 가 표시여부 정보를 갖고 있으면 참입니다.

색 변형

ASColorTransform 구조체는 위에 설명한 Object Interface (오브젝트 인터페이스) 함수를 사용하는 조작용 색 변형 정보를 저장하는 구조체입니다. 색 변형 정보는 (텍스트나 이미지 등의) 디스플레이 오브젝트 내의 색 값을 조절하기 위해 사용됩니다.

Members 멤버

  • multiply 곱 - 디스플레이 오브젝트의 원래 색에 곱해준 LinearColor (선형 색)입니다.
  • add 합 - 곱을 적용한 다음 디스플레이 오브젝트의 색에 더해준 LinearColor (선형 색)입니다.

UnrealScript 및 ActionScript


여기서는 UnrealScript와 ActionScript 사이의 전후 통신에 사용되는 주요 개념과 기술에 대해 설명해 보겠습니다.

UnrealScript에서 ActionScript 함수 호출

UnrealScript에서 ActionScript 함수 호출법은 여럿 있습니다만, 가장 자주 쓰이는 방법을 둘 꼽아 보자면:

방법 1: 감싼(wrapped) UnrealScript 함수

UnrealScript에서 ActionScript 함수를 호출하기에 좋은 방법입니다. 호출을 하려면, ActionScript 함수 호출을, 똑같은 파라미터와 반환값을 가진 UnrealScript 함수로 "감싸는"(wrap) 것입니다. 내부적으로 보면, 스케일폼 GFx ActionScript (ActionScript[Void/Int/Float/String/Widget] 등 각각의 반환형에 따라 이름지은) 함수는 그 호출 함수의 파라미터 목록을 살펴보고, 해당 파라미터를 ActionScript 런타임에 전달합니다. 즉 다음과 같은 ActionScript 함수를 호출하고 싶다 예를 들어 보자면:

public function MyActionScriptFunc(param1:String,param2:Object):Void
{
     // 뭔가 끝내주는 것을 해라!
}

UnrealScript에서, 다음과 같은 함수를 만들면 됩니다:

function CallMyActionScriptFunc(string Param1, GFxObject Param2)
{
     ActionScriptVoid("MyActionScriptFunc");
}

호출되면 통합 코드가 CallMyActionScriptFunc 의 파라미터 목록을 검사하고, 해당 파라미터를 스케일폼에 일치하는 것으로 변환한 다음, ActionScript의 함수를 호출합니다. 반환형이 있는 ActionScript 함수에 대해서는, GFxObject 내의 다른 ActionScript* 메서드를 사용할 수도 있습니다. 이러한 함수의 반환값은 ActionScript에서의 반환값이 됩니다.

방법 2: 인보크 사용하기

이 방법은 파라미터와 반환값으로 사용할 구조체를 만들고 전달해야 하기에 메모리 부하가 늘기는 합니다. 지루한 코드 구성 작업도 있습니다. 결정적으로, GFxObject 파라미터를 ActionScript로 자유로이 전달할 수가 없습니다. GFxObject 파라미터 때문에 위의 예제를 사용할 수 없으니, 여기서는 새로운 ActionScript 함수를 예로 들어 보겠습니다:

public function MyActionScriptFunc(param1:String,param2:Number):Bool
{
     // 뭔가 또 끝내주는 것을 해라!
     return TRUE;
}

Invoke 메서드를 통해 이 함수를 호출하는 데 해당하는 UnrealScript는:

function bool MyFunction()
{
     local ASValue RetVal;
     local array<ASValue> Parms;

     Parms[0].Type = AS_String;
     Parms[0].s = Param1;

     Parms[1].Type = AS_Number;
     Parms[1].n = Param2;

     RetVal = Invoke("MyActionScriptFunc", Parms);

     return RetVal.b;
}

이 메서드가 좀 더 지루하긴 해도, ActionScript 메서드를 호출하기 위한 새 함수 정의를 엄격히 요구하지는 않기에, 단순 기능 추가를 위해 GFxObject 를 서브클래싱한다거나 할 계획이 없는 일회성 경우에 대해서는 괜찮을 수 있습니다. 또한 다양한 길이의 파라미터를 가진 ActionScript 함수를 호출하기 위한 유일의 방법이기도 합니다.

ActionScript에서 UnrealScript 함수 호출하기

간단한 방법: 이벤트 통지 및 기타 일회성 상황에 좋습니다. ActionScript에서 UnrealScript 함수 호출하기는 그 반대의 경우보다 훨씬 간단합니다. ActionScript에서는 그냥 ExternalInterface's Call (외부 인터페이스의 콜) 메서드를, UnrealScript에서 호출하려는 함수 이름에다 파라미터를 전부 붙여 사용기만 하면 됩니다. 이 파라미터는 엔진이 UnrealScript에 맞게 자동으로 변환해 줍니다. 해당 GFxMoviePlayer 인스턴스에서 이름을 통해 함수를 찾습니다. 예를 들면 액션 스크립트에서:

import flash.external.ExternalInterface;
// ...
ExternalInterface.call("MyUnrealScriptFunction", param1, param2, param3);

위의 코드는 그 후 언리얼의 현재 GFxMoviePlayer 인스턴스에서 "MyUnrealScriptFunction" 라는 함수를 찾아보고, 파라미터도 UnrealScript의 해당 함수에 맞는 파라미터로 변환한 다음, 함수를 호출합니다. 이 경우에 주의할 점은 UnrealScript 함수의 파라미터 목록이 강압적이라서, UnrealScript 함수의 파라미터 형태로 변환될 수 없는 ActionScript 파라미터는 적절히 NULL 또는 디폴트 값이 됩니다.

중급 방법: ActionScript의 함수 호출을 후킹하는 데 좋습니다. 어떤 ActionScript 함수도 강제로 UnrealScript 대리자(delegatge)에 역호출(callback) 가능합니다. 이를 위해 ActionScriptSetFunction() 함수 래퍼(wrapper)를 사용합니다. 예를 들어 UnrealScript의 대리자를 호출하기 위해 무비 루트 내 DoFancyThings() 함수로의 아무 ActionScript 호출을 하려 한다 가정해 본다면, GFxMoviePlayer 내에 다음과 같은 코드를 구성해 볼 수 있겠습니다:

class MyDerivedGFxMoviePlayer extends GFxMoviePlayer;

// 무비 초기화를 위해 스크립트의 다른 곳에서 호출
event InitializeMoviePlayer()
{
     // ActionScript에서 호출되기 위한 대리자 구성
     SetupASDelegate(DoFancyThings);
}

// ...

delegate FancyThingsDelegate();

function DoFancyThings()
{
     // 여기에 코드를...
}

function SetupASDelegate(delegate<FancyThingsDelegate> d)
{
     local GFxObject RootObj;

     RootObj = GetVariableObject("_root");
     ActionScriptSetFunction(RootObj, "DoFancyThings");
}

위의 코드로 InitializeMoviePlayer() 에서 SetupASDelegate() 가 호출된 이후, DoFancyThings() 로의 모든 ActionScript 호출은 UnrealScript 함수 DoFancyThings() 를 경유하게 됩니다. 파라미터는 ActionScript에서 언리얼 형태로, 대리자에 의해 정의된 대로 자동 변환되기에, 프로그래머가 그 둘이 일치하는지 확인해 줘야 합니다.match.

주의할 점은 다른 ActionScript 래퍼 함수와 마찬가지로, ActionScriptSetFunction() 는 호출하는 함수의 파라미터, 이 경우 SetupASDelegate() 에서 대리자 정보를 얻습니다. 호출하는 함수는 대리자 외에도 다른 파라미터를 가질 수 있으나, 파라미터 목록에서 처음 발견되는 대리자가 역호출로 사용될 것입니다.

나아가 위의 예제에서, 무비의 루트에 함수를 설정했습니다. 이는 손쉽게 무비 내 다른 오브젝트가 될 수도 있습니다. GFxObject 내에도 참조하는 ActionScript 오브젝트 상에 함수 대리자를 직접 설정하는 상응 함수가 있습니다. 그 역시도 같은 방식으로 작동하는데, 유일한 차이점은 ActionScriptSetFunction() 호출에 오브젝트를 명시적으로 지정할 필요가 없다는 것입니다.

ActionScriptSetFunction 이해하기

ActionScriptSetFunction (액션 스크립트 설정 함수)를 통해 Flash 파일에서 ActionScript 함수가 호출되었을 때 UnrealScript 함수를 실행시킬 수 있습니다.

예: 가상의 Flash 파일에서 ActionScript 로 MyASFunction() 라는 함수를 호출했는데, 실제 함수는 ActionScript 에 없으며, 그 대신 Flash 파일에서 이 함수가 호출될 때마다 UnrealScript 함수 DoThis() 를 발동시키도록 하고 싶습니다.

용법: 여러가지 용법이 있습니다. 한 가지 좋은 방법이라면, 여러 뷰에 같은 Flash 파일을 공유하고 있는 상황에서 각각의 뷰를 특정 상황마다 다르게 조절하고 싶을 때입니다. 각 뷰에 대해 별도의 Flash 파일을 만들 필요 없이 UnrealScript 로 각 뷰에 대한 DoThis() 함수를 각기 다른 작업을 하도록 만들면 되는 것입니다. 그렇게 하면 MyASFunction() 이 공유 Flash 파일에서 호출될 때마다, 어느 뷰에 있는지에 따라 UnrealScript 로 된 DoThis() 함수가 호출됩니다.

1 단계

델리게이트를 정의합니다.

delegate int MyDelegate(bool bTrue);

2 단계

UnrealScript 로 DoThis() 함수를 만듭니다. 그 파라미터와 반환형은 1 단계에서 정의한 델리게이트와 일치해야 합니다. ActionScript 에서 MyASFunction() 이 호출될 때마다 실행되는 함수는 이렇습니다.

function int DoThis(bool bTrue)
{
  local int myNum;

  myNum = 5;
  `log("Do this UnrealScript function? " @ bTrue);
  return myNum;
}

3 단계

다음으로, 맞바꾸기를 하는 마법의 UnrealScript 함수를 만듭니다. 이 함수는 1 단계와 2 단계에서 작성한 델리게이트를 파라미터로 받습니다. 그런 다음 전체 무비를 아우르는 _global 로의 리퍼런스는 물론 _root 도 포함해서 캐시에 담습니다. 그 후 ActionScriptSetFunction 를 사용하여 Flash 파일에서 MyASFunction() 이 호출될 때마다 대신 전달된 델리게이트, 우리의 경우에는 DoThis() 를 호출하라고 지정합니다.

function SetMyDelegate( delegate<MyDelegate> InDelegate)
{
  local GFxObject _global;

  _global = GetVariableObject("_global");
  ActionScriptSetFunction(_global, "MyASFunction");
}

4 단계

이제 그냥 SetMyDelegate 함수를 실행하고, 거기에 DoThis() 함수를 전달해 주면 됩니다.

SetMyDelegate(none); // 우선 지우고
SetMyDelegate(DoThis);

5 단계

마지막으로, ActionScript 에서 MyASFunction 을 어떻게든 호출합니다.

ActionScript 2.0
// MyASFunction() 가 ActionScript 에 실존하지는 않습니다.
// 여기서 이 함수를 호출하면 UnrealScript 에 있는
// DoThis() 함수가 대신 실행됩니다.

MyASFunction(true);

결과는 이렇습니다. Flash 파일에서 MyASFunction() 이 호출될 때마다, 언리얼 로그에는 이런 내용이 출력됩니다:

Do this UnrealScript function? True

SWF 원래 크기 구하기

UnrealScript 로 SWF 파일 원래 크기를 구하는 법을 보여주는 코드 스니펫입니다.

Unrealscript
var GFxObject HudMovieSize;

simulated function PostBeginPlay()
{
  Super.PostBeginPlay();
  CreateHUDMovie();

  HudMovieSize = HudMovie.GetVariableObject("Stage.originalRect");
  `log("Movie Dimensions: " @ int(HudMovieSize.GetFloat("width")) @ "x" @ int(HudMovieSize.GetFloat("height")));
}

위젯 작업하기


GFxObject 서브클래스 사용하기

코드-주도 상호작용을 필요로 하는 복잡한 위젯에 대한 표준적인 방법은, UnrealScript의 GFxObject 를 서브클래싱하여 바라는 함수성을 캡슐화하고, ActionScript 함수로의 호출을 묶은 다음, 무비 클립에 대한 상태 추적 / 애니메이션 재생 정보를 추가합니다. 이 작업을 위해서는 그냥 GFxObject 를 서브클래싱한 다음 함수성을 추가하면 됩니다.

이를 통해 선호되는 UnrealScript 래퍼 메서드(위 "UnrealScript에서 ActionScript 호출하기" 에서의 1번 방법)을 사용하여 ActionScript 함수 호출을 추가할 수 있으며, 무비 클립 제어용 GotoAndPlay() 같은 시간선 명령이 포함된 도우미 함수도 만들 수 있습니다.

새로운 서브클래스를 사용하여 ActionScript 위젯으로의 참조를 구하려면, 아래와 같은 방법 중 하나를 사용하면 됩니다.

방법 1: GetObject() 사용하기 바라는 서브클래스의 위젯 참조를 구하기에 가장 간단한 방법은, 클래스를 (GFxMoviePlayer 에서 호출되었다면) GetVariableObject() 또는 (GFxObject 에서 호출되었다면) GetObject() 의 둘째 파라미터로 지정하는 것입니다. 이는 그 참조가 새로이 생성된 지정 클래스의 인스턴스가 되게끔 반환하도록 합니다. 불행히도 이는 함수의 첫째 파라미터가 아닌 옵션 파라미터기에, 반환형이 강제되지 않아 아래와 같이 수동으로 변환해 줘야 합니다:

local MyGFxObjectSubclass Widget;
Widget = MyGFxObjectSubclass( GetVariableObject("MyGFxObjectName", class'MyGFxObjectSubclass' );

방법 2: WidgetBindings 또는 SubWidgetBindings 사용하기 WidgetInitialized() 역호출을 가진 위젯은 무비 플레이어 자체적으로 처리된다면 GFxMoviePlayerWidgetBindings 배열에 추가시킬 수 있습니다. 자체 처리가 아닌 SetWidgetPathBinding() 를 통해 GFxObject 로 포워딩되는 경우라면 GFxObjectSubWidgetBindings 배열에 추가시킬 수 있습니다. WidgetInitialized() 가 그 위젯에 대해 호출될 때, WidgetBindings / SubWidgetBindings 배열에 지정된 대로 적절한 GFxObject 서브클래스에 클래스를 전달하게 합니다. 이에 대해 자세한 것은 아래의 "초기화 및 바인딩에 WidgetInitialized() 및 WidgetBindings 사용" 부분을 참고하시기 바랍니다.

위젯 인스턴싱

UnrealScript에서 위젯을 인스턴싱하려면, 그냥 GFxObjectAttachMovie() 에다 심볼명을 붙여 호출하고, 인스턴스 이름을 주면 됩니다. 주의할 점은, 무비 클립 심볼이 인스턴싱될 라이브러리에 있어야 한다는 점입니다. 그러나 만들려 하는 것과 같은 종류의 씬 안에 다른 클립이 있는 경우, 보통이 이런 경우에 해당할 텐데, 템플릿으로 다음 코드를 사용하십시오. 이 예제에서 "btn" 이라는 버튼 심볼의 인스턴스를 새로 만들어 "mc_MyNewButton" 이라는 인스턴스명을 가진 버튼을 새로 만듭니다:

function MyUnrealScriptFunction()
{
     local GFxObject MyNewWidget;

     MyNewWidget = GetVariableObject("_root").AttachMovie("btn", "mc_MyNewButton");
}

이 경우에 주의할 점은, 무비의 루트에 상대적으로 버튼을 만든다는 점입니다. 그러나 AttachMovie()GFxObject 의 함수이기에, 새로운 무비 클립 인스턴스를 어떤 것의 자식으로써도 만들 수 있습니다. (AttachMovie() 를 호출한 GFxObject 가 새로운 오브젝트의 부모가 됩니다.) 그리고서 그 오브젝트를 다른 GFxObject 와 마찬가지로 조작할 수 있습니다.

위젯 초기화 및 바인딩

특정 위젯이 시간선에 설정된 경우 역호출을 두면 편할 때가 많습니다. 특히나 첫 프레임에는 위젯이 없을 때 그렇습니다. 시간선에 위젯이 처음 등장하기 전까지 ActionScript에는 그에 대한 정보나 위젯이 없기에, ActionScript가 위젯을 만들 때마다 발동되는 WidgetInitialized() 역호출을 추가했습니다.

  • WidgetInitialized()enableInitCallback 가 참으로 설정된 CLIK 위젯에 대해서만 호출됩니다. 이는 엔진 호출을 ActionScript에서 쳐내는 것으로, 모든 위젯에(, 특히 장식이나 거의 코드로 바꿀 일이 없는 것들은) 이 함수가 필요하지는 않기 때문입니다. 그래도 수동으로 추가할 수는 있는데, 자세한 방법은 아래 "예외" 부분을 참고하십시오.
  • ActionScript에서 역호출을 구성하려면, 먼저 역호출 대상이 될 위젯을 선택합니다. 작은 위젯을 여럿 포함된 위젯의 경우, 부모 위젯에 역호출을 설정할 것을 추천합니다. 그리고서 부모 위젯의 영역 안에서 자식 위젯으로의 참조를 수동으로 조작하고 저장하시기 바랍니다. 이는 WidgetInitialized() 가 위젯의 이름으로 전달되기에, 무비의 각기 다른 클립에 공유될 수 있으며, 별개의 두 위젯이 WidgetInitialized() 역호출 내에서 이름이 같아져 버릴 수도 있기 때문입니다. 위젯 구분은 이름과 함께 WidgetInitialized() 으로 전달되는 경로명을 통해 가능하기도 합니다.
  • 위젯을 선택한 상태에서, 컴포넌트 인스펙터에서 enableInitCallback 변수를 찾아다가 참으로 설정하십시오.

compinsp.jpg

주: 현 레이아웃에 컴포넌트 인스펙터가 없는 경우, 윈도우 메뉴에서 "Component Inspector"를 선택하거나 Shift+F7키를 쳐서 띄울 수도 있습니다.

  • FLA를 저장하고, SWF를 (File->Puiblish 또는 Shift+F12를 통해) 퍼블리싱한 뒤, 다른 것들처럼 엔진으로 가져옵니다.
이제 마킹한 위젯이 무비에 생성될 때마다, UnrealScript는 GFxMoviePlayerWidgetInitialized() 를 포함하는 이벤트로의 호출을 위젯의 이름, 위젯의 전체 경로, 위젯을 가리키는 GFxObject 를 포함해서 받게 됩니다.

가끔 함수성을 캡슐화하기 위해서는, 생성 및 WidgetInitialized() 로 전달되는 참조가 보통 GFxObject 가 아닌 GFxObject 의 서브클래스가 되게 하는 것도 좋습니다. 예를 들어 라벨, 이미지, 기타 데이터 등이 담긴 특수 "점수판 아이템" 위젯이 있다는 것을 안다 칩시다. GFxObject 를 서브클래싱하고 위젯의 이미지와 라벨 변경에 대한 함수성은 UnrealScript 함수를 통해 캡슐화시킬 수 있습니다. 특정 위젯명을 어떤 GFxObject 서브클래스로 바인딩하려면, 그 위젯이 무비 플레이어의 WidgetInitialized() 함수를 통해 처리되는 경우 GFxMoviePlayer 내의 WidgetBindings 를 사용하십시오. 만약, 예를 들어 모든 'MyScoreboardWidget' 이란 이름을 가진 WidgetInitialized() 호출은 전부 생성된 ScoreboardWidget UnrealScript (GFxObject 에서 파생된) 클래스로의 참조를 전달하게 하고 싶다 칩시다. GFxMoviePlayer 의 디폴트 속성에 있는 WidgetBindings 에다 아래와 같이 항목을 추가할 수 있습니다:

class ScoreboardGFxMoviePlayer extends GFxMoviePlayer;

// ...

defaultproperties
{
     WidgetBindings(0)={(WidgetName=MyScoreboardWidget,WidgetClass=class'ScoreboardWidget')}
}

주요 콘테이너 위젯에 대한 함수성이 (메뉴 패널이나 점수판 위젯같은) 위젯 서브클래싱에 의해 독립되어버린 경우, WidgetInitialized() 호출을 그 자식 전부에 대한 위젯으로 포워딩하는 것도 좋을 수 있습니다. 위의 예제에서 보면, (플레이어 이름 라벨, 아이콘 등) 모든 자식 엘리먼트에 대한 WidgetInitialized() 호출을 ScoreboardWidget 가 전부 받게 하면 좋을 것입니다. 이 작업을 하려면 GFxMoviePlayer::SetWidgetPathBinding(GFxObject WidgetToBind, name Path) 를 사용하면 됩니다. 위젯과 경로를 전달할 때, 경로 내에 있는 초기화 역호출을 갖는 위젯은 무엇이든 지정된 위젯으로 전달될 것입니다. 이렇게 포워딩된 위젯에 대한 클래스는 그 초기화를 처리하는 GFxObjectSubWidgetBindings 배열에 정의된다는 점을 유념하십시오. 이 배열은 GFxMoviePlayerWidgetBindings 배열과 완전히 똑같은 포맷을 갖고 있으나, 포워딩을 통해 초기화된 위젯용으로 작동합니다.

위의 예제에서 경로 포워딩을 구성하기 위해 ScoreboardGFxMoviePlayerWidgetInitialized() 함수를 덮어쓸 수 있습니다. 즉 MyScoreboardWidget 가 초기화될 때, 그 자식의 WidgetInitialized() 호출 전부가 그리로 포워딩되는 것입니다. 그와 같은 구성법 예제 코드는 이와 같습니다:

class ScoreboardGFxMoviePlayer extends GFxMoviePlayer;

/** 점수판 GFx 위젯으로의 참조로, 그 모든 자식의 초기화를 이리로 보낼 수 있습니다. */
var ScoreboardWidget MyScoreboard;

event bool WidgetInitialized(name WidgetName, name WidgetPath, GFxObject Widget)
{
     if( WidgetName == 'MyScoreboardWidget' )
     {
          MyScoreboard = Widget;
          SetWidgetPathBinding(MyScoreboard, WidgetPath);

          return TRUE;
     }

     return FALSE;
}

defaultproperties
{
     WidgetBindings(0)={(WidgetName=MyScoreboardWidget,WidgetClass=class'ScoreboardWidget')}
}

위의 예제에서, 앞으로 컴포넌트 인스펙터에서 enableInitCallback 가 참으로 설정된 위젯은 MyScoreboardWidgetInitialized() 이벤트로 경유되어 처리되도록 합니다. 주어진 경로에 대한 바인딩을 제거하려면, 단순히 SetWidgetPathBinding() 의 첫 파라미터를 None 으로 하여 다시 호출하면 됩니다.

주: 가끔은 위젯이 WidgetInitialized() 에 의해 처리되었는지를 로그에 출력하여, UnrealScript가 처리하지 않았는데 잘못 enableInitCallback 마킹된 위젯의 역호출을 불가능하게 하는 데 사용할 수 있습니다. 그런 위젯을 검색하려면, GFxMoviePlayerbLogUnhandledWidgetInitializations 를 참으로 설정하십시오. 그러면 enableInitCallback 는 참인데, 그 경로에 바인딩된 무비나 위젯 중 하나의 WidgetInitialized() 에 대해서는 거짓을 반환하는 위젯이 있으면 (전체 경로명을 포함해서) 로그에 출력합니다. 이는 디버깅에 사용하는 것으로, 위젯을 처리할 때는 항상 WidgetInitialized() 에서 참을 반환해야 한다는 점을 유념하시기 바랍니다!

CLIK 위젯 요구사항의 예외: 약간의 커스텀 ActionScript를 작성하면 WidgetInitialized() 인터페이스를 CLIK 위젯 이외의 것에 사용하는 것도 가능합니다. WidgetInitialized 역호출은 CLIK_loadCallback 함수로의 후킹을 통해 구현되는데, CLIK 위젯이 아니라면 WidgetInitialized() 호출을 두고, 위젯에 다음 ActionScript를 추가해야 할 것입니다:

if( _global.CLIK_loadCallback )
{
     _global.CLIK_loadCallback(this._name, targetPath(this), this);
}

위젯 디스플레이 인포 작업하기

GFxObject 의 (위치나 표시여부같은) 속성을 조작하기에 가장 빠른 (그리고 성능에 최적인) 방법은, DisplayInfo 메서드를 사용하는 것입니다. 아래의 간단한 예제를 통해 시작해 보는 데 도움이 될 것입니다:

var GFxObject MyGFxWidget;  // 어딘가에서 설정된다 가정

function MyFunction()
{
    local ASDisplayInfo DI;

    DI = MyGFxWidget.GetDisplayInfo();
    `log("MyGFxWidget is at (" $ DI.x $ ", " $ DI.y $ ")");

    // 위젯에 대한 몇몇 속성을 새로 설정
    DI.x = 200;
    DI.y = 200;
    DI.Visible = true;
    MyGFxWidget.SetDisplayInfo(DI);
}

CLIK 컴포넌트 이벤트 역호출

CLIK 컴포넌트 이벤트가 발생할 때 UnrealScript 함수가 호출되도록 구성하는 법은 이와 같습니다:

WidgetInitialized() 또는 GetObject() 중 하나를 통해 대리자를 설정하려는 CLIK 위젯으로의 참조를 구합니다. 주: 이 참조는 GFxCLIKWidget 여야 합니다. 그 위젯의 서브클래스 지정은, WidgetInitialized() 를 통해 참조를 구한 경우 WidgetBindings 를 사용하면, 또는 GetWidget() 에 클래스를 지정하면 (예로 GFxCLIKWidget( GetWidget("MyWidgetName", class'GFxCLIKWidget')) 됩니다. 역호출을 추가하려면, CLIK 위젯으로의 참조에서 AddEventListener(name type, delegate listener) 를 호출합니다. 여기서 type 는 'CLIK_' 에다 (대소문자 구분된!) CLIK 이벤트명을 더한 것입니다. 예를 들어, "press" 이벤트에 후킹하려면, type 에는 'CLIK_press' 를 넣어야 합니다. CLIK 이벤트 전체 목록은 //depot/UnrealEngine3/Development/Flash/CLIK/gfx/events/EventTypes.as 에 있습니다.

function SetupCallback()
{
     local GFxCLIKWidget MyWidget;

     MyWidget = GFxCLIKWidget( GetObject("MyWidgetID", class'GFxCLIKWidget') );
     MyWidget.AddEventListener('CLIK_press', OnClickHandler);
}

function OnClickHandler(GFxCLIKWidget.EventData params)
{
     // 뭔가 해라...
}

무비 제거하기

한 가지 방법이라면 그냥 무비 클립을 빈 무비 클립으로 대체하는 것입니다.

var GFxObject RootMC

// _root 타임라인을 캐시합니다.

RootMC = GetVariableObject("_root");

// 'InstanceName' 이라는 깊이 0 무비 클립을
// _root 타임라인 상에 새로 만듭니다.
// 'LinkageID' 라는 링키지 ID 를 가진
// 라이브러리에서 심볼을 사용해서요.

RootMC.AttachMovie("LinkageID", "InstanceName", 0);

// 'InstanceName' 무비 클립을 깊이 0 빈 무비 클립으로 대체합니다.

RootMC.CreateEmptyMovieClip("InstanceName", 0);

The other method requires an Invoke which can then remove movie clips you no longer need.

var GFxObject RoomMC, MyMovieClip;
var array<ASValue> args;
var ASValue asval;

// 무비 클립을 _root 에 붙입니다.

RootMC = GetVariableObject("_root");
MyMovieClip = RootMC.AttachMovie("LinkageID", "InstanceName");

// 무비 클립을 제거합니다.

asval.Type = AS_Boolean;
asval.b = TRUE;
args[0] = asval;

MyMovieClip.Invoke("removeMovieClip", args);

MoviePlayer 포커스, 우선권, 입력 처리


무비가 콘트롤러/키보드 입력을 받아야 하면, 무비가 시작될 때 CLIK 위젯에 포커스를 꼭 주도록 하십시오. 포커스 초기 세팅은 입력 시스템 초기화를 담당합니다. Key.onPress 를 CLIK 입력 시스템과 병행해서 사용하지 마십시오. 그랬다간 메시지가 CLIK 과 무비 둘 다에 전송됩니다.

LocalPlayer 당 딱 하나의 무비 플레이어만 포커스가 있습니다 (FGFxEngine 의 PlayerStates 배열에 저장됩니다). LocalPlayer 에 대한 포커스는 세 가지 변수에 따라 결정됩니다:

  • bAllowFocus 포커스 허용 - 무비 플레이어가 포커스를 받을 수 있는지 입니다.
  • bOnlyOwnerFocusable 오너만 포커스 가능 - 무비의 오너 이외의 LocalPlayers 에 의해 무비 플레이어 포커스가 가능할지 입니다.
  • Priority 우선권 - 무비 플레이어의 우선권으로, 높을수록 렌더링과 포커스 둘 다 우선 받습니다.

LocalPlayer 의 포커스가 잡힌 무비 플레이어는 포커스를 받을 수 있으면서 우선권이 가장 높은 경우입니다 (bAllowFocus = true && (Owner = Me || !bOnlyOwnerFocusable)). 우선권이 같은 경우 가장 최근에 만들어진 무비 플레이어가 이깁니다. 생성 순서를 확실히 할 수 없는 경우라면, 우선권이 충돌하도록 하지 않는 것이 좋습니다.

입력을 받았을 때 시스템은 그 입력을 생성한 LocalPlayer 에 대한 포커스 무비로 입력을 보냅니다. 이 모든 것이:

UBOOL FGFxEngine::InputKey(INT ControllerId, FName ukey, EInputEvent uevent) which will call into
UBOOL FGFxEngine::InputKey(INT ControllerId, FGFxMovie* pFocusMovie, FName ukey, EInputEvent uevent) and
UBOOL FGFxEngine::InputAxis(INT ControllerId, FName key, Float delta, Float DeltaTime, UBOOL bGamepad) in GFxUIEngine.ccp.

포커스 무비가 입력을 받을 수 있고 (bAllowInput == true) 그 입력을 무시하지 않는다면 (pFocusIgnoreKeys 에 해당 입력이 들어 있다면), 무비에 그 입력을 처리할 기회가 주어져 그리고 전송됩니다.

무비가 입력을 받지 않았거나, 받았어도 그 입력을 캡처하지 않기로 했다면 (CaptureInput != true and 입력에 대해 IsKeyCaptured 가 거짓을 반환하는 경우), 또는 어떤 이유로든 그 입력을 씹거나 거부하는 경우 (포커스가 잡힌 무비가 GC되고 있을 수도 있고, 무비가 오너가 아닌 콘트롤러의 모든 입력을 씹을 수도 있고, 무비 플레이어에 대한 UnrealScript 배후 클래스가 입력을 씹기로 했을 수도 있고, 등등의 경우), 사용할 가능성이 있는 모든 텍스처 무비에도 전부 전송됩니다.

마우스 입력은 어떤 이유에서 포커스가 잡힌 무비에 거부되지 않으면, (UBOOL FGFxEngine::InputKey(INT ControllerId, FGFxMovie* pFocusMovie, FName ukey, EInputEvent uevent) 참고) 다른 모든 무비가 받는다는 점에서 약간 다르게 처리됩니다.

이미지 로딩


UILoaders 는 언리얼 패키지에서 텍스처를 로딩하기에 편한 인터페이스를 제공해 주는 CLIK 위젯입니다. 로드할 텍스처는 ActionScript 또는 런타임시 UnrealScript로 설정 가능합니다. 두 경우 모두, 참조 구문은 같습니다.

예를 들어 Texture2D'MyPackage.MyGroup.MyTexture' 텍스처를 로드하려면, UILoader 의 "source" 변수를 바이리니어 샘플 스케일링에 대해서는 "img://MyPackage.MyGroup.MyTexture" 로, 포인트 샘플링에 대해서는 "imgps://MyPackage.MyGroup.MyTexture" 로 설정해야 합니다.

uiloader.jpg

런타임시 SetString() 메서드는 새로운 이미지를 로드하는 데 사용할 수 있으며, 코드는 다음과 같습니다:

local GFxObject MyImageLoader;

MyImageLoader = GetObject("MyImageLoader_mc");
MyImageLoader.SetString("source", "img://MyPackage.MyGroup.MyTexture");

주: 텍스처는 애셋 참조가 아닌 문자열 이름으로 참조되기에, 조심해서 사용하셔야 합니다. 텍스처가 콘솔에서 로딩되도록 하려면, 텍스처는 항상 쿠킹된 패키지에 있거나, 텍스처로의 참조는 GFxMovieUserReferences 에 추가되어야 합니다.

또한 UILoader 로의 텍스처 스케일이 적절한지 확실히 하려면, 컴포넌트 인스펙터에서 maintainAspectRatio 을 거짓으로 설정하시기 바랍니다. 아니면 스케일폼 GFx 는 로딩되는 텍스처의 상 비율을 유지하려 할 것입니다.

런타임에 이미지 맞바꾸기

런타임에 이미지를 맞바꾸려면, GFxMoviePlayer 에 있는 SetExternalTexture(string Resource, Texture TextureToUse) 함수를 사용하십시오. 이는 내보낸 참조를 사용하는 이미지가 있으면 그 텍스처로 맞바꿔 줍니다. : GFxMoviePlayer 에서 Advance() 가 처음 호출된 이후에만 이 함수를 호출할 수 있습니다!

UI 사운드 테마


UI 사운드 테마 (Engine 의 UISoundTheme 클래스 참고) 를 통해 UI의 이벤트와 사운드 연결을 쉽고 빠르게 할 수 있습니다. 클래스 자체는 이벤트명과, ActionScript에서 그 이벤트가 발생할 때 재생할 사운드큐를 매핑시켜주는 역할을 할 뿐입니다.

UISoundTheme 새로 만들기:

  • 언리얼 에디터를 열고, 액터 클래스 브라우저를 클릭합니다. (보기 -> 브라우저 -> 액터 클래스)
  • "'액터'를 부모로 사용할까요?" 및 "놓을수 있는 클래스만?" 박스를 둘 다 체크 해제합니다.
  • 목록에서 UISoundTheme 위치를 찾은 다음 우클릭, "아키타입 생성..." 을 선택합니다.

SoundThemeArchetype.jpg

  • UISoundTheme 를 상주시킬 위치를 선택하고, 뜨는 대화창에서 OK를 클릭하십시오.

사운드 테마가 새로 생겼으니, 이벤트에 재생할 사운드큐를 넣어줘야 합니다. CLIK 위젯이 발동하는 이벤트 목록 전체는 EventTypes.as ActionScript 파일에서 찾아볼 수 있으며, 위치는 //UnrealEngine3/Development/Flash/CLIK/gfx/events/EventTypes.as 입니다. 이벤트에 사운드큐를 넣어주려면 그냥 Sound Event Bindings 배열에 항목을 추가(+ 아이콘 클릭)한 다음 Sound Event Name 필드에 이벤트명을 입력해 주고, Sound To Play 필드에 재생하려는 사운드큐를 아래와 같이 넣어주면 됩니다:

SoundThemeProps.jpg

사운드 테마가 새로 생겼으니, GFxMoviePlayer 에다 바인딩해 줘야 합니다. 이를 위해서는 UnrealScript에서, 아래와 같이 클래스의 디폴트 속성에서 사운드 테마 바인딩을 지정해 주거나, 그냥 새로운 바인딩을 SoundThemes 배열에 추가시켜 주면 됩니다.

class MyGFxMoviePlayer extends GFxMoviePlayer;

defaultproperties
{
     SoundThemes(0)=(ThemeName=default,Theme=UISoundTheme'MyPackage.MyUISoundTheme')
}

고급 같은 SWF에 사운드 테마 여럿 사용하기

현 시점까지, SoundThemeBinding 구조체에 있는 "ThemeName" 변수에 대해 언급한 적이 없습니다. CLIK 위젯에서 볼 수 있는 속성 SoundMap 에 보면, "theme" 에 대한 항목이 있는데 디폴트는 "default"라 되어 있습니다. 그런데 여기에는 아무 이름이나 넣어줄 수 있습니다. 엔진이 재생할 소리를 찾을 때, 먼저 이 "theme" 식별자를 기반으로 관련된 사운드 테마를 찾아봅니다. 그런 다음 그 테마에 있는 이벤트에 관련된 사운드큐를 재생합니다. 이를 통해 SWF 파일에 있는 어떤 위젯이 어떤 사운드 테마를 사용해도 되는 유연성이 확보됩니다.

예를 들어 버튼이 둘 있는데, 한 버튼은 "press" 이벤트가 발동되었을 때 "삑" 소리가 나게, 다른 버튼은 "빵" 소리가 나게 하려는 경우 각기 다른 UISoundThemes 를 둘 만들 수 있습니다: 하나는 "press" 를 "삑" 사운드큐에, 다른 하나는 "빵" 사운드큐에 바인딩합니다. 그리고서 위젯에서 볼 수 있는 속성 중 "theme" 항목을 각각 "삑" 과 "빵" 으로 바꿔주면 됩니다. 플레이어에 대한 디폴트 사운드 테마를 설정할 때, 다음과 같이 삑 과 빵 소리에 대한 테마를 추가합니다:

class MyGFxMoviePlayer extends GFxMoviePlayer;

defaultproperties
{
     SoundThemes(0)=(ThemeName=beep,Theme=UISoundTheme'MyPackage.BeepSoundTheme')
     SoundThemes(1)=(ThemeName=horn,Theme=UISoundTheme'MyPackage.HornSoundTheme')
}

고급 ActionScript에서 수동으로 이벤트 발동하기

사운드 이벤트는 CLIK 위젯 전용이 아닙니다... 사운드 이벤트는 ActionScript에서 다음 코드를 통해 임의로 발동시킬 수 있습니다:

if( _global.gfxProcessSound )
{
     _global.gfxProcessSound(this, "SoundThemeName", "SoundEventName");
}

스케일폼 현지화


현지화의 경우 저희가 사용한 방법은 WidgetInitialized() 콜백 시스템을 활용하여 문자열 값을 현지화된 값으로 대치하는 것이었습니다. 타당한 이유는 크게 두 가지 있습니다:

  • 아티스트가 실제 무비에 어떤 대체 문구를 넣을 수 있게 되어, 포맷이나 스타일 작업이 쉬워집니다.
  • 코드에서 모든 문자열을 검색할 수 있게 되어, 잠재적인 버그나 문제 추적에 도움이 됩니다.

문자열을 현지화하려면, GFxMoviePlayer 클래스에 문자열 변수를 두고 "localized" 키워드를 포함시키기만 하면 됩니다. 그러면 클래스가 들어있는 스크립트 패키지에 상응하는 현지화 파일에서 문자열을 찾아봅니다.

MyGFxMoviePlayer.uc
class MyGFxMoviePlayer extends GFxMoviePlayer;

var localized string MyTitleString;

var transient GFxObject lbl_Title;

event bool WidgetInitialized(name WidgetName, name WidgetPath, GFxObject Widget)
{
  if (WidgetName == 'TitleLabel')
  {
    lbl_Title = Widget;
    lbl_Title.SetText(MyTitleString);

    return true;
  }

  return false;
}

이제 자체 현지화 파일에서 자체 클래스에 대한 섹션과, 현지화된 문자열에 대한 항목을 만들면 됩니다. 아래처럼요:

[MyGFxMoviePlayer]
MyTitleString=제목 죽이지!

씬 테스팅과 디버깅


스케일폼 디버깅은 가끔 인내심 시험이 될 수가 있습니다. 컴파일 시간 에러를 던지지 않는 ActionScript 오타 등 에러 다수는 소리도 없이 실패하니 추적하기가 거의 불가능에 가깝습니다. 씬 안에서의 포커스 처리같은 면들은 디버깅이 정말 힘든데, 그런 데 마땅히 쓸만한 디버깅 툴이 없기 때문입니다.

GFxPlayer 의 테스팅

퍼블리싱 -> 임포트 -> 게임 부팅 -> 테스팅 에 시간이 걸리니, 반복처리와 테스팅 시간을 줄이기 위해, 종종 ActionScript 코드에 더미 데이터를 셋업하여 테스트합니다. 게임에서 이렇게 실행되는 것을 막기 위해, ActionScript 에서 이렇게 하면 됩니다:

if( _global.gfxPlayer )
{
    // 여기에 디버깅 코드.
}

이렇게 하면 코드가 외부 GFxPlayer 에서만 실행되며 게임에서는 실행되지 않아, 게임에 잘못된 데이터가 나타날까 걱정하지 않아도 됩니다. 저희가 얻은 가장 큰 교훈은, 할 수 있을 때마다 항상 플래시에서 콘텐츠를 테스트한 다음 거기서 돌아가게 하고 나서야 엔진으로 가져와야 한다는 것입니다. 반복처리 시간이 크게 절약되거든요. 그런 젼차로 저희 최근의 씬은 gears.view.Foo 클래스로 만들고 있습니다. 그 베이스 위치에다 플래시 전용 디버그 데이터를 넣어 엔진에 가져오기 전 테스트하는 것입니다. 그렇게 하여 게임에서 전송되는 데이터 시뮬레이션 작업이 쉬워집니다. 보통 모든 데이터 처리를 위해 후킹하고 보내는 곳이 바로 그 클래스이기 때문입니다.

UI 아티스트가 씬에 더미 콘텐츠를 대충 넣어두고 어때 보이는지 확인할 수 있는 장점도 있습니다.

로그 켜기

스케일폼 GFx 로그는 PC 라이브러리에서는 디폴트로 켜지지만, 콘솔 라이브러리에서는 아닙니다. 로그은 DevGFxUI 로그 채널을 통해 전송되는데, 디폴트로 억제되어 있습니다. 켜려면 게임의 Engine.ini 파일(예로 UDKEngine.ini)의 "Suppress=DevGFxUI" 줄을 (세미콜론을 사용해서) 주석으로 빼내시기 바랍니다.

주의할 점은 ActionScript Trace() 로그 또한 이 채널을 경유하기에, ActionScript 로깅을 보기 위해서는 반드시 억제를 해제해야 한다는 점입니다.