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

5.6 KiB
Raw Blame History

第 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

#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 —— 把数字变成字符串

char info[50];
std::sprintf(info, "本次点数:%d", r);
info_box->label(info);

以前你可能会写:

std::cout << "本次点数:" << r << "\n";

但 FLTK 的 label() 只能接受一个字符串。sprintf 把数字塞进字符串里——%d 的位置被 r 的值替换。

fl_rgb_color —— 自定义颜色

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