目录
球球大作战
一、开发环境
二、流程图预览
三、代码逻辑
1、初始化时间
2、设置开始界面大小
3、设置开始界面
4、让玩家选择速度
5、设置玩家小球、人机小球、食物的属性
6、一次性把图绘制到界面里
7、进入死循环
8、移动玩家小球
9、移动人机
10、食物刷新
11、画出边界、食物、人机、玩家、小地图,添加击杀数、半径(质量)、玩家名字
12、将内存中的绘图操作批量地绘制到屏幕上,从而提高绘图效率
四、代码预览+运行结果
球球大作战
一、开发环境
VisualStudio专业版:
所需依赖库:EasyX库
二、流程图预览
由于上面的比较模糊,下面分开截取流程图图片
三、代码逻辑
1、初始化时间
starttime();
2、设置开始界面大小
initgraph(WIDTH, HEIGHT);
3、设置开始界面
void start() {
setbkcolor(WHITE); // 白色背景
cleardevice(); // 初始化背景
settextcolor(BLACK); // 改字体
setbkmode(TRANSPARENT); //设置背景是透明的
//加载背景图片
IMAGE image;
loadimage(&image, _T("D:\\桌面\\file2\\球球大作战\\source\\Ball Ball\\x64\\img\\10.jpg"), WIDTH, HEIGHT);
//在当前设备上绘制指定图像
putimage(0, 0, &image);
//加入提示信息
settextcolor(WHITE);
settextstyle(22, 0, _T("黑体"));
outtextxy(50, 480, _T("请选择关卡:"));
outtextxy(50, 550, _T("1.小试牛刀"));
outtextxy(200, 550, _T("2.炉火纯青"));
outtextxy(350, 550, _T("3.登峰造极"));
settextstyle(15, 0, _T("宋体"));
outtextxy(600, 550, _T("注:按序号选择,默认选择关卡1;游戏中按空格键可以暂停。"));
settextcolor(BLACK);
settextstyle(22, 0, _T("黑体"));
TCHAR s[5];
//_stprintf(s, _T("%d"), readCount());
_stprintf_s(s, _countof(s), _T("%d"), readCount());
outtextxy(810, 200, _T("欢迎进入游戏!"));
outtextxy(810, 240, _T("游戏次数:"));
outtextxy(910, 240, s);
}
4、让玩家选择速度
void ChooseSpeed() {
switch (_getch()) {
case 1:speed = 4;
case 2:speed = 3;
case 3:speed = 2;
default:speed = 4;
}
}
5、设置玩家小球、人机小球、食物的属性
void setall() {
srand((unsigned)time(NULL)); // 随机数
mover.color = RGB(rand() % 256, rand() % 256, rand() % 256); // 随机颜色
mover.life = 1; // 赋初值1
//把玩家的球一直放在屏幕中央
mover.x = int(WIDTH * 0.5);
mover.y = int(HEIGHT * 0.5);
//设置半径
mover.r = 20;
for (int i = 0; i < AINUM; i++) { // AI 的属性
ai[i].life = 1;
ai[i].color = RGB(rand() % 256, rand() % 256, rand() % 256);
//ai[i].r = float(rand() % 10 + 10);
ai[i].r = 20;
ai[i].x = rand() % (MAPW - int(ai[i].r + 0.5)) + int(ai[i].r + 0.5);
ai[i].y = rand() % (MAPH - int(ai[i].r + 0.5)) + int(ai[i].r + 0.5);
}
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;
}
pBuffer = GetImageBuffer(NULL); // 获取显存指针
setbkcolor(WHITE); // 白色背景
cleardevice(); // 初始化背景
settextcolor(LIGHTRED); // 改字体
//设置背景是透明的
setbkmode(TRANSPARENT);
settextstyle(16, 0, _T("宋体"));
}
6、一次性把图绘制到界面里
BeginBatchDraw();
7、进入死循环
8、移动玩家小球
void move(BALL* ball) {
if (ball->r <= 0)
ball->life = false;
if (ball->life == false) { // 判定游戏是否接束
HWND hwnd = GetHWnd();
MessageBox(hwnd, _T("你被吃了"), _T("游戏结束"), MB_ICONEXCLAMATION);
endtime();
}
if (eaten + ai_eaten == AINUM) // 是否吃掉所有 AI
{
HWND hwnd = GetHWnd();
MessageBox(hwnd, _T("恭喜过关"), _T("游戏结束"), MB_OK | MB_ICONEXCLAMATION); // 结束
endtime();
}
for (int i = 0; i < AINUM; i++) { // 玩家吃 AI 判定
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 = sqrt(ai[i].r * ai[i].r + ball->r * ball->r);
eaten++;
}
}
}
for (int n = 0; n < FNUM; n++) { // 玩家吃食物
if (food[n].eat == 0) continue;
if (DISTANCE(ball->x, ball->y, food[n].x, food[n].y) < ball->r) {
ball->r += 4 / ball->r; // 增加面积
food[n].eat = 0; // 食物被吃
}
}
static int mx = 0, my = 0; // 记录偏移量
//判断怎么移动
if (GetAsyncKeyState(VK_UP) && (ball->y - ball->r > 0 && ball->y <= (MAPH - ball->r + 10))) {
ball->y -= speed;
my += speed;
}
if (GetAsyncKeyState(VK_DOWN) && (ball->y - ball->r >= -10 && ball->y < (MAPH - ball->r))) {
ball->y += speed;
my -= speed;
}
if (GetAsyncKeyState(VK_LEFT) && ball->x - ball->r > 0 && (ball->x <= (MAPW - ball->r + 10))) {
ball->x -= speed;
mx += speed;
}
if (GetAsyncKeyState(VK_RIGHT) && ball->x - ball->r >= -10 && (ball->x < (MAPW - ball->r))) {
ball->x += speed;
mx -= speed;
}
//判断是否输入其他用于退出、暂停、继续的按键
if (GetAsyncKeyState(VK_SPACE)) {
settextcolor(WHITE);
settextstyle(32, 0, _T("宋体"));
outtextxy(384 - mx, 350 - my, _T("游戏已暂停!"));
outtextxy(20 - mx, 500 - my, _T("(ESC)退出"));
outtextxy(780 - mx, 500 - my, _T("(回车键)继续"));
FlushBatchDraw();
_getch();
if (GetAsyncKeyState(VK_ESCAPE))
exit(0);
else
_getch();
}
setorigin(mx, my); //坐标修正
}
9、移动人机
void AI() {
for (int i = 0; i < AINUM; i++) { // AI 吃玩家
if (ai[i].r > mover.r) {
if (DISTANCE(mover.x, mover.y, ai[i].x, ai[i].y) < (ai[i].r + mover.r)) {
ai[i].r = sqrt(ai[i].r * ai[i].r + mover.r * mover.r);
mover.life = 0;
mover.r = 0;
}
}
for (int j = 0; j < AINUM; j++) { // AI 吃 AI
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) < (ai[i].r + ai[j].r)) {
ai[i].r = sqrt(ai[i].r * ai[i].r + ai[j].r * ai[j].r);
ai[j].life = 0;
ai[j].r = 0;
ai_eaten++;
}
}
}
double min_DISTANCE = 100000;
int min = -1;
for (int k = 0; k < AINUM; k++) { // AI 靠近 AI
if (ai[i].r > ai[k].r && ai[k].life != 0) {
if (DISTANCE(ai[i].x, ai[i].y, ai[k].x, ai[k].y) < min_DISTANCE) {
min_DISTANCE = DISTANCE(ai[i].x, ai[i].y, ai[k].x, ai[k].y);
min = k;
}
}
}
if ((min != -1) && (rand() % 2 == 1) && (ai[i].y - ai[i].r > 0 && ai[i].y <= (MAPH - ai[i].r + 10))) {
if (rand() % 2) {
if (ai[i].x < ai[min].x)
ai[i].x += speed ;
else
ai[i].x -= speed ;
}
else {
if (ai[i].y < ai[min].y)
ai[i].y += speed ;
else
ai[i].y += speed ;
}
}
for (int n = 0; n < FNUM; n++) { // AI 吃食物
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;
}
}
}
}
10、食物刷新
void Food() {
for (int i = 0; i < FNUM; i++) { // 食物刷新
if (food[i].eat == 0) {
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;
}
}
}
11、画出边界、食物、人机、玩家、小地图,添加击杀数、半径(质量)、玩家名字
void draw() {
float asp = 1;
//清空裁剪区,超出区域的绘图操作将被裁剪掉
clearcliprgn();
IMAGE image;
loadimage(&image, _T(""), WIDTH * 4, HEIGHT * 4);
putimage(0, 0, &image);
//绘画出一个矩形框
setlinestyle(PS_SOLID | PS_JOIN_BEVEL, 20); // 改变笔的颜色、状态
setlinecolor(RGB(0, 100, 0));
line(-20, MAPH + 20, -20, -20); // 左竖
line(-20, MAPH + 20, MAPW + 20, MAPH + 20); // 上横
line(-20, -20, MAPW + 20, -20); // 下横
line(MAPW + 20, -20, MAPW + 20, MAPH + 20); // 右竖
setfillcolor(GREEN);
//当球接近边界时,会在边界外侧绘制一个绿色的填充矩形,
// 是用来表示碰撞或者边界的视觉效果
if (mover.x - 0.5 * WIDTH / asp < -20)
floodfill(-20 - 11, mover.y, RGB(0, 100, 0));
if (mover.x + 0.5 * WIDTH / asp > MAPW + 20)
floodfill(MAPW + 20 + 11, mover.y, RGB(0, 100, 0));
if (mover.y - 0.5 * HEIGHT / asp < -20)
floodfill(mover.x, -20 - 11, RGB(0, 100, 0));
if (mover.y + 0.5 * HEIGHT / asp > MAPH + 20)
floodfill(mover.x, MAPH + 20 + 11, RGB(0, 100, 0));
setlinecolor(WHITE);
setlinestyle(PS_NULL);
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;
}
}
for (int i = 0; i < AINUM; i++) { // 画 AI
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));
IMAGE map(150, 100); // 小地图
SetWorkingImage(&map);
setbkcolor(RGB(120, 165, 209)); // 浅灰色背景
cleardevice();
for (int i = 0; i < AINUM; i++) // 画 AI(小地图)
{
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 + 1.5));
}
setfillcolor(mover.color); // 画玩家(小地图)
fillcircle(mover.x * 150 / WIDTH / 4, mover.y * 100 / HEIGHT / 4, int(mover.r / 28 + 3.5));
setlinecolor(RGB(0, 100, 0));
SetWorkingImage(); // 恢复绘图背景
putimage(mover.x + int(0.5 * WIDTH) - 150, mover.y - int(0.5 * HEIGHT), 150, 100, &map, 0, 0); // 画出小地图
setlinecolor(LIGHTBLUE);
setlinestyle(PS_SOLID | PS_JOIN_BEVEL, 4);
line(mover.x + int(0.5 * WIDTH) - 151, mover.y - int(0.5 * HEIGHT), mover.x + int(0.5 * WIDTH) - 151, mover.y - int(0.5 * HEIGHT) + 99); // 地图边框线
line(mover.x + int(0.5 * WIDTH) - 151, mover.y - int(0.5 * HEIGHT) + 99, mover.x + int(0.5 * WIDTH), mover.y - int(0.5 * HEIGHT) + 99); // 地图边框线
setlinestyle(PS_NULL); // 恢复笔
TCHAR str[32];
swprintf_s(str, _T("质量:%.1fg 击杀:%d"), mover.r, eaten);
settextcolor(WHITE); // 改字体
outtextxy(mover.x - int(0.5 * WIDTH), mover.y - int(0.5 * HEIGHT), str);
settextcolor(WHITE);
outtextxy(mover.x - 20, mover.y, _T("itlsl"));
}
12、将内存中的绘图操作批量地绘制到屏幕上,从而提高绘图效率
FlushBatchDraw();
四、代码预览+运行结果
源代码+流程图+README资源:https://download.csdn.net/download/2403_82436914/90623088?spm=1001.2014.3001.5503