C语言打砖块小游戏

前言

大一上学期学完C语言,做个打砖块小游戏练练手。

安装easyx图形库

编程环境:Visual Studio 2022 

代码

首先引入相关头文件并对变量进行宏定义

#include <stdio.h>
#include <graphics.h>
#include <time.h>
#include <windows.h>
#define _CRT_SECURE_NO_WARNINGS     //文件
#define WIDTH 100
#define HEIGHT 25

初始化地图(用二维数组)

int map[5][8];
void initMap()
{
	for (int i = 0; i < 5; i++)
	{
		for (int j = 0; j < 8; j++)
		{
			map[i][j] = rand() % 4 + 1;
			//0 待会消除砖块使用
		}
	}
}

画地图(调用easyx图形库中函数画砖块)

void drawMap()
{
	setlinecolor(BLACK);	
	for (int i = 0; i < 5; i++)
	{
		for (int j = 0; j < 8; j++)
		{
			int x = WIDTH * j;
			int y = HEIGHT * i;
			switch(map[i][j])
			{
				case 0:
					break;
				case 1:       //绿色
					setfillcolor(GREEN);
					fillrectangle(x, y, x + 100, y + 25); break;
				case 2:       //粉色
					setfillcolor(RGB(199,96,199));
					fillrectangle(x, y, x + 100, y + 25); break;
				case 3:       //浅蓝
					setfillcolor(LIGHTBLUE);
					fillrectangle(x, y, x + 100, y + 25); break;
				case 4:       //黄色
					setfillcolor(RGB(255,255,128));
					fillrectangle(x, y, x + 100, y + 25); break;
			}
		}
	}
}

用结构体定义木板属性

struct Board
{
	//木板的左上角坐标
	int x;
	int y;
	//木板的宽度和高度
	int w;
	int h;
	//木板的移动速度
	int speed;
};
struct Board board = { 300,600 - 25,200,25,1};

绘制木板(同样调用easyx图形库函数)

void drawBoard()
{
	setfillcolor(WHITE);
	fillrectangle(board.x, board.y, board.x + board.w, board.y + board.h);
}

用获取异步键盘状态函数获取指定虚拟键的状态,进而操控木板左右移动

void KeyDown()
{
	if (GetAsyncKeyState('A') || GetAsyncKeyState(VK_LEFT)&&board.x>0)
	{
		board.x -= board.speed;
	}
	if (GetAsyncKeyState('D') || GetAsyncKeyState(VK_RIGHT)&&board.x+board.w<WIDTH*8)
	{
		board.x += board.speed;
	}
}

用结构体定义小球属性

struct Ball
{
	//球的圆心
	int x;
	int y;
	//球的半径
	int r;
	//移动x和y上的增量
	int dx;
	int dy;
};
struct Ball ball = { 400,400,15,5,-5 };

绘制小球

void drawBall()
{
	setfillcolor(RED);
	fillcircle(ball.x, ball.y,ball.r);
}

设置定时器控制小球移动速度

int Timer(int duration, int id)
{
	static int startTime[10];        
	//静态变量会自动初始化为0,只会被初始化一次
	//记录程序上一次结果
	int endTime = clock();         //获取程序运行到当前位置的时间
	if (endTime - startTime[id] > duration)
	{
		startTime[id] = endTime;
		return 1;
	}
	return 0;
}

碰木板

int hitBoard()
{
	if (ball.y + ball.r == board.y)
	{
		if (ball.x >= board.x && ball.x <= board.x + board.w)
		{
			return 1;
		}
	}
	return 0;
}

碰砖块(数组中的值为0则打到砖块)

int hitBricks()
{
	int j = ball.x / WIDTH;
	int i = (ball.y - ball.r) / HEIGHT;
	if (i < 5 && j < 8 && map[i][j] != 0)
	{
		map[i][j] = 0;
		return 1;
	}
	return 0;
}

碰墙壁(调用碰木板和碰砖块的函数)

void hitWall()
{
	if (ball.x + ball.r >= WIDTH * 8 || ball.x - ball.r <= 0)       //撞左右墙壁
	{
		ball.dx = -ball.dx;
	}
	if (hitBoard() || hitBricks()||ball.y - ball.r <= 0)      //撞上下墙壁
	//if (ball.y+ball.r>=WIDTH*8)
	{
		ball.dy = -ball.dy;
	}
	
	ball.x += ball.dx;
	ball.y += ball.dy;
}

