2. 디스플레이 프로그래밍

모든 설정 절차가 끝나면 실제로 일부 컨텐츠를 화면에 표시할 수 있습니다!

생성하는 모든 응용 프로그램에서 주요 작업 중 하나는 여러 논리적 단위로 분할하는 것입니다. 종종 그 단위는 시각적인 표현을 가질 것입니다. 즉, 각 단위는 디스플레이 오브젝트입니다.

2.1. 디스플레이 오브젝트

화면에 나타나는 모든 요소는 디스플레이 오브젝트의 유형입니다. starling.display 패키지에는 추상 DisplayObject 클래스가 포함됩니다. 이미지, 무비 클립 및 텍스트 필드와 같은 다양한 유형의 디스플레이 오브젝트에 대한 기초를 제공합니다.

DisplayObject 클래스는 모든 디스플레이 오브젝트가 공유하는 메서드 및 속성을 제공합니다. 예를 들어 다음 속성을 사용하여 화면에서 객체의 위치를 구성합니다:

  • x, y: 현재 좌표계에서의 위치.
  • width, height: 오브젝트의 사이즈 (포인트 단위)
  • scaleX, scaleY: 1.0은 크기 조정되지 않음을 나타내며, 2.0은 크기를 두배로 나타냅니다.
  • rotation: 원점을 중심으로하는 객체의 회전각 (라디안 단위).
  • skewX, skewY: 수평 및 수직 스큐 (라디안).

다른 속성은 픽셀이 화면에 나타나는 방식을 수정합니다:

  • blendMode: 객체의 픽셀이 아래의 객체와 어떻게 블렌딩되는지 결정합니다.
  • filter: 객체의 모양을 수정하는 특별한 GPU 프로그램 (셰이더). 필터는 예를 들어 개체를 흐리게하거나 그림자를 추가합니다.
  • mask: 마스크는 특정 영역 밖에있는 모든 부분을 잘라냅니다.
  • alpha: 객체의 불투명도, 0 (보이지 않음)에서 1 (완전히 불투명)까지.
  • visible: false의 경우, 객체는 보이지 않습니다.

모든 디스플레이 오브젝트가 지원해야 하는 기본 사항입니다. Starling의 API 영역에 대한 클래스 계층 구조를 살펴보겠습니다:

class hierarchy

다이어그램이 두 개의 주요 하위 분기로 나뉘어져 있음을 알 수 있습니다. 한 쪽에서는 Mesh를 확장하는 두 가지 클래스가 있습니다 : Quad, Image 및 MovieClip.

메쉬는 Starling의 렌더링 아키텍처의 기본 요소입니다. 실제로 화면에 그려지는 모든 것은 메쉬입니다. Stage3D는 삼각형 이외의 것을 그릴 수 없으며 메쉬는 삼각형을 생성하는 포인트의 리스트는 아닙니다.

다른면에서는 DisplayObjectContainer를 확장하는 몇 가지 클래스를 찾을 수 있습니다. 이름에서 알 수 있듯이 이 클래스는 다른 디스플레이 오브젝트의 컨테이너 역할을 합니다. 디스플레이 객체를 논리적 시스템 (즉, 디스플레이 목록)으로 구성할 수 있습니다.

2.2. 표시 목록 (Display List)

렌더링 될 모든 디스플레이 오브젝트의 계층 구조를 표시 목록(Display Object)이라고 합니다. 스테이지가 표시 목록의 루트를 구성합니다. 문자 그대로 “무대(Stage)”라고 생각하십시오. 사용자 (잠재 고객)는 무대에 들어온 물체 (배우)만 볼 수 있습니다. Starling을 시작하면 스테이지가 자동으로 생성됩니다. 무대에 (직접 또는 간접적으로) 연결된 모든 것이 렌더링 됩니다.

내가 “connected to”라고 말하면, 부모 – 자식 관계가 필요하다는 의미입니다. 객체를 화면에 표시하려면 스테이지의 자식 또는 무대에 연결된 다른 DisplayObjectContainer로 만듭니다.

Display List
그림 5. 디스플레이 오브젝트는 표시 목록에 구성되어 있습니다.

스테이지의 첫 번째 (일반적으로 유일한) 하위 노드는 응용 프로그램 루트입니다. 즉, Starling 생성자에 전달한 클래스입니다. 스테이지와 마찬가지로 DisplayObjectContainer가 될 것입니다. 거기가 당신이 점령해야 할 곳입니다!

컨테이너를 만들고 다른 컨테이너와 메쉬 (예 : 이미지)를 만듭니다. 표시 목록에서 이러한 메쉬가 잎(leaves)을 구성합니다. 즉, 자식 객체를 가질 수 없습니다.

