'Direct2D'에 해당되는 글 2건

  1. 2012.01.21 [D2D 테스리스] 작업 일지.
  2. 2012.01.11 [Direct2D] dpi, dip, 그리고 백버퍼

2012. 2. 19.

image

  • 2인 플레이 방식을 버리고 1인용으로만 처리. 멀리 플레이는 추후 랭킹 시스템으로 대신할 예정이고, 혹시나 대전을 한다 해도 TCP 통신을 할 예정.
  • DXUT에서 제공하던 타이머 코드를 추가. QueryPerformanceCounter를 사용하는 정교한 시간처리 가능.
  • 블럭 버퍼링 기능(?) 추가. 한게임이나 원조 테트리스 보면 제공하는 기능. R-shift키를 누르면 현재 떨어지던 블럭이 버퍼에 저장된다. 필요할 때 꺼내 쓸 수 있다.
  • 다음 블럭, 버퍼 블럭의 표시위치 및 방식 변경. 지정된 영역(Rect)의 중앙에 위치하도록 중점 계산하고 좌표 장난치는 코드 추가.

2012. 1. 21.

  • 레벨(스테이지) 개념 추가. 레벨이 오를 수록 블럭 떨어지는 속도가 빨라짐. 레벨이 오를 수록 게임필드 배경색상이 붉게 변한다. 게임필드 배경에 현재 스테이지 번호를 렌더링하고, 필드 오른쪽에는 다음 레벨까지 얼마나 남아 있는지 표시해주는 세로방향 미터기를 달았다.
  • 블럭에 음영처리 : 포토샵에서 레이어 필터효과로 간단히 처리. 밋밋할 때보단 보기 좋아 보임.
  • 사라지는 줄의 애니메이션 처리 : 당장은 알파가 서서히 사라지는 연출만 넣어두었다. 파티클 시스템이나 혹은 다른 이펙트 시스템을 만들고 나면 추가로 연출할 생각. 지금 연출만 하더라도 아무것도 없을 때 보다는 훨씬 보기 좋음.

2인용을 포기하고 1인용으로 바꿔야겠다. 화면을 넓게 쓴 다음에 점수 시스템, 콤보 시스템, 통계(?) 시스템, 랭킹 시스템 같은 걸 넣어서 빈 화면을 채우는게 낫겠어.


2012. 1. 20.

  • 코드 리팩토링 : State Design Pattern 도입. 로직을 본격적으로 확장하기 전에 미리 정리해두는 게 좋을 것 같아 대폭 수정.
  • 배경 이미지 변경 : 이건 그냥 jpg 리소스 바꾸는 거니까.. 한게임 테트리스 포스터 무단도용(…) 나중에는 일정 시간이 지나면 부드러운 애니메이션으로 화면전환하는 기능을 해봐야겠다.
  • 고스트 블럭 출력 : 떨어지는 블록의 예상 위치를 반투명하게 미리 표시. 로직이 정리되어서 쉽게 가능했다.

다음엔 다 채운 줄이 지워질 때 이펙트를 넣어야겠다. 사실 그걸 하려고 FSM을 넣었음.

드디어 좀 더 화려한 작업을 할 수 있겠군 :)

Posted by leafbird 트랙백 0 : 댓글 0

댓글을 달아 주세요

dpi에 대해.

dpi는 Dots per inch의 약자로, 인쇄물이나 영상물 등에서 1인치당 들어있는 데이터량을 말한다. 이게 포토샵을 주로 만지시는 아트 분들에게는 익숙한 개념인데, 나는 머리에서 이게 전환이 아직 잘 안됨.. 하지만 최근에는 디스플레이의 종류도 다양해지고, 여러 장치에서 호환되는 프로그램을 만들려면 좀 더 익숙해 지는 게 좋다. (안드로이드 개발 같은 경우 장치마다 해상도가 다 다르기 때문에 이런 개념이 더 중요해진다.) 요즘은 e-book에서 보는 pdf 자료의 해상도를 이야기할 때에도 자주 사용하니깐. 그리고 윈도우7 부터는 제어판 > 디스플레이 목록에서 사용자가 시스템의 dpi를 직접 설정할 수도 있다. 그리고 visual studio 2010의 프로젝트 부터(라고 알고 있는데 2005나 2008도 되는진 잘 모름)는 이렇게 사용자가 직접 지정한 dpi에도 화면이나 폰트가 뭉개지지 않도록 dpi 수치를 자동 인식하는 manifest 설정 플래그를 가지고 있다.

 

