1、前言
本文实现了“打砖块”游戏的基本逻辑,算是一个小demo。开发环境配置可参考我的“SDL基础”系列文章,游戏截图如下:
2、代码
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#define WINDOW_WIDTH 320
#define WINDOW_HEIGHT 600
SDL_Rect BlueRect ;
SDL_Rect BallRect ;
SDL_Rect ReboundRodsRect ;
SDL_Event event;
SDL_Window* window = NULL;
SDL_Renderer* renderer = NULL;
SDL_Surface* BallSueface = NULL;
SDL_Surface* ReboundRodsSurface = NULL;
SDL_Surface* BlueBrick = NULL;
SDL_Surface* breakBlue = NULL;
SDL_Texture* BlueBrickTT;
SDL_Texture* breakBlueTT;
SDL_Texture* BallTT = NULL;
SDL_Texture* ReboundRodsTT = NULL;
typedef struct SSurface
{
SDL_Texture* Texture;
SDL_Rect TargetRect;
SDL_Rect SourceRect;
int index;
bool state;
}SSurface;
SSurface BlickManager[10][5];
const int BrickW = 64;
const int BrickH = 32;
int ReboundRodsSpeed = 3;
int BallSpeed = 2;
bool moveReboundRodsLeft = false;
bool moveReboundRodsRight = false;
bool moveRight = true;
bool moveDown = true;
void init(void);
void UpdateLogic(void);
void draw(void);
int check_collision();
bool checkCollision(SDL_Rect A,SDL_Rect B);
double GetDistance(int x1,int y1,int x2,int y2);
int main(int argc,char* argv[])
{
init();
bool quit = false;
while(quit == false)
{
while(SDL_PollEvent(&event))
{
if(event.type == SDL_QUIT)
{
quit = true;
}
else if(event.type == SDL_KEYDOWN)
{
if(event.key.keysym.sym == SDLK_LEFT)
{
moveReboundRodsLeft = true;
}
else if(event.key.keysym.sym == SDLK_RIGHT)
{
moveReboundRodsRight = true;
}
}
else if(event.type == SDL_KEYUP)
{
if(event.key.keysym.sym == SDLK_LEFT)
{
moveReboundRodsLeft = false;
}
else if(event.key.keysym.sym == SDLK_RIGHT)
{
moveReboundRodsRight = false;
}
}
}
UpdateLogic();
SDL_RenderClear(renderer);
draw();
SDL_RenderPresent(renderer);
SDL_Delay(10);
}
SDL_FreeSurface(BlueBrick);
SDL_FreeSurface(BallSueface);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
bool checkCollision(SDL_Rect A,SDL_Rect B)
{
int leftA,leftB;
int rightA,rightB;
int topA,topB;
int bottomA,bottomB;
leftA = A.x;
rightA = A.x+A.w;
topA = A.y;
bottomA = A.y+A.h;
leftB = B.x;
rightB = B.x+B.w;
topB = B.y;
bottomB = B.y + B.h;
if(bottomA <= topB)
{
return false;
}
if(topA >= bottomB)
{
return false;
}
if(rightA <= leftB)
{
return false;
}
if(leftA >= rightB)
{
return false;
}
return true;
}
void UpdateLogic(void)
{
int x,y;
for(x = 0; x < 10; x++)
{
for(y = 0;y < 5; y++)
{
if(BlickManager[x][y].state == true)
{
if(BlickManager[x][y].index == 2)
{
BlickManager[x][y].Texture = breakBlueTT;
BlickManager[x][y].SourceRect.x = 0;
BlickManager[x][y].SourceRect.y = 0;
BlickManager[x][y].index+=1;
}
else if(BlickManager[x][y].index == 3)
{
BlickManager[x][y].SourceRect.x = BrickW;
BlickManager[x][y].SourceRect.y = 0;
BlickManager[x][y].index+=1;
}
else if(BlickManager[x][y].index == 4)
{
BlickManager[x][y].SourceRect.x = BrickW*2;
BlickManager[x][y].SourceRect.y = 0;
BlickManager[x][y].index+=1;
}
else if(BlickManager[x][y].index == 5)
{
BlickManager[x][y].SourceRect.x = BrickW*3;
BlickManager[x][y].SourceRect.y = 0;
BlickManager[x][y].index+=1;
}
else if(BlickManager[x][y].index == 6)
{
BlickManager[x][y].SourceRect.x = BrickW*4;
BlickManager[x][y].SourceRect.y = 0;
BlickManager[x][y].index=1;
BlickManager[x][y].state = false;
}
}
}
}
if(true == moveRight)
{
BallRect.x=BallRect.x+BallSpeed;
if((BallRect.x + BallRect.w >=WINDOW_WIDTH)||(1 == check_collision()) )
{
moveRight = false;
}
}
else
{
BallRect.x=BallRect.x-BallSpeed;
if((BallRect.x <= 0)||(2 == check_collision()) )
{
moveRight = true;
}
}
if(true == moveDown)
{
BallRect.y=BallRect.y+BallSpeed;
if(BallRect.y + BallRect.h >=WINDOW_HEIGHT||checkCollision(BallRect,ReboundRodsRect)||(3 == check_collision()) )
{
moveDown= false;
}
}
else
{
BallRect.y=BallRect.y-BallSpeed;
if(BallRect.y <= 0||4 == check_collision() )
{
moveDown = true;
}
}
if(moveReboundRodsLeft == true)
{
if(ReboundRodsRect.x > 0)
{
ReboundRodsRect.x = ReboundRodsRect.x - ReboundRodsSpeed ;
}
}
if(moveReboundRodsRight == true)
{
if(ReboundRodsRect.x + ReboundRodsRect.w < WINDOW_WIDTH )
{
ReboundRodsRect.x = ReboundRodsRect.x + ReboundRodsSpeed ;
}
}
}
void draw(void)
{
int i,j;
for(i = 0; i < 10; i++)
{
for(j = 0;j < 5; j++)
{
if(BlickManager[i][j].state == true)
{
SDL_RenderCopy(renderer,BlickManager[i][j].Texture,&BlickManager[i][j].SourceRect,&BlickManager[i][j].TargetRect);
}
}
}
SDL_RenderCopy(renderer,BallTT,NULL,&BallRect);
SDL_RenderCopy(renderer,ReboundRodsTT,NULL,&ReboundRodsRect);
}
void init(void)
{
SDL_Init(SDL_INIT_VIDEO);
window = SDL_CreateWindow("MakeBricks",100,100,WINDOW_WIDTH,WINDOW_HEIGHT,SDL_WINDOW_SHOWN);
renderer = SDL_CreateRenderer(window,-1,SDL_RENDERER_ACCELERATED);
SDL_RenderClear(renderer);
BallSueface = IMG_Load("res/ball_pinball.png");
ReboundRodsSurface = IMG_Load("res/bat_large.png");
BlueBrick = IMG_Load("res/Stones/stone_glow_blue.png");
breakBlue = IMG_Load("res/Stones/stone_breaking_blue_strip5.png");
BallTT = SDL_CreateTextureFromSurface(renderer,BallSueface);
ReboundRodsTT = SDL_CreateTextureFromSurface(renderer,ReboundRodsSurface);
BlueBrickTT = SDL_CreateTextureFromSurface(renderer,BlueBrick);
breakBlueTT = SDL_CreateTextureFromSurface(renderer,breakBlue);
BallRect.x = WINDOW_WIDTH/2-(BallSueface->w/2);
BallRect.y = WINDOW_HEIGHT/10*9-BallSueface->h;
BallRect.w = BallSueface->w;
BallRect.h = BallSueface->h;
ReboundRodsRect.x = WINDOW_WIDTH/2-(ReboundRodsSurface->w/2);
ReboundRodsRect.y = WINDOW_HEIGHT/10*9;
ReboundRodsRect.w = ReboundRodsSurface->w;
ReboundRodsRect.h = ReboundRodsSurface->h;
BlueRect.x = 0;
BlueRect.y = 0;
BlueRect.w = BlueBrick->w;
BlueRect.h = BlueBrick->h;
int i,j;
for(i = 0; i < 10; i++)
{
for(j = 0;j < 5; j++)
{
BlickManager[i][j].Texture = BlueBrickTT;
BlickManager[i][j].SourceRect = BlueRect;
BlickManager[i][j].TargetRect.x = BrickW*j;
BlickManager[i][j].TargetRect.y = BrickH*i;
BlickManager[i][j].TargetRect.w = BlueRect.w;
BlickManager[i][j].TargetRect.h = BlueRect.h;
BlickManager[i][j].state = true;
BlickManager[i][j].index = 1;
}
}
}
double GetDistance(int x1,int y1,int x2,int y2)
{
double distance = sqrt((double)(pow(x2-x1,2)+pow(y2-y1,2)));
return distance;
}
int check_collision()
{
int x = 0;
int y = 0;
int cx = BallRect.x+BallRect.w/2;
int cy = BallRect.y+BallRect.h/2;
int cr = BallRect.w/2;
int i;
int j;
for(i = 0 ; i < 10 ; i++)
{
for(j = 0 ; j<5 ; j++)
{
if(BlickManager[i][j].state == true)
{
if(cx < BlickManager[i][j].TargetRect.x)
{
x = BlickManager[i][j].TargetRect.x;
}
else if(cx > BlickManager[i][j].TargetRect.x+BlickManager[i][j].TargetRect.w)
{
x = BlickManager[i][j].TargetRect.x+BlickManager[i][j].TargetRect.w;
}
else
{
x = cx;
}
if(cy < BlickManager[i][j].TargetRect.y)
{
y = BlickManager[i][j].TargetRect.y;
}
else if(cy > BlickManager[i][j].TargetRect.y+BlickManager[i][j].TargetRect.h)
{
y = BlickManager[i][j].TargetRect.y+BlickManager[i][j].TargetRect.h;
}
else
{
y = cy;
}
if( GetDistance(cx,cy,x,y) <= cr)
{
BlickManager[i][j].index = 2;
if(cx<x&&y>=BlickManager[i][j].TargetRect.y&&y<=BlickManager[i][j].TargetRect.y+BrickH)
{
return 1;
}else if(cx>x&&y>=BlickManager[i][j].TargetRect.y&&y<=BlickManager[i][j].TargetRect.y+BrickH)
{
return 2;
}
else if(cy<y&&x>=BlickManager[i][j].TargetRect.x&&x<=BlickManager[i][j].TargetRect.x+BrickW)
{
return 3;
}
else if(cy>y&&x>=BlickManager[i][j].TargetRect.x&&x<=BlickManager[i][j].TargetRect.x+BrickW)
{
return 4;
}
else
{
}
}
}
}
}
return 0;
}
3、后记
目前代码还没有优化。碰撞检查还有点小瑕疵。如果有朋友感兴趣,并优化,可留言交流,谢谢。
另:需要代码的朋友可以告诉我怎么在文章里放置下载吗?有些朋友需要,不能一一回复,想统一放一个下载地址,谢谢。