이 모든 것이 매우 추상적으로 들리므로 구체적인 예를 들어 봅시다. 말풍선입니다. 말풍선을 만들려면 이미지 (풍선)와 텍스트 (내용)가 필요합니다.

이 두 객체는 하나 같이 동작해야 합니다. 이미지와 텍스트가 함께 이동하면 이동해야 합니다. 크기, 배율, 회전 등의 변경에도 동일하게 적용됩니다. 매우 가벼운 DisplayObjectContainer 안에 이러한 객체를 그룹화할 수 있습니다 (Sprite를 통해):

DisplayObjectContainer와 Sprite

DisplayObjectContainer와 Sprite는 거의 동의어로 사용할 수 있습니다. 이 두 클래스의 유일한 차이점 중 하나 (DisplayObjectContainer)는 추상 클래스이고 다른 하나 (Sprite)는 추상 클래스가 아니라는 것입니다. 따라서 스프라이트를 사용하여 하위 클래스가 없어도 객체를 그룹화 할 수 있습니다. Sprite의 다른 장점 : 더 빠른 타입입니다. 일반적으로 이것이 제가 선호하는 이유입니다. 대부분의 프로그래머와 마찬가지로, 나는 게으른 사람입니다!

따라서 텍스트와 이미지를 함께 그룹화하려면 스프라이트를 만들고 텍스트 및 이미지를 자식으로 추가합니다:

1
2
3
4
5
var sprite:Sprite = new Sprite(); // (1)
var image:Image = new Image(texture);
var textField:TextField = new TextField(200, 50, "Ay caramba!");
sprite.addChild(image); // (2)
sprite.addChild(textField); // (3)

(1) sprite를 생성합니다.
(2) sprite에 이미지를 추가합니다.
(3) sprite에 텍스트필드를 추가합니다.

자식을 추가하는 순서는 중요합니다. 서로의 위에 레이어처럼 배치됩니다. 여기서 텍스트필드는 이미지 앞에 나타납니다.

Speech Bubble
그림 6. 이미지와 텍스트필드로 구성된 말풍선.

이러한 객체가 그룹화되었으므로 마치 하나의 객체인 것처럼 스프라이트를 사용하여 작업할 수 있습니다.

1
2
3
4
var numChildren:int = sprite.numChildren; // (1)
var totalWidth:Number = sprite.width; // (2)
sprite.x += 50; // (3)
sprite.rotation = deg2rad(90); // (4)

(1) 자식의 수를 가져옵니다. 결과는 2입니다.
(2) 너비와 높이는 자식들의 크기와 위치를 고려합니다.
(3) x위치를 50포인트 오른쪽으로 이동합니다.
(4) 그룹을 90도 회전시킵니다 (Starling은 항상 라디안을 사용합니다).

실제로 DisplayObjectContainer는 자식을 조작하는데 도움이 되는 여러 메서드를 정의합니다:

1
2
3
4
5
6
7
8
9
function addChild(child:DisplayObject):void;
function addChildAt(child:DisplayObject, index:int):void;
function contains(child:DisplayObject):Boolean;
function getChildAt(index:int):DisplayObject;
function getChildIndex(child:DisplayObject):int;
function removeChild(child:DisplayObject, dispose:Boolean=false):void;
function removeChildAt(index:int, dispose:Boolean=false):void;
function swapChildren(child1:DisplayObject, child2:DisplayObject):void;
function swapChildrenAt(index1:int, index2:int):void;

2.3. 좌표계

모든 디스플레이 오브젝트에는 자체 좌표계가 있습니다. 예를 들어 x 및 y 속성은 화면 좌표계에 적용되지 않습니다. 이 속성은 항상 현재 좌표계에 따라 다릅니다. 좌표계는 표시 목록 계층 구조 내에서의 위치에 따라 달라집니다.

이것을 시각화하려면 종이를 핀 보드에 고정시키는 것을 상상해보십시오. 각 시트는 가로 x 축과 세로 y 축이있는 좌표계를 나타냅니다. 핀을 고정하는 위치는 좌표계의 근원입니다.

Coordinage Systems
그림 7. 좌표 시스템은 핀 보드의 시트처럼 작동합니다.

이제는 용지를 회전시키면 그 위에 끌어온 모든 것 (예: 이미지 및 텍스트)이 함께 회전합니다 (x축 및 y축처럼). 그러나 좌표계 (핀)의 루트는 그대로 유지됩니다.

따라서 핀의 위치는 시트의 x및 y좌표가 부모 좌표계 (= 핀 보드)를 기준으로 가리키는 점을 나타냅니다.

