이 매뉴얼의 목적은 광범위한 Starling Framework에 대해 최대한 자세하게 소개하는 것입니다. Starling은 2D 게임에 중점을 둔, 모든 종류의 애플리케이션에 사용할 수 있는 ActionScript 3 용 크로스플랫폼 엔진입니다.

이 문서의 PDF 버전을 다운로드하십시오.

이 가이드에서 배울 수 있는 것들:

  • Starling의 기반 기술과 그것이 따르는 원칙들.

  • IDE를 선택하고 첫 번째 프로젝트에 맞게 설정하는 방법.

  • 디스플레이 리스트 이벤트 및 애니메이션 시스템과 같은 기본 개념.

  • 프래그먼트 및 버텍스 프로그램의 잠재력을 활용하는 방법과 같은 고급 기법.

  • 프레임 워크에서 최상의 성능을 얻는 방법.

  • 휴대 전화와 태블릿에서 게임을 실행하려면 무엇이 필요한지.

숨을 깊게 들이쉬고, 읽기를 멈추지 마세요!

Starling 핸드북
Starling 핸드북

이 설명서의 전체 내용을 포함할 뿐만 아니라 많은 내용 및 지침을 추가한 책을 작업 중입니다. 단계별 자습서를 통해 *완벽한 게임*을 개발할 수 있습니다.

  • Gamua Blog 에서 더 많은 정보를 얻으실 수 있습니다.

  • 아니면 책 미리보기 를 다운로드하십시오.

  • 출시될 때 알림을 받으려면 등록 하세요!

1. 시작하기

이 장에서는 Starling 및 Starling에 대한 개요를 제공합니다. 먼저 Flash와 AIR 및 Starling의 등장 시점에 대해 간략히 살펴 보겠습니다. 그런 다음 Starling과 협력할 때 도움이 되는 도구와 리소스를 평가할 것입니다. 이 장의 마지막 부분에서는 처음으로 "Hello World" 예제를 작성하여 실행하게 됩니다.

1.1. 소개

무엇보다 먼저: Starling 커뮤니티에 오신 것을 환영합니다! 2011년 첫 출시 이후 전세계의 개발자들은 작은 빨간 새(Starling 캐릭터)의 도움을 받아 놀라운 게임과 앱을 만들었습니다. 우리와 함께해줘서 고마워요!

잘 오셨습니다: 이 안내서는 당신이 알아야 할 모든 것을 가르쳐 줄 것입니다. 우리는 Asset Management와 같은 'A’에서 Zen 코딩과 같은 'Z’까지 모든 것을 다룰 것입니다.

프레임워크에 대한 느낌을 알 수 있도록 처음엔 간단한 게임부터 시작하겠습니다. 그런 다음 디스플레이 리스트 아키텍처 및 Starling의 애니메이션 시스템을 비롯하여 모든 개념을 자세하게 살펴보겠습니다. 마지막 챕터에는 맞춤형 렌더링 코드를 작성하는 방법과 같은 고급 Starling 사용자를위한 정보가 포함되어 있습니다. 당신이 그것을 알기 전에 당신은 스탈링의 마스터가 될 것입니다!

그러나 ActionScript 3.0 (AS3)을 기본적으로 이해해야 한다는 작은 전제 조건은 있습니다. 두려워하지 마세요: 다른 객체 지향 언어를 사용했다면 빠르게 이해할 수 있고, 여러 책과 자습서가 있습니다.

특별히 한 권의 책을 원한다면 콜린 무크의 "Essential ActionScript 3"을 권합니다. 내가 AS3를 시작했을 때 그것은 중요한 모든 너트와 볼트를 가르쳐 주었고, 나는 여전히 그것을 책꽂이에서 수시로 꺼내고 있습니다. (특히 AS3의 XML 처리를 위한 이상한 E4X 구문).

AS3와 JS 비교

ActionScript 3이 실제로 JavaScript의 후계자가 되도록 설계되었다는 것을 알고 계셨습니까? 아시다시피 JavaScript는 ECMAScript 언어 사양을 구현한 것입니다. ActionScript 3는 ECMAScript 4를 기반으로 합니다. 그러나 대부분 정치적인 이유로 인해 ECMAScript 4는 버려졌으며 브라우저에서 사용되지 않았습니다. 오늘날, ActionScript 3는 그 표준의 유일한 구현으로 남아 있습니다. 그러나 역설적이게도 최신 버전의 JavaScript는 ActionScript 3처럼 보입니다.

1.2. Adobe AIR가 뭐죠?

불과 몇 년 전만 해도, Adobe의 Flash Player는 웹을 이용할 때 어디에나 있었습니다. 그 당시에는 기본적으로 웹용 인터렉티브 또는 애니메이션 콘텐츠를 만들려는 유일한 선택이었습니다. 브라우저는 비디오 사운드 및 애니메이션과 관련하여 매우 제한적인 기능을 가지고 있었고, 그들이 가진 몇 가지 기능은 브라우저 비 호환성으로 인해 어려움을 겪었습니다. 간단히 말해: 그것은 엉망이었죠.

그래서 Adobe Flash가 인기가 있었습니다. [1] 디자이너와 개발자는 직관적인 제작 프로그램을 이용해 멀티미디어 컨텐츠를 제작할 수 있었습니다. (지금은 Adobe Animate라 불립니다) 그렇게 하면, 모든 플랫폼에서 동일하게 보였습니다. 배우기 쉽고 강력한 언어인 ActionScript 3도 있고요.

플랫폼의 인기를 기반으로 Adobe는 브라우저 외부에서 실행되는 독립 실행 형 응용 프로그램에 동일한 기술을 사용하자고 인식했습니다. 그것이 바로 Adobe AIR 런타임 입니다. AIR SDK로 작성된 응용 프로그램은 데스크톱 (Windows, macOS) 또는 모바일 (Android, iOS)에서 실행되는 독립 실행 형 응용 프로그램으로 배포될 수 있습니다. 표준 라이브러리는 플래시의 상위 라이브러리입니다, 그러므로 여러분은 Flash에서 할 수있는 모든 것을 AIR에서 할 수 있습니다; 게다가 파일 시스템 액세스 또는 윈도우 관리와 같은 기능을 위한 많은 추가 API를 제공합니다.

물론 데스크톱 응용 프로그램을 만들려면 그래픽 사용자 인터페이스를 만드는 방법이 필요합니다. 그렇죠? 표준 플래시는 이 작업에 적합하지 않았으므로 다른 SDK로 옮겨졌습니다: Flex(현재 Apache Flex). 또한 Flex는 XML 기반 마크 업 언어 (MXML이라 불리는)를 도입하여 사용자 인터페이스 레이아웃을 정의했습니다.

Starling의 경우, Flex가 필요하지 않습니다. 단지 AIR SDK만 있으면 됩니다.

1.2.1. Flash 및 AIR의 현재 상태

AIR가 소개될 당시 AIR는 "RIA (Rich Internet Application)"라는 용어로 요약 된 추세의 일부였습니다. — 2000년대 후반에 유행했던 전문 용어입니다. Adobe의 AIR와 Microsoft의 Silverlight (Sun의 JavaFX) 사이에는 치열한 경쟁이 있었습니다. 그러나 시간이 흐름에 따라 많은 것이 변했습니다. 결국 현재의 최후 승자는 엉뚱하게도 웹 기술로 애플리케이션을 구축 할 때 가장 많이 사용되는 기술 스택인 HTML5 / JavaScript입니다. 결국 어도비조차도 그 추세를 따라갔고 점점 더 많은 HTML5 지원을 자사 제품에 추가하고 있습니다.

소프트웨어 개발에 관해서는 대중을 맹목적으로 뒤쫓는 함정에 빠지지 마십시오. 모든 문제에 대해 여러 가지 해결책이 있습니다. 그들 중 일부는 다른 사람들보다 더 적합합니다. 가장 편안하게 사용할 수있는 도구를 선택하십시오. 자신의 길을 벗어나 만들고자하는 소프트웨어에 집중할 수있게 해주는 도구가 그것입니다.

더 이상 "멋진 아이"가 아닐지라도 AIR / Flash 플랫폼은 여전히 소프트웨어를 만드는 매우 매력적인 플랫폼입니다.

  • 레이디 가가 의상보다 빨리 변하는 인기 급상승 중인 HTML5의 조각난 세계와 비교하면 매우 성숙하고 사용하기 쉽습니다.

  • 그것은 일상적인 개발에 필요한 모든 도구를 제공하는 광범위한 표준 라이브러리와 함께 제공됩니다.

  • Flash 플러그인은 일반적인 웹 사이트의 쇠퇴를 분명히 보여 주지만 여전히 브라우저 게임의 표준입니다. 예를 들어 페이스북 게임의 대부분은 여전히 플래시로 제작됩니다.

  • 특히 Starling과 Feathers와 결합되어 진정한 크로스 플랫폼 개발을 위한 가장 매끄러운 길 중 하나를 제공합니다 (모든 주요 데스크톱 및 모바일 플랫폼을 단일 코드 기반으로 타겟팅).

Starling에 대해 말하자면 …​이렇게 그려진 그림에 좀 들어 맞나요?

1.3. Starling이 뭐죠?

Starling Framework를 사용하면 ActionScript 3에서 하드웨어 가속 응용 프로그램을 만들 수 있습니다. 주요 목표는 2D 게임을 만드는 것이지만 Starling은 모든 그래픽 응용 프로그램에 사용될 수 있습니다. Adobe AIR 덕분에 Starling 기반 응용 프로그램은 모든 주요 모바일 및 데스크탑 플랫폼에 배포할 수 있습니다.

The Starling logo.
Figure 1. 이 빨간 작은 친구는 Starling Framework의 로고입니다.

Starling은 Adobe AIR / Flash의 클래식 디스플레이 리스트 아키텍처를 모방하지만 훨씬 뛰어난 성능을 제공합니다. 모든 객체는 GPU에서 직접 렌더링됩니다 (Stage3D API 사용). 전체 아키텍처는 GPU와 잘 작동하도록 설계되었는데요. 이것은 일반적인 게임 개발 작업의 핵심 요소입니다. Starling은 Stage3D 내부를 개발자에게 숨기지만 완전한 성능과 유연성이 필요한 사람들을 위해 Stage3D 내부에 쉽게 액세스할 수 있게 해줍니다.

1.3.1. 다른 디스플레이 API가 필요한 이유는 무엇입니까?

위에 요약된대로 Starling의 API는 기본 Flash API와 매우 유사합니다. 즉: 'flash.display' 패키지. 그래서 여러분은 묻습니다: 왜 플래시 내부에서 플래시를 개선하려는 모든 노력을 기울이지 않지…​ 플래시가 잘못 되었습니까?

그 이유는 유연한 디스플레이 리스트, 벡터 기능, 텍스트 렌더링, 필터 및 기타가 포함된 원래의 flash.display API가 데스크톱 컴퓨터 시대에 설계 되었기 때문입니다. 이 컴퓨터는 강력한 CPU를 갖추고 있었지만 (현대 표준에 따라) 원시적이고 고정 논리 그래픽 하드웨어를 사용했습니다. 반면 오늘날의 모바일 하드웨어는 거의 반대의 설정을 가지고 있습니다: 매우 진보된 그래픽 칩을 가진 약한 (배터리 절약형) CPU.

문제점: 순수 CPU 렌더링을 위해 설계된 API를 갑자기 GPU를 효율적으로 사용하도록 변경하는 것은 (불가능하지는 않지만) 매우 어렵습니다. [2] 따라서 이러한 시도는 크게 실패했으며 플래시 플러그인은 요즘 휴대 전화와 태블릿의 브라우저에서 완전히 사라졌습니다.

Adobe는 이 문제에 대해 매우 잘 알고 있었습니다. 그래서 2011년에 Stage3D라는 저수준 그래픽 API를 도입했습니다. 그 API는 확실히 로우 레벨입니다; 이것은 기본적으로 OpenGL과 DirectX의 래퍼입니다. 개발자는 GPU의 순수 파워(raw power)에 액세스할 수 있습니다.

문제점: 그러한 낮은 수준의 API는 고전적인 디스플레이 리스트의 사용자에게 당장은 도움이 되지 못했습니다. Stage3D API는 로우 레벨이기 때문에 일반 개발자가 앱이나 게임을 만들 때 직접 작업할 수있는 것은 아닙니다. [3] 분명히 Adobe는 Stage3D 위에 구축된 더 높은 수준의 flash.display 같은 API가 필요했습니다.

흠 … 이것이 Starling이 무대(stage 객체를 비유함)에 들어선 이유죠 (말장난)! Starling은 가능한 한 많이 고전적인 Flash API를 모방하면서 Stage3D에 맞게 설계되었습니다. 이를 이용하면 무수한 개발자가 익숙한 개념을 사용하면서 오늘날의 강력한 그래픽 하드웨어를 최대한 활용할 수 있습니다.

물론 Adobe는 그러한 API를 직접 만들 수 있었습니다. 그러나 대기업이 만든 모놀리식 API는 크고 유연성이 없는 경향이 있습니다. 반면에 동등한 개발자 커뮤니티가 제공하는 소규모 오픈 소스 프로젝트는 훨씬 신속하게 행동할 수 있습니다. 이는 2011년 Flash 및 AIR 플랫폼의 제품 관리자인 Thibault Imbert가 Starling 프로젝트를 시작해야겠다는 통찰력을 갖게 했습니다.

현재까지는 Adobe에서 후원과 지원을 받고 있습니다.

1.3.2. Starling의 철학

Starling의 핵심 목표 중 하나는 가능한 한 가볍고 사용하기 쉽도록 만드는 것이었습니다. 필자가 생각하기에 오픈 소스 라이브러리는 사용하기 쉽고 — 또한 코드에 푹 빠지도록 용기를 북돋워야 합니다. 나는 개발자들이 보여지는 씬 뒤에서 무슨 일이 벌어지고 있는지 이해할 수 있기를 바랍니다. 그런 다음에는 자신의 필요에 완벽하게 맞을 때까지 확장하고 수정할 수 있어야 합니다.

이것이 바로 Starling의 소스가 잘 정리되어 있고 놀랍도록 간결한 이유입니다. 약 15k 라인 크기로 작성된 코드는 대부분의 게임보다 작을 것입니다!

정말 강조하고 싶습니다. 언젠가 코드가 예상대로 작동하지 않는 이유나 언젠가 멈추거나 혼란스럽다면 Starling의 소스 코드를 뜯어보는 것을 주저하지 마십시오. 종종 잘못된 점을 빠르게 파악하고 내부에 대해 더 잘 이해하게 될 것입니다.

Starling의 또 다른 중요한 목표는 물론 디스플레이 리스트 아키텍처와의 친밀성입니다. 디스플레이 리스트 뒤에 있는 전체 아이디어가 정말 마음에 든다는 것 뿐만 아니라 개발자가 Starling으로 쉽게 전환 할 수 있기 때문입니다.

그럼에도 불구하고, 필자는 절대로 완벽한 복제본을 만들려고 시도하지 않았습니다. GPU를 타깃으로 하는 것은 특정한 개념을 필요로 하며 이를 통해 빛나야 합니다! Textures, Meshes와 같은 개념은 마치 GPU 용으로 항상 설계된 것처럼 원래 API와 완벽하게 조화를 이루고자 합니다.

1.4. IDE 선택하기

방금 읽은 것처럼 Starling 앱과 게임은 Adobe AIR SDK를 사용하여 제작되었습니다. 전문적으로 텍스트 편집기와 명령 줄을 사용하여 코드를 컴파일하고 배포할 수는 있지만 권장하지는 않습니다. 대신, 여러분은 통합 개발 환경 (IDE)을 사용하고 싶을 것입니다. 그러면 디버깅, 리팩토링 및 배포가 훨씬 쉬워집니다. 고맙게도 선택할 수 있는 몇 가지가 있습니다. 모든 후보자를 살펴 봅시다!

1.4.1. 어도비 플래시 빌더(Adobe Flash Builder)

이전에 Flex Builder라고 불렸던 Adobe에서 만든 IDE입니다. 독립형 버전 (표준 및 프리미엄 버전)으로 구입하거나 Creative Cloud 구독의 일부로 구입할 수 있습니다.

Eclipse 기반으로 제작 된 이 소프트웨어는 모바일 디버깅 및 리팩토링과 같은 모든 기능을 지원하는 매우 강력한 소프트웨어입니다. 프리미엄 에디션은 매우 유용한 성능 프로파일러를 포함합니다.

개인적으로, 필자는 Flash Builder를 매우 오랜 시간 사용했으며 Starling 다운로드는 적절한 프로젝트 파일과 함께 제공됩니다. 그러나 한 가지 주의할 점이 있습니다. Flash Builder는 Adobe에서 포기한 것 같습니다. 마지막 업데이트 (버전 4.7)는 2012 년 말에 출시되었으며 그다지 안정적이지는 않습니다. 또한 이러한 상황이 조만간 바뀔 것이라는 징후는 없습니다.

따라서, 어쨌든 크리에이티브 클라우드 사용자인 경우에만 추천할 수 있습니다 (그 이유는 추가 비용없이 얻을 수 있기 때문입니다). 또는 어딘가에 오래된 라이선스가 있는 경우에만 사용할 수 있습니다. 나를 오해하지는 마세요: 그것은 많은 기능을 가지고 있습니다. 그러나 가끔 충돌이 발생하면 AIR SDK를 업데이트해야 하는 번거로움이 있다는 것도 기억하세요.

  • 플랫폼: Windows, macOS

  • 가격: USD 249 (스탠다드 에디션), USD 699 (프리미엄 에디션)

Adobe Flash Builder.

1.4.2. IntelliJ IDEA

다음 후보는 "모두를 다스리는 IDE"라고 할 수 있습니다, IDEA는 많은 언어와 플랫폼을 지원하기 때문이죠. AIR 지원은 플러그인 "Flash / Flex Support"를 통해 처리됩니다.

저는 IDEA를 꽤 오랫동안 사용해왔고 정말 좋아했습니다 (특히 강력한 리팩토링 기능). 기능면에서는 AS3 용으로 제작된 것처럼 느껴집니다. 모든 중요한 부분이 제자리에 있습니다.

Flash Builder와 달리 IDEA는 정기적인 업데이트를 지원합니다. 하지만 불행하게도 플래시 플러그인의 경우는 그렇지 않습니다. 상당 기간 동안 수정을 기다리고 있는 일부 (사소한) 개선사항이 있습니다.

그래도 그게 전부입니다. 그것은 훌륭한 IDE이고 여러분이 macOS를 사용한다면 더욱 추천합니다. 유일한 주의사항은 JetBrains가 최근 구독 기반 가격 정책 모델로 전환했기 때문입니다. 이는 모든 사람에게 매력적이지 않을 수 있습니다.

IDEA에는 무료 커뮤니티 버전이 있지만 불행히도 Flash/Flex Support 플러그인은 포함되어 있지 않습니다.

  • 플랫폼: Windows, macOS

  • 가격: USD 499 (첫 번째 해), USD 399 (두 번째 해), USD 299 (세 번째 해 이후)

구독 모델에는 소위 "영구적인 대체 라이센스"가 포함되어 있습니다. 이는 12개월 후에 구독을 취소하더라도 IDEA 버전을 유지할 수 있음을 의미합니다. 개인적으로 이것은 구독 모델의 단점을 완화한다고 생각합니다.
IntelliJ IDEA

1.4.3. FlashDevelop

내가 macOS에서 일하는 것을 좋아하는 만큼, 때로는 Windows 사용자를 부러워 합니다. 윈도우 사용자들은 Starling 개발을 위한 우수한 무료 (오픈 소스) IDE인 FlashDevelop을 사용할 수 있습니다. 그것은 2005년부터 있었고 계속 업데이트 중입니다. 당신이 Haxe를 사용한다면, 유용할 것입니다.

내가 주로 macOS를 사용하기 때문에 FlashDevelop에 대한 직접적인 경험은 별로 없습니다; 하지만 Starling 포럼의 수많은 게시물에서 나는 그것에 대해 좋은 말을 들었습니다. 어떤 사람들은 심지어 Parallels와 같은 가상 머신을 통해 Mac에서 그것을 사용하고 있습니다.

  • 플랫폼: Windows only

  • 가격: 무료 및 오픈 소스

FlashDevelop

1.4.4. PowerFlasher FDT

Flash Builder와 마찬가지로 FDT는 Eclipse 플랫폼을 기반으로 합니다. 따라서 Flash Builder를 떠나고 싶을 때 훌륭한 선택이 될 것입니다. FDT는 모든 Flash Builder 프로젝트를 가져올 수도 있습니다.

FDT는 여러 영역에서 Flash Builder보다 뛰어납니다; 예를 들어 프로젝트를 Flash에서 AIR로 쉽게 전환할 수 있습니다. — Flash Builder에서는 불가능 합니다. 또한 HTML5 / JavaScript, Haxe 및 PHP와 같은 몇 가지 추가 언어를 지원합니다.

대체적으로 매우 견고한 IDE입니다. Eclipse를 좋아한다면 FDT를 선택하지 않을 수 없습니다!

사용 가능한 무료 버전이 있습니다. 시작하기에 좋은 방법이죠. 제품 페이지에서 제안한 내용과 다르게 모바일 AIR 개발에도 사용할 수 있습니다.
  • 플랫폼: Windows, macOS

  • 가격: 월 25 달러에서 55 달러 사이 (계약 기간에 따라 다름) 학생과 교사는 특별 약관을 신청할 수 있습니다.

Powerflasher FDT

1.4.5. Adobe Animate

오랜 기간 동안 Flash를 사용해온 디자이너 또는 개발자라면 Adobe Flash Professional이 언제 나올지 궁금해 했을 것입니다. 으흠..여기 있습니다! 만약 인식하지 못했다면 어도비가 최근에 그것을 어도비 애니메이트로 이름을 변경했기 때문입니다. 새로운 이름은 초점의 주요 변화를 반영하기 때문에 실제로 의미가 있습니다. Animate는 이제 Flash뿐 아니라 HTML5, WebGL 및 SVG 형식의 출력을 지원하는 범용 애니메이션 도구입니다.

Starling을 Animate에서 사용할 수는 있지만 추천하지는 않습니다. 디자이너에게는 환상적인 툴이지만 코드를 작성하기 위해 제작된 것이 아닙니다. 당신은 그래픽을 위해서 그것을 사용하는 것이 훨씬 낫고 다른 언급 된 IDE 중 하나에 코드를 작성하는 것이 훨씬 낫습니다.

  • 플랫폼: Windows, macOS

  • 가격: 크리에이티브 클라우드 가입자에게는 무료

1.5. 리소스(Resources)

우리는 지금 거의 모든 빌딩을 위한 블록들을 갖추고 있습니다. 우리가 마침내 첫 번째 프로젝트를 시작할 수 있도록 모든 것을 설정하는 방법을 살펴 보겠습니다.

1.5.1. AIR SDK

IDE를 선택하여 설치한 경우 다음 단계는 최신 버전의 AIR SDK를 다운로드하는 것입니다. Adobe는 3개월마다 한 번씩 새로운 안정 버전을 출시하므로 최신 버전을 유지하십시오! 새로운 각 릴리스에는 일반적으로 최신 모바일 운영 체제와의 호환성을 유지하는 데 중요한 몇 가지 중요한 버그 수정이 포함되어 있습니다. 또한 새로운 기능을 실험하는 팀을 지속적으로 보게 될 것이며 Starling에서 그 페이스를 따라 잡기 위해 열심히 노력하고 있습니다.

최신 릴리스는 항상 여기에서 찾을 수 있습니다: Download Adobe AIR SDK

Starling 2는 최소한 AIR 19를 필요로 합니다.

1.5.2. Flash Player Projector

프로젝트가 Flash Player에서 실행되기를 원한다면 Projector라는 독립 실행 형 버전 (Debug 및 Release 버전으로 사용 가능)을 얻는 것이 좋습니다. 프로젝터의 장점은 훨씬 쉬운 디버깅 경험입니다. 예를 들어 브라우저를 통해 디버그 할 수도 있습니다 (플러그인의 디버그 버전을 설치 한 후). 하지만 (개인적인 생각이지만) 그것은 매우 귀찮습니다. 프로젝터는 훨씬 빨리 시작되므로 HTML 파일을 구성 할 필요가 없습니다.

이 페이지에는 개발자에게 적합한 모든 Flash Player 버전의 목록이 포함되어 있습니다. "projector content debugger, 프로젝터 컨텐츠 디버거"를 찾으십시오: Adobe Flash Player Debug Downloads

다시 말하지만, IDE가 올바른 플레이어를 찾는 방법을 알아야 할 수도 있습니다. 예를 들어 IDEA는 이 설정이 디버그 구성의 일부입니다.

다시 말하지만, IDE가 올바른 플레이어를 찾는 방법을 알아야 할 수도 있습니다. 예를 들어 IDEA에서 이 설정은 디버그 구성 화면의 일부입니다. 다른 IDE는 단순히 시스템 기본값을 사용할 수 있습니다. 어쨌든 항상 SWF를 컴파일한 AIR SDK 버전 이상의 플레이어 버전을 사용하는 것이 중요합니다.

1.5.3. Starling

이제 남은건 Starling 뿐입니다. 두 가지 방법 중 하나를 선택할 수 있습니다.

  1. 최신 릴리스를에서 zip 파일로 다운로드하십시오. gamua.com/starling/download.

  2. Starling의 Git 저장소를 복제하십시오.

전자의 장점은 프로젝트에 쉽게 링크되는 미리 컴파일 된 SWC 파일 (starling / bin 폴더에 있음)이 함께 제공된다는 것입니다. 그러나 항상 안정(stable) 버전만 얻을 수 있습니다. 즉, 최신 최신 업데이트 및 수정 사항을 놓치고 있습니다! 이런 이유로 나는 오히려 Git을 사용하길 권장합니다.

버그를 보고하고 며칠 후 해결한다고 가정해 봅시다. (네, 그런 일도 있습니다!) 표준 다운로드로 새로운 stable 릴리스를 만들 때까지 기다려야 합니다. 하지만, Git repository를 사용하는 경우 바로 해결할 수 있습니다.

Git에 대해 깊이있게 살펴보면 이 매뉴얼의 범위를 벗어나지만 웹에서 많은 튜토리얼을 찾을 수 있습니다. Git을 설치하면 다음 명령을 사용하여 전체 Starling 저장소를 디스크에 복제(Clone)할 수 있습니다:

git clone https://github.com/Gamua/Starling-Framework.git

그러면 Starling이 'Starling-Framework' 폴더로 복사됩니다. 'starling/src' 하위 폴더에서 실제 소스 코드를 찾습니다. 언급된 모든 IDE는 이 폴더를 프로젝트에 대한 소스 경로로 추가하는 것을 지원합니다. SWC 파일에 연결하는 것보다 더 이상 복잡하지 않습니다. 깔끔한 부작용으로 Starling의 소스를 디버깅 할 수 있습니다.

그러나 이 방법에 가장 적합한 이유는 Starling을 최신 버전으로 업데이트하는 것에 대한 것입니다. 저장소의 디렉토리로 이동하여 다음과 같이 하세요:

cd Starling-Framework
git pull

브라우저를 열고 수동으로 새 버전을 다운로드하는 것보다 훨씬 간단합니다. 그렇지 않습니까?

고급 Git 사용자를 위한 몇 가지 추가 정보:

  • Starling의 모든 일상적인 개발은 master 브랜치에서 발생합니다.

  • 안정적인 릴리스는 태그가 붙습니다. (v2.0, v2.0.1, `v2.1`와 같이).

  • 각 태그는 GitHub의 Release로 표시되어 있습니다. 그 시간에 미리 컴파일된 SWC 파일을 첨부할 것입니다.

1.5.4. 도움 얻기

우리 중 최고는 때때로 붙어 있습니다. Starling의 버그로 인해 로드 블록을 치거나 해결할 수 없는 문제가 발생할 수 있습니다. 어느 쪽이든 Starling 공동체는 당신을 불행에 빠뜨리지 않습니다! 도움이 필요할 때 갈 수있는 몇 가지 리소스가 있습니다.

Starling 포럼

그는 Starling 커뮤니티의 주요 허브입니다. 수십개의 새로운 게시물, 그것은 이전에 질문했던 문제들일 것입니다. 그래서 반드시 검색(Search) 기능을 사용하세요. 만약 도움이 되지 않는다면, 계정을 등록하여 질문해 주시기 바랍니다. 당신은 웹상에서 가장 친절 하고 인내심 많은 커뮤니티 중 하나를 볼 수 있을 것입니다!
http://forum.starling-framework.org

Starling 매뉴얼

지금 읽고 있는 온라인 설명서입니다. 난 최신 릴리즈를 유지하려고 노력할 것입니다.
http://manual.starling-framework.org

Starling 위키

링크와 다른 Starling에 대한 기사를 포함하는 위키 관련 항목, 그리고 가장 중요 한 것은: Starling 확장의 목록입니다. 우리는 나중에 그 중 일부를 설명합니다.
http://wiki.starling-framework.org

API 레퍼런스

모든 클래스와 메소드에 대한 자세한 내용은 Starling API 참조하는 것을 잊지 마세요.
http://doc.starling-framework.org

Gamua 블로그

Gamua 블로그를 통해 Starling에 대한 최신 뉴스와 상태를 확인하세요. 나는 조금 게으른 블로그 관리자입니다. 항상 각 Starling 릴리스에 대 한 하나 이상의 경우 인정 합니다.
http://gamua.com/blog

트위터(Twitter)

나에게 연락하는 가장 좋은 방법은 '@Gamua’를 통해 소셜 서비스를 이용하는 것입니다. 이 계정을 팔로우하세요. 최신 소식을 전해 드립니다.
https://twitter.com/Gamua

1.6. 헬로월드(Hello World)

휴~배경 지식이 꽤 많았네요. 드디어 손을 더럽힐 때입니다! 무엇이 "Hello World" 프로그램보다 더 좋은게 있을까요. 이 매뉴얼은 그것이 없이는 완전해질 수 없어요, 그렇죠?

1.6.1. 체크리스트

여기 사전 준비 사항을 간략히 요약 해 놓았습니다:

  • IDE를 선택하고 다운로드했습니다.

  • 최신 버전의 AIR SDK를 다운로드했습니다.

  • 최신 버전의 Flash Player 프로젝터를 다운로드했습니다.

  • Starling의 최신 버전을 다운로드했습니다.

  • 올바른 SDK 및 플레이어를 사용하도록 IDE를 구성했습니다.

IDE 구성 및 프로젝트 설정은 각 IDE에서 약간 다르게 수행됩니다. 이를 돕기 위해 Starling Wiki의 각 IDE에 대한 구체적인 방법을 만들었습니다. 계속하기 전에 해당 자습서를 따르십시오.

틀림없이 그러한 모든 설정 절차는 고통스럽습니다. 그러나 저와 함께 견뎌내봅시다: 당신은 이것을 매우 여유롭게 수행할 필요가 있습니다.

1.6.2. 시작 코드

IDE에 새 프로젝트 또는 모듈 만들기; "Hello World"라는 이름의 Flash Player 프로젝트 (대상 플랫폼: Web)로 시작하는 것이 좋습니다. 초기화 프로세스의 일부로 IDE는 최소 시작 클래스도 설정합니다. 아래 코드로 변경하세요. (일반적으로, 클래스의 이름은 프로젝트와 비슷하기 때문에 아래의 클래스 이름을 프로젝트 이름과 같게 바꾸세요.)

package
{
    import flash.display.Sprite;
    import starling.core.Starling;

    [SWF(width="400", height="300", frameRate="60", backgroundColor="#808080")]
    public class HelloWorld extends Sprite
    {
        private var _starling:Starling;

        public function HelloWorld()
        {
            _starling = new Starling(Game, stage);
            _starling.start();
        }
    }
}

이 코드는 Starling 인스턴스를 만들고 바로 시작합니다. 우리는 "Game"클래스에 대한 참조를 Starling 생성자에 전달합니다. Starling은 준비가 되면 해당 클래스를 인스턴스화 합니다. (그렇게하면 객체 정렬 순서가 올바르게 되었는지 신경쓰지 않아도 됩니다. 하나 뿐이니까요.)

그 클래스는 당연히 먼저 만들어져 있어야 됩니다. Game 클래스를 생성하고 다음 코드로 변경하세요:

package
{
    import starling.display.Quad;
    import starling.display.Sprite;
    import starling.utils.Color;

    public class Game extends Sprite
    {
        public function Game()
        {
            var quad:Quad = new Quad(200, 200, Color.RED);
            quad.x = 100;
            quad.y = 50;
            addChild(quad);
        }
    }
}

위 클래스는 우리가 모든 것을 올바르게 설정했는지 확인하기 위해 간단한 빨간색 쿼드를 표시합니다.

Game 클래스는 flash.display.Sprite가 아닌 starling.display.Sprite를 상속 받았습니다. 이것은 우리가 Starling 세계에 있기 때문에 중요합니다. flash.display 패키지와는 완전히 별개입니다.

1.6.3. 첫 번째 실행

이제 프로젝트를 시작하십시오. 여러분 중 일부는 출력이 약간의 오류를 일으킬 수 있습니다. 오류 메시지가 다음과 같이 표시되기 때문입니다:

Startup error message
Figure 2. 예상되는 쿼드 대신 이 오류를 만나보실 수 있습니다.

이 경우 독립 실행 형 Flash Player 대신 열려있는 브라우저일 가능성이 큽니다. 실행/디버그 구성을 확인하고 브라우저가 아닌 Flash Player 프로젝터 (디버그 버전)가 사용되는지 확인하십시오. 문제를 해결해야 합니다.

브라우저 오류 수정

언젠가는 SWF 파일을 HTML 페이지에 포함하려고 할 것입니다. 이 경우 SWF 파일이 포함 된 HTML 파일에서 wmode Flash 매개 변수를 직접 변경하여 오류를 해결할 수 있습니다. 일반적으로 이는 다음과 같이 변경해야 합니다:

// 다음 줄을 찾으세요 ...
var params = {};

// ... 바로 아래 줄에 추가하십시오:
params.wmode = "direct";
AIR 오류 수정

SWF 파일 대신 AIR 응용 프로그램을 만든 경우에도 이 오류가 표시됩니다. 이 경우 AIR 응용 프로그램 설명자 (HelloWorld-app.xml 또는 이와 비슷한 이름)를 편집해야 합니다. XML 노드 중 renderMode를 검색하고(주석 처리 되었을 수 있음) 값을 direct로 변경하십시오:

이것 찾기:
<!-- <renderMode></renderMode> -->

이것으로 바꾸기:
<renderMode>direct</renderMode>
여기서 우리가 하고있는 일은 런타임이 GPU에 액세스 할 수 있도록 허용하는 것입니다. 이렇게 하지 않으면, Stage3D가 접근할 수 없습니다.

1.6.4. 실행이 해결됨

축하합니다! Starling 기반 프로젝트를 성공적으로 컴파일하고 실행했습니다.

Hello World
Figure 3. 환상적이군요: 빨간색 상자 안에 있는 빨간색 Starling이라니.

진지모드: 가장 어려운 부분은 이제 당신 뒤에 있습니다. 드디어 실제 프로젝트를 파고들 준비가 되었습니다!

1.7. 요약

이제 Starling과 함께 작업 할 도구와 리소스에 대해 기본적으로 이해해야 합니다. 첫 번째 실제 게임에 뛰어 들어야 할 때입니다!

2. 기본 개념

Starling은 컴팩트한 프레임워크라고 볼 수도 있는 반면에 상당한 수의 패키지와 클래스를 자랑합니다. 그것은 서로를 보완하고 확장하도록 설계된 몇 가지 기본 개념을 중심으로 구축되었습니다. 그것들은 당신이 상상할 수있는 모든 응용 프로그램을 만들 수있는 능력을 제공하는 일련의 도구를 제공합니다.

디스플레이 프로그래밍

화면에 렌더링되는 모든 개체는 디스플레이 리스트에 구성된 디스플레이 객체입니다.

텍스처 & 이미지

픽셀, 폼 및 색상을 화면에 가져오기위해 Texture 및 Image 클래스를 사용하는 방법을 배웁니다.

다이나믹 텍스트

동적 텍스트의 렌더링은 거의 모든 애플리케이션에서의 기본 작업입니다.

이벤트 처리

커뮤니케이션은 중요합니다! 디스플레이 객체는 서로 이야기해야 하며 Starling의 강력한 이벤트 시스템을 통해 이를 수행할 수 있습니다.

애니메이션

사진에 모션을 가미해 보세요! 표시 객체를 애니메이션화 하는 데는 여러 전략이 있습니다.

에셋 관리

텍스처 및 사운드와 같은 모든 종류의 에셋을 로드하고 구성하는 방법에 대해 알아보십시오.

특별한 효과들

그래픽을 돋보이게 하는 효과 및 필터.

유틸리티들

삶을 편하게 해주는 많은 조력자들.

우리가 다룰 수 있는 근거는 충분합니다, 그러니, 슈퍼 마리오의 말로 대신합시다: "Lets-a-go!"

2.1. Starling 구성

모든 Starling 구동 응용 프로그램의 첫 번째 단계: Starling 클래스 (starling.core 패키지)의 인스턴스 만들기. 다음은 Starling의 생성자에 대한 전체 선언 구문입니다:

public function Starling(
    rootClass:Class,
    stage:Stage,
    viewPort:Rectangle = null,
    stage3D:Stage3D = null,
    renderMode:String = auto,
    profile:Object = auto);
rootClass

Stage3D가 초기화를 완료하자마자 인스턴스화 되는 클래스입니다. starling.display.DisplayObject의 서브 클래스여야 합니다.

stage

Starling의 콘텐츠를 호스팅할 전통적인 Flash 스테이지 객체. Starling과 플래시 디스플레이 리스트를 서로 연결하기 위해 필요합니다.

viewPort

Starling에서 렌더링할 플래시 스테이지 내의 영역입니다. 전체 스테이지 크기인 경우가 많으므로 이 매개 변수를 생략 (즉 null을 전달)하여 전체 영역을 사용할 수 있습니다.

stage3D

렌더링에 사용되는 Stage3D 인스턴스입니다. 각 플래시 스테이지에는 여러 Stage3D 인스턴스가 포함될 수 있으며 그 중 하나를 선택할 수 있습니다. 그러나 대부분의 경우 기본 매개 변수 (null)로 충분합니다. Starling에서는 사용 가능한 첫 번째 Stage3D 객체를 사용하게 됩니다.

renderMode

Stage3D의 모든 아이디어는 하드웨어 가속 렌더링을 제공하는 것입니다. 그러나 소프트웨어 폴백 모드도 있습니다. Context3DRenderMode.SOFTWARE를 전달하여 강제 실행될 수 있습니다. 그러나 기본 (자동)을 권장합니다. 소프트웨어 렌더링은 대안이 없을 때만 사용하는게 좋습니다.

profile

Stage3D는 (Context3DProfile 내에서 상수로 정의) 다른 프로필로 그룹화하는 기능 세트를 제공합니다. 응용 프로그램이 실행되는 하드웨어가 좋을수록 더 좋은 프로파일이 가능합니다. 기본값 (자동)은 사용 가능한 최상의 프로파일을 선택합니다.

이러한 인수의 대부분은 유용한 기본값을 가지므로 모든 값을 지정할 필요는 없을 것입니다. 아래 코드는 Starling을 시작하는 가장 간단한 방법을 보여줍니다. Flash Player 또는 AIR 프로젝트의 Main 클래스를 보시죠:

