俄罗斯方块——C语言实践(Dev-Cpp)
目录
1、创建项目(尽量不使用中文路径)
2、项目复制
3、项目配置
1、调整编译器
2、在配置窗口选择参数标签
3、添加头文件路径和库文件路径
4、代码实现
4.1、main.c
4.2、draw.h
4.3、draw.c
4.4、shape.h
4.5、shape.c
4.6、board.h
4.7、board.c
4.8、control.h
4.9、control.c
这个是在Dev-Cpp里实现的,在别的编译器,可能有些配置不一定能用
1、创建项目(尽量不使用中文路径)
第一次在Dev-Cpp里创建项目,真的难受,所以不得不讲一下
1、新建项目
2、点击Console Application ,C项目,我的名称取为day7_first
3、右击,新建文件夹(在自己平时建项目的文件夹里),取名为day7_first(一般与项目名相同)
再点击day7_first,打开
保存
2、项目复制
项目复制,我认为有必要讲一下,
可以,保存前一次的代码,然后在前一次的代码上进行改写,方便代码的管理
就是把上一次的.c,.h文件(及项目里要用到的应用程序扩展等,如果有的话),复制一份到day7_first文件夹里,但要把原来的main.c删掉(关闭,不保存),防止覆盖之前的main.c,
然后添加这些文件
3、项目配置
用了SDL2里的两个文件,在我的资源有
1、调整编译器
以支持C99标准,方便使用更多语言特性
2、在配置窗口选择参数标签
在链接处,输入三行
这里要注意两点:
减号-后面是小写L,不是数字 1 ,也不是i。
这三个库的顺序不可变。
之后再添加两个链接
-lmingw32
-lsdl2main
-lsdl2
-limm32
-lSDL2_ttf
3、添加头文件路径和库文件路径
一共添加这两个
同样的方法,在包含文件目录,添加这个两个
4、将两个应用程序扩展,复制到项目所在的文件夹
4、代码实现
4.1、main.c
#include "draw.h"
#include "shape.h"
#include "board.h"
#include "control.h"
///* run this program using the console pauser or add your own getch, system("pause") or input loop */
//
//int main(int argc, char *argv[]) {
// return 0;
//}
int main(int argc, char* argv[]) {
draw_init();//初始化
shape_init();//图形初始化
board_init();//板初始化
control_gameloop();//游戏循环
draw_free();// 清理资源
return 0;
}
4.2、draw.h
#ifndef DRAW_H
#define DRAW_H
#include <stdio.h>
#include <stdlib.h>
#include <SDL.h>
#include <stdbool.h>
#include <SDL_ttf.h>
#if 1
//苹果系统 1改0
#include <SDL_syswm.h>
#include <windows.h>
#include <imm.h>
#endif
#define WINDOW_W 640
#define WINDOW_H 510
#define INTERVAL 30//网格间隔
#define HEIGHT 16//网格高
#define WIDTH 10//网格宽
typedef struct
{
SDL_Window* window;
SDL_Renderer* renderer;
TTF_Font* font;
SDL_Texture* background;
}Renderer;
bool draw_init();
bool draw_free();
void draw_rect(const SDL_Rect* rect,const SDL_Color* color);//绘制矩形
void draw_update();//界面更新
void draw_string(int x,int y,const char* str,SDL_Color* color);//在窗口打印字符串
#endif
4.3、draw.c
#include "draw.h"
#include "shape.h"
#include "board.h"
static Renderer R;//一个存储信息的中间变量, 如果放到main()中,每个函数都要传参
#if 1
//苹果系统 1改0
void disableIME(SDL_Window *window) {//防止输入法自己改变
SDL_SysWMinfo wmInfo;
SDL_VERSION(&wmInfo.version);//初始化 wmInfo结构体的版本信息
if (SDL_GetWindowWMInfo(window, &wmInfo)) {
HWND hwnd = wmInfo.info.win.window;
//禁用 IME
ImmAssociateContext(hwnd, NULL);
}
}
#endif
bool draw_free() { // 清理资源
if(R.font) {
TTF_CloseFont(R.font);
TTF_Quit();
}
SDL_DestroyRenderer(R.renderer);
SDL_DestroyWindow(R.window);
SDL_Quit();
}
bool draw_init() {
// 初始化 SDL
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
SDL_Log("SDL could not initialize! SDL_Error: %s\n",
SDL_GetError());
return false;
}
// 创建窗口
R.window = SDL_CreateWindow("SDL2 Square",
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WINDOW_W, WINDOW_H,
SDL_WINDOW_SHOWN);
if (!R.window) {
SDL_Log("Window could not be created! SDL_Error: %s\n",
SDL_GetError());
SDL_Quit();
return false;
}
// 创建渲染器
R.renderer = SDL_CreateRenderer(R.window, -1,
SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (!R.renderer) {
SDL_Log("Renderer could not be created! SDL_Error: %s\n",
SDL_GetError());
SDL_DestroyWindow(R.window);
SDL_Quit();
return false;
}
#if 1
disableIME(R.window); //苹果系统 1改0
#endif
if(TTF_Init()<0) {
printf("Failed to initialize SDL_ttf\n");
draw_free();
return false;
}
R.font = TTF_OpenFont("bahnschrift.ttf",24);//添加一种字体,必须是.ttf后缀
if(!R.font) {
printf("Failde to load font\n",TTF_GetError());
TTF_Quit();
draw_free();
return false;
}
//Load the background BMP image
SDL_Surface* bgSurface = SDL_LoadBMP("background1.bmp");//添加背景,必须是.bmp后缀
if(!bgSurface) {
printf("Failed to load background image: %s\n",SDL_GetError());
return false;
}
R.background = SDL_CreateTextureFromSurface(R.renderer,bgSurface);
SDL_FreeSurface(bgSurface);//Free the surface after creating the texture
if(!R.background) {
printf("Failed to create background texture: %s\n",SDL_GetError());
return false;
}
printf("draw init() success\n");
return true;
}
void draw_string(int x,int y,const char* str,SDL_Color* color) {
SDL_Surface* surface = TTF_RenderText_Solid(R.font,str, *color);
if(!surface) {
printf("TTF_RenderText_Solid error: %s\n",TTF_GetError());
return;
}
SDL_Texture* texture = SDL_CreateTextureFromSurface(R.renderer,surface);
if(!texture) {
printf("SDL_CreateTextureFromSurface error:%s\n",SDL_GetError());
return;
}
SDL_Rect rect = {x,y,surface->w,surface->h};
SDL_FreeSurface(surface);
SDL_RenderCopy(R.renderer,texture,NULL,&rect);
SDL_DestroyTexture(texture);
}
//const SDL_Rect rect = {100,100,30,30};// 矩形的位置 x y w宽度 h高度
void draw_rect_grid() {//绘制网格
SDL_SetRenderDrawColor(R.renderer, 255,255,255,255);//白色
for(int i = 0; i<=HEIGHT; i++) {
SDL_RenderDrawLine(R.renderer,0,INTERVAL*i,INTERVAL*WIDTH,INTERVAL*i);//(x1,y1,x2,y2)
if(i == 0)
SDL_RenderDrawLine(R.renderer,0,INTERVAL*i+1,INTERVAL*WIDTH,INTERVAL*i+1);
if(i == HEIGHT)
SDL_RenderDrawLine(R.renderer,0,INTERVAL*i-1,INTERVAL*WIDTH,INTERVAL*i-1);
}
for(int i = 0; i<=WIDTH; i++) {
SDL_RenderDrawLine(R.renderer,INTERVAL*i,0,INTERVAL*i,INTERVAL*HEIGHT);
if(i == 0)
SDL_RenderDrawLine(R.renderer,INTERVAL*i+1,0,INTERVAL*i+1,INTERVAL*HEIGHT);
if(i == WIDTH)
SDL_RenderDrawLine(R.renderer,INTERVAL*i-1,0,INTERVAL*i-1,INTERVAL*HEIGHT);
}
}
void draw_rect_border(const SDL_Rect* rect) {//绘制矩形边框
SDL_SetRenderDrawColor(R.renderer, 255,255,255,255);//白色
SDL_RenderDrawRect(R.renderer, rect);
}
void draw_rect(const SDL_Rect* rect,const SDL_Color* color) {//绘制矩形
SDL_SetRenderDrawColor(R.renderer, color->r, color->g, color->b, color->a);
SDL_RenderFillRect(R.renderer, rect);
draw_rect_border(rect);
}
void draw_update() {//颜色更新
// 用颜色 清空渲染器,覆盖
SDL_SetRenderDrawColor(R.renderer, 0, 0, 255, 255);
SDL_RenderClear(R.renderer); //背景填充 蓝色
SDL_RenderCopy(R.renderer,R.background,NULL,NULL);//背景
//界面的更新数据
draw_rect_grid();//绘制网格
board_draw();//绘制底部的图形
shape_draw();//绘制图形
// 呈现渲染结果
SDL_RenderPresent(R.renderer);
}
4.4、shape.h
#ifndef _SHAPE_H
#define _SHAPE_H
#include <SDL.h>
#include <stdio.h>
#include <stdbool.h>
typedef enum{
MOVE_DOWN,
MOVE_LEFT,
MOVE_RIGHT,
MOVE_UP
}Movedir;
typedef struct {
bool matrix[4][4];
int x;
int y;
int size;//打印,旋转矩阵的一种标记
SDL_Color color;
}Shape;
void shape_init();
void shape_draw();
void shape_move(Movedir dir);
void shape_rotate();//图形旋转
#endif
4.5、shape.c
#include "shape.h"
#include "draw.h"
#include <stdlib.h>
#include <time.h>
#include "board.h"
#include "control.h"
//所有形状的定义
const Shape ALL_SHAPES[7] = {
{{{0,1,0,0},{0,1,1,0},{0,0,1,0},{0,0,0,0}},3,0,3,{200,200,200,255}},
{{{0,1,0,0},{0,1,1,0},{0,1,0,0},{0,0,0,0}},3,0,3,{180,180,100,255}},
{{{0,0,1,0},{0,1,1,0},{0,1,0,0},{0,0,0,0}},3,0,3,{160,180,10,255}},
{{{1,1,0,0},{1,1,0,0},{0,0,0,0},{0,0,0,0}},3,0,2,{80,160,100,255}},
{{{0,1,0,0},{0,1,0,0},{0,1,1,0},{0,0,0,0}},3,0,3,{100,180,160,255}},
{{{0,1,1,0},{0,1,0,0},{0,1,0,0},{0,0,0,0}},3,0,3,{10,160,60,255}},
{{{0,0,1,0},{0,0,1,0},{0,0,1,0},{0,0,1,0}},3,0,4,{180,10,160,255}}
};
Shape cur_shape;
Shape next_shape;
Shape rotate_shape;
int delay_cnt;
void shape_init() {//更新图形后,有回到之前的位置
srand((unsigned int)time(NULL));
cur_shape = ALL_SHAPES[rand()%7];
next_shape = ALL_SHAPES[rand()%7];
}
void matrix_rotate(int m) {
rotate_shape = cur_shape;
for(int i = 0; i<m; i++) {
for(int j = 0; j<m; j++) {
rotate_shape.matrix[j][m-i-1] = cur_shape.matrix[i][j];
}
}
if(board_check_collision(rotate_shape.x,rotate_shape.y,&rotate_shape))//旋转后越界,就不变
return;
cur_shape = rotate_shape;
}
void shape_rotate() {
if(cur_shape.size != 2) {
matrix_rotate(cur_shape.size);
return;
} else
return;
}
void shape_move(Movedir dir) {
switch(dir) {
case MOVE_DOWN:
if(!board_check_collision(cur_shape.x,cur_shape.y+1,&cur_shape))
cur_shape.y++;
else {
board_place_shape(&(cur_shape));//记录底部的图形
board_clear_fulllines();//清空满行
cur_shape = next_shape;
srand((unsigned int)time(NULL));
next_shape = ALL_SHAPES[rand()%7];
if(board_check_collision(cur_shape.x,cur_shape.y,&cur_shape)) {
control_setstate(STATE_GAME_OVER);
updatemaxscore();
updatemaxlevel();
printf("Game Over!\n");
}
}
break;
case MOVE_LEFT:
if(!board_check_collision(cur_shape.x-1,cur_shape.y,&cur_shape)) {
cur_shape.x--;
}
break;
case MOVE_RIGHT:
if(!board_check_collision(cur_shape.x+1,cur_shape.y,&cur_shape)) {
cur_shape.x++;
}
break;
case MOVE_UP:
shape_rotate();
break;
}
}
static void shape_draw_matrix(int x,int y,Shape* shape) {
SDL_Rect r = {0,0,30,30};
for(int i=0; i<shape->size; i++) {
for(int j=0; j<shape->size; j++) {
if(shape->matrix[i][j]) {
r.x = (x+j+shape->x)*30;
r.y = (y+i+shape->y)*30;//显示全
draw_rect(&r,&(shape->color));
}
}
}
}
void shape_draw() {
shape_draw_matrix(0,0,&cur_shape);
shape_draw_matrix(10,2,&next_shape);//界面预览
if(control_getstate() == STATE_RUNNING) {
if(delay_cnt++>30-board_getlevel()*3) {
delay_cnt = 0;
shape_move(MOVE_DOWN);
}
}
}
4.6、board.h
#ifndef __BOARD_H_
#define __BOARD_H_
#include <SDL.h>
#include <stdbool.h>
#include "shape.h"
#define BOARD_H 16
#define BOARD_W 10
#define BLOCKSIZE 30
//level++,所需要消的行数
#define INCREASE_LEVEL_LINES 2
typedef struct
{
SDL_Color color;
bool active;
}Block;
typedef struct
{
Block matrix[BOARD_H][BOARD_W];
int score;
int level;
int clearlines;
int maxscore;
int maxlevel;
}Gameboard;
void board_init();
void board_draw();//绘制底部的图形
bool board_check_collision(int newx,int newy,Shape* shape);//检查碰撞
void board_place_shape(Shape* cur_shape);//记录底部的图形
void board_clear_fulllines();
int board_getlevel();
void updatemaxscore();
void updatemaxlevel();
#endif
4.7、board.c
#include "board.h"
#include "draw.h"
static Gameboard gb;
int board_getlevel() {
return gb.level;
}
void getmaxscore() {
FILE* fp = fopen("俄罗斯方块最高分.txt","r");
if(fp == NULL) {
fp = fopen("俄罗斯方块最高分.txt","w");
if(fp == NULL) {
perror("getmaxscore()::fopen(w)");
return;
}
gb.maxscore = gb.score;
fprintf(fp,"%d",gb.maxscore);//初始化max为0
fclose(fp);
return;
}
fscanf(fp,"%d",&(gb.maxscore));
fclose(fp);
}
void updatemaxscore() {
FILE* fp = fopen("俄罗斯方块最高分.txt","w");
if(fp == NULL) {
perror("updatemaxscore()::fopen(w)");
return;
}
if(gb.score>gb.maxscore)
gb.maxscore = gb.score;
fprintf(fp,"%d",gb.maxscore);
fclose(fp);
}
void getmaxlevel() {
FILE* fp = fopen("俄罗斯方块最高难度.txt","r");
if(fp == NULL) {
fp = fopen("俄罗斯方块最高难度.txt","w");
if(fp == NULL) {
perror("getmaxlevel()::fopen(w)");
return;
}
gb.maxlevel = gb.level;
fprintf(fp,"%d",gb.maxlevel);//初始化level为1
fclose(fp);
return;
}
fscanf(fp,"%d",&(gb.maxlevel));
fclose(fp);
}
void updatemaxlevel() {
FILE* fp = fopen("俄罗斯方块最高难度.txt","w");
if(fp == NULL) {
perror("updatemaxlevel()::fopen(w)");
return;
}
if(gb.level>gb.maxlevel)
gb.maxlevel = gb.level;
fprintf(fp,"%d",gb.maxlevel);
fclose(fp);
}
void board_init() {//板初始化
gb.level = 1;
gb.clearlines = 0;
gb.score = 0;
getmaxscore();
getmaxlevel();
for(int i = 0; i<BOARD_H; i++) {
for(int j = 0; j<BOARD_W; j++) {
gb.matrix[i][j] = (Block) {
{160,160,160,255},false
};
}
}
}
void board_draw() {
SDL_Rect rect = {0,0,BLOCKSIZE,BLOCKSIZE};
for(int i = 0; i<BOARD_H; i++) {
for(int j = 0; j<BOARD_W; j++) {
if(gb.matrix[i][j].active) {
draw_rect(&rect,&(gb.matrix[i][j].color));
}
rect.x += rect.w;
}
rect.x = 0;
rect.y += rect.h;
}
char tempstring[32];
SDL_Color color = {200,0,0,255};
snprintf(tempstring,sizeof(tempstring),"Score: %d",gb.score);
draw_string((BOARD_W+4)*BLOCKSIZE,7*BLOCKSIZE,tempstring,&color);
color = (SDL_Color) {
0,200,0,255
};
snprintf(tempstring,sizeof(tempstring),"Level: %d",gb.level);
draw_string((BOARD_W+4)*BLOCKSIZE,8*BLOCKSIZE,tempstring,&color);
color = (SDL_Color) {
100,100,200,255
};
snprintf(tempstring,sizeof(tempstring),"Pause game press p");
draw_string((BOARD_W+2)*BLOCKSIZE,10*BLOCKSIZE,tempstring,&color);
color = (SDL_Color) {
100,100,200,255
};
snprintf(tempstring,sizeof(tempstring),"Restart game press r");
draw_string((BOARD_W+2)*BLOCKSIZE,11*BLOCKSIZE,tempstring,&color);
color = (SDL_Color) {
200,200,200,255
};
snprintf(tempstring,sizeof(tempstring),"maxscore:>%d",gb.maxscore);
draw_string(0,16*BLOCKSIZE,tempstring,&color);
color = (SDL_Color) {
200,200,200,255
};
snprintf(tempstring,sizeof(tempstring),"maxlevel:>%d",gb.maxlevel);
draw_string((BOARD_W)*BLOCKSIZE,16*BLOCKSIZE,tempstring,&color);
}
bool board_check_collision(int newx,int newy,Shape* shape) {
for(int i = 0; i<shape->size; i++) {
for(int j = 0; j<shape->size; j++) {
if(shape->matrix[i][j]) {
int x = newx + j;
int y = newy + i;
if(x<0||x>=BOARD_W||y>=BOARD_H)//碰边
return true;
if(y>0&&gb.matrix[y][x].active) //碰到已有图形
return true;
}
}
}
return false;
}
void board_place_shape(Shape* cur_shape) {
for(int i = 0; i < cur_shape->size; i++) {
for(int j = 0; j < cur_shape->size; j++) {
if(cur_shape->matrix[i][j]) {
gb.matrix[cur_shape->y+i][cur_shape->x+j].active = true;
gb.matrix[cur_shape->y+i][cur_shape->x+j].color = cur_shape->color;
}
}
}
}
void board_clear_fulllines() {
int lines = 0;
for(int y = 0; y < BOARD_H; y++) {
bool isfull = true;
for(int i = 0; i < BOARD_W; i++) {
if(!gb.matrix[y][i].active) {
isfull = false;
break;
}
}
if(isfull) { //满行
lines++;
for(int i = y; i > 0; i--) {//满行以上的所有的图形向下移动一行
for(int j = 0; j < BOARD_W; j++) {
gb.matrix[i][j] = gb.matrix[i-1][j];
}
}
for(int j = 0; j < BOARD_W; j++) {//清空最顶行
gb.matrix[0][j].active = false;
}
}
}
switch(lines) {
case 1:
gb.score += 1;
break;
case 2:
gb.score += 4;
break;
case 3:
gb.score += 9;
break;
case 4:
gb.score += 16;
break;
}
gb.clearlines += lines;
while(gb.clearlines>=INCREASE_LEVEL_LINES) {
gb.clearlines -= INCREASE_LEVEL_LINES;
gb.level++;
}
printf("Score: %d\nLevel: %d\nClear: %d\n",gb.score,gb.level,gb.clearlines+INCREASE_LEVEL_LINES*(gb.level-1));
}
4.8、control.h
#pragma once
#ifndef __CONTROL_H_
#define __CONTROL_H_
typedef enum
{
STATE_UNKOWN,
STATE_RUNNING,
STATE_PAUSE,
STATE_STOP,
STATE_GAME_OVER,
STATE_MANUAL
}GameState;
void control_gameloop();
GameState control_getstate();
void control_setstate(GameState st);
#endif
4.9、control.c
#include "control.h"
#include "shape.h"
#include "draw.h"
#include "board.h"
static GameState state;
GameState control_getstate() {
return state;
}
void control_setstate(GameState st) {
state = st;
}
void control_gameloop() {
// 主循环标志
bool running = true;
SDL_Event event;
state = STATE_RUNNING;
while (running) {
// 处理事件
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
running = false;
}
if(event.type == SDL_KEYDOWN) {
switch(event.key.keysym.sym) {
case SDLK_ESCAPE:
case SDLK_q:
running = false;
break;
case SDLK_p://暂停
if(state == STATE_RUNNING) {
state = STATE_PAUSE;
break;
}
if(state == STATE_PAUSE) {
state = STATE_RUNNING;
break;
}
case SDLK_m://自动与手动
if(state == STATE_RUNNING) {
state = STATE_MANUAL;
break;
}
if(state == STATE_MANUAL) {
state = STATE_RUNNING;
break;
}
case SDLK_r:
control_setstate(STATE_RUNNING);
board_init();//板初始化
shape_init();//图形初始化,图形回到初始位置
break;
case SDLK_UP:
if(state == STATE_RUNNING||state == STATE_MANUAL)
shape_move(MOVE_UP);
break;
case SDLK_DOWN:
if(state == STATE_RUNNING||state == STATE_MANUAL)
shape_move(MOVE_DOWN);
break;
case SDLK_LEFT:
if(state == STATE_RUNNING||state == STATE_MANUAL)
shape_move(MOVE_LEFT);
break;
case SDLK_RIGHT:
if(state == STATE_RUNNING||state == STATE_MANUAL)
shape_move(MOVE_RIGHT);
break;
}
}
}
draw_update();//颜色更新,覆盖,更新图形颜色
SDL_Delay(16);//60Hz
}
}