弹球游戏有三个主要因素:球,墙,用户输入。
(1)球以一定的速度运行;
(2)球碰到墙或挡板会逆行;
(3)用户输入会改变球的运行状态:加速、减速或退出;向上、向下或斜行(二维)。
程序主要功能设计:
(1)计时器驱动
间隔计时器被设置为产生固定的SIGALRMS信号流。响应一个信号,球向前移动一步。
(2)等待键盘输入
程序阻塞等待键盘输入。根据用户按下的键,采取不同的动作。
(3)状态变量
变量记录了球的速度和方向。用户输入修改的变量值决定了小球的速度。计时器处理函数根据速度和位置变量来决定在何时何处画小球。
一维动画:一个信号,移动一个单位。
二维动画:两个计时器,斜着移动。如当直角边的比值为1/3时,构造两个自己的计时器:一个记录平行移动的计时器信号数,一个记录垂直移动的计时器信号数。每两个计时器信号向右移动一个单位,每六个计时器信号向上移动一个单位。如果时钟间隔单位分别是10和30的话,球移动的路径相同,就是慢些。
实现:为实现二维动画,同时在两个方向移动,用两个计数器充当计时器。每个计数器有两个属性:当前值和间隔。当前值表示在下次重画之前还要等待多少个计时信号。间隔指明两次移动之间所要等待的计时信号个数。分别命名为ttg和ttm。
代码:
/* bounce.h */
#ifndef BOUNCH_H
#define BOUNCH_H
/* some settings for the game */
#define BLANK ' '
#define WALL '.'
#define USER_WALL '='
#define DFL_SYMBOL 'o'
#define TOP_ROW 6
#define BOT_ROW 19
#define LEFT_EDGE 6
#define RIGHT_EDGE 44
#define X_INIT 10 /* starting col */
#define Y_INIT 10 /* starting row */
#define TICKS_PER_SEC 25 /* affects speed */
#define X_TTM 5
#define Y_TTM 8
int score = 0;
int user_wall_pos = 5;
int user_wall_len = 9;
/** the ping pong ball **/
struct ppball {
int y_pos, x_pos,
y_ttm, x_ttm,
y_ttg, x_ttg,
y_dir, x_dir;
char symbol ;
} ;
#endif
#include <stdio.h>
#include <sys/time.h>
#include <signal.h>
/*
* set_ticker.c
* set_ticker( number_of_milliseconds )
* arranges for the interval timer to issue
* SIGALRM's at regular intervals
* returns -1 on error, 0 for ok
*
* arg in milliseconds, converted into micro seoncds
*/
set_ticker( n_msecs )
{
struct itimerval new_timeset;
long n_sec, n_usecs;
n_sec = n_msecs / 1000 ;
n_usecs = ( n_msecs % 1000 ) * 1000L ;
new_timeset.it_interval.tv_sec = n_sec; /* set reload */
new_timeset.it_interval.tv_usec = n_usecs; /* new ticker value */
new_timeset.it_value.tv_sec = n_sec ; /* store this */
new_timeset.it_value.tv_usec = n_usecs ; /* and this */
return setitimer(ITIMER_REAL, &new_timeset, NULL);
}
/* bounce2d.c 1.0
* bounce a character (default is 'o') around the screen
* defined by some parameters
*
* user input: s slow down x component, S: slow y component
* f speed up x component, F: speed y component
* Q quit a: user wall left d: user wall right
*
* blocks on read, but timer tick sends SIGALRM caught by ball_move
* build: cc bounce2d.c set_ticker.c -lcurses -o bounce2d
*/
#include <curses.h>
#include <signal.h>
#include <stdlib.h>
#include "bounce.h"
struct ppball the_ball ;
/** the main loop **/
void set_up();
void wrap_up();
void move_user_wall(int);
int main()
{
int c, dir = 0;
set_up();
while ( ( c = getchar()) != 'Q' ){
if ( c == 'f' ) the_ball.x_ttm--;
else if ( c == 's' ) the_ball.x_ttm++;
else if ( c == 'F' ) the_ball.y_ttm--;
else if ( c == 'S' ) the_ball.y_ttm++;
else if ( c == 'a' ) { dir = -1; move_user_wall(dir); }
else if ( c == 'd' ) { dir = 1; move_user_wall(dir); }
else if ( c == 'j' ) { if ( --user_wall_len <= 1 ) user_wall_len = 1; }
else if ( c == 'k' ) { if ( ++user_wall_len >= 15 ) user_wall_len = 15; }
}
wrap_up();
}
void set_up()
/*
* init structure and other stuff
*/
{
void ball_move(int);
the_ball.y_pos = Y_INIT;
the_ball.x_pos = X_INIT;
the_ball.y_ttg = the_ball.y_ttm = Y_TTM ;
the_ball.x_ttg = the_ball.x_ttm = X_TTM ;
the_ball.y_dir = 1 ;
the_ball.x_dir = 1 ;
the_ball.symbol = DFL_SYMBOL ;
initscr();
noecho();
crmode();
signal( SIGINT , SIG_IGN );
/* draw wall */
mvhline(5, 5, WALL, 40);
mvvline(5, 5, WALL, 15);
mvvline(5, 45, WALL, 15);
mvprintw( 4, 5, "score:%d left:s right:d -speed:s +speed:f +wall_len:j -wall_len:k quit:Q", 0); /* infomation */
mvaddch( the_ball.y_pos, the_ball.x_pos, the_ball.symbol ); /* init ball */
mvhline(20, user_wall_pos, '=', user_wall_len); /* init user_wall */
refresh();
signal( SIGALRM, ball_move );
set_ticker( 1000 / TICKS_PER_SEC ); /* send millisecs per tick */
}
void wrap_up()
{
set_ticker( 0 );
mvaddstr(15, 15, "GAME OVER!.....");
mvaddstr(16, 15, "Q:quit");
refresh();
while(1)
if ( getchar() == 'Q') break;
endwin();
exit(0); /* put back to normal */
}
void ball_move(int signum)
{
int y_cur, x_cur, moved;
signal( SIGALRM , SIG_IGN ); /* dont get caught now */
y_cur = the_ball.y_pos ; /* old spot */
x_cur = the_ball.x_pos ;
moved = 0 ;
if ( the_ball.y_ttm > 0 && the_ball.y_ttg-- == 1 ){
the_ball.y_pos += the_ball.y_dir ; /* move */
the_ball.y_ttg = the_ball.y_ttm ; /* reset*/
moved = 1;
}
if ( the_ball.x_ttm > 0 && the_ball.x_ttg-- == 1 ){
the_ball.x_pos += the_ball.x_dir ; /* move */
the_ball.x_ttg = the_ball.x_ttm ; /* reset*/
moved = 1;
}
if ( moved ){
mvaddch( y_cur, x_cur, BLANK );
mvaddch( y_cur, x_cur, BLANK );
mvaddch( the_ball.y_pos, the_ball.x_pos, the_ball.symbol );
bounce_or_lose( &the_ball );
move(LINES-1,COLS-1);
refresh();
}
signal( SIGALRM, ball_move); /* for unreliable systems */
}
int bounce_or_lose(struct ppball *bp)
{
int return_val = 0 ;
int user_wall_left = user_wall_pos,
user_wall_right = user_wall_pos + user_wall_len - 1;
if ( bp->y_pos == TOP_ROW ){
bp->y_dir = 1 ;
return_val = 1 ;
} else if ( (bp->y_pos == BOT_ROW) && (bp->x_pos >= user_wall_left && bp->x_pos <= user_wall_right) ){
bp->y_dir = -1;
mvprintw( 4, 5, "score:%d", score++);
return_val = 1;
} else if (bp->y_pos > BOT_ROW)
wrap_up();
if ( bp->x_pos == LEFT_EDGE ){
bp->x_dir = 1 ;
return_val = 1 ;
} else if ( bp->x_pos == RIGHT_EDGE ){
bp->x_dir = -1;
return_val = 1;
}
return return_val;
}
void move_user_wall(int dir)
{
int user_wall_left = user_wall_pos,
user_wall_right = user_wall_pos + user_wall_len - 1;
int wall_left = 5,
wall_right = 45;
user_wall_pos += dir;
if(user_wall_pos <= wall_left)
user_wall_pos = wall_left;
if( user_wall_pos >= wall_right - user_wall_len + 1 )
user_wall_pos = wall_right - user_wall_len;
mvhline( 20, user_wall_pos, USER_WALL, user_wall_len + 1 );
if (dir == 1 && user_wall_right < wall_right -1 )
mvhline( 20, user_wall_left, BLANK, 1 );
if (dir == -1 && user_wall_left > wall_left )
mvhline( 20, user_wall_right+1, BLANK, 1 );
move(LINES - 1, COLS - 1);
refresh();
}
/*
* set_ticker.c
* set_ticker( number_of_milliseconds )
* arranges for the interval timer to issue
* SIGALRM's at regular intervals
* returns -1 on error, 0 for ok
*
* arg in milliseconds, converted into micro seoncds
*/
#include <stdio.h>
#include <sys/time.h>
#include <signal.h>
set_ticker( n_msecs )
{
struct itimerval new_timeset;
long n_sec, n_usecs;
n_sec = n_msecs / 1000 ;
n_usecs = ( n_msecs % 1000 ) * 1000L ;
new_timeset.it_interval.tv_sec = n_sec; /* set reload */
new_timeset.it_interval.tv_usec = n_usecs; /* new ticker value */
new_timeset.it_value.tv_sec = n_sec ; /* store this */
new_timeset.it_value.tv_usec = n_usecs ; /* and this */
return setitimer(ITIMER_REAL, &new_timeset, NULL);
}
想实现R:重新开始游戏的功能,可是没能成功,希望有知道如何实现的牛人给指点一下啊~