Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 43 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
43
Dung lượng
273 KB
Nội dung
114 CHAPTER 4 printf("Press ’Q’ to quit.\n"); /* Start the event loop. Keep reading events until there is an error, or the user presses a mouse button. */ while (SDL_WaitEvent(&event) != 0) { SDL_keysym keysym; /* SDL_WaitEvent has filled in our event structure with the next event. We check its type field to find out what happened. */ switch (event.type) { case SDL_KEYDOWN: printf("Key pressed. "); keysym = event.key.keysym; printf("SDL keysym is %i. ", keysym.sym); printf("(%s) ", SDL_GetKeyName(keysym.sym)); /* Report the left shift modifier. */ if (event.key.keysym.mod & KMOD_LSHIFT) printf("Left Shift is down.\n"); else printf("Left Shift is up.\n"); /* Did the user press Q? */ if (keysym.sym == SDLK_q) { printf("’Q’ pressed, exiting.\n"); exit(0); } break; case SDL_KEYUP: printf("Key released. "); printf("SDL keysym is %i. ", keysym.sym); printf("(%s) ", SDL_GetKeyName(keysym.sym)); if (event.key.keysym.mod & KMOD_LSHIFT) printf("Left Shift is down.\n"); else printf("Left Shift is up.\n"); MASTERING SDL 115 break; case SDL_QUIT: printf("Quit event. Bye.\n"); exit(0); } } return 0; } It is important to note that a keystroke generates only one event, regardless of how long a key is held down. Games generally use the keyboard as a set of control buttons, not as character input devices, and so the normal key repeat feature is most often of no use to them. However, you can enable key repeat with the SDL EnableKeyRepeat function. This might be useful for implementing text fields in dialog boxes, for instance. Function SDL EnableKeyRepeat(delay, rate) Synopsis Enables key repeating. This is usually disabled for games, but it has its uses and is almost always enabled for normal typing. Parameters delay—Milliseconds to wait after a key is initially pressed before repeating its event. A delay of 0 disables key repeating. A typical value is somewhere in the range of 250–500. rate—Milliseconds between repeats. A typical value is 30. As with the mouse, it is possible to read the keyboard’s state directly, bypassing the event interface. There is no function for directly obtaining the state of an individual key, but a program can obtain a snapshot of the entire keyboard in the form of an array. The SDL GetKeyState function returns a pointer to SDL’s internal keyboard state array, which is indexed with the SDLK keysym constants. Each entry in the array is a simple Uint8 flag indicating whether that key is currently down. Remember to call SDL PumpEvents before reading the keyboard’s state array, or the array’s data will not be valid. 116 CHAPTER 4 Function SDL GetKeyState(numkeys) Synopsis Retrieves a snapshot of the entire keyboard as an array. Each entry in the array corresponds to one of the SDLK name constants, where 1 means that the corresponding key is currently down and 0 means that the key is currently up. This array pointer will never change during the course of a program; it’s one of SDL’s internal data structures. Be sure to call SDL PumpEvents periodically, or the keyboard state data will never change. Returns Pointer to SDL’s keyboard state array. Stores the size of the array in numkeys. Parameters numkeys—Pointer to an integer to receive the size of the key array. Most programs don’t care about this and just pass NULL. Processing Joystick Events SDL provides a complete interface for joystick management. A modern game can no longer assume that the player will use a traditional two-button, two-axis joystick; many joysticks are equipped with programmable buttons, hat switches, trackballs, and throttles. In addition, some serious gamers like to use more than one joystick at once. Aside from physical limitations (such as the number of ports on a computer or the availability of low-level support from the kernel), SDL can manage any number of joysticks with any number of additional buttons, hats, and trackballs. If the Linux kernel recognizes a device as a joystick, so will SDL. Joystick axes (directional controls) produce simple linear values indicating their positions. SDL reports these on a scale from −32, 768 to 32, 767. For instance, the leftmost position of a joystick would produce a value of −32, 768 on axis 0, and the rightmost position would produce 32, 767. SDL provides the SDL JOYAXISMOTION event type for joystick motion. Hat switches (small directional controls on top of a joystick) are sometimes represented as additional axes, but they are more frequently reported with a separate SDL JOYHATMOTION event type. Hat positions are reported with respect MASTERING SDL 117 to the four compass directions and the four diagonals. These positions are numbered clockwise, starting with 1 as North. The center position is 0. 3 Warning Before you assume that your joystick code isn’t working, try running the test programs included with the Linux kernel’s joystick driver. If the kernel doesn’t know how to deal with your joystick, SDL won’t either, and your code will not work. The SDL joystick event interface works as you might expect: it generates an event each time the value of a joystick axis or button changes. It also includes functions for polling the state of a joystick directly. The SDL joystick interface is fairly simple, so we won’t spend much more time on it. Code Listing 4–10 (joystick-events-sdl.c) /* Example of simple joystick input with SDL. */ #include <SDL/SDL.h> #include <stdlib.h> #include <stdio.h> int main() { SDL_Event event; SDL_Joystick *js; int num_js, i, quit_flag; /* Initialize SDL’s joystick and video subsystems. */ if (SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_VIDEO) != 0) { printf("Error: %s\n", SDL_GetError()); return 1; } 3 More recent versions of SDL allow you to access joystick hat positions with a simple bitmask instead of numbers. This change is in effect as of SDL 1.2. 118 CHAPTER 4 atexit(SDL_Quit); /* Create a 256x256 window so we can collect input events. */ if (SDL_SetVideoMode(256, 256, 16, 0) == NULL) { printf("Error: %s\n", SDL_GetError()); return 1; } /* Find out how many joysticks are available. */ num_js = SDL_NumJoysticks(); printf("SDL recognizes %i joystick(s) on this system.\n", num_js); if (num_js == 0) { printf("No joysticks were detected.\n"); return 1; } /* Print out information about each joystick. */ for (i = 0; i < num_js; i++) { /* Open the joystick. */ js = SDL_JoystickOpen(i); if (js == NULL) { printf("Unable to open joystick %i.\n", i); } else { printf("Joystick %i\n", i); printf("\tName: %s\n", SDL_JoystickName(i)); printf("\tAxes: %i\n", SDL_JoystickNumAxes(js)); printf("\tTrackballs: %i\n", SDL_JoystickNumBalls(js)); printf("\tButtons: %i\n", SDL_JoystickNumButtons(js)); /* Close the joystick. */ SDL_JoystickClose(js); } } MASTERING SDL 119 /* We’ll use the first joystick for the demonstration. */ js = SDL_JoystickOpen(0); if (js == NULL) { printf("Unable to open joystick: %s\n", SDL_GetError()); } /* Loop until the user presses Q. */ quit_flag = 0; while (SDL_WaitEvent(&event) != 0 && quit_flag == 0) { switch (event.type) { case SDL_KEYDOWN: if (event.key.keysym.sym == SDLK_q) { printf("Q pressed. Exiting.\n"); quit_flag = 1; } break; /* This event is generated when an axis on an open joystick is moved. Most joysticks have two axes, X and Y (which will be reported as axes 0 and 1). */ case SDL_JOYAXISMOTION: printf("Joystick %i, axis %i movement to %i\n", event.jaxis.which, event.jaxis.axis, event.jaxis.value); break; /* The SDL_JOYBUTTONUP and SDL_JOYBUTTONDOWN events are generated when the state of a joystick button changes. */ case SDL_JOYBUTTONUP: /* fall through to SDL_JOYBUTTONDOWN */ case SDL_JOYBUTTONDOWN: printf("Joystick %i button %i: %i\n", event.jbutton.which, event.jbutton.button, event.jbutton.state); 120 CHAPTER 4 break; } } /* Close the joystick. */ SDL_JoystickClose(js); return 0; } Multithreading with SDL Multithreading is the ability of a program to execute multiple parts of itself simultaneously in the same address space. This feature can be useful to game developers; for instance, a programmer might elect to use separate threads for video processing and music playback so that they can run simultaneously. When properly used, multithreading can simplify game programming and make the end result smoother and more efficient. Threads can significantly boost performance on multiprocessor systems, since each thread can run on a separate processor (this is up to the operating system, though). Several different thread programming libraries exist for the mainstream operating systems. Windows and Linux use completely different threading interfaces, and Solaris (Sun Microsystems’ flavor of UNIX) supports both its own threading API and the one that Linux uses. SDL solves this cross-platform inconsistency with its own set of portable threading functions. Threads are essentially asynchronous procedure calls that return immediately but continue running in the background. An SDL thread entry point is simply a pointer to a void function that takes a void pointer as a parameter. Threads have their own stacks, but they share the application’s global variables, heap, code, and file descriptors. You can start new threads with the SDL CreateThread function. SDL CreateThread returns a pointer to a thread handle (of type SDL Thread) that can be used to interact with the new thread. SDL provides functions for terminating threads (SDL KillThread) and for waiting for them to finish executing (SDL WaitThread). Waiting for a thread to finish is sometimes called joining the thread. MASTERING SDL 121 Function SDL CreateThread(func, data) Synopsis Starts func in a separate SDL thread, with data as an argument. Makes whatever low-level threading calls are appropriate for the given platform (pthread create, in the case of Linux). Returns Pointer to an SDL Thread structure that represents the newly created process. Parameters func—Entry point for the new thread. This function should take one void * argument and return an integer. data—void * to be passed verbatim to func. This is for your own use, and it’s perfectly safe to pass NULL. Function SDL KillThread(id) Synopsis Terminates an SDL thread immediately. If the thread could possibly be doing anything important, it might be a good idea to ask it to end itself rather than just terminating it. Parameters id—Pointer to the SDL Thread structure that identifies the thread you wish to kill. Function SDL WaitThread(id) Synopsis Waits for an SDL thread to terminate. This is also known as joining a thread. Parameters id—Pointer to the SDL Thread structure that identifies the thread you wish to join. Multithreaded programming requires a bit of extra caution. What happens if two threads attempt to modify the same global variable at the same time? You have no way of telling which thread will succeed, which can lead to strange and elusive bugs. If there is any chance that two threads will attempt to modify an important data structure simultaneously, it is a good idea to protect the 122 CHAPTER 4 structure with a mutex (mutual exclusion flag). A mutex is simply a flag that indicates whether a structure is currently in use. Whenever a thread needs to access a mutex-protected structure, it should set (lock) the mutex first. If another thread needs to access the structure, it must wait until the mutex is unlocked. This can prevent threads from colliding, but only if they respect the mutex. SDL’s mutexes are advisory in nature; they do not physically block access. Function SDL CreateMutex Synopsis Creates a mutex. Returns Pointer to the newly created mutex. This mutex is initially unlocked. Function SDL DestroyMutex Synopsis Frees a mutex. Parameters mutex—Pointer to the mutex to destroy. Function SDL mutexP(mutex) Synopsis Locks a mutex. If the mutex is already locked, waits until it is unlocked before locking it again. If you dislike the traditional P/V naming, you can access this function with the SDL LockMutex macro. Parameters mutex—Pointer to the mutex to lock. Function SDL mutexV(mutex) Synopsis Unlocks a mutex. There should always be a SDL mutexV call for every SDL mutexP call. If you dislike the traditional P/V naming, you can access this function with the SDL UnlockMutex macro. Parameters mutex—Pointer to the mutex to unlock. The example that follows creates three threads that increment a global variable and print out its value. A mutex is used to synchronize access to the variable, so that multiple threads can modify the variable without conflicts. MASTERING SDL 123 Code Listing 4–11 (sdl-threading.c) /* Example of SDL’s portable threading API. */ #include <stdio.h> #include <stdlib.h> #include <SDL/SDL.h> /* We must include SDL_thread.h separately. */ #include <SDL/SDL_thread.h> static int counter = 0; SDL_mutex *counter_mutex; /* The three threads will run until this flag is set. */ static int exit_flag = 0; /* This function is a thread entry point. */ int ThreadEntryPoint(void *data) { char *threadname; /* Anything can be passed as thread data. We will use it as a thread name. */ threadname = (char *) data; /* Loop until main() sets the exit flag. */ while (exit_flag == 0) { printf("This is %s! ", threadname); /* Get a lock on the counter variable. */ SDL_mutexP(counter_mutex); /* We can now safely modify the counter. */ printf("The counter is currently %i\n", counter); counter++; /* Release the lock on the counter variable. */ SDL_mutexV(counter_mutex); /* Delay for a random amount of time. */ SDL_Delay(rand() % 3000); [...]... will consume nearly 90 kilobytes of storage, or twice that for stereo Game 126 CHAPTER 4 11025 Hz 22050 Hz 44 100 Hz Mono 8 bit 16 bit 11,025 22,050 22,050 44 ,100 44 ,100 88,200 Stereo 8 bit 16 bit 22,050 44 ,100 44 ,100 88,200 88,200 176 ,40 0 Table 4 1: Storage consumed by various sound formats (in bytes per second) programmers must decide on a trade-off between sound quality and the amount of disk space... 22,050 or 44 ,100 Hz Samples can be represented as signed or unsigned numbers A 16-bit sample can obviously express the intensity of a sound with much greater precision than an 8-bit sample, but it involves twice as much data At 44 ,100 Hz with 16-bit samples, one second of sound data will consume nearly 90 kilobytes of storage, or twice that for stereo Game 126 CHAPTER 4 11025 Hz 22050 Hz 44 100 Hz Mono... 640 x480, 16 bit window with support for OpenGL rendering Unfortunately we won’t know whether this is hardware accelerated */ if (SDL_SetVideoMode( 640 , 48 0, 16, SDL_OPENGL) == NULL) { printf("Error: %s\n", SDL_GetError()); return 1; } /* Set a window title */ MASTERING SDL 143 SDL_WM_SetCaption("OpenGL with SDL!", "OpenGL"); /* We can now use any OpenGL rendering commands */ glViewport(80, 0, 48 0, 48 0);... the sound card supplied with PCM samples This is a bit of a trick If you want 44 .1 kilohertz (kHz) sound (the quality of sound stored on audio CDs), you must supply the sound card with 44 ,100 samples per second, per channel With 16-bit samples and two channels (stereo), this comes out to 176 ,40 0 bytes of sound data (see Table 4) ! In addition, timing is critical Any lapse of data will result in a noticeable... initrandom() { seed = time(NULL); } static unsigned int getrandom() { Sint32 p1 = 1103515 245 ; Sint32 p2 = 12 345 ; seed = (seed * p1 + p2) % 2 147 483 647 ; return (unsigned) seed / 3; } /* Sets up the starry background by assigning random tiles This should be called after LoadGameData() */ void InitBackground() { int x, y; 149 ... OpenGL with SDL The OpenGL library is the de facto standard for accelerated 3D graphics under Linux Designed by Silicon Graphics as a programming interface for its high-performance graphics workstations, OpenGL has been adopted as the MASTERING SDL 141 preferred 3D API on most major platforms It is used in many games as well as in professional engineering applications Microsoft’s Direct3D is OpenGL’s... check that the returned value is not NULL SDL is now initialized with OpenGL support, and we can now use any of OpenGL’s rendering commands (the semantics of which are beyond 144 CHAPTER 4 this discussion) to draw on the back buffer .4 The SDL GL SwapBuffers function serves the same purpose as SDL Flip does for 2D graphics: it displays OpenGL’s back buffer to the screen, allowing for smooth and flicker-free... this chapter’s files in the pw-ch4/ subdirectory of the listings archive, which is available on the book’s Web site Penguin Warrior was conceived to illustrate several of the topics in this book, and as such it is not the product of a formal design process (which is a 146 CHAPTER 4 substantial and complex subject worthy of its own book) Its intent is to demonstrate game programming, not design The game... Mesa 3D graphics library (http://www.mesa3d.org) or another Linux OpenGL implementation Compile and link the program with the -I/usr/X11R6/include -L/usr/X11R6/lib flags to ensure that the correct 142 CHAPTER 4 header files and libraries can be located Furthermore, you need to compile your copy of SDL with OpenGL video support Code Listing 4 13 (opengl-sdl.c) /* Example of OpenGL rendering through SDL... with atexit SDL Quit is especially important for programs that use OpenGL; Linux s OpenGL support is flaky enough as it is, and forgetting to shut it down properly could cause serious problems Next we use the SDL GL SetAttribute functions to ask for a double buffer and a 565 hicolor pixel weighting Finally, we create a 640 by 48 0, 16-bit output window We don’t care about the video surface that SDL SetVideoMode . bit 16 bit 8 bit 16 bit 11025 Hz 11,025 22,050 22,050 44 ,100 22050 Hz 22,050 44 ,100 44 ,100 88,200 44 100 Hz 44 ,100 88,200 88,200 176 ,40 0 Table 4 1: Storage consumed by various sound formats (in bytes. PCM samples. This is a bit of a trick. If you want 44 .1 kilohertz (kHz) sound (the quality of sound stored on audio CDs), you must supply the sound card with 44 ,100 samples per second, per channel. With. involves twice as much data. At 44 ,100 Hz with 16-bit samples, one second of sound data will consume nearly 90 kilobytes of storage, or twice that for stereo. Game 126 CHAPTER 4 Mono Stereo 8 bit 16 bit