969 字
5 分钟
可爱猫咪认萝卜:C++ 字符画动画实现

最近网上有一只非常可爱的猫咪火遍全网,它的主人试着教它分辨萝卜和纸巾,但猫咪贼眉鼠眼瞎猜的样子实在是有点可爱。在网上看到了各种有趣的二创作品,我也心血来潮,尝试用 C++ 编写了一个简单的字符画动画来复刻这个场景。

效果演示视频:点这里跳转

这段代码实现了猫咪伸爪、收爪以及开心晃脑袋的动画效果。虽然逻辑比较简单,但作为字符画动画入门还是挺有意思的。有兴趣的小伙伴可以尝试运行一下,或者在此基础上拓展成一个小游戏!

#include <iostream>
#include <string>
#include <vector>
#include <thread>
#include <chrono>
using namespace std;
// 毫秒延迟函数,封装 thread 和 chrono 以简化调用
void sleep_ms(int ms) {
this_thread::sleep_for(chrono::milliseconds(ms));
}
// 清除终端屏幕并重置光标位置
void clear_screen() {
// cout << "\033[2J\033[1;1H";
cout << "\033[1;1H\033[0J";
cout.flush();
}
// 猫咪头部样式
const string cat_base_normal = R"(
/\_/\
( o.o )
/ \
/| | | |\
)";
const string cat_base_wide_eyes = R"(
/\_/\
( O.O )
/ ^ \
/| | | |\
)";
// 开心状态下的头部帧(带偏移感)
const vector<string> cat_happy_heads = {
R"(
/\_/\
( ^.^ )
/ \
/| | | |\
)",
R"(
/\_/\
( ^.^ )
/ \
/| | | |\
)",
R"(
/\_/\
( ^.^ )
/ \
/| | | |\
)"
};
// 坐着时的下半部分(带小尾巴摇晃感)
const vector<string> sitting_paw_frames = {
" (_| |_| |_)\n (_) (_)\n\n\n",
" (_| |_| |_)\n (_) (_) ~\n\n\n",
" (_| |_| |_)\n ~ (_) (_)\n\n\n"
};
// 纸巾盒和萝卜的详细字符画变化的下半部分帧
const vector<string> right_paw_frames = {
" (_| |_| |_)\n (_) (_)\n\n\n",
" (_| |_\\ \\_)\n (_) \\ \\\n (_)\n\n",
};
const vector<string> left_paw_frames = {
" (_| |_| |_)\n (_) (_)\n\n\n",
" (_/ /_| |_)\n / / (_)\n (_)\n\n",
" (_/ /_| |_)\n / / (_)\n / /\n (_)\n"
};
const vector<string> left2right_paw_frames = {
" (_| |_| |_)\n (_) (_)\n\n\n",
" (__\\ \\|_|_)\n \\ \\_)\n (_)\n\n",
" (__\\ \\|_|_)\n \\ \\_)\n \\ \\\n (_)\n",
};
const vector<string> right2left_paw_frames = {
" (_| |_| |_)\n (_) (_)\n\n\n",
" (_| |/ /__)\n (_/ /\n (_)\n\n",
" (_| |/ /__)\n (_/ /\n / /\n (_)\n"
};
// 纸巾盒和萝卜的详细字符画
const string items =
" _____ \\ | /\n"
" |_---_| ( )\n"
" |_____| V\n";
void play_frame(int i, const vector<string>& paw_frames, bool wide_eyes = false) {
clear_screen();
const string& head = wide_eyes ? cat_base_wide_eyes : cat_base_normal;
cout << head << paw_frames[i];
cout << items << endl;
sleep_ms(200);
}
// 动画函数:猫咪开心地左右晃脑袋
void animate_happy(int repeats = 3) {
for (int r = 0; r < repeats; ++r) {
// 序列:中 -> 右 -> 中 -> 左
int sequence[] = {0, 1, 0, 2};
for (int idx : sequence) {
clear_screen();
cout << cat_happy_heads[idx];
// 晃脑袋时,脚部也配合摇摇尾巴
cout << sitting_paw_frames[idx == 0 ? 0 : (idx == 1 ? 1 : 2)];
cout << items << endl;
sleep_ms(100); // 晃动快一点显得更兴奋
}
}
// 动画结束后,强制回到原位并恢复正常表情
clear_screen();
cout << cat_base_normal;
cout << sitting_paw_frames[0];
cout << items << endl;
}
// 动画函数:让猫咪伸出或收回左爪
void animate_paw(bool extend, const vector<string>& paw_frames, bool wide_eyes_at_end = false) {
if (extend) {
for (size_t i = 0; i < paw_frames.size(); ++i) {
if (wide_eyes_at_end && i == paw_frames.size() - 1) {
// 最后一帧:先保持正常眼睛样式
play_frame(i, paw_frames, false);
// 稍微等待一小会(例如 300 毫秒),表现出“反应时间”
sleep_ms(300);
// 然后再睁大眼睛
play_frame(i, paw_frames, true);
} else {
play_frame(i, paw_frames, false);
}
}
} else {
for (int i = paw_frames.size() - 1; i >= 0; --i) {
// 收回时如果从最后一帧开始,也要考虑眼睛是否处于睁大状态
bool current_eyes = (wide_eyes_at_end && i == (int)paw_frames.size() - 1);
play_frame(i, paw_frames, current_eyes);
}
}
}
int main() {
// 等待 500 毫秒
sleep_ms(500);
// 向左伸出左爪(第一个参数改为 false 则为收爪)
animate_paw(true, left_paw_frames, true);
// 向右伸出左爪
animate_paw(true, left2right_paw_frames, true);
// 向左伸出右爪
animate_paw(true, right_paw_frames, true);
// 向右伸出右爪
animate_paw(true, right2left_paw_frames, true);
// 等待 500 毫秒
sleep_ms(500);
// 开心
animate_happy();
return 0;
}

希望这只“乱猜”的猫咪也能给你带来一点快乐!

可爱猫咪认萝卜:C++ 字符画动画实现
https://yezi.press/posts/nice_cat/
作者
Yezi 叶子
发布于
2025/12/28
许可协议
CC BY-NC-SA 4.0