Bài này chúng ta sẽ học cách xử lý input của người dùng.
Để tạo ra một đối tượng nhận input, bạn thừa kế đổi tượng IEventReceiver.
Đối tượng này chỉ có nhiệm vụ nhận event, mọi xử lý thì tùy vào bạn muốn nó làm gì thì sẽ code thêm vào.
class MyEventReceiver : public irr::IEventReceiver { public: virtual bool OnEvent(const irr::SEvent& event); }; |
Rất đơn giản, chỉ cần thừa kế và override method OnEvent thì đối tượng của bạn đã có thể làm việc.
SEvent là một struct (chữ S chính là viết tắt của struct) chứa các event xảy ra trong một chương trình sử dụng Irrlicht:
SEvent bao gồm
SGUIEvent: thông tin về các event về giao diện 2D ví dụ như button click, check box,….
SJoystickEvent: các event về tay cầm chơi game
SKeyInput: input bàn phím
SLogEvent: về những thông tin được log
SMouseEvent: các event về chuột như click, di chuyển, nút lăn,…
SUserEvent: event của user
Vậy chúng ta có thể thấy rằng từ SEvent chúng ta có thể kiểm soát rất nhiều khía cạnh để xử lý input. Cách sử dụng Event Receiver mà bạn đã thừa kế như sau:
Bạn tạo ra object và truyền con trỏ của nó vào hàm createDevice;
MyEventReceiver* receiver = new MyEventReceiver(); irr::IrrlichtDevice* device = irr::createDevice(DeviceType, WindowSize, BitDepth, FullScreen, StencilBuffer, VSync, receiver); |
Tức là bạn phải tạo ra Reciever và truyền cho Irrlicht khi tạo Device.
Bạn cũng có thể dùng receiver nếu như nó khởi tạo sau Irrlicht Device.
Trường hợp đó bạn có thể gọi hàm setEventReceiver từ bất cứ nơi nào của chương trình:
devicePtr->setEventReceiver(EventReceiverPtr); |
Việc khởi tạo một Event Receiver đến đây đã xong, chúng ta sẽ đi vào phần mở rộng class của bạn để xử lý những event mong muốn.
1. Keyboard Events
Ý tưởng của việc xử lý keyboard event là thay vì mỗi khi bấm nút chương trình lại chạy một đoạn code nào đó, bạn nên lưu lại trạng thái của tất cả các phím rồi sau đó kiểm tra nút có được bấm hay không, không thể làm rối code lên bằng cách chèn luôn vào EventReceiver của chúng ta được.
Thay vì
if (nút A được bấm) { do something } |
ở trong class Event Receiver, chúng ta có thể tạo ra một method kiểm tra xem nút có được bấm hay không, hệ quả là code xử lý nó sẽ nằm ở bên ngoài tạo ra sự linh hoạt và logic khi làm việc với input của người dùng.
Trên ý tương này, chúng ta sẽ viết class như sau:
class MyEventReceiver : public irr::IEventReceiver { public: MyEventReceiver() { // hàm khởi tạo đặt tất cả các trạng thái về false, tức không nút nào được bấm // u32 : unsigned int 32 bit for (irr::u32 i = 0; i < irr::KEY_KEY_CODES_COUNT; ++i) KeyStates[i] = false; } virtual bool OnEvent(const irr::SEvent& event); // method trả về trạng thái của nút bấm, để object bên ngoài gọi nó và xử lý input // các EKEY_CODE là enum của Irrlicht đặt ra bool IsKeyDown(irr::EKEY_CODE keyCode) const { // mỗi key code thực chất là một giá trị unsigned int, // key code = chỉ số của nó ở trong mảng, nơi lưu trạng thái của chính phím đó return KeyStates[keyCode]; } private: // mảng lưu trạng thái của các nút trên bàn phím // irr::KEY_KEY_CODES_COUNT = tổng số phím mà Irrlicht định nghĩa bool KeyStates[irr::KEY_KEY_CODES_COUNT]; }; |
Việc tiếp theo đó là mỗi khi có event (hàm OnEvent được gọi), bạn kiểm tra xem nút nào được bấm và đặt trạng thái vào mảng KeyStates.
bool MyEventReceiver::OnEvent(const irr::SEvent& event) { // kiểm tra xem có phải key event hay là các event khác như mouse, gui,... // event.KeyInput chứa thông tin của nút vừa được bấm hoặc thả ra // dễ dàng thấy được PressedDown là một giá trị boolean, // nếu như key được bấm, nó sẽ là true và nếu event là key này được thả ra, nó mang giá trị false if (event.EventType == irr::EET_KEY_INPUT_EVENT) KeyStates[event.KeyInput.Key] = event.KeyInput.PressedDown; return false; } |
Chúng ta cùng thử xem Object này có hoạt động tốt không nhé. Ví dụ trường hợp này mình muốn model biến mất khi bấm nút Spacebar.
while (device->run()) { if (receiver->IsKeyDown(irr::KEY_SPACE)) node->setVisible(false); else node->setVisible(true); driver->beginScene(true, true, irr::video::SColor(255, 100, 101, 140)); smgr->drawAll(); driver->endScene(); } |
2. Mouse Events
Cũng tương tự keyboard events, chúng ta sẽ mở rộng class như sau:
class MyEventReceiver : public irr::IEventReceiver { public: MyEventReceiver() { // hàm khởi tạo đặt tất cả các trạng thái về false, tức không nút nào được bấm // u32 : unsigned int 32 bit for (irr::u32 i = 0; i < irr::KEY_KEY_CODES_COUNT; ++i) KeyStates[i] = false; } virtual bool OnEvent(const irr::SEvent& event); // method trả về trạng thái của nút bấm, để object bên ngoài gọi nó và xử lý input // các EKEY_CODE là enum của Irrlicht đặt ra bool IsKeyDown(irr::EKEY_CODE keyCode) const { // mỗi key code thực chất là một giá trị unsigned int, // key code = chỉ số của nó ở trong mảng, nơi lưu trạng thái của chính phím đó return KeyStates[keyCode]; } bool LeftClick() const { return leftClick; } bool RightClick() const { return rightClick; } private: // mảng lưu trạng thái của các nút trên bàn phím // irr::KEY_KEY_CODES_COUNT = tổng số phím mà Irrlicht định nghĩa bool KeyStates[irr::KEY_KEY_CODES_COUNT]; bool leftClick; bool rightClick; // lưu vị trí hiện tại của con trỏ chuột // tọa độ cửa sổ là số nguyên nên chúng ta dùng position2di irr::core::position2di mousePosition; }; |
Hàm OnEvent sẽ cập nhật trạng thái của các thông tin input mà object của chúng ta lưu trữ, để tiện cho bên ngoài truy cập khi cần.
bool MyEventReceiver::OnEvent(const irr::SEvent& event) { // kiểm tra xem có phải key event hay là các event khác như mouse, gui,... // event.KeyInput chứa thông tin của nút vừa được bấm hoặc thả ra // dễ dàng thấy được PressedDown là một giá trị boolean, // nếu như key được bấm, nó sẽ là true và nếu event là key này được thả ra, nó mang giá trị false if (event.EventType == irr::EET_KEY_INPUT_EVENT) KeyStates[event.KeyInput.Key] = event.KeyInput.PressedDown; else if (event.EventType == irr::EET_MOUSE_INPUT_EVENT) { leftClick = event.MouseInput.isLeftPressed(); rightClick = event.MouseInput.isRightPressed(); mousePosition.X = event.MouseInput.X; mousePosition.Y = event.MouseInput.Y; } return false; } |
Trên đây là 2 ví dụ về xử lý input bàn phím và chuột với irrlicht, các bạn hoàn toàn có thể mở rộng nhiều hơn nữa, ví dụ như middle click, hay là nút cuộn của chuột,…dựa vào các ý tưởng mà chúng ta đã đi qua ở trên.
Manh
August 7, 2015
Nhập môn Lập trình Game