package
{
    import flash.display.Sprite;
    import starling.core.Starling;

    [SWF(width="640", height="480",
         backgroundColor="#808080",
         frameRate="60")]
    public class Main extends Sprite
    {
        private var _starling:Starling;

        public function Main()
        {
            _starling = new Starling(Game, stage);
            _starling.start();
        }
    }
}

위 클래스는 Starling 변형이 아닌 flash.display.Sprite를 확장합니다. 이는 AS3의 모든 기본 클래스의 필수 요소입니다. 그러나 Starling이 시작되면 바로 로직이 Game 클래스로 옮겨져 starling.display 세계에 대한 링크가 만들어집니다.

프레임 속도(Frame Rate) 구성.

일부 설정은 클래스 앞에 있는 "SWF" 메타 데이터 태그에서 바로 구성됩니다. 프레임 속도는 그 중 하나입니다. Starling 자체에는 동일한 설정이 없습니다. 항상 기본 스테이지의 frameRate를 사용합니다. 런타임시에 변경하려면 nativeStage 속성에 액세스하십시오:

Starling.current.nativeStage.frameRate = 60;

Starling의 설정 프로세스는 비동기식입니다. 즉, Main 메서드가 끝났다고 해서 아직 Game 인스턴스에 액세스할 수는 없습니다. 그러나 ROOT_CREATED 이벤트를 수신하여 클래스가 인스턴스화 될 때 알림을 받을 수 있습니다:

public function Main()
{
    _starling = new Starling(Game, stage);
    _starling.addEventListener(Event.ROOT_CREATED, onRootCreated);
    _starling.start();
}

private function onRootCreated(event:Event, root:Game):void
{
    root.start(); // 'start' needs to be defined in the 'Game' class
}

2.1.1. 뷰포트(ViewPort)

Stage3D는 Starling에 그릴 직사각형 영역을 제공합니다. 이 영역은 원래 스테이지의 아무 곳이나 될 수 있습니다. 즉 Flash Player 또는 응용 프로그램 창의 영역 (AIR 프로젝트의 경우) 내의 모든 위치를 의미합니다.

Starling에서는 해당 영역을 viewPort라고 합니다. 대부분의 경우 사용 가능한 영역을 모두 사용하고자 하지만 렌더링을 특정 영역으로 제한하는 것이 좋습니다.

16 : 9 화면에서 실행되는 종횡비가 4 : 3으로 설계된 게임을 생각해보십시오. 화면에 4 : 3 뷰포트를 중앙에 배치하면 상단과 하단에 검정색 막대가 있는 "레터 박스된(letterboxed)" 게임이 만들어집니다.

Starling의 스테이지를 보지 않고서는 viewPort에 대해 이야기 할 수는 없습니다. 기본적으로 스테이지는 viewPort와 정확히 동일한 크기입니다. 이는 물론 많은 의미가 있습니다. 디스플레이 크기가 1024 x 768 픽셀인 장치는 동일한 크기의 스테이지를 가져야 합니다.

그러나 스테이지 크기를 사용자 정의할 수 있습니다. stage.stageWidth 및 stage.stageHeight 속성을 통해 가능합니다:

stage.stageWidth = 1024;
stage.stageHeight = 768;

근데 잠깐만요, 그것은 심지어 무엇을 의미합니까? 현재 드로잉 영역의 크기가 viewPort 또는 스테이지 크기로 정의되어 있습니까?

걱정하지 마십시오. 해당 영역은 위에서 설명한대로 viewPort에 의해서만 설정됩니다. stageWidth 및 stageHeight를 수정해도 드로잉 영역의 크기가 전혀 변경되지 않습니다; 스테이지는 항상 전체 viewPort에 걸쳐 펼쳐집니다. 하지만 변화하는 것은 무대의 좌표계 크기입니다.

즉, 스테이지 너비가 1024인 경우 x좌표가 1000인 객체는 스테이지의 오른쪽 가장자리에 가까워집니다; viewPort가 512, 1024 또는 2048 픽셀인지 여부는 관계 없습니다.

이는 HiDPI 화면을 개발할 때 특히 유용합니다. 예를 들어, Apple의 iPad는 보통의 "레티나" 버전에 존재하며 후자는 픽셀 행과 열의 수를 두 배로 늘려서 (픽셀 수를 4 배 늘림). 이러한 화면에서 인터페이스 요소가 작아서는 안됩니다. 대신 그들은 더욱 선명하게 렌더링되어야 합니다.

ViewPort와 스테이지 크기를 구분하여 Starling에서 쉽게 재현할 수 있습니다. 두 기기 유형 모두 스테이지 크기는 1024 × 768; 반면에 viewPort는 화면 크기를 픽셀 단위로 나타냅니다. 이점: 응용 프로그램이 실행되는 장치에 관계없이 표시 객체에 대해 동일한 좌표를 사용할 수 있습니다.

Points와 Pixels 비교

이것을 생각해 보면 레티나 장치에서 x좌표가 1인 물체는 실제로 원점에서 2픽셀 떨어져 있는 것을 볼 수 있습니다. 즉 측정 단위가 변경되었습니다. 우리는 더 이상 픽셀에 대해 말하지 않습니다, 포인트를 말합니다! 저해상도 화면에서 한 포인트는 한 픽셀과 동일합니다. HiDPI 화면에서는 2픽셀 (또는 장치에 따라 그 이상)입니다.

포인트의 실제 너비 (픽셀 단위)를 확인하려면, view.width를 stage.stageWidth로 나눠서 간단히 구할 수 있습니다. 또는 Starling의 contentScaleFactor 속성을 사용하면 됩니다.

starling.viewPort.width = 2048;
starling.stage.stageWidth = 1024;
trace(starling.contentScaleFactor); // -> 2.0

'[Mobile Development], 모바일 개발 챕터’에서 이 개념을 최대한 활용하는 방법을 알려 드리겠습니다.

2.1.2. Context3D 프로파일

Starling이 실행중인 플랫폼은 다양한 그래픽 프로세서를 탑재하고 있습니다. 물론 이러한 GPU는 기능이 다릅니다. 문제는 런타임에서 이러한 기능을 어떻게 구별 할 것인가 하는 것입니다.

이것이 바로 Context3D 프로파일 (렌더링 프로파일이라고도 함)입니다.

Context3D 란 무엇입니까?

Stage3D를 사용할 때 많은 속성과 설정이 있는 렌더링 파이프 라인과 상호 작용합니다. 컨텍스트는 해당 파이프 라인을 캡슐화하는 개체입니다. 텍스처 만들기, 셰이더 업로드, 삼각형 렌더링 - 모두 컨텍스트를 통해 수행됩니다.

실제로 Starling은 모든 프로필 제한 사항을 숨기려고 최선을 다하고 있습니다. 도달 범위를 최대한 넓히기 위해 사용 가능한 가장 낮은 프로필에서도 작동하도록 설계되었습니다. 동시에 높은 프로필에서 실행하면 자동으로 최고의 프로필을 사용합니다.

그럼에도 불구하고, 기본 기능에 대해 아는 것이 유용할 수 있습니다. 다음은 가장 낮은 프로필부터 시작되는 각 프로필에 대한 개요입니다.

BASELINE_CONSTRAINED

장치가 Stage3D를 지원하는 경우 이 프로파일을 지원해야합니다. 이는 몇 가지 의미가 있습니다. 2의 거듭 제곱인 사이드 길이를 가진 텍스처만 지원하고 셰이더의 길이는 매우 제한적입니다. 이 프로파일은 주로 오래된 데스크탑 컴퓨터에서 발견됩니다.

BASELINE

모바일 장치에서 찾을 수 있는 최소 프로필입니다. Starling은 이 프로필을 잘 수행합니다. 2의 거듭 제곱 제한을 제거하면보다 효율적인 메모리 사용이 가능 해지고, 셰이더 프로그램의 길이는 필요에 따라 쉽게 만족됩니다.

BASLINE_EXTENDED

최대 텍스처 크기를 2048x2048에서 4096x4096 픽셀로 높이며 이는 고해상도 장치에 중요합니다.

STANDARD_CONSTRAINED, STANDARD, STANDARD_EXTENDED

Starling은 현재 이러한 프로파일과 함께 제공되는 기능을 필요로 하지 않습니다. 이것들은 추가적인 쉐이더 명령과 다른 낮은 레벨의 향상을 제공합니다.

나의 추천: 단순히 Starling에게 가장 유용한 프로필 (자동)을 선택하고 그 의미를 다루도록 하십시오.

최대 텍스처 크기

여러분 스스로를 돌보는 데 필요한 것은 단 하나뿐입니다. 텍스처가 너무 크지 않도록 하십시오. 최대 텍스처 크기는 Texture.maxSize 속성을 통해 액세스 할 수 있지만 Starling의 초기화가 완료된 후에만 가능합니다.

2.1.3. 네이티브 오버레이(Native Overlay)

Starling의 기본 아이디어는 Stage3D 기반 API로 렌더링 속도를 높이는 것입니다. 그러나 고전적인 디스플레이 리스트에는 Starling이 제공 할 수 없는 많은 기능이 있습니다. 따라서 Starling과 고전적인 Flash의 기능을 쉽게 조합하여 사용할 수 있습니다.

nativeOverlay 속성은 이렇게 하는게 가장 쉬운 방법입니다. 이것은 Starling 위에 직접 배치되는 viewPort 및 contentScaleFactor를 고려한 일반적인 flash.display.Sprite입니다. 기존의 Flash 객체를 사용해야 하는 경우 이 객체를 이 오버레이에 추가합니다.

하지만 Stage3D를 기반으로 하는 기존의 Flash 컨텐츠는 일부 (모바일) 플랫폼에서 성능 저하를 초래할 수 있습니다. 따라서 더 이상 필요하지 않은 경우 항상 오버레이에서 모든 객체를 제거하십시오.

질문하기 전에: 아니오. Starling 표시 객체 아래에 기존 표시 객체를 추가할 수 없습니다. Stage3D 서피스는 항상 맨 아래에 있습니다. 그 주위에는 방법이 없습니다.

2.1.4. 변경되지 않은 프레임 건너 뛰기

놀랍게도 종종 장면이 여러 프레임에 대해 완전히 정적인 상태로 유지되는 응용 프로그램이나 게임에서 발생합니다. 응용 프로그램은 정적 화면을 표시하거나 사용자 입력을 기다릴 수 있습니다. 왜 그런 상황에서 스테이지를 다시 그리는가?

이것이 바로 skipUnchangedFrames 속성의 핵심입니다. 활성화된 경우 정적 장면은 그대로 인식되고 백 버퍼는 그대로 왼쪽에 있습니다. 모바일 장치에서는 이 기능의 영향을 과소 평가할 수 없습니다. 배터리 수명을 향상시킬 수 있는 더 좋은 방법은 없습니다!

이미 귀하의 이의 제기를 듣고 있습니다. 이 기능이 유용하다면 왜 기본적으로 활성화되지 않는 것입니까? 캐치가 있어야 합니다. 그렇죠?

Render와 VideoTextures에서는 잘 작동하지 않습니다. 이러한 텍스처의 변경 사항은 표시되지 않습니다. 그것들을 사용하는 동안 skipUnchangedFrames를 일시적으로 사용하지 않도록 설정하거나 콘텐츠가 변경될 때마다 stage.setRequiresRedraw()를 호출하는 방법도 있습니다.

이제 이 기능에 대해 알았으니 항상 활성화시켜야 합니다. 향후의 Starling 버전에서 위에 언급된 문제를 해결할 수 있기를 바랍니다.

모바일 플랫폼에서는 주의해야 할 또 다른 제한 사항이 있습니다. 즉, 기본 (플래시) 단계 (예 Starling의 nativeOverlay를 통한)에 콘텐츠가 있는 경우 Starling은 어떤 프레임도 건너뛸 수 없습니다. 이것이 Stage3D 제한의 결과입니다.

2.1.5. 통계 표시

응용 프로그램을 개발할 때는 가능한 한 많은 정보를 원합니다. 그렇게 하면 문제를 조기에 발견하고 나중에 막다른 길을 피할 수 있습니다. 이를 위해 통계 표시가 도움이 됩니다.

_starling.showStats = true;
The statistics display
Figure 4. 통계 표시 (기본적으로는 좌상단).

그 값들의 의미는 무엇입니까?

  • framerate은 이름 그대로입니다: Starling이 이전 초 동안 렌더링 할 수 있었던 프레임의 수.

  • 표준 메모리는 간단히 말해서 AS3 오브젝트가 채우는 내용입니다. 문자열, 스프라이트, 비트 맵 또는 함수이건 간에 모든 객체는 약간의 메모리가 필요합니다. 값은 메가 바이트 단위로 표시됩니다. The value is given in megabytes.

  • GPU 메모리는 그것과 별개입니다. 텍스쳐는 버텍스 버퍼와 쉐이더 프로그램처럼 그래픽 메모리에 저장됩니다. 대부분의 경우 텍스처가 다른 모든 것을 덮어 씁니다.

  • 드로우 콜(draw call) 수는 각 프레임의 GPU로 전송되는 개별 "draw" 명령 수를 나타냅니다. 일반적으로 드로우 콜 수가 적으면 씬이 더 빨리 렌더링 됩니다. '[Performance Optimization], 성능 최적화’에 대해 이야기할 때 이 값으로 자세히 살펴보겠습니다.

통계 화면의 배경색이 검은 색과 진한 녹색으로 번갈아 나타납니다. 이것은 skipUnchangedFrames 속성을 참조하는 미묘한 단서입니다. 마지막 두 프레임의 대부분을 건너 뛸 수 있을 때마다 상자가 녹색으로 바뀝니다. 무대가 정지할 때마다 녹색으로 유지되는지 확인하십시오. 그렇지 않은 경우 일부 논리가 프레임 건너 뛰기가 시작되는 것을 방지합니다.

showStatsAt 메소드를 통해 화면에 통계 표시 위치를 사용자 정의할 수 있습니다.

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

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

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

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.2. 표시 목록 (Display List)

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

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

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

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

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

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

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

DisplayObjectContainer와 Sprite

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

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

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
Figure 6. 이미지와 텍스트필드로 구성된 말풍선.

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

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는 자식을 조작하는데 도움이 되는 여러 메서드를 정의합니다:

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.2.3. 좌표계

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

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

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

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

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

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

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

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

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

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

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

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

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

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의 인스턴스를 만들고 이를 디스플레이 트리에 추가하기만 하면 됩니다:

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

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

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

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

msgBox.removeFromParent();
msgBox.dispose();

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

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

msgBox.removeFromParent(true);

2.2.6. 피벗 포인트

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

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

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

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 스프라이트를 회전하면 이미지가 가운데로 회전합니다.

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

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
Figure 9. 피벗 점의 이동에 따라 객체가 어떻게 회전하는지 알아보십시오.

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

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

편리하죠?

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

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픽셀 넓이입니다. 상위 좌표계에서는 더 넓게 나타납니다.

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

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

2.3. 텍스쳐와 이미지(Textures & Images)

우리는 Image와 Texture 클래스를 여러번 보았습니다. 사실 Starling에서 가장 유용한 클래스 중 하나입니다. 그러나 그것들은 어떻게 사용되며 둘의 차이점은 무엇일까요?

2.3.1. 텍스쳐

텍스처는 디지털 카메라에 저장된 파일과 같이 이미지를 설명하는 데이터일 뿐입니다. 이 파일 하나로는 아무것도 보여줄 수 없습니다. 결국 0과 1 뿐입니다. 이미지 뷰어로 보거나 프린터로 출력해야 합니다.

텍스처는 GPU 메모리에 직접 로드되므로 렌더링 중에 매우 효율적으로 액세스할 수 있습니다. 일반적으로 임베디드 클래스에서 생성되거나 파일에서 로드됩니다. 다음 파일 형식 중 하나를 선택할 수 있습니다.

PNG

가장 다재다능합니다. 무손실 압축은 넓은 영역의 단색 이미지가 특히 적합합니다. 기본 텍스처 형식으로 권장됩니다.

JPG

손실 인코딩 방식 덕분에 사진 (및 사진 같은) 이미지 용으로 PNG보다 작은 파일을 생성합니다. 그러나 알파 채널이 없기 때문에 제한적입니다. 사진 및 큰 배경 이미지에만 권장됩니다.

ATF

특별히 Stage3D 용으로 만든 형식. ATF 텍스처는 텍스처 메모리가 거의 필요하지 않으며 매우 빠르게 로드됩니다. 그러나 손실 압축은 모든 종류의 이미지에 완벽하게 적합하지 않습니다. 우리는 ATF 텍스처를 이후 장에서 더 자세히 살펴볼 것입니다 ('[ATF Textures], ATF 텍스처' 참조).

starling.textures.Texture 클래스는 텍스처를 인스턴스화하는 데 사용되는 많은 팩토리 메소드를 포함합니다. 다음은 그 중 몇 가지입니다 (명확성을 위해 논쟁은 생략합니다).

public class Texture
{
    static function fromColor():Texture;
    static function fromBitmap():Texture;
    static function fromBitmapData():Texture;
    static function fromEmbeddedAsset():Texture;
    static function fromCamera():Texture;
    static function fromNetStream():Texture;
    static function fromTexture():Texture;
}

아마도 가장 일반적인 작업은 비트맵에서 텍스처를 만드는 것입니다. 쉽진 않겠지만…​:

var bitmap:Bitmap = getBitmap();
var texture:Texture = Texture.fromBitmap(bitmap);

또한 임베디드 비트맵에서 텍스처를 만드는 것이 매우 일반적입니다. 그건 똑같은 방법으로 할 수 있어요:

[Embed(source="mushroom.png")] (1)
public static const Mushroom:Class;

var bitmap:Bitmap = new Mushroom(); (2)
var texture:Texture = Texture.fromBitmap(bitmap); (3)
1 비트맵을 임베드하세요.
2 비트맵을 인스턴스화 하세요.
3 비트맵에서 텍스처를 만듭니다.

그러나, 이를 더 단순화하는 지름길이 있습니다:

[Embed(source="mushroom.png")] (1)
public static const Mushroom:Class;

var texture:Texture = Texture.fromEmbeddedAsset(Mushroom); (2)
1 비트맵을 임베드하세요.
2 임베디드 애셋을 저장하는 클래스에서 바로 텍스처를 만듭니다.
전문가의 팁

이것은 코드가 적을뿐만 아니라 메모리도 적게 소요됩니다!

fromEmbeddedAsset 메서드는 문맥상 손실을 막기 위해 씬 배후에 있는 마법을 사용하며 Bitmap 메서드에서 수행할 수 있는 기존의 것보다 더 효율적입니다. 나중에 이 주제로 다시 돌아갈 것이지만 지금은 이것이 임베디드 비트맵에서 텍스처를 생성하는 기본 방법임을 기억하십시오.

Texture 클래스의 또 다른 기능은 숨겨져 있습니다. fromTexture 메서드를 이용하면 다른 텍스처 내의 영역을 가리키는 텍스처를 설정할 수 있습니다.

이 점을 유용하게 만드는 것은 픽셀이 이 과정에서 복사되지 않는다는 사실입니다. 대신 생성된 SubTexture는 상위 텍스처에 대한 참조만 저장합니다. 이것은 매우 효율적입니다!

var texture:Texture = getTexture();
var subTexture:Texture = Texture.fromTexture(
        texture, new Rectangle(10, 10, 41, 47));

곧 TextureAtlas 클래스에 대해 알게될 것입니다. 이것은 기본적으로 이 기능을 중심으로 구축되었습니다.

2.3.2. 이미지

우리는 아직 두 개의 텍스처를 스크린에 표시하는 방법을 배우지 않았습니다. 가장 쉬운 방법은 Image 클래스 또는 그 사촌 중 하나를 사용하는 것입니다.

클래스 가계도의 일부분을 확대합시다.

mesh classes
  • Mesh는 삼각형의 평면 컬렉션입니다 (GPU는 삼각형 만 그릴 수 있음을 기억하십시오).

  • 쿼드는 사각형을 생성하는 적어도 두 개의 삼각형의 모음입니다.

  • 이미지는 편리한 생성자와 몇 가지 추가 메서드가 있는 쿼드입니다.

  • MovieClip은 시간이 지남에 따라 텍스처를 전환하는 이미지입니다.

이모든 클래스는 텍스처를 처리할 수 있도록 준비되어 있지만 Image 클래스를 사용하는 것이 가장 일반적입니다. 직사각형 텍스처가 가장 보편적이기 때문에 간단합니다. Image 클래스가 가장 편리한 방법입니다.

시연하기 위해 Quad 대 Image로 텍스처를 표시하는 방법을 보여 드리겠습니다:

var texture:Texture = Texture.fromBitmap(...);

var quad:Quad = new Quad(texture.width, texture.height); (1)
quad.texture = texture;
addChild(quad);

var image:Image = new Image(texture); (2)
addChild(image);
1 적절한 크기의 쿼드를 만들고 텍스처를 지정하거나 또는:
2 표준 생성자로 이미지를 만듭니다.

개인적으로 나는 항상 나에게 더 많은 키 스트로크를 저장하는 접근 방식을 선택했습니다. 그러나 뒤에서 일어나는 일은 두 경우 모두 동일합니다.

Texture-Mapping
Figure 10. 텍스처가 쿼드에 매핑됩니다.

2.3.3. 하나의 텍스쳐, 여러개의 이미지

텍스처가 여러 이미지(메쉬)에 매핑될 수 있다는 점에 유의해야 합니다. 실제로 텍스처가 한 번만 로드된 다음 응용 프로그램의 수명이 다할 때까지 다시 사용하십시오.

// 이렇게 하지 마세요!!
var image1:Image = new Image(Texture.fromEmbeddedAsset(Mushroom));
var image2:Image = new Image(Texture.fromEmbeddedAsset(Mushroom));
var image3:Image = new Image(Texture.fromEmbeddedAsset(Mushroom));

// 대신 텍스처를 한 번 만들고 참조를 유지하십시오:
var texture:Texture = Texture.fromEmbeddedAsset(Mushroom));
var image1:Image = new Image(texture);
var image2:Image = new Image(texture);
var image3:Image = new Image(texture);

거의 모든 메모리 사용량은 텍스처에서 나옵니다. 텍스처 메모리를 낭비할 경우 RAM이 빨리 소모됩니다.

2.3.4. 텍스처 아틀라스 (Texture Atlases)

모든 이전 샘플에서는 각 텍스처를 별도로 로드했습니다. 그러나 실제 응용 프로그램에서는 그렇게 하지 말아야 합니다. 이유가 여기 있습니다.

  • 효율적인 GPU 렌더링을 위해 Starling은 렌더링된 메쉬를 일괄 처리합니다. 그러나 텍스처가 변경될 때마다 일괄 처리는 중단되죠.

  • 상황에 따라 Stage3D에서는 폭과 높이가 2의 배수가 되는 텍스처가 필요합니다. Starling은 이 제한을 숨기지만 그 규칙을 따르지 않으면 더 많은 메모리를 사용하게 됩니다.

텍스처 아틀라스를 사용하면 텍스처 스위치와 2배수의 제한 제한을 피할 수 있습니다. 모든 텍스처는 하나의 큰 "수퍼 텍스처" 내에 있고 Starling은 이 텍스처의 올바른 부분이 표시되도록 합니다.

Texture Atlas
Figure 11. 텍스처 아틀라스.

트릭은 Stage3D가 작은 텍스처 대신 이 큰 텍스처를 사용하고 렌더링된 각 쿼드에 그 중 일부만 매핑하는 것입니다. 이렇게 하면 매우 효율적인 메모리 사용으로 이어져 가능한 한 최소한의 공간만 낭비하게 됩니다. (다른 프레임 워크에서는 이 기능을 스프라이트 시트라고 부릅니다.)

"Texture Packer" 팀은 실제로 스프라이트 시트에 대한 멋진 소개 비디오를 만들었습니다. 여기에서 확인: 스프라이트 시트란 무엇인가?
아틀라스 만들기

각 SubTexture의 위치는 이와 같은 XML 파일에 정의됩니다:

<TextureAtlas imagePath="atlas.png">
 <SubTexture name="moon" x="0" y="0" width="30" height="30"/>;
 <SubTexture name="jupiter" x="30" y="0" width="65" height="78"/>;
 ...
</TextureAtlas>;

보시다시피 XML은 하나의 큰 텍스처를 참조하고 각각 이 해당 텍스처 내의 영역을 가리키는 여러 개의 명명 된 하위 텍스처를 정의합니다. 런타임에 이 하위 텍스처를 이름으로 참조할 수 있으며 마치 독립적인 텍스처인 것처럼 작동합니다.

그런데 어떻게 모든 텍스처를 그러한 아틀라스에 결합합니까? 고맙게도 수동으로 할 필요는 없습니다. 그 일로 당신을 도울 도구가 많이 있습니다. 여기에 두 명의 후보자가 있지만 Google에서 검색하면 많이 나올 것입니다.

  • TexturePacker 제가 즐겨쓰는 것입니다. 어떤 것보다 스프라이트 시트에 대한 많은 제어가 가능합니다. Starling 지원이 뛰어납니다 (ATF 텍스처뿐 아니라 뭐든지?).

  • Shoebox 는 AIR로 제작 된 무료 도구입니다. TexturePacker로 아틀라스를 만들 수있는 옵션은 많지 않지만 비트맵 글꼴 생성이나 스프라이트 추출과 같은 많은 관련 기능이 포함되어 있습니다.

아틀라스 사용하기

좋아요. 여러분에게 텍스쳐 아틀라스가 있습니다. 그런데 어떻게 사용하죠? 텍스처와 XML 데이터를 임베드하는 것으로 시작해 보겠습니다:

[Embed(source="atlas.xml", mimeType="application/octet-stream")] (1)
public static const AtlasXml:Class;

[Embed(source="atlas.png")] (2)
public static const AtlasTexture:Class;
1 아틀라스 XML을 임베드하십시오. mimeType을 지정하는 것을 잊지 마십시오.
2 아틀라스 텍스처를 삽입합니다.
또는 URL이나 디스크에서 이러한 파일을 로드할 수도 있습니다 (AIR의 경우). Starling의 AssetManager에 대해 자세히 살펴보겠습니다.

사용 가능한 두 객체를 사용하여 새로운 TextureAtlas 인스턴스를 만들고 getTexture() 메서드를 통해 모든 하위 텍스처에 액세스할 수 있습니다. 게임이 초기화되고 끝까지 참조될 아틀라스 개체를 만듭니다.

var texture:Texture = Texture.fromEmbeddedAsset(AtlasTexture); (1)
var xml:XML = XML(new AtlasXml());
var atlas:TextureAtlas = new TextureAtlas(texture, xml);

var moonTexture:Texture = atlas.getTexture("moon"); (2)
var moonImage:Image = new Image(moonTexture);
1 아틀라스를 만듭니다.
2 SubTexture를 표시합니다.

매우 간단합니다!

2.3.5. 텍스처 렌더링

RenderTexture 클래스를 사용하면 텍스처를 동적으로 생성할 수 있습니다. 모든 디스플레이 오브젝트를 칠할 수 있는 캔버스라고 생각하십시오.

렌더링 텍스처를 만든 후에는 drawObject 메서드를 호출하여 객체를 텍스처에 직접 렌더링합니다. 객체는 현재 위치의 텍스처에 그려지며 현재 회전 크기 및 알파 속성을 유지합니다.

var renderTexture:RenderTexture = new RenderTexture(512, 512); (1)

var brush:Sprite = getBrush(); (2)
brush.x = 40;
brush.y = 120;
brush.rotation = 1.41;

renderTexture.draw(brush); (3)
1 지정된 크기(포인트 단위)로 새로운 RenderTexture를 만듭니다. 완전한 투명 픽셀로 초기화됩니다.
2 이 샘플에서는 브러시를 묘사한 디스플레이 오브젝트를 참조합니다. 그것을 특정 위치로 옮깁니다.
3 브러시 객체는 현재 위치 및 방향 설정으로 텍스처에 그려집니다.

그리기는 그래픽 메모리에서 직접 일어나므로 매우 효율적으로 수행됩니다. 텍스처 위에 오브젝트를 그린 후에는 얼마나 많은 오브젝트를 그려 넣었는지에 관계없이 일반 텍스처와 비슷한 성능을 얻을 수 있습니다:

var image:Image = new Image(renderTexture);
addChild(image); (1)
1 텍스처는 다른 텍스처와 마찬가지로 사용할 수 있습니다.

한 번에 많은 객체를 그릴 경우 아래와 같이 Bundled draw 메서드를 통해 블록에 도면 호출을 묶는 것이 좋습니다. 이를 통해 Starling은 비용이 많이 드는 작업을 약간 건너 뛸 수있어 프로세스 속도가 상당히 빨라졌습니다:

renderTexture.drawBundled(function():void (1)
{
    for (var i:int=0; i<numDrawings; ++i)
    {
        image.rotation = (2 * Math.PI / numDrawings) * i;
        renderTexture.draw(image); (2)
    }
});
1 함수에서 모든 드로우 콜을 캡슐화하여 묶음 도면을 활성화합니다.
2 함수 내부에서 이전처럼 'draw’를 호출합니다.

렌더 텍스처의 일부를 지우려면 블렌드 모드를 BlendMode.ERASE로 설정하여 "지우개"와 같이 디스플레이 오브젝트를 사용할 수 있습니다.

brush.blendMode = BlendMode.ERASE;
renderTexture.draw(brush);

깨끗하게 지우려면 clear 메소드를 사용하십시오.

컨텍스트 손실

불행히도 렌더링 텍스처는 하나의 큰 단점을 가지고 있습니다. 렌더링 컨텍스트가 손실되면 모든 내용을 잃게됩니다. '[Context Loss]' 는 다음 장에서 자세히 설명합니다. 요컨대 특정 상황에서 Stage3D가 모든 버퍼의 내용을 잃을 수 있음을 의미합니다. (네. 발음만큼 꽤 불쾌합니다.)

따라서, 텍스처의 내용이 계속 유지되는 것이 중요하다면 (즉 눈가림만이 아니라) 몇 가지 조치를 취해야 합니다. 우리는 언급된 장에서 가능한 전략들을 조사할 것입니다 - 제가 이 사실을 여기에서 언급했기 때문에 당신은 덜 놀라게 될 것입니다.

2.4. 다이나믹 텍스트(Dynamic Text)

텍스트는 모든 응용 프로그램의 중요한 부분입니다. 이미지로 많은 정보를 전달할 수 있지만, 런타임시 동적으로 단어로 설명할 필요가 있기 때문입니다.

2.4.1. 텍스트필드(TextFields)

Starling을 사용하면 동적 텍스트를 쉽게 표시할 수 있습니다. TextField 클래스는 아주 자명해야 합니다!

var textField:TextField = new TextField(100, 20, "text"); (1)
textField.format.setTo("Arial", 12, Color.RED); (2)
textField.format.horizontalAlign = Align.RIGHT; (3)
textField.border = true; (4)
1 "text"라는 텍스트를 표시하는 100x20 포인트 크기의 TextField를 만듭니다.
2 "Arial" 폰트를 12 포인트의 크기와 빨간색으로 설정합니다.
3 텍스트가 오른쪽으로 정렬됩니다.
4 border 속성은 개발 과정에서 주로 유용합니다 TextField의 경계를 보여줍니다.
텍스트의 스타일은 starling.text.TextFormat 인스턴스를 가리키는 format 속성을 통해 설정됩니다.

일단 만들어지면 이미지나 쿼드를 사용하는 것처럼 TextField를 사용할 수 있습니다.

TextField Samples
Figure 12. Starling의 텍스트 렌더링 기능에 대한 몇 가지 샘플.

2.4.2. 트루타입(TrueType) 폰트

기본적으로 Starling은 시스템 폰트를 사용하여 텍스트를 렌더링합니다. 예를 들어 "Arial"을 사용하도록 TextField를 설정하면 시스템에 설치된 폰트를 사용합니다 (설치되어 있는 경우).

그러나 이 방법의 렌더링 품질은 최적이 아닙니다. 예를 들어 폰트가 앤티 앨리어싱 없이 렌더링될 수 있습니다.

더 나은 출력을 위해 트루 타입 폰트를 SWF 파일에 직접 포함시켜야합니다. 다음 코드를 사용하면 됩니다:

[Embed(source="Arial.ttf", embedAsCFF="false", fontFamily="Arial")]
private static const Arial:Class; (1)

[Embed(source="Arial Bold.ttf", embedAsCFF="false", fontFamily="Arial", fontWeight="bold")]
private static const ArialBold:Class; (2)

[Embed(source="Arial Italic.ttf", embedAsCFF="false", fontFamily="Arial", fontStyle="italic")]
private static const ArialItalic:Class; (3)

[Embed(source="Arial.ttf", embedAsCFF="false", fontFamily="Arial", unicodeRange = "U+0020-U+007e")]
private static const ArialJustLatin:Class; (4)
1 표준 Arial 폰트를 포함합니다. embedAsCFF 부분에 주목하세요: 건너 뛰지 마십시오! 그렇지 않으면 폰트가 표시되지 않습니다.
2 굵게 및 기울임 꼴은 별도로 삽입해야 합니다. 여기서 fontWeight 속성을 주목하십시오.
3 또한 fontStyle 속성을 참조하십시오.
4 포함할 글리프를 정의할 수 있습니다. 모든 유니 코드 문자가 필요하지 않은 경우 큰 폰트에 유용합니다. 여기에 표시된 범위는 기본 라틴어 (대문자, 소문자, 숫자 및 공통 기호 / 문장 부호)입니다.

폰트를 포함하고 나면 해당 폰트 이름 (폰트 모음, Font family)과 가중치로 설정된 모든 TextField가 자동으로 사용됩니다. 설정하거나 구성할 수있는 다른 방법은 없습니다.

폰트의 모든 문자 모양을 포함할 때 큰 공간을 주의하십시오. 위에 표시된 "unicodeRange"는 이 문제를 완화합니다. 예를 들어 범위를 생성 할 수 있습니다. 유니 코드 범위 생성기.
텍스트가 잘리거나 올바른 위치에 나타나지 않으면 현재 스테이지를 살펴보십시오. 품질 설정. 품질 값이 낮으면 Flash / AIR에서 텍스트 범위와 관련된 잘못된 값을 보고하고 Starling은 텍스트를 그릴 때 그 값에 의존합니다. (여기서는 Flash 스테이지에 대해 말하고 있으며 이는 TrueType 폰트에만 적용됩니다.)

2.4.3. 비트맵 폰트(Bitmap Fonts)

위에 표시된 것처럼 트루 타입 폰트를 사용하는건 자주 변경되지 않는 텍스트에 적합합니다. 그러나 TextField가 내용을 계속 변경하거나 TrueType 형식으로 제공되지 않는 멋진 폰트를 표시하려면 비트맵 폰트를 사용해야 합니다.

비트맵 폰트는 표시하려는 모든 문자가 포함된 텍스처입니다. TextureAtlas와 마찬가지로 XML 파일은 텍스처 안에 글리프의 위치를 저장합니다.

이것은 Starling이 비트맵 폰트를 렌더링하는 데 필요한 모든 것입니다. 필요한 파일을 만들려면 몇 가지 옵션이 있습니다:

  • Littera, 모든 기능을 갖춘 무료 온라인 비트 맵 폰트 생성기.

  • Bitmap Font Generator, AngelCode에서 제공하는 도구로 트루 타입 폰트에서 비트맵 폰트를 만들 수 있습니다. 그러나 Windows에서만 사용할 수 있습니다.

  • Glyph Designer MacOS 전용, 멋진 특수 효과를 폰트에 추가할 수있는 훌륭한 도구입니다.

  • bmGlyph, 또한 MacOS 전용으로 App Store에서 사용할 수 있습니다.

위 도구들은 모두 사용법이 유사하므로 시스템 폰트 중 하나를 선택하고 선택적으로 특수 효과를 적용할 수 있습니다. 익스포트 할 때 고려해야 할 사항이 몇 가지 있습니다:

  • Starling에는 ".fnt"형식의 XML 변형이 필요합니다.

  • 올바른 글리프 세트를 선택해야 합니다. 그렇지 않으면 폰트 질감이 매우 커집니다.

결과는 ".fnt"파일과 문자가 포함된 관련 텍스처입니다.

Bitmap Font
Figure 13. 색상 및 그림자가 포함 된 비트 맵 폰트.

이러한 폰트를 Starling에서 사용할 수 있도록 하려면 SWF에 포함하고 TextField 클래스에 등록할 수 있습니다.

[Embed(source="font.png")]
public static const FontTexture:Class;

[Embed(source="font.fnt", mimeType="application/octet-stream")]
public static const FontXml:Class;

var texture:Texture = Texture.fromEmbeddedAsset(FontTexture);
var xml:XML = XML(new FontXml());
var font:BitmapFont = new BitmapFont(texture, xml); (1)

TextField.registerCompositor(font); (2)
1 BitmapFont 클래스의 인스턴스를 만듭니다.
2 TextField 클래스에 폰트를 등록합니다.

비트맵 폰트 인스턴스가 TextField 클래스에 등록되면 더 이상 필요하지 않습니다. Starling은 해당 이름의 폰트를 사용하는 TextField를 발견하면 해당 폰트를 선택합니다. 여기처럼:

var textField:TextField = new TextField(100, 20, "Hello World");
textField.format.font = "fontName"; (1)
textField.format.fontSize = BitmapFont.NATIVE_SIZE; (2)
1 폰트를 사용하려면 폰트 이름을 참조하십시오. 기본적으로 XML 파일의 얼굴 속성에 저장됩니다.
2 비트맵 폰트는 폰트 텍스처를 만드는 데 사용된 것과 동일한 크기로 표시 될 때 가장 잘 보입니다. 수동으로 그 크기를 할당할 수 있습니다. 그러나 NATIVE_SIZE 상수를 통해 Starling이 이를 수행하게하는 것이 더 좋습니다.
더 알아보기

꼭알아야 할 것이 한 가지 더 있습니다. 비트맵 폰트가 단일 색상 (예: 일반 트루 타입 폰트 색상 효과 없음)을 사용하는 경우 글리프를 순수한 흰색으로 내보내야 합니다. 그런 다음 TextField의 format.color 속성을 사용하여 런타임에 폰트를 임의의 색상으로 채울 수 있습니다 (텍스처의 RGB 채널을 곱하는 것만으로).

반면에 폰트에 위의 샘플 이미지와 같은 색상이 포함되어 있으면 TextField의 format.color 속성을 흰색 (Color.WHITE)으로 설정해야 합니다. 이렇게하면 TextField의 색조가 텍스처 색에 영향을 미치지 않습니다.

최적의 성능을 위해 텍스쳐 아틀라스에 폰트 텍스처를 추가할 수도 있습니다! 이렇게 하면 텍스트를 일반 이미지와 함께 배치하여 그리기 호출을 더 줄일 수 있습니다.
MINI 폰트

Starling에는 실제로 매우 가벼운 비트맵 폰트 하나가 포함되어 있습니다. 그것은 아마도 어떤 미인 대회에서도 이기지 못할 것입니다. 하지만 프로토 타입에 텍스트를 표시해야 하거나 디버그 출력을 위해 필요할 때 완벽합니다.

BitmapFont.MINI
Figure 14. "MINI" 비트맵 폰트.

