Post date: Aug 16, 2011 5:18:56 PM
Không cần phải nói nhiều về vai trò của text trong một ứng dụng. Có thể nói, text là một loại "hình ảnh" đặc biệt, chuyển tải những thông điệp dựa trên hệ thống những ký hiệu đặc biệt, tuy không mang lại cho người tiếp nhận một hình ảnh trực quan, nhưng lại chứa đựng nhiều thông tin, ngữ nghĩa.
Đã là hình ảnh, thì text cũng sẽ được "vẽ". Có nhiều cách khác nhau đễ "vẽ" text.
[1] Trong trường hợp sử dụng đơn giản nhất, các dòng text được vẽ thành các hình ảnh, và được vẽ lên màn hình.
Với "text" này cho phép người dùng thỏa sức sáng tạo, thiết kế theo ý thích. Tuy nhiên, có một hạn chế là các text này tĩnh, và trong trường hợp sữ dụng nhiều text trong game, một lượng rất lớn cần được vẽ trước, gây lãng phí tài nguyên.
[2] Ở một hình thái khác, thay vì lưu trữ từng dòng text, từng ký tự riêng biệt sẽ được lưu dưới dạng hình ảnh. Các ký tự này có thể được sinh ra từ một bộ font nhất định, hoặc được vẽ trước.
Lúc này, việc hiển thị text là trở thành việc sắp xếp các hình ảnh của các ký tự cạnh nhau
[3] Một phương pháp khác là các hình ảnh của text được sinh ra "run-time" thông qua các thư viện như Free Type Font
Cùng với mục đích phát triển game cho đa dạng đối tượng người chơi từ nhiều quốc gia khác nhau, cùng một đoạn text như "Hello" sẽ được dịch trước thành nhiều ngôn ngữ khác nhau như "こんにちは", "안녕하세요", "Halo", "สวัสดี", "bonjour", "xin chào", ... Tùy vào lựa chọn của người chơi, và các dòng text của ngôn ngữ thích hợp sẽ được vẽ.
Hình trên minh họa cách mà một ký tự trong bộ font được thiết kế. Trong đó, kích thước một ký tự được tính từ phần Ascent (phần dành cho các ký tự có dấu như ^, `, ...) đến Descent cho chiều cao. Chiều rộng sẽ tùy vào từng ký tự trong bộ font. Với fixed-size font, kích thước này là cố định cho tất cả các kí tự (font courier).
Với Java, khi lấy khung bao quanh ký tự, ta sẽ nhận được giá trị y âm, do đường base-line cũng chính là đường y = 0, và chiều dương hướng xuống.
font = new Font("Tahoma", Font.PLAIN, 10);
char c = 'g';
AttributedString attributedString = new AttributedString("" + c);
attributedString.addAttribute(TextAttribute.FONT, font );
attributedString.addAttribute(TextAttribute.FOREGROUND, Color.RED);
Rectangle2D rect = font .getStringBounds(attributedString.getIterator(), 0, 1, m_graphics2D.getFontRenderContext());
Với cách tiếp cận thức 2 cho phép đơn giản hóa việc vẽ text:
Với cách tiếp cận này, việc đầu tiên là phải cung cấp hình bảng hình ảnh ký tự theo một font nhất định. Bảng hình ảnh này được sinh ra bằng code, hoặc dùng phần mềm đồ họa.
Bài viết sẽ trình bày phương pháp sinh bảng ký tự bằng code. Để làm được việc này, ta cần sự hổ trợ của Java với các thư việc tiện ích rất tốt.
Qui trình thực hiện:
+ Bước 1: các dòng text được lưu trong file (tạm gọi là Textfile.txt). Tiến hành đọc file và trích lọc các ký tự cần thiết.
+ Bước 2: Từ tập ký tự có được, tiến hành sinh charmap (bảng ký tự) dựa vào font người dùng lựa chọn. Đồng thời xuất các thông tin về các ký tự này như char-code, vị trí trên hình (x, y), và kích thước (w, h)
Từ các thông tin trên, ví dụ để vẽ ký tự "T", tra trong bảng, ta thấy T có vị trí tại (21, 6), và kích thước là 33x66
Font có thể được chọn từ những font hệ thống như serif, courier, times, tahoma, ... hoặc từ các file .ttf (có thể download rất nhiều file font .ttf free và đẹp). Lưu ý: một số font khi sử dụng cần phải mua license. Trong trường hợp dùng font free phải được nói rõ trong phần giới thiệu game.
Download tool (hiệu chỉnh đường dẫn JAVA trong config.bat, thực hiện test.bat để có ví dụ)
Ở phần trên, ta đã có được font. Bước tiếp theo, ta sẽ tìm cách lưu trữ các text của các ngôn ngữ khác nhau. Giải pháp được lựa chọn là lưu text trong file excel.
Cột ID là "tên" dại diện cho tất cả các ngôn ngữ. Ví dụ với textID "Hello", tương ứng với từng ngôn ngữ có một dạng text khác nhau. Trong ví dụ trên, ta sử dụng 3 ngôn ngữ Tiếng Anh, Tiếng việt và tiếng Nhật. Trong game, người lập trình chỉ cần quan tâm đến textID.
Các dòng text có thể được thêm vào tùy ý theo cấu trúc trên. Lưu ý là tránh trùng ID, và ID phải là chữ Latin không dấu.
Việc tiếp theo là cần có sự liên kết giữa text-font-lang. Để làm được việc này, ta cần tổ chức file excel như sau:
File excel sẽ gồm 3 sheet:
Đến lúc này, thay vì cần file quản lý rời rạc như text, font, lang thì tất cả các thông tin cần thiết đã được qui định trong file excel. Việc tiếp theo là cần có 1 tool export thông tin cần thiết trong file excel thành dạng đơn giản dùng trong game, cũng như coding để sử dụng nó.
Trước khi trình bày tool, ta cần tái cấu trúc lại một ít về proj hiện tại. Trước tiên, xin giới thiệu một chút về data, quản lý data cơ bản.
Ở trong những phần trước ta đã có sử dụng 1 số resource đọc từ file như hình ảnh, sprite, âm thanh... Các file này được để 1 cách tùy tiện. Tuy nhiên, với một project tương đối, lượng tata lớn, thì việc để tùy tiện này trở nên không hợp lý.
Ngoài ra, trong nhiều trường hợp, ta không thể dùng trực tiếp các file dữ liệu này, mà phải thông qua quá trình "sơ chế" vì một số nguyên nhân như:
Với nhiều lý dó, quá trình sơ chế gần như không thể tránh khỏi, nhưng quá trình này thường tạo ra các sản phẩm phụ không mong muốn là rối dữ liệu. Do đó ta cần tổ tức tốt các dữ liệu đầu vào.
Một mô hình đơn giản là trong project, ta tạo thêm thư mục #data chứ tất cả các data thô, cùng mới đoạn batchscript ngắn dùng cho quá trình sơ chế. Các file trung gian trong quá trình sơ chế được lưu trong #temp, và kết quả sau cùng lưu trong #build
Mộ file #makedata.bat đơn giản
@echo off
if not exist "#config.bat" (
echo ERROR: #config.bat not found
goto END
)
call #config.bat
if not exist #build (md #build)
if exist #temp (rd /s /q #temp)
md #temp
xcopy /s/q/y .\#data .\#temp\*.* >nul
if not exist "%TOOLPACK_PATH%\%TOOLPACK%" (
echo ERROR: can not file %TOOLPACK_PATH%\%TOOLPACK%
goto END
)
copy %TOOLPACK_PATH%\%TOOLPACK% #temp>nul
echo Build Text
cd .\#temp
%JDK%\java -jar -Xmx512M %TOOLPACK% le text.xls
copy /y .\*.ff ..\#build>nul
copy /y .\*.lang ..\#build>nul
copy /y .\*.h %SOURCE_DIR%>nul
echo copy rest
copy /y .\*.wav ..\#build>nul
copy /y .\*.ogg ..\#build>nul
copy /y .\*.tga ..\#build>nul
copy /y .\*.spr ..\#build>nul
copy /y .\*.bin ..\#build>nul
:END
cd %CUR_DIR%
Một điểm lưu ý là ta cần tách những file cấu trình cho quá trình build thành nhưng file rời, để tiện cho việc thay đổi (chép sang máy khác cũng có thể dễ dàng hiệu chỉnh và build được). Ví dụ:
#config.bat
@echo off
set CUR_DIR=%CD%
REM set TOOLPACK_PATH=F:\gametutor\toolpack\release
set TOOLPACK_PATH=..\gametutor\tools
set TOOLPACK=ToolPack.jar
set JDK=C:\jdk1.6.0_10\bin
set SOURCE_DIR=%CUR_DIR%
Sản phẩm của quá trình build data không chỉ là data, mà còn có thể là các file source (.cpp/.h) được sinh ra trong quá trình build. Những file này cần được copy vào thư mục source.
Việc chuyển đổi thư mục chứa data có thể làm cho chương trình không thể thực thi khi nhấn F5. Bạn cần thiết lập lại working directory của project Demo
Tool này có sử dụng thư viện jxl để đọc file excel: http://jexcelapi.sourceforge.net/
Tool đã được đính kèm trong source. Cách dùng có thể tham khảo trong đoạn script trên
%JDK%\java -jar -Xmx512M %TOOLPACK% le text.xls
trong đó text.xls là file text cần xuất thông tin. Tool này thực hiện các công việc sau:
Trong đó, file .ff là file tổ hợp của hình ảnh dùng cho font và các frame hình ảnh (xem phần font). Cấu trúc file như sau:
Để thuận tiện cho việc tra cứu, ký tự được lưu dạng binary code 4 bytes chứ không lưu theo các format như utf.
Các file .lang chứa thông tin về các text dùng trong game, format:
Khác với .ff, .lang có kích thước khá lớn. Để giảm không gian lưu trữ, thay vì lưu trữ dạng binary code 4 bytes, phần nội dung text được lưu trữ dạng utf-8. Tất cả các dòng text được lưu liên tiếp nhau, và được nhận diện với Text LUT. Mỗi một text sẽ có 1 record tương ứng trong TextLut, qui định:
Khi sữ dụng, các ký tự utf-8 trong text cần được decode về dạng binary code (4 bytes). Từ đó, các ký tự được tìm trong bảng tra frame của font tương ứng (.ff) và được vẽ.
TextIndex.h chứa các define các ID của text theo nguyên tắc:
Ví dụ:
#define TXT_HELLO 0
#define TXT_HOWAREYOU 1
Bước cuối cùng là thực hiện coding để load các file .ff và .lang vào game để sử dụng. Để làm việc này, ta xây dựng 2 lớp CFont và CText.
Việc thiết kế CText sao cho càng đơn giản cho người dùng càng tốt. Trong ví dụ, CText bao gồm các hàm cơ bản sau:
Ví dụ
+ Load file text tiếng Nhật:
CText::GetInstance()->Load<CFileWin32Driver>("JP");
+ Vẽ 1 dòng text có ID là TXT_HOWAREYOU:
CText::GetInstance()->DrawString(CGraphics2D::GetInstance(), TXT_HOWAREYOU, 100, 100, ETEXTANCHOR_LEFT, TEXTANCHOR_TOP);
Việc coding 2 lớp này không quá phúc tạp, chỉ lưu ý là cần có sự có sử chuyển đổi giữa mã UTF-8 thành Binary code point. Lưu ý, file .ff lưu mã ký tự dạng binary code point, file lang lưu text dạng utf-8. Để có thể dùng .ff như bảng tra ký tự, khi đọc .lang, các text utf-8 sẽ được chuyển về binary code point 4 bytes.
Xem: http://en.wikipedia.org/wiki/UTF-8
Một vấn đề lưu ý khác là cầm implement thư việc nén/giải nén RLE (xem CCompressor.cpp).
Vị trí các dòng text được vẽ ngoài phụ thuộc vào các giái trị x, y, còn phụ thuộc vào loại anchor sử dụng. Có 2 loại Anchor cơ bản là Verizon Anchor và Horizontal Anchor.
Sự kết hợp của 2 loại anchor cho ta 9 tổ hợp anchor như sau:
Là kỹ thuật chèn các ký tự xuống dòng hợp lý khi vẽ một đoạn text dài trong một phạm vi cho trước
Kỹ thuật này hiện tại chưa được viết trong source code. Tuy nhiên, bạn có thể dễ dàng thực hiện nó.
Ngoài ra còn có những kỹ thuật wrapping khó hơn, nhưng hầu như ít dùng trong game, xem : http://www.homeandlearn.co.uk/mw/s4p4.html
Lưu ý, để chạy được source, này bạn cần gọi #makedata trước khi build source (chỉnh sữa các thông tin trong #config.bat tương ứng với máy bạn).
Download