Font được hủy thông qua hàm Dispose.
Kết luận
Cùng với các tính năng vẽ chuỗi và tạo các Font có sẵn, thư viện đồ họa IGraphics đã chứa gần như đầy đủ những tính năng cần thiết của một thư viện đồ họa đơn giản cũng như đầy đủ các chức năng chúng em cần thiết để phát triển game.
4.4. Xử lý ảnh có độ trong suốt
Vấn đề
Hiệu ứng hình ảnh có thể tạo nên sự hấp dẫn cho game. Tuy nhiên, thư viện
GDI+ trên .Net Compact Framework không hỗ trợ sử dụng các ảnh có thông số trong suốt (alpha). Chính vì vậy, khi nạp các ảnh vào các đối tượng Bitmap thì các tấm ảnh sẽ bị biến đổi sang dạng thể hiện đúng trên màn hình, điều này nghĩa là các thông số alpha trong ảnh sẽ bị bỏ qua. Tuy nhiên, việc xử lý ảnh trong suốt có ý nghĩa tương đối quan trọng trong phát triển game. Một tấm ảnh có độ trong suốt có thể thể hiện tốt các hiệu ứng làm ảnh bóng bẩy, bắt mắt hơn nhờ đó đẹp hơn rất nhiều so với ảnh bitmap thông thường. Ngoài ra, một trong những đặc điểm quan trọng của ảnh có độ trong suốt cũng được thể hiện rất rõ khi vẽ các ảnh chồng lên nhau.
Chính vì vậy, nhằm thể hiện các hiệu ứng hấp dẫn hơn trong game, chúng em cần giải quyết vấn đề nạp các ảnh có độ trong suốt alpha và thể hiện chúng lên màn hình.
Thư viện ảnh gốc chỉ hỗ trợ tạo các ảnh Bitmap không có độ trong suốt. Trong khi đó, đối tượng Bitmap của .Net Compact Framework khi nạp ảnh lên sẽ làm mất đi độ trong suốt. Chính vì vậy, chúng em cần tìm giải pháp để nạp các ảnh có độ trong suốt.
Giải pháp
Các hệ điều hành từ Windows Mobile 5 trở về sau cung cấp Imaging API cho phép thể hiện các hình ảnh có độ trong suốt. Imaging API là các API cho phép
nạp thêm các loại ảnh có định dạng khác như JPEG, PNG,... trên các máy sử dụng hệ điều hành Windows Mobile. Vì vậy, chúng em ghép thêm phần xử lý của Imaging API vào trong bộ thư viện IGraphics nhằm tích hợp thêm khả năng xử lý ảnh có độ trong suốt.
Mọi thao tác xử lý với ảnh trong suốt đều được thực hiện qua interface IImage có trong Imaging API. Nhờ đó, mọi thao tác với các ảnh này cũng hoàn toàn thông qua interface nên sẽ dễ dàng thay đổi về sau mà không làm thay đổi mã nguồn của game.
4.4.1. Tạo ảnh có độ trong suốt
Để tạo ảnh có độ trong suốt, ta cần sử dụng hàm có trong interface IImagingFactory của Imaging API
uint CreateImageFromFile(string filename, out IImage image);
Thao tác để nạp một ảnh qua interface IImage
IImage image;
IImagingFactory factory = ImagingFactory.GetImaging(); factory.CreateImageFromFile(filename, out image);
4.4.2. Vẽ ảnh có độ trong suốt
Interface IGraphics được chúng em cập nhật thêm các chức năng để vẽ ảnh trong suốt. Người dùng có thể vẽ ảnh bằng cách sử dụng interface IGraphics.
Vẽ ảnh lên màn hình ở tọa độ x, y
void DrawImageAlphaChannel(IImage image, int x, int y)
Vẽ ảnh lên một vùng xác định trên màn hình
void DrawImageAlphaChannel(IImage image, Rectangle dest)
Vẽ một vùng trong ảnh gốc lên một vùng xác định nào đó trên màn hình void DrawImageAlphaChannel(IImage image, Rectangle dest, Rectangle src)
Kết luận
Bằng cách sử dụng Imaging API, chúng em đã kết hợp với thư viện xử lý đồ họa IGraphics để xử lý cho các ảnh có độ trong suốt. Nhờ đó, ảnh trong suốt có thể được nạp và sử dụng thông qua interface IImage.
4.5. Xử lý chuyển động animation
Vấn đề
Chuyển động animation là một phần không thể thiếu trong lập trình game. Thư viện xử lý đồ họa có sẵn lớp Animation hỗ trợ tạo ra các chuyển động. Tuy nhiên, lớp này chỉ hỗ trợ việc vẽ các quản lý và vẽ một dãy các khung hình trong một ảnh ra màn hình. Vì vậy, chúng em cần xây dựng thêm lớp hỗ trợ xử lý các chuyển động animation từ lớp Animation đã có.
Giải pháp
Như đã phân tích trong mục 2.2.5, chúng em lưu các chuyển động animation dưới dạng một ảnh gồm nhiều khung hình cho các chuyển động.
Bên cạnh đó, các hiệu ứng phép thuật trong game được xây dựng dựa trên các ảnh có độ trong suốt với định dạng PNG. Các hiệu ứng này cũng là các animation nên chúng em cũng sử dụng giải pháp như khi xây dựng animation cho các chuyển động bằng ảnh Bitmap.
Hình 4-28-Ảnh animation phép thuật bằng PNG
Lớp Animation nạp các ảnh animation theo một dây các khung hình liên tiếp của chuyển động. Vì lớp Animation ban đầu chỉ hỗ trợ xử lý trên ảnh Bitmap nên để hỗ trợ các ảnh trong suốt, chúng em cài đặt thêm các chức năng xử lý trên interface IImage.
4.5.1. Tạo Animation
Lớp Animation ban đầu chỉ cung cấp 2 hàm khởi tạo từ đường dẫn đến tập tin hình ảnh và từ Stream.
Animation(string fileName, IGraphics graphics, int numberColumns, int startCell, int cellWidth, int cellHeight) Animation(Stream stream, IGraphics graphics,
int numberColumns, int startCell, int cellWidth, int cellHeight)
Tuy nhiên, trong game chúng em sử dụng 2 loại ảnh chính qua 2 interface là IBitmap và IImage. Vì vậy, chúng em cài đặt thêm 2 chức năng cho phép khởi tạo Animation từ IBitmap và IImage.
Animation(IBitmap bmp, int numberColumns)
Animation(IImage img, int numberColumns, bool single)
4.5.2. Cập nhật Animation
Animation xử lý chuyển động trên một dãy các khung hình chuyển động. Vì vậy, trước khi vẽ cần phải thông qua một thao tác cập nhật. Thao tác này có nhiệm
vụ tính toán khung hình sẽ được hiển thị tiếp theo. Hàm cập nhật trong lớp Animation gốc.
void Update()
Trong khi xử lý game, đôi khi ta kiểm tra chuyển động animation này đã vẽ toàn bộ khung hình hay chưa. Chính vì vậy, nhóm chúng em cài thêm chức năng cập nhật có trả về giá trị cho biết các khung hình của chuyển động đã được vẽ hết hay chưa.
void Update(ref bool cycleFinished)
4.5.3. Vẽ Animation
Lớp Animation cung cấp chức năng cơ bản cho phép vẽ lên một vị trí có tọa độ x, y.
void DrawAnimation(int x, int y, Animation animation)
Tuy nhiên, không phải lúc nào các animation cũng được vẽ lên chỉ với thông tin tọa độ x, y mà đôi khi game còn đòi hỏi thay đổi kích thước khung hình được vẽ hay chỉ vẽ một vùng trong khung hình đó. Do đó, chúng em cài thêm 2 chức năng vẽ một vùng trong khung hình của animation và chức năng vẽ animation với kích thước khác khung hình gốc.
void DrawAnimation(int x, int y, Rectangle rectSrc, Animation animation)
void DrawAnimationScale(int x, int y, int w, int h, Animation animation)
4.5.4. Hủy Animation
Lớp Animation cũng được hủy bằng hàm Dispose().
Kết luận
Lớp Animation nhóm chúng em xây dựng thêm một số chức năng để hỗ trợ tốt hơn cho lập trình game. Lớp này cũng được sử dụng để xây dựng đối tượng quản lý các chuyển động của đối tượng thể hiện trong game.
4.6. Xây dựng đối tượng thể hiện chuyển động
Mọi game đều có các đối tượng hoạt động trên màn hình. Các đối tượng này vừa thể hiện hoạt động là dãy các chuyển động animation vừa quản lý các đối tượng thể hiện như chuyển đổi trạng thái animation của quân lính (chuyển đổi từ trạng thái đứng sang di chuyển cần thay đổi loại animation),... Trong khi đó, lớp Animation chỉ hỗ trợ quản lý 1 chuyển động animation nào đó mà không quan tâm đến hướng, trạng thái hay một số thông tin phụ thêm khác. Vì vậy, nhóm chúng em
xây dựng lớp Sprite cho các đối tượng chuyển động trong game.
Giải pháp
Lớp Sprite làm nhiệm vụ quản lý một đối tượng chuyển động trên màn hình như quân lính, công trình,...
4.6.1. Tạo Sprite
Sử dụng 2 hàm khởi tạo trong lớp Sprite để khởi tạo một Sprite
Khởi tạo Sprite từ chiều rộng một khung hình, chiều cao hình, tên hình, đối tượng đồ họa và đội của Sprite. Hàm này thường sử dụng để khởi tạo các đối tượng các đối tượng quân lính trong các trận chiến khi các quân thuộc các đội khác nhau có những màu khác nhau.
Sprite(int width, int height, string tenHinh, IGraphics g, byte team)
Khởi tạo Sprite từ một Bitmap, đối tượng đồ họa, chiều rộng một khung hình của ảnh và tọa độ Sprite sẽ được vẽ. Hàm này thường để khởi tạo các đối tượng nhà cửa, quân lính trong thành phố khi chỉ có một thể hiện duy nhất cho một đội quân mà không cần phải phân biệt với những đội quân khác.
Sprite(Bitmap bmp, IGraphics g, int widthFrame, int x, int y)
4.6.2. Cập nhật Sprite
Đối tượng Animation cần phải được cập nhật trước khi vẽ để tính toán khung hình tiếp theo sẽ được vẽ. Do đó, đối tượng Sprite cũng cần có một thao tác cập nhật.
void Update()
4.6.3. Vẽ Sprite
Sprite có thể vẽ bằng 2 hàm
Vẽ Sprite lên màn hình với vị trí mới
void Paint(IGraphics g, int xcur, int ycur)
Vẽ Sprite lên màn hình với vị trí mới cũng như hướng của Sprite void Paint(IGraphics g, int xcur, int ycur,
CurrentInstance curInstance)
4.6.4. Quản lý các Sprite với SpriteManager
Lớp SpriteManager làm nhiệm vụ quản lý danh sách các Sprite. Qua đó, lớp này cũng thực hiện các chức năng như thêm, xóa, cập nhật Sprite, vẽ toàn bộ danh sách các Sprite,...
Kết luận
Cùng với chức năng vẽ ảnh chuyển động animation. Chúng em đã xây dựng hoàn chỉnh các lớp cần thiết để phát triển game.
Tuy nhiên, chính vì xây dựng ảnh animation là 1 dãy các khung hình nên cần thiết phải có chức năng vẽ một vùng trong ảnh gốc vào đối tượng đồ họa.
Hình 4-29-Lớp quản lý Sprite-SpriteManager
4.7. Vẽ từng vùng trong ảnh
Vấn đề
Như đã đề cập trong phần 4.5, animation của phép thuật xây dựng bằng hình PNG. Mỗi animation bao gồm một ảnh với 1 dãy các khung hình. Chính vì vậy, cần phải vẽ một phần của ảnh gốc ra màn hình chứ không vẽ toàn bộ ảnh ra toàn bộ màn hình.
Giải pháp
Ảnh Bitmap cung cấp chức năng vẽ một phần của ảnh. Ta chỉ cần truyền vào vùng cần vẽ. Vì vậy, ta có thể vẽ một phần của ảnh Bitmap lên màn hình.
public void DrawBitmap(int x, int y, Rectangle sourceRegion, IBitmap bmp)
IImage cũng cung cấp phương thức vẽ một vùng của ảnh ra một phần của màn hình
STDMETHOD(Draw)(IN HDC hdc,
IN const RECT* dstRect,
dstRect được tính bằng pixel và được tính dựa trên device context. Trong khi đó, srcRect lại được tính dựa trên DPI (dot per inch). Trong C#, một RECT được tính dựa trên pixel nên cần phải chuyển đổi phù hợp mới tính toán lấy được một vùng của ảnh. Hàm trên được gọi trên C# bằng kỹ thuật Platform Invoke với hàm tương ứng là
uint Draw(IntPtr hdc, ref Rectangle dstRect, IntPtr p);
Trong .NET khoảng cách giữa các pixel là 0.01mm. Vì thế, ta phải sử dụng công thức:
2540 / dpiValue * value
Ví dụ, một IImage có kích thước 200x100 chỉ cần vẽ với srcRect là (100, 50, 100, 50) tại góc phần tư cuối thì ta tính srcRect cho hàm vẽ là:
(100 * 2540 / XdpiValue, 0, 100 * 2540 / XdpiValue, 50 * 2540 / YdpiValue)
Kết luận
Bằng việc xử lý vẽ từng vùng của ảnh lên vùng nào đó trên màn hình bằng cả Bitmap và PNG bằng IBitmap và IImage, các chuyển động animation có thể được vẽ lên màn hình dựa trên cả ảnh Bitmap và PNG. Nhờ đó, các hiệu ứng animation trong game có thể được xây dựng cả cho các đối tượng trong game (ảnh Bitmap) và các hiệu ứng phép thuật trong game (ảnh PNG).
4.8. Giới hạn những vùng cần vẽ trên màn hình
Vấn đề
Qui trình xử lý game của chúng em gồm 3 bước chính là Update, Render và Draw. Trong đó, Draw là quá trình tiêu tốn nhiều thời gian nhất do phải thao tác với thiết bị để vẽ lên màn hình. Hơn nữa, màn hình có độ phần giải càng lớn thì quá trình này càng lâu do phải vẽ càng nhiều pixel. Tốc độ càng trở nên chậm hơn nữa với độ phân giải 480x640 mà chúng em đã chọn. Do đó, tìm ra một giải pháp để giới hạn những vùng cần được vẽ lại là một trong những giải pháp hữu hiệu để làm tốc độ vẽ hình nhanh hơn nữa.
Giải pháp
Trong hầu hết các game, nếu như ảnh nền không thay đổi thì sự khác biệt giữa những khung hình liên tiếp nhau thường không chiếm hết màn hình mà nó chỉ là một phần nhỏ trên màn hình. Hay nói cách khác không phải phần nào trên màn hình cũng thay đổi cần phải vẽ lại mà chỉ có một phần nhỏ trên màn hình là phải được cập nhật và vẽ lại. Hơn nữa, những vùng cần vẽ lại có thể được xác định sau quá trình Render.
Dựa trên khái niệm Invalidate trong Windows Form, chúng em lồng ghép thêm một bước là khi vẽ các hình ảnh vào vùng đệm thì ghi nhận lại những vùng cần vẽ. Điều này giúp hạn chế việc vẽ lại toàn màn hình sau mỗi chu kỳ xử lý game.
Kết luận
Với giải pháp ghi nhận lại những vùng cần vẽ lên màn hình, tốc độ vẽ của game đã được tăng lên đáng kể.
4.9. Xây dựng hình ảnh các đối tượng trong game
Vấn đề
Một game dàn trận thông thường cần có những đối tượng đồ họa cần được thể hiện như nhà cửa, quân lính,... Vì thế, vấn đề là tìm ra cách dựng hình ảnh của các đối tượng lên màn hình nhanh chóng, phù hợp với môi trường thiết bị mobile nói riêng cũng như lập trình game nói chung.
Giải pháp
Hình ảnh một đối tượng có thể được tạo ra bằng nhiều cách:
Ghép các hình ảnh hình học đơn giản như đoạn thẳng, hình chữ nhật, hình tròn,... lại để tạo thành một đối tượng hình ảnh. Các hàm vẽ các hình học đơn giản đó hầu hết đều được các thư viện đồ họa hỗ trợ và tốc độ vẽ là khá nhanh. Tuy nhiên, tìm ra các giải thuật có thể tính toán tạo thành các hình ảnh đối tượng một cách chính xác là một vấn đề cực kỳ khó khăn. Tất nhiên ta vẫn có thể tạo thành các đối tượng đơn giản như nhà cửa (có thể được tạo
thành từ các đoạn thằng), con người (hình tròn và các đoạn thẳng),... hay phức tạp hơn như các hình ảnh Fractal nhưng liệu các hình ảnh tạo ra có đẹp hay không hay làm sao để tạo ra được các đối tượng phức tạp không hề có qui luật với thời gian tương đối nhanh là một vấn đề cực kỳ khó khăn.
Sử dụng hình ảnh các đối tượng được xây dựng sẵn bên ngoài để tạo hình ảnh trong game. Đây là cách làm phổ biến nhất và luôn được đề cập trong các tài liệu về lập trình game. Cách làm này giúp xây dựng nên các hình ảnh đẹp nhưng lại đơn giản hơn rất nhiều so với cách trên.
Lá dương xỉ vẽ bằng thuật toán vẽ hình Fractal
Ngôi nhà vẽ bằng các hình ảnh đồ họa đơn giản
Ngôi nhà sử dụng ảnh nhóm sử dụng phát triển game
Hình 4-30-So sánh giữa ảnh vẽ bằng 3 phương pháp
Kết luận
Chính vì những lý do đó, chúng em quyết định sử dụng các hình ảnh được xây dựng sẵn từ bên ngoài để tạo hình cho các đối tượng trong game. Với cách làm này, chúng em có thể tập trung vào xử lý các vấn đề trong lập trình game hơn là nghiên cứu các giải thuật vẽ hình cho từng đối tượng đơn thuần.
4.10. Tìm kiếm và xây dựng các hình ảnh cho game
Vấn đề
Một trong những giai đoạn quan trọng trong qui trình làm game là thiết kế và xây dựng hình ảnh, tài nguyên cho game. Từ những hình ảnh đối tượng trong game như nhà cửa, quân lính,... đến những hiệu ứng cho game đều cần phải có hình ảnh. Vì vậy, tìm ra giải pháp tìm kiếm hình ảnh và điều chỉnh sao cho các tài nguyên đó phù hợp với game chúng em xây dựng là việc vô cùng cần thiết.
Qui trình làm game bao gồm nhiều bước. Trong đó, một bước quan trọng là thiết kế hình ảnh, tài nguyên thể hiện cho game là một khâu quan trọng, đem lại sự hấp dẫn cho game. Tuy nhiên, các hình ảnh này được xây dựng bởi những họa sĩ thiết kế chuyên nghiệp với khả năng cũng như kinh nghiệm nhiều năm trong lĩnh vực đồ họa. Đây chính là vấn đề cực kỳ khó khăn, vượt quá yêu cầu lập trình game của chúng em. Chính vì vậy, chúng em sử dụng các công cụ để tìm kiếm các tài nguyên từ các game đã có trên thị trường và thực hiện những bước xử lý, biến đổi