Files
chouka/第05课-卡片名字与布局.md
T
2026-05-16 11:09:02 +08:00

250 lines
7.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.
# 第 5 课:卡片名字与布局
**本课目标**:给每种稀有度配上具体的卡片名字(火龙、冰狼、雷霆龙……),让抽卡真正像游戏。学会把界面分区布局,更整齐好看。
**复习**:第 3~4 课只显示稀有度等级;命令行时期你已掌握 `std::string` 数组。
---
## 1. 这一课要搞懂什么
### 从"普通卡"到"小火龙"
以前的结果:
```
★ 稀有卡 ★
```
本课的结果:
```
★ 稀有卡 ★
冰霜之狼
```
**设计思路**:每种稀有度维护一个卡片名字数组,随机抽到某档 → 再随机从该档的卡片里挑一张。
### 卡片表设计
| 稀有度 | 卡片名数组 | 数量 |
|--------|-----------|------|
| 普通 | 小火龙、水跃鱼、绿草龟、小电鼠、土拨仔 | 5 张 |
| 稀有 | 烈焰飞龙、冰霜之狼、暗影豹、风暴鹰、水晶鹿 | 5 张 |
| 超稀有 | 雷霆神龙、凤凰王、深海巨兽 | 3 张 |
### 界面分区
本课把窗口分成三个区域,比之前全部控件堆在一起更好看:
```
┌─────────────────────────────┐
│ 操作区(按钮) │ ← 顶部
├─────────────────────────────┤
│ │
│ 结果显示区 │ ← 中部大面积
│ │
├─────────────────────────────┤
│ 统计/信息区 │ ← 底部
└─────────────────────────────┘
```
---
## 2. 完整示例程序
保存为 `lesson_gui_5.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>
// ===== 卡片数据 =====
// 普通卡名字
std::string normal_cards[5] = {
"小火龙", "水跃鱼", "绿草龟", "小电鼠", "土拨仔"
};
// 稀有卡名字
std::string rare_cards[5] = {
"烈焰飞龙", "冰霜之狼", "暗影豹", "风暴鹰", "水晶鹿"
};
// 超稀有卡名字
std::string super_cards[3] = {
"雷霆神龙", "凤凰王", "深海巨兽"
};
// ===== 全局控件 =====
Fl_Box *result_box;
Fl_Box *stats_box;
// ===== 单抽回调 =====
void draw_one_callback(Fl_Widget *w, void *data) {
int r = std::rand() % 100;
std::string result = "";
std::string card_name = "";
if (r < 70) {
result = "☆ 普通卡 ☆";
card_name = normal_cards[std::rand() % 5];
result_box->labelcolor(fl_rgb_color(128, 128, 128));
} else if (r < 95) {
result = "★ 稀有卡 ★";
card_name = rare_cards[std::rand() % 5];
result_box->labelcolor(FL_BLUE);
} else {
result = "★★★ 超稀有卡!! ★★★";
card_name = super_cards[std::rand() % 3];
result_box->labelcolor(FL_RED);
}
// 拼接:稀有度 + 换行 + 卡片名
std::string display = result + "\n " + card_name + "\n";
result_box->labelsize(24);
result_box->label(display.c_str());
stats_box->label("单抽模式");
}
// ===== 十连抽回调 =====
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 += "普通 " + normal_cards[std::rand() % 5];
} else if (r < 95) {
all += "★稀有 " + rare_cards[std::rand() % 5];
rare_count++;
} else {
all += "★★★超稀有 " + super_cards[std::rand() % 3];
super_count++;
}
all += "\n";
}
result_box->labelsize(15);
result_box->label(all.c_str());
result_box->labelcolor(FL_BLACK);
char stats[100];
std::sprintf(stats, "十连:普通 %d | 稀有 %d | 超稀有 %d",
10 - rare_count - super_count, rare_count, super_count);
stats_box->label(stats);
stats_box->labelcolor(super_count > 0 ? FL_RED : FL_BLUE);
}
int main() {
std::srand(std::time(nullptr));
Fl_Window *window = new Fl_Window(420, 520, "抽卡世界 - 第5课");
// ===== 操作区 =====
Fl_Button *btn1 = new Fl_Button(40, 15, 150, 45, "🎴 抽一张");
btn1->labelsize(18);
btn1->callback(draw_one_callback);
Fl_Button *btn10 = new Fl_Button(230, 15, 150, 45, "🎴🎴 十连抽");
btn10->labelsize(18);
btn10->callback(draw_ten_callback);
// ===== 结果显示区 =====
result_box = new Fl_Box(20, 80, 380, 350, "点击按钮开始抽卡!");
result_box->labelsize(22);
result_box->align(FL_ALIGN_TOP | FL_ALIGN_LEFT);
result_box->box(FL_DOWN_BOX); // 凹陷边框,像卡片
// ===== 统计区 =====
stats_box = new Fl_Box(20, 445, 380, 40, "等待抽卡...");
stats_box->labelsize(14);
stats_box->box(FL_FLAT_BOX);
stats_box->color(fl_rgb_color(230, 230, 230)); // 浅灰背景
window->end();
window->show();
return Fl::run();
}
```
---
## 3. 新知识点
### 多组卡片数组
```cpp
std::string normal_cards[5] = { "小火龙", "水跃鱼", ... };
std::string rare_cards[5] = { ... };
std::string super_cards[3] = { ... };
```
- 三组独立数组,每组长度可以不同
- 访问:`normal_cards[rand() % 5]` —— 随机选一张普通卡
### `box()` —— 控件边框样式
```cpp
result_box->box(FL_DOWN_BOX); // 凹陷效果,像一张卡片
stats_box->box(FL_FLAT_BOX); // 扁平
```
常用样式:
| 样式 | 效果 |
|------|------|
| `FL_NO_BOX` | 无边框 |
| `FL_FLAT_BOX` | 平面(默认) |
| `FL_UP_BOX` | 凸起 |
| `FL_DOWN_BOX` | 凹陷 |
| `FL_EMBOSSED_BOX` | 浮雕 |
### `color()` —— 控件背景色
```cpp
stats_box->color(fl_rgb_color(230, 230, 230));
```
给控件设置背景颜色。注意:`color()` 是背景色,`labelcolor()` 是文字颜色。
---
## 4. 本课课堂小练习
1. 把三组卡片名字全部换成你自己喜欢的(宝可梦、数码宝贝、自创的都行)。
2. 给超稀有卡多加 2 张,改数组长度和相关 `% N`
3. 试三种不同的 `box()` 样式,看哪个最好看。
---
## 5. 本课课后作业(选做)
- 加一个"查看图鉴"按钮(先用文字列表显示所有卡片,不要求图片)。提示:把所有卡名拼成一个长字符串显示。
- 在单抽结果中,如果抽到超稀有,把 `result_box``box()` 临时改成 `FL_UP_BOX`(凸起效果,突出显示)。
- 把窗口背景色改一下:`window->color(fl_rgb_color(245, 245, 255));`
---
## 6. 常见小问题
- **`normal_cards` 越界**`% 5` 对应数组长度 5;如果改了数组长度,别忘了改取余的数。
- **卡片名全一样**:检查 `rand() % N` 是否写在需要它的地方——每抽一次应该调一次。
- **窗口不够大**:加了更多内容后,适当增大窗口和控件的尺寸。
---
## 7. 下一课预告
第 6 课把前面所有内容整合成一个**完整的抽卡游戏**——多个按钮(单抽/十连/退出)、金币系统、累计统计。这是本教程的大作业。
---
*学完请在 `学习进度与状态-GUI.md` 里更新第 5 课的状态。*
**下一课文档**`第06课-完整抽卡游戏.md`