내가 'lightweight’라고 말할 때는 각 글자가 5픽셀이라는걸 의미합니다. 하지만 트릭을 사용하면 원래 크기의 200%까지 확장할 수 있습니다.

var textField:TextField = new TextField(100, 10, "The quick brown fox ...");
textField.format.font = BitmapFont.MINI; (1)
textField.format.fontSize = BitmapFont.NATIVE_SIZE * 2; (2)
1 MINI 폰트를 사용하십시오.
2 네이티브 크기의 정확히 두 배를 사용하십시오. 폰트는 가장 가까이 이웃한 스케일링을 사용하므로 선명하게 유지됩니다!

2.5. 이벤트 핸들링

이벤트는 프로그래머가 관심 있을만한 모든 종류의 사건으로 생각할 수 있습니다.

  • 예를 들어 모바일 앱에서 기기 방향이 변경되었거나 사용자가 화면을 터치한 것을 알릴 수 있습니다.

  • 낮은 레벨에서, 버튼이 트리거되었다는 것을 나타낼 수도 있고, 기사(knight, 게임에서)가 에너지를 모두 소모했다는 것을 나타낼 수도 있습니다.

그것이 Starling의 이벤트 메커니즘입니다.

2.5.1. Motivation

Starling의 이벤트 메커니즘 아키텍처의 핵심 기능입니다. 간단히 말해서, 이벤트 개체가 서로 통신할 수 있습니다.

당신은 생각할 수 있습니다: 우리는 이미 그에 대한 메커니즘을 가지고 있습니다 - 바로 메소드! 네, 그건 사실입니다. 하지만 메소드는 한 방향으로만 작동합니다. 예를 들어 버튼을 포함한 MessageBox를 보세요.

messagebox calls button

메시지 박스는 버튼을 소유합니다. 그래서 버튼의 메소드와 속성을 사용할 수 있습니다:

public class MessageBox extends DisplayObjectContainer
{
    private var _yesButton:Button;

    private function disableButton():void
    {
        _yesButton.enabled = false; (1)
    }
}
1 속성을 통해 버튼과 통신합니다.

한편 Button 인스턴스는 메시지 상자에 대한 참조를 소유하지 않습니다. 결국 버튼은 모든 구성 요소에서 사용할 수 있습니다. MessageBox 클래스와 완전히 독립적입니다. 다른 점은 메시지 상자 안의 버튼만 사용할 수 있다는 것입니다. 이런!

여전히: 버튼이 거기 있는 이유가 있습니다 - 버튼이 눌려지면, 누군가에게 그 사실을 알릴 필요가 있습니다! 즉 버튼의 소유자인 주인이라면 누구나 메시지를 받을 수 있어야 합니다.

button dispatches to messagebox

2.5.2. 이벤트와 이벤트 디스패쳐(Event & EventDispatcher)

고백할 부분이 있습니다. Starling의 표시 객체 클래스 계층을 표시할 때 실제 기본 클래스인 EventDispatcher가 생략되었습니다.

class hierarchy with eventdispatcher

이클래스는 모든 표시 객체에 이벤트를 전달하고 처리할 수 있는 수단을 제공합니다. 모든 표시 객체가 EventDispatcher에서 상속받는 것은 우연이 아닙니다. Starling에서는 이벤트 시스템이 표시 목록과 긴밀하게 통합되어 있습니다. 여기에는 나중에 우리가 보게될 몇 가지 장점이 있습니다.

이벤트는 예제를 통해 가장 잘 설명됩니다.

개가 있다고 잠시 상상해보십시오. 그를 아인슈타인이라고 부르죠. 매일 몇 차례 아인슈타인은 당신에게 그가 산책하러 나가길 원한다는 것을 알려줄 것입니다. 그는 짖는 소리로 그것을 알립니다.

class Dog extends Sprite
{
    function advanceTime():void
    {
        if (timeToPee)
        {
            var event:Event = new Event("bark"); (1)
            dispatchEvent(event); (2)
        }
    }
}

var einstein:Dog = new Dog();
einstein.addEventListener("bark", onBark); (3)

function onBark(event:Event):void (4)
{
    einstein.walk();
}
1 문자열 "bark" 소리가 이벤트를 식별합니다. Event 인스턴스에 캡슐화되어 있습니다.
2 이벤트 발신(Dispatching event)은 짖는 이벤트에 가입한 모든 사람들에게 이벤트를 전달합다.
3 addEventListener를 호출하여 구독합니다. 첫 번째 인수는 이벤트 유형이고 두 번째 인수는 리스너 (함수)입니다.
4 개가 짖는 경우 이 메소드는 이벤트를 매개 변수로 호출됩니다.

방금 이벤트 메커니즘의 세 가지 주요 구성 요소를 보았습니다:

  • 이벤트는 Event 클래스 (또는 그 하위 클래스)의 인스턴스에 캡슐화 됩니다.

  • 이벤트를 발신하려면 보낸 사람이 이벤트 인스턴스를 전달하여 dispatchEvent를 호출합니다.

  • 이벤트를 수신하기 위해 클라이언트는 관심있는 이벤트 유형과 호출할 함수 또는 메소드를 나타내는 addEventListener를 호출합니다.

때때로 숙모도 개를 보살펴줍니다. 그런 일이 생기면 개가 짖는 소리를 하지 않아도 됩니다. 숙모는 그녀가 무엇을 신청했는지 알고 있습니다! 그래서 개 주인만을 위한 좋은 연습뿐만 아니라 Starling 개발자를 위한 이벤트 리스너를 제거합니다.

einstein.removeEventListener("bark", onBark); (1)
einstein.removeEventListeners("bark"); (2)
1 onBark 리스너를 제거합니다.
2 해당 타입의 모든 리스너를 제거합니다.

bark 이벤트가 너무 많네요. 물론 아인슈타인은 몇 가지 다른 이벤트 유형, 예를 들어 울부 짖거나(howl) 으르렁대는(growl) 이벤트를 디스패치할 수 있습니다. 정적 정수에 이러한 문자열을 저장하는 것이 좋습니다. Dog 클래스에 곧바로.

class Dog extends Sprite
{
    public static const BARK:String = "bark";
    public static const HOWL:String = "howl";
    public static const GROWL:String = "growl";
}

einstein.addEventListener(Dog.GROWL, burglar.escape);
einstein.addEventListener(Dog.HOWL, neighbor.complain);

Starling은 Event 클래스에서 몇 가지 매우 유용한 이벤트 유형을 미리 정의합니다. 다음은 가장 인기있는 것 중 하나입니다:

  • Event.TRIGGERED: 버튼이 트리거 된(탭) 경우

  • Event.ADDED: 디스플레이 오브젝트가 컨테이너에 추가된 경우

  • Event.ADDED_TO_STAGE: 스테이지에 연결된 컨테이너에 디스플레이 오브젝트가 추가된 경우

  • Event.REMOVED: 디스플레이 오브젝트가 컨테이너에서 제거된 경우

  • Event.REMOVED_FROM_STAGE: 디스플레이 오브젝트가 스테이지에 연결되지 않은 경우

  • Event.ENTER_FRAME: 새 프레임이 렌더링된 경우 (나중에 알아 보겠습니다).

  • Event.COMPLETE: 무언가 완료된 경우

2.5.3. 커스텀 이벤트(Custom Events)

개는 여러 이유들에 대해 짖습니다. 그렇죠? 아인슈타인은 그가 소변을 보고 싶어하거나 그가 배고픈 것을 나타낼 수 있습니다. 고양이에게 퇴장할 시간이 많다고 말하는 방법일 수도 있습니다.

개 사람들(Dog people, 의인화)은 아마도 그 차이를 듣게 될 것입니다 (나는 고양이 인이고 그렇지 않습니다). 똑똑한 개들이 그들의 의도를 저장하는 BarkEvent를 설정하기 때문입니다.

public class BarkEvent extends Event
{
    public static const BARK:String; (1)

    private var _reason:String; (2)

    public function BarkEvent(type:String, reason:String, bubbles:Boolean=false)
    {
        super(type, bubbles); (3)
        _reason = reason;
    }

    public function get reason():Boolean { return _reason; } (4)
}
1 이벤트 유형을 사용자 정의 이벤트 클래스에 바로 저장하는 것이 좋습니다.
2 커스텀 이벤트를 만드는 이유: 정보를 함께 저장하고 싶습니다. 여기에 그 이유가 있습니다.
3 생성자에서 슈퍼 클래스를 호출합니다. (우리는 곧 버블링의 의미를 살펴볼 것입니다.)
4 속성을 통해 reason을 접근 가능하게 만든다.

개는 짖을 때 이 커스텀 이벤트를 사용할 수 있습니다:

class Dog extends Sprite
{
    function advanceTime():void
    {
        var reason:String = this.hungry ? "hungry" : "pee";
        var event:BarkEvent = new BarkEvent(BarkEvent.BARK, reason);
        dispatchEvent(event);
    }
}

var einstein:Dog = new Dog();
einstein.addEventListener("bark", onBark);

function onBark(event:BarkEvent):void (1)
{
    if (event.reason == "hungry") (2)
        einstein.feed();
    else
        einstein.walk();
}
1 매개 변수의 유형은 BarkEvent입니다.
2 그래서 우리는 이제 reason 속성에 접근하여 이에 따라 행동할 수 있습니다.

그렇게 하면 BarkEvent에 익숙한 개 소유자가 마침내 진정으로 개를 이해할 수 있게 됩니다. 해냈습니다!

2.5.4. 단순화(Simplifying)

합의: 그 reason 문자열을 전달할 수 있도록 여분의 클래스를 만드는 것은 약간 성가신 일입니다. 결국 우리가 관심을 갖는 정보의 단 하나의 부분일 뿐입니다. 간단한 메커니즘을 위한 추가 클래스를 생성하는 것은 다소 비효율적입니다.

그렇기 때문에 하위 클래스 방식을 자주 사용하지 않아도 됩니다. 대신 임의의 참조 (유형 Object)를 저장할 수 있는 Event 클래스의 data 속성을 사용할 수 있습니다.

BarkEvent 로직을 이것으로 대체하십시오:

// 이벤트 생성 및 발신
var event:Event = new Event(Dog.BARK);
event.data = "hungry"; (1)
dispatchEvent(event);

// 이벤트 수신
einstein.addEventListener(Dog.BARK, onBark);
function onBark(event:Event):void
{
    trace("reason: " + event.data as String); (2)
}
1 데이터 속성 안에 짖는 이유(reason)를 저장하십시오.
2 이유를 다시 얻으려면 데이터를 String으로 형변환 하십시오.

이방법의 단점은 형의 안정성을 잃는 것입니다. 그러나 제 의견으로는 완전한 클래스를 구현하는 것보다 String에 캐스트하는 것이 좋을 것 같습니다.

또한 Starling에는이 코드를 더 간단하게 만드는 몇 가지 단축키가 있습니다! 이걸 보세요:

// 이벤트 생성 및 발신
dispatchEventWith(Dog.BARK, false, "hungry"); (1)

// 이벤트 수신
einstein.addEventListener(Dog.BARK, onBark);
function onBark(event:Event, reason:String):void
{
    trace("reason: " + reason); (2)
}
1 Dog.BARK 유형의 이벤트를 작성하고 data 속성을 채우고 이벤트를 한 행에 디스패치 합니다.
2 data 속성은 이벤트 처리기의 두 번째 인수 (선택적)로 전달됩니다.

그렇게 보일러 플레이트 코드를 상당량 제거했습니다! 물론 사용자 정의 데이터가 필요하지 않은 경우에도 동일한 메커니즘을 사용할 수 있습니다. 가능한 가장 간단한 이벤트 상호 작용을 살펴 보겠습니다.

// 이벤트 생성 및 발신
dispatchEventWith(Dog.HOWL); (1)

// 이벤트 수신
dog.addEventListener(Dog.HOWL, onHowl);
function onHowl():void (2)
{
    trace("hoooh!");
}
1 이벤트 유형을 지정하여 이벤트를 전달하십시오.
2 이 함수에는 매개 변수가 없습니다. 필요하지 않은 경우 지정할 필요가 없습니다.
Starling이 이벤트 객체를 뒤에서 모으기 때문에 단순화 된 dispatchEventWith 호출은 실제로 훨씬 더 효율적입니다.

2.5.5. 버블링(Bubbling)

앞의 예제에서 이벤트 디스패처와 이벤트 리스너는 addEventListener 메소드를 통해 직접 연결되었습니다. 그러나 때때로 그것은 당신이 원하는 것이 아닙니다.

복잡한 디스플레이 리스트가 있는 복잡한 게임을 만들었다고 가정해 보겠습니다. 아인슈타인 (이 게임의 주인공 - 개)이 이 목록의 일부 지점에서 함정에 빠졌습니다. 그는 고통스럽고 마지막 숨을 쉬면서 GAME_OVER 이벤트를 발송합니다.

안타깝게도 이 정보는 게임의 루트 클래스에서 디스플레이 리스트까지 멀리 있어야 합니다. 이러한 이벤트에서는 일반적으로 레벨을 재설정하고 개를 마지막 저장점으로 반환합니다. 게임 루트에 도달할 때까지 수 많은 표시 개체를 통해 개에서 이 이벤트를 넘겨주는 것은 정말 성가실 것입니다.

이는 매우 일반적인 요구 사항이며 이벤트가 버블링 (bubbling)이라고 하는 것을 지원하는 이유입니다.

실제 나무 (디스플레이 목록)를 상상해보고 트렁크가 위쪽을 향하도록 180도 돌리십시오. 트렁크 그게 너의 무대이고 나무의 나뭇잎이 너의 전시 물건이야. 이제 나뭇잎이 버블링 이벤트를 만드는 경우 그 이벤트는 마침내 트렁크에 도달할 때까지 나뭇 가지에서 지점 (부모에서 부모까지)으로 이동하는 소다 잔에 있는 거품처럼 위로 이동합니다.

Bubbling
Figure 15. 이벤트는 스테이지까지 계속 거품(bubbles)을 냅니다.

이 라우트의 모든 디스플레이 오브젝트는 이 이벤트를 수신할 수 있습니다. 거품을 터뜨려 더 멀리 여행하는 것을 멈출 수도 있습니다. 그렇게 하기 위해 필요한 것은 이벤트의 bubbles 속성을 true로 설정하는 것입니다.

// 고전적 접근법:
var event:Event = new Event("gameOver", true); (1)
dispatchEvent(event);

// 한 줄 대안:
dispatchEventWith("gameOver", true); (2)
1 Event 생성자의 두 번째 매개 변수로 true를 전달하면 버블링이 활성화됩니다.
2 또는 dispatchEventWith는 똑같은 매개 변수를 사용합니다.

경로를 따라 어디에서나 이 이벤트를 들을 수 있습니다 (예: 개 부모 또는 스테이지에서):

dog.addEventListener("gameOver", onGameOver);
dog.parent.addEventListener("gameOver", onGameOver);
stage.addEventListener("gameOver", onGameOver);

이 기능은 여러 상황에서 유용합니다. 특히 마우스 또는 터치 스크린을 통한 사용자 입력과 관련이 있습니다.

2.5.6. 터치 이벤트(Touch Events)

일반적인 데스크톱 컴퓨터는 마우스로 제어되지만 스마트폰이나 태블릿과 같은 대부분의 모바일 장치는 손가락으로 제어됩니다.

Starling은 이러한 입력 방법을 통합하고 모든 "포인팅 장치" 입력을 TouchEvent로 처리합니다. 그렇게하면 게임을 제어하는 실제 입력 방법에 신경 쓸 필요가 없습니다. 입력 장치가 마우스 스타일러스 또는 손가락인지 여부에 상관없이 Starling은 항상 터치 이벤트를 전달합니다.

우선 우선 멀티 터치를 지원하려면 Starling 인스턴스를 만들기 전에 활성화해야 합니다.

Starling.multitouchEnabled = true;

var starling:Starling = new Starling(Game, stage);
starling.simulateMultitouch = true;

simulateMultitouch 속성에 주목하십시오. 이 기능을 사용하면 개발 컴퓨터에서 마우스로 멀티 터치 입력을 시뮬레이션 할 수 있습니다. 마우스 커서를 움직이면 Ctrl 또는 Cmd 키 (Windows 또는 Mac)를 길게 누르십시오. 대체 커서가 움직이는 방식을 변경하려면 Shift를 추가하십시오.

Simulate Multitouch
Figure 16. 마우스와 키보드로 멀티 터치 시뮬레이션.

터치 이벤트 (실제 또는 시뮬레이션)에 반응하려면 TouchEvent.TOUCH 유형의 이벤트를 수신해야 합니다.

sprite.addEventListener(TouchEvent.TOUCH, onTouch);

방금 Sprite 인스턴스에 이벤트 리스너를 추가했음을 눈치챘을 것입니다. 그러나 Sprite는 컨테이너 클래스입니다. 그것은 명백한 표면 자체를 가지고 있지 않습니다. 그럼 어떻게 그걸 만질 수 있습니까?

그렇습니다. 버블링 덕분입니다.

이를 이해하기 위해 우리가 이전에 작성한 MessageBox 클래스를 생각해보십시오. 사용자가 텍스트 필드를 클릭하면 텍스트 필드를 터치하는 모든 사람에게 알림을 보내야 합니다. — 지금까지 명백했습니다. 그러나 메시지 상자 자체에서 터치 이벤트를 듣는 사람에게도 마찬가지입니다; 결국 텍스트 필드는 메시지 상자의 일부입니다. 누군가 무대 위의 이벤트를 듣는 사람에게 통보해야 합니다. 디스플레이 목록에 있는 어떤 오브젝트를 만지는 것은 스테이지를 만지는 것을 의미합니다.

버블링 이벤트 덕분에 Starling은 이러한 유형의 상호 작용을 쉽게 나타낼 수 있습니다. 화면에서 터치를 감지하면 어떤 리프 객체가 터치되었는지 파악합니다. TouchEvent를 생성하고 이를 해당 객체에 전달합니다. 여기에서 디스플레이 목록을 따라 버블이 생깁니다.

터치 단계(Touch Phases)

실제 이벤트 리스너를 볼 시간:

private function onTouch(event:TouchEvent):void
{
    var touch:Touch = event.getTouch(this, TouchPhase.BEGAN);
    if (touch)
    {
        var localPos:Point = touch.getLocation(this);
        trace("Touched object at position: " + localPos);
    }
}

가장 기본적인 경우입니다: 누군가가 화면을 터치하고 좌표를 추적하는지 알아보십시오. getTouch 메소드는 TouchEvent 클래스에서 제공되며 관심있는 터치를 찾는 데 도움이 됩니다.

Touch 클래스는 단일 터치의 모든 정보를 캡슐화합니다. 발생한 위치 이전 프레임의 위치 등입니다.

첫번째 매개 변수로 getTouch 메소드에 전달했습니다. 따라서 우리는 이벤트에서 그 자식들에게 일어난 모든 터치를 되돌려 줄 것을 요청하고 있습니다.

터치는 유효하게 살아 있는 동안 여러 단계를 거칩니다:

TouchPhase.HOVER

마우스 입력에만 해당. 커서가 마우스 버튼 업과 함께 객체 위로 움직일 때 전달됩니다.

TouchPhase.BEGAN

손가락이 화면을 누르거나 마우스 버튼을 눌렀습니다.

TouchPhase.MOVED

화면에서 손가락이 움직이거나 버튼을 누르고있는 동안 마우스가 움직입니다.

TouchPhase.STATIONARY

마지막 프레임 이후 손가락이나 마우스 (누른 버튼 사용)가 움직이지 않았습니다.

TouchPhase.ENDED

손가락이 화면이나 마우스 버튼에서 들었습니다.

따라서, 위의 샘플 (BEGAN 단계를 찾음)은 손가락이 화면에 닿는 순간 추적 출력을 기록하지만 화면을 돌아 다니거나 화면을 벗어나는 동안에는 추적 출력을 작성하지 않습니다.

멀티터치(Multitouch)

위의 샘플에서는 단 한 번의 터치 (예: 한 손가락만)를 만들었습니다. 멀티 터치는 매우 유사하게 처리됩니다. 유일한 차이점은 touchEvent.getTouches를 대신 호출한다는 것입니다 (복수형 참고).

var touches:Vector.<Touch> = event.getTouches(this, TouchPhase.MOVED);

if (touches.length == 1)
{
    // one finger touching (or mouse input)
    var touch:Touch = touches[0];
    var movement:Point = touch.getMovement(this);
}
else if (touches.length >= 2)
{
    // two or more fingers touching
    var touch1:Touch = touches[0];
    var touch2:Touch = touches[1];
    // ...
}

getTouches 메서드는 touches의 벡터를 반환합니다. 우리는 그 벡터의 길이와 내용에 논리를 적용 할 수 있습니다.

  • 첫 번째 if 절에서 한 손가락만 화면에 나타납니다. getMovement를 통해 예를 들어 드래그 제스처를 구현하십시오.

  • else 절에서 손가락 두 개가 화면에 나타납니다. 두 터치 객체에 액세스함으로써 예를 들면, 꼬집는 제스처를 구현하십시오.

Starling 다운로드의 일부인 데모 응용 프로그램에는 멀티 터치 장면에서 사용되는 TouchSheet 클래스가 포함되어 있습니다. 스프라이트의 드래그 회전 및 스케일링을 허용하는 터치 핸들러의 샘플 구현을 보여줍니다.
마우스 아웃 및 오버 종료(Mouse Out and End Hover)

마우스가 객체에서 멀리 이동한 것을 감지하고자 할 때 고려해야 할 특별한 경우가 있습니다. (마우스 버튼은 "up" 상태 임) 마우스 입력과 관련이 있습니다.

호버링 터치의 대상이 변경되면 TouchEvent가 이전 대상으로 보내져 더이상 마우스 오버되지 않는 것을 알립니다. 이 경우 getTouch 메소드는 null을 리턴합니다. 그 지식을 사용하여 마우스 아웃 이벤트라고 불리는 것을 잡으십시오.

var touch:Touch = event.getTouch(this);
if (touch == null)
    resetButton();

2.6. 애니메이션

애니메이션은 모든 게임에서 기본적인 부분일 뿐만 아니라 현대 비즈니스 애플리케이션에서도 부드럽고 역동적인 전환을 제공할 것으로 기대됩니다. 잘 배치된 애니메이션 중 일부는 반응적이고 직관적인 인터페이스를 제공하기 위해 먼 길을 갑니다. 이를 돕기 위해 Starling은 매우 유연한 애니메이션 엔진을 제공합니다.

생각해 보면 두 가지 유형의 애니메이션이 있습니다.

  • 한편으로는 어떻게 움직일지 미리 알 수 없는 매우 역동적인 애니메이션이 있을 것입니다. 플레이어를 향해 움직이는 적을 생각해보십시오. 환경에 따라 방향과 속도가 각 프레임마다 업데이트되어야 합니다. 또는 물리학: 각각 추가되는 힘 또는 충돌에 의해 모든 것이 바뀝니다.

  • 그런 다음 세심한 계획을 따르는 애니메이션이 있습니다. 처음부터 정확히 무슨 일이 일어날지 알 것입니다. 메시지 상자에서 사라지거나 한 화면에서 다른 화면으로 전환하는 것을 생각해보십시오.

다음 섹션에서 두 가지 유형을 모두 살펴 보겠습니다.

2.6.1. EnterFrameEvent

일부 게임 엔진에서는 실행 루프(run-loop)라고 하는 것이 있습니다. 이것은 장면의 모든 요소를 지속적으로 업데이트하는 무한 루프입니다.

Starling에서는 디스플레이 목록 아키텍처로 인해 그러한 실행 루프가 별로 의미가 없습니다. 게임을 여러 가지 다른 사용자 정의 표시 객체로 분리했으며 시간이 지났을 때 수행할 작업을 각각 알아야 합니다.

이것이 바로 EnterFrameEvent의 요점입니다. 시간이 지남에 따라 표시 객체가 자동으로 업데이트되도록 허용합니다. 모든 프레임에서 이 이벤트는 표시 목록의 일부인 모든 표시 객체에 전달됩니다. 사용 방법은 다음과 같습니다:

public function CustomObject()
{
    addEventListener(Event.ENTER_FRAME, onEnterFrame); (1)
}

private function onEnterFrame(event:Event, passedTime:Number):void (2)
{
    trace("Time passed since last frame: " + passedTime);
    bird.advanceTime(passedTime);
}
1 어디서나 이 이벤트에 리스너를 추가할 수 있지만 생성자는 좋은 후보입니다.
2 해당 이벤트 리스너가 어떻게 생겼는지 확인하세요.

onEnterFrame 메서드는 프레임 당 한 번 호출되며 이전 프레임 이후로 경과된 정확한 시간에 따라 전달됩니다. 그 정보로 적을 움직이거나 태양 고도를 업데이트하거나 필요한 다른 일을 할 수 있습니다.

이 이벤트 뒤에 숨은 힘은 그것이 발생할 때마다 완전히 다른 일을 할 수 있다는 것입니다. 게임의 현재 상태에 동적으로 대응할 수 있습니다.

예를 들어 적을 플레이어쪽으로 한 걸음 내딛게 할 수 있습니다. 원한다면 적 AI의 간단한 형태도!

2.6.2. 트윈(Tweens)

이제 사전 정의된 애니메이션으로 이동하십시오. 그것들은 매우 일반적이며 움직임(movement), 스케일(scale), 페이드(fade) 등과 같은 이름을 가지고 있습니다. 이러한 종류의 애니메이션에 대한 Starling의 접근 방식은 간단하지만 동시에 매우 유연합니다. 기본적으로 숫자 (Number, int, uint)인 경우 모든 객체의 속성에 애니메이션을 적용할 수 있습니다. 이러한 애니메이션은 Tween이라는 객체에 설명되어 있습니다.

"Tween"이라는 용어는 손으로 그려진 애니메이션에서 나옵니다. 여기에서 리드 일러스트레이터가 중요한 키 프레임을 그리는 반면 나머지 팀은 프레임 사이에 프레임을 그렸습니다.
Soccer Tween
Figure 17. 트윈의 다른 프레임.

이론은 충분하고, 예를 들어 보죠:

var tween:Tween = new Tween(ball, 0.5);

tween.animate("x", 20);
tween.animate("scale", 2.0);
tween.animate("alpha", 0.0);

이 트윈은 볼 오브젝트를 x = 20으로 이동하고 크기를 두 배로 늘리고 보이지 않을 때까지 불투명도를 줄이는 애니메이션을 설명합니다. 모든 변경 사항은 0.5 초 동안 동시에 수행됩니다. 시작 값은 단순히 지정된 특성의 현재 값입니다.

이 샘플은…​

  • 당신은 객체의 임의의 속성을 애니메이트 할 수 있습니다.

  • 하나의 트윈 객체에 여러 애니메이션을 결합 할 수 있습니다.

때마침: 스케일링 페이딩 및 이동이 자주 이루어지기 때문에 Tween 클래스는 그에 대한 구체적인 방법을 제공합니다. 그래서 다음과 같이 작성할 수 있습니다:

tween.moveTo(20, 0); // animate "x" and "y"
tween.scaleTo(2);    // animate "scale"
tween.fadeTo(0);     // animate "alpha"

트윈의 흥미로운 점은 애니메이션이 실행되는 방식을 변경할 수 있다는 것입니다. 속도가 느려지고 시간이 지나면서 빨라집니다. 전환 유형을 지정하면 됩니다.

Transitions
Figure 18. 사용 가능한 변환 유형. 기본값인 'linear’는 생략되었습니다.

다음 예제에서는 이러한 전환을 지정하는 방법과 클래스가 수행할 수 있는 몇 가지 트릭을 소개합니다.

var tween:Tween = new Tween(ball, 0.5, Transitions.EASE_IN); (1)
tween.onStart    = function():void { /* ... */ };
tween.onUpdate   = function():void { /* ... */ }; (2)
tween.onComplete = function():void { /* ... */ };
tween.delay = 2; (3)
tween.repeatCount = 3; (4)
tween.reverse = true;
tween.nextTween = explode; (5)
1 세 번째 생성자 인수를 통해 전환(transition)을 지정합니다.
2 이러한 콜백은 트윈이 시작될 때 각 프레임이 끝날 때 또는 완료될 때마다 실행됩니다.
3 애니메이션을 시작하기 전에 2초간 기다립니다.
4 트윈을 세 번 반복합니다 (요요 스타일 (역방향)). repeatCount를 0으로 설정하면 트윈이 무기한 반복됩니다.
5 다른 트윈이 완료되면 바로 시작하도록 지정합니다.

방금 트윈을 만들고 구성했지만 아직 아무 일도 일어나지 않았습니다. Tween 객체는 애니메이션을 설명하지만 실행하지 않습니다.

tweens의 advanceTime 메서드를 사용하여 수동으로 수행할 수 있습니다:

ball.x = 0;
tween = new Tween(ball, 1.0);
tween.animate("x", 100);

tween.advanceTime(0.25); // -> ball.x =  25
tween.advanceTime(0.25); // -> ball.x =  50
tween.advanceTime(0.25); // -> ball.x =  75
tween.advanceTime(0.25); // -> ball.x = 100

흠, 그건 효과가 있지만 조금 성가십니다. 그쵸? 물론 ENTER_FRAME 이벤트 핸들러에서 advanceTime을 호출할 수는 있지만 계속해서 하나 이상의 애니메이션을 얻자마자 지루해집니다.

걱정 마세요. 나는 당신을 위한 녀석을 알고 있습니다. 그는 그런 것들을 다루는 것에 정말로 능숙합니다.

2.6.3. 저글러(Juggler)

juggler는 여러 개의 애니메이션 가능 객체를 받아들이고 실행합니다. 어떤 진정한 예술가와 마찬가지로 그것은 당신이 그것에 던지는 모든 것에 계속해서 advanceTime을 부르는 그것의 진정한 열정을 끈질기게 추구할 것입니다.

활성 Starling 인스턴스에는 항상 기본 juggler가 있습니다. 애니메이션을 실행하는 가장 쉬운 방법은 아래 줄을 보는 것입니다. 기본 juggler에 애니메이션 (트윈)을 추가하기만 하면 됩니다.

Starling.juggler.add(tween);

트윈이 끝나면 자동으로 버려집니다. 많은 경우 그 간단한 접근 방식만 있으면 됩니다.

그러나 다른 경우에는 좀 더 제어해야 합니다. 무대에 메인 액션이 이루어지는 게임 영역이 있다고 가정해 봅시다. 사용자가 일시 중지 버튼을 클릭하면 게임을 일시 중지하고 애니메이션 메시지 상자를 표시하여 메뉴로 돌아갈 수있는 옵션을 제공할 수 있습니다.

이런 일이 생기면 게임은 완전히 멈춰야 합니다. 더 이상 애니메이션을 재생할 수 없습니다. 문제 메시지 상자 자체는 일부 애니메이션을 사용하기 때문에 기본 juggler를 중지할 수 없습니다.

이 경우 게임 영역에 자체 juggler를 부여하는 것이 좋습니다. exit 버튼을 누르면 juggler는 아무 것도 움직이지 않게 해야 합니다. 게임은 현재 상태로 고정되고 메시지 상자 (기본 juggler 또는 다른 게임을 사용하는 경우)가 올바르게 움직입니다.

커스텀 juggler를 만들 때는 모든 프레임에서 advanceTime 메서드를 호출하면 됩니다. juggler를 다음과 같은 방법으로 사용하는 것이 좋습니다:

public class Game (1)
{
    private var _gameArea:GameArea;

    private function onEnterFrame(event:Event, passedTime:Number):void
    {
        if (activeMsgBox)
            trace("waiting for user input");
        else
            _gameArea.advanceTime(passedTime); (2)
    }
}

public class GameArea
{
    private var _juggler:Juggler; (3)

    public function advanceTime(passedTime:Number):void
    {
        _juggler.advanceTime(passedTime); (4)
    }
}
1 게임의 루트 클래스에서 Event.ENTER_FRAME을 수신합니다.
2 활성 메시지 상자가 없을 때만 gameArea를 진행시킵니다.
3 GameArea에는 자체 juggler가 있습니다. 게임 내 모든 애니메이션을 관리합니다.
4 juggler는 advanceTime 메서드 (Game에 의해 호출 됨)로 진행됩니다.

그렇게하면 게임과 메시지 상자의 애니메이션을 깔끔하게 구분할 수 있습니다.

그런데: juggler는 Tweens에만 국한되지 않습니다. 클래스가 IAnimatable 인터페이스를 구현하자마자 juggler에게 추가할 수 있습니다. 해당 인터페이스에는 하나의 메소드만 있습니다.

function advanceTime(time:Number):void;

이 방법을 구현하면 직접 간단한 MovieClip 클래스를 만듭니다. advanceTime 메서드에서는 항상 표시되는 텍스처를 변경합니다. 무비 클립을 시작하려면 juggler에 추가하기만 하면 됩니다.

이것은 한 가지 질문을 남깁니다: 언제 juggler에서 오브젝트를 제거해야 하죠?

애니메이션 멈추기

트윈이 완료되면 자동으로 juggler에서 제거됩니다. 애니메이션이 끝나기 전에 중단하려는 경우 juggler에서 애니메이션을 제거하기만 하면 됩니다.

공을 움직여서 기본 juggler에 추가한 트윈을 만들었다고 가정해 봅시다:

tween:Tween = new Tween(ball, 1.5);
tween.moveTo(x, y);
Starling.juggler.add(tween);

애니메이션을 중단할 수있는 몇 가지 방법이 있습니다. 상황에 따라 게임 로직에 가장 적합한 것을 선택하십시오.

var animID:uint = juggler.add(tween);

Starling.juggler.remove(tween); (1)
Starling.juggler.removeTweens(ball); (2)
Starling.juggler.removeByID(animID); (3)
Starling.juggler.purge(); (4)
1 트윈을 직접 제거하기. 이것은 모든 IAnimatable 객체와 함께 작동합니다.
2 공에 영향을 미치는 모든 트윈을 제거하기. 트윈을 위해서만 작동합니다!
3 ID로 트윈을 제거하기. Tween 인스턴스에 액세스 할 수 없을 때 유용합니다.
4 모든 것을 중단하려면 juggler를 제거하십시오.

퍼지(purge) 메서드에 약간 주의를 기울이십시오: 기본 juggler에서 호출하면 코드의 다른 부분이 갑자기 중단된 애니메이션에 직면하여 게임이 중단될 수 있습니다. 커스텀 juggler에게만 퍼지를 사용하는 것이 좋습니다.

자동 제거

Tween 클래스가 완료되면 트위닝이 자동으로 juggler에서 제거되도록 관리하는 방법을 스스로 물어볼 수도 있습니다. REMOVE_FROM_JUGGLER 이벤트로 완료됩니다.

IAnimatable을 구현하는 모든 객체는 이러한 이벤트를 전달할 수 있습니다. juggler는 그 이벤트를 수신하고 그에 따라 대상을 제거합니다.

public class MyAnimation extends EventDispatcher implements IAnimatable
{
    public function stop():void
    {
        dispatchEventWith(Event.REMOVE_FROM_JUGGLER);
    }
}
단일 명령 트윈(Single-Command Tweens)

트윈과 juggler 사이의 분리는 매우 강력하지만 때로는 방해가 되므로 간단한 작업을 위한 많은 코드를 작성해야 합니다. 그래서 juggler에 단일 명령으로 트윈을 만들고 실행할 수 있는 편리한 방법이 있습니다. 다음은 샘플입니다:

juggler.tween(msgBox, 0.5, {
   transition: Transitions.EASE_IN,
   onComplete: function():void { button.enabled = true; },
   x: 300,
   rotation: deg2rad(90)
});

이렇게하면 msgBox 객체의 트윈이 0.5초 동안 생성되어 x 및 rotation 속성에 애니메이션이 적용됩니다. 보시다시피 매개 변수는 애니메이션하려는 모든 속성과 Tween 자체의 속성을 나열하는 데 사용됩니다. 거대한 시간이 절약되었습니다!

2.6.4. 딜레이 콜(Delayed Calls)

기술적으로 Starling이 지원하는 모든 애니메이션 유형을 다루었습니다. 그러나 실제로 이 주제와 깊은 관련이 있는 또 다른 개념이 있습니다.

이벤트 시스템에 우리를 소개한 우리의 개 영웅 아인슈타인(Einstein)을 기억하나요? 우리가 마지막으로 그를 보았을 때 그는 그의 모든 건강 상태를 잃어 버렸고 gameOver를 막 불렀습니다. 그러나 기다려보세요. 즉시 그 메소드를 호출하지 마세요. 그러면 게임이 너무 갑자기 끝날 것입니다. 대신 2초(플레이어가 펼치는 드라마를 깨닫기에 충분한 시간)의 지연으로 이를 호출하십시오.

지연을 구현하려면 네이티브 Timer 또는 setTimeout 메소드를 사용할 수 있습니다. 그러나 당신은 또한 juggler를 사용할 수 있으며 그것은 큰 장점이 있습니다. 당신이 완전히 통제할 수 있다는 것입니다.

이 2초가 지나기 전에 플레이어가 "일시 중지"버튼을 누르는 것을 상상해 보면 분명해집니다. 이 경우 게임 영역에 애니메이션을 적용하는 것을 중지 할 뿐만 아니라 당신은 게임이 지연되기를(delayed) 원할 것입니다.

그렇게 하려면 다음과 같이 호출하십시오:

juggler.delayCall(gameOver, 2);

gameOver 함수는 지금부터 2초 후에 호출됩니다 (juggler가 중단되면 더 길어집니다). 또한 메소드에 몇 가지 인수를 전달할 수도 있습니다. 대신 이벤트를 발신하기 원하십니까?

juggler.delayCall(dispatchEventWith, 2, "gameOver");

딜레이 콜을 사용하는 또 다른 편리한 방법은 정기적인 작업을 수행하는 것입니다. 3초에 한 번 새로운 적을 생성하려고 한다고 상상해보십시오.

juggler.repeatCall(spawnEnemy, 3);

뒤에서는 delayCall 및 repeatCall이 모두 DelayedCall 유형의 객체를 만듭니다. juggler.tween 메소드가 트윈을 사용하기 위한 단축키인 것과 마찬가지로 이러한 메소드는 지연 호출을 작성하기위한 단축키입니다.

딜레이 콜을 중단하려면 다음 방법 중 하나를 사용하십시오:

var animID:uint = juggler.delayCall(gameOver, 2);

juggler.removeByID(animID);
juggler.removeDelayedCalls(gameOver);

2.6.5. 무비클립(Movie Clips)

Mesh를 둘러싼 클래스 다이어그램을 보았을 때 이미 MovieClip 클래스를 보았을 것입니다. 그거 맞습니다. MovieClip은 실제로 Image의 서브 클래스로 시간이 지남에 따라 텍스처가 변경됩니다. Starling이 애니메이션 GIF와 동일하다고 생각하십시오!

텍스처 획득하기

무비 클립의 모든 프레임은 하나의 텍스처 맵에서 가져온 것이고 모든 프레임은 동일한 크기를 갖는 것이 좋습니다 (그렇지 않은 경우 첫 번째 프레임의 크기로 늘어납니다). Adobe Animate와 같은 도구를 사용하여 그러한 애니메이션을 만들 수 있습니다. Starling의 텍스처 아틀라스 형식으로 직접 내보낼 수 있습니다.

이것은 무비 클립의 프레임을 포함하는 텍스처 아틀라스의 샘플입니다. 먼저 프레임 좌표로 XML을 보십시오. 각 프레임은 접두사 'flight_'로 시작됩니다.

