pong game using ncurses
bounce2d2.c
1 /* 2 * bounce2d 1.0 3 * bounce a character (default is 'o') around the screen 4 * defined by some parameters 5 * user input: s slow down x component, S: slow y component 6 * f speed up x component, F: speed y component 7 * Q quit 8 * blocks on read, but timer tick sends SIGALRM caught by ball_move 9 * build: cc bounce2d.c set_ticker.c -lcurses -o bounce2d 10 */ 11 #include <curses.h> 12 #include <string.h> 13 #include <signal.h> 14 #include "bounce.h" 15 16 struct ppball the_ball; 17 18 /** the main loop **/ 19 20 //int flap_pos = RIGHT_EDGE / 2 - LEFT_EDGE; 21 int flap_pos =0; 22 int old_pos = 0; 23 void set_up(); 24 void wrap_up(); 25 void move_flap(); 26 int bounce_or_lose(struct ppball *); 27 28 int main() 29 { 30 // printf("LEFT_EDGE: %d, RIGHT_EDGE: %d\n",LEFT_EDGE, RIGHT_EDGE); 31 // printf("TOP_EDGE: %d, BOT_EDGE: %d\n",TOP_ROW, BOT_ROW); 32 int c; 33 set_up(); 34 while (((c = getchar())) != 'Q') 35 { 36 if (c == 'f') the_ball.x_ttm--; 37 else if (c == 's') the_ball.x_ttm++; 38 else if (c == 'F') the_ball.y_ttm--; 39 else if (c == 'S') the_ball.y_ttm++; 40 else if (c == 'a'){ 41 if (flap_pos > LEFT_EDGE){ 42 old_pos = flap_pos; 43 flap_pos -= FLAP_SPEED; 44 move_flap(); 45 } 46 }else if (c == 'd'){ 47 if (flap_pos + (int)strlen(FLAP) < RIGHT_EDGE){ 48 old_pos = flap_pos; 49 flap_pos += FLAP_SPEED; 50 move_flap(); 51 } 52 } 53 } 54 wrap_up(); 55 // printf("LINES: %d, COLS: %d\n",LINES, COLS); 56 return 0; 57 } 58 59 void set_up() 60 /* 61 * init structure and other stuff 62 */ 63 { 64 void ball_move(int); 65 the_ball.y_pos = Y_INIT; 66 the_ball.x_pos = X_INIT; 67 the_ball.y_ttg = the_ball.y_ttm = Y_TIM; 68 the_ball.x_ttg = the_ball.x_ttm = X_TIM; 69 the_ball.y_dir = 1; 70 the_ball.x_dir = 1; 71 the_ball.symbol = DFL_SYMBOL; 72 the_ball.x_moved = the_ball.y_moved = false; 73 74 initscr(); 75 noecho(); 76 crmode(); 77 78 signal(SIGINT, SIG_IGN); 79 mvaddch(the_ball.y_pos, the_ball.x_pos, the_ball.symbol); 80 move_flap(); 81 signal(SIGALRM, ball_move); 82 set_ticker(1000 / TICKS_PER_SEC); 83 } 84 85 void wrap_up() 86 { 87 set_ticker(0); 88 endwin(); 89 } 90 91 void move_flap() 92 { 93 move(BOT_ROW + 1, old_pos); 94 addstr(FLAP); //FLAP is blank. Here it is used to clear its old existence. 95 96 move(BOT_ROW + 1, flap_pos); 97 standout(); 98 addstr(FLAP); 99 standend(); 100 refresh(); 101 } 102 103 void ball_move(int signum) 104 { 105 int y_cur, x_cur, moved; 106 107 signal(SIGALRM, SIG_IGN); 108 x_cur = the_ball.x_pos; 109 y_cur = the_ball.y_pos; 110 moved = 0; 111 112 if (the_ball.y_ttm > 0 && the_ball.y_ttg-- == 1){ 113 the_ball.y_pos += the_ball.y_dir; /* move */ 114 the_ball.y_ttg = the_ball.y_ttm; /* reset */ 115 the_ball.y_moved = 1; 116 moved = 1; 117 } 118 119 if (the_ball.x_ttm > 0 && the_ball.x_ttg-- == 1){ 120 the_ball.x_pos += the_ball.x_dir; /* move */ 121 the_ball.x_ttg = the_ball.x_ttm; /* reset */ 122 the_ball.x_moved = 1; 123 moved = 1; 124 } 125 126 if (moved){ 127 mvaddch(y_cur, x_cur, BLANK); 128 mvaddch(y_cur, x_cur, BLANK); 129 mvaddch(the_ball.y_pos, the_ball.x_pos, the_ball.symbol); 130 if(bounce_or_lose(&the_ball)){ 131 // signal(SIGALRM, SIG_IGN); 132 move(LINES / 2, COLS / 2); 133 addstr("GAME OVER"); 134 refresh(); 135 return; 136 } 137 move(LINES-1, COLS-1); 138 if (the_ball.x_moved && the_ball.y_moved){ 139 refresh(); 140 the_ball.x_moved = the_ball.y_moved = false; /* reset */ 141 } 142 } 143 signal(SIGALRM, ball_move); 144 } 145 146 int bounce_or_lose(struct ppball *bp) 147 /* 148 * 1 lose 149 * 0 not lose 150 */ 151 { 152 int return_val = 0; 153 154 if (bp->y_pos == TOP_ROW){ 155 bp->y_dir = 1; 156 }else if (bp->y_pos == BOT_ROW){ 157 bp->y_dir = -1; 158 if (!(bp->x_pos >= flap_pos && bp->x_pos <= (flap_pos + (int)strlen(FLAP)))){ 159 return_val = 1; 160 } 161 } 162 163 if (bp->x_pos == LEFT_EDGE){ 164 bp->x_dir = 1; 165 }else if (bp->x_pos == RIGHT_EDGE){ 166 bp->x_dir = -1; 167 } 168 return return_val; 169 }
bounce.h
1 #define BLANK ' ' 2 #define DFL_SYMBOL 'o' 3 #define TOP_ROW 0 4 #define BOT_ROW 20 5 #define LEFT_EDGE 0 6 #define RIGHT_EDGE 81 7 #define X_INIT 3 8 #define Y_INIT 5 9 #define TICKS_PER_SEC 50 10 #define Y_TIM 8 11 #define X_TIM 8 12 #define FLAP " " 13 //#define FLAP_LEN 21 14 #define FLAP_SPEED 1 15 16 struct ppball { 17 int x_ttg; // x 轴下次重画还要等待多少个计时器 18 int y_ttg; // y 轴下次重画还要等待多少个计时器 19 int x_ttm; // x 轴移动需要等待的信号间隔 20 int y_ttm; // y 轴移动絮叨等待的信号间隔 21 int y_pos; 22 int x_pos; 23 int y_dir; 24 int x_dir; 25 int x_moved; 26 int y_moved; 27 char symbol; 28 };
set_ticker.c
1 #include <stdio.h> 2 #include <sys/time.h> 3 #include <signal.h> 4 #include <stdlib.h> 5 6 /* 7 * set_ticker.c 8 * set_ticker( number_of_milliseconds ) 9 * arranges for the interval timer to issue 10 * SIGALRM's at regular intervals 11 * returns -1 on error, 0 for ok 12 * 13 * arg in milliseconds, converted into micro seoncds 14 */ 15 16 17 set_ticker( n_msecs ) 18 { 19 struct itimerval new_timeset; 20 long n_sec, n_usecs; 21 22 n_sec = n_msecs / 1000 ; 23 n_usecs = ( n_msecs % 1000 ) * 1000L ; 24 25 new_timeset.it_interval.tv_sec = n_sec; /* set reload */ 26 new_timeset.it_interval.tv_usec = n_usecs; /* new ticker value */ 27 new_timeset.it_value.tv_sec = n_sec ; /* store this */ 28 new_timeset.it_value.tv_usec = n_usecs ; /* and this */ 29 30 return setitimer(ITIMER_REAL, &new_timeset, NULL); 31 }
====================================================================================================
A different version:
1 #include <curses.h> 2 #include <sys/time.h> 3 #include <signal.h> 4 5 #define RIGHT COLS-1 /*球所能到达的当前屏幕最大水平范围*/ 6 #define BOTTOM LINES-1 /*球所能到达的当前屏幕最大垂直范围*/ 7 #define BOARD_LENGTH 10 /*挡板长度*/ 8 #define LEFT 0 /*当前屏幕的最左边*/ 9 #define TOP 0 /*当前屏幕的最上边*/ 10 char BALL= 'O'; /*球的形状*/ 11 char BLANK= ' '; /*覆盖球走过的轨迹*/ 12 13 14 int left_board; /*挡板左侧坐标*/ 15 int right_board; /*挡板右侧坐标*/ 16 int is_lose=0; 17 18 19 int hdir; /*控制球水平运动的变量*/ 20 int vdir; /*控制球垂直运动的变量*/ 21 int pos_X; /*球的横坐标*/ 22 int pos_Y; /*球的纵坐标*/ 23 24 int delay=100; 25 void moveBall(); 26 void init(); 27 void control(); 28 29 int main() 30 { 31 //初始化 curses 32 initscr(); 33 crmode(); /*中断模式*/ 34 noecho(); /*关闭回显*/ 35 36 move(6,28); 37 attron(A_BOLD); 38 addstr("Welcome to the BallGame!"); 39 move(8,20); 40 attroff(A_BOLD); 41 addstr("Help:"); 42 move(9,23); 43 addstr("'N':Start a new game."); 44 move(10,23); 45 addstr("'Q':Quit game."); 46 move(11,23); 47 addstr("'KEY_LEFT' :Control baffle left shift."); 48 move(12,23); 49 addstr("'KEY_RIGHT':Control baffle right shift."); 50 move(13,23); 51 addstr("'KEY_UP' :Control of the ball speed."); 52 move(14,23); 53 addstr("'KEY_DOWN' :Control of the ball reducer."); 54 int flag=1; 55 char choice; 56 move(16,24); 57 addstr("Please choose your choice!(n/q):"); 58 refresh(); 59 choice=getch(); 60 while(flag){ 61 if(choice=='q'||choice=='Q'||choice=='n'||choice=='N') 62 flag=0; 63 else choice=getch(); 64 } 65 if(choice=='n'||choice=='N'){ /*开始游戏*/ 66 clear(); 67 move(10,25); 68 addstr("BallGame will start! Are you ready?"); 69 refresh(); 70 sleep(3); 71 control(); 72 } 73 else if(choice=='q'||choice=='Q'){ /*退出游戏*/ 74 clear(); 75 move(10,25); 76 addstr("You quit the game successfully!"); 77 refresh(); 78 sleep(3); 79 endwin(); 80 } 81 endwin(); /*结束 curses*/ 82 return 0; 83 } 84 85 void init(){ 86 int i,j; 87 clear(); 88 if(start_color()==OK){ /*改变球和挡板的颜色*/ 89 attron(A_BOLD); /*打开粗体*/ 90 init_pair(1,COLOR_YELLOW,COLOR_BLACK); 91 attron(COLOR_PAIR(1)); 92 } 93 //初始球 94 pos_X =22; /*球初始的横坐标*/ 95 pos_Y = BOTTOM-1; /*球初始的纵坐标*/ 96 //初始化球的运动方向,朝右上方运动 97 hdir=1; 98 vdir=-1; 99 100 //初始挡板 101 left_board=20; 102 right_board=left_board+BOARD_LENGTH; 103 for(i=left_board;i<=right_board;i++){ /*显示挡板*/ 104 move(BOTTOM,i); 105 addch('-'); 106 } 107 108 //初始刷新时间 109 signal(SIGALRM,moveBall); 110 set_ticker(delay); 111 112 keypad(stdscr,TRUE); /*打开 keypad 键盘响应*/ 113 attroff(A_BLINK); /*关闭 A_BLINK 属性*/ 114 115 is_lose=0; 116 move(pos_Y,pos_X); 117 addch(BALL); 118 move(LINES-1, COLS-1); 119 refresh(); 120 usleep(100000); /*睡眠*/ 121 move(LINES-1,COLS-1); 122 refresh(); 123 } 124 125 void moveBall(){ 126 if(is_lose) return; 127 signal(SIGALRM,moveBall); 128 move(pos_Y,pos_X); 129 addch(BLANK); 130 pos_X += hdir; 131 pos_Y += vdir; 132 //改变球的方向时 133 if(pos_X >= RIGHT) { /*当球横坐标大于右边边缘时,球反弹朝左运动*/ 134 hdir = -1; 135 beep(); /*球撞墙时,发出声音*/ 136 } 137 if(pos_X <= LEFT) { /*当球横坐标大于左边边缘时,球反弹朝右运动*/ 138 hdir = 1; 139 beep(); /*球撞墙时,发出声音*/ 140 } 141 if(pos_Y <= TOP) { /*当球纵坐标大于顶部边缘时,球反弹朝下运动*/ 142 vdir = 1; 143 beep(); /*球撞墙时,发出声音*/ 144 } 145 146 //当球在底部的时候进行额外的处理 147 if(pos_Y >= BOTTOM-1){ 148 if(pos_X>=left_board&&pos_X<=right_board) /*球在挡板处*/ 149 vdir=-1; 150 else{ /*球不在挡板处*/ 151 is_lose=1; 152 move(pos_Y,pos_X); 153 addch(BALL); 154 move(LINES-1, COLS-1); 155 refresh(); 156 usleep(delay*1000); /*睡眠*/ 157 move(pos_Y,pos_X); 158 addch(BLANK); 159 pos_X += hdir; 160 pos_Y += vdir; 161 move(pos_Y,pos_X); 162 addch(BALL); 163 move(LINES-1, COLS-1); 164 refresh(); 165 } 166 } 167 //不改变球的方向时 168 move(pos_Y,pos_X); 169 addch(BALL); 170 move(LINES-1, COLS-1); 171 refresh(); 172 } 173 void control(){ 174 init(); 175 int cmd; 176 while (1) 177 { 178 if(!is_lose){ 179 cmd=getch(); 180 if(cmd=='q'||cmd=='Q'||cmd==27) break; //强制退出游戏 181 //挡板左移 182 if(cmd==KEY_LEFT){ 183 if(left_board>0){ 184 move(BOTTOM,right_board); 185 addch(' '); 186 right_board--; 187 left_board--; 188 move(BOTTOM,left_board); 189 addch('-'); 190 move(BOTTOM,RIGHT); 191 refresh(); 192 } 193 } 194 //挡板右移 195 else if(cmd==KEY_RIGHT){ 196 if(right_board<RIGHT){ 197 move(BOTTOM,left_board); 198 addch(' '); 199 right_board++; 200 left_board++; 201 move(BOTTOM,right_board); 202 addch('-'); 203 move(BOTTOM,RIGHT); 204 refresh(); 205 } 206 } 207 //给球加速 208 else if(cmd==KEY_UP){ 209 delay/=2; 210 set_ticker(delay); 211 } 212 //给球减速 213 else if(cmd==KEY_DOWN){ 214 delay*=2; 215 set_ticker(delay); 216 } 217 218 } 219 else{ 220 //输掉球后的处理 221 int flag=1; 222 char choice; 223 move(8,15); 224 addstr("Game Over!try again?(y/n):"); 225 refresh(); 226 choice=getch(); 227 228 while(flag){ 229 if(choice=='y'||choice=='Y'||choice=='n'||choice=='N') 230 flag=0; 231 else choice=getch(); 232 } 233 if(choice=='y'||choice=='Y'){ /*游戏重新开始*/ 234 delay=100; /*恢复球的初始速度*/ 235 init(); 236 continue; 237 } 238 else if(choice=='n'||choice=='N'){ /*结束游戏*/ 239 break; 240 } 241 } 242 } 243 } 244 //设置定时器 245 int set_ticker(int n_msecs){ 246 struct itimerval new_timeset; 247 long n_sec,n_usecs; 248 n_sec=n_msecs/1000; 249 n_usecs=(n_msecs%1000)*1000L; 250 new_timeset.it_interval.tv_sec=n_sec; 251 new_timeset.it_interval.tv_usec=n_usecs; 252 new_timeset.it_value.tv_sec=n_sec; 253 new_timeset.it_value.tv_usec=n_usecs; 254 return setitimer(ITIMER_REAL,&new_timeset,NULL); 255 }