0基础学习C++做贪吃蛇,边玩边学习!(三)SetConsoleCursorPosition光标移动效果——一条前进的蛇

你对C++的好奇心已经被黑漆漆的控制台窗口和一大串莫名其妙的错误提示消磨殆尽了吗?

你想亲手实现一个电脑游戏吗?

如果你想学习C++,又不知道如何入手,快上车!跟着我一起,0基础学习C++做贪吃蛇, 边玩儿边学习!

在上一篇我们用windows.h中的Sleep()函数停顿两秒后继续输出,实现了类似移动的效果。
我们知道,程序运行速度非常快,如果不使用Sleep()函数
两条输出会同时出现在控制台窗口————肉眼无法捕捉他们的先后,而使用Sleep()函数才能实现我们想要的效果。

事实上,Sleep函数的参数决定了我们贪吃蛇的移动速度(或许是级别)

现在我们必须实现“真正”的移动——在蛇第二次输出(位置向右移动了)的同时去掉第一次的输出,使得他看起来是在移动
——而不是残影之类的狡辩

OK!
安排!

//定义一个HANDLE型的变量handle(名字可以随便取)
//GetStdHandle()函数用来获取HANDLE(返回一个HANDLE类型)
//所以 handle 的值就是STD_OUTPUT_HANDLE(标准输出句柄)
HANDLE handle=GetStdHandle(STD_OUTPUT_HANDLE);
//SetConsoleCursorPosition()有两个参数,第一个参数为句柄,第二个参数是坐标
//这句意思是:将handle指向的输出窗口的光标位置改为{x,y}
SetConsoleCursorPosition(handle, {x,y});

可以看出,SetConsoleCursorPosition()函数的作用是光标移动,为了能让我们的控制台窗口的光标移动到指定位置,我们必须先指向(访问)窗口,因此定义一个句柄来指向当前窗口。
窗口句柄是windows系统为每个窗口规定的标识符,可以通过它访问此窗口,STD_OUTPUT_HANDLE(标准输出句柄)指向的就是运行时刻的控制台。

我们修改程序为:

#include <iostream>
#include <windows.h>
using namespace std;
int main() {
    cout << "oooo¥";
    Sleep(20000);
    cout << " oooo¥";
    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
    Sleep(500);
    SetConsoleCursorPosition(handle, { 15,6});
    Sleep(500);
    SetConsoleCursorPosition(handle, { 7,8 });
    return 0;
    }

编译运行,输出结果:
光标移动

我们可以观察到光标的移动。
如果我们在每次移动光标后打印蛇,就像下面这样:

#include <iostream>
#include <windows.h>
using namespace std;
int main() {
    cout << "oooo¥";
    Sleep(20000);
    cout << " oooo¥";
    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
    Sleep(500);
    SetConsoleCursorPosition(handle, { 15,6 });
    cout << "oooo¥";
    Sleep(500);
    SetConsoleCursorPosition(handle, { 7,8 });
    cout << "oooo¥";
    return 0;
}

效果会像这样:
似乎大功告成

好了,似乎大功告成了,但是见鬼的残影不但没有消失,而且似乎越来越多了?

那就消灭它!

//代码有了一些改动,不要在之前代码的基础上修改,最好重新复制粘贴或重新码。
#include <iostream>
#include <windows.h>
using namespace std;
int main() {
    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleCursorPosition(handle, { 3,0 });
    cout << "oooo¥";
    Sleep(500);
    SetConsoleCursorPosition(handle, { 3,0 });
    cout << "     ";
    SetConsoleCursorPosition(handle, { 15,6 });
    cout << "oooo¥";
    Sleep(500);
    SetConsoleCursorPosition(handle, { 15,6 });
    cout << "     ";
    SetConsoleCursorPosition(handle, { 7,8 });
    cout << "oooo¥";
    return 0;
}

这是一条自由穿梭在控制台的小蛇,路过之后,无影无踪!

整体移动
……
……
……
……

难道贪吃蛇是整个移动的吗?

它不得爬啊!

???

