Files
chouka/第03课-随机抽卡.md
T
2026-05-16 11:09:02 +08:00

184 lines
5.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 第 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 <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Box.H>
#include <cstdlib> // rand, srand
#include <ctime> // 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 <cstdlib>``<ctime>`**:报 `rand`/`srand`/`time` 未定义。
- **`sprintf` 缓冲区太小**:如果信息很长,`char info[50]` 可以改成 `char info[100]`
---
## 9. 下一课预告
第 4 课做**十连抽**:加一个"十连抽"按钮,用 `for` 循环一口气生成 10 个结果,全部显示在窗口里。
---
*学完请在 `学习进度与状态-GUI.md` 里更新第 3 课的状态。*
**下一课文档**`第04课-十连抽.md`