<TextureAtlas imagePath="atlas.png">
    <SubTexture name="flight_00" x="0"   y="0" width="50" height="50" />
    <SubTexture name="flight_01" x="50"  y="0" width="50" height="50" />
    <SubTexture name="flight_02" x="100" y="0" width="50" height="50" />
    <SubTexture name="flight_03" x="150" y="0" width="50" height="50" />
    <!-- ... -->
</TextureAtlas>

여기에 해당 텍스처가 있습니다:

Flight Animation
Figure 19. MovieClip의 프레임.
MovieClip 만들기

이제 MovieClip을 만듭니다. 아틀라스 변수가 모든 프레임을 포함하는 TextureAtlas를 가리키고 있다고 가정하면 매우 쉽습니다:

var frames:Vector.<Texture> = atlas.getTextures("flight_"); (1)
var movie:MovieClip = new MovieClip(frames, 10); (2)
addChild(movie);

movie.play();
movie.pause(); (3)
movie.stop();

Starling.juggler.add(movie); (4)
1 getTextures 메소드는 주어진 접두사로 시작하여 알파벳순으로 정렬된 모든 텍스처를 반환합니다.
2 MovieClip에 이상적입니다. 왜냐하면 우리는 그 텍스처를 생성자에게 전달할 수 있기 때문입니다. 두 번째 매개 변수는 초당 재생되는 프레임의 수를 나타냅니다.
3 클립의 재생을 제어하는 ​​메소드입니다. 기본적으로 "play" 모드로 전환됩니다.
4 중요: Starling의 다른 애니메이션과 마찬가지로 무비 클립을 저글러(juggler)에 추가해야 합니다!

접두어(flight_)로 텍스처를 참조하는 방법에 주목 했습니까? 따라서 다른 무비 클립과 텍스처가 포함된 혼합된 아틀라스를 만들 수 있었습니다. 한 클립의 프레임을 함께 그룹화하려면 모든 클립에 동일한 접두어를 사용하면 됩니다.

클래스는 특정 프레임에 도달할 때마다 사운드 또는 임의 콜백을 실행하는 기능도 지원합니다. 가능한지 확인하려면 API 참조를 확인하십시오!

더 복잡한 무비클립

애니메이션 기법의 단점은 언급해야 할 것 같네요. 애니메이션이 매우 길거나 개별 프레임이 매우 큰 경우 텍스처 메모리가 부족합니다. 애니메이션이 여러 텍스처 애플릿을 사용하면 메모리에 적합하지 않을 수 있습니다.

이러한 종류의 애니메이션의 경우 보다 정교한 솔루션인 골격(skeletal) 애니메이션으로 전환해야 합니다. 이것은 캐릭터가 다른 부분 (뼈)으로 나뉘어 있음을 의미합니다. 그 부분들은 캐릭터의 뼈대에 따라 개별적으로 움직입니다. 이것은 매우 유연합니다.

이러한 애니메이션에 대한 지원은 Starling 자체의 일부가 아니지만 도움을 주는 여러 가지 도구와 라이브러리가 있습니다. 다음은 Starling과 함께 사용할 수 있는 툴들입니다:

2.7. 애셋 관리(Asset Management)

한 가지 분명한 사실은 모든 응용 프로그램의 자원에서 텍스처가 차지하는 비중이 가장 크다는 것입니다. 특히 게임에는 많은 그래픽이 필요합니다. 사용자 인터페이스에서 문자 항목 배경 등으로 변환할 수 있습니다. 하지만 그것이 전부는 아닙니다. 사운드 및 구성 파일을 관리해야 할 수도 있습니다.

이러한 애셋을 참조하려면 몇 가지 옵션이 있습니다.

  • 애플리케이션 내부에 바로 임베드합니다 ([Embed] 메타 데이터를 통해).

  • 디스크에서 로드하기 (AIR 응용 프로그램에서만 가능).

  • URL에서 로드하기 (예: 웹 서버에서).

모든 옵션에는 애셋 유형 및 로드 메커니즘에 따라 다른 코드가 필요하기 때문에 일관된 방식으로 애셋에 액세스하기가 어렵습니다. 고맙게도 Starling에는 AssetManager와 함께 도움이 되는 클래스가 있습니다.

다음 유형의 애셋을 지원합니다:

  • 텍스처 (비트 맵 또는 ATF 데이터에서)

  • 텍스처 아틀라스

  • 비트맵 폰트

  • 사운드

  • XML 데이터

  • JSON 데이터

  • 바이트 어레이(ByteArrays)

이를 달성하기 위해, AssetManager는 3단계 접근법을 사용합니다:

  1. 애셋에 대한 포인터를 대기열에 추가합니다. (예: 파일 오브젝트 또는 URL)

  2. 대기열을 처리하도록 AssetManager에 지시합니다.

  3. 대기열에서 처리가 완료되면 해당 'get’메소드를 사용하여 모든 에셋에 액세스할 수 있습니다.

AssetManager에는 자세한 정보가 들어 있습니다. 사용하도록 설정하면 대기열에 포함되고 로드되는 모든 단계가 콘솔로 추적됩니다. 이는 디버깅에 유용하거나 특정 애셋이 왜 보이지 않는지 이해하지 못하는 경우에 유용합니다! 이러한 이유로 Starling 최신 버전에서는 기본적으로 활성화되어 있습니다.

2.7.1. 애셋을 대기열에 넣기

첫 번째 단계는 사용하려는 모든 애셋을 큐에 넣는 것입니다. 그것이 어떻게 완료되었는지는 각 애셋의 유형과 출처에 따라 다릅니다.

디스크 또는 네트워크의 애셋

디스크 또는 원격 서버에서 파일을 대기열에 넣는 것은 다소 간단합니다:

// 원격 URL에서 에셋 대기열에 넣기
assets.enqueue("http://gamua.com/img/starling.jpg");

// 디스크에서 에셋을 대기열에 포함 (AIR에만 해당)
var appDir:File = File.applicationDirectory;
assets.enqueue(appDir.resolvePath("sounds/music.mp3"));

// 재귀적으로 디렉토리의 모든 내용을 큐에 넣습니다 (AIR 만 해당).
assets.enqueue(appDir.resolvePath("textures"));

텍스처 아틀라스를 로드하려면 XML 파일과 해당 텍스처를 모두 대기열에 추가하기만 하면 됩니다. XML 파일의 imagePath 속성에 올바른 파일 이름이 포함되어 있는지 확인하십시오. AssetManager가 나중에 아틀라스를 만들 때 찾을 것이기 때문입니다.

assets.enqueue(appDir.resolvePath("textures/atlas.xml"));
assets.enqueue(appDir.resolvePath("textures/atlas.png"));

비트맵 폰트는 동일하게 작동합니다. 이 경우 XML (.fnt 파일)의 파일 특성이 올바르게 설정되어 있는지 확인해야 합니다.

assets.enqueue(appDir.resolvePath("fonts/desyrel.fnt"));
assets.enqueue(appDir.resolvePath("fonts/desyrel.png"));
임베디드 애셋

임베디드 애셋의 경우 모든 임베드 명령문을 하나의 전용 클래스에 넣는 것이 좋습니다. public static const로 선언하고 명명 규칙을 따릅니다:

  • 삽입된 이미지의 클래스는 확장자가 없는 파일과 완전히 동일한 이름이어야 합니다. 이것은 XML (atlas, bitmap font)의 참조가 깨지지 않도록 하기 위해 필요합니다.

  • 아틀라스 및 폰트 XML 파일은 파일 이름으로 절대 참조되지 않으므로 임의의 이름을 가질 수 있습니다.

다음은 그러한 클래스의 샘플입니다:

public class EmbeddedAssets
{
    /* PNG texture */
    [Embed(source = "/textures/bird.png")]
    public static const bird:Class;

    /* ATF texture */
    [Embed(source   = "textures/1x/atlas.atf",
           mimeType = "application/octet-stream")]
    public static const atlas:Class;

    /* XML file */
    [Embed(source   = "textures/1x/atlas.xml",
           mimeType = "application/octet-stream")]
    public static const atlas_xml:Class;

    /* MP3 sound */
    [Embed(source = "/audio/explosion.mp3")]
    public static const explosion:Class;
}

해당 클래스를 큐에 넣으면 나중에 애셋 관리자가 포함된 모든 애셋을 인스턴스화 합니다.

var assets:AssetManager = new AssetManager();
assets.enqueue(EmbeddedAssets); (1)
1 새(bird) 텍스처, 폭발음 및 텍스처 맵을 큐에 넣습니다.
애셋 별 구성

Texture.from…​() 팩토리 메소드를 통해 수동으로 텍스처를 생성하면 생성 방법을 미세하게 조정할 수 있습니다. 예를 들어 텍스처 형식이나 축척 비율을 결정할 수 있습니다.

해당 설정의 문제점: 일단 텍스처가 생성되면 더 이상 변경할 수 없습니다. 따라서 텍스처가 생성될 때 올바른 설정이 적용되었는지 확인해야 합니다. 애셋 관리자는 이러한 종류의 구성도 지원합니다:

var assets:AssetManager = new AssetManager();
assets.textureFormat = Context3DTextureFormat.BGRA_PACKED;
assets.scaleFactor = 2;
assets.enqueue(EmbeddedAssets);

애셋 관리자는 자신이 생성한 모든 텍스처에 대해 이러한 설정을 준수합니다. 그러나 이것은 모든 로드된 텍스처에 대해 하나의 속성 집합만 허용하는 것으로 보입니다. 사실은 아닙니다: 각 호출을 대기열에 넣기 전에 올바른 설정을 지정하고 여러 단계로 대기열에 넣기만하면 됩니다.

assets.scaleFactor = 1;
assets.enqueue(appDir.resolvePath("textures/1x"));

assets.scaleFactor = 2;
assets.enqueue(appDir.resolvePath("textures/2x"));

이렇게 하면 1x 및 2x 폴더의 텍스처가 각각 1 및 2의 축척 비율을 사용하게 됩니다.

2.7.2. 애셋 로딩

애셋이 대기열에 추가되었으므로 이제 모든 애셋을 한꺼번에 로드할 수 있습니다. 적재중인 애셋의 수와 크기에 따라 다소 시간이 걸릴 수 있습니다. 이러한 이유로 사용자에게 진행률 막대 또는 로드 표시기를 표시하는 것이 좋습니다.

assets.loadQueue(function(ratio:Number):void
{
    trace("Loading assets, progress:", ratio);

    // when the ratio equals '1', we are finished.
    if (ratio == 1.0)
        startGame();
});

startGame 메서드는 직접 구현해야 하는 것입니다. 여기서 로딩 화면을 숨기고 실제 게임을 시작할 수 있습니다.

활성화된 자세한 속성을 사용하면 에셋에 액세스 할 수있는 이름이 표시됩니다:

[AssetManager] Adding sound 'explosion'
[AssetManager] Adding texture 'bird'
[AssetManager] Adding texture 'atlas'
[AssetManager] Adding texture atlas 'atlas'
[AssetManager] Removing texture 'atlas'

알아 차렸나요? 마지막 줄에서는 텍스처 아트라스를 생성한 직후에 아트라스 텍스처가 실제로 제거됩니다. 왜 그런가요?

아틀라스가 만들어지면 더 이상 아틀라스 텍스처에 관심이 없으며 텍스처 텍스처에만 포함됩니다. 따라서 실제 아틀라스 텍스처가 제거되어 다른 텍스처를 위한 슬롯이 해제됩니다. 비트맵 폰트에서도 마찬가지입니다.

2.7.3. 애셋에 액세스하기

끝으로: 대기열이 처리를 마쳤으므로 AssetManager의 다양한 get…​ 메소드를 사용하여 에셋에 액세스할 수 있습니다. 각 애셋은 애셋의 파일 이름 (확장자 없음) 또는 포함 된 개체의 클래스 이름으로 참조됩니다.

var texture:Texture = assets.getTexture("bird"); (1)
var textures:Vector.<Texture> = assets.getTextures("animation"); (2)
var explosion:SoundChannel = assets.playSound("explosion"); (3)
1 먼저 명명 된 텍스처를 검색 한 다음 아틀라스를 찾습니다.
2 위와 같지만 주어진 문자열로 시작하는 모든 (서브) 텍스처를 반환합니다.
3 사운드를 재생하고 사운드를 제어하는 ​​사운드 채널을 반환합니다.

도중에 비트맵 폰트를 대기열에 추가하면, 이미 등록되어 사용할 준비가 되었을 것입니다.

내 게임에서는 일반적으로 정적 속성을 통해 액세스할 수있는 루트 관리자에 자산 관리자에 대한 참조를 저장합니다. 따라서 Game.assets.get…​() (루트 클래스가 Game이라고 가정 할 때)를 호출하여 게임의 어느 곳에서나 내 애셋에 쉽게 액세스할 수 있습니다.

2.8. 조각 필터(Fragment Filters)

지금까지 우리가 렌더링한 모든 것은 그 위에 맵핑된 텍스처와 결합된 텍스처 (또는 텍스처 없이)입니다. 메쉬를 이동하고 크기를 조정하고 회전하고 다른 색상으로 색칠할 수 있습니다. 그러나 게임의 외관은 텍스처에 의해서만 정의됩니다.

어떤 점에서 당신은 이 접근법의 한계를 뛰어 넘을 것입니다; 어쩌면 여러 색상의 이미지 변형, 흐린(blurred) 이미지 또는 그림자가 필요할 수 있습니다. 이러한 모든 변형을 텍스처 맵에 추가하면 곧 메모리가 부족해질 것입니다.

이때, 조각 필터가 도움이 될 수 있습니다. 필터는 모든 표시 객체 (컨테이너 포함)에 첨부할 수 있으며 모양을 완전히 변경할 수 있습니다.

예를 들어 객체에 가우시안 블러를 추가하려고 한다고 가정해 봅시다.

var filter:BlurFilter = new BlurFilter(); (1)
object.filter = filter; (2)
1 원하는 필터 클래스의 인스턴스를 작성하고 구성하십시오.
2 표시 객체의 filter 속성에 필터를 지정합니다.

필터가 할당되면 표시 객체의 렌더링이 다음과 같이 수정됩니다:

  • 각 프레임에서 대상 객체는 텍스처로 렌더링 됩니다.

  • 이 텍스쳐는 프래그먼트 쉐이더 (GPU 바로 위)에서 처리됩니다.

  • 일부 필터는 다중 패스를 사용합니다. 즉 하나의 셰이더 출력이 다음 패스로 전달됩니다.

  • 마지막으로 마지막 쉐이더의 출력이 백 버퍼로 그려집니다.

Filter Pipeline
Figure 20. 조각 필터의 렌더링 파이프 라인.

이방법은 매우 유연하여 모든 종류의 다양한 효과를 생성할 수 있습니다 (곧 살펴 보겠지만). 또한 GPU의 병렬 처리 기능을 잘 활용합니다. 픽셀 당 비싼 모든 로직이 그래픽 칩에서 바로 실행됩니다.

즉, 필터는 일괄 처리를 중단하고 각 필터 단계에는 별도의 그리기 호출이 필요합니다. 메모리 사용과 성능면에서 모두 저렴하지는 않습니다. 따라서 조심스럽게 사용하고 현명하게 사용하십시오.

2.8.1. 쇼케이스(Showcase)

Starling은 별도의 추가 구성없이 사용할 수 있는 몇 가지 매우 유용한 필터를 제공합니다.

BlurFilter

객체에 가우시안 블러를 적용합니다. 흐림의 강도는 x 및 y 축에 대해 개별적으로 설정할 수 있습니다.

  • 흐림 방향 당 하나의 렌더 패스가 필요합니다 (draw call).

  • 유닛 단위당 필터는 한 번의 렌더 패스를 필요로 합니다 (강도 1은 one pass, 강도 2는 two pass).

  • 흐림 강도를 높이는 대신 필터 해상도를 낮추는 것이 좋습니다. 비슷한 효과가 있지만 훨씬 저렴합니다.

BlurFilter
Figure 21. BlurFilter 적용 예시.
ColorMatrixFilter

객체의 색상을 동적으로 변경합니다. 객체의 밝기 채도 색조를 변경하거나 전체를 반전시킵니다.

이 필터는 각 픽셀의 색상 값과 알파 값에 4x5 매트릭스를 곱합니다. 이는 매우 유연한 개념이지만 올바른 매트릭스 설정을 얻는 것은 상당히 번거롭습니다. 이러한 이유로 클래스에는 달성하려는 효과 (예: 색조 또는 채도 변경)에 대한 행렬을 설정하는 여러 가지 도우미 메서드가 포함되어 있습니다.

  • 하나의 필터 인스턴스에서 여러 색상 변환을 결합할 수 있습니다. 예를 들어 밝기와 채도를 모두 변경하려면 필터에서 해당하는 두 가지 방법을 호출하십시오.

  • 이 필터는 항상 단방향 패스를 필요로 합니다.

ColorMatrixFilter
Figure 22. ColorMatrixFilter 적용 예시.
DropShadow-와 GlowFilter

이 두 필터는 원래의 객체를 앞에 그려 넣고 그 뒤에 희미하고 색다른 변형을 추가합니다.

  • 또한 순수 BlurFilter에 필요한 렌더링 패스를 추가하기 때문에 비용이 많이 듭니다.

DropShadow and Glow filter
Figure 23. DropShadow-와 GlowFilter 적용 예시.
DisplacementMapFilter

맵 텍스처의 색상에 따라 대상 객체의 픽셀을 배치합니다.

  • 정확하게 사용하기 쉽지는 않지만 매우 강력합니다!

  • 물 위 반사, 돋보기, 폭발의 충격파 - 이 필터로 그것들을 할 수 있습니다.

Other filters
Figure 24. 몇 가지 맵을 사용하는 DisplacementMapFilter 적용 예시.
FilterChain

하나의 표시 객체에서 여러 필터를 결합하려면 FilterChain 클래스를 통해 필터를 함께 연결할 수 있습니다. 필터는 지정된 순서로 처리됩니다. 필터 당 그리기 호출 수가 단순히 합산됩니다.

FilterChain
Figure 25. ColorMatrix- 및 DropShadowFilter가 결합되었습니다.

2.8.2. 퍼포먼스 팁

GPU 처리 부분은 매우 효율적이지만 추가 드로우 콜은 조각 필터를 다소 비용이 많이 들게 만듭니다. 그러나 Starling은 필터를 최적화하기 위해 최선을 다합니다.

  • 오브젝트가 두 개의 연속 프레임에 대해 스테이지 (또는 배율 및 색상과 같은 다른 속성)에 상대적인 위치를 변경하지 않으면 Starling은 이를 인식하고 자동으로 필터 출력을 캐시합니다. 즉, 필터를 더 이상 처리 할 필요가 없습니다. 대신 단일 이미지처럼 작동합니다.

  • 반면에 객체가 계속 움직이면 마지막 필터 패스는 항상 텍스처가 아닌 백 버퍼에 직접 렌더링됩니다. 그건 하나의 드로우 콜로 유지됩니다.

  • 객체가 움직이고 있어도 필터 출력을 계속 사용하려면 filter.cache()를 호출하십시오. 다시 말하지만, 이것은 객체가 정적인 이미지처럼 동작하도록 합니다. 그러나 표시될 대상 객체의 변경 사항에 대해서는 캐시를 다시 호출 (또는 캐시 취소)해야 합니다.

  • 메모리를 절약하려면 resolution 및 textureFormat 속성을 사용하여 실험해 보십시오. 단, 이미지 품질이 떨어지는건 감수해야 합니다.

2.8.3. 더 많은 필터들

직접 필터를 만드는 방법을 알고 싶습니까? 걱정하지 마세요. 우리는 잠시 후에 그 주제를 살펴볼 것입니다.

그동안 다른 Starling 개발자가 만든 필터를 사용해 볼 수 있습니다. 훌륭한 샘플로는 devon-o의 filter collection, 필터 모음 이 있습니다.

2.9. 메쉬(Meshes)

메쉬는 모든 유형의 표시 객체의 기본 구성 요소입니다. "tangible" 을 사용하면 표시 목록의 "leaf" 객체 즉, 컨테이너가 아니지만 백 버퍼에 직접 렌더링되는 객체를 의미합니다. 그것이 매우 중요하기 때문에 나는 이 클래스를 좀 더 자세하게 보고 싶다.

요컨대 Mesh는 Stage3D를 통해 렌더링될 삼각형 목록을 나타냅니다. Quad와 Image의 기본 클래스이기 때문에 이미 몇 번 언급되었습니다. 여기에 우리가 이야기하고 있는 클래스 계층 구조가 있습니다.

mesh classes from display object

메쉬는 추상 클래스가 아닙니다. 직접 인스턴스화 할 수 있습니다. 방법은 다음과 같습니다.

var vertexData:VertexData = new VertexData();
vertexData.setPoint(0, "position", 0, 0);
vertexData.setPoint(1, "position", 10, 0);
vertexData.setPoint(2, "position", 0, 10);

var indexData:IndexData = new IndexData();
indexData.addTriangle(0, 1, 2);

var mesh:Mesh = new Mesh(vertexData, indexData);
addChild(mesh);

보시다시피 두 개의 클래스를 인스턴스화 해야했습니다: VertexData 및 IndexData. 그들은 각각 정점과 인덱스의 집합을 나타냅니다.

  • VertexData는 각 꼭지점의 속성을 효율적으로 저장합니다. 그것의 위치와 색깔.

  • IndexData는 이러한 정점에 인덱스를 저장합니다. 세 개의 인덱스가 삼각형을 구성합니다.

위의 코드는 가장 기본적인 드로잉 프리미티브인 삼각형을 만들었습니다. 우리는 세 개의 꼭지점을 정의하고 시계 방향으로 그것을 참조함으로써 이것을 했습니다. 결국 GPU가 가장 잘 할 수 있는 것입니다. 삼각형 그리기 - 아주 많은.

Triangle
Figure 26. 방금 만든 삼각형.

2.9.1. Mesh 확장하기

VertexData 및 IndexData를 직접 사용하면 시간이 지남에 따라 상당히 번거로울 수 있습니다. 모든 것을 설정하는 클래스에서 코드를 캡슐화하는 것이 합리적입니다.

커스텀 메쉬를 생성하는 방법을 설명하기 위해 우리는 이제 NGon이라는 간단한 클래스를 작성합니다. 그것의 임무는 사용자 정의 색으로 정규 n다각형을 렌더링하는 것입니다.

Polygons
Figure 27. 이것들은 우리가 그리기 원하는 정다각형의 종류입니다.

클래스는 내장된 표시 객체처럼 작동해야 합니다. 이를 인스턴스화하고 특정 위치로 이동한 다음 표시 목록에 추가합니다.

var ngon:NGon = new NGon(100, 5, Color.RED); (1)
ngon.x = 60;
ngon.y = 60;
addChild(ngon);
1 생성자의 인수는 반경 모서리 수 및 색상을 정의합니다.

이런걸 어떻게 할 수 있는지 살펴 보겠습니다.

2.9.2. 정점(Vertex) 설정

다른 모든 모양과 마찬가지로 우리의 규칙적인 다각형은 단지 몇 개의 삼각형으로 만들 수 있습니다. 다음은 우리가 어떻게 5 각형 (n = 5인 n-gon)의 삼각형을 만들 수 있는지를 보여줍니다.

Pentagon
Figure 28. 오각형과 정점.

5각형은 5개의 삼각형에 걸쳐있는 6개의 꼭지점으로 구성됩니다. 우리는 각 꼭지점에 0과 5 사이의 숫자를 주고 5는 가운데에 있습니다.

앞서 언급했듯이 Vertex는 VertexData 인스턴스에 저장됩니다. VertexData는 각 정점에 대해 명명된 속성 집합을 정의합니다. 이 샘플에서는 두 가지 표준 속성이 필요합니다:

  • position은 2차원 점 (x, y)을 저장합니다.

  • color는 RGBA 색상 값을 저장합니다.

VertexData 클래스는 이러한 속성을 참조하는 몇 가지 메소드를 정의합니다. 그것은 우리가 다각형의 꼭지점을 설정할 수 있게 해줍니다.

메쉬를 확장하는 NGon이라는 새 클래스를 만듭니다. 그런 다음 다음 인스턴스 메소드를 추가하십시오.

private function createVertexData(
    radius:Number, numEdges:int, color:uint):VertexData
{
    var vertexData:VertexData = new VertexData();

    vertexData.setPoint(numEdges, "position", 0.0, 0.0); (1)
    vertexData.setColor(numEdges, "color", color);

    for (var i:int=0; i<numEdges; ++i) (2)
    {
        var edge:Point = Point.polar(radius, i*2*Math.PI / numEdges);
        vertexData.setPoint(i, "position", edge.x, edge.y);
        vertexData.setColor(i, "color", color);
    }

    return vertexData;
}
1 중앙 정점 (마지막 색인)을 설정하십시오.
2 가장자리 정점을 설정합니다.

우리 메쉬는 색이 균일하기 때문에 각 꼭지점에 같은 색을 할당합니다. 가장자리 꼭짓점 (모서리)의 위치는 주어진 반경을 가진 원을 따라 분포됩니다.

2.9.3. 인덱스 설정(Index Setup)

정점(Vertex)이 아주 많은 것. 이제 다각형을 구성하는 삼각형을 정의해야 합니다.

Stage3D는 하나의 삼각형을 참조하는 세 개의 연속적인 인덱스가 있는 인덱스인 경우 간단한 목록을 원합니다. 인덱스를 시계 방향으로 참조하는 것이 좋습니다. 그 대회는 우리가 삼각형의 앞면을 보고 있음을 나타냅니다. 우리의 5각형 목록은 다음과 같을 것입니다.

5, 0, 1,   5, 1, 2,   5, 2, 3,   5, 3, 4,   5, 4, 0

Starling에서는 IndexData 클래스를 사용하여 이러한 목록을 설정합니다. 다음 메서드는 적절한 인덱스로 IndexData 인스턴스를 채웁니다.

private function createIndexData(numEdges:int):IndexData
{
    var indexData:IndexData = new IndexData();

    for (var i:int=0; i<numEdges; ++i)
        indexData.addTriangle(numEdges, i, (i+1) % numEdges);

    return indexData;
}

2.9.4. NGon 생성자

이것은 실제로 NGon 클래스에 필요한 모든 것입니다! 이제 우리는 위의 메소드를 생성자에서 사용할 필요가 있습니다. 표시 객체의 다른 모든 책임 (히트 테스트, 렌더링, 경계 계산 등)은 수퍼 클래스에서 처리합니다.

public class NGon extends Mesh
{
    public function NGon(
        radius:Number, numEdges:int, color:uint=0xffffff)
    {
        var vertexData:VertexData = createVertexData(radius, numEdges, color);
        var indexData:IndexData = createIndexData(numEdges);

        super(vertexData, indexData);
    }

    // ...
}

간단하네요. 그렇죠? 이 접근 방식은 생각할 수 있는 모든 모양에 적용됩니다.

커스텀 메쉬로 작업할 때 (starling.geom 패키지에있는) Polygon 클래스도 보십시오. 임의의 닫힌 모양 (여러 정점으로 정의 됨)을 삼각형으로 변환하는 데 도움이됩니다. '[Masks], 마스크' 섹션에서 좀 더 자세히 살펴 보겠습니다.

2.9.5. 텍스처 추가하기

텍스처를 이 폴리곤에 매핑할 수 있다면 좋지 않을까요? 기본 클래스인 Mesh는 이미 텍스처 속성을 정의합니다. 우리는 필요한 텍스처 좌표가 부족합니다.

텍스처 좌표를 통해 텍스처의 어느 부분이 정점에 매핑되는지 정의합니다. 일반적으로 좌표축 (u 및 v)에 사용되는 이름에 대한 참조인 UV 좌표라고도 합니다. UV 범위는 실제 텍스처 크기에 관계없이 0과 1 사이로 정의됩니다.

Pentagon Texture Coordinates
Figure 29. 다각형의 텍스처 좌표는 0-1 범위입니다.

이 정보를 사용하여 createVertexData 메소드를 적절하게 업데이트 할 수 있습니다.

function createVertexData(
    radius:Number, numEdges:int, color:uint):VertexData
{
    var vertexData:VertexData = new VertexData(null, numEdges + 1);
    vertexData.setPoint(numEdges, "position", 0.0, 0.0);
    vertexData.setColor(numEdges, "color", color);
    vertexData.setPoint(numEdges, "texCoords", 0.5, 0.5); (1)

    for (var i:int=0; i<numEdges; ++i)
    {
        var edge:Point = Point.polar(radius, i*2*Math.PI / numEdges);
        vertexData.setPoint(i, "position", edge.x, edge.y);
        vertexData.setColor(i, "color", color);

        var u:Number = (edge.x + radius) / (2 * radius); (2)
        var v:Number = (edge.y + radius) / (2 * radius);
        vertexData.setPoint(i, "texCoords", u, v);
    }

    return vertexData;
}
1 중심 정점의 텍스처 좌표: 0.5, 0.5.
2 n-gon의 원점은 중심이지만 텍스처 좌표는 모두 양수여야 합니다. 그래서 우리는 정점 좌표를 (반지름으로) 오른쪽으로 이동시키고 2 * 반지름으로 나누어 0-1의 범위에서 정합니다.

텍스처가 지정되면 렌더링 코드가 자동으로 해당 값을 선택합니다.

var ngon:NGon = new NGon(100, 5);
ngon.texture = assets.getTexture("brick-wall");
addChild(ngon);
Textured Pentagon
Figure 30. 텍스쳐를 입힌 오각형.

2.9.6. 안티 앨리어싱(Anti-Aliasing)

우리 n-gon의 가장자리를 자세히 보면 가장자리가 아주 들쭉날쭉하게 보인다는 것을 알 수 있습니다. 이는 GPU가 n-gon 또는 외부의 픽셀을 처리하기 때문입니다. 즉 내부 픽셀이 없기 때문입니다. 이를 수정하기 위해 앤티 앨리어싱을 활성화 할 수 있습니다. Starling 클래스에 해당 이름의 속성이 있습니다.

starling.antiAliasing = 4;

이 값은 Stage3D에서 렌더링시 사용하는 하위 샘플 수와 관련이 있습니다. 더 많은 하위 샘플을 사용하려면 계산을 더 많이 수행해야하므로 앤티 앨리어스가 잠재적으로 매우 비싼 옵션입니다. 또한 Stage3D는 모든 플랫폼에서 앤티 앨리어싱을 지원하지 않습니다.

모바일에서 앤티 앨리어싱은 현재 RenderTexture 내에서만 작동합니다.

따라서 그것은 이상적인 솔루션이 아닙니다. 내가 제공할 수있는 유일한 위로: 스크린의 전형적인 픽셀 밀도는 끊임없이 증가하고 있습니다. 현대의 하이엔드 휴대 전화에서는 픽셀이 매우 작아서 앨리어싱이 더 이상 문제가 되지 않습니다.

Anti-Aliasing
Figure 31. 앤티 앨리어싱은 픽셀화된 가장자리를 매끄럽게 할 수 있습니다.

2.9.7. 메쉬 스타일(Mesh Styles)

이제 임의의 모양을 가진 텍스쳐 메쉬를 만드는 법을 알게 되었습니다. 이를 위해 Starling에 내장된 표준 렌더링 메커니즘을 사용하고 있습니다.

그러나 렌더링 프로세스 자체를 사용자 정의하려면 어떻게해야 합니까? Mesh 클래스의 속성과 메서드는 탄탄한 토대를 제공하지만 머지 않아 그 이상의 것을 원할 것입니다.

구출하기 위한 방법: Starling의 메쉬 스타일.

스타일은 Starling에 새로 추가된 것으로 (버전 2.0에서 도입) 맞춤형 고성능 렌더링 코드를 만드는 데 권장되는 방법입니다. 사실 Starling의 모든 렌더링은 이제 메쉬 스타일을 통해 수행됩니다.

  • 스타일은 임의의 메시 (Mesh 클래스 또는 그 서브 클래스의 인스턴스)에 할당될 수 있습니다.

  • 기본적으로 각 메쉬의 스타일은 기본 MeshStyle 클래스의 인스턴스입니다.

  • 후자는 Starling의 표준 렌더링 기능을 제공합니다. 색칠하고 질감이 있는 삼각형을 그립니다.

메시에 새로운 트릭을 가르치기 위해 MeshStyle을 확장할 수 있습니다. 이를 통해 모든 종류의 재미있는 효과를 위한 맞춤 셰이더 프로그램을 만들 수 있습니다. 예를 들어 빠른 색상 변환 또는 멀티 텍스처링을 구현할 수 있습니다.

스타일의 가장 인상적인 샘플 중 하나는 Dynamic Lighting extension, 동적 조명 확장입니다. 노멀 맵 (텍스쳐 서페이스 법선)을 사용하여 현실적인 실시간 조명 효과를 제공 할 수 있습니다. Starling Wiki에서 이 확장 기능을 확인해보십시오!

스타일을 사용하려면 그것을 인스턴스화하고 메쉬의 스타일 속성에 지정하십시오

var image:Image = new Image(texture);
var lightStyle:LightStyle = new LightStyle(normalTexture);
image.style = lightStyle;
Dynamic Lighting
Figure 32. Dynamic Lighting 확장 기능.

스타일은 매우 다양합니다; 가능한 응용 프로그램은 거의 제한이 없습니다. 그리고 같은 스타일의 메쉬가 함께 일괄 처리될 수 있으므로 어떤 방식으로든 성능을 희생하지 않아도 됩니다. 이 점에서 이들은 조각 필터 (비슷한 목적을 수행함)보다 훨씬 효율적입니다.

스타일의 주된 단점은 단지 메시 (스프라이트가 아닌)에만 할당할 수 있고, 실제 메시 영역 내에서만 작동할 수 있으므로 (블러 같은 것을 불가능하게 만든다는 것). 또한 하나의 메쉬에 여러 스타일을 결합할 수 없습니다.

스타일은 Starling 개발자가 익숙해야 할 강력한 도구입니다. 계속 지켜봐 주십시오: 나중의 섹션에서는 처음부터 자신의 메쉬 스타일을 만드는 법을 보여드리겠습니다.

Mesh와 MeshStyle의 차이점에 대해 아직도 혼란스럽다면 Mesh는 꼭지점 목록일 뿐이며, 그 꼭지점이 어떻게 삼각형을 생성하는지에 대한 것을 알면 됩니다.

스타일은 각 정점에 추가 데이터를 추가하여 렌더링시 사용할 수 있습니다. 표준 MeshStyle은 색상 및 텍스처 좌표를 제공합니다. MultiTextureStyle은 텍스처 좌표의 추가 세트를 추가할 수 있습니다. 그러나 스타일은 결코 객체의 원래 모양을 수정해서는 안됩니다. 정점을 추가하거나 제거하거나 위치를 변경하지 않습니다.

2.10. 마스크(Masks)

마스크를 사용하여 표시 객체의 일부를 잘라낼 수 있습니다. 마스크를 다른 표시 객체의 내용을 볼 수 있는 "구멍" 이라고 생각하십시오. 그 구멍은 임의의 모양을 가질 수 있습니다.

고전적인 디스플레이 목록의 "마스크" 속성을 사용했다면 이 기능을 바로 알아차렸을 것입니다. 아래와 같이 새 마스크 속성에 표시 객체를 할당하면 됩니다. 모든 표시 객체는 마스크로 작동할 수 있으며 표시 목록에 포함되거나 포함되지 않을 수 있습니다.

var sprite:Sprite = createSprite();
var mask:Quad = new Quad(100, 100);
mask.x = mask.y = 50;
sprite.mask = mask; // ← 쿼드를 마스크로 사용

그러면 다음과 같은 결과가 나타납니다:

Rectangular Mask
Figure 33. 사각형 마스크 사용하기.

마스크 뒤에 있는 논리는 간단합니다. 마스크된 객체의 픽셀은 마스크의 다각형 내에 있는 경우에만 그려집니다. 이것은 중요합니다. 마스크의 모양은 텍스처가 아닌 다각형으로 정의됩니다! 따라서, 그러한 마스크는 순전히 바이너리입니다. 픽셀은 가시적이거나 그렇지 않을 수 있습니다.

마스크와 AIR

AIR 응용 프로그램에서 마스크가 작동하려면 응용 프로그램 설명자에서 스텐실 버퍼를 활성화해야 합니다. initialWindow 요소에 다음 설정을 추가합니다:

<depthAndStencil>true</depthAndStencil>

걱정하지 마십시오. Starling은 잊고 설정하지 않았을 경우, 콘솔에 경고를 출력합니다.

2.10.1. 캔버스 및 다각형(Canvas and Polygon)

"이 마스크 피처는 정말 멋지게 보입니다" 라고 말할 수 있습니다. "하지만 내가 말하는 그 임의의 도형을 어떻게 만들지?!" 글쎄요. 당신이 물어봐줘서 고맙군요!

실제로 마스크는 텍스처가 아닌 지오메트리에만 의존하기 때문에 마스크 모양을 그릴 수 있는 방법이 필요합니다. 우연의 일치에서 실제로 이 작업을 수행하는 데 도움이 되는 두 가지 클래스인 Canvas 및 Polygon이 있습니다. 그들은 스텐실 마스크와 함께 사용합니다.

Canvas 클래스의 API는 Flash의 Graphics 객체와 비슷합니다. 예제: 빨간 동그라미를 그립니다:

var canvas:Canvas = new Canvas();
canvas.beginFill(0xff0000);
canvas.drawCircle(0, 0, 120);
canvas.endFill();

타원 직사각형 또는 임의의 다각형을 그리는 방법도 있습니다.

이러한 기본 메소드 이외에도 Canvas 클래스는 다소 제한적입니다. 아직 Graphics 클래스에 대한 완전한 대안을 기대하지 마십시오. 그러나 향후 릴리스에서 변경 될 수 있습니다!

그러면 Polygon 클래스가 생깁니다. 다각형 (패키지 starling.geom)은 여러 개의 직선 세그먼트로 정의된 닫힌 모양을 나타냅니다. Flash의 Rectangle 클래스의 후계이지만 임의의 모양을 지원합니다.

Canvas에는 폴리곤 객체에 대한 직접적인 지원이 포함되어 있으므로 Polygon의 이상적인 동반자입니다. 이 한 쌍의 수업은 모든 마스크 관련 요구 사항을 해결할 것입니다.

var polygon:Polygon = new Polygon(); (1)
polygon.addVertices(0,0,  100,0,  0,100);

var canvas:Canvas = new Canvas();
canvas.beginFill(0xff0000);
canvas.drawPolygon(polygon); (2)
canvas.endFill();
1 이 다각형은 삼각형을 나타냅니다.
2 삼각형을 캔버스에 그립니다.

마스크에 대해 알려주고 싶은게 몇 가지 더 있습니다:

표시(Visibility)

마스크 자체는 결코 보이지 않습니다. 마스크된 표시 객체에 미치는 영향을 통해 간접적으로만 표시됩니다.

포지셔닝(Positioning)

마스크가 표시 목록에 포함되지 않은 경우 (즉 부모가 없는 경우) 마스크된 객체의 로컬 좌표계에 그려집니다. 객체를 이동하면 마스크가 따라옵니다. 마스크가 표시 목록의 일부인 경우 해당 위치는 평소대로 계산됩니다.

스텐실 버퍼(Stencil Buffer)

