Files
chouka/第04课-十连抽.md
T
2026-05-16 11:09:02 +08:00

228 lines
6.3 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.
# 第 4 课:十连抽
**本课目标**:加一个"十连抽"按钮,用 `for` 循环一口气抽 10 次,所有结果一起显示。学会在 GUI 里用循环 + 字符串拼接。
**复习**:第 3 课单抽;命令行时期你已掌握 `for` 循环和 `string`
---
## 1. 这一课要搞懂什么
### 命令行 vs GUI 的多抽
**命令行时代(第 6 课)**
```cpp
for (int i = 1; i <= 10; i++) {
int r = rand() % 100;
cout << "第" << i << "抽:";
// ... 判断稀有度 ...
}
```
每轮 `cout` 一行,10 行自动排队显示。
**GUI 时代(本课)**
```cpp
for (int i = 1; i <= 10; i++) {
int r = rand() % 100;
// 不能直接 "cout" → 要把结果攒起来,最后一次性 label
}
```
GUi 的 `label()` 每次都会覆盖旧内容。所以需要**先把 10 行拼成一个大字符串,再一次性显示**。
### 核心技巧:字符串拼接
```cpp
std::string result = ""; // 空字符串
result += "第1抽:普通\n"; // 追加一行
result += "第2抽:稀有\n"; // 再追加
// ... 最后 label(result.c_str())
```
`+=` 像胶水,把一段段文字粘在一起。`\n` 是换行。
---
## 2. 程序结构
- 保留"单抽"按钮(第 3 课功能)
- 新增"十连抽"按钮 → 调用新的回调函数 `draw_ten_callback`
- 两个按钮共用一个结果框
## 3. 完整示例程序
保存为 `lesson_gui_4.cpp`
```cpp
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Box.H>
#include <cstdlib>
#include <ctime>
#include <string> // for std::string
// 全局变量
Fl_Box *result_box;
Fl_Box *info_box;
// 单抽回调
void draw_one_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);
}
}
// 十连抽回调
void draw_ten_callback(Fl_Widget *w, void *data) {
std::string all = ""; // 攒结果的字符串
int rare_count = 0; // 统计稀有数量
int super_count = 0;
for (int i = 1; i <= 10; i++) {
int r = std::rand() % 100;
all += "第" + std::to_string(i) + "抽:";
if (r < 70) {
all += "普通";
} else if (r < 95) {
all += "★稀有★";
rare_count++;
} else {
all += "★★★超稀有★★★";
super_count++;
}
all += "\n"; // 换行
}
// 用 sprintf 做统计信息
char stats[100];
std::sprintf(stats, "十连抽结果 | 稀有:%d 超稀有:%d",
rare_count, super_count);
info_box->label(stats);
// 统计信息变颜色:有超稀有时红色,否则蓝色
if (super_count > 0) {
info_box->labelcolor(FL_RED);
} else if (rare_count > 0) {
info_box->labelcolor(FL_BLUE);
} else {
info_box->labelcolor(fl_rgb_color(128, 128, 128));
}
result_box->labelsize(16); // 十连字多,用小一点的字
result_box->label(all.c_str());
result_box->labelcolor(FL_BLACK);
}
int main() {
std::srand(std::time(nullptr));
// 窗口加高,容纳十连抽的内容
Fl_Window *window = new Fl_Window(420, 480, "抽卡世界 - 第4课");
// 单抽按钮
Fl_Button *btn1 = new Fl_Button(50, 20, 140, 50, "🎴 抽一张");
btn1->labelsize(18);
btn1->callback(draw_one_callback);
// 十连抽按钮
Fl_Button *btn10 = new Fl_Button(230, 20, 140, 50, "🎴🎴 十连抽");
btn10->labelsize(18);
btn10->callback(draw_ten_callback);
// 信息行
info_box = new Fl_Box(30, 80, 360, 30, "");
info_box->labelsize(14);
// 结果区域(加大高度)
result_box = new Fl_Box(30, 120, 360, 330, "点击按钮开始抽卡!");
result_box->labelsize(20);
result_box->align(FL_ALIGN_TOP | FL_ALIGN_LEFT); // 左上对齐
window->end();
window->show();
return Fl::run();
}
```
---
## 4. 新知识点
### `std::to_string(i)` —— 数字变字符串
```cpp
all += "第" + std::to_string(i) + "抽:";
```
`std::to_string(5)` 变成 `"5"`。在旧课程里你用 `cout << i`GUI 里需要用 `to_string` 把数字变成字符串后才能用 `+` 拼接。
> 需要 `#include <string>`
### `align()` —— 文字对齐方式
```cpp
result_box->align(FL_ALIGN_TOP | FL_ALIGN_LEFT);
```
`FL_ALIGN_TOP` = 靠上,`FL_ALIGN_LEFT` = 靠左,`|` 表示"同时用"。
### `.c_str()` —— 从 `std::string` 到 C 字符串
```cpp
result_box->label(all.c_str());
```
FLTK 的 `label()` 需要 C 风格字符串(`const char*`),`.c_str()``std::string` 转换过去。
---
## 5. 本课课堂小练习
1. 把十连抽改成"五连抽":改循环次数,改按钮文字。
2. 在十连抽的结果中,把"超稀有"那行用特殊符号标出来(比如加 `>>>``!!!`)。
3. 加一个"重置"按钮,清空结果显示区。
---
## 6. 本课课后作业(选做)
- 让单抽和十连抽**共用**一个判断稀有度的代码,不重复写两遍(提示:写一个函数返回稀有度名字,回调里调用它)。这是旧课程第 8 课"函数"的 GUI 版应用!
- 让窗口可以调整大小——试试 `window->resizable(window)` 的效果。
- 十连抽结果显示不下怎么办?试试把窗口再调大一点,把 `result_box` 也调大。
---
## 7. 常见小问题
- **十连抽结果只显示最后一行**:检查是否在循环里反复调用了 `result_box->label()`(应该先拼完再统一 label)。
- **`to_string` 报错**:需要 `#include <string>`
- **`.c_str()` 不认识的报错**:确认变量是 `std::string` 类型。
- **中文在十连结果中乱码**:确认文件保存为 UTF-8。
---
## 8. 下一课预告
第 5 课给每档稀有度配上**具体的卡片名字**(皮卡丘、喷火龙…),用数组存储。还要学会让界面更整齐漂亮。
---
*学完请在 `学习进度与状态-GUI.md` 里更新第 4 课的状态。*
**下一课文档**`第05课-卡片名字与布局.md`