Xử lý sự kiện trong game vô cùng quan trọng, đó có thể là sự kiện chuột, bàn phím, hay touch (đối với những dòng có màn hình cảm ứng). Framework SDL (Simple DirectMedia Layer) hỗ trợ đầy đủ những sự kiện đó để thao tác và xây dựng ứng dụng.
Sự kiện trong SDL
Cấu trúc SDL_Event
SDL có cung cấp một union
có tên là SDL_Event
để chứa tất cả các thông tin của tất cả các event trong bộ thư viện này, cấu trúc của SDL_Event
như sau:
Uint32 | type | event type, shared with all events |
---|---|---|
SDL_CommonEvent |
common | common event data |
SDL_WindowEvent |
window | window event data |
SDL_KeyboardEvent |
key | keyboard event data |
SDL_TextEditingEvent |
edit | text editing event data |
SDL_TextInputEvent |
text | text input event data |
SDL_MouseMotionEvent |
motion | mouse motion event data |
SDL_MouseButtonEvent |
button | mouse button event data |
SDL_MouseWheelEvent |
wheel | mouse wheel event data |
SDL_JoyAxisEvent |
jaxis | joystick axis event data |
SDL_JoyBallEvent |
jball | joystick ball event data |
SDL_JoyHatEvent |
jhat | joystick hat event data |
SDL_JoyButtonEvent |
jbutton | joystick button event data |
SDL_JoyDeviceEvent |
jdevice | joystick device event data |
SDL_ControllerAxisEvent |
caxis | game controller axis event data |
SDL_ControllerButtonEvent |
cbutton | game controller button event data |
SDL_ControllerDeviceEvent |
cdevice | game controller device event data |
SDL_AudioDeviceEvent |
adevice | audio device event data (>= SDL 2.0.4) |
SDL_QuitEvent |
quit | quit request event data |
SDL_UserEvent |
user | custom event data |
SDL_SysWMEvent |
syswm | system dependent window event data |
SDL_TouchFingerEvent |
tfinger | touch finger event data |
SDL_MultiGestureEvent |
mgesture | multi finger gesture data |
SDL_DollarGestureEvent |
dgesture | multi finger gesture data |
SDL_DropEvent |
drop | drag and drop event data |
Dưới đây là định nghĩa SDL_Event
trong bộ thư viện SDL:
typedef union SDL_Event { Uint32 type; /**< Event type, shared with all events */ SDL_CommonEvent common; /**< Common event data */ SDL_WindowEvent window; /**< Window event data */ SDL_KeyboardEvent key; /**< Keyboard event data */ SDL_TextEditingEvent edit; /**< Text editing event data */ SDL_TextInputEvent text; /**< Text input event data */ SDL_MouseMotionEvent motion; /**< Mouse motion event data */ SDL_MouseButtonEvent button; /**< Mouse button event data */ SDL_MouseWheelEvent wheel; /**< Mouse wheel event data */ SDL_JoyAxisEvent jaxis; /**< Joystick axis event data */ SDL_JoyBallEvent jball; /**< Joystick ball event data */ SDL_JoyHatEvent jhat; /**< Joystick hat event data */ SDL_JoyButtonEvent jbutton; /**< Joystick button event data */ SDL_JoyDeviceEvent jdevice; /**< Joystick device change event data */ SDL_ControllerAxisEvent caxis; /**< Game Controller axis event data */ SDL_ControllerButtonEvent cbutton; /**< Game Controller button event data */ SDL_ControllerDeviceEvent cdevice; /**< Game Controller device event data */ SDL_QuitEvent quit; /**< Quit request event data */ SDL_UserEvent user; /**< Custom event data */ SDL_SysWMEvent syswm; /**< System dependent window event data */ SDL_TouchFingerEvent tfinger; /**< Touch finger event data */ SDL_MultiGestureEvent mgesture; /**< Gesture event data */ SDL_DollarGestureEvent dgesture; /**< Gesture event data */ SDL_DropEvent drop; /**< Drag and drop event data */ /* This is necessary for ABI compatibility between Visual C++ and GCC Visual C++ will respect the push pack pragma and use 52 bytes for this structure, and GCC will use the alignment of the largest datatype within the union, which is 8 bytes. So... we'll add padding to force the size to be 56 bytes for both. */ Uint8 padding[56]; } SDL_Event;
Lấy thông tin Event
Tất cả các event của Windows đều được lưu ở hàng đợi (queue), SDL có định nghĩa một hàm để lấy ra event từ hằng đợi đó, hàm này có prototype như sau:
int SDL_PollEvent(SDL_Event* event)
Có tham số truyền vào là một con trỏ tới đối tượng SDL_Event
. Trả về 1 nếu có event tồn tại trong queue ngược lại trả về 0 nếu queue rỗng. Và có chức năng là lấy thông tin của event ở queue và đổ dữ liệu vào đối tượng SDL_Event
sau đó xóa event đó khỏi queue.
Ví dụ:
SDL_Event mainEvent; SDL_PollEvent(&mainEvent);
Thao tác với Event trong ứng dụng
Sử dụng mã nguồn ở bài Khởi Tạo Môi Trường Lập Trình Game Sử Dụng Thư Viện SDL (Simple DirectMedia Layer) để khởi tạo 1 cửa sổ, tiếp tục project với đoạn code như dưới đây:
#include <stdio.h> #include <SDL.h> #undef main int main() { //initializes the subsystems if (SDL_Init(SDL_INIT_EVERYTHING) < 0) { printf("Unable to initialize SDL %s\n", SDL_GetError()); return -1; } SDL_Window* window = NULL; //Create window window = SDL_CreateWindow("Stdio.vn - SDL", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 640, SDL_WINDOW_SHOWN); if (window == NULL) { printf("Could not create window %s", SDL_GetError()); return -1; } //Delay 2s SDL_Delay(2000); //Destroy a window. SDL_DestroyWindow(window); //cleans up all initialized subsystems SDL_Quit(); return 0; }
Ở project trước, ứng dụng delay 2s sau đó giải phóng tài nguyên và thoát chương trình, do bản chất của game là cập nhật và render liên lục, nên project bắt buộc phải có một vòng lặp while
để duy trì vòng đời của game (main game, main loop), trong vòng lặp đó sẽ lấy ra các sự kiện và xử lý riêng tùy theo yêu cầu của game.
bool isRunning = true; SDL_Event mainEvent; while (isRunning) { while (SDL_PollEvent(&mainEvent)) { switch (mainEvent.type) { //User - requested quit case SDL_QUIT: { isRunning = false; break; } //Mouse button pressed case SDL_MOUSEBUTTONDOWN: { //Do Something break; } //Mouse button released case SDL_MOUSEBUTTONUP: { //Do Something break; } //Mouse moved case SDL_MOUSEMOTION: { //Do Something break; } //Mouse wheel motion case SDL_MOUSEWHEEL: { //Do Something break; } //Key released case SDL_KEYDOWN: { //Do Something break; } //Key pressed case SDL_KEYUP: { //Do Something break; } default: break; } } }
Giá trị main.type
được so sánh với các giá trị như SDL_KEYDOWN
, SDL_KEYUP
để biết rằng đó là loại sự kiện gì.
Ta có mối quan hệ giữa trường dữ liệu type
của đối tượng SDL_Event
với các cấu trúc SDL_EventType
như sau, dựa vào đó có thể xử lý theo từng event cho phù hợp.
Event Type | Event Structure | SDL_Event Field |
---|---|---|
SDL_AUDIODEVICEADDED SDL_AUDIODEVICEREMOVED |
SDL_AudioDeviceEvent |
adevice |
SDL_CONTROLLERAXISMOTION |
SDL_ControllerAxisEvent |
caxis |
SDL_CONTROLLERBUTTONDOWN SDL_CONTROLLERBUTTONUP |
SDL_ControllerButtonEvent |
cbutton |
SDL_CONTROLLERDEVICEADDED SDL_CONTROLLERDEVICEREMOVED SDL_CONTROLLERDEVICEREMAPPED |
SDL_ControllerDeviceEvent |
cdevice |
SDL_DOLLARGESTURE SDL_DOLLARRECORD |
SDL_DollarGestureEvent |
dgesture |
SDL_DROPFILE |
SDL_DropEvent |
drop |
SDL_FINGERMOTION SDL_FINGERDOWN SDL_FINGERUP |
SDL_TouchFingerEvent |
tfinger |
SDL_KEYDOWN SDL_KEYUP |
SDL_KeyboardEvent |
key |
SDL_JOYAXISMOTION |
SDL_JoyAxisEvent |
jaxis |
SDL_JOYBALLMOTION |
SDL_JoyBallEvent |
jball |
SDL_JOYHATMOTION |
SDL_JoyHatEvent |
jhat |
SDL_JOYBUTTONDOWN SDL_JOYBUTTONUP |
SDL_JoyButtonEvent |
jbutton |
SDL_JOYDEVICEADDED SDL_JOYDEVICEREMOVED |
SDL_JoyDeviceEvent |
jdevice |
SDL_MOUSEMOTION |
SDL_MouseMotionEvent |
motion |
SDL_MOUSEBUTTONDOWN SDL_MOUSEBUTTONUP |
SDL_MouseButtonEvent |
button |
SDL_MOUSEWHEEL |
SDL_MouseWheelEvent |
wheel |
SDL_MULTIGESTURE |
SDL_MultiGestureEvent |
mgesture |
SDL_QUIT |
SDL_QuitEvent |
quit |
SDL_SYSWMEVENT |
SDL_SysWMEvent |
syswm |
SDL_TEXTEDITING |
SDL_TextEditingEvent |
edit |
SDL_TEXTINPUT |
SDL_TextInputEvent |
text |
SDL_USEREVENT |
SDL_UserEvent |
user |
SDL_WINDOWEVENT |
SDL_WindowEvent |
window |
SDL_EventType
được định nghĩa trong bộ thư viện SDL như sau:
typedef enum { SDL_FIRSTEVENT = 0, /**< Unused (do not remove) */ /* Application events */ SDL_QUIT = 0x100, /**< User-requested quit */ /* These application events have special meaning on iOS, see README-ios.txt for details */ SDL_APP_TERMINATING, /**< The application is being terminated by the OS Called on iOS in applicationWillTerminate() Called on Android in onDestroy() */ SDL_APP_LOWMEMORY, /**< The application is low on memory, free memory if possible. Called on iOS in applicationDidReceiveMemoryWarning() Called on Android in onLowMemory() */ SDL_APP_WILLENTERBACKGROUND, /**< The application is about to enter the background Called on iOS in applicationWillResignActive() Called on Android in onPause() */ SDL_APP_DIDENTERBACKGROUND, /**< The application did enter the background and may not get CPU for some time Called on iOS in applicationDidEnterBackground() Called on Android in onPause() */ SDL_APP_WILLENTERFOREGROUND, /**< The application is about to enter the foreground Called on iOS in applicationWillEnterForeground() Called on Android in onResume() */ SDL_APP_DIDENTERFOREGROUND, /**< The application is now interactive Called on iOS in applicationDidBecomeActive() Called on Android in onResume() */ /* Window events */ SDL_WINDOWEVENT = 0x200, /**< Window state change */ SDL_SYSWMEVENT, /**< System specific event */ /* Keyboard events */ SDL_KEYDOWN = 0x300, /**< Key pressed */ SDL_KEYUP, /**< Key released */ SDL_TEXTEDITING, /**< Keyboard text editing (composition) */ SDL_TEXTINPUT, /**< Keyboard text input */ /* Mouse events */ SDL_MOUSEMOTION = 0x400, /**< Mouse moved */ SDL_MOUSEBUTTONDOWN, /**< Mouse button pressed */ SDL_MOUSEBUTTONUP, /**< Mouse button released */ SDL_MOUSEWHEEL, /**< Mouse wheel motion */ /* Joystick events */ SDL_JOYAXISMOTION = 0x600, /**< Joystick axis motion */ SDL_JOYBALLMOTION, /**< Joystick trackball motion */ SDL_JOYHATMOTION, /**< Joystick hat position change */ SDL_JOYBUTTONDOWN, /**< Joystick button pressed */ SDL_JOYBUTTONUP, /**< Joystick button released */ SDL_JOYDEVICEADDED, /**< A new joystick has been inserted into the system */ SDL_JOYDEVICEREMOVED, /**< An opened joystick has been removed */ /* Game controller events */ SDL_CONTROLLERAXISMOTION = 0x650, /**< Game controller axis motion */ SDL_CONTROLLERBUTTONDOWN, /**< Game controller button pressed */ SDL_CONTROLLERBUTTONUP, /**< Game controller button released */ SDL_CONTROLLERDEVICEADDED, /**< A new Game controller has been inserted into the system */ SDL_CONTROLLERDEVICEREMOVED, /**< An opened Game controller has been removed */ SDL_CONTROLLERDEVICEREMAPPED, /**< The controller mapping was updated */ /* Touch events */ SDL_FINGERDOWN = 0x700, SDL_FINGERUP, SDL_FINGERMOTION, /* Gesture events */ SDL_DOLLARGESTURE = 0x800, SDL_DOLLARRECORD, SDL_MULTIGESTURE, /* Clipboard events */ SDL_CLIPBOARDUPDATE = 0x900, /**< The clipboard changed */ /* Drag and drop events */ SDL_DROPFILE = 0x1000, /**< The system requests a file open */ /* Render events */ SDL_RENDER_TARGETS_RESET = 0x2000, /**< The render targets have been reset */ /** Events ::SDL_USEREVENT through ::SDL_LASTEVENT are for your use, * and should be allocated with SDL_RegisterEvents() */ SDL_USEREVENT = 0x8000, /** * This last event is only for bounding internal arrays */ SDL_LASTEVENT = 0xFFFF } SDL_EventType;
Ví dụ demo
#include <stdio.h> #include <SDL.h> #undef main int main() { //initializes the subsystems if (SDL_Init(SDL_INIT_EVERYTHING) < 0) { printf("Unable to initialize SDL %s\n", SDL_GetError()); return -1; } SDL_Window* window = NULL; bool isRunning = true; SDL_Event mainEvent; //Create window window = SDL_CreateWindow("Stdio.vn - SDL", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 640, SDL_WINDOW_SHOWN); if (window == NULL) { printf("Could not create window %s", SDL_GetError()); return -1; } while (isRunning) { while (SDL_PollEvent(&mainEvent)) { switch (mainEvent.type) { //User - requested quit case SDL_QUIT: { isRunning = false; break; } //Mouse button pressed case SDL_MOUSEBUTTONDOWN: { if (mainEvent.button.button == SDL_BUTTON_LEFT) { printf("Left Mouse Clicked\n"); } else if (mainEvent.button.button == SDL_BUTTON_RIGHT) { printf("Right Mouse Clicked\n"); } break; } //Mouse button released case SDL_MOUSEBUTTONUP: { if (mainEvent.button.button == SDL_BUTTON_LEFT) { printf("Left Mouse Released\n"); } else if (mainEvent.button.button == SDL_BUTTON_RIGHT) { printf("Right Mouse Released\n"); } break; } //Mouse moved case SDL_MOUSEMOTION: { printf("Current Position Mouse: (%d, %d)\n", mainEvent.motion.x, mainEvent.motion.y); break; } //Mouse wheel motion case SDL_MOUSEWHEEL: { printf("Mouse Wheel Motion\n"); break; } //Key pressed case SDL_KEYDOWN: { printf("%c released\n", mainEvent.key.keysym.sym); break; } //Key released case SDL_KEYUP: { printf("%c pressed\n", mainEvent.key.keysym.sym); break; } default: break; } } } //Destroy a window. SDL_DestroyWindow(window); //cleans up all initialized subsystems SDL_Quit(); return 0; }