씬(Scenes) 뒤에서, 마스크는 GPU의 스텐실 버퍼를 사용하여 매우 가볍고 빠릅니다. 하나의 마스크는 스텐실 버퍼에 마스크를 그리는 마스크와 마스크된 모든 컨텐츠가 렌더링되었을 때 마스크를 제거하는 마스크라는 두 가지 드로우 콜이 필요합니다.

가위 사각형(Scissor Rectangle)

마스크가 스테이지 축과 평행한 텍스쳐 없는(untextured) Quad인 경우 Starling은 렌더링을 최적화할 수 있습니다. 스텐실 버퍼 대신에 가위 직사각형을 사용하여 하나의 드로우 콜로 절약할 수 있습니다.

텍스쳐 마스크(Texture Masks)

단순한 벡터 모양으로 잘라내지 못하면 텍스쳐의 알파 채널을 스텐실 마스크로 사용할 수 있는 확장 기능이 있습니다. Texture Mask라고 불리며 Starling Wiki에 있습니다.

2.11. 스프라이트 3D(Sprite3D)

이전 섹션에서 살펴본 모든 표시 객체는 순수한 2차원 객체를 나타냅니다. Starling은 2D 엔진입니다. 그러나 2D 게임에서도 간단한 3D 효과를 추가하면 더 나을 것입니다. 두 화면 사이를 전환하거나 게임 카드의 뒷면을 표시하는 등의 작업 말이죠.

이러한 이유 때문에 Starling에는 기본 3D 기능인 Sprite3D를 쉽게 추가할 수있는 클래스가 포함되어 있습니다. 그것은 여러분이 3차원 공간에서 2D 객체를 움직일 수 있게 합니다.

2.11.1. 기본

기존의 Sprite와 마찬가지로 이 컨테이너에 자식을 추가 및 제거할 수 있으므로 여러 표시 객체를 그룹화 할 수 있습니다. 그러나 Sprite3D는 여러 가지 흥미로운 속성을 제공합니다:

  • z — Z 축을 따라 스프라이트를 이동합니다 (카메라에서 멀어지는 방향).

  • rotationX — 스프라이트를 x축을 중심으로 회전합니다.

  • rotationY — 스프라이트를 y축을 중심으로 회전합니다.

  • scaleZ — Z축을 따라 스프라이트의 크기를 조절합니다.

  • pivotZ — 피벗 점을 z축을 따라 이동합니다.

이러한 속성 덕분에 스프라이트와 모든 자식을 3D 세계에 배치할 수 있습니다.

var sprite:Sprite3D = new Sprite3D(); (1)

sprite.addChild(image1); (2)
sprite.addChild(image2);

sprite.x = 50; (3)
sprite.y = 20;
sprite.z = 100;
sprite.rotationX = Math.PI / 4.0;

addChild(sprite); (4)
1 Sprite3D의 인스턴스를 만듭니다.
2 몇 가지 일반적인 2D 객체를 스프라이트에 추가합니다.
3 3D 공간에서 물체의 위치와 방향을 설정합니다.
4 평상시처럼 디스플레이 목록에 추가하십시오.

보시다시피 Sprite3D를 사용하는 것은 어렵지 않습니다. 탐색할 몇 가지 새로운 속성이 있습니다. 히트-테스트, 애니메이션, 사용자 정의 렌더링 - 다른 모든 표시 객체에서 익숙해진 것처럼 모든 것이 작동합니다.

2.11.2. 카메라 설정(Camera Setup)

물론 3D 오브젝트를 표시하는 경우 해당 오브젝트를 보는 퍼스펙티브를 구성할 수 있기를 원합니다. 카메라를 설치하면 가능합니다. Starling에서는 카메라 설정이 무대에서 발견됩니다.

다음 스테이지 속성은 카메라를 설정합니다:

  • fieldOfView — 시야 (FOV)의 각도 (라디안 0과 PI[파이] 사이)를 지정합니다.

  • focalLength — 스테이지와 카메라 사이의 거리.

  • projectionOffset — 카메라를 기본 위치에서 멀리 이동시키는 벡터로 무대 중앙 바로 앞에 있습니다.

Camera Diagram
Figure 34. 이들은 카메라를 설정하는 속성입니다.

Starling은 스테이지가 전체 뷰포트를 채울 수 있는지 항상 확인합니다. 시야를 변경하면 초점 길이가 이 제약 조건을 준수하도록 수정되고 다른 방법은 반올림 됩니다. 다른 말로 하면 fieldOfView와 focalLength는 동일한 속성의 다른 표현입니다.

다음은 서로 다른 fieldOfView 값이 Starling 데모에서 큐브의 모양에 어떻게 영향을 미치는지 보여주는 예입니다:

Field-of-View
Figure 35. fieldOfView의 다른 값 (도).

기본적으로 카메라는 항상 스테이지 중앙을 향하도록 정렬됩니다. projectionOffset을 사용하면 이 시점에서 원근감을 변경할 수 있습니다. 다른 방향에서 물건을 보려고 할 때 사용하십시오. 위 또는 아래에서. 이번에는 projectionOffset.y에 대해 다른 설정을 사용하여 다시 큐브를 만듭니다:

Projection Offset
Figure 36. projectionOffset.y의 다른 값.

2.11.3. 제한 사항

Starling은 여전히 2D 엔진이므로 몇 가지 제한 사항이 있습니다:

  • Starling은 깊이 테스트를 하지 않습니다. 시정은 아동의 순서에 의해서만 결정됩니다.

  • 성능에 주의해야 합니다. 각 Sprite3D 인스턴스는 일괄 처리를 중단합니다.

그러나 많은 경우에 후자의 문제를 완화하는 속임수가 있습니다. 객체가 실제로 3D로 변환되지 않을 때 즉 2D 스프라이트도 마찬가지로 수행할 수없는 경우 Starling은 이것을 2D 객체처럼 취급합니다 - 동일한 성능 및 배치 동작.

즉 많은 수의 Sprite3D 인스턴스가 있는 것을 피할 필요가 없습니다. 당신은 너무 많은 것들이 동시에 3D 변환된다는 것을 피하기만 하면 됩니다.

2.11.4. 샘플 프로젝트

이 기능을 실제 프로젝트에서 사용하는 방법을 보여주는 비디오 자습서를 만들었습니다. 그것은 2D 게임을 3차원으로 옮기는 방법을 보여줍니다.

  • Vimeo 에서 비디오를 시청하세요.

  • GitHub 에서 소스를 다운로드 하세요.

2.12. 유틸리티(Utilities)

starling.utils 패키지에는 간과해서는 안되는 몇 가지 유용한 작은 도우미가 들어 있습니다.

2.12.1. 컬러(Colors)

기존의 Flash 및 Starling에서 색상은 16진수 형식으로 지정됩니다. 몇 가지 예가 있습니다:

// 형식:         0xRRGGBB
var red:Number   = 0xff0000;
var green:Number = 0x00ff00; // 또는 0xff00
var blue:Number  = 0x0000ff; // 또는 0xff
var white:Number = 0xffffff;
var black:Number = 0x000000; // 또는 simply 0

Color 클래스는 명명된 색상 값 목록을 포함합니다. 또한 이 도구를 사용하여 색상의 구성 요소에 쉽게 액세스 할 수 있습니다:

var purple:uint = Color.PURPLE; (1)
var lime:uint   = Color.LIME;
var yellow:uint = Color.YELLOW;

var color:uint = Color.rgb(64, 128, 192); (2)

var red:int   = Color.getRed(color);   // ->  64 (3)
var green:int = Color.getGreen(color); // -> 128
var blue:int  = Color.getBlue(color);  // -> 192
1 몇 가지 공통 색상이 미리 정의되어 있습니다.
2 이 방법으로 다른 색상을 만들 수 있습니다. 이 방법으로 RGB 값을 전달하면 됩니다 (범위 0 - 255).
3 각 채널의 정수 값을 추출할 수도 있습니다.

2.12.2. 각도(Angles)

Starling은 모든 각도를 라디안 단위로 계산합니다 (플래시의 경우 각도가 다른 곳에서는 각도를 사용하고 다른 각도에서는 라디안을 사용함). 각도와 라디안을 변환하려면 다음과 같은 간단한 함수를 사용할 수 있습니다.

var degrees:Number = rad2deg(Math.PI); // -> 180
var radians:Number = deg2rad(180);     // -> PI

2.12.3. 문자열 유틸(StringUtil)

format 메서드를 사용하여 .Net / C # 형식의 문자열 형식을 지정할 수 있습니다.

StringUtil.format("{0} plus {1} equals {2}", 4, 3, "seven");
  // -> "4 plus 3 equals seven"

같은 클래스에는 문자열의 시작과 끝에서 공백을 제거하는 메서드가 포함되어 있습니다. 사용자 입력을 처리해야 할 때마다 빈번히 작업합니다.

StringUtil.trim("  hello world\n"); // -> "hello world"

2.12.4. 시스템 유틸(SystemUtil)

앱이나 게임이 현재 실행되는 환경에 대한 정보를 찾기에 유용합니다. SystemUtil에는 해당 작업을 돕는 몇 가지 메소드와 속성이 있습니다.

SystemUtil.isAIR; // AIR 또는 플래시?
SystemUtil.isDesktop; // 데스크탑 또는 모바일?
SystemUtil.isApplicationActive; // 사용 중이거나 최소화 되었습니까?
SystemUtil.platform; // WIN, MAC, LNX, IOS, ...

2.12.5. 수학 유틸(MathUtil)

이 클래스는 주로 몇 가지 기하학적 문제를 해결하도록 설계되었지만 다음과 같은 매우 유용한 도우미 메서드도 포함되어 있습니다:

var min:Number = MathUtil.min(1, 10); (1)
var max:Number = MathUtil.max(1, 10); (2)
var inside:Number = MathUtil.clamp(-5, 1, 10); (3)
1 두 숫자 중 가장 작은 숫자를 구하십시오. 결과: 1
2 두 숫자 중 가장 큰 숫자를 구하십시오. 결과: 10
3 번호 (첫 번째 인수)를 특정 범위로 이동합니다. 결과: 1

과거에 AS3을 사용해 본 경험이 있다면, 비슷한 수학 클래스가 이미 기본 수학 클래스에서 제공되는데 왜 이러한 메소드를 작성했는지 궁금할 것입니다.

불행히도 예전 메소드에는 부작용이 있습니다: 예를 들어 Math.min을 호출할 때마다 임시 객체를 만들게 됩니다 (적어도 iOS용 앱을 컴파일하면 그렇습니다). 새로운 메소드는 그러한 부작용이 없으므로 항상 선호해야 합니다.

2.12.6. 풀링(Pooling)

이제 임시 객체에 대한 주제를 다루었으므로 Pool 클래스를 소개할 때가 되었습니다.

숙련된 AS3 개발자는 모든 객체 할당이 가격으로 제공된다는 사실을 알고 있습니다. 나중에 객체는 가비지 컬렉터에 의해 수집되어야 합니다. 이것은 완전히 씬(Scenes) 뒤에서 발생합니다. 당신은 이것을 알아 채지 못할 것입니다.

그러나 정리 프로세스가 너무 많은 시간이 걸리면 앱이 잠시 멈춥니다. 이러한 일이 자주 발생하면 사용자는 불편을 겪게됩니다.

이 문제를 피하기 위한 한 가지 방법은 개체를 재활용하고 반복적으로 사용하는 것입니다. 예를 들어 Point 및 Rectangle과 같은 클래스는 잠시 동안만 필요한 경우가 많습니다. 즉 만들고 데이터를 채우고 버립니다.

이제부터 Starling의 Pool 클래스가 이러한 객체를 처리하도록 합니다.

var point:Point = Pool.getPoint(); (1)
doSomethingWithPoint(point);
Pool.putPoint(point); (2)

var rect:Rectangle = Pool.getRectangle(); (1)
doSomethingWithRectangle(rect);
Pool.putRectangle(rect); (2)
1 풀에서 객체를 가져옵니다. 그것은 클래스에서 new를 호출하는 것을 대체합니다.
2 더 이상 필요가 없을 때 Pool에 다시 넣으십시오.

이 클래스는 Vector3D, Matrix 및 Matrix3D도 비슷한 스타일로 지원합니다.

항상 get과 put 호출이 균형을 이루는지 확인하십시오. 수영장에 너무 많은 물체를 집어 넣고 그것들을 꺼내지 않으면 시간이 지남에 따라 너무 많은 메모리를 사용하게 됩니다.

2.12.7. 더 나아가서 …​

starling.utilities 패키지에는 여기에 나열할 수 있는 것보다 더 많은 도우미가 들어 있습니다. 메소드 및 클래스의 전체 목록은 API 레퍼런스를 참조하십시오. 그것은 분명히 대가를 지불할 것입니다!

2.13. 요약

이제 Starling Framework의 모든 기본 개념을 잘 알고 있을 것입니다. 이것은 여러분이 머리 속에 가지고 있는 게임이나 창조되기를 기다리고 있는 그 앱으로 시작하는데 필요한 모든 지식입니다.

다른 한편으로 우리의 작은 새 (새들은 소매자락을 가졌습니까?)의 소매자락을 돌리는 몇 가지 트릭이 있습니다. 만약 당신이 그 고급 주제로 뛰어들 준비가 되면 제 리드를 따르십시오.

3. 고급 주제

이전 장에서 배운 모든 것들을 통해 실제 프로젝트에서 Starling을 시작할 수 있습니다. 그러나 이렇게 하는 동안 수수께끼 같은 몇 가지 문제가 발생할 수 있습니다. 예를 들어,

  • 텍스처가 사용 가능한 모든 메모리를 빠르게 소모합니다.

  • 때때로 컨텍스트 손실이 발생합니다. 무슨 이런 일이!? [4]

  • 실제로 응용 프로그램의 성능에 조금 실망합니다. 더 빠른 속도를 원해요!

  • 또는 자신의 버텍스 및 프래그먼트 셰이더를 쓰고 싶지만 시작점을 알지 못하는 매조키스트 중 하나일 수 있습니다.

재미있게도, 이 장의 내용을 완벽하게 요약한 내용들입니다. 꽉 잡아요, 도로시! 우리는 이제 몇 가지 고급 주제로 뛰어 들어갈거에요!

3.1. ATF 텍스쳐

기존 플래시에서는 대부분의 개발자가 이미지에 PNG 형식을 사용하고 투명성이 필요하지 않은 경우 JPG 형식을 사용합니다. 그것들은 Starling에서도 매우 인기가 있습니다. 그러나 Stage3D는 압축 텍스처를 저장할 수있는 Adobe Texture Format과 같은 몇 가지 고유한 장점을 가진 대안을 제공합니다.

  • 압축된 텍스처는 기존의 일부 텍스처만 필요합니다.

  • 압축 풀기는 GPU에서 직접 수행됩니다.

  • 그래픽 메모리에 업로드하는 것이 더 빠릅니다.

  • 업로드는 비동기적으로 수행할 수 있습니다: 게임 플레이를 중단하지 않고 새로운 텍스처를 로드할 수 있습니다.[5]

3.1.1. 그래픽 메모리

우리가 계속하기 전에 어쨌든 텍스쳐가 얼마나 많은 메모리를 필요로 하는지 아는 것은 흥미로울 것입니다.

PNG 이미지는 모든 픽셀에 대해 4개의 채널을 저장합니다. 빨강 초록 파랑 및 알파 각 8 비트 (채널당 256 개의 값을 가짐). 512 x 512 픽셀 텍스처가 차지하는 공간을 계산하는 것은 쉽습니다:

512 × 512 RGBA 텍스처의 메모리 풋 프린트(footprint):
512 × 512 pixels × 4 bytes = 1,048,576 bytes ≈ 1 MB

JPG 이미지도 계산은 비슷합니다; 알파 채널만 제외하면 됩니다.

512 × 512 RGB 텍스처의 메모리 풋 프린트(footprint):
512 × 512 pixels × 3 bytes = 786,432 bytes ≈ 768 kB

작은 텍스처에 비하면 상당히 많은 양입니다. 그렇죠? PNG와 JPG의 기본 제공 파일 압축은 도움이 되지 않습니다. Stage3D에서 이미지를 처리하려면 이미지의 압축을 풀어야 합니다. 즉 파일 크기는 중요하지 않습니다. 메모리 소비는 항상 위 공식을 사용하여 계산됩니다.

그럼에도 불구하고 텍스처가 그래픽 메모리에 쉽게 들어 맞으면 바로 사용할 수 있습니다. 이러한 형식은 작업하기 매우 쉽습니다. 특히 응용 프로그램이 데스크탑 하드웨어를 대상으로 하는 경우에는 여러 가지 상황에서 문제가 되지 않습니다.

그러나 메모리 소비가 장치에서 사용 가능한 것보다 높은 개발 단계가 있을 수 있습니다. ATF 형식을 살펴볼 적절한 시간이 되었네요.

3.1.2. 압축 텍스처

위에서 우리는 전통적인 텍스처의 파일 크기가 얼마나 많은 그래픽 메모리와 관련이 있는지를 배웠습니다. 엄청나게 압축된 JPG라도 순수한 BMP 형식의 동일한 이미지 만큼의 공간을 차지합니다.

하지만 이것은 압축된 텍스처에서는 그렇지 않습니다. 그것은 GPU에서 직접 처리 할 수 있게 됩니다. 즉, 압축 설정에 따라 같은 공간에 최대 10배의 텍스처를 로드할 수 있습니다. 아주 인상적이네요. 그렇죠?

불행한 것은 각 GPU 공급 업체가 각각 다른 회사들보다 잘 할 수 있다고 생각했기 때문에 압축된 텍스쳐를 위한 몇 가지 다른 포맷을 만들었습니다. 즉 게임이 실행되는 환경에 따라 다른 종류의 텍스처가 필요합니다. 그럼 파일이 어디에서 사용될지 미리 어떻게 알 수 있을까요?

구출을 위해 ATF가 왔습니다. 이는 Adobe가 Stage3D에서 특별히 만든 형식입니다. 사실 이것은 텍스처의 최대 네 가지 버전을 포함할 수 있는 컨테이너 파일입니다.

  • PVRTC (PowerVR 텍스처 압축) 는 PowerVR GPU에서 사용됩니다. 모든 세대의 iPhone, iPod Touch 및 iPad에서 지원됩니다.

  • DXT1/5 (S3 텍스처 압축) 는 원래 S3 Graphics에서 개발했습니다. Nvidia와 AMD GPU에서 모두 지원되므로 대부분의 데스크탑 컴퓨터와 일부 안드로이드 폰에서 사용할 수 있습니다.

  • ETC (Ericsson 텍스처 압축) 는 대부분의 Android, 특히, 많은 휴대 전화에서 사용됩니다.

  • ETC2 는 고품질 RGB 및 RGBA 압축을 제공합니다. OpenGL ES 3을 지원하는 모든 Android 및 iOS 기기에서 지원됩니다.

저는 ATF가 컨테이너 형식이라는 것을 위에서 알려드렸습니다. 이는 위 형식의 모든 조합을 포함할 수 있음을 의미합니다.

ATF container
Figure 37. ATF 파일은 실제로 다른 형식의 컨테이너입니다.

모든 형식 (기본값)을 포함 시키면 응용 프로그램이 iOS, Android 또는 데스크탑에서 실행 중인지 관계없이 모든 Stage3D 지원 장치에 텍스처를 로드할 수 있습니다. 아무것도 신경 쓸 필요가 없습니다!

그러나 게임이 iOS 기기에만 포된다는 것을 알고 있다면 PVRTC를 제외한 모든 형식을 생략할 수 있습니다. 또는 고급 휴대 기기 (OpenGL ES 3 이상) 만 타겟팅하는 경우 ETC2 만 포함하십시오. Android와 iOS에서 모두 작동합니다. 그렇게하면 게임의 다운로드 크기를 최적화 할 수 있습니다.

DXT1과 DXT5의 차이는 후자가 알파 채널을 지원한다는 것입니다. ATF 도구가 자동으로 올바른 형식을 선택합니다.

ETC1은 실제로 알파 채널을 지원하지 않지만 Stage3D는 내부적으로 두 개의 텍스처를 사용하여 이 문제를 해결합니다. 다시 말하지만 이것은 완전히 씬(Scenes) 뒤에서 발생합니다.

3.1.3. ATF 텍스처 만들기

Adobe는 ATF로 변환하거나 ATF에서 변환하고 생성된 파일을 미리 볼 수 있는 일련의 명령행 도구를 제공합니다. 것들은 AIR SDK의 일부입니다 (atftools 폴더 찾기).

아마도 가장 중요한 도구는 png2atf일 것입니다. 다음은 기본적인 사용 예입니다. 사용 가능한 모든 형식의 표준 설정으로 텍스처를 압축합니다.

png2atf -c -i texture.png -o texture.atf

지금 당장 사용을 시도했다면 다음과 같은 오류 메시지가 표시될 것입니다:

Dimensions not a power of 2!

이것이 아직 언급하지 않은 한계입니다. ATF 텍스처는 항상 2의 배수인 사이드 길이를 가져야 합니다. 약간 짜증나지만 실제로는 거의 문제가 되지 않습니다. 거의 항상 아트라스 텍스처로 사용하기 때문입니다.

대부분의 아틀라스 생성기는 2의 2승 텍스처를 생성하도록 구성할 수 있습니다.

호출이 성공하면 ATFViewer에서 출력을 검토할 수 있습니다:

ATFViewer
Figure 38. ATFViewer 도구.

왼쪽의 목록에서 보려는 내부 형식을 선택할 수 있습니다. 또한 기본적으로 모든 밉맵 변형이 만들어 졌음을 알 수 있습니다.

'[Memory Management], 메모리 관리' 장에서 밉맵을 논의할 것입니다.

압축으로 인해 화질이 약간 떨어지는 것을 알 수 있습니다. 이는 모든 압축 형식이 손실이 크기 때문입니다. 메모리 크기가 작아질수록 품질이 떨어집니다. 이미지의 종류에 따라 품질이 얼마나 달라지는가 유기적인 사진 같은 질감은 잘 작동하지만 딱딱한 가장자리가 있는 만화 같은 이미지는 상당히 심하게 손상 될 수 있습니다.

물론이 도구는 다양한 옵션을 제공합니다. 물론 여러분은 iOS에만 적용가능한 PVRTC 형식만 패키지로 만들 수 있습니다:

png2atf -c p -i texture.png -o texture.atf

또는 메모리를 절약하기 위해 밉맵을 생략하도록 할 수 있습니다:

png2atf -c -n 0,0 -i texture.png -o texture.atf

또 다른 유용한 유틸리티는 atfinfo입니다. 포함된 텍스처 형식 밉맵 수 등과 같이 특정 ATF 파일에 저장된 데이터에 대한 세부 정보를 표시합니다:

> atfinfo -i texture.atf

File Name          : texture.atf
ATF Version        : 2
ATF File Type      : RAW Compressed With Alpha (DXT5+ETC1/ETC1+PVRTV4bpp)
Size               : 256x256
Cube Map           : no
Empty Mipmaps      : no
Actual Mipmaps     : 1
Embedded Levels    : X........ (256x256)
AS3 Texture Class  : Texture (flash.display3D.Texture)
AS3 Texture Format : Context3DTextureFormat.COMPRESSED_ALPHA

3.1.4. ATF 텍스처 사용

Starling에서 압축된 텍스처를 사용하는 것은 다른 텍스처와 마찬가지로 간단합니다. 파일 내용이 있는 바이트 배열을 팩토리 메서드 Texture.fromAtfData()에 전달합니다.

var atfData:ByteArray = getATFBytes(); (1)
var texture:Texture = Texture.fromATFData(atfData); (2)
var image:Image = new Image(texture); (3)
1 원시 데이터를 파일에서 가져옵니다.
2 ATF 텍스처를 만듭니다.
3 다른 텍스쳐와 같이 사용하십시오.

그게 다입니다! 이 텍스처는 Starling의 다른 텍스처와 마찬가지로 사용할 수 있습니다. 또한 아틀라스 텍스처에 가장 적합한 후보입니다.

그러나 위 코드는 텍스처를 동기적으로 업로드합니다. 즉 AS3 실행은 완료될 때까지 일시 중지됩니다. 대신 비동기적으로 텍스처를 로드하려면 메서드에 콜백을 전달합니다.

Texture.fromATFData(atfData, 1, true,
    function(texture:Texture):void
    {
        var image:Image = new Image(texture);
    });

매개 변수 2와 3은 배율 인수와 밉맵을 각각 사용해야 하는지 여부를 제어합니다. 네 번째는 콜백을 전달하면 비동기 적재가 시작됩니다. Starling은 그동안 아무렇지도 않게 렌더링을 계속할 수 있습니다. 콜백이 실행 되자마자 (더 빨리!) 텍스쳐를 사용할 수 있습니다.

물론 AS3 소스에 ATF 파일을 직접 포함시킬 수도 있습니다.

[Embed(source="texture.atf", mimeType="application/octet-stream")]
public static const CompressedData:Class;

var texture:Texture = Texture.fromEmbeddedAsset(CompressedData);

그러나, 이 경우 비동기 업로드를 사용할 수 없습니다.

3.1.5. 기타 리소스

다음 소스에서 이 주제에 대한 자세한 내용을 볼 수 있습니다:

3.2. 컨텍스트 손실(Context Loss)

모든 Stage3D 렌더링은 "렌더링 컨텍스트"(Context3D 클래스의 인스턴스)를 통해 발생합니다. 액티브 텍스처 목록 버텍스 데이터에 대한 포인터 등과 같이 GPU의 모든 현재 설정을 저장합니다. 렌더링 컨텍스트는 GPU에 대한 연결이므로 Stage3D 렌더링을 수행할 수 없습니다.

그리고 문제는 여기에 있습니다. 그 맥락은 때때로 잃어 버릴 수 있습니다. 즉 그래픽 메모리에 저장된 모든 데이터에 대한 참조가 손실된다는 의미입니다. 가장 주목할 만한건 텍스쳐입니다.

이러한 컨텍스트 손실은 모든 시스템에서 똑같이 자주 발생하지는 않습니다. 그것은 iOS와 macOS에서 드문 일이며 때로는 Windows에서 발생하며 매우 자주 Android에서 발생합니다 (화면 회전? 빵야!). 그러므로 주위를 둘러 볼 방법이 없습니다. 최악의 상황을 예상하고 상황에 대비할 준비를 해야 합니다.

3.2.1. 기본 동작

Starling은 현재 렌더링 컨텍스트가 손실되었음을 인식하면 다음 절차를 시작합니다:

  • Starling은 자동으로 새 컨텍스트를 만들고 이전과 같은 설정으로 초기화합니다.

  • 모든 정점(Vertex-) 버퍼와 인덱스 버퍼가 복원됩니다.

  • 모든 정점 및 조각 프로그램 (셰이더)은 다시 컴파일됩니다.

  • 텍스처는 가능한 모든 수단으로 복원됩니다 (메모리 / 디스크 / 등에서).

버퍼와 프로그램을 복원하는 것은 문제가 되지 않습니다. Starling에는 필요한 모든 데이터가 있으며 시간이 많이 걸리지 않습니다. 그러나 텍스처는 두통을 야기합니다. 이를 설명하기 위해 최악의 경우인 임베디드 비트맵에서 생성된 텍스처를 살펴 보겠습니다:

[Embed(source="hero.png")]
public static const Hero:Class;

var bitmap:Bitmap = new Hero();
var texture:Texture = Texture.fromBitmap(bitmap);

Texture.fromBitmap을 호출하면 비트맵이 GPU 메모리에 업로드됩니다. 즉, 이제는 비트맵이 컨텍스트의 일부가 되었다는 것입니다. 우리가 영원히 살아있는 컨텍스트에 의존할 수 있다면, 게임은 끝났습니다.

그러나 우리는 그것에 의존할 수 없습니다. 텍스처 데이터가 언제든지 손실될 수 있습니다. 이것이 Starling이 원본 비트맵의 사본을 보관하는 이유입니다. 최악의 상황이 발생하면 텍스처를 다시 생성하는 데 사용됩니다. 그 모든 일은 씬(Scenes) 뒤에서 발생합니다.

보고 또 보세요! 이는 텍스처가 메모리에 세 번 있음을 의미합니다.

  • "Hero"클래스 (기본 메모리)

  • 백업 비트맵 (기본 메모리)

  • 텍스처 (그래픽 메모리)

우리가 모바일에서 직면하고 있는 엄중한 메모리 제약 조건을 감안할 때 이것은 재앙입니다. 이 일이 일어나기를 원하지 않습니다!

코드를 약간 변경하면 조금 더 좋아집니다:

// 대신 'fromEmbeddedAsset' 메소드를 사용하세요.
var texture:Texture = Texture.fromEmbeddedAsset(Hero);

그런 식으로 Starling은 내장된 클래스에서 직접 텍스처를 다시 만들 수 있습니다 (new Hero() 호출). 이는 텍스처가 메모리에 두 번만 있음을 의미합니다. 임베디드 애셋의 경우 최상의 선택입니다.

그러나 이상적으로는 텍스처를 한 번만 메모리에 저장하려고 할 것입니다. 이렇게 하려면 애셋을 포함시키지 말아야 합니다. 대신 로컬 또는 원격 파일을 가리키는 URL에서 로드해야 합니다. 그렇게 하면 URL만 저장하면 됩니다. 실제 데이터를 원래 위치에서 다시 로드할 수 있습니다.

이 일을 가능하게 하는 두 가지 방법이 있습니다:

  • AssetManager를 사용하여 텍스처를 로드하십시오.

  • 텍스처를 수동으로 복원하십시오.

가능한 경우 AssetManager를 사용하는 것이 좋습니다. 메모리를 낭비하지 않고 컨텍스트 손실을 처리합니다. 특별한 복원 논리를 추가할 필요가 없습니다.

그럼에도 불구하고 무엇이 일어나고 있는지 알면 좋습니다. 누가 알겠습니까? 수작업으로 복원하는 것이 유일한 선택이 될 수 있습니다.

3.2.2. 수동 복원

Texture.fromEmbeddedAsset()이 내부적으로 어떻게 작동하는지 궁금할 것입니다. 이 메소드의 가능한 구현을 살펴 보겠습니다:

public static function fromEmbeddedAsset(assetClass:Class):Texture
{
    var texture:Texture = Texture.fromBitmap(new assetClass());
    texture.root.onRestore = function():void
    {
        texture.root.uploadFromBitmap(new assetClass());
    };
    return texture;
}

당신은 root.onRestore 콜백에서 마법이 일어나고 있음을 알 수 있습니다. 잠깐 root는 무엇입니까?

당신은 그것을 알지 못할 수도 있지만 Texture 인스턴스를 가지고 있을 때 그것은 사실 구체적인 텍스처가 아닙니다. 실제로는 다른 텍스처 (SubTexture)의 일부에 대한 포인터일 수 있습니다. fromBitmap 호출조차 그런 텍스처를 반환할 수 있습니다! (그 뒤에 있는 추론을 설명하는 것은 이 장의 범위를 벗어날 것입니다.)

어쨌든 texture.root는 항상 ConcreteTexture 객체를 반환할 것이고 이것이 onRestore 콜백이 있는 곳입니다. 이 콜백은 컨텍스트 손실 직후에 실행되며 텍스처를 다시 생성할 기회를 제공합니다.

우리의 경우 콜백은 비트맵을 다시 한번 인스턴스화하여 루트 텍스처에 업로드 합니다. 여기! (Voilà), 텍스쳐가 복원되었습니다!

그러나 악마는 세부 사항에 있습니다. onRestore 콜백을 잘 모르는 상태에서 다른 비트맵 복사본을 저장하지 않도록 주의 깊게 작성해야 합니다. 실제로 완전히 쓸모없는 한 가지 무고한 예가 있습니다:

public static function fromEmbeddedAsset(assetClass:Class):Texture
{
    // 이 코드를 사용하지 마십시오! 나쁜 예제.

    var bitmap:Bitmap = new assetClass();
    var texture:Texture = Texture.fromBitmap(bitmap);
    texture.root.onRestore = function():void
    {
        texture.root.uploadFromBitmap(bitmap);
    };
    return texture;
}

오류를 발견 할 수 있습니까?

문제는 메서드가 Bitmap 객체를 만들어 콜백에서 사용한다는 것입니다. 그 콜백은 실제로 소위 폐쇄적입니다. 인라인 함수는 이 변수에 수반되는 변수 중 일부와 함께 저장됩니다. 즉 메모리에 남아 있는 함수 객체가 있어 컨텍스트를 잃을 때 호출 할 준비가 되었습니다. 그리고 명시적으로 말한 적이 없더라도 비트맵 인스턴스는 그 안에 저장됩니다. (사실 콜백 내부에서 비트맵을 사용했습니다.)

원래 코드에서 비트맵은 참조되지 않지만 콜백 내부에서 작성됩니다. 따라서 클로저에 저장할 비트맵 인스턴스가 없습니다. 어쨌든 assetClass 객체만 콜백에서 참조되며 메모리에 있습니다.

이 기술은 모든 종류의 시나리오에서 작동합니다:

  • URL에서 텍스처가 생성 된 경우 해당 URL 만 콜백에 전달하고 거기에서 다시로드하십시오.

  • ATF 텍스처의 경우 프로세스는 root.uploadATFData를 사용하여 데이터를 업로드해야 한다는 점을 제외하면 동일합니다.

  • 기존 표시 객체의 렌더링을 포함하는 비트맵의 경우 해당 표시 객체를 참조하고 콜백의 새 비트맵에 그려야 합니다. (그게 Starling의 TextField 클래스가 하는 일입니다.)

저를 강조하겠습니다: AssetManager가 이 모든 작업을 수행하므로 그렇게하는 것이 좋습니다. 나는 그것이 어떻게 성취되었는지를 보여주고 싶었습니다.

3.2.3. 텍스처 렌더링

컨텍스트 상실이 특히 심한 또 다른 영역: 텍스처를 렌더링할 때. 다른 텍스처와 마찬가지로 모든 내용을 잃어 버릴 수 있지만 쉽게 복원 할 수는 없습니다. 결국 그 내용은 임의의 수의 동적 그리기 작업의 결과입니다.

RenderTexture가 단지 아이-캔디 (예: 눈 속의 풋 프린트(발자국))에 사용되는 경우 그와 함께 살 수 있습니다. 반면에 그 내용이 중요하다면이 문제에 대한 해결책이 필요합니다.

그 주위에는 방법이 없습니다. 텍스처의 전체 내용을 수동으로 다시 그려야합니다. 다시 구조하기 위해 onRestore 콜백이 올 수 있습니다:

renderTexture.root.onRestore = function():void
{
    var contents:Sprite = getContents();
    renderTexture.clear(); // 텍스처 복원에 필요합니다.
    renderTexture.draw(contents);
});

나는 당신의 말을 들었습니다. 그것은 단지 하나의 객체 이상이었을 것입니다. 그러나 더 오랜 기간 동안 실행된 많은 draw call이 있습니다. 예를 들어 RenderTexture-canvas가 있는 드로잉 응용 프로그램에는 수십 가지 브러시 획이 들어 있을 것입니다.

이 경우 모든 그리기 명령에 대한 충분한 정보를 저장하여 재현할 수 있어야 합니다.

그리기 앱 시나리오를 고수하면 실행 취소 / 다시 실행 시스템에 대한 지원을 추가할 수 있습니다. 이러한 시스템은 일반적으로 개별 명령을 캡슐화하는 객체 목록을 저장하여 구현됩니다. 컨텍스트가 손실된 경우 해당 시스템을 다시 사용하여 모든 그리기 작업을 복원할 수 있습니다.

이제 이 시스템을 구현하기 전에 알아야 할 점이 하나 더 있습니다. root.onRestore 콜백이 실행된다고 해서 모든 텍스처가 이미 사용 가능한 것은 아닙니다. 결국 그들은 복원되어야 합니다. 그리고 그것은 시간이 좀 걸릴 것입니다!

그러나 AssetManager로 텍스처를 로드한 경우에는 텍스처가 덮여 있습니다. 이 경우 대신 TEXTURES_RESTORED 이벤트를 수신 할 수 있습니다. 또한 최적의 성능을 위해 drawBundled를 사용해야 합니다:

assetManager.addEventListener(Event.TEXTURES_RESTORED, function():void
{
    renderTexture.drawBundled(function():void
    {
        for each (var command:DrawCommand in listOfCommands)
            command.redraw(); // `renderTexture.draw()`를 실행합니다.
    });
});
이번에는 onRestore의 기본 동작이므로 명확하게 호출할 필요가 없습니다. 우리는 이를 수정하지 않았습니다. 여기서 다른 콜백 (Event.TEXTURES_RESTORED)에 있으며 onRestore가 기본 구현에서 수정되지 않았음을 기억하십시오.

3.3. 메모리 관리

많은 Starling 개발자는 이 프레임워크를 사용하여 모바일 장치 용 앱 및 게임을 만듭니다. 그리고 거의 모든 개발자가 조만간 모바일 장치가 메모리 부족으로 악명이 높다는 것을 알게 될 것입니다. 왜 그런가요?

  • 대부분의 휴대 기기에는 매우 높은 해상도의 화면을 사용합니다.

  • 이러한 장치 용 2D 게임은 고해상도 텍스처를 필요로 합니다.

  • 사용 가능한 RAM이 너무 작아서 모든 텍스처 데이터를 저장할 수 없습니다.

즉, 정말 악조건인 조합입니다.

메모리가 부족하면 어떻게 됩니까? 대부분의 경우, 유명한 오류 3691 ("이 리소스 유형에 대한 리소스 제한을 초과했습니다")이 표시되고 응용 프로그램이 중단됩니다. 다음과 같은 힌트는 이 심한 오류를 피하는 방법을 보여줍니다!

3.3.1. 폐기물 처리(Dispose your Waste)

더 이상 객체가 필요하지 않을 때 객체를 폐기하는 것을 잊지 마십시오. 기존의 Flash 객체와 달리 가비지 수집기는 Stage3D 리소스를 정리하지 않습니다! 당신은 그 기억에 대한 책임이 있습니다.

텍스쳐(Textures)

그것들은 당신이 돌봐야 할 가장 중요한 물건입니다. 텍스처는 항상 메모리의 가장 큰 부분을 차지합니다.

Starling은 물론 당신을 도와줍니다. 예를 들어, 아틀라스에서 텍스처를 로드하면 실제 서브 텍스처가 아닌 아틀라스만 처리하면 됩니다. 아틀라스만 GPU 메모리가 필요합니다. "offspring" 텍스처는 아틀라스 텍스처를 참조합니다.

var atlas:TextureAtlas = ...;
var hero:Texture = atlas.getTexture("hero");

atlas.dispose(); // "hero"도 무효화됩니다.
디스플레이 오브젝트(Display Objects)

디스플레이 오브젝트 자체는 많은 그래픽 메모리 (일부는 전혀 필요하지 않음)를 필요로하지 않지만, 디스플레이 오브젝트는 무효화(dispose)하는 것이 좋습니다. TextField와 같은 "heavy, 무거운" 객체는 특히 조심하십시오.

디스플레이 오브젝트 컨테이너는 예상대로 모든 하위 항목을 처리합니다. 컨테이너를 처분하면 모든 자식 객체가 자동으로 무효화됩니다.

var parent:Sprite = new Sprite();
var child1:Quad = new Quad(100, 100, Color.RED);
var child2:Quad = new Quad(100, 100, Color.GREEN);