计算得分(分数显示在控制台)

int Score()
{
	int score = 0;
	for (int i = 0; i < 5; i++)
	{
		for (int j = 0; j < 8; j++)
		{
			if (map[i][j] == 0)
			{
				score += 5;
			}
		}
	}
	return score;
}

游戏失败的情况

int die()
{
	if (ball.y >=WIDTH * 8 - 15)
	{
		return 1;
	}
	return 0;
}

游戏成功结束

int gameOver()
{
	for (int i = 0; i < 5; i++)
	{
		for (int j = 0; j < 8; j++)
		{
			if (map[i][j] != 0)
			{
				return 0;
			}
		}
	}
	return 1;
}

主函数

创建窗口

initgraph(WIDTH * 8, WIDTH * 6);

使用双缓冲地图防止闪屏

BeginBatchDraw();       //开始双缓冲绘图

别忘了结束双缓冲

EndBatchDraw();        //结束双缓冲

调整小球速度

if (Timer(20, 0))
	hitWall();

游戏失败的弹窗

if (die())
{
	closegraph;
	printf("%d分,加油!", s);
	MessageBox(NULL, TEXT("gameOver"), TEXT("gameOver"), MB_OK);
	exit(0);
}

游戏成功的弹窗

if (gameOver())
{
	closegraph;
	printf("200分,真棒!");
	MessageBox(NULL, TEXT("Congratulation!"), TEXT("gameOver"), MB_OK);
	exit(0);
}

将分数存入文件(注意vs里使用文件要用安全函数或者添加宏定义)

FILE* file;
errno_t err = fopen_s(&file, "scoreinfo.txt", "a");
fprintf_s(file, "%d\n", s);
fclose(file);

主函数整体

int main()
{
	srand((unsigned int)time(0));
	initgraph(WIDTH * 8, WIDTH * 6);
	initMap();
	BeginBatchDraw();       //开始双缓冲绘图(防闪)
	while (1)
	{
		cleardevice();     //刷新(清除屏幕)
		drawMap();
		drawBoard();
		drawBall();
		if (Timer(20, 0))
			hitWall();
		KeyDown();
		int s = Score();
		if (die())
		{
			closegraph;
			printf("%d分,加油!", s);
			MessageBox(NULL, TEXT("gameOver"), TEXT("gameOver"), MB_OK);
			exit(0);
		}
	    if (gameOver())
		{
			closegraph;
			printf("200分,真棒!");
			MessageBox(NULL, TEXT("Congratulation!"), TEXT("gameOver"), MB_OK);
			exit(0);
		}
		FlushBatchDraw();   //显示画面
	}
	int s = Score();
	FILE* file;
	errno_t err = fopen_s(&file, "scoreinfo.txt", "a");
	fprintf_s(file, "%d\n", s);
	fclose(file);
	EndBatchDraw();        //结束双缓冲
	return 0;
}

总结

这个游戏制作的比较粗糙,我还要改进一下,加个菜单,再在碰砖块的时候加个音效。若大家有建议,欢迎提出。

