1 / 27

Linux game programming

Linux game programming. An introduction to the use of interval timers and asynchronous input notifications. Our prior animation demo. We achieved the illusion of “smooth” and “flicker-free” animation, by synchronizing drawing-operations with Vertical Retrace

amil
Download Presentation

Linux game programming

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Linux game programming An introduction to the use of interval timers and asynchronous input notifications

  2. Our prior animation demo • We achieved the illusion of “smooth” and “flicker-free” animation, by synchronizing drawing-operations with Vertical Retrace • But more is needed in a game that’s “fun” • Some deficiencies in our ‘animate1’ demo: • Ball movements tied to Vertical Retrace • The viewer lacked any real-time control • How can we overcome these limitations?

  3. Decoupling ‘move’ from ‘draw’ • Our program-loop had used logic like this: do { vsync(); // delay until start of the next retrace hide_ball(); // “erase” the ball move_ball(); // adjust its location show_ball(); // “redraw” the ball --count; // decrement a counter } while ( count > 0 ); • So ball movement is delayed by vertical retraces

  4. Linux provides ‘interval timers’ • #include <sys/time.h> • struct itimerval itval, itold; • itval.it_value.tv_sec = 2; • itval.it_value.tv_usec = 0; • itval.it_interval.tv_sec = 0; • itval.it_interval.tv_usec = 10000; • setitimer( ITIMER_REAL, &itval, &itold ); • (See the ‘man’ page for additional details)

  5. structs timeval and itimerval struct timeval tv_sec tv_usec struct itimerval tv_sec tv_usec it_interval tv_sec tv_usec it_value It_itimerval = next delay value, it_timeval = current delay value

  6. SIGALRM • When timer “expires” our application gets notified, by being sent a signal from Linux • Normally an application gets terminated if the SIGALRM signal is delivered to it • But we can alter that default behavior, by installing a ‘signal-handler’ that we design • We can ‘move-the-ball’ when SIGALRM is received, regardless of Vertical Retrace

  7. Our signal-handler void on_alarm( int signum ) { // modify these global variables ball_xcoordinate += xincrement; ball_ycoordinate += yincrement; } // The ‘signal()’ function “installs” our handler signal( SIGALRM, on_alarm);

  8. Main program-loop “revised” • We can now omit ball-movement in main loop: do { vsync(); // delay until start of the next retrace hide_ball(); // “erase” the old ball oldx = newx; oldy = newy; // remember new position show_ball(); // “redraw” the new ball --count; // decrement a counter } while ( count > 0 ); • Ball-movement is managed by ‘signal-handler’

  9. ‘draw’ versus ‘move’ Program-loop Signal-handler The code in this signal-handler takes care of all movements whenever it’s time for them to occur The code in this loop handles all the actual re-drawing in sync with the vertical retrace The Operating System takes care of switching the CPU between these two separate threads-of-control

  10. Giving the user control • Linux supports “asynchronous” terminal i/o • We can reprogram the terminal console so a SIGIO signal will be sent to our program whenever the user decides to press a key • And we can install a ‘signal-handler’ of our own design that executes if SIGIO arrives • This will allow a user to “control” our game

  11. The ‘tty’ interface • ‘tty’ is an acronyn for ‘TeleTYpe’ terminal • Such devices have a keyboard and screen • Behavior emulates technology from 1950s • Usually a tty operates in ‘canonical’ mode: • Each user-keystroke is ‘echoed’ to screen • Some editing is allowed (e.g., backspace) • The keyboard-input is internally buffered • The <ENTER>-key signals an ‘end-of-line’ • Programs receive input one-line-at-a-time

  12. ‘tty’ customization • Sometimes canonical mode isn’t suitable (an example: animated computer games) • The terminal’s behavior can be modified! • UNIX provides a convenient interface: • #include <termios.h> • struct termios tty; • int tcgetattr( int fd, struct termios *tty ); • int tcsetattr( int fd, int flag, struct termios *tty );

  13. How does the ‘tty’ work? application User space tty_driver c_lflag Kernel space input handling c_iflag c_cc output handling c_oflag SOFTWARE struct tty { c_iflag; c_oflag; c_cflag; c_lflag; c_line; c_cc[ ]; }; terminal_driver c_cflag HARDWARE TeleTYpe display device

  14. The ‘c_lflag’ field • This field is just an array of flag bits • Individual bits have symbolic names • Names conform to a POSIX standard • Linux names match other UNIX’s names • Though actual symbol values may differ • Your C/C++ program should use: #include <termios.h> for portability to other UNIX environments

  15. ICANON and ECHO • Normally the ‘c_lflag’ field has these set • They can be cleared using bitwise logic: tty.c_lflag &= ~ECHO; // inhibit echo tty.c_lflag &= ~ICANON; // no buffering

  16. The ‘c_cc[ ]’ array • ‘struct termios’ objects include an array • The array-indices have symbolic names • Symbol-names are standardized in UNIX • Array entries are ‘tty’ operating parameters • Two useful ones for our purposes are: tty.c_cc[ VMIN ] and tty.c_cc[ VTIME ]

  17. How to setup ‘raw’ terminal-mode • Step 1: Use ‘tcgetattr()’ to get a copy of the current tty’s ‘struct termios’ settings • Step 2: Make a working copy of that object • Step 3: Modify its flags and control-codes • Step 4: Use ‘tcsetattr()’ to install changes • Step 5: Perform desired ‘raw’ mode input • Step 6: Use ‘tcsetattr()’ to restore the terminal to its original default settings

  18. ‘raw’ mode needs four changes • tty.c_cc[ VMIN ] = 1; • so the ‘read()’ function will return as soon as at least one new input-character is available • tty.c_cc[ VTIME ] = 0; • so there will be no time-delay after each new key pressed until the ‘read()’ function returns • tty.c_lflag &= ~ECHO; // no echoing • tty.c_lflag &= ~ICANON; // no buffering

  19. Demo program: ‘rawtty.cpp’ • This program may soon prove useful • It shows the keyboard scancode values • It demonstrates ‘noncanonical’ tty mode • It clears the ISIG bit (in ‘c_lflags’ field) • This prevents <CONTROL>-C from being used to abort the program: the user must ‘quit’ by hitting the <ESCAPE>-key; so default terminal-settings will get reinstalled

  20. ‘Noncanonical’ terminal i/o • We’ve now learned how to reprogram the terminal to allow “raw” keyboard input #include <termios.h> struct termios tty; tcgetattr( 0, &tty ); // get tty settings tty.c_lflag &= ~( ICANON | ECHO | ISIG ); tty.c_cc[ VMIN ] = 1; tty.c_cc[ VTIME ] = 0; tcsetattr( 0, TCSAFLUSH, &tty ); // install

  21. Handling a key-press • Here’s a ‘simple’ signal-handler that lets a user decide to terminate our program (by hitting the <ESCAPE>-key) instead of the program itself deciding to quit when a counter reaches zero void on_input( int signum ) { int inch = 0; read( 0, &inch, 4 ); if ( inch == ESCAPE_KEY ) done = 1; }

  22. Enabling ‘asynchronous’ I/O • Now we need to install our signal-handler, specify which program will receive SIGIO, then enable the delivery of these signals • signal( SIGIO, on_input ); • fcntl( 0, F_SETOWN, getpid() ); • int flagval = fcntl( 0, F_GETFL, NULL ); • flagval |= O_ASYNC; // turn on flag-bit • fcntl( 0, F_SETFL, flagval );

  23. ‘animate2.cpp’ main_loop on_input on_alarm Handles redrawing whenever Vertical Retrace Is active Handles User’s keystrokes (SIGIO) Handles Timer’s expirations (SIGALRM) The Signal-Handlers Waiting and Drawing

  24. Program-loop revised again done = 0; do { oldx = xloc, oldy = yloc; // remember these draw_ball(); // use current location vsync(); // await next retrace hide_ball(); // erase previous ball } while ( !done ); // ‘xloc’, ‘yloc’, and ‘done’ get changed by handlers

  25. Enhancment: more user-control • In ‘pong’ game the user moves a ‘paddle’ • The paddle can only be moved left or right • Lower wall of the playing-court is removed • Ball will “escape” unless it hits the paddle • Keyboard has left and right “arrow keys” • Our input signal-handler could “move” the paddle whenever a user hits an arrow-key

  26. In-class exercise #1 • Before you remove the game-court’s lower wall, see if you can implement the paddle-movements (left or right) in response to a user’s hitting the left-arrow or right-arrow • You will need to investigate the numerical code-sequence that Linux generates when a user hits one of these arrow-keys • Our ‘rawtty.cpp’ application may be useful!

  27. In-class exercise #2 • For brevity, our ‘animate2’ demo-program removed the capability of users controlling the speed of the ball-movements (with an argument supplied on the command-line) • Can you devise a way for users to exert “run-time” control over the ball’s speed by pressing keys while the program is running (for example, the ‘+’ key and the ‘-’ key)?

More Related