parent.addChild(child1);
parent.addChild(child2);

parent.dispose(); // 자식 객체도 함께 무효화됩니다.

그러나 최근 Starling 버전은 디스플레이 객체를 폐기할 때 더욱 관대해졌습니다. 대부분의 디스플레이 오브젝트는 더 이상 Stage3D 자원을 저장하지 않으므로 처분하는 것을 잊어 버려도 괜찮긴 합니다.

이미지(Images)

여기에 첫 번째 함정이 있습니다: 이미지를 삭제해도 텍스처는 삭제되지 않습니다.

var texture:Texture = Texture.fromBitmap(/* ... */);
var image:Image = new Image(texture);

image.dispose(); // 텍스쳐는 사라지지 않습니다.

Starling은 이 텍스쳐를 다른 곳에서 사용하고 있는지 알 수 없기 때문입니다! 결국, 당신은 같은 질감을 사용하는 다른 이미지를 가질 수 있습니다.

반면에 텍스처가 다른 곳에서 사용되지 않는다는 것을 알고 있다면 제거하십시오.

image.texture.dispose();
image.dispose();
필터(Filters)

단편(Fragment) 필터도 약간 섬세합니다. 개체를 처분하면 필터도 처분됩니다:

var object:Sprite = createCoolSprite();
object.filter = new BlurFilter();
object.dispose(); // 필터가 무효화됩니다.

그러나 조심하세요: 다음의 유사한 코드는 필터를 처리하지 않을 것입니다:

var object:Sprite = createCoolSprite();
object.filter = new BlurFilter();
object.filter = null; // 필터가 무효화되지 않습니다.

다시 말해, Starling은 다른 곳에서 필터를 사용할지 여부를 알 수 없기 때문입니다.

그러나 실제로 이것은 문제가 되지 않습니다. 필터는 처리되지 않지만 Starling은 모든 리소스를 정리합니다. 따라서 메모리 누출은 발생하지 않습니다.

이전 Starling 버전 (<2.0)에서는 메모리 누수가 발생했습니다.

3.3.2. 텍스쳐를 임베드 하지 마세요

ActionScript 개발자는 항상 Embed 메타 데이터를 사용하여 비트맵을 SWF 파일에 직접 포함시키는 데 익숙합니다. 모든 웹 게임 데이터를 하나의 파일로 결합할 수 있기 때문에 웹용으로 좋습니다.

문맥 상실 (Context Loss) 섹션에서 Starling (또는 Stage3D)에 심각한 단점이 있다는 것을 이미 알았습니다. 텍스처는 메모리에 적어도 두 번: 기본 메모리에서 한 번, 그래픽 메모리에서 한 번입니다.

[Embed(source="assets/textures/hero.png")]
private static var Hero:Class; (1)

var texture:Texture = Texture.fromEmbeddedAsset(Hero); (2)
1 클래스는 기본 메모리에 저장됩니다.
2 텍스처는 그래픽 메모리에 저장됩니다.

이샘플은 Texture.from Embedded Asset을 사용하여 텍스처를 로드합니다. Context Loss에서 논의된 이유 때문에 대안 (Texture.fromBitmap)은 더 많은 메모리를 사용합니다.

텍스처가 실제로 그래픽 메모리에만 저장되도록 하는 유일한 방법은 URL에서 로드하는 것입니다. 이 작업을 위해 AssetManager를 사용하면 많은 일이 발생하지 않습니다.

var appDir:File = File.applicationDirectory;
var assets:AssetManager = new AssetManager();

assets.enqueue(appDir.resolvePath("assets/textures"));
assets.loadQueue(...);

var texture:Texture = assets.getTexture("hero");

3.3.3. RectangleTexture를 사용하세요.

Starling의 Texture 클래스는 실제로 두 개의 Stage3D 클래스에 대한 래퍼입니다:

flash.display3D.textures.Texture

모든 프로파일에서 사용할 수 있습니다. 밉맵 및 줄 바꿈을 지원하지만 두 배의 길이를 필요로 합니다.

flash.display3D.textures.RectangleTexture

BASELINE 프로파일부터 사용할 수 있습니다. 밉맵이 없고 랩핑이 없지만 임의의 길이를 지원합니다.

이전의 (Texture)에는 이상하고 거의 알려지지 않은 부작용이 있습니다. 필요로하든 그렇지 않든, 항상 밉맵에 메모리를 할당합니다. 이는 텍스처 메모리의 약 1/3을 낭비한다는 것을 의미합니다!

따라서, 대체 (RectangleTexture)를 사용하는 것이 좋습니다. Starling은 가능할 때마다 이 텍스처 유형을 사용합니다.

그러나 적어도 BASELINE 프로파일에서 실행하고 밉맵을 비활성화한 경우에만 수행할 수 있습니다. 첫 번째 요구 사항은 사용 가능한 최상의 Context3D 프로파일을 선택하여 수행할 수 있습니다. Starling의 기본 생성자를 사용하면 자동으로 발생합니다.

// 이렇게 Starling을 초기화 하세요:
... = new Starling(Game, stage);

// 위 코드는 아래와 같습니다:
... = new Starling(Game, stage, null, null, "auto", "auto");

마지막 매개 변수 (자동)는 Starling에게 사용 가능한 최상의 프로파일을 사용하도록 지시합니다. 즉, 장치가 RectangleTextures를 지원하면 Starling에서 이를 사용합니다.

밉맵의 경우 : 명시적으로 요청하면 생성됩니다. Texture.from…​ 팩토리 메소드 중 일부는 그러한 매개 변수를 포함하고 AssetManager는 useMipMaps 속성을 특징으로 합니다. 기본적으로 항상 비활성화되어 있습니다.

3.3.4. ATF 텍스쳐를 사용하세요.

이전에 ATF 텍스처에 대해 이미 이야기했지만 이 섹션에서 다시 언급하는 것이 좋겠습니다. GPU는 JPG 또는 PNG 압축을 사용할 수 없음을 기억하십시오. 이러한 파일은 항상 압축 해제된 후 압축되지 않은 형태로 그래픽 메모리에 업로드됩니다.

하지만 ATF 텍스처는 그렇지 않습니다. 압축된 폼에서 직접 렌더링 할 수 있기 때문에 많은 메모리를 절약할 수 있습니다. 따라서 ATF 섹션을 건너 뛰었다면 다시 한 번 살펴볼 것을 권장합니다!

물론 ATF 텍스처의 단점은 이미지 품질이 떨어지는 것입니다. 그러나 모든 유형의 게임에서 실행 가능한 것은 아니지만 다음과 같은 트릭을 시도해 볼 수 있습니다.

  1. 실제로 필요한 것보다 약간 큰 텍스처를 만듭니다.

  2. ATF 도구로 압축하십시오.

  3. 런타임에는 크기를 원래 크기로 축소합니다.

상당한 메모리가 절약될 것이며, 압축 아티팩트가 없어집니다.

3.3.5. 16 비트 텍스쳐를 사용하세요.

ATF 텍스처가 작동하지 않는다면, 응용 프로그램이 제한된 색상 표와 함께 만화 스타일을 사용할 가능성이 있습니다. 나는 여러분을 위한 좋은 소식이 있습니다: 이러한 종류의 텍스처에 대한 다른 해결책이 있습니다!

  • 기본 텍스쳐 포맷 (Context3DTextureFormat.BGRA)은 32 비트를 사용합니다. (각 채널당 8 비트).

  • 그 중 절반만 사용하는 다른 형식(Context3DTextureFormat.BGRA_PACKED)이 있습니다. 픽셀 당 16 비트 (각 채널 당 4 비트)입니다.

Starling에서 이 포맷을 Texture.from…​ 메소드의 인수로 사용하거나 AssetManager의 textureFormat 속성을 통해 사용할 수 있습니다. 이렇게하면 50%의 메모리가 절약됩니다!

당연히 이는 이미지 품질이 떨어지게 만듭니다. 특히 그라데이션을 사용하는 경우 16비트 텍스쳐가 다소 엉망이 될 수 있습니다. 그러나, 이것을 위한 해결책이 있습니다: 디더링!

Dithering
Figure 39. 디더링은 색 농도를 줄일 수 있는 방법입니다.

보다 명확하게 하기위해 이 예제의 그라데이션은 그냥 16색(4 비트)으로 축소시켰습니다. 이처럼 적은 수의 색상으로도 디더링은 허용할만한 이미지 품질을 제공합니다.

대부분의 이미지 처리 프로그램은 색상 수를 줄이면 자동으로 디더링을 사용합니다. TexturePacker처럼 말이죠.

AssetManager는 파일 단위로 적절한 색상 수를 선택하도록 구성할 수 있습니다.

var assets:AssetManager = new AssetManager();

// 16 비트 텍스처를 대기열에 넣기
assets.textureFormat = Context3DTextureFormat.BGRA_PACKED;
assets.enqueue(/* ... */);

// 32 비트 텍스처를 대기열에 넣기
assets.textureFormat = Context3DTextureFormat.BGRA;
assets.enqueue(/* ... */);

// 이제 로딩 프로세스를 시작합니다.
assets.loadQueue(/* ... */);

3.3.6. 밉맵(Mipmaps)을 피하세요.

밉맵은 렌더링 속도를 높이고 앨리어싱 효과를 줄이기 위해 텍스처를 다운샘플링 시킵니다.

Mipmap
Figure 40. 밉맵을 적용한 텍스쳐 샘플.

버전 2.0부터 Starling은 기본적으로 밉맵을 만들지 않으며, 이는 바람직한 것으로 판명되었습니다:

  • 텍스처가 더 빨리 로드됩니다.

  • 텍스처의 경우 텍스처 메모리가 더 적게 필요합니다 (원본 픽셀만 있고, 밉맵 없음).

  • 흐릿한 이미지를 피할 수 있습니다 (밉맵이 때때로 흐려짐).

반면에 객체 크기를 크게 줄이면 렌더링 속도가 약간 빨라지고 앨리어싱 효과 (예 : 흐리게 처리되는 효과)를 피할 수 있습니다. 밉맵을 사용하려면 Texture.from…​ 메서드에서 해당 매개 변수를 사용하십시오.

3.3.7. 비트맵 폰트를 사용하세요.

이미 설명했듯이, TextFields는 트루타입 글꼴과 비트맵 폰트, 두 가지 종류의 글꼴을 지원합니다.

트루 타입 글꼴은 사용하기가 쉽지만 몇 가지 단점이 있습니다.

  • 텍스트를 변경할 때마다 새로운 텍스처를 만들어 그래픽 메모리에 업로드해야 합니다. 이것은 느립니다.

  • 텍스트 필드가 많거나 큰 텍스트 필드가 있는 경우 텍스처 메모리가 많이 필요합니다.

다른 한편으로 비트맵 폰트는:

  • 매우 빠르게 업데이트되고

  • 일정한 양의 메모리만 사용합니다 (글리프 텍스처인 경우).

따라서 비트맵 폰트는 Starling에서 선호하는 텍스트 표시 방법이 됩니다. 저는 가능할 때마다 그들을 사용하길 추천합니다!

비트맵 폰트 텍스처는 런타임시 실제 TextField 색상으로 채워지는 순수한 흰색이기 때문에 16 비트 텍스처를 가장 많이 사용합니다.

3.3.8. 텍스쳐 아틀라스를 최적화하세요.

가능한 한 타이트하게 텍스쳐 아틀라스를 만드는 것이 가장 중요합니다. TexturePacker와 같은 도구에는 여러 가지 옵션이 있습니다:

  • 투명 테두리를 잘라내기.

  • 더 효과적이라고 판단되는 경우 텍스쳐를 90도 돌리기.

  • 컬러 뎁스 감소시키기. (위에서 설명한 것을 보세요)

  • 중복된 텍스쳐 제거하기.

  • 기타 등등.

이걸 사용하세요! 하나의 아틀라스에 더 많은 텍스처를 패킹하면 전반적인 메모리 사용량이 줄어들뿐만 아니라 그리기 호출 수가 줄어 듭니다. (다음 장에서 자세한 내용 참조)

3.3.9. 어도비 스카우트(Scout)를 사용하세요.

Adobe Scout 는 ActionScript 및 Stage3D를위한 가볍지만 포괄적인 프로파일링 도구입니다. 모바일 장치 또는 브라우저에서 실행되는지 여부에 관계없이 모든 Flash 또는 AIR 응용 프로그램은 코드를 변경하지 않고도 빠르게 프로파일링 할 수 있으며 Adobe Scout는 성능에 영향을 줄 수 있는 문제를 빠르고 효율적으로 감지합니다.

스카우트를 사용하면 ActionScript 코드에서 성능 병목 현상을 찾을 수 있을 뿐 아니라 시간이 지남에 따라 메모리 소비에 대한 자세한 검색을 할 수 있습니다. 이것은 값을 매길 수 없을 정도입니다!

Adobe Scout는 Adobe의 Creative Cloud 멤버십 무료 버전의 일부입니다. 그것을 얻기 위해 유료 CC 구독자가 될 필요는 없습니다.

Thibault Imbert의 훌륭한 자습서는 Adobe Scout 사용 방법을 자세히 설명합니다: Getting started with Adobe Scout

Adobe Scout
Figure 41. Adobe Scout

3.3.10. 통계 표시창에서 눈을 떼지 마세요.

통계 표시 (starling.showStats를 통해 사용 가능)에는 기본 메모리 및 그래픽 메모리에 대한 정보가 포함됩니다. 개발 중에 이러한 가치에 주목할 필요가 있습니다.

물론 가비지 수집기가 언제 실행되는지는 알 수 없으므로 기존 메모리 값은 오보된 경우가 많습니다. 반면 그래픽 메모리 값은 매우 정확합니다. 텍스처를 만들면 값이 올라갑니다. 텍스처를 처리하면 즉시 감소합니다.

실제로 제가 Starling에 이 기능을 추가했을 때 약 5분이 걸렸으며, 이를 이용하자마자 Starling의 데모 응용 프로그램에서 이미 첫 번째 메모리 누수가 발견되었습니다. 나는 다음 접근법을 사용했습니다.

  • 메인 메뉴에서, 사용된 GPU 메모리를 기록했습니다.

  • 그런 다음 데모 장면을 차례로 입력했습니다.

  • 주 메뉴로 돌아올 때마다 GPU 메모리가 원래 값으로 돌아 왔는지 확인했습니다.

  • 장면 중 하나에서 돌아온 후에는 그 값이 복원되지 않았고 코드 검토에서 텍스처 중 하나를 처리하는 것을 잊어 버린 것으로 나타났습니다.

The statistics display
Figure 42. 통계 화면에 현재 메모리 사용량이 표시됩니다.

말할 필요도없이: 스카우트는 메모리 사용에 대한 훨씬 더 자세한 정보를 제공합니다. 그러나 통계 표시가 항상 사용 가능하다는 단순한 사실은 쉽게 간과될 수있는 것을 발견하는 것을 가능하게 합니다.

3.4. 퍼포먼스 최적화

Starling이 고전적인 Flash 디스플레이 리스트를 모방하는 동안 장면 뒤에서 하는 일은 전혀 다릅니다. 최상의 성능을 얻으려면 아키텍처의 핵심 개념을 이해해야 합니다. 다음은 가능한 한 빨리 게임을 실행하기 위해 따라할 수있는 모범 사례 목록입니다.

3.4.1. AS3 일반 팁

항상 릴리스 빌드 만들기

처음부터 가장 중요한 규칙: 성능을 테스트할 때 항상 릴리스 빌드를 만들어야 합니다. 기존의 Flash 프로젝트와 달리, 릴리스 빌드는 Stage3D 프레임워크를 사용할 때 큰 차이를 만듭니다. 속도 차이는 엄청납니다. 작업중인 플랫폼에 따라 디버그 빌드의 여러 프레임을 쉽게 얻을 수 있습니다.

  • Flash Builder에서 프로젝트를 클릭하여 릴리스 빌드를 만듭니다. Project ▸ Export Release Build.

  • Flash Develop에서 "Release" 구성을 선택하고 프로젝트를 빌드하십시오. "PackageApp.bat"스크립트를 실행할 때 "ipa-ad-hoc"또는 "ipa-app-store"옵션을 선택하십시오.

  • IntelliJ IDEA에서 패키지 만들기 Build ▸ Package AIR Application; 을 선택합니다. Android의 경우 'release’를 선택하고 iOS의 경우 'ad hoc distribution’을 선택하십시오. AIR 이외의 프로젝트의 경우 모듈의 컴파일러 옵션에서 "Generate debuggable SWF"을 선택 취소합니다.

  • 명령 행에서 Starling 프로젝트를 빌드하는 경우 -optimize가 true이고 -debug가 false인지 확인하십시오.

Flash Builder Dialog
Figure 43. 이 Flash Builder 대화 상자에서 혼동하지 마십시오.
하드웨어를 체크하세요.

Starling이 실제로 렌더링을 위해 GPU를 사용하는지 확인하십시오. 확인하기 쉽습니다. Starling.current.context.driverInfo에 Software 문자열이 들어 있으면 Stage3D는 소프트웨어 폴백 모드이고, 그렇지 않으면 GPU를 사용하는 것입니다.

또한 일부 모바일 장치는 배터리 절약 모드로 실행될 수 있습니다. 성능 테스트를 할 때는 반드시 끄십시오.

프레임 속도(Framerate)을 설정하세요.

최적화를 했음에도 불구하고 프레임 속도가 초당 24 프레임에 머물러 있습니까? 원하는 프레임 속도를 설정하지 않으면 Flash Player의 기본 설정으로 밖에 표시되지 않습니다.

이를 변경하려면 시작 클래스에서 적절한 메타 데이터를 사용하거나 Flash 스테이지에서 수동으로 프레임 속도를 설정하십시오.

[SWF(frameRate="60", backgroundColor="#000000")]
public class Startup extends Sprite
{ /* ... */ }

// 또는 다른 곳에서도
Starling.current.nativeStage.frameRate = 60;
Adobe Scout를 사용하세요.

Adobe Scout는 메모리 분석에만 유용하지 않습니다. 퍼포먼스 프로파일링 분야에도 강력함을 발휘합니다.

그것은 실제로 각 ActionScript(및 Starling의) 메서드에서 얼마나 많은 시간이 실제로 소비되었는지 확인할 수 있게 해줍니다. 최적화를 통해 얻을 수있는 부분을 보여주기 때문에 매우 유용합니다. 이것은 실제로 프레임 속도와 관련이없는 코드 영역을 최적화할 수 있습니다!

기억하세요! 제때 최적화 하지 않는 것은 모든 악의 근원입니다!

클래식 프로파일러와 비교할 때 좋은 점은 모든 최적화가 제대로 수행된 릴리스 모드에서도 작동한다는 것입니다. 따라서 출력이 매우 정확합니다.

로드한 이미지를 비동기적으로 디코딩하세요.

기본적으로 로더를 사용하여 PNG 또는 JPEG 이미지를 로드하는 경우 이미지 데이터는 즉시 디코딩되지 않지만 처음 사용하면 디코딩됩니다. 이것은 주 스레드에서 발생하며 텍스처 생성시 응용 프로그램이 버벅거리는 원인이 될 수 있습니다. 이를 방지하려면 이미지 디코딩 정책 플래그를 ON_LOAD로 설정하십시오. 이렇게하면 이미지가 로더의 백그라운드 스레드에서 직접 디코딩됩니다.

loaderContext.imageDecodingPolicy = ImageDecodingPolicy.ON_LOAD;
loader.load(url, loaderContext);

반면에 여러분은 아마도 Starling의 AssetManager를 사용하여 텍스쳐를 로드하고 있을 것입니다. 그렇죠? 그래도 걱정하지 말고 이 예제를 사용하시기 바랍니다.

"for each"를 피하세요.

매우 자주 반복되거나 깊게 중첩된 루프로 작업할 때는 'for each’를 피하는 것이 좋습니다. 고전적인 'for i’는 더 나은 성능을 제공합니다. 또한 루프 조건은 루프 당 한 번씩 실행되므로 외부 변수에 저장하는 것이 더 빠릅니다.

// 느립니다:
for each (var item:Object in array) { ... }

// 조금 더 낫습니다:
for (var i:int=0; i<array.length; ++i) { ... }

// 빠릅니다:
var length:int = array.length;
for (var i:int=0; i<length; ++i) { ... }
배정(Allocations)을 피하세요.

많은 임시 객체를 생성하지 마십시오. 그들은 메모리를 차지하며 가비지 컬렉터에 의해 정리되어야 할 필요가 있습니다. 실행시 작은 hiccups(딸꾹질)을 일으킬 수 있습니다.

// 좋지 않은 예:
for (var i:int=0; i<10; ++i)
{
    var point:Point = new Point(i, 2*i);
    doSomethingWith(point);
}

// 보다 나은 예:
var point:Point = new Point();
for (var i:int=0; i<10; ++i)
{
    point.setTo(i, 2*i);
    doSomethingWith(point);
}

실제로 Starling은 그것을 돕는 클래스를 포함합니다: Pool. Point Rectangle 및 Matrix와 같이 종종 필요한 개체 풀을 제공합니다. 완료되면 풀에서 객체를 빌려 올 수 있습니다.

// 최상의 예:
var point:Point = Pool.getPoint();
for (var i:int=0; i<10; ++i)
{
    point.setTo(i, 2*i);
    doSomethingWith(point);
}
Pool.putPoint(point); // 이걸 잊지마세요!

3.4.2. Starling 특정 팁

상태 변경 최소화

Starling은 Stage3D를 사용하여 디스플레이 리스트를 렌더링합니다. 즉 모든 그리기는 GPU가 수행합니다.

이제 Starling은 다른 쿼드를 하나씩 GPU로 전송하여 하나씩 그려볼 수 있습니다. 사실 이것은 첫 번째 Starling 릴리스가 어떻게 작동 했는가 입니다! 그러나 최적의 성능을 위해 GPU는 거대한 데이터 더미를 얻고 모든 데이터를 한 번에 그려내는 것을 선호합니다.

그래서 새로운 Starling 버전은 GPU에 보내기 전에 가능한 많은 쿼드를 함께 배치합니다. 그러나 비슷한 특성을 지닌 쿼드만 배치할 수 있습니다. 다른 "state, 상태"가 있는 쿼드가 만날 때마다 "state change, 상태 변경"이 발생하고 이전에 일괄 처리된 쿼드가 그려집니다.

이 섹션에서는 Quad와 Image를 동의어로 사용합니다. 기억하세요, 이미지는 몇 가지 메소드를 추가한 Quad의 하위 클래스입니다. 게다가 Quad는 Mesh를 확장하고 아래에서 읽은 내용은 메쉬에서도 마찬가지입니다.

이것들은 상태를 구성하는 중요한 속성들입니다:

  • 텍스쳐 (같은 아틀라스와 다른 하위 텍스처는 괜찮습니다.)

  • 디스플레이 오브젝트의 blendMode

  • 메쉬 / 쿼드 / 이미지의 textureSmoothing 값

  • 메쉬 / 쿼드 / 이미지의 textureRepeat 모드

가능한 한 작은 상태 변경을 생성하는 방식으로 장면을 설정하면 렌더링 성능이 크게 향상됩니다.

Starling의 정적 디스플레이는 유용한 데이터를 제공합니다. 프레임 당 얼마나 많은 draw call이 실행되는지를 정확하게 보여줍니다. 상태 변화가 많을수록 이 숫자가 더 높습니다.

Statistics Display
Figure 44. 통계 표시에는 현재 draw call 수가 나와 있습니다.

통계 표시는 draw call도 발생시킵니다. 그러나 Starling은 이를 고려하여 표시된 draw 횟수를 명시적으로 줄입니다.

목표는 항상 가능한 한 낮게 유지하는 것입니다. 다음 팁은 방법을 보여줍니다.

페인터(Painter)의 알고리즘

상태 변경을 최소화하는 방법을 알기 위해서는 Starling에서 개체를 처리하는 순서를 알아야합니다.

Flash와 마찬가지로 Starling에서는 Painter의 알고리즘을 사용하여 디스플레이 리스트를 처리합니다. 이는 화가가 하는 것처럼 씬을 그려야 한다는 것을 의미합니다. 맨 아래 레이어의 오브젝트 (예 배경 이미지)에서 시작하여 위쪽으로 이동하여 이전 오브젝트 위에 새로운 오브젝트를 그립니다.

Painter's algorithm
Figure 45. Painter의 알고리즘으로 장면 그리기.

Starling에서 이와 같은 장면을 설정하면 멀리있는 산 범위를 포함하는 스프라이트와 땅이 있는 스프라이트 및 식물이 있는 스프라이트의 세 가지 스프라이트를 만들 수 있습니다. 산맥은 가장 아래에 위치하며(인덱스 0), 식물들은 가장 위에(인덱스 2) 위치합니다. 각 스프라이트에는 실제 객체가 포함된 이미지가 포함됩니다.

Landscape Scene Graph
Figure 46. 위 풍경 이미지의 장면 그래프.

렌더링시 Starling은 왼쪽에서 "Mountain 1"로 시작하여 오른쪽으로 계속 진행하여 "Tree 2"에 도달합니다. 모든 오브젝트의 상태가 다른 경우 6회의 그리기 호출을 의미합니다. 그것은 개별 Bitmap에서 각 객체의 텍스쳐를 로드하는 경우 정확히 일어날 것입니다.

텍스쳐 아틀라스

이것이 텍스처 아틀라스 레이어가 중요한 이유 중 하나입니다. 하나의 아틀라스에서 모든 텍스처를 로드하면 Starling은 모든 오브젝트를 한 번에 그릴 수 있습니다! (적어도 위에 나열된 다른 속성이 변경되지 않는 경우).

Landscape Scene Graph 2
Figure 47. 하나의 아틀라스 텍스처를 사용하는 동일한 장면 그래프.

결과적으로 텍스쳐에 항상 아틀라스를 사용해야 합니다. 여기서 각 이미지는 동일한 아틀라스를 사용합니다 (동일한 색상을 가진 모든 노드로 표시).

때로는 모든 텍스처가 하나의 아틀라스에 들어 맞는 것은 아닙니다. 텍스처의 크기가 제한되어 있으므로 조만간 공간이 부족할 것입니다. 그러나 이것은 똑똑한 방식으로 텍스처를 배열하는 한 아무런 문제가 되지 않습니다.

Landscape Scene Graph 3
Figure 48. 개체의 순서에 차이가 있습니다.

두 예제 모두 두 개의 아틀라스 (아틀라스 당 하나의 색상)를 사용합니다. 그러나 왼쪽의 디스플레이 리스트는 각 객체의 상태 변경을 강제로 수행하지만 오른쪽의 버전은 모든 객체를 단 두개의 배치(batches, 일괄처리)로 그릴 수 있습니다.

MeshBatch 클래스 사용하기

한 번에 많은 수의 쿼드 또는 다른 메시를 그릴 수 있는 가장 빠른 방법은 MeshBatch 클래스를 사용하는 것입니다. 이것은 Starling이 모든 렌더링을 위해 내부적으로 사용하는 클래스이므로 상당히 최적화되어 있습니다.[6] 그것은 다음과 같이 작동합니다:

var meshBatch:MeshBatch = new MeshBatch();
var image:Image = new Image(texture);

for (var i:int=0; i<100; ++i)
{
    meshBatch.addMesh(image);
    image.x += 10;
}

addChild(meshBatch);

알아 차렸나요? 같은 이미지를 원하는만큼 추가할 수 있습니다! 또한 추가하는 작업은 매우 빠릅니다. 예를 들어, 어떤 이벤트도 전달되지 않습니다 (컨테이너에 객체를 추가하는 경우).

예상대로 여기에는 몇 가지 단점이 있습니다:

  • 추가하는 모든 객체는 동일한 상태 (즉, 동일한 아틀라스의 텍스처 사용)여야 합니다. MeshBatch에 추가하는 첫 번째 이미지는 상태를 결정합니다. 완전히 재설정한 경우를 제외하고는 나중에 상태를 변경할 수 없습니다.

  • Mesh 클래스 또는 그 하위 클래스 (Quad Image 심지어 MeshBatch 포함)의 인스턴스만 추가 할 수 있습니다.

  • 개체 제거는 매우 까다 롭습니다: 일괄 처리의 정점 및 인덱스 수를 트리밍해야지만 메쉬를 제거할 수 있습니다. 그러나 특정 인덱스에서 메쉬를 덮어 쓸 수 있습니다.

이러한 이유로 매우 특정한 사용 사례에만 적합합니다. (예: BitmapFont 클래스는 내부적으로 메쉬 배치를 사용합니다). 이 경우 확실히 가장 빠른 옵션입니다. Starling에서 많은 수의 객체를 렌더링하는보다 효율적인 방법을 찾지 못할 것입니다.

텍스트필드를 일괄처리하기

기본적으로 TextField는 글리프 텍스처가 기본 텍스처 맵의 일부인 경우에도 한 번의 그리기 호출을 필요로 합니다. 긴 텍스트는 일괄 처리에 많은 CPU 시간이 필요하기 때문입니다. (MeshBatch에 복사하지 않고) 즉시 그리는 것이 더 빠릅니다.

그러나 텍스트 필드에 몇 개의 문자만 포함된 경우 (규칙 16 자 이하) TextField에서 일괄 처리 가능한 속성을 활성화 할 수 있습니다. 이를 사용하면 다른 표시 객체와 마찬가지로 텍스트가 일괄 처리됩니다.

BlendMode.NONE 사용하기

완전히 불투명한 직사각형 텍스처를 가지고 있다면 텍스처에 블렌딩을 사용하지 않도록 설정하여 GPU를 도와주십시오. 이것은 큰 배경 이미지에 특히 유용합니다.

backgroundImage.blendMode = BlendMode.NONE;

당연히 이것은 또한 상태 변화가 추가됨을 의미하므로 이 기술을 과도하게 사용하지는 마십시오. 작은 이미지의 경우 아마도 그럴만 한 가치가 없을 것입니다 (어쨌든 다른 이유로 인해 상태가 변경되는 것을 제외하고는).

stage.color 사용하기

스테이지의 상단에는 항상 이미지나 메시(Meshes)가 있기 때문에 실제 스테이지 색상은 게임에서 실제로 볼 수 없는 경우가 종종 있습니다.

이 경우 항상 검은 색 (0x0) 또는 흰색 (0xffffff)을 지우도록 설정하십시오. 일부 모바일 하드웨어에서는 all 1 또는 all 0으로 호출될 때 context.clear에 대해 빠른 하드웨어 최적화 경로가 있는 것으로 보입니다. 일부 개발자는 프레임 당 렌더링 시간을 1 밀리 초로 줄였습니다. 이는 단순한 변경에 매우 좋은 결과를 가져옵니다!

[SWF(backgroundColor="#0")]
public class Startup extends Sprite
{
    // ...
}

다른 한편으로는, 게임의 배경이 평면 컬러인 경우 이미지 또는 컬러 쿼드를 표시하는 대신 스테이지 색상을 해당 값으로 설정하면 됩니다. Starling은 프레임마다 한 번씩 스테이지를 지워야 합니다. 따라서 스테이지 색상을 변경하면 작업에 비용이 들지 않습니다.

[SWF(backgroundColor="#ff2255")]
public class Startup extends Sprite
{
    // ...
}
너비(width)와 높이(height) 쿼리하지 않기

width 및 height 속성은 특히 스프라이트에서 알아낼 때 시간이 많이 소요됩니다. 행렬을 계산해야 하며 각 자식의 각 꼭지점에 해당 행렬이 곱해져야 하기 때문입니다.

그렇기 때문에 반복해서 액세스하지 마십시오. (루프문에서). 경우에 따라 상수 값을 대신 사용하는 것이 좋습니다.

// 나쁜 예:
for (var i:int=0; i<numChildren; ++i)
{
    var child:DisplayObject = getChildAt(i);
    if (child.x > wall.width)
        child.removeFromParent();
}

// 좋은 예:
var wallWidth:Number = wall.width;
for (var i:int=0; i<numChildren; ++i)
{
    var child:DisplayObject = getChildAt(i);
    if (child.x > wallWidth)
        child.removeFromParent();
}
콘테이너를 터치 못하게 만들기

화면 위로 마우스나 손가락을 움직일 때 Starling은 어느 대상이 터치되었는지 찾아야 합니다. 이는 각 디스플레이 개체 (최악의 경우)에 대한 적중 테스트가 필요하기 때문에 값 비싼 작업이 될 수 있습니다.

따라서 터치되지 않도록 만드는 것이 도움이 됩니다. 컨테이너에서 터치를 비활성화하는 것이 가장 좋습니다. 이렇게 하면 Starling은 자식 위로 반복할 필요가 없어집니다.

// 좋은 예:
for (var i:int=0; i<container.numChildren; ++i)
    container.getChildAt(i).touchable = false;

// 더 나은 예:
container.touchable = false;
스테이지 범위 밖에 있는 개체 숨기기

Starling은 디스플레이 리스트의 모든 객체를 GPU로 보냅니다. 무대 바운드 밖에 있는 오브젝트의 경우에도 마찬가지입니다!

왜 Starling은 단순히 보이지 않는 객체를 무시하지 않는가? 그 이유는 보편적인 방식으로 가시성을 확인하는 것이 비용이 많이 들기 때문입니다. 실제로 개체를 GPU로 보내고 클리핑 작업을 수행하는 것이 더 빠릅니다. 실제로 GPU는 매우 효율적이며 객체가 스크린 범위 밖에 있을 경우 렌더링 파이프라인 전체를 빠르게 중단합니다.

그러나 데이터를 업로드하는데 여전히 시간이 걸리므로 이를 피하는게 좋습니다. 높은 수준의 게임 논리에서는 가시성 검사를 하는 것이 더 쉽습니다 (예: 상수와 x / y 좌표를 비교할 수 있음). 이러한 범위를 벗어나는 많은 객체를 가지고 있다면 그만한 가치가 있습니다. 스테이지에서 해당 요소를 제거하거나 visible 속성을 false로 설정합니다.

이벤트 풀링 사용

클래식 Flash에 비해 Starling은 이벤트 전달을 위한 추가 방법을 추가합니다:

// 클래식한 방식:
object.dispatchEvent(new Event("type", bubbles));

// 새로운 방식:
object.dispatchEventWith("type", bubbles);

새로운 접근 방식은 첫 번째 이벤트 객체와 마찬가지로 이벤트 객체를 전달하지만 뒷 배경에서는 이벤트 객체를 풀링합니다. 즉 가비지 수집기를 일부만 저장하면 됩니다.

즉, 작성하는 코드가 적어지고 속도가 빨라집니다. 따라서 이벤트를 전달하는 가장 좋은 방법입니다. (Event의 사용자 정의 하위 클래스를 전달해야 하는 경우를 제외하고는 해당 메소드로 디스패치 할 수 없습니다.)

3.5. 커스텀 필터

손을 더럽힐 준비가 되셨습니까? 우리는 이제 간단한 조각 필터로 시작하는 사용자 지정 렌더링 코드의 영역에 들어서고 있습니다.

네. 여기에는 저수준 코드가 포함될 것입니다. 도대체 몇 줄의 어셈블러를 작성할 것입니다! 그러나 두려워할 일은 아닙니다. 로켓 과학이 아니니까요. 나의 예전 수학 선생님 왈: 멍청한 원숭이는 그것을 할 수 있다!

기억하세요: 필터는 표시 객체의 픽셀 수준에서 작동합니다. 필터링된 객체는 텍스처로 렌더링된 다음 사용자 정의 조각 쉐이더 (따라서 이름 조각 필터)로 처리됩니다.

3.5.1. 목표

우리가 단순한 목표를 설정했지만 유용한 것이어야 합니다. 그렇죠? 자, 이제 ColorOffsetFilter를 만듭시다.

색상을 지정하여 메쉬의 정점에 색을 입힐 수 있다는 것을 알고 있을 것입니다. 렌더링시 색상에 텍스처 색상이 곱해지므로 텍스처의 색상을 매우 간단하고 신속하게 수정할 수 있습니다:

var image:Image = new Image(texture);
image.color = 0x808080; // R = G = B = 0.5

수학적 배경 지식은 매우 간단합니다: GPU에서 각 색상 채널 (빨강 초록 파랑)은 0과 1 사이의 값으로 표시됩니다. 예를 들어 순수한 빨강이 될 것입니다:

R = 1, G = 0, B = 0

렌더링시 이 색상은 텍스처의 각 픽셀의 색상( "texel, 텍셀"이라고도 함)과 곱해집니다. 이미지 색상의 기본값은 모든 채널에서 1인 순수한 흰색입니다. 따라서 텍셀 컬러는 변경되지 않은 것처럼 보입니다 (1과 곱셈은 무연산입니다). 다른 색상을 지정하면 곱셈은 새로운 색상을 생성합니다:

R = 1,   G = 0.8, B = 0.6  ×
B = 0.5, G = 0.5, B = 0.5
-------------------------
R = 0.5, G = 0.4, B = 0.3

그리고 여기에 문제가 있습니다: 이것은 오직 대상을 더 어둡게 결코 더 밝게 만들지 않을 것입니다. 이는 0과 1 사이의 값만 곱할 수 있기 때문입니다. 0은 결과가 검은 색이고 하나는 의미가 없음을 의미합니다.

Tinting
Figure 49. 이미지를 회색으로 착색.

이것이 우리가 이 필터로 해결하고자 하는 것입니다! 이 공식에 오프셋을 포함할 것입니다. 클래식 플래시에서는 ColorTransform을 사용하면 됩니다.

  • 새로운 red 값 = (기존 red 값 × redMultiplier) + redOffset

  • 새로운 green 값 = (기존 green 값 × greenMultiplier) + greenOffset

  • 새로운 blue 값 = (기존 blue 값 × blueMultiplier) + blueOffset

  • 새로운 alpha 값 = (기존 alpha 값 × alphaMultiplier) + alphaOffset

승수(multiplier)는 이미 기본 Mesh 클래스에서 처리되므로 이미 배수가 있습니다. 필터는 오프셋을 추가해야 합니다.

Offset
Figure 50. 모든 채널에 offset 더하기.

드디어 시작합시다, 함께 하실까요?!

3.5.2. FragmentFilter 확장

모든 필터는 starling.filters.FragmentFilter 클래스를 확장하며 이 필터도 예외는 아닙니다. 이제 꽉 잡으세요. 이제 완전한 ColorOffsetFilter 클래스를 제공하겠습니다. 이것은 스텁이 아니라 최종 코드입니다. 더 이상 수정하지 않겠습니다.

public class ColorOffsetFilter extends FragmentFilter
{
    public function ColorOffsetFilter(
        redOffset:Number=0, greenOffset:Number=0,
        blueOffset:Number=0, alphaOffset:Number=0):void
    {
        colorOffsetEffect.redOffset = redOffset;
        colorOffsetEffect.greenOffset = greenOffset;
        colorOffsetEffect.blueOffset = blueOffset;
        colorOffsetEffect.alphaOffset = alphaOffset;
    }

    override protected function createEffect():FilterEffect
    {
        return new ColorOffsetEffect();
    }

    private function get colorOffsetEffect():ColorOffsetEffect
    {
        return effect as ColorOffsetEffect;
    }

    public function get redOffset():Number
    {
        return colorOffsetEffect.redOffset;
    }

    public function set redOffset(value:Number):void
    {
        colorOffsetEffect.redOffset = value;
        setRequiresRedraw();
    }

    // the other offset properties need to be implemented accordingly.

