[[TableOfContents]] = 스터디 소개 = * 그래픽스 엔진 등 공학적인 주제에 초점을 맞춤 * RPG만들기 에뮬레이터 스마트폰 버전 * 참고 위키: [http://ko.wikipedia.org/wiki/RPG_%EB%A7%8C%EB%93%A4%EA%B8%B0 RPG만들기 한글], [http://en.wikipedia.org/wiki/RPG_Maker_VX 영문] * ~~[http://www.jpct.net/ Java로 만든 3D 물리 엔진]으로 2D 엔진 만들기~~ * 툴이 아닌 에뮬레이터 위주 * UDK로 뭔가 삽질 하다가 멘붕하고 선회한 스터디 -ㅅ- == 참가자 == * [권순의], [박재민], [박한기] == 진행 시간 == * 여름방학: 매 주 화, 목 저녁 7시 반 ~ 10시 * 2학기: 매 주 목요일 오후 5시 이후 = 목표 = * 알피지 만들기(RPG Maker, 이하 알만툴)은 누구든지 손쉽게 바람의나라 같은 형식의 2D 롤플레잉 게임을 만들 수 있도록 도와주는 툴이다. 요즘 한창 날리는 UDK(이쒸....)나, Unity같은 게임엔진과 딸려오는 툴과 같은 개념이다. 현재 VX Ace버전까지 나와있으며 아직도 전세계에서 많은 이들이 알만툴로 게임을 만들고 있다. * PC에서 돌아가는 게임 엔진(런타임)은 있지만 요즘 대세인 모바일에서 돌릴 수 있는 런타임이 존재하지 않는다. 따라서 이번 기회에 알만툴용 모바일 버전 엔진을 만들어 보려고 한다. * 제작 순서는 다음과 같이 진행하려고 한다. RPGMaker VX Version을 Java로 -> Android용으로 -> Windows용으로... (겨울 방학 끝날 때 쯤엔 완성 되겠지...) 실질적으로 2단계서부터 모바일화가 진행되며 1단계가 가장 오래 걸릴 듯 싶다. == 얻고 싶은 것 == * 3D 그래픽스 지식 및 기본 그래픽스 개념 * 리버스 엔지니어링 노하우 * 모바일 프로그래밍 * 게임 로직 및 흐름의 이해 * 설계 패턴 * 다양한 프로그래밍 언어의 습득(Java, Ruby, C# 등등..) = 일정 기록 = == 2012.07.26 == === 한 것 === * jpct 설치 및 RPG 만들기가 어떻게 생겨 먹은 놈인지 확인 * jpct 튜토리얼 보고 따라 해 보기 - Hello World! ==== 프로젝트 설정 ==== 1. 이클립스 프로젝트 생성은 생략. 게다가 이미 [http://nforge.zeropage.org/projects/rma/code?viewfunc=browse svn repository]에 등록해놓았음 1. [http://www.jpct.de/download/net/jpctapi.zip PC용 jPCT 엔진]을 받는다 1. 받은 엔진 안에는 jpct에 해당하는 jpct.jar과 또다른 그래픽 라이브러리인 lwjgl이 있다. lwjgl.jar까지 라이브러리로 등록한다 1. lwjgl.jar은 내부적으로 native 함수를 호출한다. 이 native함수는 dll파일에 담겨져 있는데 이클립스에서 Window -> preferences -> java/installed JREs 에서 vm 설정에 -Djava.library.path=D:\Workspace\rma\libs 형식으로 lwjgl.dll 또는 lwjgl64.dll의 폴더 경로를 입력함으로써 참조하게 한다. 이와 관련된 오류는 java.lang.UnsatisfiedLinkError이다. ==== 또다른 방법(2012.08.30) ==== 3. 이클립스 메뉴에서 window -> preferences -> java -> build_path -> user_libraries로 가면 유저 라이브러리를 등록할 수 있다. jPCT와 LWJGL을 등록하는데 둘이 묶어서 할 수도 있고 서로 다른 라이브러리로 등록해도 된다. lwjgl의 경우 참조하는 dll(안드로이드라면 so) 파일을 등록해야 하는데 이건 native library location에서 경로를 지정해주면 된다. 4. 프로젝트 속성 -> java build path에서 방금 등록한 유저 라이브러리를 추가한다. (개인적으로 이게 더 깔끔한 방법인 것 같다) === 참고 === * [http://ppparkje.tistory.com/entry/Java%EB%A5%BC-%EC%9C%84%ED%95%9C-3D-%EC%97%94%EC%A7%84-jPCT 재민이 블로그] - jpct 설치?에 대한 내용 == 2012.07.31 == === 한 것 === * 3D에서 정점, UV좌표, 삼각형의 표현에 대해 설명하고 실제로 객체를 제작해봄 * 다음 스터디까지 과제로 오각형 만들어오삼 * 파일 분석 정보를 기반으로 parser 제작 시작함. *.rvdata는 들어가는 정보의 종류는 다르지만 일정한 포맷이 있는듯함 예상보다 파일분석은 빨리 끝날지도. == 2012.08.02 == === 한 것 === * 과제 검사(FAIL) - 코딩에 앞서 엄밀한 수학 모델링을 먼저 해보는 것이 필요할듯 * Actors 파일 해석법을 소개하고 다른 파일을 맛보기로 분석해봄 * Actor를 읽어들이는 [http://wiki.zeropage.org/wiki.php/Code/RPGMaker#s-5 Actors.rvdata용 파일 파서] 제작 완료 == 뻘짓(2012.08.09) == * 알만툴을 이해하기 위해 데이터베이스에 해당하는 *.rvdata를 뜯어봐야겠다 라는 생각을 초기에 했었지만 곧 뻘짓임을 알았다 -_-;; .rvdata 파일은 단지 클래스들을 [http://www.ruby-doc.org/core-1.9.3/Marshal.html Marshal 모듈]을 이용하여 덤프한 내용을 기록한 것임을 알게 되었기 때문이다. 게다가 알만툴에서 F1키 누르면 나오는 도움말에 Built-in 클래스나 모듈을 제외한 모든 자료구조(RPG::Actor, RPG::Map 등등..)의 소스가 오픈되어 있었다. * 따라서 파일분석은 곧바로 때려치고 루비스크립트로 각종 .rvdata들을 불러오는 작업을 시작했다. == 2012.08.14 == === 한 것 === * built-in class제작 시작함 워밍업으로 Font, Color 클래스 제작함 == 2012.08.23 == * [박한기] 참가 === 한 것 === * 프로젝트 오리엔테이션 및 기초이론 소개 * Audio 초기화 오류 문제 탐색 * 그래픽이 잘리던 문제 탐색 == 2012.08.28 == === 한 것 === * 오디오는 여전히 말썽(...) * BufferedImage 클래스를 사용하여 글자를 출력하는 방법 학습 == 2012.09.07 == === 한 것 === == 2012.09.13 == === 한 것 === * 글자의 정렬 구현 * 프레임워크에서 비트맵과 스프라이트의 개념, uv좌표에 대한 과외(...) * ogg파일 출력 * Tilemap 작업 Tilemap은 loop이 있는 경우에는 좀더 머리를 써야할듯 싶다 == 2012.09.18 == * 그래픽 엔진을 2D 라이브러리인 [http://slick.cokeandcode.com/ Slick2D]로 교체하고 작업중 branch 주소 [http://nforge.zeropage.org/svn/rma/branches/slick_version] - 메모리 사용 퍼포먼스가 많이 좋아졌다. - 코드가 '많이' 간결해졌다. * 3D에서도 2D를 구현할 수 있음을 jPCT를 통해 충분히 보였고 이 이상의 작업은 3D지식을 필요로 하지 않기 때문에 3D구현은 여기서 끝내도 좋을 것이라 판단되었다. = System = == 게임 루프 == 일반적인 싱글 스레드 게임의 경우 다음과 같은 큰 루프를 가진다. {{{#!vim java void run() { initialize(); while( !isGameEnded() ) { logicUpdate(); renderScene(); } finish(); } }}} 이 중에서 가장 많은 시간을 렌더링에 소모한다. 안드로이드에서 위 두개의 큰 함수를 같은 스레드에서 루프를 돌리는 것은 상당한 속도 저하가 발생할 수 있다. 따라서 logicUpdate() 함수와 renderScene() 함수를 서로 다른 스레드로 분리하는 것이 성능에도 도움이 되며 프로그램 설계상으로도 보기 좋다. 알만툴은 pc버전의 경우 싱글 스레드이다. 따라서 스레드를 분리하는 작업을 해 주어야 한다. - [http://warmz.tistory.com/762 커맨드 패턴]을 사용할 예정. = Graphics = == 직교투영 렌더링 == * jPCT에서는 명시적으로 Orthographic rendering(직교투영 렌더링)을 지원하지 않는다 [http://www.jpct.net/forum2/index.php/topic,1789.msg13175.html#msg13175] 따라서 근사적으로라로 직교투영을 만든다 * 근사 직교투영 좌표계 만들기 : [http://blog.jidolstar.com/826 안드로이드 jPCT-AE로 (근사) 직교투영(Orthogonal Projection) 실현하기] 한줄요약: 카메라를 겁나 멀리 갖다두면 근사적으로 직교투영이 됨ㅋ * [http://wiki.zeropage.org/wiki.php/Code/RPGMaker?action=show#s-1 실제 구현 코드] === 보정(Interpolation) === * 이를 이용하면 직교투영 좌표가 된 것처럼 보이긴 하지만 depth에 해당하는 z값이 바뀌면 조금이라도 오차가 생기기 때문에 이를 보정하여야 한다 특히 렌더링 순서를 z값을 조정함으로 결정시키기 때문에 정확한 렌더링을 위해서는 보정 테크닉을 아는 것이 필수이다. attachment:projection.png * 위 그림에서 볼 수 있듯이 실제 Object의 중점과 사람이 느끼는 Object의 위치는 다르다. 크기 역시 마찬가지이다. 만약 위 그림처럼 Object가 스크린에서 delta만큼 떨어져 있다면 간단한 비례식으로 (length-delta)/length 만큼 크기가 조정되어야 한다 중심의 이동은 Object의 중심을 O, Projection의 중심을 P라고 할 때 벡터 PO만큼을 이동시키면 된다. 벡터 PO는 카메라 위치와 Object 중심을 잇는 직선의 연장선상에 있다는 것을 생각하면 계산이 간편해진다. * [http://wiki.zeropage.org/wiki.php/Code/RPGMaker#s-4 interpolation 구현 코드] == 오브젝트 그리기 == === 삼각형 === * 모든 도형의 기초가 되는 도형이다. 모든 도형은 삼각형만으로 구성할 수 있고 또 쪼갤 수 있다. * jpct에서 텍스쳐 있는 삼각형을 그리기 위해 필요한 정보는 다음과 같다 * 정점(vertex, 꼭지점) : 삼각형은 서로 다른 3개의 한 직선 위에 있지 않은 정점들로 구성된다. * uv좌표 : 텍스쳐가 매핑되는 좌표계를 설정한다. (0, 0)부터 (1, 1)까지의 좌표에 텍스쳐 하나가 들어가게 된다 텍스쳐에 대해서는 나중에 할 말이 있을것. * 정점들을 잇는 순서 : 이 순서에 따라 삼각형의 보이는 면과 보이지 않는 면이 결정된다. 다른 곳은 잘 모르겠으나 openGL에서는 오른나사의 법칙에 따라 엄지가 가리키는 방향의 면이 보이는 면이다. [http://niceyth.springnote.com/pages/3165274 CCW 판별 알고리즘]을 이용해서 순서를 하드코딩하지 않고 자동으로 계산하게 할 수도 있다. * Texture : 도형을 색칠하기 위한 정보가 들어있는 2차원 정사각형이라고 생각해도 좋다. 한 변의 길이는 속도를 위해 2의 제곱수여야 한다. === 직사각형(FillRect) 그리기 === * 왼쪽 위, 오른쪽 아래 좌표만 알면 그릴 수 있다. * 사각형은 2개의 삼각형으로 구성된다. * [http://wiki.zeropage.org/wiki.php/Code/RPGMaker?action=show#s-2 실제 구현] === 직선(Line) 그리기 === * 직선도 실제로는 너비를 가져야 렌더링이 가능하기 때문에 다음과 같은 구조를 가져야 한다 attachment:line.png * vLine = vEnd - vStart * normal(vLine과 수직인 벡터) = vLine × (-z방향 벡터) -> normalize하여 길이가 1인 벡터로 만듦. * (u1, v1)과 (u1, v2)는 vStart ± (normal/2) * (u2, v1)과 (u2, v2)는 vEnd ± (normal/2) * [http://wiki.zeropage.org/wiki.php/Code/RPGMaker?action=show#s-3 실제 구현] == Viewport == 뷰포트는 일종의 창이라고 할 수 있다. attachment:viewport.png 운영체제 윈도우의 개념을 생각하면 편한데, 각 윈도우의 영역을 벗어나는 그래픽 조작은 철저히 차단된다. 이 차단하는 과정을 그래픽스에서는 '''clipping'''이라 한다. jPCT에서는 FrameBuffer에서 setClippingPlane 함수를 통해 클리핑을 지원하므로 이와 적절한 카메라 조작으로 뷰포트를 구현할 수 있었다. 아래는 실제 구현 화면이다. attachment:viewportTest.png 위 사진에서는 가운데 아래쪽 윈도우가 뷰포트이다. 윈도우를 가로지르는 굵은 선을 출력했으나 뷰포트 바깥으로는 나가지 않는걸 확인할 수 있다. == 그래픽 엔진의 구조 == ※ 이 설계는 변할 수 있음 현재 제작중인 그래픽 엔진의 구조는 크게 두 부분으로 나눌 수 있으며 다음과 같다. attachment:structure.png 인터페이스 IDrawable 하위 클래스들은 jPCT의 Object3D 객체를 포함하고 있으며 특히 RMPolygon 및 하위 클래스는 내부에 여러개의 IDrawable을 가질 수 있다. 또한 Viewport는 화면의 일부분만을 보여줄 수 있는 클래스로서 IDrawable을 포함할 수 있으며, Sprite, Window 등의 클래스의 슈퍼클래스이다. 가방 안에 가방을 또 넣는 것처럼 뷰포트 안에 또 뷰포트가 들어갈 수 있으며(트리 구조 가능), 최상위 뷰포트는 화면 전체를 아우르는 크기를 갖는다. = RPG Maker VX 분석 = 알만툴로 만든 게임은 크게 세 부분으로 되어 있다. * 게임의 메인 로직을 담당하는 스크립트(루비로 작성됨) * 캐릭터 정보, 아이템 정보, 스킬 정보 등의 리소스 부분(파일로 저장되어 있음) * 그래픽, 사운드 등 게임의 엔진에 해당하는 부분 .. 이러고보니 철저히 MVC 패턴을 준수한다고 볼 수 있겠다. 알만툴에서는 메인 로직을 수정할 수 있는 스크립트 에디터를 제공함으로 게임 제작에 유연성을 더한다. 툴이 RPG에 최적화된 환경일 뿐이지, 슈팅이나 횡스크롤 게임 등도 얼마든지 만들 수 있다. ~~대신 알만툴의 기능이 쓸모없어질 뿐.~~ == 데이터베이스(리소스) == * 알만툴에서 데이터베이스에 해당하는 .rvdata 파일은 루비 클래스를 덤프한 내용을 기록한 것이다. 이를 불러오고 저장하는 load_data함수와 save_data함수를 도움말에서 제공하고 있다. {{{#!vim ruby def load_data filename File.open(filename, "rb") { |f| obj = Marshal.load(f) return obj } end def save_data filename, obj File.open(filename, "wb") { |f| Marshal.dump(obj, f) } end }}} == 게임의 메인 로직(스크립트) == 알만툴에서 F11을 누르면 나오는 스크립트는 게임이 어떻게 진행되는지에 관련된 내용이 담겨 있다. 하지만 우리가 제작하려는 것은 게임 콘텐츠가 아닌 에뮬레이터이기 때문에 구현 참고용으로만 사용할 계획이다. 에뮬레이터에서 메인로직과 관련해 할 일은 단지 메인 로직을 불러주는 일뿐이다. === Java에서 루비스크립트 실행하기 === 1. [http://jruby.org/download JRuby 홈페이지]에서 JRuby를 설치한다. 64bit OS에서도 32비트 전용으로 설치해야 돌아가는듯 하다. 2. JRuby설치경로/lib 에 위치한 jruby.jar파일을 프로젝트 라이브러리 폴더에 넣는다. 3. 이클립스에서 .jar파일 오른쪽 클릭 메뉴에서 build path->add to build path로 등록한다. 4. 간단하게 코딩한다. 아래 코드는 프로젝트 내에서 src/ruby/main.rb 파일을 호출하는 코드. {{{#!vim ruby public static void main(String[] args) throws IOException, ScriptException { ScriptEngine rbEngine = new ScriptEngineManager().getEngineByName("jruby"); // start rubyscript rbEngine.eval(new FileReader("src/ruby/main.rb")); } }}} == 코어(엔진) == 메인 로직을 보면 각종 그래픽 처리, 입력 처리, 자료구조 등은 따로 제작된 모듈을 사용하는 것을 볼 수 있다. 도움말을 보면 대부분의 게임 내에서 사용하는 이러한 클래스에 대한 명세를 제공하고 있다. 하지만 실제 구현에 대한 소스코드를 제공하지 않기 때문에 '''이런 Built-in 클래스와 모듈은 명세만 보고 직접 구현해야 한다'''. 예제: [http://wiki.zeropage.org/wiki.php/Code/RPGMaker?action=show#s-5 Table 클래스의 구현] === JRuby에서 자바 클래스의 사용 === 그낭 루비가 아닌 자바로 구현된 JRuby의 경우 루비스크립트 내에서 자바 클래스들을 사용할 수 있다. 사용방법은 간단한다. 스크립트 상단에 '''require "java"'''를 명시하고, 각종 클래스들을 import하거나 패키지 이름을 모조리 명시하면 자바클래스를 그대로 사용할 수 있다. * 예제: [http://wiki.zeropage.org/wiki.php/Code/RPGMaker?action=show#s-6 Color 클래스의 구현] === 직접 구현해야 하는 클래스/모듈 목록 === * Built-in class * Bitmap: 이미지 데이터가 저장되는 클래스 * Color: 색상 정보(RGBA)를 가지고 있는 클래스 * Font: 글꼴 정보를 가지고 있는 클래스. .ttf파일을 지원해야 한다. * Plane: 이미지가 표시되는 평면 클래스. 이미지가 타일형으로 반복표시된다. * Rect: top-left 코너와 너비, 높이 정보를 가지는 클래스. * Sprite: Bitmap의 표현이 이루어지는 클래스. * Table: 3차원 배열 클래스. * Tilemap: 맵의 표시와 관련된 클래스(?) * Tone: 이미지의 색상보정을 위한 클래스(?) * Viewport: 2D상의 뷰포트를 구현한 클래스 * Window: 알만툴 내부에서의 윈도우 표시를 담당하는 클래스. * RGSSError: 에러처리 클래스. StandardError클래스를 상속함 * Built-in module * [http://wiki.zeropage.org/wiki.php/RPGMaker/2012%EB%85%84%EC%8A%A4%ED%84%B0%EB%94%94?action=show#s-5.4 Audio]: 오디오 출력을 담당하는 모듈 * [http://wiki.zeropage.org/wiki.php/RPGMaker/2012%EB%85%84%EC%8A%A4%ED%84%B0%EB%94%94?action=show#s-4 Graphics]: 그래픽 출력을 담당하는 모듈 * Input: 유저의 입력(키보드)를 담당하는 모듈 * RPG: 알만툴의 각종 자료구조들을 담고 있는 모듈 == Audio == === 나와라 소리야 === * [http://www.lwjgl.org/wiki/index.php?title=Main_Page Audio를 위한 Java OpenAL 튜토리얼] * .wav파일을 불러 오는 것이 다인데 문제는 사용해야 할 파일들이 midi 아님 ogg 파일 <- 코덱이 있어야 한다나 -ㅅ- * [http://blog.naver.com/PostView.nhn?blogId=harkon&logNo=120094059408&parentCategoryNo=7&viewDate=¤tPage=1&listtype=0 참고 1] * [http://lwjgl.org/forum/index.php?topic=191.0 참고 2] * [http://www.java2s.com/Open-Source/Android/Game/libgdx/com/badlogic/gdx/backends/openal/Mp3.java.htm 참고 3?] * OGG 파일을 사용하기 위해선 [http://www.lwjgl.org/wiki/index.php?title=Slick-Util_Library_-_Part_2_-_Loading_Sounds_for_LWJGL Loading Sound]에서 말하길 Slick-Util zip을 받아 라이브러리에 추가하라고 함... 그럼 된다나.. * [https://bitbucket.org/kevglass/slick/src/tip/trunk/Slick/lib/ 다운로드] * jorbis-*.jar 과 jogg-*.jar library jar files 를 classpath 에 넣어야 하는데 그게 Slick-Util zip 있다고.. * 파일이 재생이 안 됨 * 박재민: [http://lwjgl.org/forum/index.php/topic,3606.0.html 이 링크]가 재생오류시 도움이 될듯.. {{{#!vim ruby public void bgm_start(String filename, int volume, double pitch) { // Initialize OpenAL and clear the error bit. try { AL.create(); } catch (LWJGLException le) { le.printStackTrace(); return; } AL10.alGetError(); // Load the wav data. if (loadALData("test") == AL10.AL_FALSE) { System.out.println("Error loading data."); return; } setListenerValues(); AL10.alSourcePlay(source.get(0)); killALData(); AL.destroy(); } }}} * 맨 마지막 함수 killALData() 함수와 AL.destroy()가 실행하자마자 종료시킴.. 주석처리 하니까 소리는 나옴 -_- === 여러 개 소리가 같이 날 수 있게 만들긔 === * [http://slick.cokeandcode.com/javadoc-util/org/newdawn/slick/openal/Audio.html 오디오 라이브러리]에 playAsSoundEffect()를 여러번 호출하면 여러번 음악 파일이 실행되는 것을 확인 함. {{{#!vim ruby try { // you can play wavs by loading the complete thing into // a sound wavEffect = AudioLoader.getAudio("WAV", ResourceLoader.getResourceAsStream("sound/Test.wav")); } catch (IOException e) { e.printStackTrace(); } while (Keyboard.next()) { if (Keyboard.getEventKeyState()) { if (Keyboard.getEventKey() == Keyboard.KEY_T) { // play as a one off sound effect wavEffect.playAsSoundEffect(1.0f, 1.0f, false); } if (Keyboard.getEventKey() == Keyboard.KEY_G) { wavEffect.stop(); } } } }}} * 위와 같은 방식으로 키보드를 통한 음악 실행과 정지가 가능하다.... * 근데 이렇게 하고 보니 기존에 했던 코드가 아무짝에도 쓸모 없다는 사실을 알았다. -_-; {{{#!vim ruby public void init(InputStream filename) { try { // you can play oggs by loading the complete thing into // a sound oggEffect = AudioLoader.getAudio("OGG", filename); // java.lang.ArithmeticException: / by zero oggEffect.playAsSoundEffect(1.0f, 1.0f, false); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } // polling is required to allow streaming to get a chance to // queue buffers. SoundStore.get().poll(0); } }}} * 왜 저기서 0으로 나눠서 문제가 된다는거지??????? 박재민: 좀더 검색해보니까 '''길이가 짧은 스테레오 ogg파일'''은 문제가 있는듯 보임. 모노로 변환해보니깐 되더라;; * 난 안됨 -_- - [권순의] {{{#!vim ruby public void init(InputStream filename) { URL file = null; try { file = new URL(filename); } catch (MalformedURLException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } try { // you can play oggs by loading the complete thing into // a sound oggStream = AudioLoader.getStreamingAudio("OGG", file); oggStream.playAsMusic(1.0f, 1.0f, true); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } // polling is required to allow streaming to get a chance to // queue buffers. SoundStore.get().poll(0); } }}} * 위와 같이 바꾸었을 경우 getStreamingAudio()에 들어가는 인자가 String, String이 될 수 없고 URL로 바꾸어 주어야 함. 그래서 소리가 날 까 했는데 no protocol: assets/rpgvx_resources/Audio/SE/Cursor.ogg 이라는 아름다운 문자를 날려줌. -ㅅ-... * 이 경우 file = new URL(filename); 를 file = new URL("file://" + filename);로 바꾸어 주면 소리가 날 것 처럼 사람들이 써 놨으나 실패. 그래서 절대 경로를 모두 적어주었지만 역시나 파일을 읽는데 실패했다. {{{#!vim ruby public void init(InputStream filename) { try { // you can play oggs by loading the complete thing into // a sound oggStream = AudioLoader.getStreamingAudio("OGG", ResourceLoader.getResource(filename)); oggStream.playAsMusic(1.0f, 1.0f, true); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } // polling is required to allow streaming to get a chance to // queue buffers. SoundStore.get().poll(0); } }}} * Test로 실행이 되었던 소스였으나, 여기선 java.io.BufferedInputStream.read(Unknown Source)라며 즐을 날려 준다 -_-.... 아니 왜!!! * 맨 위 처음 Test한 방법과 같은 방법으로 시도 해 보았으나 소리가 안 나서 멘붕하고 있음 = Porting to Android = ※ 공사중 == Project setup == pc와 안드로이드는 다른 환경이다. 자바라는 언어가 플랫폼간의 이질성을 초월하는 언어라고는 하지만, 어느 정도는 native한 기능들도 있기 때문에 그에 맞추어 환경을 설정해야 한다. === ADT plugin setup === * 이클립스에서 help - install new software로 가서 업데이트 사이트에 '''http://dl-ssl.google.com/android/eclipse/''' 를 입력하면 가능한 업데이트 목록이 나온다. * 나오는 목록들 중에 Developer tools - '''Android DDMS''', '''Android development tools'''를 선택한 뒤 설치를 진행한다. * 이클립스를 재부팅하면 '''Android SDK'''를 설치할 거냐고 묻는 경우가 있는데, 설치해야 한다. === Slick-AE 빌드 === Slick2D의 안드로이드 버전인 slick-ae는 따로 jar파일이 제공되지 않는다. 따라서 직접 빌드를 해야 한다.(또는 내가 빌드한 jar을 활용해도 된다) * slick은 [http://mercurial.selenic.com/ Mercurial(HG)]로 버전관리를 하기 때문에 미리 Mercurial을 설치한다. * ![hg clone https://bitbucket.org/kevglass/slick] 명령으로 slick의 전체 코드를 다운받는다. * 이클립스에서 slick의 코드들 중 Slick과 Slick-AE만 각각 프로젝트로 import한다. * 빌드하기 전에 '''[http://libgdx.badlogicgames.com/ libgdx]'''를 [http://code.google.com/p/libgdx/downloads/list 최신버전]으로(여기서는 0.9.6) 바꿔야 하는데, 버전이 올라가면서 InputProcessor부분에 인터페이스가 바뀐 부분이 있어 그부분을 새로 고쳐주어야 한다. touchUp, touchDown함수는 있는 것을 사용하고, scrolled와 touchMoved 역시 비슷하게 구현해 주자. * 이클립스의 package explorer에 보면 Slick-AE에 build.xml이 있다. 이것을 실행하면 빌드가 수행되며, target폴더에 slick-ae.jar 파일이 생성된다. === 안드로이드 프로젝트 생성 === * 안드로이드 환경에서 루비스크립트를 실행하기 위해서는 [http://ruboto.org ruboto]라는 프레임워크가 필요하다. 내부적으로 JRuby를 사용하고 있기 때문에 우리 프로젝트와도 잘 맞는 프레임워크라 할 수 있다. [https://github.com/ruboto/ruboto/wiki/Getting-started-with-Ruboto 컴퓨터에 ruboto 세팅하기] ※ ruboto를 설치/빌드하기 위해서는 JRuby(또는 그냥 Ruby), Ant(자바용 make)가 필요하다. * 모든 세팅이 완료된 후 커맨드라인에서 '''ruboto gen app --package <애플리케이션 패키지 이름>''' 으로 앱을 생성하면 알아서 템플릿을 만들어준다. 그 뒤 생성된 폴더로 가서 '''rake install start'''를 하면 바로 컴파일을 하고 폰으로 옮겨 실행까지 해줘서(...) 디버깅이 가능해진다. attachment:ruboto-sample-screenshot.png?width=200 attachment:ruboto-sample-screenshot2.png?width=200 기본 앱을 실행한 화면, ruboto가 동작하기 위해서는 마켓에서 [https://play.google.com/store/apps/details?id=org.ruboto.core ruboto-core]을 설치해야 한다. 왼쪽 그림에서 이미지를 누르면 ruboto-core을 받을 수 있는 마켓으로 이동한다. 오른쪽 그림은 core을 설치한 뒤 제대로 실행되는 앱의 모습이다. == 안드로이드의 OpenGL == 안드로이드에서는 OpenGL의 경량화 버전인 OpenGL-ES 1.x버전을 지원한다. 일부 기기에서는 2.0이 지원된다. (하지만 두 버전은 서로 호환이 되지 않는다;) 안드로이드 Activity상에서 OpenGL을 사용하기 위해서는 [http://developer.android.com/reference/android/opengl/GLSurfaceView.html GLSurfaceView]를 사용해야 한다. 다행히도 ruboto wiki에 이 GLSurfaceView을 사용하는 [https://gist.github.com/723691 예제]가 있다. 앞으로 이를 분석하여 응용할 계획이다. == Rendering Loop == 기존 slick의 경우 다음과 같은 렌더링 루프를 가진다. 주요 함수는 init()과 updateAndRender() 함수인데 불리는 위치가 pc버전과 안드로이드 버전이 상이하다. {{{#!vim java public void start() { init(); while(running()) gameLoop(); finish(); } protected void gameLoop() { updateAndRender(); } }}} 단일 스레드이다. 하지만 안드로이드에서는 여러 스레드가 존재한다. UI 스레드가 있으며, OpenGL을 위한 GL스레드도 있다. {{{#!vim java public void start() { init(); startGLThread(); } ... protected void inThread() { while(running()) onDrawFrame(); } public void onDrawFrame(GL10 gl) { updateAndRender(gl); } }}} 구조가 약간 다르기 때문에 조금 혼돈이 오고있다(...) 결국 렌더링 스레드와 로직 스레드가 분리된 방식으로 돌아가야 하는데 지금 프로그램 구조는 두 작업이 하나의 스레드로 수행되고 있다. 이를 고칠 수 있는 방안을 강구해야 한다. == 최종 선택: 엔진을 버린다 == * OpenGL을 사용하는 GLSurfaceView의 경우 자체적으로 렌더링 루프를 관리하므로 개발자가 손을 댈 수 없다. * 게다가 GLThread가 아닌 다른 스레드에서 OpenGL작업을 할 수 없다. * Canvas로 직접 제어가 가능한 View인 SurfaceView는 다른 스레드에서 렌더링 작업을 할 수 있으며 이를 이용하여 렌더링 루프도 직접 만들 수 있다. * 많은 게임엔진들이 나와 있으나 모조리 OpenGL을 사용한다. ... 이러한 이유로 OpenGL 대신 일반 SurfaceView를 사용하게 되었다. 다 해결되었으나 렌더링 성능의 심각한 저하가 예상된다. = 프로젝트 종료 = * 프로젝트 시작 7개월여만에 잉여력을 발산하여 최종 완성. * 안드로이드 플레이 영상 [[HTML()]] * 느리다. 초당 20프레임 이상을 낼 수 없다. 싱글 스레드의 한계. RTP 엔진의 구조적 문제이므로 맛폰 하드웨어 스펙이 더 좋아지지 않는 이상 원래 소스를 그대로 가져다 쓰면서 성능까지 좋을 수는 없을듯. * 터치가 구린건 애교. -_-a ---- * 와 여기 쩐다. 내용 그대로 튜토리얼로 만들 정도인듯.. - [서지혜] * 오.. 근데 마지막 결론이..ㅋㅋ ---- [2012년활동지도]