리듬게임을 만들어보자 8. 로직과 디자인을 분리하자2

Posted by 적분 ∫2tdt=t²+c
2009.12.18 23:09 프로그래밍/스페샬
저번에 파일로부터 텍스쳐와 스프라이트를 불러들이는 부분을 짜 보았다. 이번에는 로직과 디자인을 확실히 분리해보자. 그러기 위해서 그리기를 수행하는 최소의 단위를 설정해보자. 그 최소의 단위를 '그리기 객체'라고 부르겠다. 프로그래머는 그리기 객체를 여럿 화면에 배치해 놓으면, 그리기 객체는 알아서 자기가 화면에 그림을 그려주는 식이다. 물론 그리기 객체가 어떤 그림을 그릴지는 디자인하는 사람이 잘 정해줄거다.
그리기 객체의 행동을 나타내는 그리기 코드도 정의해보자. 이런 명령어들이 그리기에 주로 필요할 것 같아서 정해보았다.

setdata(byte index, long x, long y)
: index번호의 데이터를 (x, y)로 설정한다.
draw(word index, short x, short y, dword color)
: index번호의 스프라이트를 (x, y)에 color 색으로 그린다.
draws(word index, short x, short y, float sx, floay sy, dword color)
: index번호의 스프라이트를 (x, y)에 (sx, sy)배 늘려서 color색으로 그린다.
drawr(word index, short x, short y, float rot, dword color)
: index번호의 스프라이트를 (x, y)에 rot(라디안)만큼 회전해서 color색으로 그린다.
drawsr(word index, short x, short y, float sx, float sy, float rot, dword color)
: index번호의 스프라이트를 (x, y)에 (sx, sy)배 늘리고 rot(라디안)만큼 회전해서 color색으로 그린다.
setdrawmode(byte mode, byte channel)
: 그리기모드를 mode로, 채널을 channel로 설정한다.
goto(word address)
: address 주소로 점프한다.
randomjump(byte prob, word address)
: prob/255 의 확률로 address로 점프한다.
frameend()
: 그리기를 멈춘다. (프레임)
end()
: 그리기를 끝낸다. (객체)
setcount()
: 카운터를 0으로 설정한다.
inccount()
: 카운터를 1 증가한다.
countjump(byte limit, word address)
: 카운터가 limit보다 작으면 address로 점프한다.
drawvh(word index, short x, short y, short w, short h, dword color)
: index번호의 스프라이트를 (x, y)에 (w, h+vdata) 크기로 늘려서 color색으로 그린다.
drawvy(word index, short x, short y, short sx, short sy, short w, short h, dword color)
: index번호의 스프라이트를 (sx, sy+vdata)지점에서부터 (x, y+vdata)에 (w, h) 크기로 늘려서 color색으로 그린다.
drawcombo(short left, short top, short right, short bottom, word align, float s, dword color)
: s 크기의 combo 텍스트를 (left, top, right, bottom) 사각형에 align으로 정렬하여 color색으로 그린다.
drawline(short x1, short y1, short x2, short y2, dword color)
: (x1, y1)-(x2, y2) 직선을 color색으로 그린다.

좀 지저분하게 많은데, 크게 두 종류로 나누어 볼 수 있다.
그리기 명령어
  Draw, DrawS, DrawR, DrawSR, SetDrawMode, DrawVH, DrawVY, DrawCombo, DrawLine
제어 명령어
 SetData, Goto, RandomJump, FrameEnd, End, SetCount, IncCount, CountJump

명령어들을 다음과 같이 바이너리로 저장하기로 약속하자.
{
[1바이트] 명령어 종류
[x바이트] 명령어에 따른 인자1
[x바이트] 명령어에 따른 인자2
...
[x바이트] 명령어에 따른 인자n
}

자, 이제 이런 명령어들을 따라서 작동하는 그리기 객체를 다루는 클래스 GObject를 만들어보자.


바이트 코드로 저장되어있는 그리기 코드를 m_pcodedata가 가리키고,
m_address에 현재 처리하고 있는 명령어의 주소를 저장하고,
m_count에 카운터값을 저장한다.


GObject를 초기화하고, 특정 지점으로 점프하고, 소멸시키는 함수들이다. 참 쉽죠?


GObject의 핵심이라고 할 수 있는 그리기 코드를 처리하는 부분이다.
제일 첫 바이트를 읽어 어떤 명령어인지 알아내고, 인자들을 차례로 읽어들인후, 적절한 처리를 해준다.
나머지도 다 이런식으로 구현하면 되므로 생략했다.

그리기 코드를 만들어내는 프로그램을 간단하게 짜서 첨부파일의 acc폴더안에 goc.exe라는 이름으로 넣어놓았다. 코드도 같이 있으니 참고해보면 좋다. (코드가 상당히 지저분하므로 별 기대는 하지 말것.)

goc.exe 는 일종의 어셈블리어에서 그리기 코드를 만들어낸다.

Foo:
draw 0 0 0 0xffffffff
frameend
draw 0 0 0 0xeeffffff
draw 1 0 0 0x11ffffff
frameend
draw 0 0 0 0xddffffff
draw 1 0 0 0x22ffffff
frameend
draw 0 0 0 0xccffffff
draw 1 0 0 0x33ffffff
frameend
goto Foo

위는 간단한 코드 예시이다.
acc폴더의 script.txt에 또 다른 코드예시가 들어있다.


m_obj, m_obj2는 그리기 객체이다. Init함수를 호출해서 초기화하고, Jump함수를 호출하여 원하는 코드가 있는 곳으로 간다. 그리고 매 프레임마다 Proc함수를 호출하여 그리기를 수행한다. 그러면 그리기 코드에서 정의한 대로 화면에 여러 그래픽이 출력될 것이다.

아래는 실행결과이다.
그리기 코드를 바꿔가면서 다양한 효과를 줘보자.

이 댓글을 비밀 댓글로