디스플레이 계층을 만들 때 핀 보드를 유추하십시오. Starling을 사용할 때 이해해야 할 매우 중요한 개념입니다.

2.4. 사용자 정의 디스플레이 오브젝트

이미 언급한 것처럼 응용 프로그램을 만들 때 논리부로 분할합니다. 간단한 체스 게임에는 보드 조각 일시 정지 단추 및 메시지 상자가 포함될 수 있습니다. 모든 요소가 화면에 표시됩니다. 따라서 각 요소는 DisplayObject에서 파생 된 클래스로 나타납니다.

간단한 메시지 상자를 예로 들어 보겠습니다.

Message Box
그림 8. 게임의 메시지 상자.

실제로 지난번에 만든 말풍선과 아주 비슷합니다. 배경 이미지와 텍스트 외에도 두 개의 버튼이 있습니다.

이번에는 스프라이트에서 객체를 그룹으로 묶는 대신 구현 세부 사항을 숨기는 편리한 클래스로 캡슐화하려고 합니다.

이를 위해 DisplayObjectContainer에서 상속받은 새로운 클래스를 만듭니다. 생성자에서 우리는 메시지 상자를 구성하는 모든 것을 만듭니다:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class MessageBox extends DisplayObjectContainer
{
    [Embed(source = "background.png")]
    private static const BackgroundBmp:Class;

    [Embed(source = "button.png")]
    private static const ButtonBmp:Class;

    private var _background:Image;
    private var _textField:TextField;
    private var _yesButton:Button;
    private var _noButton:Button;

    public function MessageBox(text:String)
    {
        var bgTexture:Texture = Texture.fromEmbeddedAsset(BackgroundBmp);
        var buttonTexture:Texture = Texture.fromEmbeddedAsset(ButtonBmp);

        _background = new Image(bgTexture);
        _textField  = new TextField(100, 20, text);
        _yesButton  = new Button(buttonTexture, "yes");
        _noButton   = new Button(buttonTexture, "no");

        _yesButton.x = 10;
        _yesButton.y = 20;
        _noButton.x  = 60;
        _noButton.y  = 20;

        addChild(_background);
        addChild(_textField);
        addChild(_yesButton);
        addChild(_noButton);
    }
}

이제 배경 이미지 두 개의 버튼 및 일부 텍스트가 포함된 간단한 클래스가 있습니다. 이것을 사용하려면 MessageBox의 인스턴스를 만들고 이를 디스플레이 트리에 추가하기만 하면 됩니다:

1
2
var msgBox:MessageBox = new MessageBox("Really exit?");
addChild(msgBox);

클래스에 fadeIn 및 fadeOut과 같은 메소드를 추가하고 사용자가 해당 버튼 중 하나를 클릭할 때 트리거 되는 코드를 추가할 수 있습니다. 이것은 Starling의 이벤트 메커니즘을 사용하여 수행됩니다. 이 메커니즘은 다음 장에서 설명됩니다.

2.5. 디스플레이 오브젝트 제거(dispose)

더 이상 객체를 표시하지 않으려면 상위 객체에서 객체를 제거하면 됩니다. removeFromParent()를 호출하여. 개체는 물론 주위에 계속있을 수 있으며 원하는 경우 다른 표시 개체에 추가 할 수는 있습니다. 그러나 종종 개체의 유용성이 오래되었다고 판단되면, 제거하는 것이 좋습니다:

1
2
msgBox.removeFromParent();
msgBox.dispose();

디스플레이 오브젝트를 제거(dispose)하면 해당 객체 (또는 자식 객체)가 할당한 모든 리소스가 해제됩니다. 이는 매우 중요합니다. 왜냐면 dispose 하지 않을 경우, Stage3D와 관련된 많은 데이터를 가비지 컬렉터가 가져갈 수 없기 때문입니다. 해당 데이터를 제거하지 않으면 메모리에 남아있게 됩니다. 즉 앱에 조만간 리소스가 고갈되고 충돌이 발생합니다.

일을 더 쉽게하기 위해 removeFromParent()는 부울 매개 변수를 가지고 있어서 자동으로 DisplayObject를 제거합니다. 그렇게하면 위의 코드를 이 단일 행으로 단순화 할 수 있습니다:

1
msgBox.removeFromParent(true);

2.6. 피벗 포인트

피벗 포인트는 기존 디스플레이 리스트에서 찾을 수 없는 기능입니다. Starling에서 디스플레이 오브젝트에는 pivotX와 pivotY라는 두 가지 추가 속성이 있습니다. 객체 (원점 루트 또는 앵커라고도 함)의 피벗 점은 좌표계의 루트를 정의합니다.