别急,继续本系列,0基础学习C++做贪吃蛇,边玩边学习!

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是一个简单的 C++ 贪吃游戏示例,使用了 Windows API 中的控制台颜色设置来实现颜色: ```c++ #include <iostream> #include <conio.h> #include <windows.h> #include <vector> using namespace std; const int consoleWidth = 80; // 控制台窗口宽度 const int consoleHeight = 25; // 控制台窗口高度 const int snakeInitialLength = 3; // 贪吃初始长度 // 贪吃结构体,包含身和方向 struct Snake { vector<pair<int, int>> body; // 身,存储每个节的坐标 int direction; // 方向,0-上,1-下,2-左,3-右 }; // 游戏状态枚举,包括游戏进行中、失败和胜利 enum class GameState { Playing, Failed, Win }; // 食物结构体,包含位置 struct Food { pair<int, int> position; // 食物位置 }; // 控制台颜色枚举,用于设置控制台输出颜色 enum ConsoleColor { Black = 0, Blue = 1, Green = 2, Cyan = 3, Red = 4, Magenta = 5, Brown = 6, LightGray = 7, DarkGray = 8, LightBlue = 9, LightGreen = 10, LightCyan = 11, LightRed = 12, LightMagenta = 13, Yellow = 14, White = 15 }; // 设置控制台输出颜色 void SetConsoleTextColor(ConsoleColor color) { HANDLE consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleTextAttribute(consoleHandle, static_cast<WORD>(color)); } // 在控制台指定位置输出字符,坐标从左上角开始计算 void SetCursorPosition(int x, int y) { COORD cursorPosition = { x, y }; HANDLE consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleCursorPosition(consoleHandle, cursorPosition); } // 隐藏控制台光标 void HideConsoleCursor() { CONSOLE_CURSOR_INFO cursorInfo; HANDLE consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE); GetConsoleCursorInfo(consoleHandle, &cursorInfo); cursorInfo.bVisible = false; SetConsoleCursorInfo(consoleHandle, &cursorInfo); } // 生成一个随机坐标 pair<int, int> GenerateRandomPosition() { int x = rand() % (consoleWidth - 2) + 1; int y = rand() % (consoleHeight - 2) + 1; return { x, y }; } // 在控制台输出游戏界面 void RenderGameScreen(const Snake& snake, const Food& food, const GameState& gameState) { system("cls"); // 清屏 for (int i = 0; i < consoleHeight; i++) { for (int j = 0; j < consoleWidth; j++) { if (i == 0 || i == consoleHeight - 1 || j == 0 || j == consoleWidth - 1) { SetConsoleTextColor(ConsoleColor::LightGray); // 绘制边框 cout << "#"; } else if (gameState == GameState::Failed && snake.body.front().first == j && snake.body.front().second == i) { SetConsoleTextColor(ConsoleColor::Red); // 绘制失败的头 cout << "X"; } else if (gameState == GameState::Win && snake.body.front().first == j && snake.body.front().second == i) { SetConsoleTextColor(ConsoleColor::Yellow); // 绘制胜利的头 cout << "O"; } else if (food.position.first == j && food.position.second == i) { SetConsoleTextColor(ConsoleColor::Green); // 绘制食物 cout << "F"; } else { bool isBodyPart = false; for (auto& bodyPart : snake.body) { if (bodyPart.first == j && bodyPart.second == i) { SetConsoleTextColor(ConsoleColor::White); // 绘制身 cout << "o"; isBodyPart = true; break; } } if (!isBodyPart) { cout << " "; } } } cout << endl; } } // 更新的位置和状态 void UpdateSnake(Snake& snake, const Food& food, GameState& gameState) { auto head = snake.body.front(); // 根据方向更新头位置 switch (snake.direction) { case 0: // 上 head.second--; break; case 1: // 下 head.second++; break; case 2: // 左 head.first--; break; case 3: // 右 head.first++; break; } // 检查头是否碰到边界或身 if (head.first == 0 || head.first == consoleWidth - 1 || head.second == 0 || head.second == consoleHeight - 1) { gameState = GameState::Failed; // 碰到边界,游戏失败 return; } for (int i = 1; i < snake.body.size(); i++) { if (head.first == snake.body[i].first && head.second == snake.body[i].second) { gameState = GameState::Failed; // 碰到身,游戏失败 return; } } snake.body.insert(snake.body.begin(), head); // 将头插入身 if (head.first == food.position.first && head.second == food.position.second) { // 吃到食物 if (snake.body.size() == consoleWidth * consoleHeight - 2) { gameState = GameState::Win; // 身占满整个游戏区域,游戏胜利 return; } food.position = GenerateRandomPosition(); // 生成新的食物 } else { snake.body.pop_back(); // 没有吃到食物,则删除尾 } } // 获取用户输入的方向 void GetUserInput(Snake& snake) { if (_kbhit()) { int key = _getch(); switch (key) { case 'w': case 'W': case 72: // 上 if (snake.direction != 1) { snake.direction = 0; } break; case 's': case 'S': case 80: // 下 if (snake.direction != 0) { snake.direction = 1; } break; case 'a': case 'A': case 75: // 左 if (snake.direction != 3) { snake.direction = 2; } break; case 'd': case 'D': case 77: // 右 if (snake.direction != 2) { snake.direction = 3; } break; } } } int main() { HideConsoleCursor(); // 隐藏控制台光标 srand(static_cast<unsigned int>(time(nullptr))); // 初始化随机数生成器 Snake snake; snake.direction = 3; // 初始方向为右 int x = consoleWidth / 2; int y = consoleHeight / 2; for (int i = 0; i < snakeInitialLength; i++) { snake.body.push_back({ x - i, y }); // 初始身为一条水平线 } Food food; food.position = GenerateRandomPosition(); // 随机生成初始食物位置 GameState gameState = GameState::Playing; while (gameState == GameState::Playing) { RenderGameScreen(snake, food, gameState); UpdateSnake(snake, food, gameState); GetUserInput(snake); Sleep(100); } RenderGameScreen(snake, food, gameState); // 最后一次绘制游戏界面 SetCursorPosition(consoleWidth / 2 - 4, consoleHeight / 2); if (gameState == GameState::Failed) { SetConsoleTextColor(ConsoleColor::Red); cout << "Failed"; } else { SetConsoleTextColor(ConsoleColor::Yellow); cout << "Win"; } SetConsoleTextColor(ConsoleColor::White); cout << endl; return 0; } ``` 注意:这个示例中使用了 Windows API 中的一些函数,只能在 Windows 平台上编译运行。如果您需要在其他平台上运行,请使用相应的 API 替代函数或使用跨平台库(如 SDL)。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值