DIP에 대해.

지금 Direct2D를 이용해 만들고 있는 게임에서 윈도우 창을 리사이즈 했을 때의 처리를 하고 있는데, 일단은 사용자가 창을 늘이거나 줄이거나 상관없이 게임 화면은 그에 맞게 알아서 스케일 되도록 처리하려고 한다. 하지만 내가 프로그램을 짤 때에는 이런 리사이즈 문제에 신경 쓰지 않고 작성할 수 있게, 스크립트에서 사용하는 좌표 수치는 윈도우의 리사이즈와 상관 없도록. 그렇게 해야 쓸데 없는데 고민 안하고 편하게 게임을 만들 수 있으니깐.

다시 말해서 윈도우의 화면 좌표와 내가 게임에서 사용하는 좌표가 분리 되어야 하는데, 작업하면서 찾아보니 이것을 DIP(Device Indipendent Pixel)라고 부른다. Direct3D에서는 어차피 world – view transform을 거칠 때 까지는 장치에 독립적인 별도의 3D 공간좌표를 이용해 프로그래밍 하고, 마지막 Projection 단계에서 실제 화면 영역과 매핑하기 때문에 이미 장치에 독립적인 좌표를 쓰고 있다고 볼 수 있다. D3D에서도 UI 시스템에서 2차원 좌표계를 처리할 땐 윈도우 시스템 좌표계에 휘둘리지 않게 신경 써 주어야 하겠지만 그다지 어려운 일은 아니다.

DIP 개념을 Direct2D에서 구현하려면 어떻게 해야 할까. 우선 맨 처음에는 d3d처럼 흉내를 내서 월드 변환 –> 프로젝션 변환 순으로 transform 단계를 나눠볼까 하고 생각을 했는데, 그러기에는 d2d가 허락한 1단계의 transform api만을 이용해야 하기에 깔끔하게 처리하기 곤란하다. 모든 행렬을 넣을 때마다 proj 행렬을 뒤에 곱해서 넣으면 되겠지만 그러기엔 나중에 파티클 시스템이라도 구현하게 되는 단계가 오면 행렬 곱셈을 미친듯이 하게 될 거잖아. 아무리 취미로 만드는 코드라도 그렇게 무식하게 만들 순 없다.

 

백버퍼 생성 – 더블 버퍼링의 아련한 추억

그래서 뜻하지 않게(?) 더블 버퍼링처럼 구현을 변경했다. 처음부터 화면에 바로 그리지 말고, 백버퍼에 우선 그렸다가 화면으로 옮겨 그리는 단계를 집어넣는다. GDI에서 메모리 DC를 얻어서 이곳에 먼저 그렸다가 한 번에 옮겨 그리는 기법이랑 로직은 똑같다. 하지만 GDI때처럼 flickering(화면 깜박임 현상)을 없애기 위해 넣는 것이 아니니 헷갈리지 말 것. 설명을 위해 비유를 한 것 뿐이다.

렌더 타겟을 ID2D1HwndRenderTarget으로 바로 사용하면 윈도우 리사이즈의 고난을 피해갈 수 없다. 호환되는 ID2D1BitmapRenderTarget을 만들어 실질적인 드로잉 처리는 모두 이곳에 한다.

