adsense



AndEngine 게임 개발 탐구생활 4.

4. scene관리 커스터마이즈

예제 프로젝트 : andenginesample3.zip

Scene1과 Scene2를 보면 공통된 부분이 있지요. Scene 상속, 클래스 내에서 사용하기 위한 Engine 객체 선언, Engine객체를 받는 생성자, 터치 인터페이스 설정 등.

그리고 awt나 안드로이드 SDK에서 화면 단위 관리와 마찬가지로 화면 내에서 사용하기 위한 지역 리소스의 선언과 해제 역시 들어가게 됩니다.

이것을 별개의 추상 클래스로 만들어 3.의 샘플을 다시 만들어 보았습니다.


Scene1.java와 Scene2.java를 추상클래스를 상속하는 방법으로 새롭게 다시 만들어봤습니다.

추상 클래스~ CustomScene.java 내용은 아래와 같습니다.



하는 김에, 엔진 자체에서 Back키에 의한 종료를 구현하고 있지만, 이걸 슬쩍 가로채서 종료 팝업을 띄우게 했습니다.
종료 팝업 자체는 이전에 SurfaceView 버전에서 사용한 것과 동일합니다.



이것으로 게임을 개발하기 위한 가장 기본적인 프레임워크가 준비됐습니다.

뭔가 빠지지 않았나? 싶을텐데요, 예. 매 프레임마다 그림을 그려주는 부분과 이런저런 처리를 담당하는 부분, 그리고 이걸 돌리기 위한 스레드가 빠졌습니다.

이게 실은 엔진에서 해 주고 있는 부분인 겁니다.
기존의 싱글스레드 구조에서는 그리기와 처리를 각각 분리해 모든 객체에 대한 그리기를 모아서, 모든 객체에 대한 처리를 모아서 했는데..
안드엔진에서는 객체 각각에 대한 그리기와 처리를 객체 단위로 처리합니다.※

그리고 이 객체를 scene에 붙여줌으로서 Engine이 스레드에서 이 객체를 확인, 그려주고 지정된 처리를 하는 사이클이 완성됩니다.


※기존과 유사하게 일괄로 묶어서 처리할 수도 있습니다.

차후에 다시 설명하겠지만, 각 객체는 자신의 draw, update를 별도로 오버라이드하여 실제로 그려지거나 상태의 변화 등을 객체 별로 컨트롤하도록 할 수 있습니다.

그런데, 객체 처리 중에 도달한 함수에서 객체를 소멸(detach)시켜버리면 안드엔진은 높은 확률로 오류를 냅니다.
객체를 처리하기 위한 루프에서 리스트에 있던 객체가 소멸한 후의 처리를 해주지 않고 있기 때문입니다.

그때문에 객체의 소멸 처리는, 별도로 scene에 update핸들러를 등록하여 여기서 싱글 스레드처럼 처리해줘야 합니다.


5. 스프라이트의 생성과 처리

예제 프로젝트 : andenginesample4.zip

기존의 awt나 안드로이드 SDK와는 이미지 다루는 법이 많이 달라지므로 슬슬 주의해야 합니다.
기존 비트맵으로 화면을 구성하는 방식에 익숙할수록 이쪽으로 넘어갈 때 시행착오가 커질 수 있습니다.

openGL 기반 엔진에서는 공통적으로
텍스처로 사용할 이미지를 로드하고,
이 텍스처로 스프라이트를 생성해서
스프라이트를 화면(scene)에 연결하는 방식으로 갑니다.


안드엔진에서는 다음 순서로 진행됩니다.

1. TextureAtlas로 텍스처 메모리 확보
2. TextureRegionFactory로 텍스처 메모리에 비트맵을 로드
3. 텍스처로 Sprite를 생성
4. Scene에 스프라이트를 붙인다(attach)


..라는 것은, 이런 개념에 해당한다~ 정도로, 실제 안드엔진 내부에서 처리되는 내용과는 다를 수 있습니다.
안드엔진에 한해서는 이 순서로 기억해두면 되겠습니다.


텍스처 로드

