# 第 3 课:随机抽卡——核心来了 **本课目标**:点击按钮 → 随机出"普通卡 / 稀有卡 / 超稀有卡",结果显示在窗口里。把命令行抽卡逻辑搬进 GUI。 **复习**:第 2 课学会了按钮和回调;命令行时期你已掌握 `rand()`、`srand()`、`if/else`。 --- ## 1. 这一课要搞懂什么 把以前学过的三样东西组合进一个 GUI 程序: | 老朋友 | 在 GUI 里怎么用 | |--------|---------------| | `srand(time(...))` | 在 `main` 开头调一次(和以前一样) | | `rand() % 100` | 放在回调函数里,每次点击执行 | | `if / else if / else` | 放在回调函数里,根据 `r` 决定显示什么 | **关键区别**:以前判断完用 `cout` 打印,现在用 `label()` 更新窗口上的文字。 --- ## 2. 稀有度与颜色搭配 本课不仅显示文字,还**用不同颜色**区分稀有度: | 随机数 r | 稀有度 | 文字颜色 | 概率 | |----------|--------|---------|------| | 0 ~ 69 | 普通卡 | 灰色 | ~70% | | 70 ~ 94 | 稀有卡 | 蓝色 | ~25% | | 95 ~ 99 | 超稀有卡! | 红色 | ~5% | 颜色让稀有卡一眼就能看出来——比命令行效果震撼多了。 --- ## 3. 完整示例程序 保存为 `lesson_gui_3.cpp`: ```cpp #include #include #include #include #include // rand, srand #include // time // 全局变量 Fl_Box *result_box; Fl_Box *info_box; // 显示本次点数的辅助信息 // 回调函数——每次点击按钮执行 void draw_callback(Fl_Widget *w, void *data) { int r = std::rand() % 100; // 显示点数(调试用,熟悉后可以删掉这行) char info[50]; std::sprintf(info, "本次点数:%d", r); info_box->label(info); // 判断稀有度 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); // 红色 } } int main() { std::srand(std::time(nullptr)); // 种子设一次 Fl_Window *window = new Fl_Window(400, 350, "抽卡世界 - 第3课"); // 抽卡按钮 Fl_Button *btn = new Fl_Button(130, 30, 140, 50, "🎴 抽一张!"); btn->labelsize(18); btn->callback(draw_callback); // 结果标签(大字号) result_box = new Fl_Box(50, 110, 300, 80, "点击按钮抽卡吧!"); result_box->labelsize(28); result_box->labelfont(FL_BOLD); // 信息标签(小字号,显示点数) info_box = new Fl_Box(50, 200, 300, 50, ""); info_box->labelsize(14); window->end(); window->show(); return Fl::run(); } ``` --- ## 4. 新知识点 ### sprintf —— 把数字变成字符串 ```cpp char info[50]; std::sprintf(info, "本次点数:%d", r); info_box->label(info); ``` 以前你可能会写: ```cpp std::cout << "本次点数:" << r << "\n"; ``` 但 FLTK 的 `label()` 只能接受一个字符串。`sprintf` 把数字塞进字符串里——`%d` 的位置被 `r` 的值替换。 ### fl_rgb_color —— 自定义颜色 ```cpp fl_rgb_color(128, 128, 128) // R=128, G=128, B=128 → 灰色 fl_rgb_color(255, 200, 0) // → 金色 ``` - R(红)、G(绿)、B(蓝),每个 0~255 - 试试不同的数值创造你自己的颜色! --- ## 5. 运行效果 ``` ┌───────── 抽卡世界 - 第3课 ────────┐ │ │ │ ┌──────────────┐ │ │ │ 🎴 抽一张! │ │ │ └──────────────┘ │ │ │ │ ★ 稀有卡 ★ │ ← 蓝色大字 │ │ │ 本次点数:78 │ ← 小字 │ │ └─────────────────────────────────────┘ ``` 连续点击多次,观察普通(灰)、稀有(蓝)、超稀有(红)的分布。超稀有大约 20 次出 1 次。 --- ## 6. 本课课堂小练习 1. 调概率:把 `r < 70` 改成 `r < 50`,把 `r < 95` 改成 `r < 85`。看看稀有和超稀有是不是变多了。 2. 把"超稀有卡"的颜色改成金色 `fl_rgb_color(255, 200, 0)`。 3. 给超稀有卡多加一行"🎉恭喜!"——提示:可以改 `info_box` 的内容。 --- ## 7. 本课课后作业(选做) - 增加第四个稀有度:"传说卡"(`r >= 99`),颜色用金色,概率约 1%。和旧课程第 5 课的练习类似,但现在是 GUI 版本。 - 把按钮文字改成"再抽一次"。 - 试着把窗口上所有文字都换成你自己喜欢的卡牌主题(比如"SSR""SR""R"等)。 --- ## 8. 常见小问题 - **每次结果都一样**:检查 `srand(time(nullptr))` 是否在 `main` 里、是否只写了一次。写在回调里会导致每次点按钮重置种子。 - **缺少 `#include ` 或 ``**:报 `rand`/`srand`/`time` 未定义。 - **`sprintf` 缓冲区太小**:如果信息很长,`char info[50]` 可以改成 `char info[100]`。 --- ## 9. 下一课预告 第 4 课做**十连抽**:加一个"十连抽"按钮,用 `for` 循环一口气生成 10 个结果,全部显示在窗口里。 --- *学完请在 `学习进度与状态-GUI.md` 里更新第 3 课的状态。* **下一课文档**:`第04课-十连抽.md`