TC编写 main() { int mode=VGAHI,driver=VGA; char ch; unsigned int l; int i,gi,gj,j,flag=1;/*i,j是循环变量,flag是标记变量,-1:向x负半轴移动,+1:向x正半轴移动*/ double qx,qy,k,b=0.0,speech=0.4,x,y; double r=2.0,bx=60.0,byy=270.0; double pianx=100.0,piany=100.0,tx=20.0,ty=10.0,jx=2.0,jy=2.0; int mx=0,my=0,mb,sum;/*sum纪录砖块的数目*/ FILE * p; if((p = fopen("record.dat", "r")) == NULL) { p=fopen("record.dat","w"); fprintf(p,"0 0 0 0 0\n"); rewind(p); } fclose(p); initgraph(&driver,&mode,"C:\\tc"); setbkcolor(BLUE); Msinit(); Setmouse((int)(pianx+1+bx/2),(int)((tx+jx)*8+pianx-1-bx/2),(int)byy+piany,(int)byy+piany); star: cleardevice();/*程序重载的介入点*/ setcolor(RED); outtextxy(30,20,"^_^ Welcome to Play Hitting Brick Game! ^_^"); sum=0; qx=100.0+pianx-10;qy=180.0+pianx-10;k=0.33; setcolor(7); rectangle((int)(pianx-2),(int)(piany-2),(int)((tx+jx)*8+2+pianx),302+piany); setcolor(1); rectangle((int)(pianx-1),(int)(piany-1),(int)((tx+jx)*8+1+pianx),301+piany); /*读取盘面情况*/ p=fopen("record.dat", "r"); for(i=0;i<5;i++) fscanf(p,"%x ",&zhuan[i]); fclose(p); /*画砖块*/ for(i=0;i<5;i++) { l=1; for(j=0;j<16;j++) { if((zhuan[i]&l)==0) { Draw((int)((jx+tx)*((16*i+j)%8)+pianx+jx),(int)((jy+ty)*((int)((16*i+j)/8))+piany+jy),(int)tx,(int)ty); sum+=1; } l=l*2; } } gotoxy(5,4); printf("Press any key to start game...Q key to quit..."); ch=getch(); if(ch=='q'||ch=='Q') quitgame(); else { gotoxy(5,4); printf(" "); } for(;;) { setfillstyle(1, 0); bar(mx-bx/2,my,mx+bx/2,my+5); Msread(&mx, &my, &mb); Draw(mx-bx/2,my,bx,5); setcolor(0); mycircle(qx,qy,r,0); /*判断求是否反弹*/ if(qx-r<=pianx+1 || qx+r>=(tx+jx)*8+pianx-1) { flag=-flag; k=-k; } if(qy-r<=piany+1) k=-k; for(gi=0;gi<5;gi++) { l=1; for(gj=0;gj<16;gj++) { if((zhuan[gi]&l)==0) { j=(16*gi+gj)/8; i=(16*gi+gj)%8; x=(jx+tx)*i+jx+tx/2+pianx; y=(jy+ty)*j+jy+ty/2+piany; /*边判断1*/ if(qy>=y-ty/2 && qy<=y+ty/2 &&(pow(qx+r-x+tx/2,2)<1 || pow(qx-r-x-tx/2,2)<1)) { flag=-flag; k=-k; zhuan[gi]=zhuan[gi]|l; sum-=1; if(sum==0) wingame(); setfillstyle(1, 0); bar((jx+tx)*i+pianx+jx,(jy+ty)*j+piany+jy,(jx+tx)*i+pianx+jx+tx,(jy+ty)*j+piany+jy+ty); } else /*边判断2*/ if(qx>=x-tx/2 && qx<=x+tx/2 &&(pow(qy+r-y+ty/2,2)<1 || pow(qy-r-y-ty/2,2)<1)) { k=-k; zhuan[gi]=zhuan[gi]|l; sum-=1; if(sum==0) wingame(); setfillstyle(1, 0); bar((jx+tx)*i+pianx+jx,(jy+ty)*j+piany+jy,(jx+tx)*i+pianx+jx+tx,(jy+ty)*j+piany+jy+ty); } else /*角判断*/ if(pow(qx-x+tx/2,2)+pow(qy-y+ty/2,2)<=r*r || pow(qx-x-tx/2,2)+pow(qy-y+ty/2,2)<=r*r || pow(qx-x+tx/2,2)+pow(qy-y-ty/2,2)<=r*r || pow(qx-x-tx/2,2)+pow(qy-y-ty/2,2)<=r*r) { flag=-flag; zhuan[gi]=zhuan[gi]|l; sum-=1; if(sum==0) wingame(); setfillstyle(1, 0); bar((jx+tx)*i+pianx+jx,(jy+ty)*j+piany+jy,(jx+tx)*i+pianx+jx+tx,(jy+ty)*j+piany+jy+ty); } } l=l*2; } } /*棍棒的反弹*/ if(qx<=mx+bx/2 && qx>=mx-bx/2 && pow(qy+r-my,2)<1) { k=-(k/pow(k*k,0.5))*(0.3*bx/(pow(pow(qx-mx,2),0.5)+0.000001)); } if((int)(qy+r)>my+0.5) { gotoxy(5,5); printf("You have lost the game!Press any key to restart..."); getch(); goto star; } b=qy-qx*k; if(flag==1) qx=qx+speech/pow(1.0+k*k,0.5); if(flag==-1) qx=qx-speech/pow(1.0+k*k,0.5); qy=qx*k+b;/*计算球心坐标*/ setcolor(14); mycircle((int)qx,(int)qy,r,14); delay(1); if(mb==1) { gotoxy(5,5); printf("You have lost the game!Press any key to restart..."); getch(); goto star; } } }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值