📄 game.c
字号:
#include <stdlib.h>
#include <stdio.h>
#include <time.h> //需要产生随机数,加载时间头文件
#include "SDL.h"
#ifdef macintosh
#define DIR_SEP ":"
#define DIR_CUR ":"
#else
#define DIR_SEP "/"
#define DIR_CUR ""
#endif
#define DATAFILE(X) DIR_CUR "data" DIR_SEP X
#define FRAMES_PER_SEC 10 //每秒钟的帧数
#define PLANE_SPEED 8 //飞机运动速度
#define MAX_SHOTS 2 //最多可以发射多少发激光
#define SHOT_SPEED 20 //激光的速度
#define MAX_BOMBS 10 //最多出现多少枚炮弹
#define BOMB_SPEED_X 8 //炮弹的x速度
#define BOMB_SPEED_Y 8
#define BOMB_ODDS (1*FRAMES_PER_SEC) //剩下的炮弹数
#define EXPLODE_TIME 4 //爆炸时间
SDL_Rect dst;
int shoot_bomb_num=0;
struct timeval tv1,tv2,tz;
typedef struct { //对象结构体
int alive; //是否存活标志
int facing; //运动方向
int x, y; //坐标
SDL_Surface *image; //图像指针
} object;
SDL_Surface *screen; //屏幕指针
SDL_Surface *background; //背景指针
SDL_Surface *gameover;
object plane; //飞机对象
int reloading; //重新加载标志
object shots[MAX_SHOTS]; //激光对象
object bombs[MAX_BOMBS]; //炮弹对象
object explosions[MAX_BOMBS+1]; //爆炸对象
#define MAX_UPDATES 3*(1+MAX_SHOTS+MAX_BOMBS) //最大更新次数
int numupdates; //更新次数的变量
SDL_Rect srcupdate[MAX_UPDATES]; //源目标更新
SDL_Rect dstupdate[MAX_UPDATES]; //目标更新
struct blit { //定义快速重绘对象结构体
SDL_Surface *src;
SDL_Rect *srcrect;
SDL_Rect *dstrect;
} blits[MAX_UPDATES];
SDL_Surface *LoadImage(char *datafile) //加载图片函数
{
SDL_Surface *image, *surface;
image = SDL_LoadBMP(datafile); //用SDL_LoadBMP()函数加载图片
if ( image == NULL ) {
fprintf(stderr, "Couldn't load image %s: %s\n",
datafile,SDL_GetError());
return(NULL);
}
surface = SDL_DisplayFormat(image); //用图片的形式显示
SDL_FreeSurface(image); //释放图片
return(surface);
}
int LoadData(void) //加载数据
{
int i;
plane.image = LoadImage(DATAFILE("plane.bmp")); //加载飞机图片
if ( plane.image == NULL ) {
return(0);
}
shots[0].image = LoadImage(DATAFILE("shot.bmp")); //加载激光图片
if ( shots[0].image == NULL ) {
return(0);
}
for ( i=1; i<MAX_SHOTS; ++i ) {
shots[i].image = shots[0].image;
}
bombs[0].image = LoadImage(DATAFILE("bomb.bmp")); //加载炮弹图片
if ( bombs[0].image == NULL ) {
return(0);
}
for ( i=1; i<MAX_BOMBS; ++i ) {
bombs[i].image = bombs[0].image;
}
explosions[0].image = LoadImage(DATAFILE("explosion.bmp"));//加载爆炸图片
for ( i=1; i<MAX_BOMBS+1; ++i ) {
explosions[i].image = explosions[0].image;
}
background = LoadImage(DATAFILE("background.bmp"));//加载背景图片
gameover = LoadImage(DATAFILE("gameover.bmp"));
//设置更新目标矩形的指针
for ( i=0; i<MAX_UPDATES; ++i ) {
blits[i].srcrect = &srcupdate[i];
blits[i].dstrect = &dstupdate[i];
}
return(1);
}
void FreeData(void) //释放数据
{
int i;
SDL_FreeSurface(plane.image); //释放飞机图片
SDL_FreeSurface(shots[0].image); //释放激光图片
SDL_FreeSurface(bombs[0].image); //释放炮弹图片
SDL_FreeSurface(explosions[0].image); //释放爆炸图片
SDL_FreeSurface(background); //最后释放背景图片
}
void CreateBomb(void) //产生一枚新的炮弹
{
int i;
for ( i=0; i<MAX_BOMBS; ++i ) //当炮弹数没有达到最大值,并且存活值为0时,产生一枚新的炮弹
if ( ! bombs[i].alive )
break;
if ( i == MAX_BOMBS ) //已经到达最大值就返回,不产生
return;
bombs[i].facing=rand()%3-1;
bombs[i].y = 0; //确定炮弹初始时的y方向上的位置,0即表示屏幕的上部
bombs[i].x = (int)(screen->w-bombs[i].image->w)*(rand()/(RAND_MAX+1.0));
bombs[i].alive = 1;
}
void DrawObject(object *sprite) //画对象
{
struct blit *update;
update = &blits[numupdates++];
update->src = sprite->image;
update->srcrect->x = 0; //根据源目标的大小和位置坐标来画
update->srcrect->y = 0;
update->srcrect->w = sprite->image->w;
update->srcrect->h = sprite->image->h;
update->dstrect->x = sprite->x;
update->dstrect->y = sprite->y;
update->dstrect->w = sprite->image->w;
update->dstrect->h = sprite->image->h;
}
void EraseObject(object *sprite) //消除目标
{
struct blit *update;
int wrap;
//背景水平重叠达到清除画面效果
update = &blits[numupdates++];
update->src = background;
update->srcrect->x = sprite->x%background->w;
update->srcrect->y = sprite->y;
update->srcrect->w = sprite->image->w;
update->srcrect->h = sprite->image->h;
wrap = (update->srcrect->x+update->srcrect->w)-(background->w);
if ( wrap > 0 ) {
update->srcrect->w -= wrap;
}
update->dstrect->x = sprite->x;
update->dstrect->y = sprite->y;
update->dstrect->w = update->srcrect->w;
update->dstrect->h = update->srcrect->h;
//一个背景一个背景的把屏幕重绘
if ( wrap > 0 ) {
update = &blits[numupdates++];
update->src = background;
update->srcrect->x = 0;
update->srcrect->y = sprite->y;
update->srcrect->w = wrap;
update->srcrect->h = sprite->image->h;
update->dstrect->x =((sprite->x/background->w)+1)*background->w;
update->dstrect->y = sprite->y;
update->dstrect->w = update->srcrect->w;
update->dstrect->h = update->srcrect->h;
}
}
void UpdateScreen(void) //更新屏幕
{
int i;
for ( i=0; i<numupdates; ++i ) {
SDL_LowerBlit(blits[i].src, blits[i].srcrect,
screen, blits[i].dstrect);
}
SDL_UpdateRects(screen, numupdates, dstupdate);
numupdates = 0;
}
int Collide(object *sprite1, object *sprite2) //两个物体碰撞的情况
{
if ( (sprite1->y >= (sprite2->y+sprite2->image->h-8)) ||
(sprite1->x >= (sprite2->x+sprite2->image->w-8)) ||
(sprite2->y >= (sprite1->y+sprite1->image->h-8)) ||
(sprite2->x >= (sprite1->x+sprite1->image->w-8)) ) {
return(0);
}
return(1);
}
void WaitFrame(void)
{
static Uint32 next_tick = 0;
Uint32 this_tick;
this_tick = SDL_GetTicks(); //得到当前时间值
if ( this_tick < next_tick ) {
SDL_Delay(next_tick-this_tick);//延时时间
}
next_tick = this_tick + (1000/FRAMES_PER_SEC);//下一帧
}
void RunGame(void) //开始游戏
{
int i, j;
SDL_Event event; //SDL事件
Uint8 *keys;
SDL_Rect dst;
dst.x = 0;
dst.y = 0;
dst.w = background->w;
dst.h = background->h;
gettimeofday(&tv1,&tz);
numupdates = 0;
SDL_BlitSurface(background, NULL, screen, &dst);
SDL_UpdateRect(screen, 0, 0, 0, 0); //更新屏幕
plane.alive = 1; //初始化飞机参数,存活,位置和运动方向
plane.x = (screen->w - plane.image->w)/2;
plane.y = (screen->h - plane.image->h) -1;
plane.facing = 0;
DrawObject(&plane); //画飞机
for ( i=0; i<MAX_SHOTS; ++i ) { //初始化激光的存活参数
shots[i].alive = 0;
}
for ( i=0; i<MAX_BOMBS; ++i ) { //初始化炮弹的存活参数
bombs[i].alive = 0;
}
CreateBomb(); //产生炮弹
DrawObject(&bombs[0]); //画炮弹
UpdateScreen();
while ( plane.alive ) { //当飞机没有被炸毁的时候,游戏正常进行
WaitFrame();
while ( SDL_PollEvent(&event) ) { //循环接受键盘事件,直到退出
if ( event.type == SDL_QUIT )
return;
}
keys = SDL_GetKeyState(NULL); //得到键盘键的状态
for ( i=0; i<MAX_SHOTS; ++i ) { //清除激光
if ( shots[i].alive ) {
EraseObject(&shots[i]);
}
}
for ( i=0; i<MAX_BOMBS; ++i ) { //清除炮弹
if ( bombs[i].alive ) {
EraseObject(&bombs[i]);
}
}
EraseObject(&plane);
for ( i=0; i<MAX_BOMBS+1; ++i ) { //清除爆炸
if ( explosions[i].alive ) {
EraseObject(&explosions[i]);
}
}
for ( i=0; i<MAX_BOMBS+1; ++i ) { //爆炸次数记录,并减少
if ( explosions[i].alive ) {
--explosions[i].alive;
} SDL_BlitSurface(background, NULL, screen, &dst);
}
if ( (rand()%BOMB_ODDS) == 0 ) { //产生新的炮弹
CreateBomb();
}
if ( ! reloading ) { //产生新的激光
if ( keys[SDLK_SPACE] == SDL_PRESSED ) {//按下空格键,发射
for ( i=0; i<MAX_SHOTS; ++i ) {
if ( ! shots[i].alive ) {
break;
}
}
if ( i != MAX_SHOTS ) { //激光移动轨迹
shots[i].x = plane.x +
(plane.image->w-shots[i].image->w)/2;
shots[i].y = plane.y -
shots[i].image->h;
shots[i].alive = 1;
}
}
}
reloading = (keys[SDLK_SPACE] == SDL_PRESSED);
plane.facing = 0; //移动飞机
if ( keys[SDLK_RIGHT] ) { //右方向键向右运动
++plane.facing;
}
if ( keys[SDLK_LEFT] ) { //左方向键向左运动
--plane.facing;
}
if ( keys[SDLK_UP] ) { //上方向键向上运动
plane.y-=PLANE_SPEED;
if(plane.y < 0)
plane.y= 0 ;
}
if ( keys[SDLK_DOWN] ) { //下方向键向下运动
plane.y+=PLANE_SPEED;
if(plane.y > screen->h-plane.image->h)
plane.y=screen->h-plane.image->h;
}
plane.x += plane.facing*PLANE_SPEED; //计算移动的位移
if ( plane.x < 0 ) {
plane.x = 0;
} else
if ( plane.x >= (screen->w-plane.image->w) ) {//两边碰头的处理
plane.x = (screen->w-plane.image->w)-1;
}
for ( i=0; i<MAX_BOMBS; ++i ) {//移动炮弹
if ( bombs[i].alive ) {
if(bombs[i].facing){
if(rand()%2){
bombs[i].x += bombs[i].facing*BOMB_SPEED_X; //计算炮弹位移
}
else
bombs[i].y += BOMB_SPEED_Y;
}
else
bombs[i].y += BOMB_SPEED_Y;
if ( bombs[i].x < 0 ) { //炮弹两边碰头的计算
bombs[i].x = (screen->w-bombs[i].image->w)-1;
bombs[i].y += bombs[i].image->h;
} else
if ( bombs[i].x >=
(screen->w-bombs[i].image->w) ) {
bombs[i].x = 0;
}
if(bombs[i].y>=(screen->h-bombs[i].image->h))
bombs[i].alive=0;
}
}
for ( i=0; i<MAX_SHOTS; ++i ) { //激光的移动
if ( shots[i].alive ) {
shots[i].y -= SHOT_SPEED; //计算激光位移
if ( shots[i].y < 0 ) {
shots[i].alive = 0;
}
}
}
for ( j=0; j<MAX_SHOTS; ++j ) { //处理碰撞
for ( i=0; i<MAX_BOMBS; ++i ) {
if ( shots[j].alive && bombs[i].alive &&
Collide(&shots[j], &bombs[i]) ) {
bombs[i].alive = 0;
explosions[i].x = bombs[i].x; //如果碰撞了,出现爆炸
explosions[i].y = bombs[i].y;
explosions[i].alive = EXPLODE_TIME;
shots[j].alive = 0;
shoot_bomb_num++;
break;
}
}
}
for ( i=0; i<MAX_BOMBS; ++i ) { //炮弹和飞机碰撞的处理
if ( bombs[i].alive && Collide(&plane, &bombs[i]) ) {
bombs[i].alive = 0;
explosions[i].x = bombs[i].x; //出现爆炸
explosions[i].y = bombs[i].y;
explosions[i].alive = EXPLODE_TIME;
plane.alive = 0;
explosions[MAX_BOMBS].x = plane.x; //飞机爆炸
explosions[MAX_BOMBS].y = plane.y;
explosions[MAX_BOMBS].alive = EXPLODE_TIME;
}
}
for ( i=0; i<MAX_BOMBS; ++i ) { //画炮弹
if ( bombs[i].alive ) {
DrawObject(&bombs[i]);
}
}
for ( i=0; i<MAX_SHOTS; ++i ) { //画激光
if ( shots[i].alive ) {
DrawObject(&shots[i]);
}
}
if ( plane.alive ) { //画飞机
DrawObject(&plane);
}
for ( i=0; i<MAX_BOMBS+1; ++i ) { //画爆炸
if ( explosions[i].alive ) {
DrawObject(&explosions[i]);
}
}
UpdateScreen();
if ( keys[SDLK_ESCAPE] == SDL_PRESSED ) {//按下Esc退出
plane.alive = 0;
}
}
return;
}
void init()
{
//初始化视频子系统
int i;
if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
fprintf(stderr, "Couldn't initialize SDL: %s\n",SDL_GetError());
exit(2);
}
atexit(SDL_Quit);
//设置视频模式
screen = SDL_SetVideoMode(640, 480, 0, SDL_SWSURFACE);
if ( screen == NULL ) {
fprintf(stderr, "Couldn't set 640x480 video mode: %s\n",
SDL_GetError());
exit(2);
}
srand(time(0));//随机时间产生器
}
void output(void)
{
double dt;
dt=(tv2.tv_sec-tv1.tv_sec)+(tv2.tv_usec-tv1.tv_usec)/1000000.0;
printf("\n********************************************************************");
printf("\n You have shot down %d bombs!!",shoot_bomb_num);
if(shoot_bomb_num>=15)
printf("\n You are a good ## SHOOTER ## !!!!");
printf("\n You have successfully survived %.2lf seconds!!!",dt);
if(dt>=30)
printf("\n You have passed 30 seconds,\n"
" so you are a real ## MAN ## now! Congratulations!!");
else
printf("\n You have not passed 30 seconds\n"
" so you are not a real MAN,catch on and try again!!!");
printf("\n********************************************************************\n");
}
int main(int argc, char *argv[])
{
init();
if ( LoadData() ) { //加载数据
RunGame(); //运行游戏
SDL_Delay(1000);
gettimeofday(&tv2,&tz);
SDL_BlitSurface(gameover, NULL, screen, &dst);
SDL_UpdateRect(screen, 0, 0, 0, 0); //更新屏幕
SDL_Delay(2000);
output();
FreeData(); //释放数据
}
exit(0); //退出
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -