引言
在这个项目中,我将介绍一个名为“星号游走程序”的控制台应用程序。该程序通过控制台输出渐变色的星号,并支持多种游走模式和用户交互。本文将详细介绍程序的功能、实现方式以及如何使用它。
功能概述
1. 随机游走模式
在随机游走模式下,星号会在控制台内随机移动,形成一种动态的视觉效果。每个星号的颜色会随着时间逐渐变化,形成渐变效果。
2. 从上至下游走模式
在从上至下游走模式下,星号会从屏幕顶部生成并向下移动,到达底部后重新从顶部生成。这种模式模拟了雨滴或雪花的效果。
3. 清屏功能
用户可以随时按下数字键 3
来清屏,清除所有当前的星号。
4. 添加对象
用户可以通过按下空格键来添加一个新的星号对象,增加屏幕上的动态元素。
5. 全屏显示
程序启动时会自动将控制台窗口设置为全屏模式,提供更好的视觉体验。
实现细节
1. 控制台颜色设置
我们使用 ANSI 转义码来设置控制台输出的颜色。通过一个包含多种颜色代码的 std::vector
,我们可以轻松地在不同颜色之间切换。
2. 光标移动
使用 ANSI 转义码 \033[y;xH
可以将光标移动到指定的位置 (x, y)
,从而实现星号在屏幕上的精确定位。
3. 随机游走对象
RandomWalker
类实现了随机游走的逻辑。每个对象在更新时会随机移动一小段距离,并且颜色会逐渐变化。当对象的生存时间超过 100 帧后,它会被标记为“死亡”并从列表中移除。
4. 从上至下游走对象
TopToBottomWalker
类实现了从上至下的游走逻辑。每个对象从屏幕顶部生成,向下移动,到达底部后重新从顶部生成。同样,颜色会逐渐变化,生存时间超过 100 帧后被移除。
5. 用户输入处理
使用 _kbhit()
和 _getch()
函数来检测用户的按键输入。根据不同的按键,程序会切换模式、清屏或添加新的对象。
6. 全屏设置
通过调用 Windows API,程序在启动时将控制台窗口设置为全屏模式。具体实现包括设置缓冲区大小、窗口区域和发送最大化消息。
代码结构
1. 主函数
int main() {
const int maxX = 210;
const int maxY = 51;
std::vector<RandomWalker> randomWalkers;
std::vector<TopToBottomWalker> topToBottomWalkers;
bool randomMode = true;
// 显示主界面说明规则
clearScreen();
std::cout << "欢迎来到星号游走程序!\n";
std::cout << "1 - 随机游走模式\n";
std::cout << "2 - 从上至下游走模式\n";
std::cout << "3 - 清屏\n";
std::cout << "空格 - 添加一个对象\n";
std::cout << "按任意键开始...\n";
_getch();
while (true) {
// 检测按键
if (_kbhit()) {
char key = _getch();
switch (key) {
case '1':
randomMode = true;
break;
case '2':
randomMode = false;
break;
case '3':
clearScreen();
randomWalkers.clear();
topToBottomWalkers.clear();
break;
case ' ':
if (randomMode) {
randomWalkers.emplace_back(maxX, maxY);
} else {
topToBottomWalkers.emplace_back(maxX, maxY);
}
break;
}
}
// 清除已死亡的对象
randomWalkers.erase(std::remove_if(randomWalkers.begin(), randomWalkers.end(), [](const RandomWalker& w) { return w.isDead(); }), randomWalkers.end());
topToBottomWalkers.erase(std::remove_if(topToBottomWalkers.begin(), topToBottomWalkers.end(), [](const TopToBottomWalker& w) { return w.isDead(); }), topToBottomWalkers.end());
// 更新和绘制所有对象
for (auto& walker : randomWalkers) {
walker.update();
walker.draw();
}
for (auto& walker : topToBottomWalkers) {
walker.update();
walker.draw();
}
// 延迟
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
return 0;
}
2. 辅助函数
// 设置控制台颜色
void setTextColor(const std::string& color) {
std::cout << color;
}
// 清屏
void clearScreen() {
std::cout << "\033[2J\033[1;1H";
}
// 移动光标到指定位置
void gotoxy(int x, int y) {
std::cout << "\033[" << y << ";" << x << "H";
}
3. 随机游走对象类
class RandomWalker {
public:
RandomWalker(int maxX, int maxY) : maxX(maxX), maxY(maxY), x(rand() % maxX), y(rand() % maxY), age(0) {
colorIt = colors.begin();
}
void update() {
// 随机移动
x += (rand() % 3) - 1;
y += (rand() % 3) - 1;
// 限制在屏幕范围内
x = std::max(0, std::min(x, maxX - 1));
y = std::max(0, std::min(y, maxY - 1));
// 增加年龄
age++;
}
void draw() {
gotoxy(x, y);
setTextColor(*colorIt);
std::cout << "*\033[0m";
// 更新颜色
if (++colorIt == colors.end()) {
colorIt = colors.begin();
}
}
bool isDead() const {
return age > 100; // 生存时间超过100帧后消失
}
private:
int maxX, maxY;
int x, y;
int age;
std::vector<std::string>::const_iterator colorIt;
};
4. 从上至下游走对象类
class TopToBottomWalker {
public:
TopToBottomWalker(int maxX, int maxY) : maxX(maxX), maxY(maxY), x(rand() % maxX), y(0), age(0) {
colorIt = colors.begin();
}
void update() {
// 向下移动
y++;
// 限制在屏幕范围内
if (y >= maxY) {
y = 0;
x = rand() % maxX;
}
// 增加年龄
age++;
}
void draw() {
gotoxy(x, y);
setTextColor(*colorIt);
std::cout << "*\033[0m";
// 更新颜色
if (++colorIt == colors.end()) {
colorIt = colors.begin();
}
}
bool isDead() const {
return age > 100; // 生存时间超过100帧后消失
}
private:
int maxX, maxY;
int x, y;
int age;
std::vector<std::string>::const_iterator colorIt;
};
使用方法
-
编译和运行
- 使用支持 C++11 的编译器编译程序。
- 在命令行中运行生成的可执行文件。
-
操作指南
- 按
1
切换到随机游走模式。 - 按
2
切换到从上至下游走模式。 - 按
3
清屏。 - 按空格键添加一个新的星号对象。
- 按
源码
#include <iostream>
#include <thread>
#include <chrono>
#include <vector>
#include <random>
#include <conio.h> // 用于检测按键
// ANSI颜色代码
const std::vector<std::string> colors = {
"\033[38;5;0m", "\033[38;5;1m", "\033[38;5;2m", "\033[38;5;3m", "\033[38;5;4m", "\033[38;5;5m", "\033[38;5;6m", "\033[38;5;7m",
"\033[38;5;8m", "\033[38;5;9m", "\033[38;5;10m", "\033[38;5;11m", "\033[38;5;12m", "\033[38;5;13m", "\033[38;5;14m", "\033[38;5;15m",
"\033[38;5;16m", "\033[38;5;17m", "\033[38;5;18m", "\033[38;5;19m", "\033[38;5;20m", "\033[38;5;21m", "\033[38;5;22m", "\033[38;5;23m",
"\033[38;5;24m", "\033[38;5;25m", "\033[38;5;26m", "\033[38;5;27m", "\033[38;5;28m", "\033[38;5;29m", "\033[38;5;30m", "\033[38;5;31m",
"\033[38;5;32m", "\033[38;5;33m", "\033[38;5;34m", "\033[38;5;35m", "\033[38;5;36m", "\033[38;5;37m", "\033[38;5;38m", "\033[38;5;39m",
"\033[38;5;40m", "\033[38;5;41m", "\033[38;5;42m", "\033[38;5;43m", "\033[38;5;44m", "\033[38;5;45m", "\033[38;5;46m", "\033[38;5;47m",
"\033[38;5;48m", "\033[38;5;49m", "\033[38;5;50m", "\033[38;5;51m", "\033[38;5;52m", "\033[38;5;53m", "\033[38;5;54m", "\033[38;5;55m",
"\033[38;5;56m", "\033[38;5;57m", "\033[38;5;58m", "\033[38;5;59m", "\033[38;5;60m", "\033[38;5;61m", "\033[38;5;62m", "\033[38;5;63m",
"\033[38;5;64m", "\033[38;5;65m", "\033[38;5;66m", "\033[38;5;67m", "\033[38;5;68m", "\033[38;5;69m", "\033[38;5;70m", "\033[38;5;71m",
"\033[38;5;72m", "\033[38;5;73m", "\033[38;5;74m", "\033[38;5;75m", "\033[38;5;76m", "\033[38;5;77m", "\033[38;5;78m", "\033[38;5;79m",
"\033[38;5;80m", "\033[38;5;81m", "\033[38;5;82m", "\033[38;5;83m", "\033[38;5;84m", "\033[38;5;85m", "\033[38;5;86m", "\033[38;5;87m",
"\033[38;5;88m", "\033[38;5;89m", "\033[38;5;90m", "\033[38;5;91m", "\033[38;5;92m", "\033[38;5;93m", "\033[38;5;94m", "\033[38;5;95m",
"\033[38;5;96m", "\033[38;5;97m", "\033[38;5;98m", "\033[38;5;99m", "\033[38;5;100m", "\033[38;5;101m", "\033[38;5;102m", "\033[38;5;103m",
"\033[38;5;104m", "\033[38;5;105m", "\033[38;5;106m", "\033[38;5;107m", "\033[38;5;108m", "\033[38;5;109m", "\033[38;5;110m", "\033[38;5;111m",
"\033[38;5;112m", "\033[38;5;113m", "\033[38;5;114m", "\033[38;5;115m", "\033[38;5;116m", "\033[38;5;117m", "\033[38;5;118m", "\033[38;5;119m",
"\033[38;5;120m", "\033[38;5;121m", "\033[38;5;122m", "\033[38;5;123m", "\033[38;5;124m", "\033[38;5;125m", "\033[38;5;126m", "\033[38;5;127m",
"\033[38;5;128m", "\033[38;5;129m", "\033[38;5;130m", "\033[38;5;131m", "\033[38;5;132m", "\033[38;5;133m", "\033[38;5;134m", "\033[38;5;135m",
"\033[38;5;136m", "\033[38;5;137m", "\033[38;5;138m", "\033[38;5;139m", "\033[38;5;140m", "\033[38;5;141m", "\033[38;5;142m", "\033[38;5;143m",
"\033[38;5;144m", "\033[38;5;145m", "\033[38;5;146m", "\033[38;5;147m", "\033[38;5;148m", "\033[38;5;149m", "\033[38;5;150m", "\033[38;5;151m",
"\033[38;5;152m", "\033[38;5;153m", "\033[38;5;154m", "\033[38;5;155m", "\033[38;5;156m", "\033[38;5;157m", "\033[38;5;158m", "\033[38;5;159m",
"\033[38;5;160m", "\033[38;5;161m", "\033[38;5;162m", "\033[38;5;163m", "\033[38;5;164m", "\033[38;5;165m", "\033[38;5;166m", "\033[38;5;167m",
"\033[38;5;168m", "\033[38;5;169m", "\033[38;5;170m", "\033[38;5;171m", "\033[38;5;172m", "\033[38;5;173m", "\033[38;5;174m", "\033[38;5;175m",
"\033[38;5;176m", "\033[38;5;177m", "\033[38;5;178m", "\033[38;5;179m", "\033[38;5;180m", "\033[38;5;181m", "\033[38;5;182m", "\033[38;5;183m",
"\033[38;5;184m", "\033[38;5;185m", "\033[38;5;186m", "\033[38;5;187m", "\033[38;5;188m", "\033[38;5;189m", "\033[38;5;190m", "\033[38;5;191m",
"\033[38;5;192m", "\033[38;5;193m", "\033[38;5;194m", "\033[38;5;195m", "\033[38;5;196m", "\033[38;5;197m", "\033[38;5;198m", "\033[38;5;199m",
"\033[38;5;200m", "\033[38;5;201m", "\033[38;5;202m", "\033[38;5;203m", "\033[38;5;204m", "\033[38;5;205m", "\033[38;5;206m", "\033[38;5;207m",
"\033[38;5;208m", "\033[38;5;209m", "\033[38;5;210m", "\033[38;5;211m", "\033[38;5;212m", "\033[38;5;213m", "\033[38;5;214m", "\033[38;5;215m",
"\033[38;5;216m", "\033[38;5;217m", "\033[38;5;218m", "\033[38;5;219m", "\033[38;5;220m", "\033[38;5;221m", "\033[38;5;222m", "\033[38;5;223m",
"\033[38;5;224m", "\033[38;5;225m", "\033[38;5;226m", "\033[38;5;227m", "\033[38;5;228m", "\033[38;5;229m", "\033[38;5;230m", "\033[38;5;231m",
"\033[38;5;232m", "\033[38;5;233m", "\033[38;5;234m", "\033[38;5;235m", "\033[38;5;236m", "\033[38;5;237m", "\033[38;5;238m", "\033[38;5;239m",
"\033[38;5;240m", "\033[38;5;241m", "\033[38;5;242m", "\033[38;5;243m", "\033[38;5;244m", "\033[38;5;245m", "\033[38;5;246m", "\033[38;5;247m",
"\033[38;5;248m", "\033[38;5;249m", "\033[38;5;250m", "\033[38;5;251m", "\033[38;5;252m", "\033[38;5;253m", "\033[38;5;254m", "\033[38;5;255m"
};
// 设置控制台颜色
void setTextColor(const std::string& color) {
std::cout << color;
}
// 清屏
void clearScreen() {
std::cout << "\033[2J\033[1;1H";
}
// 移动光标到指定位置
void gotoxy(int x, int y) {
std::cout << "\033[" << y << ";" << x << "H";
}
// 随机游走对象
class RandomWalker {
public:
RandomWalker(int maxX, int maxY) : maxX(maxX), maxY(maxY), x(rand() % maxX), y(rand() % maxY), age(0) {
colorIt = colors.begin();
}
void update() {
// 随机移动
x += (rand() % 3) - 1;
y += (rand() % 3) - 1;
// 限制在屏幕范围内
x = std::max(0, std::min(x, maxX - 1));
y = std::max(0, std::min(y, maxY - 1));
// 增加年龄
age++;
}
void draw() {
gotoxy(x, y);
setTextColor(*colorIt);
std::cout << "*\033[0m";
// 更新颜色
if (++colorIt == colors.end()) {
colorIt = colors.begin();
}
}
bool isDead() const {
return age > 100; // 生存时间超过100帧后消失
}
private:
int maxX, maxY;
int x, y;
int age;
std::vector<std::string>::const_iterator colorIt;
};
// 从上至下游走对象
class TopToBottomWalker {
public:
TopToBottomWalker(int maxX, int maxY) : maxX(maxX), maxY(maxY), x(rand() % maxX), y(0), age(0) {
colorIt = colors.begin();
}
void update() {
// 向下移动
y++;
// 限制在屏幕范围内
if (y >= maxY) {
y = 0;
x = rand() % maxX;
}
// 增加年龄
age++;
}
void draw() {
gotoxy(x, y);
setTextColor(*colorIt);
std::cout << "*\033[0m";
// 更新颜色
if (++colorIt == colors.end()) {
colorIt = colors.begin();
}
}
bool isDead() const {
return age > 100; // 生存时间超过100帧后消失
}
private:
int maxX, maxY;
int x, y;
int age;
std::vector<std::string>::const_iterator colorIt;
};
int main() {
const int maxX = 210;
const int maxY = 51;
std::vector<RandomWalker> randomWalkers;
std::vector<TopToBottomWalker> topToBottomWalkers;
bool randomMode = true;
// 显示主界面说明规则
clearScreen();
std::cout << "欢迎来到星号游走程序!\n";
std::cout << "1 - 随机游走模式\n";
std::cout << "2 - 从上至下游走模式\n";
std::cout << "3 - 清屏\n";
std::cout << "空格 - 添加一个对象\n";
std::cout << "按任意键开始...\n";
_getch();
while (true) {
// 检测按键
if (_kbhit()) {
char key = _getch();
switch (key) {
case '1':
randomMode = true;
break;
case '2':
randomMode = false;
break;
case '3':
clearScreen();
randomWalkers.clear();
topToBottomWalkers.clear();
break;
case ' ':
if (randomMode) {
randomWalkers.emplace_back(maxX, maxY);
} else {
topToBottomWalkers.emplace_back(maxX, maxY);
}
break;
}
}
// 清除已死亡的对象
randomWalkers.erase(std::remove_if(randomWalkers.begin(), randomWalkers.end(), [](const RandomWalker& w) { return w.isDead(); }), randomWalkers.end());
topToBottomWalkers.erase(std::remove_if(topToBottomWalkers.begin(), topToBottomWalkers.end(), [](const TopToBottomWalker& w) { return w.isDead(); }), topToBottomWalkers.end());
// 更新和绘制所有对象
for (auto& walker : randomWalkers) {
walker.update();
walker.draw();
}
for (auto& walker : topToBottomWalkers) {
walker.update();
walker.draw();
}
// 延迟
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
return 0;
}
结论
通过这个项目,我们不仅实现了一个有趣的控制台动画程序,还学习了如何使用 ANSI 转义码、Windows API 以及 C++ 中的一些高级特性。希望这个项目能够激发你对控制台编程的兴趣,并为你提供一些实用的技术参考。
如果你有任何问题或建议,欢迎在评论区留言!
标签:std,maxX,38,int,多彩的,maxY,033,控制台,流星雨 From: https://blog.csdn.net/2301_81482480/article/details/144470482