기본적으로 피벗 포인트는 (0, 0)입니다. 이미지에서 왼쪽 상단 위치입니다. 대부분의 경우 이것은 문제없지만, 때로는 다른 위치 (예: 그 중심을 가운데로)로 옮기고 싶을 때가 있을 것입니다.

피벗 포인트가 없으면 컨테이너 스프라이트 안에 객체를 래핑하여 이를 수행해야 합니다:

1
2
3
4
5
6
7
8
var image:Image = new Image(texture);

var sprite:Sprite = new Sprite(); // (1)
image.x = -image.width / 2.0;
image.y = -image.height / 2.0;
sprite.addChild(image); // (2)

sprite.rotation = deg2rad(45); // (3)

(1) 스프라이트를 생성합니다.
(2) 이미지의 중심이 스프라이트의 원점 위에 정확히 오도록 이미지를 추가합니다.
(3) 스프라이트를 회전하면 이미지가 가운데로 회전합니다.

대부분의 오랜 플래시 개발자는 이 트릭을 알게 될 것입니다. 그것은 꽤 규칙적으로 필요했습니다. 그러나 그와 같은 간단한 일에 대해서는 많은 코드가 있다고 주장할 수도 있습니다. 피벗 포인트를 사용하면 코드가 다음과 같이 줄어 듭니다:

1
2
3
4
var image:Image = new Image(texture);
image.pivotX = image.width  / 2.0; // (1)
image.pivotY = image.height / 2.0; // (2)
image.rotation = deg2rad(45); // (3)

(1) pivotX를 이미지의 수평 중앙으로 이동합니다.
(2) 피벗을 이미지의 수직 중앙으로 이동합니다.
(3) 중심을 중심으로 회전하십시오.

더 이상 컨테이너 스프라이트가 필요하지 않습니다! 이전 챕터에서 사용한 비유를 사용하여 피봇 포인트는 부모에게 객체를 부착할 때 객체를 통해 핀을 찔러 넣는 위치를 정의합니다. 위의 코드는 피벗 점을 객체의 중심으로 이동합니다:

Pivot Point
그림 9. 피벗 점의 이동에 따라 객체가 어떻게 회전하는지 알아보십시오.

피벗 포인트 좌표를 개별적으로 제어하는 방법을 배웠으니 이제 alignPivot() 메서드를 살펴 보겠습니다. 이를 통해 피벗 점을 한 줄의 코드만으로 객체 중심으로 이동할 수 있습니다:

1
2
3
var image:Image = new Image(texture);
image.alignPivot();
image.rotation = deg2rad(45);

편리하죠?

또한, 피벗 점을 다른 곳 (예: 오른쪽 하단)에서 원한다면 선택적으로 메서드에 정렬 인수를 전달할 수 있습니다:

1
2
3
var image:Image = new Image(texture);
image.alignPivot(Align.RIGHT, Align.BOTTOM);
image.rotation = deg2rad(45);

이 코드는 객체를 이미지의 오른쪽 하단 모퉁이를 중심으로 회전시킵니다.

더 알아보기

주의하십시오. 피벗 점은 항상 객체의 로컬 좌표계에서 제공됩니다. 실제로는 부모 좌표계와 관련된 width 및 height 속성과 다릅니다. 이는 물체가 예를 들어 예기치 않은 경우 놀라운 결과를 가져옵니다. 크기 조정 또는 회전.

예를 들어 너비가 100픽셀이고 200%로 축소된 이미지를 생각해 보십시오 (image.scaleX = 2.0). 이제 해당 이미지는 너비가 200픽셀 (원래 너비의 두 배)가 됩니다. 그러나 피벗 점을 수평 중심에 배치하려면 pivotX를 100이 아닌 50으로 설정하십시오! 로컬 좌표계에서 이미지는 여전히 100픽셀 넓이입니다. 상위 좌표계에서는 더 넓게 나타납니다.

이 섹션의 시작 부분에서 부모 스프라이트 내에 이미지의 중심을 두어 코드를 되돌아 볼 때 이해하는 것이 더 쉬울 수도 있습니다. 스프라이트의 크기를 변경하면 어떻게 될까요? 이것은 이미지의 위치를 중앙에 유지하기 위해 이미지의 위치를 업데이트해야 한다는 것을 의미합니까? 당연히 아니죠. 스케일은 스프라이트 내부에서 일어나는 일에는 영향을 미치지 않습니다. 피벗 포인트 속성과 동일합니다.

여전히 두통이 생겼다면 (실제로 나에게 일어난 것처럼), 객체의 크기나 회전을 변경하기 전에 피벗 점을 설정하는 것을 잊었을 수 있습니다. 기억하세요. 그것은 많은 문제를 피할 수 있게 도와줍니다.

Was this article helpful?

Related Articles