184 lines
5.6 KiB
Markdown
184 lines
5.6 KiB
Markdown
# 第 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`
|