7.1 KiB
7.1 KiB
第 3 课:随机抽卡——核心来了
本课目标:把命令行时学过的
rand()+if/else搬进 GUI。点按钮 → 随机出"普通卡 / 稀有卡 / 超稀有卡",不同稀有度配不同颜色。预计时间:40 分钟
学完之后:你能在回调函数里写随机和判断,让 GUI 程序"每次都不一样"。
0. 上课前先理理
一句话回顾旧知识
std::srand(std::time(nullptr)); // 设一次种子
int r = std::rand() % 100; // 每次生成 0~99 的随机数
if (r < 70) { ... 普通 ... }
else if (r < 95) { ... 稀有 ... }
else { ... 超稀有 ... }
这些你以前用 std::cout 打印结果。现在改用 Fl_Box 显示。
概率设计
| 随机数 r | 稀有度 | 颜色 | 大约概率 |
|---|---|---|---|
| 0 ~ 69 | 普通卡 | 灰色 | 70% |
| 70 ~ 94 | 稀有卡 | 蓝色 | 25% |
| 95 ~ 99 | 超稀有卡 | 红色 | 5% |
这一课认识 2 个新朋友 + 1 个老技巧的新用法
| 名字 | 干什么 |
|---|---|
sprintf |
把数字"塞"进字符串里(GUI 不能直接 cout) |
fl_rgb_color |
用 R/G/B 三个数自定义任意颜色 |
labelcolor |
(老朋友)配合上面两个,让稀有度看一眼区分 |
1. 起步:先编译空骨架
打开 lesson_gui_3.cpp。骨架里已经准备好了第 2 课学过的东西(窗口 + 按钮 + 回调 + 全局 result_box),但回调里还没写"抽卡"。
cmake -B build -DLESSON=3
cmake --build build && ./build/gacha3
✅ 检查点 1
- 窗口出来了,按钮 "抽一张!" 在上面
- 下面写着 "点击按钮抽卡吧!"
- 点按钮文字会变成 "(写好步骤 4 后这里会变)"(提示当前回调没真正工作)
2. 步骤 1:让随机数能用起来
🤔 先想一下
- 设随机种子
srand()要放哪儿?回调里行不行? - 为什么不能每次点按钮都
srand一下?
💡 答案:种子只能设一次,否则每次都"重置",结果会重复甚至不变。所以
srand放main里、Fl::run()之前。
✏️ 改两个地方
(1) 加 include(替换对应 TODO):
#include <cstdlib> // rand, srand
#include <ctime> // time
(2) 在 main 的第一行加(替换对应 TODO):
std::srand(std::time(nullptr));
▶️ 编译运行
cmake --build build && ./build/gacha3
✅ 检查点 2
- 编译没报错
- 程序行为和之前一样(因为我们还没真正用
rand)
3. 步骤 2:在回调里掷骰子
✏️ 在 draw_callback 函数里第一行加(替换对应 TODO):
int r = std::rand() % 100;
这一行还看不到效果。下一步把
r显示在窗口上。
4. 步骤 3:用 sprintf 把数字显示出来
🤔 先想一下
result_box->label(...)只接受字符串。你怎么把整数r变成字符串?- 以前命令行用
std::cout << "r=" << r,但label没有<<。怎么办?
💡 新工具:sprintf
char info[50]; // 准备一个 50 字节的盒子
std::sprintf(info, "本次点数:%d", r); // 把 "本次点数:" + r 的值 塞进盒子
info_box->label(info); // 把盒子的内容贴到 label 上
%d 是占位符,运行时被 r 的值替换。
✏️ 改 3 个地方
(1) 顶上加(替换对应 TODO):
#include <cstdio> // sprintf
(2) 在全局区,result_box 下面加(替换对应 TODO):
Fl_Box *info_box;
(3) 在 main 里,紧挨 result_box 的创建代码下面加(替换对应 TODO):
info_box = new Fl_Box(50, 200, 300, 50, "");
info_box->labelsize(14);
(4) 在 draw_callback 里、int r = ... 那一行下面加(替换对应 TODO):
char info[50];
std::sprintf(info, "本次点数:%d", r);
info_box->label(info);
▶️ 编译运行
✅ 检查点 3
- 点按钮 → 下方多出一行 "本次点数:XX",每次 XX 都不同
- 多点几次,能看到 0~99 各种数值
5. 步骤 4:根据 r 决定稀有度和颜色 ⭐核心⭐
终于到抽卡的灵魂部分了。
✏️ 把 draw_callback 里那行假提示 result_box->label("(写好步骤 4 后这里会变)"); 整段替换成:
if (r < 70) {
result_box->label("☆ 普通卡 ☆");
result_box->labelcolor(fl_rgb_color(128, 128, 128)); // 灰色
} else if (r < 95) {
result_box->label("★ 稀有卡 ★");
result_box->labelcolor(FL_BLUE);
} else {
result_box->label("★★★ 超稀有卡!! ★★★");
result_box->labelcolor(FL_RED);
}
💡 新工具:fl_rgb_color(R, G, B)
每个数 0~255,组合任意颜色:
| 颜色 | RGB |
|---|---|
| 纯白 | (255, 255, 255) |
| 灰 | (128, 128, 128) |
| 金 | (255, 200, 0) |
| 橙 | (255, 128, 0) |
| 紫 | (180, 100, 200) |
▶️ 编译运行
✅ 检查点 4
- 点按钮 → 看到三种结果之一,且颜色不一样
- 多点 20 次,能至少出现 1 次蓝色"稀有"
- 多点 50 次以上,能出现红色"超稀有"
🎉 第 3 课通关! 你的程序"每次都不一样"了。
6. 玩一玩
- 把概率改成"超稀有更多":
r < 50普通,r < 80稀有,其他超稀有。点 20 次试试感受。 - 把超稀有改成金色:
fl_rgb_color(255, 200, 0)。 - 给"超稀有"再加一行祝贺:在那个
else分支里加info_box->label("🎉 恭喜抽到超稀有!🎉");。 - 思考题:如果把
srand那一行从main删掉,多次运行程序会发生什么?
7. 课后挑战(选做)
- A. 加第四档"传说卡":
r >= 99(约 1%),金色,更大字号。注意调整原来 95 的判断。 - B. 抽卡计数:用全局
int total = 0;统计点击次数,每次抽完显示在info_box上("第 N 次:r=XX")。 - C. 思考题:
if (r < 70) ... else if (r < 95) ...—— 为什么 95 那行不需要写r >= 70 && r < 95?
8. 卡住了?常见小问题
| 现象 | 多半是 |
|---|---|
| 每次结果都一样 | srand 没写、或者写在了回调里 |
编译报 rand / srand / time 未定义 |
忘了 #include <cstdlib> 或 <ctime> |
编译报 sprintf 未定义 |
忘了 #include <cstdio> |
| 信息行显示乱码 | char info[50] 太小?改成 info[100] 试试 |
| 颜色没变 | labelcolor 拼错;或 fl_rgb_color 写成了 FL_rgb_color |
9. 偷看老师答案
cmake -B build -DLESSON=3 -DUSE_SOLUTION=ON
cmake --build build && ./build/gacha3
10. 下一课预告
第 4 课加一个 "十连抽" 按钮:一次抽 10 张全部显示。你会用上老朋友 for 循环和字符串拼接。
📝 学完记得在
学习进度与状态-GUI.md里把第 3 课打勾。
下一课:第04课-十连抽.md