引言
这段时间正在学习移动规划的相关知识,但是发现大部分的代码都是用的C++来编写,而自己只用c写过代码,c++的知识实属是薄弱到了极致,所以用了半年突击了一下从C转到C++的视频,但是还是缺少实战经验,所幸网上贪吃蛇这个游戏非常简单,所以作为新手练手非常的合适。
函数代码
drawsnake
这一函数的主要目的在于在屏幕上根据当前snake的坐标,利用easyx库将其画出来。
void drawsnake(std::deque<Snake>& snake)
{
setfillcolor(GREEN);
for (const auto& body : snake)
{
fillrectangle(body.x * size, body.y * size, (body.x + 1) * size, (body.y + 1) * size);
}
Sleep(speed);
}
-
Isover
这一函数的主要目的是判断游戏是否结束了,判断的条件也很简单,要么就是蛇头超过了设定的屏幕边界,要么就是蛇头碰到了蛇身自己,然后标记游戏结束,同时退出游戏。
void isover(std::deque<Snake>& snake)
{
for (auto iter = snake.begin() + 1; iter != snake.end(); iter++)
{
if ((snake.front().x < 0 || snake.front().x >= weight / size
|| snake.front().y < 0 || snake.front().y >= hight / size) ||
(snake.front().x == iter->x && snake.front().y == iter->y))
{
settextcolor(RED);
settextstyle(100, 0,_T("Consolas"));
TCHAR endStar[16];
_stprintf_s(endStar,_T("GAME OVER!!!"));
outtextxy(weight / size, hight / size, endStar);
Sleep(3000);
closegraph();
exit(0);
}
}
}
-
Movesnake 以及changedir
两个函数的目的主要是首先通过按键捕获,获取用户当前按下的键值(还是判定一下当前的方向,比如向前就算键值为向后也不可以),然后改变蛇头的移动方向。同时判断蛇头是否触碰到食物,如果没触碰到就将双端队列的最后一位(蛇尾的位置)进行去掉,触碰则进行保留,蛇身长度加一。
void movesnake(std::deque<Snake>& snake, bool grow = false)
{
Snake newHead = snake.front();
switch (newHead.dir)
{
case UP:
newHead.y--;
break;
case DOWN:
newHead.y++;
break;
case RIGHT:
newHead.x++;
break;
case LEFT:
newHead.x--;
break;
}
snake.push_front(newHead);
isover(snake);
if (!grow)
{
clearrectangle(snake.back().x * size, snake.back().y * size,
(snake.back().x + 1) * size, (snake.back().y + 1) * size);
snake.pop_back();
}
drawsnake(snake);
}
void changedir(std::deque<Snake>& snake)
{
ExMessage msg = { 0 };
peekmessage(&msg, EX_KEY);
if (msg.message == WM_KEYDOWN)
{
switch (msg.vkcode)
{
case 'W' :case 'w ':case VK_UP:
if (snake.front().dir != DOWN)
{snake.front().dir = UP;}
break;
case 's':case 'S ':case VK_DOWN:
if (snake.front().dir != UP)
{ snake.front().dir = DOWN; }
break;
case 'A': case'a ':case VK_LEFT:
if (snake.front().dir != RIGHT)
{snake.front().dir = LEFT;}
break;
case 'D':case 'd ':case VK_RIGHT:
if (snake.front().dir != LEFT)
{snake.front().dir = RIGHT;}
break;
}
}
}
-
Genertedfood 以及 cheakeat
这两个函数的主要目的还是在于产生食物,以及判断蛇头是否与食物相碰撞。判断逻辑非常的简单,即蛇头的x y坐标是否等于到食物的x y坐标,返回值为一个布尔值表示是否触碰。产生食物的时候,限制了一个范围,同时限制不能产生在蛇的身上。
void genertedfood(Food& food, std::deque<Snake>& snake)
{
while (true)
{
food.x = rand() % ((weight-200) / size);
food.y = rand() % ((hight-200) / size);
bool overlap = false;
for (const auto& body : snake)
{
if (food.x == body.x && food.y == body.y)
{
overlap = true;
}
}
if (!overlap)
{
break;
}
}
}
bool cheakeat(Food& food, std::deque<Snake>& snake)
{
if (snake.front().x == food.x && snake.front().y == food.y)
{
return true;
}
return false;
}
-
主函数 main
程序从main函数开始执行,首先通过initgraph初始化一个800×800像素的图形窗口,创建表示蛇体的双端队列snake,并将初始蛇头(位置居中、方向向右)压入队列。调用drawsnake函数绘制初始蛇体后,初始化计分器score为0,并通过genertedfood函数生成第一个食物,用红色方块绘制在随机位置。进入无限循环后,每帧先执行movesnake移动蛇体,此时会根据当前方向计算新蛇头坐标并插入队列头部,如果是正常移动则删除尾部元素实现位置更新,如果检测到吃食物则保留尾部实现生长。移动后立即调用changedir函数检测键盘输入,通过peekmessage非阻塞获取方向键或WSAD键值,在确保不反向移动的前提下更新蛇头方向。接着通过cheakeat判断蛇头是否与食物坐标重合,若发生吞噬则分数自增、生成新食物并重新绘制,此时通过movesnake的第二个参数true触发蛇体生长。每帧最后都会用黄色字体在窗口右下角刷新当前分数。当发生撞墙或自碰时,isover函数会显示红色"GAME OVER"提示,暂停3秒后直接退出程序,因此最后的closegraph实际上不会被执行到。整个游戏通过循环持续更新蛇体位置、处理用户输入并响应状态变化,构成完整的游戏运行流程。
int main()
{
initgraph(weight, hight); //画出一个窗口 800*1000
std::deque<Snake> snake; //定义了一条蛇
snake.push_front({ weight / size / 2, hight / size / 2,RIGHT }); //在双端队列头部添加一个元素作为蛇头
drawsnake(snake); //把蛇画出来
int score = 0;
Food food;
genertedfood(food, snake);
setfillcolor(RED);
fillrectangle(food.x * size, food.y * size, (food.x + 1) * size, (food.y + 1) * size);
while (1)
{
movesnake(snake);
changedir(snake);
if (cheakeat(food, snake))
{
score++;
genertedfood(food, snake);
movesnake(snake, true);
setfillcolor(RED);
fillrectangle(food.x * size, food.y * size, (food.x + 1) * size, (food.y + 1) * size);
}
settextcolor(YELLOW);
settextstyle(40, 0, _T("Consolas"));
TCHAR stescore[16];
_stprintf_s(stescore, _T("Score:%d"), score);
outtextxy(weight / size, hight / size, stescore);
}
system("pause");
closegraph();
return 0;
}
-
写在最后
其实这个代码的逻辑并不算很复杂,主要目的还是练习从c转到c++的写法上的区别。同时,这个代码中并没有涉及到面向对象的编程,下一步将找一个面向对象编程的项目进行实战。
2万+

被折叠的 条评论
为什么被折叠?



