Difference between r1.171 and the current
@@ -4,10 +4,9 @@
* 그래픽스 엔진 등 공학적인 주제에 초점을 맞춤
* 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 영문]
* [권순의], [박재민], [박한기]
* 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로 뭔가 삽질 하다가 멘붕하고 선회한 스터디 -ㅅ-''
* ~~[http://www.jpct.net/ Java로 만든 3D 물리 엔진]으로 2D 엔진 만들기~~
* 툴이 아닌 에뮬레이터 위주
* UDK로 뭔가 삽질 하다가 멘붕하고 선회한 스터디 -ㅅ-
== 참가자 ==* [권순의], [박재민], [박한기]
@@ -127,6 +126,31 @@
- 메모리 사용 퍼포먼스가 많이 좋아졌다.
- 코드가 '많이' 간결해졌다.
* 3D에서도 2D를 구현할 수 있음을 jPCT를 통해 충분히 보였고 이 이상의 작업은 3D지식을 필요로 하지 않기 때문에 3D구현은 여기서 끝내도 좋을 것이라 판단되었다.
= Graphics =
== 직교투영 렌더링 ==
* jPCT에서는 명시적으로 Orthographic rendering(직교투영 렌더링)을 지원하지 않는다
- 코드가 '많이' 간결해졌다.
* 3D에서도 2D를 구현할 수 있음을 jPCT를 통해 충분히 보였고 이 이상의 작업은 3D지식을 필요로 하지 않기 때문에 3D구현은 여기서 끝내도 좋을 것이라 판단되었다.
= System =
== 게임 루프 ==
일반적인 싱글 스레드 게임의 경우 다음과 같은 큰 루프를 가진다.
{{{#!vim java
void run()
{
initialize();
while( !isGameEnded() )
{
logicUpdate();
renderScene();
}
finish();
}
}}}
이 중에서 가장 많은 시간을 렌더링에 소모한다.
안드로이드에서 위 두개의 큰 함수를 같은 스레드에서 루프를 돌리는 것은 상당한 속도 저하가 발생할 수 있다.
따라서 logicUpdate() 함수와 renderScene() 함수를 서로 다른 스레드로 분리하는 것이 성능에도 도움이 되며 프로그램 설계상으로도 보기 좋다.
알만툴은 pc버전의 경우 싱글 스레드이다. 따라서 스레드를 분리하는 작업을 해 주어야 한다.
- [http://warmz.tistory.com/762 커맨드 패턴]을 사용할 예정.
== 직교투영 렌더링 ==
* jPCT에서는 명시적으로 Orthographic rendering(직교투영 렌더링)을 지원하지 않는다
@@ -463,7 +487,7 @@
* 이클립스를 재부팅하면 '''Android SDK'''를 설치할 거냐고 묻는 경우가 있는데, 설치해야 한다.
=== Slick-AE 빌드 ===
Slick2D의 안드로이드 버전인 slick-ae는 따로 jar파일이 제공되지 않는다. 따라서 직접 빌드를 해야 한다.(또는 내가 빌드한 jar을 활용해도 된다)
* slick은 [http://mercurial.selenic.com/ Mercurial(HG)]로 버전관리를 하기 때문에 미리 Mercurial을 설치한다.
=== Slick-AE 빌드 ===
Slick2D의 안드로이드 버전인 slick-ae는 따로 jar파일이 제공되지 않는다. 따라서 직접 빌드를 해야 한다.(또는 내가 빌드한 jar을 활용해도 된다)
* slick은 [http://mercurial.selenic.com/ Mercurial(HG)]로 버전관리를 하기 때문에 미리 Mercurial을 설치한다.
@@ -471,13 +495,113 @@
* 이클립스에서 slick의 코드들 중 Slick과 Slick-AE만 각각 프로젝트로 import한다.
* 이클립스의 package explorer에 보면 Slick-AE에 build.xml이 있다. 이것을 실행하면 빌드가 수행되며, target폴더에 slick-ae.jar 파일이 생성된다.
* 빌드하기 전에 '''[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 파일이 생성된다.
=== 안드로이드 프로젝트 생성 === * package explorer에서 new - project를 선택한 뒤 android application project를 고른다.
* 안드로이드 환경에서 루비스크립트를 실행하기 위해서는 [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(<object width="420" height="315"><param name="movie" value="http://www.youtube.com/v/Hxk6xOmjFUY?version=3&hl=ko_KR"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/Hxk6xOmjFUY?version=3&hl=ko_KR" type="application/x-shockwave-flash" width="420" height="315" allowScriptAccess="always" allowFullScreen="true"></embed></object>)]]
* 느리다. 초당 20프레임 이상을 낼 수 없다. 싱글 스레드의 한계.
RTP 엔진의 구조적 문제이므로 맛폰 하드웨어 스펙이 더 좋아지지 않는 이상 원래 소스를 그대로 가져다 쓰면서 성능까지 좋을 수는 없을듯.
* 터치가 구린건 애교. -_-a
----
* 와 여기 쩐다. 내용 그대로 튜토리얼로 만들 정도인듯.. - [서지혜]
* 오.. 근데 마지막 결론이..ㅋㅋ
----[2012년활동지도]
Contents
1. 스터디 소개 ¶
- 그래픽스 엔진 등 공학적인 주제에 초점을 맞춤
- RPG만들기 에뮬레이터 스마트폰 버전
- 참고 위키: RPG만들기 한글, 영문
Java로 만든 3D 물리 엔진으로 2D 엔진 만들기
- 툴이 아닌 에뮬레이터 위주
- UDK로 뭔가 삽질 하다가 멘붕하고 선회한 스터디 -ㅅ-
2. 목표 ¶
- 알피지 만들기(RPG Maker, 이하 알만툴)은 누구든지 손쉽게 바람의나라 같은 형식의 2D 롤플레잉 게임을 만들 수 있도록 도와주는 툴이다.
요즘 한창 날리는 UDK(이쒸....)나, Unity같은 게임엔진과 딸려오는 툴과 같은 개념이다.
현재 VX Ace버전까지 나와있으며 아직도 전세계에서 많은 이들이 알만툴로 게임을 만들고 있다.
- PC에서 돌아가는 게임 엔진(런타임)은 있지만 요즘 대세인 모바일에서 돌릴 수 있는 런타임이 존재하지 않는다.
따라서 이번 기회에 알만툴용 모바일 버전 엔진을 만들어 보려고 한다.
- 제작 순서는 다음과 같이 진행하려고 한다.
RPGMaker VX Version을 Java로 -> Android용으로 -> Windows용으로... (겨울 방학 끝날 때 쯤엔 완성 되겠지...)
실질적으로 2단계서부터 모바일화가 진행되며 1단계가 가장 오래 걸릴 듯 싶다.
2.1. 얻고 싶은 것 ¶
- 3D 그래픽스 지식 및 기본 그래픽스 개념
- 리버스 엔지니어링 노하우
- 모바일 프로그래밍
- 게임 로직 및 흐름의 이해
- 설계 패턴
- 다양한 프로그래밍 언어의 습득(Java, Ruby, C# 등등..)
3.1.1.1. 프로젝트 설정 ¶
- 이클립스 프로젝트 생성은 생략. 게다가 이미 svn repository에 등록해놓았음
- PC용 jPCT 엔진을 받는다
- 받은 엔진 안에는 jpct에 해당하는 jpct.jar과 또다른 그래픽 라이브러리인 lwjgl이 있다. lwjgl.jar까지 라이브러리로 등록한다
- 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이다.
3.1.1.2. 또다른 방법(2012.08.30) ¶
- 이클립스 메뉴에서 window -> preferences -> java -> build_path -> user_libraries로 가면 유저 라이브러리를 등록할 수 있다.
jPCT와 LWJGL을 등록하는데 둘이 묶어서 할 수도 있고 서로 다른 라이브러리로 등록해도 된다.
lwjgl의 경우 참조하는 dll(안드로이드라면 so) 파일을 등록해야 하는데 이건 native library location에서 경로를 지정해주면 된다.
- 프로젝트 속성 -> java build path에서 방금 등록한 유저 라이브러리를 추가한다.
(개인적으로 이게 더 깔끔한 방법인 것 같다)
3.2.1. 한 것 ¶
- 3D에서 정점, UV좌표, 삼각형의 표현에 대해 설명하고 실제로 객체를 제작해봄
- 다음 스터디까지 과제로 오각형 만들어오삼
- 파일 분석 정보를 기반으로 parser 제작 시작함. *.rvdata는 들어가는 정보의 종류는 다르지만 일정한 포맷이 있는듯함
예상보다 파일분석은 빨리 끝날지도.
3.3.1. 한 것 ¶
- 과제 검사(FAIL) - 코딩에 앞서 엄밀한 수학 모델링을 먼저 해보는 것이 필요할듯
- Actors 파일 해석법을 소개하고 다른 파일을 맛보기로 분석해봄
- Actor를 읽어들이는 Actors.rvdata용 파일 파서 제작 완료
3.4. 뻘짓(2012.08.09) ¶
- 알만툴을 이해하기 위해 데이터베이스에 해당하는 *.rvdata를 뜯어봐야겠다 라는 생각을 초기에 했었지만
곧 뻘짓임을 알았다 -_-;;
.rvdata 파일은 단지 클래스들을 Marshal 모듈을 이용하여 덤프한 내용을 기록한 것임을 알게 되었기 때문이다.
게다가 알만툴에서 F1키 누르면 나오는 도움말에 Built-in 클래스나 모듈을 제외한 모든 자료구조(RPG::Actor, RPG::Map 등등..)의 소스가 오픈되어 있었다.
- 따라서 파일분석은 곧바로 때려치고 루비스크립트로 각종 .rvdata들을 불러오는 작업을 시작했다.
3.9.1. 한 것 ¶
- 글자의 정렬 구현
- 프레임워크에서 비트맵과 스프라이트의 개념, uv좌표에 대한 과외(...)
- ogg파일 출력
- Tilemap 작업
Tilemap은 loop이 있는 경우에는 좀더 머리를 써야할듯 싶다
3.10. 2012.09.18 ¶
- 그래픽 엔진을 2D 라이브러리인 Slick2D로 교체하고 작업중
branch 주소 http://nforge.zeropage.org/svn/rma/branches/slick_version
- 메모리 사용 퍼포먼스가 많이 좋아졌다.
- 코드가 '많이' 간결해졌다.
- 3D에서도 2D를 구현할 수 있음을 jPCT를 통해 충분히 보였고 이 이상의 작업은 3D지식을 필요로 하지 않기 때문에 3D구현은 여기서 끝내도 좋을 것이라 판단되었다.
4.1. 게임 루프 ¶
일반적인 싱글 스레드 게임의 경우 다음과 같은 큰 루프를 가진다.
void run() { initialize(); while( !isGameEnded() ) { logicUpdate(); renderScene(); } finish(); }
이 중에서 가장 많은 시간을 렌더링에 소모한다.
안드로이드에서 위 두개의 큰 함수를 같은 스레드에서 루프를 돌리는 것은 상당한 속도 저하가 발생할 수 있다.
따라서 logicUpdate() 함수와 renderScene() 함수를 서로 다른 스레드로 분리하는 것이 성능에도 도움이 되며 프로그램 설계상으로도 보기 좋다.
따라서 logicUpdate() 함수와 renderScene() 함수를 서로 다른 스레드로 분리하는 것이 성능에도 도움이 되며 프로그램 설계상으로도 보기 좋다.
5.1. 직교투영 렌더링 ¶
- jPCT에서는 명시적으로 Orthographic rendering(직교투영 렌더링)을 지원하지 않는다
http://www.jpct.net/forum2/index.php/topic,1789.msg13175.html#msg13175
따라서 근사적으로라로 직교투영을 만든다
- 근사 직교투영 좌표계 만들기 : 안드로이드 jPCT-AE로 (근사) 직교투영(Orthogonal Projection) 실현하기
한줄요약: 카메라를 겁나 멀리 갖다두면 근사적으로 직교투영이 됨ㅋ
- 실제 구현 코드
5.1.1. 보정(Interpolation) ¶
- 이를 이용하면 직교투영 좌표가 된 것처럼 보이긴 하지만 depth에 해당하는 z값이 바뀌면 조금이라도 오차가 생기기 때문에 이를 보정하여야 한다
특히 렌더링 순서를 z값을 조정함으로 결정시키기 때문에 정확한 렌더링을 위해서는 보정 테크닉을 아는 것이 필수이다.
[PNG image (14.84 KB)]
- 위 그림에서 볼 수 있듯이 실제 Object의 중점과 사람이 느끼는 Object의 위치는 다르다. 크기 역시 마찬가지이다.
만약 위 그림처럼 Object가 스크린에서 delta만큼 떨어져 있다면 간단한 비례식으로 (length-delta)/length 만큼 크기가 조정되어야 한다
중심의 이동은 Object의 중심을 O, Projection의 중심을 P라고 할 때 벡터 PO만큼을 이동시키면 된다. 벡터 PO는 카메라 위치와 Object 중심을 잇는 직선의 연장선상에 있다는 것을 생각하면 계산이 간편해진다.
- interpolation 구현 코드
5.2.1. 삼각형 ¶
- 모든 도형의 기초가 되는 도형이다. 모든 도형은 삼각형만으로 구성할 수 있고 또 쪼갤 수 있다.
- jpct에서 텍스쳐 있는 삼각형을 그리기 위해 필요한 정보는 다음과 같다
- 정점(vertex, 꼭지점) : 삼각형은 서로 다른 3개의 한 직선 위에 있지 않은 정점들로 구성된다.
- uv좌표 : 텍스쳐가 매핑되는 좌표계를 설정한다. (0, 0)부터 (1, 1)까지의 좌표에 텍스쳐 하나가 들어가게 된다
텍스쳐에 대해서는 나중에 할 말이 있을것.
- 정점들을 잇는 순서 : 이 순서에 따라 삼각형의 보이는 면과 보이지 않는 면이 결정된다.
다른 곳은 잘 모르겠으나 openGL에서는 오른나사의 법칙에 따라 엄지가 가리키는 방향의 면이 보이는 면이다.
CCW 판별 알고리즘을 이용해서 순서를 하드코딩하지 않고 자동으로 계산하게 할 수도 있다.
- Texture : 도형을 색칠하기 위한 정보가 들어있는 2차원 정사각형이라고 생각해도 좋다. 한 변의 길이는 속도를 위해 2의 제곱수여야 한다.
- 정점(vertex, 꼭지점) : 삼각형은 서로 다른 3개의 한 직선 위에 있지 않은 정점들로 구성된다.
5.2.3. 직선(Line) 그리기 ¶
- 직선도 실제로는 너비를 가져야 렌더링이 가능하기 때문에 다음과 같은 구조를 가져야 한다
[PNG image (7.47 KB)]
- vLine = vEnd - vStart
- normal(vLine과 수직인 벡터) = vLine × (-z방향 벡터) -> normalize하여 길이가 1인 벡터로 만듦.
- (u1, v1)과 (u1, v2)는 vStart ± (normal/2)
- (u2, v1)과 (u2, v2)는 vEnd ± (normal/2)
- 실제 구현
5.3. Viewport ¶
뷰포트는 일종의 창이라고 할 수 있다.
[PNG image (8.53 KB)]
운영체제 윈도우의 개념을 생각하면 편한데, 각 윈도우의 영역을 벗어나는 그래픽 조작은 철저히 차단된다. 이 차단하는 과정을 그래픽스에서는 clipping이라 한다.
jPCT에서는 FrameBuffer에서 setClippingPlane 함수를 통해 클리핑을 지원하므로 이와 적절한 카메라 조작으로 뷰포트를 구현할 수 있었다.
아래는 실제 구현 화면이다.
[PNG image (681.93 KB)]
위 사진에서는 가운데 아래쪽 윈도우가 뷰포트이다. 윈도우를 가로지르는 굵은 선을 출력했으나 뷰포트 바깥으로는 나가지 않는걸 확인할 수 있다.
5.4. 그래픽 엔진의 구조 ¶
※ 이 설계는 변할 수 있음
현재 제작중인 그래픽 엔진의 구조는 크게 두 부분으로 나눌 수 있으며 다음과 같다.
[PNG image (7.18 KB)]
인터페이스 IDrawable 하위 클래스들은 jPCT의 Object3D 객체를 포함하고 있으며 특히 RMPolygon 및 하위 클래스는 내부에 여러개의 IDrawable을 가질 수 있다.
또한 Viewport는 화면의 일부분만을 보여줄 수 있는 클래스로서 IDrawable을 포함할 수 있으며, Sprite, Window 등의 클래스의 슈퍼클래스이다.
가방 안에 가방을 또 넣는 것처럼 뷰포트 안에 또 뷰포트가 들어갈 수 있으며(트리 구조 가능), 최상위 뷰포트는 화면 전체를 아우르는 크기를 갖는다.
6. RPG Maker VX 분석 ¶
알만툴로 만든 게임은 크게 세 부분으로 되어 있다.
- 게임의 메인 로직을 담당하는 스크립트(루비로 작성됨)
- 캐릭터 정보, 아이템 정보, 스킬 정보 등의 리소스 부분(파일로 저장되어 있음)
- 그래픽, 사운드 등 게임의 엔진에 해당하는 부분
알만툴에서는 메인 로직을 수정할 수 있는 스크립트 에디터를 제공함으로 게임 제작에 유연성을 더한다.
툴이 RPG에 최적화된 환경일 뿐이지, 슈팅이나 횡스크롤 게임 등도 얼마든지 만들 수 있다. 대신 알만툴의 기능이 쓸모없어질 뿐.
6.1. 데이터베이스(리소스) ¶
- 알만툴에서 데이터베이스에 해당하는 .rvdata 파일은 루비 클래스를 덤프한 내용을 기록한 것이다.
이를 불러오고 저장하는 load_data함수와 save_data함수를 도움말에서 제공하고 있다.
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
6.2. 게임의 메인 로직(스크립트) ¶
알만툴에서 F11을 누르면 나오는 스크립트는 게임이 어떻게 진행되는지에 관련된 내용이 담겨 있다.
하지만 우리가 제작하려는 것은 게임 콘텐츠가 아닌 에뮬레이터이기 때문에 구현 참고용으로만 사용할 계획이다.
에뮬레이터에서 메인로직과 관련해 할 일은 단지 메인 로직을 불러주는 일뿐이다.
6.2.1. Java에서 루비스크립트 실행하기 ¶
- JRuby 홈페이지에서 JRuby를 설치한다. 64bit OS에서도 32비트 전용으로 설치해야 돌아가는듯 하다.
- JRuby설치경로/lib 에 위치한 jruby.jar파일을 프로젝트 라이브러리 폴더에 넣는다.
- 이클립스에서 .jar파일 오른쪽 클릭 메뉴에서 build path->add to build path로 등록한다.
- 간단하게 코딩한다.
아래 코드는 프로젝트 내에서 src/ruby/main.rb 파일을 호출하는 코드.
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")); }
6.3. 코어(엔진) ¶
메인 로직을 보면 각종 그래픽 처리, 입력 처리, 자료구조 등은 따로 제작된 모듈을 사용하는 것을 볼 수 있다.
도움말을 보면 대부분의 게임 내에서 사용하는 이러한 클래스에 대한 명세를 제공하고 있다.
하지만 실제 구현에 대한 소스코드를 제공하지 않기 때문에 이런 Built-in 클래스와 모듈은 명세만 보고 직접 구현해야 한다.
예제: Table 클래스의 구현
6.3.1. JRuby에서 자바 클래스의 사용 ¶
그낭 루비가 아닌 자바로 구현된 JRuby의 경우 루비스크립트 내에서 자바 클래스들을 사용할 수 있다.
사용방법은 간단한다. 스크립트 상단에 require "java"를 명시하고, 각종 클래스들을 import하거나 패키지 이름을 모조리 명시하면 자바클래스를 그대로 사용할 수 있다.
사용방법은 간단한다. 스크립트 상단에 require "java"를 명시하고, 각종 클래스들을 import하거나 패키지 이름을 모조리 명시하면 자바클래스를 그대로 사용할 수 있다.
- 예제: Color 클래스의 구현
6.3.2. 직접 구현해야 하는 클래스/모듈 목록 ¶
* Built-in class
- Bitmap: 이미지 데이터가 저장되는 클래스
- Color: 색상 정보(RGBA)를 가지고 있는 클래스
- Font: 글꼴 정보를 가지고 있는 클래스. .ttf파일을 지원해야 한다.
- Plane: 이미지가 표시되는 평면 클래스. 이미지가 타일형으로 반복표시된다.
- Rect: top-left 코너와 너비, 높이 정보를 가지는 클래스.
- Sprite: Bitmap의 표현이 이루어지는 클래스.
- Table: 3차원 배열 클래스.
- Tilemap: 맵의 표시와 관련된 클래스(?)
- Tone: 이미지의 색상보정을 위한 클래스(?)
- Viewport: 2D상의 뷰포트를 구현한 클래스
- Window: 알만툴 내부에서의 윈도우 표시를 담당하는 클래스.
- RGSSError: 에러처리 클래스. StandardError클래스를 상속함
6.4.1. 나와라 소리야 ¶
- Audio를 위한 Java OpenAL 튜토리얼
- .wav파일을 불러 오는 것이 다인데 문제는 사용해야 할 파일들이 midi 아님 ogg 파일 <- 코덱이 있어야 한다나 -ㅅ-
- 참고 1
- 참고 2
- 참고 3?
- OGG 파일을 사용하기 위해선 Loading Sound에서 말하길 Slick-Util zip을 받아 라이브러리에 추가하라고 함... 그럼 된다나..
- 다운로드
- jorbis-*.jar 과 jogg-*.jar library jar files 를 classpath 에 넣어야 하는데 그게 Slick-Util zip 있다고..
- 다운로드
- 참고 1
- 파일이 재생이 안 됨
- 박재민: 이 링크가 재생오류시 도움이 될듯..
- 박재민: 이 링크가 재생오류시 도움이 될듯..
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()가 실행하자마자 종료시킴.. 주석처리 하니까 소리는 나옴
6.4.2. 여러 개 소리가 같이 날 수 있게 만들긔 ¶
- 오디오 라이브러리에 playAsSoundEffect()를 여러번 호출하면 여러번 음악 파일이 실행되는 것을 확인 함.
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(); } } }
- 위와 같은 방식으로 키보드를 통한 음악 실행과 정지가 가능하다....
- 근데 이렇게 하고 보니 기존에 했던 코드가 아무짝에도 쓸모 없다는 사실을 알았다. -_-;
- 근데 이렇게 하고 보니 기존에 했던 코드가 아무짝에도 쓸모 없다는 사실을 알았다. -_-;
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파일은 문제가 있는듯 보임. 모노로 변환해보니깐 되더라;;
- 난 안됨 - 권순의
- 난 안됨 - 권순의
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);로 바꾸어 주면 소리가 날 것 처럼 사람들이 써 놨으나 실패. 그래서 절대 경로를 모두 적어주었지만 역시나 파일을 읽는데 실패했다.
- 이 경우 file = new URL(filename); 를 file = new URL("file://" + filename);로 바꾸어 주면 소리가 날 것 처럼 사람들이 써 놨으나 실패. 그래서 절대 경로를 모두 적어주었지만 역시나 파일을 읽는데 실패했다.
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한 방법과 같은 방법으로 시도 해 보았으나 소리가 안 나서 멘붕하고 있음
- 맨 위 처음 Test한 방법과 같은 방법으로 시도 해 보았으나 소리가 안 나서 멘붕하고 있음
7.1. Project setup ¶
pc와 안드로이드는 다른 환경이다. 자바라는 언어가 플랫폼간의 이질성을 초월하는 언어라고는 하지만, 어느 정도는 native한 기능들도 있기 때문에 그에 맞추어 환경을 설정해야 한다.
7.1.1. ADT plugin setup ¶
- 이클립스에서 help - install new software로 가서 업데이트 사이트에 http://dl-ssl.google.com/android/eclipse/ 를 입력하면 가능한 업데이트 목록이 나온다.
- 나오는 목록들 중에 Developer tools - Android DDMS, Android development tools를 선택한 뒤 설치를 진행한다.
- 이클립스를 재부팅하면 Android SDK를 설치할 거냐고 묻는 경우가 있는데, 설치해야 한다.
7.1.2. Slick-AE 빌드 ¶
Slick2D의 안드로이드 버전인 slick-ae는 따로 jar파일이 제공되지 않는다. 따라서 직접 빌드를 해야 한다.(또는 내가 빌드한 jar을 활용해도 된다)
- slick은 Mercurial(HG)로 버전관리를 하기 때문에 미리 Mercurial을 설치한다.
- [hg clone https://bitbucket.org/kevglass/slick] 명령으로 slick의 전체 코드를 다운받는다.
- 이클립스에서 slick의 코드들 중 Slick과 Slick-AE만 각각 프로젝트로 import한다.
- 빌드하기 전에 libgdx를 최신버전으로(여기서는 0.9.6) 바꿔야 하는데, 버전이 올라가면서 InputProcessor부분에 인터페이스가 바뀐 부분이 있어 그부분을 새로 고쳐주어야 한다.
touchUp, touchDown함수는 있는 것을 사용하고, scrolled와 touchMoved 역시 비슷하게 구현해 주자.
- 이클립스의 package explorer에 보면 Slick-AE에 build.xml이 있다. 이것을 실행하면 빌드가 수행되며, target폴더에 slick-ae.jar 파일이 생성된다.
7.1.3. 안드로이드 프로젝트 생성 ¶
- 안드로이드 환경에서 루비스크립트를 실행하기 위해서는 ruboto라는 프레임워크가 필요하다.
내부적으로 JRuby를 사용하고 있기 때문에 우리 프로젝트와도 잘 맞는 프레임워크라 할 수 있다.
컴퓨터에 ruboto 세팅하기
※ ruboto를 설치/빌드하기 위해서는 JRuby(또는 그냥 Ruby), Ant(자바용 make)가 필요하다.
- 모든 세팅이 완료된 후 커맨드라인에서 ruboto gen app --package <애플리케이션 패키지 이름> 으로 앱을 생성하면 알아서 템플릿을 만들어준다.
그 뒤 생성된 폴더로 가서 rake install start를 하면 바로 컴파일을 하고 폰으로 옮겨 실행까지 해줘서(...) 디버깅이 가능해진다.
[PNG image (198.25 KB)][PNG image (56.95 KB)]
기본 앱을 실행한 화면, ruboto가 동작하기 위해서는 마켓에서 ruboto-core을 설치해야 한다.
왼쪽 그림에서 이미지를 누르면 ruboto-core을 받을 수 있는 마켓으로 이동한다.
오른쪽 그림은 core을 설치한 뒤 제대로 실행되는 앱의 모습이다.
7.3. Rendering Loop ¶
기존 slick의 경우 다음과 같은 렌더링 루프를 가진다.
주요 함수는 init()과 updateAndRender() 함수인데 불리는 위치가 pc버전과 안드로이드 버전이 상이하다.
public void start() { init(); while(running()) gameLoop(); finish(); } protected void gameLoop() { updateAndRender(); }
단일 스레드이다.
하지만 안드로이드에서는 여러 스레드가 존재한다.
UI 스레드가 있으며, OpenGL을 위한 GL스레드도 있다.
public void start() { init(); startGLThread(); } ... protected void inThread() { while(running()) onDrawFrame(); } public void onDrawFrame(GL10 gl) { updateAndRender(gl); }
구조가 약간 다르기 때문에 조금 혼돈이 오고있다(...)
결국 렌더링 스레드와 로직 스레드가 분리된 방식으로 돌아가야 하는데 지금 프로그램 구조는 두 작업이 하나의 스레드로 수행되고 있다.
이를 고칠 수 있는 방안을 강구해야 한다.
7.4. 최종 선택: 엔진을 버린다 ¶
- OpenGL을 사용하는 GLSurfaceView의 경우 자체적으로 렌더링 루프를 관리하므로 개발자가 손을 댈 수 없다.
- 게다가 GLThread가 아닌 다른 스레드에서 OpenGL작업을 할 수 없다.
- Canvas로 직접 제어가 가능한 View인 SurfaceView는 다른 스레드에서 렌더링 작업을 할 수 있으며 이를 이용하여 렌더링 루프도 직접 만들 수 있다.
- 많은 게임엔진들이 나와 있으나 모조리 OpenGL을 사용한다.
다 해결되었으나 렌더링 성능의 심각한 저하가 예상된다.