这篇文章主要为大家详细介绍了C语言实现——《球球大作战项目》,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
游戏介绍:
这是一个大球吃小球的世界,玩家的目标是要努力吃成最大的球球。在游戏一开始,玩家出现在地图上随机位置,地图里洒满了小彩豆,玩家吃掉小彩豆体积就会增大,当增大到比别人的球大时,就可以吃别人的球啦,当然也得躲避更大的球。小球速度快,大球速度慢。球球到达一定重量后,可以分身,一个球变成等大的两个球,可以再次分身,但是最多可以拥有16个分身。
效果图展示
1.操作方式:
使用键盘控制球体的移动,如W、A、S、D 或者 上、下、左、右方向键。
2.玩法:
玩家控制的球体通过键盘控制进行移动,可以朝不同的方向移动以吞噬其他球体。
球体可以通过碰撞来吞噬其他较小的球体,从而增大自己的体积。
此处包含需要用到的头文件,一些声明和定义 声明一个结构体可以让食物,玩家和ai玩家用,避免重复的定义。
#include
#include //包含图形库头文件
#include
#include
#include
#define FOOD_NUM 200 //食物数量
#define WIDTH 1024
#define HEIGHT 640
#define HEIFGT 10
#define AI_NUM 10//人工智障数量
/*
做游戏:1.窗口,画图,贴图。。。。。easyx图形库的
食物:圆(圆心坐标,半径,颜色,是否存在的标志)
*/
并且要明确要定义的对象
如下
struct Ball//小球结构,玩家,食物,人工智障
{
int x;
int y;
int r;//半径
DWORD color;//颜色
bool flag;//是否存在
};
struct Ball food[FOOD_NUM];
struct Ball player;//玩家
struct Ball ai[AI_NUM];
1.设置随机数种子:通过调用 srand((unsigned)time(NULL)),将随机数种子设置为当前时间的秒数,以确保每次游戏运行时生成的随机数序列是不同的。
2.初始化食物数据:通过循环遍历每个食物球体,为每个食物设置随机的位置、半径、颜色,并将存在标志 flag 设置为 true,表示初始状态下食物存在。
3.初始化玩家数据:生成玩家球体的初始位置、半径、颜色,并将存在标志 flag 设置为 true。
4.初始化人工智能角色(AI)数据:通过循环遍历每个 AI 角色,为每个角色设置随机的位置、半径和颜色,并将存在标志 flag 设置为 true。
void GameInit()
{
//设置随机数种子,时间是在不断变化的
srand((unsigned)time(NULL));
for (int i = 0;i < FOOD_NUM; ++i)
{
food[i].x = rand() % WIDTH;
food[i].y = rand() % HEIGHT;
food[i].flag = true;//刚开始食物存在
food[i].r = rand() % 6 + 1;
food[i].color = RGB(rand() % 256, rand() % 256, rand() % 256);
}
//初始化玩家数据
player.x = rand() & WIDTH+1;
player.y = rand() % HEIGHT+1;
player.r = 18;//只要比食物大就可以
player.flag = true;
player.color= RGB(rand() % 256, rand() % 256, rand() % 256);
//初始化ai
for (int i = 0; i < AI_NUM; ++i)
{
ai[i].x = rand() % WIDTH;
ai[i].y = rand() % HEIGHT;
ai[i].r = rand() % 10 + 5;
ai[i].flag = true;
ai[i].color= RGB(rand() % 256, rand() % 256, rand() % 256);
}
}
BeginBatchDraw() 函数开始绘制,这可以避免屏幕闪烁。
setbkcolor(WHITE) 设置背景色为白色。
cleardevice() 清空屏幕,将整个屏幕颜色填充为背景色。
循环遍历每个食物球体(FOOD_NUM 个),进行绘制。
如果 food[i].flag 为真,表示食物存在,则根据 food[i] 的坐标和半径,绘制一个填充圆,颜色为 food[i].color。
否则,表示食物已经消失,在随机位置重新生成一个食物。
void GameDraw()
{
//防止闪屏
BeginBatchDraw();
setbkcolor(WHITE);
cleardevice();//清屏
for (int i = 0; i < FOOD_NUM; ++i)
{
if (food[i].flag)
{
setfillcolor(food[i].color);//设置填充颜色
solidcircle(food[i].x, food[i].y,food[i].r);//画一个填充圆
}
else
{
food[i].x = rand() % WIDTH;
food[i].y = rand() % HEIGHT;
food[i].flag = true;//刚开始食物存在
food[i].r = rand() % 6 + 1;
food[i].color = RGB(rand() % 256, rand() % 256, rand() % 256);
}
}
绘制玩家球体:
设置填充颜色为 player.color。
根据玩家的坐标和半径,绘制一个填充圆。
设置文本颜色为绿色。
设置文本样式为大小为 30、字体为黑体。
设置文字背景为透明。
输出文字在玩家球体的位置上。
//绘制玩家
setfillcolor(player.color);//设置填充颜色
solidcircle(player.x, player.y, player.r);
settextcolor(GREEN);
settextstyle(30, 0, L"黑体");
setbkmode(0);//设置文字背景透明
outtextxy(player.x-50, player.y, L"我是最靓的崽仔");
EndBatchDraw();
//绘制ai
for (int i = 0; i < AI_NUM;++i)
{
if (ai[i].flag)
{
setfillcolor(ai[i].color);
solidcircle(ai[i].x, ai[i].y, ai[i].r);
}
}
}
1.GetAsyncKeyState() 函数用于获取键盘消息,判断是否按下了特定的键。
2.键盘控制包括上、下、左、右四个方向键。根据按下的键,判断移动的方向并更新玩家球体的位置。
3.如果按下了 ‘W’ 键或者上方向键 (VK_UP),并且玩家球体的 y 坐标大于等于 0,则向上移动,即 player.y -= speed。
其他位置都如上
4.根据按键的状态和边界限制,函数根据移动速度 speed 更新玩家球体的 函数
void keyControl(int speed)
{
//获取键盘消息,按的是哪一个键
if ((GetAsyncKeyState('W') || GetAsyncKeyState(VK_UP))&&player.y>=0)//按了上键
{
player.y -= speed;
}
if ((GetAsyncKeyState('S') || GetAsyncKeyState(VK_DOWN))&& player.y<=HEIGHT)//按了下键
{
player.y += speed;
}
if ((GetAsyncKeyState('A') || GetAsyncKeyState(VK_LEFT))&& player.x >= 0)//按了左键
{
player.x -= speed;
}
if ((GetAsyncKeyState('D') || GetAsyncKeyState(VK_RIGHT))&& player.x<=WIDTH)//按了右键
{
player.x += speed;
}
}
特别注意边界,不能越界
函数通过遍历所有的食物球体,检查玩家球体是否和食物重叠,并进行吃食物的操作。
对于每一个食物球体:
如果食物存在(food[i].flag 为真)且食物球体和玩家球体的距离小于等于玩家球体的半径 player.r,则表示玩家球体吃到了该食物。
吃到食物后,将食物的存在标志 food[i].flag 设置为假,表示食物消失。
将玩家球体的半径 player.r 增加吃到食物球体半径 food[i].r 的1/4,以增加玩家球体的大小。
其中为判断是否被吃,也就是距离问题,需要引入求距离函数
double distance(struct Ball a, struct Ball b)
{
return sqrt((double)(a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
玩家吃食物
void eatFood()
{
for (int i = 0; i < FOOD_NUM; ++i)
{
if (food[i].flag && distance(food[i], player) <= player.r)
{
food[i].flag = false;
player.r += food[i].r / 4;
}
}
}
1.对于每个AI角色(AI_NUM 个):
2.如果AI角色存在(ai[i].flag 为真),则分别生成随机的X轴和Y轴偏移量。
3.X轴偏移量通过 rand() % 5 - 2 生成一个介于-2到2之间的随机数。
4.Y轴偏移量同样通过 rand() % 5 - 2 生成一个介于-2到2之间的随机数。
5.将随机生成的偏移量加到AI角色的当前位置上,实现随机移动的效果。
void aiMove()
{
for (int i = 0; i < AI_NUM; ++i)
{
if (ai[i].flag)
{
ai[i].x += rand() % 5 - 2; // 生成随机的X轴偏移量
ai[i].y += rand() % 5 - 2; // 生成随机的Y轴偏移量
}
}
}
以下是优化版本
void aiMove()
{
for (int i = 0; i < AI_NUM; ++i)
{
if (ai[i].flag)
{
int xOffset = rand() % 5 - 2; // 生成随机的X轴偏移量
int yOffset = rand() % 5 - 2; // 生成随机的Y轴偏移量
ai[i].x += xOffset;
ai[i].y += yOffset;
// 限制AI角色移动的范围,确保不超出窗口边界
if (ai[i].x < 0)
ai[i].x = 0;
else if (ai[i].x > WIDTH)
ai[i].x = WIDTH;
if (ai[i].y < 0)
ai[i].y = 0;
else if (ai[i].y > HEIGHT)
ai[i].y = HEIGHT;
}
}
}
优化说明:
1.使用变量 xOffset 和 yOffset 存储随机生成的X轴和Y轴的偏移量,以减少重复的随机数生成。
2.限制AI角色移动的范围,确保AI角色的位置不会超出窗口的边界。通过比较偏移量后的位置和窗口宽度和高度的大小来限制移动范围。
int main()
{
initgraph(WIDTH, HEIGHT);
GameInit();
while (1)
{
keyControl(1);
GameDraw();
eatFood();
aiMove();
}
getchar();
return 0;
}
#include
#include //包含图形库头文件
#include
#include
#include
#define FOOD_NUM 200 //食物数量
#define WIDTH 1024
#define HEIGHT 640
#define HEIFGT 10
#define AI_NUM 10//人工智障数量
/*
做游戏:1.窗口,画图,贴图。。。。。easyx图形库的
食物:圆(圆心坐标,半径,颜色,是否存在的标志)
*/
struct Ball//小球结构,玩家,食物,人工智障
{
int x;
int y;
int r;//半径
DWORD color;//颜色
bool flag;//是否存在
};
struct Ball food[FOOD_NUM];
struct Ball player;//玩家
struct Ball ai[AI_NUM];
void GameInit()
{
//设置随机数种子,时间是在不断变化的
srand((unsigned)time(NULL));
for (int i = 0; i < FOOD_NUM; ++i)
{
food[i].x = rand() % WIDTH;
food[i].y = rand() % HEIGHT;
food[i].flag = true;//刚开始食物存在
food[i].r = rand() % 6 + 1;
food[i].color = RGB(rand() % 256, rand() % 256, rand() % 256);
}
//初始化玩家数据
player.x = rand() & WIDTH + 1;
player.y = rand() % HEIGHT + 1;
player.r = 18;//只要比食物大就可以
player.flag = true;
player.color = RGB(rand() % 256, rand() % 256, rand() % 256);
//初始化ai
for (int i = 0; i < AI_NUM; ++i)
{
ai[i].x = rand() % WIDTH;
ai[i].y = rand() % HEIGHT;
ai[i].r = rand() % 10 + 5;
ai[i].flag = true;
ai[i].color = RGB(rand() % 256, rand() % 256, rand() % 256);
}
}
//绘制
void GameDraw()
{
//防止闪屏
BeginBatchDraw();
setbkcolor(WHITE);
cleardevice();//清屏
for (int i = 0; i < FOOD_NUM; ++i)
{
if (food[i].flag)
{
setfillcolor(food[i].color);//设置填充颜色
solidcircle(food[i].x, food[i].y,food[i].r);//画一个填充圆
}
else
{
food[i].x = rand() % WIDTH;
food[i].y = rand() % HEIGHT;
food[i].flag = true;//刚开始食物存在
food[i].r = rand() % 6 + 1;
food[i].color = RGB(rand() % 256, rand() % 256, rand() % 256);
}
}
//绘制玩家
setfillcolor(player.color);//设置填充颜色
solidcircle(player.x, player.y, player.r);
settextcolor(GREEN);
settextstyle(30, 0, L"黑体");
setbkmode(0);//设置文字背景透明
outtextxy(player.x-50, player.y, L"我是最靓的崽仔");
EndBatchDraw();
//绘制ai
for (int i = 0; i < AI_NUM;++i)
{
if (ai[i].flag)
{
setfillcolor(ai[i].color);
solidcircle(ai[i].x, ai[i].y, ai[i].r);
}
}
}
void keyControl(int speed)
{
//获取键盘消息,按的是哪一个键
if ((GetAsyncKeyState('W') || GetAsyncKeyState(VK_UP))&&player.y>=0)//按了上键
{
player.y -= speed;
}
if ((GetAsyncKeyState('S') || GetAsyncKeyState(VK_DOWN))&& player.y<=HEIGHT)//按了下键
{
player.y += speed;
}
if ((GetAsyncKeyState('A') || GetAsyncKeyState(VK_LEFT))&& player.x >= 0)//按了左键
{
player.x -= speed;
}
if ((GetAsyncKeyState('D') || GetAsyncKeyState(VK_RIGHT))&& player.x<=WIDTH)//按了右键
{
player.x += speed;
}
}
//求两点之间的距离
double distance(struct Ball a, struct Ball b)
{
return sqrt((double)(a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
//玩家吃食物
void eatFood()
{
for (int i = 0; i < FOOD_NUM; ++i)
{
if (food[i].flag && distance(food[i], player) <= player.r)
{
food[i].flag = false;
player.r += food[i].r / 4;
}
}
}
//ai移动
void aiMove()
{
for (int i = 0; i < AI_NUM; ++i)
{
if (ai[i].flag)
{
ai[i].x += rand() % 5-2;
ai[i].y += rand() % 5 - 2;
}
}
}
int main()
{
initgraph(WIDTH, HEIGHT);
GameInit();
while (1)
{
keyControl(1);
GameDraw();
eatFood();
aiMove();
}
getchar();
return 0;
}
其他具体功能读者可自行添加。