-
[Direct2D] 비트맵 이미지 움직이기 (1)C++/미분류 2024. 3. 10. 15:59
지난 글에서 DirectX와 WIP로 이미지를 출력하는 방법을 알아보았습니다.
[Direct2D] 비트맵 이미지 그리기
[Direct2D] 움직이는 비트맵 이미지 그리기이번 글에서는 위와 같이 출력할 수 있는 이미지가 있을 때, 이미지를 이동시키는 방법에 대해 알아보도록 하겠습니다.
이번 글은 지난 글에서 코드가 이어집니다.
1. 아이디어
더보기게임 뿐만 아니라, 화면 속 움직이는 대부분의 매체들은 정적인 이미지의 연속된 출력입니다.
정적인 이미지가 출력될 때 이전 이미지에서 변화를 주는 것으로 움직이는 것 처럼 보이게 할 수 있습니다.
WinAPI는 매 프레임마다 WM_PAINT메시지가 전송되고, 우리 코드에서는 그 때마다 OnRender함수를 호출합니다.
그러므로, OnRender함수에서 이미지의 출력 위치 등을 변화시켜보도록 하겠습니다.
2. MyBitmap 수정 (선언)
더보기MyBitmap객체는 비트맵 인스턴스를 다루기 위해 정의한 객체입니다.
gif등의 연속 이미지에도 대응할 수 있도록 인스턴스 벡터를 가지고 있으며, 연속 출력을 위해 진행된 프레임과 시간을 저장할 수 있는 필드도 있습니다.
자세한 선언은 코드를 참조해주세요.
우선, OnRender함수에서 구현한 MyBitmap이미지를 출력하는 코드를 살펴보도록 하겠습니다.
if (mySequenceBitmap) { ID2D1Bitmap* tmp = mySequenceBitmap->GetBitmap(); if (tmp) { myRenderTarget->DrawBitmap(tmp, D2D1::RectF( 0.0f, 0.0f, rtSize.width, rtSize.height )); } }
Direct2D의 이미지 출력은 크기와 위치가 아닌, 상하좌우 끝점의 위치를 인자로 받습니다.
따라서, 위 코드는 mySequenceBitmap의 이미지를 화면 전체에 출력하는 코드가 됩니다.
이를 크기와 위치를 기준으로 할 경우, 다음과 같은 값을 생각해볼 수 있습니다.
이미지의 크기 {Left, Top, Right, Bottom} + 이미지의 위치 {X, Y, X, Y}
오른쪽 방향이 +X, 아래쪽 방향이 +Y라고 가정할 경우, 위와 같은 방식으로 이미지의 크기와 위치를 기반으로 한 출력 범위를 지정할 수 있습니다.
위 아이디어를 구현하기 위해, MyBitmap에 변수를 추가하도록 하겠습니다.
class MyBitmap { private: std::vector<D2D1_SIZE_F> bitmapSize; D2D1_SIZE_F currentPosition; UINT currentFrame; ... }
MyBitmap객체는 연속된 이미지에 대응하며, 이미지 별 크기가 다를 수 있습니다.
bitmapSize는 index에 해당하는 이미지의 크기를 저장하는 변수입니다.
currentPosition은 이미지의 현재 위치를 저장하는 변수입니다.
currentFrame은 현재 출력되는 이미지의 index를 확인하기 위해 추가했습니다.
다음은 함수입니다.
class MyBitmap{ public: D2D1_RECT_F GetBitmapPosition(); void SetPosition(FLOAT x, FLOAT y); void Move(FLOAT x, FLOAT y); ... }
GetBitmapPosition은 이미지의 크기, 위치값을 Rect로 반환하는 함수입니다.
SetPosition과 Move는 각각 지정된 위치로 이동, 현재 위치에서 지정한 값 만큼 이동하는 함수입니다.
3. MyBitmap 수정 (구현)
더보기위 문단에서 선언한 함수들을 구현하고, 그에 맞춰 이전에 구현한 함수들을 약간 수정하도록 하겠습니다.
우선 수정입니다.
MyBitamp::GetBitmap함수는 현재 프레임에 해당하는 비트맵 인스턴스를 반환합니다.
기존 코드 및 변경한 코드는 다음과 같습니다.
// Before ID2D1Bitmap* MyBitmap::GetBitmap() { if (bitmap.size() > 0) { return bitmap[(UINT)elapseTime%bitmap.size()]; } else { return nullptr; } } // After ID2D1Bitmap* MyBitmap::GetBitmap() { if (bitmap.size() > 0) { currentFrame = (UINT)elapseTime % bitmap.size(); return bitmap[currentFrame]; } else { return nullptr; } }
프레임을 계산해서 바로 참조하는 이전 코드에서, 계산된 프레임을 저장하는 코드로 변경되었습니다.
현재 프레임 index는 bitmap의 크기를 참조할 때 사용됩니다.
우선, SetPositino과 Move함수를 살펴보도록 하겠습니다.
void MyBitmap::SetPosition(FLOAT x, FLOAT y) { currentPosition.width = x; currentPosition.height = y; } void MyBitmap::Move(FLOAT x, FLOAT y) { currentPosition.width += x; currentPosition.height += y; }
두 함수는 각각 위치 변경과 이동을 수행합니다.
구현도 이에 맞게 값에 대한 대입, 덧셈 연산의 차이만 존재합니다.
GetBitmapPosition은 이미지의 크기, 위치에 대응하는 출력 범위를 반환하는 함수입니다.
D2D1_RECT_F MyBitmap::GetBitmapPosition() { return D2D1::RectF( 0 + currentPosition.width, // Left 0 + currentPosition.height, // Top bitmapSize[currentFrame].width + currentPosition.width, // Right bitmapSize[currentFrame].height + currentPosition.height // Bottom ); }
비트맵의 크기와 위치는 이미 다른 함수에서 수정이 이루어지기 때문에, GetBitmapPositino함수는 변경된 값들로 Rect를 생성하는 역할만 수행합니다.
이 함수는 Render target에 의해 이미지가 출력될 때 같이 호출됩니다.
4. 출력하기
더보기기존 코드에서 기능만 추가되었기 때문에 위 코드들은 MyApp코드의 다른 편집 없이 정상적으로 작동합니다.
하지만 움직임과 관련된 기능을 추가하는 것이 목표이기 때문에, OnRender함수를 수정하도록 하겠습니다.
다음은 기존 MyBitmap을 출력하는 코드입니다.
if (mySequenceBitmap) { ID2D1Bitmap* tmp = mySequenceBitmap->GetBitmap(); if (tmp) { myRenderTarget->DrawBitmap(tmp, D2D1::RectF( 0.0f, 0.0f, rtSize.width, rtSize.height )); } }
위 코드를 다음과 같이 수정하도록 하겠습니다.
mySequenceBitmap->Move(deltaTime * 10, deltaTime * 10); if (mySequenceBitmap) { ID2D1Bitmap* tmp = mySequenceBitmap->GetBitmap(); if (tmp) { myRenderTarget->DrawBitmap(tmp, mySequenceBitmap->GetBitmapPosition() ); } }
MyBitmap의 이동 함수를 호출하여 10pixel/second의 속도로 +X, +Y방향으로 움직입니다.
움직이는 위치에 대응하기 위해, 고정된 값 대신 MyBitmap의 위치 함수를 호출하여 두 번째 인자로 전달합니다.
이미지를 움직이는 방법에 대해 알아보았습니다.
이미지의 이동, 애니메이션 진행 등 움직임을 표현할 때 주의할 점 중 하나는, 출력 함수의 대부분이 프레임 단위로 호출된다는 점 입니다.
프로그램을 실행시키는 환경에 따라 프레임이 달라질 수 있으므로, 함수의 호출 간격과 같이 기준점이 되는 시간과 변위를 맞춰주는 것이 매우 중요합니다.
이번 글에서 사용된 전체 코드는 다음 Github에서 확인하실 수 있습니다.
GitHub - ruru14/WinapiStudy: WinAPI및 DirectX를 공부하며, 간단한 운동 법칙을 적용해봅니다.
WinAPI및 DirectX를 공부하며, 간단한 운동 법칙을 적용해봅니다. Contribute to ruru14/WinapiStudy development by creating an account on GitHub.
github.com
이번 글이 도움이 되셨기를 바랍니다.
감사합니다.
'C++ > 미분류' 카테고리의 다른 글
[DirectX] 스마트 포인터 ComPtr (0) 2024.03.21 [Direct2D] 비트맵 이미지 움직이기 (2) (0) 2024.03.13 [Direct2D] 움직이는 비트맵 이미지 그리기 (1) 2024.02.28 [Direct2D] 비트맵 이미지 그리기 (1) 2024.02.26 [WinAPI] 키보드 입력 (WM_KEYDOWN, WM_KEYUP, GetAsyncKeyState) (0) 2024.02.23