// ID2D1HwndRenderTarget 생성.
JIF( d2d::pD2DFactory->CreateHwndRenderTarget(
    D2D1::RenderTargetProperties(),
    D2D1::HwndRenderTargetProperties(m_hwnd, size),
    &d2d::pRenderTarget ) );
 
// ID2D1BitmapRenderTarget 생성.
JIF( d2d::pRenderTarget->CreateCompatibleRenderTarget(
    d2d::SizeF( m_DISize ),       // 게임 내에서 사용할 가상 좌표계.
    size,                         // 렌더 타겟의 실제 크기
    &d2d::pBitmapTarget ) );

그리고 WM_PAINT를 받는 곳에선 비트맵을 얻어내서 hwnd 타겟에 한 방에 drawing.

static ID2D1Bitmap* pBitmap = NULL;
d2d::pBitmapTarget->GetBitmap( &pBitmap );
 
d2d::pRenderTarget->BeginDraw();
d2d::pRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity());
d2d::pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::White));
 
D2D1_SIZE_F size = d2d::pRenderTarget->GetSize();
D2D1_RECT_F rect = D2D1::RectF( 0.f, 0.f, size.width, size.height );
d2d::pRenderTarget->DrawBitmap( pBitmap, &rect );
 
HRESULT hr = d2d::pRenderTarget->EndDraw();
 
SafeRelease( &pBitmap );

무척 식상한 코드인데 (…) 말로만 설명하기 뭐해서 한 번 옮겨 적어 봄..;;

여기까지 처리하고 나면 이제 윈도우 사이즈를 사용자가 변경해도 게임 클라이언트의 내용은 변경된 사이즈에 맞게 스케일 된다.

 

다시 dpi로.. 화면 확대시 해상도 퀄리티 보장하기

여기까지만 하면 일단 장치에 독립적인 좌표계를 만드는 것은 성공이다. 스크린샷을 보면 가로세로 비율이 다르게 창을 줄였지만 게임 클라이언트는 적절하게 view 영역에 제대로 맞추어 줄어든다. 하지만 원본 사이즈보다 확대 될 때 문제가 있는데, 이미 백버퍼에서 가상 좌표계의 픽셀 크기 비트맵으로 래스터라이징 끝난 이미지를 늘리는 것이기 때문에, 크게 확대할 수록 화면이 흐리게 번지는 현상이 생긴다.

이걸 Direct2D에서는 간단히 해결할 수 있는데, 비트맵 타겟을 만들 때 좌표계로 사용할 비트맵 크기(size)와 실제 픽셀의 크기(pixel size)를 다르게 입력할 수 있기 때문. 두 크기의 값이 다르면 적당한 dpi가 자동으로 계산된다.

윈도우 사이즈가 변경되면 hwnd 타겟은 사이즈를 늘려주고, 비트맵 타겟은 변경된 윈도우 사이즈에 맞게 다시 생성해준다.

void BaseApp::OnResize(UINT width, UINT height)
{
    if (d2d::pRenderTarget)
    {
        D2D1_SIZE_U size;
        size.width = width;
        size.height = height;
 
        // Note: This method can fail, but it's okay to ignore the
        // error here -- it will be repeated on the next call to
        // EndDraw.
        d2d::pRenderTarget->Resize(size);
 
        ::SafeRelease( &d2d::pBitmapTarget );
 
        // bitmap target을 새로 생성.
        LIF( d2d::pRenderTarget->CreateCompatibleRenderTarget(
            d2d::SizeF( m_DISize ),
            size, &d2d::pBitmapTarget ) );
    }
}

그러면 화면이 확대 되어도 이미지가 뭉개지지 않고 선명하게 출력됨. 오 이거 좀 멋지군!

dpi 재계산 하기 전
dpi 재계산 후

난 갑자기 이걸 왜 짜고 블로그에 정리중인지 모르겠지만…

재미있네염 `ㅂ`)/

Posted by leafbird 트랙백 0 : 댓글 0

댓글을 달아 주세요