Gamepad và các cần điều khiển đã trỏ thành thông dụng hiện nay. Ngoải ra hầu hết các joystick controller sử dụng để gắn vào trong game port trên card âm thanh, hầu hết các thiết bị bán trên thị trường hiện nay sử dụng kết nối USB. Kết nối USB cho thiết bị một ưu thế hơn các thiết bị khác. Thiết bị USB dễ dàng tìm thấy bởi hệ thống và điều khiển thông qua giao diện thông dụng HID. Vì thế, đọc từ gamepad và joystick đã trở nên dễ dàng hơn.
Sự khác nhau chính giữa sử dụng joystick và gamepad là sự cần thiết liệt kê tuyệt đối các Input Device. Vì nhiều joystick có thể gắn vào hệ thống, nên DirectInput không có GUID xác định trước cho những thiết bị này. Trước khi bạn có thể gọi CreateDevice để chuẩn bị sử dụng một joystick, bạn phải liệt kê các Input Device mà đã cài đặt trên hệ thống.
Liệt kê Joystick
Liệt kê các Device làm cho DirectInput yêu cầu mỗi thiết bị lại một lần nữa tìm kiếm các chuẩn mà nó thiết lập. Ví dụ, nếu bạn gọi EnumDevices như sau:
hr= g_lpDI->EnumDevices(DI8DEVCLASS_GAMECTRL, EnumDevicesCallback,
NULL,
DIEDFL_ATTACHEONLY);
Sau đó những thiết bị trả về cho hàm EnumDevicesCallback sẽ chỉ có thể là dạng
DI8DEVICECLASS_GAMECTRL, đây là đích thực những gì mà bạn cần khi tìm kiếm cho
Kiểm soát một Joystick
Bàn phím và chuột gây ra ngắt phần cứng báo hiệu cho hệ thống rằng có dữ liệu Input mới đang hiện hành. Điều mà hầu hết các joystick cần là thỉnh thoảng chúng được kiểm soát. Thời hạn kiểm soát có liên quan tới việc kiểm tra Device để phát hiện Input mới. Sau khi một thiết bị đã được kiểm soát, bạn có thể giới hạn Input hợp lệ mới từ chúng.
Chú ý:
Joystick và gamepad sử dụng cấu trúc định trước DIDATAFORMAT và DIJOYSTATE2
Joystick là những thiết bị số không hoàn chỉnh, chúng cũng bao gồm một bộ phận analog. Thông thường, joystick sử dụng Digital Input cho các nút bấm, có nghĩa là chúng là một trong hai kiểu: lên hoặc xuống, và chúng sử dụng Analog Input cho chính cần diều khiển của mình. Kiểu Analog Input cho phép bạn nhận biết khoảng cách mà joystick đã dịch chuyển.
Một dịch chuyển nhỏ của cần điều khiển hướng về phía bên phải cũng sẽ gửi đi một giá trị nhỏ tới điều khiển chương trình, ngược lại nếu đẩy cần điều khiển hoàn toàn sang phải sẽ gửi đi một giá trị khá lớn. Độ lớn của giá trị này được xác định bởi đặc tính phạm vi của thiết bị.
Đặc tính phạm vi thường thiết lập cho phần Analog của cần điều khiển và nó bao gồm các giá trị lớn nhất và giá trị nhỏ nhất mà thiết bị sẽ tạo. Ví dụ, thiết lập hạn nhỏ nhất của phạm vi tới -1000 và lớn nhất tới 1000 thì nó chỉ cho phép game của bạn có những giá trị mà rơi vào trong khoảng giới hạn này. Dịch chuyển cần điều khiển bằng mọi cách sang trái sẽđưa giá trị về -1000, ngược lại nếu dịch chuyển nó sang phải sẽ làm tăng giá trị về phía 1000. Bạn có thể thiết lập giới hạn của thiết bị tới bất kỳ giá trị nào mà làm nên cảm giác thật cho game của bạn.
Thiết lập phạm vi của một cần điều khiển.
Để thiết lập phạm vi đặc tính cho phần Analog của cần điều khiển, bạn phải sử dụng hàm
EnumObjects. Như bạn đã biết từ trước, hàm EnumObjects làm việc tương tự như
EnumDevices nhưng nó gửi cho hàm callback của nó các detail trên các bộ phận khác nhau
của Device. Một ví dụ hàm callback được chỉ ra dưới đây:
/************************************************************** EnumObjCallback EnumObjCallback
**************************************************************/
BOOL CALLBACK EnumObjCallback(const DIDEVICEOBJECTINSTANCE *pdidoi, VOID* pContext)
{
//nếu đối tượng này là một axis type object, ta thử thiết lập phạm vi if (pdidoi->dwType & DIDFT_AXIS)
{ //tạo một cấu trúc DIPROPRANGE //tạo một cấu trúc DIPROPRANGE DIPROPRANGE diprg; //mỗi cấu trúc cần một cấu trúc kiểu DIPROPHEADER được gán diprg.diph.dwSize = sizeof(DIPROPRANGE); diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER); diprg.diph.dwHow=DIPH_BYID; diprg.diph.dwObj=pdidoi->dwType; //chỉ định trục liệt kê //giá trị lớn nhất và nhở nhất của phạm vi đang thiết lập ở đây diprg.lMin=-100;
HRESULT hr;
//thiết lập phạm vi cho trục
hr=g_JoystickDevice->SetProperty(DIPROP_RANGE, &diprg.diph); //kiểm tra để biết được nếu thiết lập phạm vi đặc tính thành công if FAILED(hr)
return DIENUM_STOP; }
//Thông báo cho EnumObjects tiếp tục tới Object tiếp theo tsrong Device này return DIENUM_CONTINUE;
}
Ở ví dụ này, trước tiên là kiểm tra để biết nếu đối tượng được truyền cho callback có kiểu trục
(axis). Một axis object là một kiểu biểu diễn phần điều khiển analog của joystick controller. Nếu
một axis Deivce hợp lệđược sử dụng, chương trình sẽ thử thiết lập giá trị phạm vi cho chúng.
Đầu tiên một cấu trúc DIPROPRANGEđược tạo sẽ giữ thông tin liên quan đến phạm vị. Cấu trúc
DIPROPRANGEđược định nghĩa như sau:
Typedef struct DIPROPRANGE { DIPROPRANGE diph;
LONG lMin; LONG lMax;
} DIPROPRANGE, * DIPROPRANGE;
Biến thứ hai và thứ ba trong cấu trúc này: lMin và lMax trên thực tế biểu diễn giá trị giới hạn lớn nhất và nhỏ nhất. Bạn có thể thiết lập hai giá trị này tới bất cứ nơi đâu mà game của bạn cần, và biến lMin luôn nhỏ hơn giá trị lMax nhưđã biết.
Biến đầu tiên trong cấu trúc DIPROPRANGE là một cấu trúc khác: DIPROPHEADER. Cấu trúc
DIPROPHEADER cần thiết cho tất cả các cấu trúc đặc tính.
Typedef struct DIPROPHEADER{ DWORD dwSize;
DWORD dwHeaderSize; DWORD dwObj;
DWORD dwHow;
} DIPROPHEADER, *DIPROPHEADER;
Cấu trúc DIPROPHEADER cần chỉ 4 biến được thiết lập. Biến thứ nhất dwSize biểu diễn kích thước của cấu trúc gửi kèm tính theo byte. Trong trường hợp này, nó là cấu trúc DIPROPRANGE. Biến thứ hai dwHeaderSize là kích thước của cấu trúc DIPROPHEADER.
Biến thứ ba và thứ tư làm việc cùng nhau. Nội dung của biến dwHow biểu diễn kiểu của dữ liệu trong biến dwObj. dwHow có thể là một trong những giá trị sau:
DIPH_DEVICE – dwObj phải thiết lập về 0.
DIPH_BYOFSET – dwObj là phần bù trong định dạng dữ liệu hiện tại
DIPH_BYUSAGE – dwObj phải thiết lập về cách sử dụng trang HID và sử dụng các giá trị.
DIPH_BYID – dwObjđược thiết lập định dạng đối tượng. Bạn có thể tìm thấy nó trong cấu
trúc DIDEVICEOBJECTINSTANCE mà được truyền cho hàm callback.
Cuối cùng, sau khi những cấu trúc đã được bổ xung đầy đủ. Hàm này sẽ áp dụng GUID của đặc tính để thiết lập tham sốđầu tiên của nó và một địa chỉ tới cấu trúc chứa thông tin đặc tính mới.
Chú ý:
Vài thiết bị không cho phép phạm vi thay đổi. Đặc tính phạm vi chỉđược đọc (read-only).
Bạn có thể thay đổi những đặc tính khác của một Device theo cùng một phương thức nhờ đặc tính phạm vi nào được thay đổi. Các đặc tính còn lại sẽ tồn tại cho các thiết lập khác. Ví dụ,
DIPROP_DEADZONE là giá trị phạm vi chỉ ra phần nào của joystick sẽ dịch chuyển mà không
có tác dụng. DIPROP_FFGAIN thiết lập tăng tốc cho force feedback, và
DIPROP_AUTOCENTER thông báo cho thiết bị là nó nên quay về tâm của chính nó hay không
khi người sử dụng thoát khỏi.
Đọc từ joystick
Joystick cũng giống như các Input Devices khác, cần sủ dụng hàm GetDeviceState. Trong truờng hợp cần điều khiển và gamepad, bộ nhớđệm phải lưu giữ nguồn dữ liệu vào là một trong hai kiểu DIOYSTATE hoặc là DIOYSTATE2. Sự khác nhau chính giữa hai cấu trúc này là số object trên 1 Joystick Device được đọc. Cấu trúc DIOYSTATE cho phép chỉ hai Analog Device, ngược lại cấu trúc DIOYSTATE2 có thểđiều khiển nhiều hơn.
Vì Input từ Joystick không phải là một phần tuyệt đối, bạn có thể giữ lại bất kỳ một dich chuyển nào trước đó. Ví dụ, nếu bạn đang sử dụng joystick để điều khiển dịch chuyển của một sprite xung quanh màn hình, bạn cần giữ lại trong một biến riêng biệt vị trí hiện tại X và Y. khi new input được đọc từ joystick, nguồn dữ liệu mới sẽđược điền vào vị trí hiện tại X và Y. Ví dụ sau minh họa điều này:
//có hai biến lưu vị trí hiện tại của sprite LONG curX;
LONG curY;
//đây là vị trí của sprite thiết lập theo mặc định. curX=320; curY=240; while (1) { sử dụng cấu trúc DIOYSTATE để lưu dữ liệu từ joystick DIOYSTATE2 js;
//đầu tiên là kiểm sóat joystick g_joystickDevice->Poll();
//lấy nguồn dữ liệu vào hiện tại từ thiết bị.
g_joystickDevice->GetDeviceState( sizeof(DIOYSTATE), &js); //điền giá trị mới vào vị trí hiện tại của X,Y
curX+=js.lX; curY+=js.lY;
//vẽ sprite với vị trí mới cập nhật }
Đây là một phần mã nguồn nhỏđầu tiên kiểm soát thiết bị cần điều khiển để đưa ra Input mới. Sau đó new Input được đưa vào cấu trúc DIJOYSTATE2. Cuối cùng lX và lYđược điền vào vị trí hiện tại X,Y của sprite. Biến lX và lY biểu diễn Input trả về từđiều khiển analog đầu tiên.
Bạn có thể tìm thấy ví dụ đầy đủ về đọc từ joystick trong thư mục chapter9\example4 trên đĩa CD-ROM.