windows游戏编程:球球大作战吃鸡版(C语言游戏开发)

您所在的位置:网站首页 球球大作战设计思路 windows游戏编程:球球大作战吃鸡版(C语言游戏开发)

windows游戏编程:球球大作战吃鸡版(C语言游戏开发)

2024-07-16 07:20| 来源: 网络整理| 查看: 265

球球大作战: 前言:

本游戏用到了图形界面库graphics.h,图形界面库下载安装:https://blog.csdn.net/alzzw/article/details/100043681

下方有源码。

 

功能实现:

玩家球,AI球,食物,及三者之间的吞食,毒圈,小地图等

所以要完成上面的功能,我们要:

界面的绘制(按开始键开始游戏)游戏场景绘制(地图的大小,边界等)角色(玩家球,AI球,食物)的初始化角色的绘制玩家与AI的移动,吞食,死亡判定AI算法小地图

 

具体实现: 界面:

我们先设置窗口背景颜色,清理画布,然后设置文字颜色,设置窗口透明,设置文字大小,字体与内容

void start() { setbkcolor(WHITE); //设置背景颜色为白色 cleardevice(); //清理画布 settextcolor(RED); // 设置文字颜色 setbkmode(TRANSPARENT); //设置窗口透明 settextstyle(128, 0, "宋体"); //字符集问题 outtextxy(100, 40, "球球大作战"); settextstyle(32, 0, "宋体"); outtextxy(384, 500, "按任意键开始游戏"); getch(); }

效果:

地图绘制:

定义宏与全局变量:屏幕的宽,屏幕高,地图的宽 地图的高,及地图边界点(lx,ly,rx,ry)已知两点可以画出一个矩形

#define WIDTH 1024 //屏幕的宽 #define HEIGHT 576 //屏幕高 #define MAPW (WIDTH*4) //地图的宽 #define MAPH (HEIGHT*4) //地图的高 int relx = -20, rely = MAPH + 20, rerx = MAPW + 20, rery = -20;

绘制:画线

对线进行风格设置

clearcliprgn(); //清理画板 setlinestyle(PS_SOLID | PS_JOIN_BEVEL, 20); //设置线的风格样式 setlinecolor(RGB(0, 100, 0)); //线的颜色

画  左竖 上横 下横 右竖,并填充为绿色

//左竖 上横 下横 右竖 line(relx, rely, relx, rery); line(relx, rely, rerx, rely); line(relx, rery, rerx, rery); line(rerx, rery, rerx, rely); setfillcolor(GREEN);

效果:

初始化:

1.食物,球的结构体(食物,AI是用结构体数组存储的)

struct FOOD { bool eat; //是否被吃 COLORREF color; //颜色 int x, y; //坐标 char type; //食物的类型(即形状) }; struct BALL //小球结构 { bool life; //生命 COLORREF color; //颜色 int x, y; //坐标 float r; //半径 };

2.设置:

#define AINUM 200 //AI数量 #define FNUM 2000 //食物数量

初始化:

FOOD food[FNUM]; //结构体数组 元素类型是所创建结构体的类型 BALL mover = { 1, RGB(0, 0, 0), 0, 0, 0 }; //玩家初始化 BALL ai[AINUM] = { 1, RGB(0, 0, 0), 0, 0, 0 }; //Ai初始化

玩家和AI设定思想:利用随机数对玩家和Ai的颜色进行初始化(RGB(rand() % 256, rand() % 256, rand() % 256))

玩家的位置无论怎么移动都一直固定在地图的中央:

玩家:

srand((unsigned int)time(NULL)); //随机函数种子 mover.color = RGB(rand() % 256, rand() % 256, rand() % 256);//rand()%256 随机取值 0~255 mover.life = 1; mover.x = int(WIDTH*0.5); //将玩家固定在屏幕中心处 mover.y = int(HEIGHT*0.5); mover.r = 20;

AI:

for (int i = 0; i < AINUM; i++) { ai[i].color = RGB(rand() % 256, rand() % 256, rand() % 256); //rand()%256 随机取值 0-255 ai[i].life= 1; ai[i].x = rand() % (MAPW - int(ai[i].r + 0.5)) + int(ai[i].r + 0.5); //AI产生的位置不会出现一个越界 ai[i].y = rand() % (MAPH - int(ai[i].r + 0.5)) + int(ai[i].r + 0.5); ai[i].r = float(rand()%10+10); }

食物:

for (int i = 0; i < FNUM; i++) { food[i].eat = 1; food[i].color = RGB(rand() % 256, rand() % 256, rand() % 256); //位置随机出现 food[i].x = rand() % MAPW; food[i].y = rand() % MAPH; food[i].type = rand() % 10 + 1; } 绘制玩家,AI及食物:

食物形状的绘制:

随机数产生1~10的数,case不同的数,绘制不同的形状(圆形,长方形,矩形等)大小也随机

for (int i = 0; i < FNUM; i++) { if (food[i].eat == 0) continue; setfillcolor(food[i].color); switch (food[i].type) //食物的形状 { case 1: solidellipse(food[i].x, food[i].y, food[i].x + 2, food[i].y + 4); break; case 2: solidellipse(food[i].x, food[i].y, food[i].x + 4, food[i].y + 2); break; case 3: solidrectangle(food[i].x, food[i].y, food[i].x + 4, food[i].y + 2); break; case 4: solidrectangle(food[i].x, food[i].y, food[i].x + 2, food[i].y + 4); break; case 5: solidroundrect(food[i].x, food[i].y, food[i].x + 2, food[i].y + 4, 2, 2); break; case 6: solidroundrect(food[i].x, food[i].y, food[i].x + 4, food[i].y + 2, 2, 2); break; case 7: solidroundrect(food[i].x, food[i].y, food[i].x + 4, food[i].y + 2, 4, 2); break; case 8: solidroundrect(food[i].x, food[i].y, food[i].x + 4, food[i].y + 2, 2, 4); break; case 9: solidroundrect(food[i].x, food[i].y, food[i].x + 4, food[i].y + 2, 1, 1); break; case 10: fillcircle(food[i].x, food[i].y, 4); break; } }

玩家及AI的绘制:

AI数量多,所以for循环绘制,玩家和AI的绘制方法相同

//画出AI for (int i = 0; i < AINUM; i++) { if (ai[i].life == 0) continue; setfillcolor(ai[i].color); fillcircle(ai[i].x, ai[i].y, int(ai[i].r + 0.5)); //四舍五入 } //画玩家 setfillcolor(mover.color); fillcircle(mover.x, mover.y, int(mover.r + 0.5)); 移动:

判断距离的宏函数:

#define DISTANCE(x1,y1,x2,y2) (sqrt((float)(x1-x2)*(float)(x1-x2)+(float)(y1-y2)*(float)(y1-y2))) //两点之间的距离公式 玩家移动:

通过读取玩家按键盘的键位,获取上下左右(WASD)

static int mx = 0, my = 0; //记录偏移量 if (GetAsyncKeyState(65) & 0x8000) { ball->x -= 3, mx += 3; } //获取键盘的值 if (GetAsyncKeyState(87) & 0x8000) { ball->y -= 3, my += 3; } if (GetAsyncKeyState(83) & 0x8000) { ball->y += 3, my-= 3; } if (GetAsyncKeyState(68) & 0x8000) { ball->x += 3, mx -= 3; } /*awsd*/ setorigin(mx, my); //坐标修正

如果超出边界,慢慢变小,半径逐渐减小

if (ball->x > (MAPW - ball->r) || ball->x - ball->r < 0 || ball->y - ball->ry>(MAPH - ball->r)) ball->r -= 0.1f;

死亡判断:

弹出对话框,显示“你已经死亡,游戏结束”

if (ball->life == false) { HWND hwnd = GetHWnd(); //获取窗口句柄 MessageBox(hwnd, "你已经死亡", "游戏结束", MB_OK | MB_ICONEXCLAMATION); closegraph(); exit(0); }

玩家吃AI:

遍历AI,如果玩家的半径比AI的大,说明玩家可以吃AI,如果AI是存活的,在玩家的球吞食AI   1/5时,AI死亡,玩家的球半径增大

for (int i = 0; i < AINUM; i++) { if (ball->r >= ai[i].r) { if (ai[i].life == 0) continue; if (DISTANCE(ball->x, ball->y, ai[i].x, ai[i].y) < (4 / 5.0*(ball->r + ai[i].r))) { ai[i].life = 0; //吃掉AI ball->r += (ai[i].r*ai[i].r / 2) / ball->r; eaten++; } } }

吃食物:同理吃AI,只是不用判断是否为4/5

for (int n = 0; n < FNUM; n++) { if (food[n].eat == 0) continue; if (DISTANCE((float)ball->x, (float)ball->y, (float)food[n].x, (float)food[n].y) < ball->r) { ball->r += 4 / ball->r; food[n].eat = 0; } } AI移动:

AI吃玩家:和玩家吃AI同理

if (ai[i].r>mover.r) { if (DISTANCE(mover.x, mover.y, ai[i].x, ai[i].y) < 2 / 3.0*ai[i].r + mover.r) { ai[i].r += (mover.r*mover.r) / ai[i].r; mover.life = 0; mover.r = 0; } }

AI吃AI:和玩家吃AI同理

//AI吃AI for (int j = 0; j < AINUM;j++) { if (ai[i].r>ai[j].r) { if (ai[j].life == 0) continue; if (DISTANCE(ai[i].x, ai[i].y, ai[j].x, ai[j].y) < 4 / 5.0*ai[i].r + ai[j].r) { ai[i].r += (ai[j].r*ai[j].r) / ai[i].r; ai[j].life = 0; ai_eaten++; } } }

AI与AI之间的追逐与远离:

假的AI [i],如果有AI [k]的半径小于AI [i],且AI [k]存活,两球圆心之间的距离小于最小值的某一个值,则追赶,反之远离,因为是遍历,所以在追逐过程中如果有距离更小的会改变追逐方向

double min_DISTANCE = 100000; int min = -1; //AI靠近AI for (int k = 0; k < AINUM; k++) { if (ai[i].r>ai[k].r&&ai[k].life == 1) { if (DISTANCE(ai[i].x, ai[k].x, ai[i].y, ai[k].y) < min_DISTANCE) { min_DISTANCE = DISTANCE(ai[i].x, ai[k].x, ai[i].y, ai[k].y); min = k; } } } if ((min != -1) && (rand() % 2 == 1)) { if (rand() % 2) { if (ai[i].x < ai[min].x) ai[i].x += 2; else ai[i].x-=2; } else { if (ai[i].y < ai[min].y) ai[i].y += 2; else ai[i].y -= 2; } } for (int n = 0; n < FNUM; n++) { if (food[n].eat == 0) continue; if (DISTANCE(ai[i].x, ai[i].y, food[n].x, food[n].y) < ai[i].r) { ai[i].r += 4 / ai[i].r; food[n].eat = 0; } } 小地图:

小地图的绘制原理就是,在图形界面上再次创建一个新的图形界面,在整个地图的右上角按比例缩放  大地图,把玩家,AI,食物的位置

等比例绘到新的界面

IMAGE map(150, 100); //小地图 SetWorkingImage(&map); setbkcolor(RGB(120, 165, 209)); cleardevice(); //相当于我们重新创建了一个图形界面 for (int i = 0; i < AINUM; i++) { if (ai[i].life == 0) continue; setfillcolor(ai[i].color); fillcircle(ai[i].x * 150 / WIDTH / 4, ai[i].y * 100 / HEIGHT / 4, int(ai[i].r / 28 + 0.5)); } //画玩家 setfillcolor(mover.color); fillcircle(mover.x * 150 / WIDTH / 4, mover.y * 100 / HEIGHT / 4, int(mover.r / 28 + 0.5)); SetWorkingImage(); //恢复之前的绘图界面 putimage(mover.x + int(0.5*WIDTH) - 150, mover.y - int(0.5*HEIGHT), 150, 100, &map, 0, 0);

 

这个AI的算法只是特别简单的,AI比较幼稚,或者说是傻,如果大家想写一些NB一点的,可以搜一下四叉树

 

源码

 

 

 



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3