BitmapTextureAtlas
public BitmapTextureAtlas(final TextureManager pTextureManager, final int pWidth, final int pHeight)

BuildableBitmapTextureAtlas
public BuildableBitmapTextureAtlas(final TextureManager pTextureManager, final int pWidth, final int pHeight, final TextureOptions pTextureOptions) throws IllegalArgumentException

1. 위 클래스 가운데 하나를 사용해 텍스처 메모리에 비트맵을 로드할 수 있는 공간을 확보합니다.

2. 이후 BitmapTextureAtlasTextureRegionFactory.createFromAsset 을 통해 텍스처 메모리에 비트맵을 로드하고, 텍스처를 다룰 수 있는 객체를 얻게 됩니다.

샘플의 Scene1.java와 Scene2.java의 loadResources() 함수에서 각각의 예제가 사용되고 있습니다.



두 클래스의 차이

BitmapTextureAtlas는 텍스처 메모리상의 임의의 위치에 비트맵을 배치할 수 있으며
BuildableBitmapTextureAtlas는 시스템이 알아서 적절한 위치에 비트맵을 배치해 줍니다.

여러장의 비트맵을 텍스처 메모리에 동시에 올릴 경우 전자는 비트맵들이 서로 충돌하지 않도록 위치를 코드에서 직접 지정해줘야 하고, 후자는 자동으로 적절한 위치를 계산해 냅니다.

만일 먼저 로드된 비트맵과 겹치거나, 비트맵을 배치할 빈 자리가 없다면 넘치는 분량의 비트맵은 로드되지 않습니다

BuildableBitmapTextureAtlas가 편리하지만 그만큼 로딩에 시간이 좀 더 걸립니다.


사용할 비트맵을 한 장의 비트맵으로 만들어 로딩 시간과 배치의 편의 두 마리 토끼를 한꺼번에 노린 외부 툴이 있습니다. 제일 많이 사용되는 것으로는 TexturePacker가 있는데, 그것에 대해서는 차후에 다루겠습니다.


텍스처 메모리 크기 - pWidth와 pHeight

TextureAtlas 선언 시에는 메모리 영역의 가로 세로 크기를 지정하는데, 이 크기는 2의 승수(2, 4, 8, 16, 32, 64, 256, 512, 1024...)로만 쓸 수 있습니다.

단일 TextureAtlas의 경우 구형 단말에서는 1024*1024가 처리 한계인 경우가 있습니다. 그래서 호환성을 고려한다면 1024*1024를 한계로, 이보다 큰 TextureAtlas가 필요하다면 여러개의 TextureAtlas를 사용하도록 권장합니다.
하지만 해상도가 높아지고 있는 요즘 추세에서는 2048같이 크게 잡는 경우도 있습니다.



텍스처 객체 확보

BitmapTextureAtlasTextureRegionFactory를 통해 비트맵을 직접적으로 다룰 객체(TextureRegion)를 얻어내는데 해당 객체의 형식은

ITextureRegion : 한 장의 비트맵처럼 다룰 수 있습니다
ITiledTextureRegion : 타일의 가로 세로 수를 지정하여 여러 장의 타일을 한 장으로 모아놓은 것처럼 다룹니다. 각 타일의 가로 세로 크기는 일정합니다. 숫자처럼 하나의 스프라이트가 여러장의 그림 중 하나의 그림만 보여줘야 하거나, 단순 반복형 애니메이션(TiledSprite, AnimatedSprite)을 만드는데 주로 쓰입니다.

gameTextureAtlas = new BitmapTextureAtlas(this.getTextureManager(), 256,32, TextureOptions.BILINEAR);
_num_TR = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(gameTextureAtlas, this, "numpic.png", 0,0, 10,1);
gameTextureAtlas.load();



이상과 같이
BitmapTextureAtlas 선언
-> ITextureRegion으로 불러들일 비트맵 지정
-> BitmapTextureAtlas.load()로 비트맵을 로딩하여 준비 완료의 과정을 거치고,
Buildable일 경우는
gameTextureAtlas.build(new BlackPawnTextureAtlasBuilder(0, 1, 0));
이렇게 build 절차가 한 번 더 들어갑니다. (Scene2.java 참조)



