Nếu như các bạn đã đọc bài viết "Lập trình Game thú vui tao nhã" hẳn là các bạn cũng đang sôi sục lên chuẩn bị viết ngay một trò Game cho riêng mình. Tuy nhiên để viết một trò Game cũng đòi hỏi một số kỹ thuật cơ bản. Xin giới thiệu kỹ thuật Sprite "xoá phần thừa trong ảnh BMP" khi sử dụng hình ảnh cho trò Game.
Như các bạn đã biết để thực hiện một trò Game thì giao diện phải đẹp. Điều đó đồng nghĩa với việc ta không thể tự lập trình vẽ ra khung cảnh hay nhân vật mà phải sử dụng các chương trình đồ hoạ để tạo hình ảnh và dùng nó để hiển thị. Với các chương trình 3D thì đó là 3DMAX hay MAYA, còn với 2D như chúng ta đang thảo luận thì là Photoshop hay một số chương trình khác. File hình ảnh chúng ta dùng sẽ là định dạng BMP chuẩn của Windows. Tuy nhiên có một vấn đề phát sinh mà khi mới bắt đầu tôi đã phải suy nghĩ rất nhiều để tìm cách giải quyết. Đó là hầu hết các hình ảnh trong các trò chơi đều không có hình dạng chữ nhật. Nhưng một ảnh Bitmap lại là một hình chữ nhật vì vậy chúng ta phải có cách nào đó để vẽ được hình dạng thực của nó mà không bị hiện cả hình chữ nhật.
Có một hình đĩa bay như sau:
Khi ta vẽ 10 hình đĩa bay sẽ có dạng sau:
Các hình sẽ bị mất góc do các hình chữ nhật chèn lên nhau. Trong khi đó cái ta cần là hình phải không bị chồng lên nhau. Vậy làm thế nào bây giờ? Các bạn đã có cách giải quyết chưa? Dưới đây xin chia sẻ với các bạn 2 cách. Về cơ bản có hai giải thuật phổ biến áp dụng giải quyết vấn đề này đó là chroma key và alpha channel.
Kỹ thuật loại bỏ màu hay kĩ thuật chroma key là một phương phá nổi tiếng vì nó dễ hiểu và dễ sử dụng Ảnh Bitmap hay ảnh quét là một phương pháp lưu trữ ảnh dạng số. Nó chuyển các Pixel của ảnh thành các số nhị phân và thể hiện dữ liệu dưới các hàng và cột của ma trận, do đó ảnh Bitmap phải có dạng chữ nhật.
00 01 10 FF FE 12 15 11
11 12 11 16 18 90 11 C0
00 00 01 02 03 04 05 06
11 12 11 15 16 D0 ED DD
00 01 10 FF FE 12 15 11
11 12 11 16 18 90 11 C0
00 00 01 02 03 04 05 06
11 12 11 15 16 D0 ED DD
Với phương pháp chroma key ta sẽ loại bỏ màu khoá (màu thừa của ảnh) khi ta vẽ ảnh lên thiết bị hiển thị. Ảnh cần hiển thị sẽ có dạng:
Như chúng ta thấy màu hồng không nằm trong các màu của vật thể UFO. Do vậy ta chọn màu hồng là màu khoá của ảnh. Ta loại bỏ phần thừa trong ảnh chính bằng màu khoá này với đoạn mã sau:
for(nRow=0; nRow;>{ for(nCol=0; nCol;> {
dwPixel = GetPixel(hMemDC,nCol,nRow);
if(dwPixel != dwTransparentColor)
SetPixel(hDC,nPosX+nCol,nPosY+nRow,dwPixel);
}
Đó là phương pháp Chroma key tuy nhiên hạn chế của phương pháp này là không phải lúc nào cũng có mầu đặc biệt cho ta làm màu khoá. Theo phương pháp anpha chanel ta sẽ có nhiều khả năng hơn, đầu tiên ta tạo ảnh UFO lúc đầu thành hai ảnh như sau:
Phương pháp này sử dụng một ảnh mặt nạ là một ảnh gồm 2 màu đen trắng để loại bỏ phần thừa trong ảnh bitmap. Giải thuật Sprite sau sẽ thực hiện việc loại bỏ phần thừa trong hình UFO.
Giải thuật Sprite:
- Một ảnh bitmap đối tượng và một ảnh mặt nạ
- Nghịch đảo ( INVERT) ảnh mặt nạ
- Đưa ảnh mặt nạ lên màn hình hiển thị
- Dùng phép toán OR ảnh bitmap đối tượng với màn hình hiển thị
Đoạn mã thực hiện như sau:
void PutSprite(HDC hDC, int nPosX, int nPosY, int nCX, int nCY, HBITMAP hObj, HBITMAP hMask)
{HDC hMemDC, hMaskDC;
hMemDC = CreateCompatibleDC(hDC);
hMaskDC = CreateCompatibleDC(hDC);
SelectObject(hMemDC,hObj);
SelectObject(hMaskDC,hMask);
BitBlt(hMemDC,0,0,nCX,nCY,hMask,0,0,SRCAND);
PatBlt(hMaskDC,0,0,nCX,nCY,DSTINVERT);
BitBlt(hDC,nPosX,nPosY,nCX,nCY,hMaskDC,0,0,SRCAND);
BitBlt(hDC,nPosX,nPosY,nCX,nCY,hMemDC,0,0,SRCPAINT);
DeleteDC(hMemDC);
DeleteDC(hMaskDC);
}
Xét ví dụ sau:
#include "windows.h"
100 BITMAP ufo.bmp
// replace to startup.c
LRESULT MainWndProc(HWND hWnd, UINT message, WPARAM uParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hDC, hMemDC;
HBITMAP hBitmap;
int nCnt;
switch(message)
{
case WM_PAINT:
hDC = BeginPaint(hWnd,&ps);
hBitmap = LoadBitmap(g_hInst,MAKEINTRESOURCE(100));
hMemDC = CreateCompatibleDC(hDC);
SelectObject(hMemDC,hBitmap);
for(nCnt=10; nCnt<100; nCnt+=5)
TransparentImage(hDC,10+nCnt,10+nCnt,41,38,hMemDC,0,0,41,38,
RGB(255,0,255));
DeleteDC(hMemDC);
DeleteObject(hBitmap);
EndPaint(hWnd,&ps);
break;
case WM_LBUTTONDOWN:
DestroyWindow(hWnd);
break;
default:
DefWindowProc(hWnd, message, uParam, lParam);
}
return 0;
}
Chúng ta sẽ thu được kết quả đúng như thuật toán. Ngoài kỹ thuật này chúng ta còn cần một số kỹ thuật khác chẳng hạn như OffScreen, Paloma, Collision, Tile... mà chúng ta sẽ thảo luận sau.