[App 개발] Xcode 2.2 에서 카본 프로그램 만들자 (7) 응용문제
본문
- HIView
HIView 는 카본 프로그래밍에서 Button, Combo box 등의 컨트롤을 만들 수 있도록 해 주는 객체입니다. 카본으로 커스텀 컨트롤을 만들려면 반드시 사용해야 하겠지요. 또한 간편하게 코어 그래픽스 함수들을 사용할 수 있는 패널이기도 합니다.
HiView 는 컨트롤 객체이기 때문에 버튼 클릭과 같은 개념의 마우스 입력을 받을 수 있습니다.
void InstallPuzzle( WindowRef window )
{
HIViewRef myHIView ;
HIViewID myHIViewID = { 'mVue', 130 } ;
EventTypeSpec myHIViewSpec[] = {
{ kEventClassControl, kEventControlDraw },
{ kEventClassControl, kEventControlHitTest },
} ;
OSStatus err ;
HIViewFindByID( HIViewGetRoot( window ), myHIViewID, &myHIView ) ;
err = InstallEventHandler( GetControlEventTarget( myHIView ),
NewEventHandlerUPP( PuzzleEventHandler ),
GetEventTypeCount( myHIViewSpec ),
myHIViewSpec,
(void *)myHIView,
NULL ) ;
}
여기서 보시는대로 kEventClassControl => kEventControlHitTest 가 바로 마우스 클릭이 왔을 때의 이벤트입니다.
OSStatus DoTileMove( EventRef event, WindowRef window )
{
OSStatus status = noErr ;
HIRect bounds ;
int px, py, temp ;
OSStatus err ;
Point where ;
err = GetEventParameter( event,
kEventParamMouseLocation,
typeQDPoint,
NULL,
sizeof( where ),
NULL,
&where ) ;
HIViewGetBounds( (HIViewRef) window, &bounds ) ;
require_noerr( status, CantGetBoundingRectangle ) ;
px = where.h / 160 ;
py = ( bounds.size.height - where.v ) / 160 ;
if( ( px == bx && ( py == by + 1 || py == by - 1 ) ) ||
( py == by && ( px == bx + 1 || px == bx - 1 ) ) ) {
temp = puzzledata[ bx ][ by ] ;
puzzledata[ bx ][ by ] = puzzledata[ px ][ py ] ;
puzzledata[ px ][ py ] = temp ;
err = HIViewSetNeedsDisplay( (HIViewRef)window,
true ) ;
bx = px ;
by = py ;
}
CantGetGraphicsContext:
CantGetBoundingRectangle:
return err ;
}
이벤트가 들어왔을 때 이벤트 핸들러에서 DoTileMove() 함수를 호출하도록 하였습니다. 마우스 입력 좌표는 일반적으로 쓰이는 GetEventParameter() 함수에 kEventParamMouseLocation 를 지정하여 얻어낼 수 있습니다. 퀵드로우 형식의 래스터 그래픽스 환경에 익숙하신 분들에게는 Quartz 의 좌표 문제가 항상 여러모로 신경쓰이는 부분일 것입니다만, 이것도 익숙해지면 되죠.
마우스 입력 이벤트가 들어오면 이 함수에서는 마우스가 퍼즐 빈 칸의 옆을 선택했는지를 검사하여 그 타일을 빈 칸과 바꾸어 넣습니다. 화면을 다시 그리는 것은 HIViewSetNeedsDisplay() 함수를 호출하여 다시 전체 화면을 그리도록 하였는데, 좀 더 세련된 프로그램을 만들려면 HIViewSetNeedsDisplayInRect() 같은 함수를 써서 원하는 부분만 갱신하도록 하는 것이 더 좋을 것입니다.
- 게임
모든 게임이 다 마찬가지이겠습니다만, 퍼즐 게임은 가장 모범적인 게임 데이터 관리 예제라고 할 수 있겠습니다. 이 예제에서는 4 x 3 타일을 사용하고 있으므로, 모두 12개의 상자를 마련해 둔 다음 각각의 상자에 현재 퍼즐의 번호를 넣어두고 그대로 화면에 출력합니다.
void InitGame( void )
{
int i, j ;
int rnd_flag[ 12 ] ;
int rnd_index ;
LoadPicture() ;
srandom( (long)time(0) ) ;
// initialize puzzle matrix
for( i = 0 ; i < 12 ; i++ ) {
rnd_flag[ i ] = 0 ;
}
for( j = 0 ; j < 3 ; j++ ) {
for( i = 0 ; i < 4 ; i++ ) {
do {
rnd_index = random() % 12 ;
} while( rnd_flag[ rnd_index ] != 0 ) ;
rnd_flag[ rnd_index ] = 1 ;
if( rnd_index != 11 ) {
puzzledata[ i ][ j ] = rnd_index ;
} else {
// where the blank puzzle is
puzzledata[ i ][ j ] = -1 ;
bx = i ;
by = j ;
}
}
}
}
처음에 퍼즐을 섞는 것은 난수를 이용합니다. 난수 함수는 srand() 와 rand() 가 주로 사용되고 있습니다만, 최신 GCC 에서는 srandom() 과 random() 으로 대치되었다고 매뉴얼에 기록되어 있군요. 맨 마지막 타일 (11번) 을 -1 로 설정하여 이것을 빈 칸으로 사용합니다.
OSStatus DoDraw( EventRef event, WindowRef window )
{
CGContextRef myContext ;
OSStatus status = noErr ;
HIRect bounds ;
int i, j ;
CGRect rect ;
status = GetEventParameter( event,
kEventParamCGContextRef,
typeCGContextRef,
NULL,
sizeof( CGContextRef ),
NULL,
&myContext ) ;
require_noerr( status, CantGetGraphicsContext ) ;
HIViewGetBounds( (HIViewRef) window, &bounds ) ;
require_noerr( status, CantGetBoundingRectangle ) ;
/////
CGContextTranslateCTM( myContext, 0, bounds.size.height ) ;
CGContextScaleCTM( myContext, 1.0, -1.0 ) ;
for( j = 0 ; j < 3 ; j++ ) {
for( i = 0 ; i < 4 ; i++ ) {
if( puzzledata[ i ][ j ] != -1 ) {
rect = CGRectMake( i * 160, j * 160, 160, 160 ) ;
CGContextDrawImage( myContext,
rect,
puzzle_piece[ puzzledata[ i ][ j ] ] ) ;
} else {
rect = CGRectMake( i * 160, j * 160, 160, 160 ) ;
CGContextSetRGBFillColor( myContext, 0, 0, 0, 1 ) ;
CGContextFillRect( myContext, rect ) ;
}
}
}
CantGetGraphicsContext:
CantGetBoundingRectangle:
return status ;
}
지난 번 예제에서도 사용된 방법으로, 큰 그림 하나를 읽은 다음 이것을 12개의 작은 CGImage 타일로 나눈 다음 그것을 화면에 puzzledata 매트릭스에 기록된 내용대로 출력을 하게 됩니다.
전체적인 게임의 흐름은 puzzledata 매트릭스를 검사하면 알아낼 수 있습니다. 이 소스에는 빠져 있지만, 마우스 클릭이 이루어질 때마다 puzzledata 가 번호 순서대로 나열되어 있는지를 검사하면 플레이어가 퍼즐을 다 맞추었는지를 알아낼 수가 있겠지요.
키브도 입력 이벤트 루틴은 준비해 놓긴 했습니다만 아직 연결하지 않았습니다. 여러분 각자 이 소스를 응용하여 키보드 입력 핸들러를 구현해 보면 좋은 연습문제가 될 것 같습니다. ^^
최신글이 없습니다.
최신글이 없습니다.
댓글목록 1
악동시니님의 댓글
앗싸..ㅋㅋㅋ 집에가서 해봐야지.. 흐흐흐.. 아주 좋습니다..ㅋㅋㅋㅋㅋ
그나저나 홍준님 아니메 조아라 하시나보다..ㅋㅋㅋㅋ