1.1 此处包含需要用到的头文件,一些声明和定义 声明一个结构体可以让食物,玩家和ai玩家用,避免重复的定义。
#include2.主体函数#include #include #include #include #pragma comment(lib,"Winmm.lib") #define WIDTH 1024 //窗口宽度 #define HEIGHT 640//窗口高度 #define AI_NUM 10 //ai数量 const int Gridsize = 10; //格子宽度 int food_num = 100;//食物数量 int ai_num = 10;//ai玩家数量 //给ai玩家起名字 const char* names[AI_NUM] = { "南夕子","马斯克","地狱烈火","非常可乐","今夜无眠","十大恶人","有你真好","世界和平", "马赛克","统一" }; //制作一个定时器 bool timer(int ms,int id) { static int start[5] = { clock() }; int end = clock(); if (end - start[id] > ms) { start[id] = end; return true; } return false; } //精灵结构体包括坐标,半径,颜色等 struct Sprite { double x;//坐标 double y; double r;//半径 DWORD color;//颜色 char name[20];//名字 bool exist;//判断是否存在 Sprite* food_index;//目标食物 Sprite* next;//结点指针 }; //定义一个玩家结构体 Sprite player; //定义一个食物 Sprite* one_food; //定义ai玩家 Sprite*one_aiplayer; //调用函数 void InitSprite(Sprite* spr, double x, double y,double r, const char* name = "Sprite"); void DrawPlayer(Sprite* spr, bool showName = true); void AddFood(); void CreatFood(); void CreatAIplayer(); void AddAIplayer(int num); void sort(); void ranking(); int game_menu();
2.1 游戏画面初始化和绘制 画出食物,玩家和ai玩家
//初始化数据 void InitData() { srand(unsigned int (time(NULL))); setbkcolor(WHITE); cleardevice();//清空原来颜色 mciSendString("open resource/bk.mp3",NULL,0,NULL); mciSendString("play resource/bk.mp3 repeat", NULL, 0, NULL); InitSprite(&player, rand() % WIDTH, rand() % HEIGHT,10, "必胜客");//初始化玩家 CreatFood();//创建食物头指针 //将所有食物加载出来 for (int i = 0; i < food_num; i++) { AddFood(); } CreatAIplayer(); for (int i = 0; i < ai_num; i++) { AddAIplayer(i); } } //绘制图画 void DrawImg() { //画坐标格子 setlinecolor(RGB(100, 250, 100));//设置格子颜色 for (int i = 0; i <= WIDTH / Gridsize; i++)//画纵坐标线段 { line(i*Gridsize, 0, i*Gridsize, HEIGHT); } for (int k = 0; k <= HEIGHT/Gridsize; k++)//画横坐标线段 { line(0, k*Gridsize, WIDTH, k*Gridsize); } DrawPlayer(&player);//绘制玩家 //绘制食物 Sprite* fp; fp = one_food->next; while (fp != NULL) { DrawPlayer(fp,false); fp = fp->next; } //绘制ai玩家 Sprite* tp; tp = one_aiplayer->next; while (tp != NULL) { DrawPlayer(tp, true); tp = tp->next; } } //操作结构体函数(玩家,食物,ai玩家都适用) void InitSprite(Sprite* spr, double x, double y,double r, const char* name ) { spr->x = x; spr->y = y; spr->r = r; spr->color = RGB(rand()% 256, rand()% 256, rand()% 256); spr->exist = true; strcpy(spr->name, name); spr->food_index = NULL; }
2.2 创建角色 创建玩家,对玩家的按键处理来控制方向移动,用链表来创建食物以及ai玩家,当食物被吃时重新随机刷新。
void DrawPlayer(Sprite* spr, bool showName ) { if (spr->exist) { //绘制图形 setfillcolor(spr->color);//填充颜色 solidcircle(spr->x, spr->y, spr->r);//绘制图形位置 if (showName)//判断是否需要名字 { //绘制文字 setbkmode(TRANSPARENT);//设置文字背景颜色透明 settextcolor(RED);//设置文字颜色红色 settextstyle(18, 0, "黑体");//设置文字大小,粗细,字体 outtextxy(spr->x - textwidth(spr->name) / 2, spr->y - textheight(spr->y) / 2, spr->name);//绘制文字位置 } } } //玩家移动 void MovePlayer(Sprite* spr,double dx,double dy) { spr->x += dx; spr->y += dy; } //按键处理 void KeyDownDeal(Sprite* spr) { int speed = 2; if (GetAsyncKeyState(VK_LEFT) && spr->x >= spr->r) { MovePlayer(&player, -speed, 0); } if (GetAsyncKeyState(VK_RIGHT) && spr->x <= getwidth() - spr->r) { MovePlayer(&player, speed, 0); } if (GetAsyncKeyState(VK_UP) && spr->y >= spr->r) { MovePlayer(&player, 0, -speed); } if (GetAsyncKeyState(VK_DOWN) && spr->y <= getheight() - spr->r) { MovePlayer(&player, 0, speed); } } //创建食物头指针 void CreatFood() { one_food = (Sprite*)malloc(sizeof(Sprite)); if (one_food == NULL) { _exit(0); } one_food->next = NULL; } //增加食物并初始化 void AddFood() { Sprite* pnew; pnew = (Sprite*)malloc(sizeof(Sprite)); if (pnew == NULL)//创建未成功退出 { _exit(0); } pnew->next = NULL; InitSprite(pnew, rand() % getwidth(), rand() % getheight(), 1 + rand() % 3); pnew->next = one_food->next; one_food->next = pnew; } //获得两圆距离 int collision(Sprite* spr1, Sprite* spr2) { return sqrt(pow(spr1->x - spr2->x, 2) + (pow(spr1->y - spr2->y, 2))); } //吃食物长大 void EatFood(Sprite* spr) { Sprite* ftp0; ftp0 = one_food->next; while (ftp0!=NULL) { if (ftp0->exist == false) { ftp0 = ftp0->next; continue; } if (ftp0->exist == true && collision(spr, ftp0) < (spr->r+ftp0->r)) { ftp0 ->exist = false; spr->r += ftp0->r / 8; } ftp0 = ftp0->next; } } //重新刷新食物 void RefreshFood() { Sprite* ftp; ftp = one_food->next; while (ftp!=NULL) { if (ftp->exist == false) { InitSprite(ftp, rand() % getwidth(), rand() % getheight(), 1 + rand() % 3); } ftp = ftp->next; } } //创建ai玩家头指针 void CreatAIplayer() { one_aiplayer = (Sprite*)malloc(sizeof(Sprite)); if (one_aiplayer == NULL) _exit(0); one_aiplayer->next = NULL; } //添加ai玩家 void AddAIplayer(int num) { Sprite* aitp; aitp = (Sprite*)malloc(sizeof(Sprite)); if (aitp == NULL) { _exit(0); } aitp->next = NULL; InitSprite(aitp, rand() % getwidth(), rand() % getheight(), 10,names[num]); aitp->next = one_aiplayer->next; one_aiplayer->next = aitp; }
2.3 ai玩家移动 ai玩家吃食物,ai与玩家和ai与ai之间进行互吃,ai追击目标(也可以自己写一个逃跑)
//判断是否玩家与ai玩家互吃,ai玩家互吃 void DevourEachOther() { //判断玩家与ai玩家之间能否吃掉对方 Sprite* aitemp1; aitemp1 = one_aiplayer->next; while (aitemp1 != NULL) { if (player.exist == true && aitemp1->exist == true && collision(&player, aitemp1) <= player.r&&player.r>=aitemp1->r*1.1) { aitemp1->exist = false; player.r += aitemp1->r / 8; } if (player.exist == true && aitemp1->exist == true && collision(&player, aitemp1) <= aitemp1->r&&player.r*1.1 <= aitemp1->r) { player.exist = false; aitemp1->r += player.r / 8; } //判断ai玩家之间是否能吃掉对方 Sprite* aitemp2; aitemp2 = one_aiplayer->next; while (aitemp2!=NULL) { if (aitemp1 == aitemp2) { aitemp2 = aitemp2->next; continue; } if (aitemp2->exist == true && aitemp1->exist == true && collision(aitemp1, aitemp2) <= aitemp1->r&&aitemp1->r >= aitemp2->r*1.1) { aitemp2->exist = false; aitemp1->r += aitemp2->r / 8; } aitemp2 = aitemp2->next; } //判断ai玩家吃食物 EatFood(aitemp1); aitemp1 = aitemp1->next; } } //追击目标 void chase(Sprite* run, Sprite* chase) { if (run->x > chase->x) { chase->x++; } else { chase->x--; } if (run->y > chase->y) { chase->y++; } else { chase->y--; } } //判断两个精灵距离是否符合要求 bool SpriteOfRelation(Sprite* spr1,Sprite* spr2) { int index_distance = 100;//目标在最小范围才去追击 if (spr2->exist == true && spr1->r > spr2->r*1.5&&collision(spr1,spr2)<=index_distance) { return true; } return false; } //ai玩家找最近的食物 void FandFoods(Sprite* spr) { Sprite* fw;//食物 指针 fw = one_food->next; int min_distance = getheight();//ai玩家与食物最小距离 while (fw != NULL) { if (fw->exist == true) { if (collision(spr, fw) < min_distance) { min_distance = collision(spr, fw); spr->food_index = fw; } } fw = fw->next; } } //ai玩家移动 void AiplayersMove() { Sprite* aimp1 = one_aiplayer->next;//搜索每一个ai玩家 while (aimp1 != NULL) { if (aimp1->exist) { //寻找合适的ai玩家目标 Sprite* aimp2; aimp2 = one_aiplayer->next; while (aimp2 != NULL) { if (aimp1 == aimp2) { aimp2 = aimp2->next; continue; } if (SpriteOfRelation(aimp1, aimp2)) { aimp1->food_index = aimp2;//当发现目标时将目标存入在标记中 } aimp2 = aimp2->next; } //判断能否将玩家作为目标 if (SpriteOfRelation(aimp1, &player)) { aimp1->food_index = &player; } //追击目标必须存在且被追击的目标半径必须小于追击者的半径 if (aimp1->food_index != NULL&&aimp1->food_index->exist == true && aimp1->food_index->r < aimp1->r)//如果标记目标,就追击目标 { chase(aimp1->food_index, aimp1); } else { FandFoods(aimp1);//如果没有其他ai玩家目标,就找小食物 chase(aimp1->food_index, aimp1); } } aimp1 = aimp1->next; } }
2.4 从大到小排序 进行实时排名显示(没有把玩家加入排名,可以自行添加)
//从大到小排序 void sort() { Sprite* p1 = one_aiplayer;//定义第一个结点 Sprite* p2 = NULL;//下一个结点 Sprite* tempt = NULL;//临时结点位置存放点 Sprite* p3 = NULL;//用于存放临时最大数的上一个结点 while (p1->next != NULL) { p2 = p1->next; tempt = p2; p3 = p1; while (p2 != NULL) { //将P2的半径与临时暂存半径进行比较,大的保存在tempt临时指针里 if (tempt->r < p2->r) { tempt = p2; } p2 = p2->next; } while (p3->next != tempt) { p3 = p3->next; } p3->next = tempt->next; tempt->next = p1->next; p1->next = tempt; p1 = p1->next; } } //排名显示 void ranking() { Sprite* r; r = one_aiplayer->next; int i = 0; while (r!=NULL) { settextcolor(RGB(255 - i * 5, 215 - i * 10, 0 + i * 10)); setbkmode(TRANSPARENT); settextstyle(20, 0, "黑体"); char ch[60] = {0}; sprintf(ch, "第%d名:%s", i+1, r->name); outtextxy(WIDTH - 250, i * 30 + 20, ch); i = i % 10; i++; r = r->next; } }3. 其他模块
3.1 菜单界面 包括开始游戏,退出游戏,以及说明(以及返回)
//开始初始化界面菜单UI void InitUI() { //一级菜单 initgraph(WIDTH, HEIGHT);//初始化窗口 setbkcolor(GREEN); cleardevice(); //游戏名 char GameName[20] = { "球球大作战" }; settextcolor(BLUE); settextstyle(60, 20, "黑体"); outtextxy(WIDTH / 2 - textwidth(GameName) / 2, 50, GameName); //三个选项的背景 fillrectangle(430, 200, 580, 260); fillrectangle(430, 300, 580, 360); fillrectangle(430, 400, 580, 460); setbkmode(TRANSPARENT); settextstyle(40, 10, "黑体"); settextcolor(BLACK); //三个选项 char select1[10] = { "开始游戏" }; char select2[10] = { "退出游戏" }; char select3[10] = { "游戏说明" }; outtextxy(430 + textwidth(select1) / 2, 200 + textheight(select1) / 2, select1); outtextxy(430 + textwidth(select2) / 2, 300 + textheight(select2) / 2, select2); outtextxy(430 + textwidth(select3) / 2, 400 + textheight(select3) / 2, select3); } //菜单运行 int game_menu() { InitUI(); //BeginBatchDraw(); MOUSEMSG m1; while (true) { m1 = GetMouseMsg();//获取鼠标操作 if (m1.x >= 430 && m1.x <= 580 && m1.y >= 200 && m1.y <= 260)//游戏开始界面 { setlinecolor(RED); rectangle(430, 200, 580, 260); if (m1.uMsg == WM_LBUTTONDOWN) { InitData(); while (true) { //运行游戏 BeginBatchDraw();//双缓冲绘图 cleardevice();//在每次绘制之前清空屏幕 DrawImg(); KeyDownDeal(&player); EatFood(&player); RefreshFood(); AiplayersMove(); DevourEachOther(); ranking(); sort(); EndBatchDraw(); } } } else if (m1.x >= 430 && m1.x <= 580 && m1.y >= 300 && m1.y <= 360) { setlinecolor(RED); rectangle(430, 300, 580, 360); if (m1.uMsg == WM_LBUTTONDOWN) { closegraph(); _exit(0); } } else if (m1.x >= 430 && m1.x <= 580 && m1.y >= 400 && m1.y <= 460) { setlinecolor(RED); rectangle(430, 400, 580, 460); if (m1.uMsg == WM_LBUTTONDOWN) { //二级菜单 setbkcolor(WHITE); cleardevice(); settextcolor(BLACK); settextstyle(20, 0, "黑体"); setfillcolor(YELLOW); fillrectangle(40, 60, 140, 100); setbkmode(TRANSPARENT); char returna[20] = { "返回上一层" }; outtextxy(40 +(50 - textwidth(returna) / 2), 60 + textheight(returna) / 2, returna); outtextxy(200, 100, "感谢大家的支持,有不足之处,请多多指正 ,请使用方向键,控制方向!!"); MOUSEMSG m2;//第二层使用鼠标 while (true) { m2 = GetMouseMsg(); if (m2.x >= 40 && m2.x <= 140 && m2.y >= 60 && m2.y <= 100)//游戏开始界面 { setlinecolor(RED); rectangle(40, 60, 140, 100); if (m2.uMsg == WM_LBUTTONDOWN) { closegraph(); InitUI(); break; } } else { setlinecolor(YELLOW); rectangle(40, 60, 140, 100); } } } } else { setlinecolor(WHITE); rectangle(430, 200, 580, 260); rectangle(430, 300, 580, 360); rectangle(430, 400, 580, 460); } } }
3.2 主函数人口 内容不是太多,可以自行添加
//主窗口 int main() { game_menu(); closegraph(); return 0; }