Scene1에서는 BitmapTextureAtlas를 사용해서, Scene2에서는 BuildableBitmapTextureAtlas를 사용해서 비트맵을 로드하고 있습니다.
BuildableBitmapTextureAtlas를 load 할 때 인수로 주는 BlackPawnTextureAtlasBuilder가 실제 로딩 작업을 수행합니다.

이것의 인수는 비트맵을 텍스처에 배치할 때 비트맵 간의 간격을 지정하는 옵션인데, 비트맵과 비트맵을 간격 없이 너무 바짝 붙이면 아래 그림과 같이 비트맵에 다른 이미지의 음영이 비쳐 보일 수 있습니다.



스프라이트의 생성

마지막으로 스프라이트를 생성, scene에 붙여줍니다.
스프라이트의 attach는, 당연하지만 loadResources가 끝난 후에 해 줘야 합니다.

Sprite _spr = new Sprite(x,y, ITextureRegion, Engine.getVertexBufferObjectManager());
scene.attachChild(_spr);


앞으로 스프라이트의 리프레쉬 자체는 엔진 스레드에서 알아서 합니다.



비트맵과 스프라이트의 차이?

비트맵은 한 번 로드하면 화면에 몇 개고 그릴 수 있지만, 스프라이트는 그림 한 장에 하나씩 생성하여 attach 해 줘야 합니다. 비트맵은 메모리에서 캔버스 메모리로 복사하는 것이지만 스프라이트는 객체로서 다뤄야 합니다.

그때문에 숫자나 총알 같이 단순반복적인 이미지를 다량으로 뿌려줘야 할 때 비트맵처럼 생각하면 맘먹은대로 제어되지 않을 수 있습니다.

비트맵에서는 나중에 그린 비트맵이 제일 위에 오고, 스프라이트에서도 attach한 순서대로 위에 오기는 하지만 이미 attach되어있는 스프라이트의 우선 순위를 변경할 수도 있습니다. 좀 복잡하고, 실전에서는 거의 쓰지 않지만요.


다음에는, 지금까지의 내용을 토대로 기존 안드로이드버전 위치즈 플라이트의 타이틀 화면을 이식해 보겠습니다.



덧글

  • 이수환 2015/07/26 15:51 # 삭제 답글

    좋은 강의 감사합니다. 안드엔진을 찾다보니 여기까지 들르게 되었습니다.
    관련문서가 없어 여러 삽질을 하고 있는데요..
    Scene관리가 까다로운 것 같습니다. 단순히 생각에는 mEngine.setScene(원하는씬)으로 보낼 때 변수를 넣어주면 좋을 것 같았는데
    그렇게 되는 것이 아니더라구요..
    4강에서 끝난 것이 아쉽습니다. ㅠㅠ
  • 펭귄대왕 2015/07/27 08:24 #

    찾아주셔서 감사합니다.
    안드엔진은 업데이트가 중지된 상황이다보니 롤리팝에서 다소 문제가 있다든가..
    지금 주력엔진으로 계속 쓰기에는 좀 애매하게 되어버렸습니다.
    github같은데서 오픈업데이트가 진행되고 있을지도 모르겠지만,
    최근엔 제가 cocos2d-x를 메인으로 쓰는지라, 추가 내용을 진행하지 못하고 미적거리다 여기까지 질질 끌게 되어버렸네요.

    그 대신이라기엔 뭣하지만 https://github.com/ChangseOh/WitchesFlight 이쪽에서 풀소스를 공개하고 있습니다. 조금이라도 도움이 되었으면 좋겠습니다.
댓글 입력 영역


Books

Geek라이프

메가 드라이브 퍼펙트 카탈로그
마에다 히로유키 저/조기현 역

미소녀 일러스트 테크닉
B-은하, pen스케, 카와이 저/정유진 역

핵심강좌! Cocos2d-x
이재환 저

피규어의 교과서 레진 키트 & 도색 입문 편
후지타 시게토시 저/김정규 역
예스24 | 애드온2
일본서적 전문사이트 NEPIC