    public function get/set greenOffset():Number;
    public function get/set blueOffset():Number;
    public function get/set alphaOffset():Number;
}

그것은 놀랍도록 컴팩트한 것입니다. 그렇죠? 글쎄..나는 인정해야만 합니다: 이것은 이야기의 절반에 불과한데요. 실제 색상 처리를 하는 또 다른 클래스도 써야하기 때문입니다. 그래도 우리가 위에서 본 것을 분석하는 것은 가치가 있습니다.

물론 클래스는 FragmentFilter를 확장하고 createEffect 메서드를 재정의합니다. starling.rendering.Effect 클래스는 예전에 저수준 렌더링을 위해서만 필요했기 때문에 사용하지 않았을 것입니다. API 문서의 내용:

이펙트는 Stage3D 그리기 작업의 모든 단계를 캡슐화합니다. 렌더링 컨텍스트를 구성하고 쉐이더 프로그램과 인덱스 및 버텍스 버퍼를 설정하여 모든 하위 레벨 렌더링의 기본 메커니즘을 제공합니다.

FragmentFilter 클래스는 이 클래스 또는 실제로 FilterEffect라는 하위 클래스를 사용합니다. 이 간단한 필터의 경우 createEffect()를 재정의하여 사용자 지정 효과를 제공하면 됩니다. 속성은 우리의 효과를 구성하는 것 외에는 아무것도 하지 않습니다. 렌더링 시 기본 클래스는 필터를 렌더링 하는데 자동으로 이 효과를 사용합니다. 그게 다입니다!

colorOffsetEffect 속성이 하는 일이 궁금하다면 ColorOffsetEffect에 지속적으로 캐스트하지 않고 효과에 액세스 할 수 있는 바로 가기입니다. 기본 클래스는 효과 속성도 제공하지만 이는 FilterEffect 유형의 객체를 반환하며 offset 속성에 액세스하려면 전체 유형인 ColorOffsetEffect가 필요합니다.

보다 복잡한 필터는 프로세스 메서드를 재정의 해야할 수도 있습니다. 예를들어, 멀티 패스 필터를 만드는 데 필요합니다. 샘플 필터의 경우에는 필요하지 않습니다.

마지막으로 setRequiresRedraw에 대한 호출을 주목하십시오. 설정이 변경될 때마다 효과가 다시 렌더링되는지 확인하십시오. 그렇지 않으면 Starling은 개체를 다시 그려야한다는 것을 알 수 없습니다.

3.5.3. FilterEffect 확장

실제 작업을 할 시간입니다. 그렇죠? 음, 우리의 FilterEffect 서브 클래스는 이 필터의 실제 주력자입니다. 그렇다고 이것이 매우 복잡하다는 것을 의미하지 않기 때문에 나와 함께 감내해 보시죠.

스텁(stub)으로 시작하시죠:

public class ColorOffsetEffect extends FilterEffect
{
    private var _offsets:Vector.<Number>;

    public function ColorOffsetEffect()
    {
        _offsets = new Vector.<Number>(4, true);
    }

    override protected function createProgram():Program
    {
        // TODO
    }

    override protected function beforeDraw(context:Context3D):void
    {
        // TODO
    }

    public function get redOffset():Number { return _offsets[0]; }
    public function set redOffset(value:Number):void { _offsets[0] = value; }

    public function get greenOffset():Number { return _offsets[1]; }
    public function set greenOffset(value:Number):void { _offsets[1] = value; }

    public function get blueOffset():Number { return _offsets[2]; }
    public function set blueOffset(value:Number):void { _offsets[2] = value; }

    public function get alphaOffset():Number { return _offsets[3]; }
    public function set alphaOffset(value:Number):void { _offsets[3] = value; }
}

우리는 벡터에 오프셋을 저장하고 있습니다. 오프셋을 GPU에 쉽게 업로드 할 수 있기 때문입니다. 해당 벡터에서 읽고 쓰는 오프셋 속성입니다. 충분히 간단합니다.

재정의된 두 가지 방법을 살펴보면 더 흥미로워집니다.

createProgram

이 메서드는 실제 Stage3D 셰이더 코드를 생성합니다.

기본을 보여 드리겠지만 Stage3D에 대해 자세히 설명하는 것은 이 설명서의 범위를 벗어납니다. 주제에 대해 자세히 알아보려면 항상 다음 자습서 중 하나를 살펴보십시오:

모든 Stage3D 렌더링은 정점 및 조각 쉐이더를 통해 수행됩니다. 그것들은 GPU에 의해 직접 실행되는 작은 프로그램이며 두 가지 종류가 있습니다:

  • Vertex Shaders 는 각 꼭지점에 대해 한 번 실행됩니다. 입력은 VertexData 클래스를 통해 일반적으로 설정하는 정점 속성으로 구성됩니다. 그들의 출력은 화면 좌표의 정점 위치입니다.

  • Fragment Shaders 는 각 픽셀 (조각)에 대해 한 번 실행됩니다. 입력은 삼각형의 세 꼭지점의 보간 속성으로 구성됩니다. 출력은 단순히 픽셀의 색상입니다.

  • 조각과 정점 셰이더가 함께 *프로그램*을 구성합니다.

언어 필터(language filters)는 어셈블리 언어인 AGAL로 작성됩니다. (네. 당신이 읽을 수 있습니다! 이것은 낮은 수준입니다.) 고맙게도 전형적인 AGAL 프로그램은 매우 짧기 때문에 나쁘지는 않습니다.

좋은 소식은 조각 쉐이더만 작성하면 됩니다. 버텍스 쉐이더는 대부분의 프래그먼트 필터에서 동일하므로 Starling은 이를 위한 표준 구현을 제공합니다. 코드를 살펴 보겠습니다:

override protected function createProgram():Program
{
    var vertexShader:String = STD_VERTEX_SHADER;
    var fragmentShader:String =
        "tex ft0, v0, fs0 <2d, linear> \n" +
        "add oc, ft0, fc0";

    return Program.fromSource(vertexShader, fragmentShader);
}

약속대로 버텍스 쉐이더는 상수로부터 가져옵니다. 프래그먼트 셰이더는 두 줄의 코드일 뿐입니다. 둘 다 메소드의 리턴 값인 하나의 프로그램 인스턴스로 결합됩니다.

조각 쉐이더는 물론 좀 더 정교함이 필요합니다.

Nutshell 내의 AGAL

AGAL에서 각 행에는 간단한 메소드 호출이 들어 있습니다.

[opcode] [destination], [argument 1], ([argument 2])
  • 처음 세 문자는 작업의 이름입니다 (tex, add).

  • 다음 인수는 조작 결과가 저장되는 위치를 정의합니다.

  • 다른 인수는 메소드의 실제 인수입니다.

  • 모든 데이터는 사전 정의된 레지스터에 저장됩니다. 그것들을 Vector3D 인스턴스 (x, y, z 및 w에 대한 속성 포함)로 생각하십시오.

여러 가지 유형의 레지스터가 있습니다 (예: 상수 임시 데이터 또는 셰이더 출력용). 셰이더에서는 일부 데이터에 이미 데이터가 포함되어 있습니다. 그들은 필터의 다른 방법으로 설정되었습니다. (나중에 설명하겠습니다)

  • v0 에는 현재 텍스처 좌표가 포함됩니다 (레지스터 0 변경).

  • fs0 은 입력 텍스처를 가리킴 (프래그먼트 샘플러 0)

  • fc0 에는 이것에 대한 색 오프셋이 포함되어 있습니다 (조각 상수 0).

프래그먼트 셰이더의 결과는 항상 색상이어야 합니다. 그 색상은 oc 레지스터에 저장됩니다.

코드 리뷰

우리의 프래그먼트 셰이더의 실제 코드로 돌아가 봅시다. 첫 번째 줄은 텍스처에서 색상을 읽습니다:

tex ft0, v0, fs0 <2d, linear>

레지스터 v0에서 읽은 텍스처 좌표와 일부 옵션 (2d linear)으로 텍스처 fs0을 읽습니다. 텍스처 좌표가 v0에있는 이유는 표준 정점 셰이더 (STD_VERTEX_SHADER)가 거기에 저장하기 때문입니다; 믿어주세요. 결과는 임시 레지스터 ft0에 저장됩니다 (AGAL에서는 결과가 항상 연산의 첫 번째 인수에 저장됩니다).

잠깐만요. 우리는 어떤 텍스쳐도 만들어 내지 못했습니다. 그렇죠? 이게 뭐죠?

위에서 쓴 것처럼 조각 필터는 픽셀 단위로 작동합니다. 그것의 입력은 텍스쳐로 렌더링된 원래의 객체입니다. 우리의 기본 클래스 (FilterEffect)는 우리를 위해 그것을 설정합니다; 프로그램이 실행되면 텍스처 샘플러 fs0이 필터링되는 객체의 픽셀을 가리키는지 확인할 수 있습니다.

사실 이 줄을 조금 바꾸고 싶습니다. 마지막에 텍스처 데이터를 해석하는 방법을 나타내는 옵션을 발견했을 것입니다. 글쎄요. 이 옵션은 우리가 접근하고 있는 텍스처 유형에 달려 있다고 밝혀졌습니다. 코드가 모든 텍스처에 대해 작동하는지 확인하려면 도우미 메서드를 사용하여 해당 AGAL 연산을 작성하십시오.

tex("ft0", "v0", 0, this.texture)

그것은 똑같이 (AGAL 문자열을 반환하는) 방법이지만 더 이상 옵션을 신경 쓸 필요가 없습니다. 텍스처에 액세스 할 때는 항상이 메서드를 사용하십시오. 여러분이 밤에 잘 잘 수 있게 해줄 것입니다.

두 번째 라인은 실제로 우리가 여기 온 작업을 수행합니다. 텍셀 색상에 색상 오프셋을 추가합니다. 오프셋은 fc0에 저장되며 곧 살펴볼 것입니다. 그것은 ft0 레지스터 (방금 읽은 텍셀 색상)에 추가되고 출력 레지스터 (oc)에 저장됩니다.

add oc, ft0, fc0

지금은 AGAL과 같습니다. 오버라이드된 다른 메소드를 살펴 보겠습니다.

beforeDraw

beforeDraw 메서드는 셰이더가 실행되기 전에 직접 실행됩니다. 셰이더에 필요한 모든 데이터를 설정할 때 사용할 수 있습니다.

override protected function beforeDraw(context:Context3D):void
{
    context.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 0, _offsets);
    super.beforeDraw(context);
}

이것은 프래그먼트 셰이더에 오프셋 값을 전달하는 곳입니다. 두 번째 매개 변수 인 0은 데이터가 끝날 레지스터를 정의합니다. 실제 쉐이더 코드를 살펴보면 fc0에서 오프셋을 읽음을 알 수 있습니다. 여기 정확히 채우면 됩니다. 프래그먼트 상수 0.

슈퍼 콜은 모든 나머지를 설정합니다. 텍스처 (fs0)와 텍스처 좌표를 할당합니다.

여러분이 질문하기 전에: 네. afterDraw() 메서드도 있습니다. 일반적으로 리소스를 정리하는 데 사용됩니다. 그러나 상수의 경우에는 필요하지 않으므로 이 필터에서는 무시해도 됩니다.

3.5.4. 시도하기

우리 필터가 실제로 준비되었습니다. 여기에서 전체 코드를 다운로드하십시오! 시운전 시간입니다.

var image:Image = new Image(texture);
var filter:ColorOffsetFilter = new ColorOffsetFilter();
filter.redOffset = 0.5;
image.filter = filter;
addChild(image);
Custom Filter PMA Issue
Figure 51. 이 필터에는 부작용이 있는 것 같습니다.

이럴수가! 네. 빨간색 값이 확실히 높긴 하지만 왜 지금 새의 영역을 넘어서 확장하고 있을까요? 우리는 결국 알파 값을 변경하지 않았습니다!

당황하지 마십시오. 방금 첫 번째 필터를 만들었고 그것은 여러분에게 동기부여가 되지 않았나요? 그것은 가치가 있어야 합니다. 할 수 있는 미세 조정이 있을 것으로 예상됩니다.

"premultiplied alpha, 미리 곱셈 된 알파"(PMA)를 사용하는 것을 잊어 버렸네요. 모든 일반적인 텍스처는 RGB 채널에 알파 값이 미리 곱하여 저장됩니다. 아래처럼 50% 알파를 가진 빨강 말이죠:

R = 1, G = 0, B = 0, A = 0.5

실제로 이런 식으로 저장됩니다:

R = 0.5, G = 0, B = 0, A = 0.5

그리고 우리는 이것을 고려하지 않았습니다. 그가 해야할 일은 오프셋 값을 출력에 추가하기 전에 현재 픽셀의 알파 값으로 곱하는 것입니다. 이를 수행하는 한 가지 방법이 있습니다:

tex("ft0", "v0", 0, texture)   // get color from texture
mov ft1, fc0                   // copy complete offset to ft1
mul ft1.xyz, fc0.xyz, ft0.www  // multiply offset.rgb with alpha (pma!)
add  oc, ft0, ft1              // add offset, copy to output

보시다시피 레지스터의 xyzw 속성에 액세스하여 개별 색상 채널 (rgba 채널에 해당)에 액세스 할 수 있습니다.

텍스처가 PMA와 함께 저장되지 않으면 어떻게 될까요? tex 메서드는 항상 PMA로 값을 수신하므로 걱정할 필요가 없습니다.
두 번째 시도

필터를 다른 것으로 시도하면 (완전한 코드: ColorOffsetFilter.as) 올바른 알파 값을 볼 수 있습니다:

Custom Filter with solved PMA issue
Figure 52. 더 좋아요!

축하합니다! 방금 첫 번째 필터를 만들었고 완벽하게 작동합니다. (그렇습니다, 당신은 Starling의 ColorMatrixFilter를 대신 사용할 수있었습니다 -하지만 이게 조금 더 빠르므로 노력할 만한 가치가 있었습니다.)

좀 더 용기가 필요하지만, 이제 그 대신에 메쉬 스타일로 같은 결과를 얻을 수 있으니 시도해 보세요. 그다지 다르지는 않아요. 약속하죠!

3.6. 커스텀 스타일

이제 Stage3D의 파워를 활용했으므로 이 길을 계속가십시오! 이 섹션에서는 간단한 메쉬 스타일을 작성합니다. Starling 2에서는 모든 렌더링이 스타일을 통해 수행됩니다. 자신만의 스타일을 만들어 어떤 식으로든 성능을 희생하지 않고 특수 효과를 만들 수 있습니다.

계속하기 전에 '커스텀 필터, Custom Filters' 섹션을 읽었는지 확인하십시오. 필터와 스타일은 많은 개념을 공유하므로 두 가지 중 더 간단한 것으로 시작하는 것이 좋습니다. 아래에서는 다른 섹션에 표시된 모든 내용을 잘 알고 있다고 가정합니다.

3.6.1. 목표

목표는 ColorOffsetFilter와 같습니다. 모든 렌더링된 픽셀의 색상 값에 오프셋을 추가할 수 있게 하는 것입니다. 이번에 우리는 그것을 스타일로 합니다! 우리는 그것을 ColorOffsetStyle이라고 부를 것입니다.

Offset with a Style
Figure 53. 스타일을 적용한 색상 오프셋 적용.

계속하기 전에 필터와 스타일의 차이점을 이해하는 것이 중요합니다.

필터 vs. 스타일

앞서 언급했듯이 필터는 픽셀 단위로 작동합니다. 객체는 텍스처로 렌더링되고 필터는 어떤 방식으로든 해당 텍스처를 처리합니다. 반면 스타일은 객체의 모든 원래 기하학에 액세스하거나 더 정확하게는 객체의 정점에 액세스할 수 있습니다.

이렇게 하면 스타일이 일부 제한됩니다 (예 : 스타일로 흐림 효과를 얻을 수 없음). 첫 번째로, 객체를 텍스처로 그리는 첫 번째 단계가 필요 없기 때문입니다. 둘째, 가장 중요한 것은 스타일이 지정된 메시를 일괄 처리 할 수 있습니다.

아시다시피, 드로우 콜의 수를 줄이는 것은 높은 프레임 속도에 매우 중요합니다. 이러한 상황이 발생하는지 확인하기 위해 Starling은 드로잉을 하기 전에 최대한 많은 오브젝트를 배치합니다. 문제는 함께 묶을 수있는 객체를 결정하는 방법입니다. 스타일이 작동하는 위치: 같은 스타일의 오브젝트만 함께 배치할 수 있습니다.

스테이지에 ColorOffsetFilter가 적용된 세 개의 이미지를 추가하면 적어도 세 번의 드로우 콜이 표시됩니다. 대신에 ColorOffsetStyle을 사용하여 세 개의 객체를 추가하면 하나만 있을 것입니다. 따라서 스타일을 작성하기가 조금 더 어려워집니다. 하지만 그만큼 가치가 있습니다!

3.6.2. MeshStyle 확장

모든 스타일의 기본 클래스는 starling.styles.MeshStyle입니다. 이 클래스는 우리가 필요로 하는 모든 인프라를 제공합니다. 먼저 스텁을 살펴 보겠습니다.

public class ColorOffsetStyle extends MeshStyle
{
    public static const VERTEX_FORMAT:VertexDataFormat =
            MeshStyle.VERTEX_FORMAT.extend("offset:float4");

    private var _offsets:Vector.<Number>;

    public function ColorOffsetStyle(
        redOffset:Number=0, greenOffset:Number=0,
        blueOffset:Number=0, alphaOffset:Number=0):void
    {
        _offsets = new Vector.<Number>(4, true);
        setTo(redOffset, greenOffset, blueOffset, alphaOffset);
    }

    public function setTo(
        redOffset:Number=0, greenOffset:Number=0,
        blueOffset:Number=0, alphaOffset:Number=0):void
    {
        _offsets[0] = redOffset;
        _offsets[1] = greenOffset;
        _offsets[2] = blueOffset;
        _offsets[3] = alphaOffset;

        updateVertices();
    }

    override public function copyFrom(meshStyle:MeshStyle):void
    {
        // TODO
    }

    override public function createEffect():MeshEffect
    {
        return new ColorOffsetEffect();
    }

    override protected function onTargetAssigned(target:Mesh):void
    {
        updateVertices();
    }

    override public function get vertexFormat():VertexDataFormat
    {
        return VERTEX_FORMAT;
    }

    private function updateVertices():void
    {
        // TODO
    }

    public function get redOffset():Number { return _offsets[0]; }
    public function set redOffset(value:Number):void
    {
        _offsets[0] = value;
        updateVertices();
    }

    // the other offset properties need to be implemented accordingly.

    public function get/set greenOffset():Number;
    public function get/set blueOffset():Number;
    public function get/set alphaOffset():Number;
}

그것이 우리의 출발점입니다. 마지막 예제의 초기 필터 클래스보다 조금 더 많은 부분이 이미 있음을 알 수 있습니다. 이제 코드의 개별 부분을 살펴 보겠습니다.

버텍스 포맷(Vertex Formats)

가장 주목할만한 점은 클래스 맨 꼭대기에 있는 정점 형식 상수입니다. 이미 스타일은 오브젝트의 모든 지오메트리에 대한 액세스를 제공하는 정점 레벨에서 작동한다고 언급했습니다. VertexData 클래스는 그 지오메트리를 저장하지만, 이 클래스가 자신에게 저장되는 데이터와 해당 클래스를 어떻게 알 수 있는지 결코 설명하지 않았습니다. 이것은 VertexDataFormat에 의해 정의됩니다.

MeshStyle에서 사용하는 기본 형식은 다음과 같습니다.

position:float2, texCoords:float2, color:bytes4

이 문자열의 구문은 익숙해 보일 것입니다. 특정 데이터 유형이 있는 속성 목록입니다.

  • position 속성은 두 개의 부동 소수점을 저장합니다 (정점의 x 및 y 좌표 용).

  • texCoords 속성은 두 개의 부동 소수점을 저장합니다 (꼭지점의 텍스처 좌표).

  • color 속성은 정점 색상 (각 채널 당 1 바이트)에 대해 4 바이트를 저장합니다.

이형식의 VertexData 인스턴스는, 서식 캐릭터 라인과 완전히 같은 순서로, 메시의 각 정점에 대해 그러한 속성을 포함합니다. 이것은 각 버텍스가 20 바이트 (8 + 8 + 4)를 차지한다는 것을 의미합니다.

메쉬를 생성하고 특히 스타일을 지정하지 않으면 표준 MeshStyle에 의해 렌더링되어 정확하게 이 형식을 정점에 강제 적용합니다. 이것은 결국 질감이 있는 채색된 메쉬를 그리는 데 필요한 모든 정보입니다.

하지만 ColorOffsetStyle의 경우 충분하지 않습니다. 색상 오프셋도 저장해야 합니다. 따라서 4 개의 부동 소수점 값으로 구성된 오프셋 속성을 추가하는 새로운 형식을 정의해야 합니다.

MeshStyle.VERTEX_FORMAT.extend("offset:float4");
// => position:float2, texCoords:float2, color:bytes4, offset:float4

자, 여러분은 이제 질문할 것입니다. 왜 우리는 이것을 필요로 합니까? 결국 이 필터는 맞춤 버텍스 형식을 사용하지 않고도 정상적으로 작동했습니다.

그건 아주 좋은 질문입니다, 당신이 물어봐서 기뻐요! 그 답은 Starling의 일괄 처리 코드에 있습니다. 우리가 스타일을 일부 후속 메쉬에 지정할 때, 그것들은 함께 일괄적으로 배치될 것입니다. 이것이 바로 우리가 이 노력을 하는 이유입니다.

하지만 배치는 무엇을 의미할까요? 그것은 단지 모든 개별 메쉬의 정점을 하나의 더 큰 메쉬로 복사하여 렌더링한다는 것을 의미합니다. Starling의 렌더링 내부에는 다음과 비슷한 코드가 있습니다.

var batch:Mesh = new Mesh();

batch.add(meshA);
batch.add(meshB);
batch.add(meshC);

batch.style = meshA.style; // ← !!!
batch.render();

문제가 보이나요? 큰 메쉬 (일괄 처리)는 처음 추가된 메쉬 스타일의 복사본을 받습니다. 하지만, 이 세 가지 스타일은 아마도 다른 설정을 사용할 것입니다. 이러한 설정이 스타일에 저장되는 경우 렌더링을 통해 하나만 제외하고 모두 사라집니다. 대신 스타일은 대상 메쉬의 VertexData에 데이터를 저장해야 합니다! 그래야 큰 배치 메쉬가 모든 오프셋을 개별적으로 받을 수 있습니다.

그것이 중요하기 때문에 나는 다음과 같이 다시 한번 말합니다: 스타일의 설정은 항상 대상 메쉬의 정점 데이터에 저장해야 합니다.

관습에 따라 정점 형식은 항상 스타일 클래스의 정적 상수로 액세스 할 수 있으며 vertexFormat 속성에서도 반환됩니다. 스타일이 메시에 지정되면 정점이 자동으로 새 형식에 맞춰집니다.

당신이 그 개념을 이해할 때, 당신은 이미 이 모든 과정의 중간 단계일 것입니다. 나머지는 단편 상수 대신 정점 데이터에서 오프셋을 읽도록 코드를 업데이트하는 것입니다.

하지만 나는 나 자신보다 앞서 가고 있습니다.

멤버 변수(Member Variables)

방금 모든 데이터가 꼭지점에 저장되어야 한다고 주장했지만 여전히 멤버 변수에 저장된 오프셋 집합이 있습니다:

private var _offsets:Vector.<Number>;

개발자가 스타일을 메시에 지정하기 전에 구성할 수 있기를 바랍니다. 대상 객체가 없으면 이러한 오프셋을 저장할 수 있는 정점 데이터가 없습니다. 그래서 우리는 이 벡터를 대신 사용할 것입니다. 대상이 지정되자마자 값은 대상의 정점 데이터에 복사됩니다 (onTargetAssigned 참조).

copyFrom

일괄 처리 중 스타일은 가끔씩 한 인스턴스에서 다른 인스턴스로 복사되어야 합니다 (주로 가비지 컬렉터를 고민하지 않고도 스타일을 다시 사용할 수 있어야 함). 따라서, copyFrom 메소드를 오버라이드 (override) 할 필요가 있습니다. 우리는 이렇게 할 것입니다 :

override public function copyFrom(meshStyle:MeshStyle):void
{
    var colorOffsetStyle:ColorOffsetStyle = meshStyle as ColorOffsetStyle;
    if (colorOffsetStyle)
    {
        for (var i:int=0; i<4; ++i)
            _offsets[i] = colorOffsetStyle._offsets[i];
    }

    super.copyFrom(meshStyle);
}

이것은 다소 간단합니다. 복사하려는 스타일이 올바른 유형인지 확인한 다음 현재 인스턴스에서 모든 오프셋을 복제합니다. 나머지는 수퍼 클래스가 수행합니다.

createEffect

이건 좀 친근해 보이네요. 그렇죠?

override public function createEffect():MeshEffect
{
    return new ColorOffsetEffect();
}

그것은 필터 클래스처럼 작동합니다; 나중에 생성할 ColorOffsetEffect를 반환할 것입니다. 아니요. 오프셋 값이 정점에서 읽혀지기 때문에 필터에 사용 된 것과 같지 않지만 두 가지 모두에 적용되는 효과를 만들 수 있습니다.

onTargetAssigned

위에서 언급했듯이 타겟 메쉬의 정점 데이터에 오프셋을 저장해야 합니다. 예, 이것은 각 옵셋이 모든 버텍스에 저장된다는 것을 의미합니다. 스타일이 일괄 처리를 지원한다는 것을 보장하는 유일한 방법입니다.

필터에 대상이 지정되면 이 콜백이 실행됩니다. 즉, 정점을 업데이트하는 단서가 됩니다. 우리는 다른 곳에서도 이 작업을 다시 수행할 것이므로 실제 프로세스를 updateVertices 메소드로 옮겼습니다.

override protected function onTargetAssigned(target:Mesh):void
{
    updateVertices();
}

private function updateVertices():void
{
    if (target)
    {
        var numVertices:int = vertexData.numVertices;
        for (var i:int=0; i<numVertices; ++i)
            vertexData.setPoint4D(i, "offset",
                _offsets[0], _offsets[1], _offsets[2], _offsets[3]);

        setRequiresRedraw();
    }
}

해당 vertexData 객체의 출처를 궁금해 할 수 있습니다. 대상이 지정되자 마자 vertexData 속성은 대상의 꼭짓점을 참조합니다 (스타일 자체는 결코 어떤 꼭짓점도 소유하지 않습니다). 따라서 위의 코드는 대상 메쉬의 모든 정점을 반복하고 올바른 오프셋 값을 할당하므로 렌더링 중에 사용할 준비가 되었습니다.

3.6.3. MeshEffect 확장

이제 스타일 클래스가 완성되었습니다. 실제 렌더링이 이루어지는 곳으로 이동합니다. 이번에는 MeshEffect 클래스를 확장할 것입니다. 효과는 저수준 렌더링 코드 작성을 단순화 함을 기억하십시오. 저는 실제로 다음과 같은 상속을 가진 클래스 그룹에 대해 말하고 있습니다 :

effect classes

기본 클래스 (효과)는 절대 최솟값만 수행합니다. 흰색 삼각형을 그립니다. FilterEffect는 텍스처에 대한 지원을 추가하고 색상 및 알파에 대한 MeshEffect를 추가합니다.

이 두 클래스는 TexturedEffect와 ColoredTexturedEffect라는 이름이 붙어있을 수도 있지만 사용법을 염두에두고 이름을 지정했습니다. 필터를 만들면 FilterEffect를 확장해야 합니다. 메쉬 스타일, MeshEffect를 만든다면.

이제 ColorOffsetEffect의 설정을 보겠습니다. 몇 개의 스텁이 나중에 채워집니다.

class ColorOffsetEffect extends MeshEffect
{
    public  static const VERTEX_FORMAT:VertexDataFormat =
        ColorOffsetStyle.VERTEX_FORMAT;

    public function ColorOffsetEffect()
    { }

    override protected function createProgram():Program
    {
        // TODO
    }

    override public function get vertexFormat():VertexDataFormat
    {
        return VERTEX_FORMAT;
    }

    override protected function beforeDraw(context:Context3D):void
    {
        super.beforeDraw(context);
        vertexFormat.setVertexBufferAt(3, vertexBuffer, "offset");
    }

    override protected function afterDraw(context:Context3D):void
    {
        context.setVertexBufferAt(3, null);
        super.afterDraw(context);
    }
}

이전 자습서의 아날로그 필터 효과와 비교하면 모든 오프셋 속성이 제거 된 것을 볼 수 있습니다. 이제 우리는 이제 vertexFormat을 오버라이드하여 해당 스타일과 동일한 형식을 사용하고 오프셋 값을 각 꼭지점에 저장할 준비가 되었습니다.

beforeDraw와 afterDraw

이제 beforeDraw 및 afterDraw 메소드는 셰이더의 오프셋 특성을 va3 (정점 특성, vertex attribute 3)으로 읽을 수 있도록 컨텍스트를 구성합니다. beforeDraw에서 그 라인을 살펴 보겠습니다.

vertexFormat.setVertexBufferAt(3, vertexBuffer, "offset");

이것은 다음과 같습니다.

context.setVertexBufferAt(3, vertexBuffer, 5, "float4");

세번째 매개 변수 (5 → bufferOffset)는 꼭지점 형식 내에서 색상 오프셋의 위치를 나타내고 마지막 점은 속성의 형식을 나타냅니다 (float4 → format). 이러한 값을 계산하고 기억할 필요가 없도록 vertexFormat 객체에 해당 속성을 설정하도록 요청할 수 있습니다. 그렇게 했을 때 형식이 변경되면 코드가 계속 작동하며 오프셋 전에 다른 속성을 추가합니다.

드로우 콜은 다른 형식을 사용하기 때문에, 드로잉이 끝나면 버텍스 버퍼 속성은 항상 지워져야 합니다. 그게 우리가 afterDraw 메소드에서 하는 일입니다.

createProgram

마침내 스타일의 핵심을 다룰 시간입니다. 실제 렌더링을 수행하는 AGAL 코드 이번에는 버텍스 쉐이더도 구현해야 합니다. 사용자 정의 로직을 추가해야 하기 때문에 표준 구현을 사용하지 않습니다. 그러나 조각 쉐이더는 필터에 대해 작성한 셰이더 쉐이더와 거의 동일합니다. 한 번 보시죠!

override protected function createProgram():Program
{
    var vertexShader:String = [
        "m44 op, va0, vc0", // 4x4 matrix transform to output clip-space
        "mov v0, va1     ", // pass texture coordinates to fragment program
        "mul v1, va2, vc4", // multiply alpha (vc4) with color (va2), pass to fp
        "mov v2, va3     "  // pass offset to fp
    ].join("\n");

    var fragmentShader:String = [
        tex("ft0", "v0", 0, texture) +  // get color from texture
        "mul ft0, ft0, v1",             // multiply color with texel color
        "mov ft1, v2",                  // copy complete offset to ft1
        "mul ft1.xyz, v2.xyz, ft0.www", // multiply offset.rgb with alpha (pma!)
        "add oc, ft0, ft1"              // add offset, copy to output
    ].join("\n");

    return Program.fromSource(vertexShader, fragmentShader);
}

버텍스 쉐이더가 무엇을 하는지 이해하려면 먼저 작업하고 있는 입력을 이해해야 합니다.

  • VA 레지스터 ( 「정점 속성」)에는, 정점 버퍼로부터 취해진 현재 정점의 속성이 포함됩니다. 그것들은 조금 더 일찍 설정한 정점 포맷의 속성과 같이 정렬됩니다. va0은 정점 위치이고, va1은 텍스처 좌표이며, va2는 색상이며, va3는 오프셋입니다.

  • 두 개의 상수는 모든 정점에 대해 동일합니다. vc0-3은 모델 뷰 - 프로젝션 행렬을 포함하고 vc4는 현재 알파 값을 포함합니다.

모든 버텍스 쉐이더의 주요 임무는 꼭지점 위치를 소위 "clip-space, 클립 공간"으로 이동시키는 것입니다. 꼭짓점 위치에 mvpMatrix (modelview-projection 행렬)를 곱하면 됩니다. 첫 번째 라인은 이를 처리하고 Starling의 모든 버텍스 쉐이더에서 찾을 수 있습니다. 꼭지점이 화면에서 끝나는 곳을 알아내는 일은 책임이라는 말로 충분합니다.

그렇지 않으면 우리는 "다양한 레지스터" v0 - v2를 통해 프래그먼트 셰이더에 데이터를 전달하는 것보다 더 많거나 적습니다.

프래그먼트 셰이더는 필터 클래스에 상응하는 거의 동일한 복제본입니다. 차이점을 찾을 수 있습니까? 오프셋을 읽는 레지스터입니다. 이전에는 v2에서 상수에 저장되었습니다.

3.6.4. 시도해 보기

당신은 그것을 가지고 있습니다: 우리는 거의 우리 스타일로 끝냈습니다! 테스트 합시다. 진정으로 대담한 움직임에서, 저는 두 개의 객체에서 즉시 사용하므로 일괄 처리가 올바르게 작동하는지 확인할 수 있습니다.

var image:Image = new Image(texture);
var style:ColorOffsetStyle = new ColorOffsetStyle();
style.redOffset = 0.5;
image.style = style;
addChild(image);

var image2:Image = new Image(texture);
image2.x = image.width;
var style2:ColorOffsetStyle = new ColorOffsetStyle();
style2.blueOffset = 0.5;
image2.style = style2;
addChild(image2);
Custom Style Sample
Figure 54. 두 개의 스타일이 지정된 이미지. 하나의 드로우 콜로 렌더링됩니다.

만세, 이건 실제로 작동합니다! 왼쪽 상단의 그리기 횟수를 확인하십시오. 이는 정직하고 지속적인 "1"입니다.

그래도 조금 더 할 일이 있습니다. 위의 셰이더는 항상 데이터를 읽을 텍스처가 있다고 가정하여 만들어졌습니다. 그러나 텍스쳐를 사용하지 않는 메쉬에 스타일을 할당할 수도 있습니다. 그래서 우리는 이 케이스를 위한 특정 코드를 작성해야 합니다 (너무 간단해서 지금 당장 그것에 대해 자세히 설명하지 않을 것입니다).

이 마지막 순간의 수정을 포함한 전체 클래스는 여기에서 찾을 수 있습니다 : ColorOffsetStyle.as.

3.6.5. 이제 어디로 가야 하나

그것은 우리 스타일입니다! 나는 우리가 우리의 임무에 성공했다는 사실에 대해 당신이 기분이 흥분되기를 바랍니다. 위에서 보는 것은 상상력에 의해서만 제한되는 방식으로 Starling을 확장하는 열쇠입니다. MeshStyle 클래스는 슬리브를 조금 더 트릭하기 때문에 전체 클래스 문서를 읽으십시오.

저는 여러분이 무엇을 생각해낼지 기대하고 있습니다!

3.7. 거리 필드 렌더링(Distance Field Rendering)

여러 번 언급했듯이 비트맵 폰트는 Starling에서 텍스트를 렌더링하는 가장 빠른 방법입니다. 그러나 여러 크기로 텍스트를 표시해야 하는 경우 비트맵 폰트의 크기가 잘 조정되지 않는다는 것을 곧 알게 될 것입니다. 위로 스케일 업하면 흐리게 처리되고 축소하면 앨리어싱 문제가 발생합니다. 따라서 최상의 결과를 얻으려면 응용 프로그램 내에서 사용되는 모든 크기로 글꼴을 포함해야 합니다.

Distance Field Rendering은 이 문제를 해결합니다. 높은 배율에서도 비트맵 폰트 및 기타 단색 도형을 들쭉날쭉하게 그릴 수 있습니다. 이 기술은 Valve Software의 SIGGRAPH paper에서 처음 소개되었습니다. Starling에는 이 기능을 Starling에 추가하는 MeshStyle이 포함되어 있습니다.

그것이 어떻게 작동하는지 이해하기 위해, 나는 당신에게 하나의 이미지에 그것을 사용하는 방법을 보여줌으로써 시작할 것입니다. 이것은 예를 들어, 응용 프로그램 전체에서 사용하려는 아이콘이 되어야 합니다.

3.7.1. 한 장의 이미지를 렌데링하기

우리는 이 매뉴얼에 이미 많은 새가 있었으므로 이번에는 포식자를 찾으러 가겠습니다! 내 고양이는 그 일을 할 자격이 있습니다. 그녀의 초상화가 검은 색 벡터 윤곽선으로 되어있어 이 사용 사례에 아주 좋습니다.

Cat
Figure 55. "Seven of Nine, 세븐 오브 나인"에게 인사 하시죠, 제 고양이입니다!

불행히도 Starling은 벡터 이미지를 표시할 수 없습니다. 비트맵 텍스처 (PNG 형식)로 Seven이 필요합니다. 고양이를 대략 원래 크기 (척도 == 1)로 표시하고자 하는 한 훌륭한 작품입니다. 그러나 이미지를 확대하면 금세 흐려집니다.

Scaled Cat
Figure 56. 스케일을 조정하면 기존 텍스처가 흐려집니다.

이것은 정확하게 이 이미지를 거리 필드 텍스처(distance field texture)로 변환하여 피할 수 있는 것입니다. Starling에는 실제로 이 변환 프로세스를 처리하는 편리한 도구가 포함되어 있습니다. "Field Agent, 필드 에이전트"라고 하며 Starling 저장소의 util 디렉토리에서 찾을 수 있습니다.

필드 에이전트를 사용하려면 Ruby와 ImageMagick이 모두 설치되어 있어야 합니다. 동봉된 README 파일을 참조하여 종속성을 설치하는 방법을 찾으십시오. 이 도구는 Windows와 macOS에서 모두 작동합니다.

나는 고양이의 고해상도 PNG 버전으로 시작하여 현장 대리인에게 전달했습니다.

ruby field_agent.rb cat.png cat-df.png --scale 0.25 --auto-size

이렇게하면 원본 크기의 25% 거리 필드 텍스처가 생성됩니다. 필드 에이전트는 고해상도 텍스처를 전달하고 그 텍스처를 스케일링하면 가장 잘 작동합니다. 거리 필드는 모양의 세부 사항을 인코딩하므로 입력 텍스처보다 훨씬 작을 수 있습니다.

Cat Distance Field Texture
Figure 57. 거리 필드 텍스처 결과.

원래의 날카로운 윤곽선이 흐릿한 그라데이션으로 바뀌었습니다. 이것이 거리 필드입니다. 각 픽셀에서 원래 모양의 가장 가까운 가장자리까지의 거리를 인코딩합니다.

이 텍스처는 투명한 배경에 실제로는 순수한 흰색입니다. 나는 당신이 더 나은 결과를 볼 수 있도록 그냥 배경을 검정색으로 했다.

흐려지는 정도를 스프레드라고 합니다. 필드 에이전트는 기본값 인 8 픽셀을 사용하지만 이를 사용자 정의할 수 있습니다. 퍼짐이 높을수록 스케일링이 향상되고 특수 효과를 더 쉽게 추가 할 수 있지만 (나중에 자세히 설명합니다) 가능한 범위는 입력 이미지에 따라 다릅니다. 입력에 매우가는 선이 들어 있으면 높은 퍼짐을 위한 충분한 공간이 없습니다.

Starling에서 이 텍스처를 표시하려면 텍스처를 로드하고 이미지에 할당하기만 하면 됩니다. DistanceFieldStyle을 지정하면 Starling이 거리 필드 렌더링으로 전환됩니다.

var texture:Texture = assets.getTexture("cat-df");
var image:Image = new Image(texture);
image.style = new DistanceFieldStyle();
image.color = 0x0; // we want a black cat
addChild(image);

이 스타일을 적용하면 높은 스케일 값에서도 텍스처가 완벽하게 선명하게 유지됩니다. 매우 세분화된 영역 (예 : 세븐의 헤어컷) 주변에는 작은 유물만 표시됩니다.

Scaled cat using a distance field texture
Figure 58. 거리 필드 텍스처 크기 조정.

텍스처를 만들 때 사용한 "spread, 확산"에 따라 부드러움 매개 변수를 업데이트하여 원하는 선명도 / 부드러움을 얻을 수 있습니다. 이것이 스타일 생성자의 첫 번째 매개 변수입니다.

Rule of thumb(엄지손가락의 법칙, 대략적인 계산법): softness = 1.0 / spread.
렌더 모드(Render Modes)

실제로 거리 필드 텍스처의 가장 기본적인 사용법입니다. 거리 필드 스타일은 몇 가지 다른 렌더링 모드를 지원합니다. 윤곽선, 그림자, 광선 등이 있습니다. 이러한 효과는 모두 특정 조각 쉐이더에서 렌더링되므로 추가 드로우 콜이 필요하지 않습니다. 다른 말로하면, 이러한 효과는 기본적으로 성능면에서 무료입니다!

var style:DistanceFieldStyle = new DistanceFieldStyle();
style.setupDropShadow(); // or
style.setupOutline(); // or
style.setupGlow();
Cat rendered with different modes
Figure 59. 거리 필드 스타일의 다른 모드.

멋집니다, 그렇죠?

유일한 제한 사항은 두 가지 모드를 결합 할 수 없다는 것입니다. 외곽선과 그림자를 모두 가질 수 있습니다. 그래도 조각 필터를 다시 사용할 수는 있습니다.

3.7.2. 거리 필드 폰트(Distance Field Fonts)

거리 필드 렌더링의 특성은 텍스트에도 완벽하게 잘 어울립니다. 좋은 소식: Starling의 표준 비트맵 폰트 클래스는 거리 필드 스타일과 잘 작동합니다. 실제 글꼴 텍스쳐를 만드는 것은 약간 번거롭기만 합니다.

비트맵 폰트는 모든 글리프를 포함하는 아틀라스 텍스처와 각 글리프의 속성을 설명하는 XML 파일로 구성됩니다. 후 처리 단계에서 필드 에이전트를 사용하여 간단하게 텍스처를 변환할 수는 없습니다. 그리드마다 스프레드를 보충하기 위해 패딩이 필요하기 때문입니다.

따라서 거리 필드 텍스처를 기본적으로 지원하는 비트맵 폰트 도구를 사용하는 것이 가장 좋습니다. 가능한 도구들은 다음과 같습니다:

  • Littera — 무료 온라인 비트맵 폰트 생성기.

  • Hiero — 무료 온라인 비트맵 폰트 생성기.

  • BMFont — AngelCode의 Windows 전용 도구.

개인적으로는 Hiero를 사용하여 최상의 결과를 얻었지만 사용자 인터페이스가 그다지 좋지는 않습니다. 앞으로 제품이 향상 될 수 있기를 바랍니다.

Hiero에 관해서는 여기에 프로세스를 설명하는 아주 좋은 소개가 있습니다. 안타깝게도 here는 Starling에서 요구하는 XML 형식을 내보낼 수 없습니다. 이 작은 perl script, 펄 스크립트가 도움이 될 수도 있습니다.

사용하는 도구나 프로세스는 무엇이든 간에 결국: 평소처럼 텍스처와 .fnt 파일을 갖게 됩니다. 비트맵 폰트를 만들고 등록하는 코드는 다음과 같습니다.

[Embed(source="font.fnt", mimeType="application/octet-stream")]
public static const FontXml:Class;

[Embed(source="font.png")]
public static const FontTexture:Class;

var texture:Texture = Texture.fromEmbeddedAsset(FontTexture);
var xml:XML = XML(new FontXml());
var font:BitmapFont = new BitmapFont(texture, xml)
TextField.registerCompositor(font);

var textField:TextField = new TextField(200, 50, "I love Starling");
textField.format.setTo(font.name, BitmapFont.NATIVE_SIZE);
addChild(textField);

이 시점까지는 새로운 것이 없습니다. 거리 필드 렌더링으로 전환하기 위해 적절한 스타일을 TextField에 추가합니다.

var style:DistanceFieldStyle = new DistanceFieldStyle();
textField.style = style;

이 모든 노력에 대한 보상: 이제는 해당 폰트를 거의 모든 크기에서 사용할 수 있으며 위에서 설명한 모든 유연한 렌더링 모드를 사용할 수 있습니다.

Scaled TextField with a Bitmap Font
Figure 60. 거리 필드를 사용하는 비트맵 폰트는 크기에 관계없이 멋지게 보입니다.

3.8. 요약

스스로에게 확인해 보세요: 우리는 아주 진보된 주제의 많은 것을 포함했습니다.

  • 이제는 메모리 효율성뿐만 아니라 표준 PNG보다 빠르게 로드되는 ATF 텍스처에 익숙합니다.

  • 컨텍스트 손실을 복구하는 방법은 AssetManager를 사용하거나 자체 복원 코드를 제공하는 방법을 알고 있습니다.

  • 메모리를 낭비하지 않는 방법과 메모리 누수를 피하고 찾아내는 방법에 대한 느낌이 있습니다.

  • 퍼포먼스가 문제가 되면, 당신의 첫번째 모습은 드로우 카운트입니다. 배치 작업이 중단되지 않도록 하는 방법을 알고 있습니다.

  • 로우 레벨 렌더링 코드는 처음에는 생각보다 훨씬 덜 무섭습니다. 여러분은 이미 자신의 필터와 스타일을 작성했습니다!

  • 거리 필드 렌더링은 확장 가능한 글꼴 또는 다른 단색 모양을 염두에 두는 데 유용한 기술입니다.

이 지식은 다가오는 모든 프로젝트에서 많은 시간과 문제를 줄여줍니다. 그리고 그 중 일부는 모바일 하드웨어에서 실행해야 할 것입니다. 맞습니까?

4. 모바일 개발

Adobe AIR는 크로스 플랫폼 개발과 관련하여 현재 사용할 수 있는 가장 강력한 솔루션 중 하나입니다. 누군가가 "크로스 플랫폼"이라고 말하면 일반적으로 iOS와 Android를 의미합니다.

이러한 모바일 플랫폼을 위한 개발은 매우 어려울 수 있습니다. 다양한 종류의 다양한 장치 유형이 있습니다. 다양한 화면 해상도와 논리적으로 불만족하는 종횡비가 있습니다. 부상에 대한 모욕을 주기 위해 포켓 계산기 이외에 다른 것을 강화할 의도가 없었던 CPU가 장착되어 있는 경우도 있습니다.

개발자라면 어깨를 으쓱하고 소매를 감고서 바로 뛰어들 수 있습니다. 여행의 반대편에 명성과 운이 있다는 것을 적어도 알고 있습니다![7]

4.1. 다중 해상도 개발

오, 우리가 하나의 화면을 위해 게임을 개발한 적이 있었나요? 그때로 돌아가보면, HTML 페이지에 작은 직사각형 영역이 있었으며, 여기에서 스프라이트, 텍스트 및 이미지를 배치했습니다. 그때는 하나의 해상도가 효과가 있었습니다.

아아…​지금은 그들이 변화하는 시대입니다! 휴대 전화는 모든 종류와 크기로 제공되며 데스크탑 컴퓨터와 노트북도 고밀도 디스플레이를 제공합니다. 이것은 우리 소비자를 위한 좋은 소식이지만 개발자로서의 우리 삶을 좋게 만들어주는 것은 아닙니다.

그러나 희망을 포기하지 마십시오: 당신은 그것을 관리할 수 있습니다. Starling이 제공하는 몇 가지 간단한 메커니즘을 사용하여 미리 생각해 보는 것입니다.

문제는 처음에는 다소 압도적인 것입니다. 이것이 우리가 작은 단계에서 수행하는 이유입니다. 우리는 2007년에 시작할 것입니다.

네, 정확하게 들으셨습니다: DeLorean에 올라서 Flux Capacitor ™를 시동하고 시간 당 80마일을 달리는 동안 단단히 잡으십시오.

4.1.1. iPhone

아이폰은 틀림없이 캐주얼 게임을 위한 가장 인기있는 플랫폼입니다. 2007년으로 돌아가서, 다시 개발할 수 있는 유일한 도구입니다. 그때는 거대한 App Store 골드 러시의 시간이었습니다!

해상도 320 x 480 픽셀로 첫 번째 iPhone은 매우 쉽게 개발할 수 있었습니다. 허락된다면 Starling은 그 당시에는 없었지만 다음과 같이 시작했을 것입니다:

var screenWidth:int  = stage.fullScreenWidth;
var screenHeight:int = stage.fullScreenHeight;
var viewPort:Rectangle = new Rectangle(0, 0, screenWidth, screenHeight);

starling = new Starling(Game, stage, viewPort);

우리는 viewPort를 화면의 전체 크기 (320 × 480 픽셀)로 설정했습니다. 기본적으로 스테이지의 크기는 정확히 동일합니다.

PenguFlip on the iPhone
Figure 61. 원래 iPhone에서의 게임.

지금까지는 너무 쉽습니다. 예를 들어 브라우저 용 게임처럼 작동합니다. (그러면 Internet Explorer 6이 될 것입니다.)

다음 정거장 : 2010년

4.1.2. iPhone Retina

우리는 DeLorean을 오래된 Apple 캠퍼스 구석에 두고 App Store 차트를 확인합니다. 만세! 분명히 우리 게임은 2007년에 큰 성공을 거두었으며 여전히 상위 10 위 안에 들었습니다! 지체할 시간이 없습니다. 아이폰 4가 몇 주 후에 나올 것으로 보입니다.

우리가 미래에서 나올 때부터, 우리는 Apple 마케팅 팀의 "Retina Display"라는 고해상도 스크린의 주요 혁신에 대해 알고 있습니다. 우리는 2007년부터 게임을 시작하고 출시 예정인 이 장치에서 게임을 시작합니다.

PenguFlip with a wrong scale on the iPhone4
Figure 62. 이것은 의도한 것이 아닙니다.

젠장, 게임은 이제 화면의 4 분의 1을 차지하고 있습니다! 왜 그런가요?

2007년에 작성한 코드를 살펴보면 viewPort를 화면만큼 크게 만들었음을 알 수 있습니다. iPhone 4의 경우 이 값은 두 배가 됩니다. 화면의 크기는 640 × 960 픽셀입니다. 스테이지에 표시 객체를 배치한 코드는 단지 320 × 480의 좌표 시스템을 예상했습니다. 오른쪽에 배치된 것들 (x = 320)은 갑자기 가운데에 나타납니다.

그래도 쉽게 해결할 수 있습니다. 주의 : Starling의 viewPort 및 stageWidth / Height 속성은 독립적으로 설정할 수 있습니다.

  • viewPort는 Starling이 렌더링하는 화면의 영역을 결정합니다. 항상 픽셀 단위로 지정됩니다.

  • 스테이지 크기는 해당 viewPort에 표시되는 좌표계의 크기를 결정합니다. 스테이지 너비가 320 인 경우 0에서 320 사이의 x 좌표를 갖는 모든 객체는 viewPort의 크기에 관계없이 스테이지 내에 있습니다.

이 정도 지식이면, 업 스케일링은 간단합니다.

var screenWidth:int  = stage.fullScreenWidth;
var screenHeight:int = stage.fullScreenHeight;
var viewPort:Rectangle = new Rectangle(0, 0, screenWidth, screenHeight);

starling = new Starling(Game, stage, viewPort);
starling.stage.stageWidth  = 320;
starling.stage.stageHeight = 480;

viewPort는 게임이 시작되는 장치에 따라 여전히 동적입니다. 바닥에 고정된 값으로 스테이지 크기를 하드 코딩하는 두 줄을 추가했습니다.

이 값은 더 이상 픽셀을 나타내지 않으므로 포인트라고 부릅니다. 스테이지 크기는 이제 320 × 480 포인트입니다.

iPhone 4에서 게임은 다음과 같이 보입니다:

PenguFlip scaled up blurry
Figure 63. 좋아졌지만, 약간 흐릿하네요.

더 나은 방법입니다: 이제 전체 화면 크기를 사용하고 있습니다. 그러나 조금 흐릿합니다. 우리는 큰 화면을 실제로 사용하지 않습니다. 나쁜 평가가 들어오는 것을 예상할 수 있겠죠 …​ 우리는 이것을 고칠 필요가 있습니다!

HD 텍스쳐

이문제에 대한 해결책은 고해상도를 위한 특수 텍스처를 제공하는 것입니다. 픽셀 밀도에 따라 저해상도 또는 고해상도 텍스처 집합을 사용합니다. 장점: 텍스처를 선택하는 로직을 제외하고 코드를 변경할 필요가 없습니다.

단순히 다른 파일 세트를 로드하는 것만으로는 충분하지 않습니다. 결국 텍스처가 커지면 너비와 높이가 더 큰 값을 반환합니다. 320 포인트의 고정 스테이지 너비로,

  • 너비가 160 픽셀인 SD 텍스처가 스테이지의 절반을 채웁니다.

  • 해당 HD 텍스처 (폭: 320 픽셀)가 전체 스테이지를 채웁니다.

우리가 원하는 것은 HD 텍스처가 SD 텍스처와 동일한 크기를 보고하지만 더 자세한 내용을 제공하는 것입니다.

그것이 바로 Starling의 contentScaleFactor가 유용할 때입니다. 우리는 Starling의 stage와 viewPort 크기를 설정할 때 이를 암묵적으로 설정했습니다. 위에 표시된 설정을 사용하여 iPhone 4에서 다음 코드를 실행합니다.

trace(starling.contentScaleFactor); // → 2

contentScaleFactor는 뷰포트 너비를 스테이지 폭으로 나눈 값을 반환합니다. 레티나 장치에서는 "2"가 됩니다. 비 레티나 장치에서는 "1"이 됩니다. 이것은 런타임에 로드할 텍스처를 알려줍니다.

contentScaleFactor가 정수라는 것은 우연이 아닙니다. 앨리어싱 문제를 가능한 한 피하기 위해 Apple은 열 / 행당 픽셀 수를 정확히 두 배로 늘렸습니다.

텍스처 클래스에는 단순히 스케일(scale)이라고 하는 비슷한 속성이 있습니다. 올바르게 설정하면 텍스처가 원하는대로 작동합니다.

var scale:Number = starling.contentScaleFactor; (1)
var texturePath:String = "textures/" + scale + "x"; (2)
var appDir:File = File.applicationDirectory;

assetManager.scaleFactor = scale; (3)
assetManager.enqueue(appDir.resolvePath(texturePath));
assetManager.loadQueue(...);

var texture:Texture = assetManager.getTexture("penguin"); (4)
trace(texture.scale); // → Either '1' or '2'  (5)
1 Starling 인스턴스에서 contentScaleFactor를 가져옵니다.
2 배율 인수에 따라 텍스처가 1x 또는 2x 디렉토리에서 로드됩니다.
3 AssetManager에 동일한 축척 비율을 지정하면 모든 텍스처가 해당 값으로 초기화됩니다.
4 텍스처에 액세스 할 때 스케일 인수를 신경 쓸 필요가 없습니다.
5 그러나 스케일 속성을 통해 언제든지 텍스처의 스케일을 찾을 수 있습니다.
AssetManager를 사용하지 않습니까? 걱정하지 마세요: 모든 Texture.from…​ 메소드에는 스케일 인수에 대한 추가 인수가 포함되어 있습니다. 텍스처를 만들 때 바로 구성해야 합니다. 나중에 값을 변경할 수 없으니까요.

텍스처는 너비 또는 높이를 쿼리할 때 배율 인수를 고려합니다. 예를 들어, 다음은 게임의 전체 화면 배경 텍스처에서 일어날 일입니다.

File Size in Pixels Scale Factor Size in Points

textures/1x/bg.jpg

320×480

1.0

320×480

textures/2x/bg.jpg

640×960

2.0

320×480

이제 우리에게는 필요한 모든 도구가 있습니다!

  • 뒷자리에 앉은 그래픽 디자이너 (Biff라고 부름)는 모든 텍스처를 고해상도 (이상적으로는 벡터 그래픽)로 만듭니다.

  • 뒷자리에 앉은 그래픽 디자이너 (Biff라고 부름)는 모든 텍스처를 고해상도 (이상적으로는 벡터 그래픽)로 만듭니다.

  • 런타임에 Starling의 contentScaleFactor를 확인하고 이에 따라 텍스처를 로드합니다.

이것이 바로 그것입니다: 이제 우리는 레티나 게임을 가지고 있습니다! 우리 게임의 플레이어가 그 점을 고맙게 생각할 거라고 나는 확신합니다.

PenguFlip on the iPhone
Figure 64. 이제 우리는 레티나 스크린을 사용합니다!
TexturePacker 와 같은 도구를 사용하면 이 과정을 쉽게 처리할 수 있습니다. 개별 텍스처 (가장 높은 해상도)로 이미지를 제공하고 각각의 스케일 팩터에 대해 하나씩 여러 텍스처 아틀라스 작업물을 만들 수 있습니다.

레드 우드 (Redwood)의 bar 에서 성공을 축하하고 맥주 한 두 잔을 마시며 계속 나아갑시다.

4.1.3. iPhone 5

2012년에 출시된 아이폰은 우리를 위해 또 다른 놀라움을 선사합니다. 애플은 화면의 화면 비율을 바꾸었습니다. 가로로 보면 여전히 640 픽셀입니다. 하지만 수직으로 보면, 이제 조금 더 길어졌습니다 (1136 픽셀). 물론 레티나 디스플레이이기 때문에 우리의 새로운 논리적 해상도는 320 × 568입니다.

빠른 수정으로 viewPort에 씬(Scenes)을 올려 놓고 상단과 하단의 검은색 막대를 사용하여 간단히 라이브를 진행합니다.

var offsetY:int = (1136 - 960) / 2;
var viewPort:Rectangle = new Rectangle(0, offsetY, 640, 960);

흠, 확실히 효과가 있는 것 같아요! 이 시간대에 팝업을 시작하는 모든 Android 스마트 폰을 위한 공정한 전략입니다. 네, 일부 게임에서는 게임이 약간 흐릿하게 보일 수 있지만 너무 좋지 않습니까. 이미지 품질은 여전히 놀랍습니다. 대부분의 사용자는 알 수 없죠.

PenguFlip with letterbox bars
Figure 65. 레터 박스 크기 조정.

이것을 *레터 박스 전략*이라고 합니다.

  • 고정 스테이지 크기 (320x480 포인트)로 게임을 개발하십시오.

  • 축척 계수 (예 : 1x, 2x, 3x)에 따라 여러 애셋 세트를 추가합니다.

  • 그런 다음 왜곡없이 화면을 채울 수 있도록 응용 프로그램의 크기를 조정합니다.

이것은 아마도 가장 실용적인 해결책일 것입니다. 사용 가능한 모든 디스플레이 해상도에서 허용되는 품질로 게임을 실행할 수 있으므로 viewPort를 올바른 크기로 설정하는 것 이외의 추가 작업을 수행할 필요가 없습니다.

Starling과 함께 제공되는 RectangleUtil을 사용하면 후자가 매우 쉽습니다. viewPort를 "확대 / 축소"하려면 다음 코드를 사용하여 작성하십시오.

const stageWidth:int  = 320; // points
const stageHeight:int = 480;
const screenWidth:int  = stage.fullScreenWidth; // pixels
const screenHeight:int = stage.fullScreenHeight;

var viewPort:Rectangle = RectangleUtil.fit(
    new Rectangle(0, 0, stageWidth, stageHeight),
    new Rectangle(0, 0, screenWidth, screenHeight),
    ScaleMode.SHOW_ALL);

간단하면서도 효과적입니다! 우리는 확실히 타임머신과 함께 또 다른 여행을 했습니다. 올라 타세요!

4.1.4. iPhone 6와 Android

우리는 지금 2014년에…​ 그레이트 스캇! "App Store Almanac"을 확인해 보면 마지막 업데이트 이후에 판매가 좋지 않은 것으로 나타났습니다. 분명히 애플은 우리의 레터 박스 접근법에 만족하지 않았으며 이번에는 우리를 특징 짓지 않았네요. 조금도.

우리는 이제 다른 선택의 여지가 없다고 생각합니다. 총알을 물고 그 추가 스크린 공간을 사용합시다. 오랫동안 하드 코딩 된 좌표! 이제부터는 모든 표시 객체에 대해 상대적 위치를 사용해야 합니다.

나는 이 전략을 스마트 오브젝트 배치라고 부를 것입니다. 시작 코드는 여전히 매우 유사합니다.

var viewPort:Rectangle = new Rectangle(0, 0, screenWidth, screenHeight);

starling = new Starling(Game, stage, viewPort);
starling.stage.stageWidth  = 320;
starling.stage.stageHeight = isIPhone5() ? 568 : 480;

그래요, 나도 냄새가 납니다. 우리가 실행중인 장치에 따라 스테이지 높이를 하드 코딩하는 것은 별로 좋은 생각이 아닙니다. 약속하죠, 곧 고칠 것입니다.

현재까지는, 그것이 작동합니다. viewPort와 stage 모두 올바른 크기입니다. 그러나 우리는 그것을 어떻게 활용합니까? 이제 Game 클래스를 살펴보겠습니다. 클래스는 Starling 루트로 사용됩니다.

public class Game extends Sprite
{
    public function Game()
    {
        addEventListener(Event.ADDED_TO_STAGE, onAddedToStage); (1)
    }

    private function onAddedToStage():void
    {
        setup(stage.stageWidth, stage.stageHeight); (2)
    }

    private function setup(width:Number, height:Number):void
    {
        // ...

        var lifeBar:LifeBar = new LifeBar(width); (3)
        lifeBar.y = height - lifeBar.height;
        addChild(lifeBar);

        // ...
    }
}
1 게임 생성자가 호출되었지만 아직 스테이지에 연결되지 않았습니다. 그래서 우리는 완료될 때까지 초기화를 연기합니다.
2 커스텀 셋업 메소드를 호출하고 스테이지 크기를 전달합니다.
3 예를 들어, 화면 하단에 LifeBar 인스턴스 (사용자 정의 사용자 인터페이스 클래스)를 만듭니다.

모두들 너무 힘들지는 않았죠? 트릭은 항상 스테이지 크기를 고려하는 것입니다. 여기에서는 깨끗한 구성 요소로 게임을 만들었을 때 다른 인터페이스 요소를 담당하는 별도의 클래스로 비용을 지불합니다. 그것이 의미가 있는 요소라면 위의 LifeBar 생성자와 같이 크기를 전달하고 그에 따라 적절하게 처리하십시오.

PenguFlip without letterbox bars
Figure 66. 레터박스 바가 더 이상 없음 : 전체 화면이 사용됩니다.

iPhone 5에서 정말 잘 작동합니다. 우리는 2012년에 완료해야 합니다. 2014년에는 상황이 더욱 복잡해졌습니다.

  • 안드로이드는 다양한 크기와 해상도의 휴대폰으로 빠르게 시장 점유율을 확보하고 있습니다.

  • 심지어 애플은 아이폰 6과 아이폰 6 플러스로 더 큰 스크린을 소개했습니다.

  • 제가 태블릿 컴퓨터에 대해 언급 했나요?

스테이지 개체를 기준으로 표시 개체를 구성하여 이를 해결하기위한 기초를 이미 마련했습니다. 우리 게임은 거의 모든 스테이지 크기로 실행됩니다.

나머지 문제는 스테이지 크기 및 콘텐츠 배율 인수에 사용할 값입니다. 우리가 다루어야하는 스크린의 범위를 살펴보면, 이것은 어려운 일처럼 보입니다!

Device Screen Size Screen Density Resolution

iPhone 3

3,50"

163 dpi

320×480

iPhone 4

3,50"

326 dpi

640×960

iPhone 5

4,00"

326 dpi

640×1136

iPhone 6

4,70"

326 dpi

750×1334

iPhone 6 Plus

5,50"

401 dpi

1080×1920

Galaxy S1

4,00"

233 dpi

480×800

Galaxy S3

4,80"

306 dpi

720×1280

Galaxy S5

5,10"

432 dpi

1080×1920

Galaxy S7

5,10"

577 dpi

1440×2560

스케일 팩터를 알아내는 핵심은 화면의 밀도를 고려하는 것입니다.

  • 밀도가 높을수록 스케일 팩터가 높아집니다. 즉, 밀도로부터 스케일 인자를 추론할 수 있습니다.

  • 스케일 팩터로부터 적절한 스테이지 크기를 계산할 수 있습니다. 기본적으로, 우리는 이전 접근법을 되돌립니다.

원래 iPhone의 화면 밀도는 약 160 dpi였습니다. 우리는 이것을 계산의 기초로 삼습니다. 모든 장치에서 밀도를 160으로 나누고 그 결과를 다음 정수로 반올림합니다. 그 접근 방식에 대한 온전한 확인을 해봅시다.

Device Screen Size Screen Density Scale Factor Stage Size

iPhone 3

3,50"

163 dpi

1.0

320×480

iPhone 4

3,50"

326 dpi

2.0

320×480

iPhone 5

4,00"

326 dpi

2.0

320×568

iPhone 6

4,70"

326 dpi

2.0

375×667

iPhone 6 Plus

5,50"

401 dpi

3.0

414×736

Galaxy S1

4,00"

233 dpi

1.5

320×533

Galaxy S3

4,80"

306 dpi

2.0

360×640

Galaxy S5

5,10"

432 dpi

3.0

360×640

Galaxy S7

5,10"

577 dpi

4.0

360×640

결과 스테이지 크기를 살펴보십시오. 현재 320 × 480에서 414 × 736까지 다양합니다. 그것은 적당한 범위이며 또한 의미가 있습니다. 물리적으로 큰 화면은 더 큰 스테이지가 있어야 합니다. 중요한 것은 적절한 축척 계수를 선택함으로써 합리적인 좌표계가 만들어졌습니다. 이것은 우리가 확실히 작업할 수있는 범위입니다!

당신은 갤럭시 S1의 스케일 인자가 정수 값이 아니라는 것에 주목했을 것입니다. 이것은 수용 가능한 무대 크기로 끝나기 위해 필요했습니다.

제가 어떻게 그 저울 값을 생각해 냈는지 보도록 하겠습니다. ScreenSetup 클래스를 만들고 다음 내용으로 시작하십시오:

public class ScreenSetup
{
    private var _stageWidth:Number;
    private var _stageHeight:Number;
    private var _viewPort:Rectangle;
    private var _scale:Number;
    private var _assetScale:Number;

    public function ScreenSetup(
        fullScreenWidth:uint, fullScreenHeight:uint,
        assetScales:Array=null, screenDPI:Number=-1)
    {
        // ...
    }

    public function get stageWidth():Number { return _stageWidth; }
    public function get stageHeight():Number { return _stageHeight; }
    public function get viewPort():Rectangle { return _viewPort; }
    public function get scale():Number { return _scale; }
    public function get assetScale():Number { return _assetScale; }
}

이클래스에서는 viewPort와 스테이지 크기 Starling을 구성해야 합니다. 대부분의 속성은 assetScale을 제외하고는 자체적으로 설명해야 합니다.

위의 표는 "1"에서 "4"범위의 스케일 인수로 끝나는 것을 보여줍니다. 그러나 우리는 아마도 모든 크기로 텍스쳐를 생성하고 싶지 않을 것입니다. 가장 짙은 화면의 픽셀은 너무 작아서 눈이 어차피 구분할 수 없습니다. 따라서 이러한 규모 요인 (예 : 1-2 또는 1-3)의 하위 집합에 대한 애셋만 제공하면 됩니다.

  • 생성자의 assetScales 인수는 텍스처를 만든 배율 팩터로 채워진 배열로 되어 있습니다.

  • assetScale 속성은 로드해야 하는 애셋 세트를 알려줍니다.

요즘에는 응용 프로그램이 스케일 인수 "1"을 필요로 하는 경우는 거의 없습니다. 그러나 매우 큰 컴퓨터 화면을 요구하지 않고 인터페이스를 미리 볼 수 있기 때문에 이 크기는 개발 중에 편리합니다.

그렇다면 그 생성자의 구현에 대해 살펴 보겠습니다.

public function ScreenSetup(
    fullScreenWidth:uint, fullScreenHeight:uint,
    assetScales:Array=null, screenDPI:Number=-1)
{
    if (screenDPI <= 0) screenDPI = Capabilities.screenDPI;
    if (assetScales == null || assetScales.length == 0) assetScales = [1];

    var iPad:Boolean = Capabilities.os.indexOf("iPad") != -1; (1)
    var baseDPI:Number = iPad ? 130 : 160; (2)
    var exactScale:Number = screenDPI / baseDPI;

    if (exactScale < 1.25) _scale = 1.0; (3)
    else if (exactScale < 1.75) _scale = 1.5;
    else _scale = Math.round(exactScale);

    _stageWidth  = int(fullScreenWidth  / _scale); (4)
    _stageHeight = int(fullScreenHeight / _scale);

    assetScales.sort(Array.NUMERIC | Array.DESCENDING);
    _assetScale = assetScales[0];

    for (var i:int=0; i<assetScales.length; ++i) (5)
        if (assetScales[i] >= _scale) _assetScale = assetScales[i];

    _viewPort = new Rectangle(0, 0, _stageWidth * _scale, _stageHeight * _scale);
}
1 Apple iPad에 대한 간단한 해결 방법을 추가해야 합니다. iOS에서 기본적으로 사용하는 것과 동일한 척도를 사용하고 싶습니다.
2 기본 밀도는 160dpi (또는 iPads의 경우 130dpi)입니다. 이러한 밀도를 갖는 장치는 스케일 팩터 "1"을 사용합니다.
3 스케일 인수는 정수 값 또는 1.5 여야 합니다. 이 코드는 가장 가까운 것을 선택합니다.
4 여기서 로드해야 하는 애셋 세트를 결정합니다.
위의 표에서 사용한 장치에서 이 코드의 결과를 보려면 이 Gist를 참조하십시오. 이 목록에 장치를 쉽게 추가 할 수 있으며 결과에 만족하는지 확인할 수 있습니다.

모든 것이 준비되었으므로 Starling의 시작 코드를 적용할 수 있습니다. 이 코드에서는 축척 비율 "1"및 "2"로 애셋을 제공한다고 가정합니다.

var screen:ScreenSetup = new ScreenSetup(
    stage.fullScreenWidth, stage.fullScreenHeight, [1, 2]);

_starling = new Starling(Root, stage, screen.viewPort);
_starling.stage.stageWidth  = screen.stageWidth;
_starling.stage.stageHeight = screen.stageHeight;

애셋을 로드할 때는 assetScale 속성을 사용하십시오.

var scale:Number = screen.assetScale;
var texturePath:String = "textures/" + scale + "x";
var appDir:File = File.applicationDirectory;

assetManager.scaleFactor = scale;
assetManager.enqueue(appDir.resolvePath(texturePath));
assetManager.loadQueue(...);

그게 다입니다! 스테이지 크기를 염두에 두고 사용자 인터페이스를 설정해야 하는 반면에 확실히 관리할 수 있는 장점이 있습니다.

Starling 저장소에는 이 코드가 모두 포함된 Mobile Scaffold라는 프로젝트가 있습니다. 모든 모바일 애플리케이션을 위한 완벽한 출발점입니다. (아직 다운로드에서 ScreenSetup 클래스를 찾을 수 없다면 GitHub 프로젝트의 헤드 개정판을 보십시오.)
Feathers를 사용하고 있다면, ScreenDensityScaleFactorManager 클래스의 경우 위에서 쓴 ScreenSetup 클래스의 작업을 수행합니다. 사실 여기에 설명된 논리는 그 클래스에서 크게 영향을 받았습니다.

4.1.5. iPad와 기타 태블릿들

지금까지 우리가 만든 게임이 태블릿으로 이식하는 것이 타당한지 궁금해지기 시작했습니다. 위에서 나온 코드는 태블릿에서 정상적으로 작동합니다. 그러나 우리는 훨씬 더 큰 스테이지에 직면하게 될 것이며, 콘텐츠를 위한 더 많은 공간이 생길 것입니다. 처리하는 방법은 만들고 있는 응용 프로그램에 따라 다릅니다.

일부 게임은 단순히 크기를 조정할 수 있습니다.

슈퍼 마리오 브라더스 (Super Mario Bros) 또는 비주얼드 (Bejeweled)와 같은 게임은 세부적인 텍스쳐를 이용해 큰 화면을 축소한 것처럼 보입니다. 이 경우 화면 밀도를 무시하고 사용 가능한 픽셀 양을 기준으로 축척 비율을 계산할 수 있습니다.

  • 첫 번째 iPad (해상도 : 768 × 1024)는 단순히 384 × 512 크기의 스테이지 크기와 2의 배율 계수를 갖는 장치가 됩니다.

  • 레티나 iPad (해상도 : 1536 × 2048)의 스테이지 크기는 384 × 512이지만 배율은 4입니다.

다른 것들은 더 많은 콘텐츠를 표시할 수 있습니다.

Sim City 또는 Command & Conquer를 생각해보십시오. 그러한 게임은 사용자에게 더 많은 배경을 보여줄 수 있습니다. 사용자 인터페이스 요소는 게임 내용에 비해 공간을 덜 차지합니다.

일부는 완전한 인터페이스를 재고해야 합니다.

이는 특히 생산성이 높은 앱의 경우에 해당됩니다. 휴대 전화의 작은 화면에서 전자 메일 클라이언트는 단일 메일, 받은 편지함 또는 사서함을 표시합니다. 반면에 타블렛은 세 가지 요소를 모두 한꺼번에 표시할 수 있습니다. 이것이 야기할 개발 노력을 과소 평가하지 마십시오.

4.2. 기기 회전(Device Rotation)

오늘날 스마트 폰 및 태블릿의 매우 멋진 기능은 물리적으로 기기의 방향을 인식하고 이에 따라 사용자 인터페이스를 업데이트할 수 있다는 것입니다.

Starling의 방향 변경을 감지하려면 먼저 응용 프로그램의 AIR 구성 파일을 업데이트해야 합니다. 다음 설정이 포함되어 있는지 확인하십시오.

<aspectRatio>any</aspectRatio> (1)
<autoOrients>true</autoOrients> (2)
1 초기 종횡비 (세로, 가로 또는 기타).
2 시작할 때 앱이 자동 방향 지정을 시작할지 여부를 나타냅니다.

회전기기가 하면 Starling 스테이지에서 RESIZE 이벤트를 수신할 수 있습니다. 오리엔테이션이 변경될 때마다 전달됩니다. 결국 오리엔테이션 변경은 스테이지 크기가 항상 변경되도록 합니다 (너비 및 높이 전환).

해당 이벤트 핸들러에서 Starling viewPort 및 stage의 크기를 업데이트하십시오.

stage.addEventListener(Event.RESIZE, onResize);

private function onResize(event:ResizeEvent):void (1)
{
    updateViewPort(event.width, event.height);
    updatePositions(event.width, event.height);
}

private function updateViewPort(width:int, height:int):void (2)
{
    var current:Starling = Starling.current;
    var scale:Number = current.contentScaleFactor;

    stage.stageWidth  = width  / scale;
    stage.stageHeight = height / scale;

    current.viewPort.width  = stage.stageWidth  * scale;
    current.viewPort.height = stage.stageHeight * scale;
}

private function updatePositions(width:int, height:int):void (3)
{
    // Update the positions of the objects that make up your game.
}
1 이 이벤트 핸들러는 장치가 회전할 때 호출됩니다.
2 현재 화면 크기 (픽셀 단위)에 따라 stage 및 viewPort의 크기를 업데이트합니다.
3 사용자 인터페이스를 업데이트하여 새로운 방향에 맞춥니다.

이벤트 리스너에서 viewPort 및 스테이지 크기를 수동으로 업데이트해야 한다는 점에 유의하십시오. 기본적으로 응용 프로그램은 변경되지 않고 그대로 유지되므로 응용 프로그램이 잘리게 나타납니다. 위의 코드는 이를 수정합니다. 그것은 모든 스케일 팩터에 대해 작동합니다.

마지막 부분은 훨씬 더 어려워질 것입니다. 사용자 인터페이스를 업데이트하여 새로운 스테이지 크기에 맞춥니다. 이것은 모든 게임에서 의미가 있는 것은 아닙니다. 그렇다면 추가적인 노력을 고려해야 합니다. 여러분의 사용자가 그것을 고마워할 것입니다!

Starling에 제공되는 Scaffold 프로젝트에는 이 기능을 구현할 수 있는 기능이 포함되어 있습니다.

4.3. 요약

실수하지 마십시오: 모바일 플랫폼을 위한 개발은 쉽지 않습니다. 하드웨어가 다양하기 때문에 계획을 세우고 코드를 현명하게 구조화해야 합니다. 또한 시장은 매우 경쟁적이어서 디자인은 대중으로부터 눈에 띄어야 합니다.

Starling은 이 과정에서 당신을 도울 수있는 일을 합니다! 이 장에서 배운 도구를 사용하면 이러한 어려움을 극복할 수 있습니다.

5. 마지막 드리는 말씀

5.1. 업적이 잠금 해제되었습니다!

Starling Manual을 통해 작업을 완료했습니다. 우리가 정말 많은 근거를 다뤘죠? 마지막 시간 동안은 자란 수염을 좀 깍으세요.

축하합니다!

Impressive

5.2. 도움 얻기

방금 얻은 지식을 공유하고 싶습니까? 아니면 아직 답변되지 않은 질문이 있습니까? Starling 커뮤니티는 여러분을 돕기 위해 열심입니다! 공식 Starling Forum을 방문하십시오.

5.3. 더 원하나요?

Starling에 대해 충분히 알지 못했다면, Starling Handbook 의 출시를 잊지 마시기 바랍니다! sign up 이 설명서의 모든 내용 외에도 자신의 프로젝트에서 사용할 수 있는 레시피를 쉽게 따라할 수 있습니다. 동시에 Starling의 지속적인 개발을 지원하고 있습니다!

The Starling Handbook

1. Flash는 원래 2005년 Adobe에서 인수한 Macromedia에 의해 만들어졌습니다.
2. Adobe의 엔지니어 중 한 사람이 그 이유에 대해 'archive.org’를 통해 얻은 흥미로운 기사가 있습니다: http://tinyurl.com/hkbdgfn
3. 일부 픽셀 쉐이더를 어셈블리 언어로 작성하는 것이 좋습니다. 나는 그렇게 추측합니다.
4. 편집인이 이런 말 하는 것이 예의가 아니긴한데, (1) 저는 약어를 사용했고 (2) 컨텍스트 손실은 실제로는 왕짜증!! 입니다.
5. Beginning with AIR 24 and Starling 2.2, this is possible with conventional textures, as well.
6. Starling 1.x를 사용하고 있다면 'QuadBatch’를 찾아보십시오.
7. 그래도 나를 괴롭히지는 마십시오.