系列最后一篇:一个"剧情向"的文本 RPG,没有画面,全靠文字和想象力。玩家冒险、战斗、捡装备吃药水,把类、std::vector、std::map 和随机数串起来。
玩法介绍
四个地点:村庄回血、森林与城堡打怪、山洞捡宝箱。回合制战斗,击败得经验;血低喝药续命。
第 1 步:玩家类 Player
玩家属性装进 Player 类:血量、攻击、等级、经验,以及 std::vector<std::string> 背包。gainExp() 用 while 处理连升多级:
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <random>
class Player {
public:
std::string name;
int hp, maxHp, atk, level, exp;
std::vector<std::string> bag;
Player(const std::string& n)
: name(n), hp(30), maxHp(30), atk(5), level(1), exp(0) {}
void heal(int amount) {
hp += amount;
if (hp > maxHp) hp = maxHp;
}
void gainExp(int e) {
exp += e;
while (exp >= level * 10) {
exp -= level * 10;
++level;
maxHp += 10;
hp = maxHp;
atk += 2;
std::cout << "升级!Lv." << level << "\n";
}
}
};
第 2 步:怪物与回合制战斗
怪物用 struct 表示,makeMonster() 按等级随机生成属性。战斗:选攻击,双方互相扣血,击败得经验:
struct Monster {
std::string name;
int hp, atk;
};
Monster makeMonster(int lv) {
static const std::vector<std::string> names = {"史莱姆", "野狼", "骷髅"};
std::mt19937 g(std::random_device{}());
std::uniform_int_distribution<int> pick(0, (int)names.size() - 1);
std::uniform_int_distribution<int> hpR(10 + lv * 3, 15 + lv * 4);
std::uniform_int_distribution<int> atkR(2 + lv, 3 + lv * 2);
return {names[pick(g)], hpR(g), atkR(g)};
}
void battle(Player& p, Monster m) {
std::cout << "遭遇 " << m.name << "!\n";
while (p.hp > 0 && m.hp > 0) {
std::cout << "按 1 攻击: ";
int cmd;
std::cin >> cmd;
m.hp -= p.atk;
p.hp -= m.atk;
std::cout << "你打它 " << p.atk << ",它打你 " << m.atk << "\n";
}
if (p.hp > 0) {
int r = 8 + p.level * 3;
p.gainExp(r);
std::cout << "击败 " << m.name << ",得 " << r << " 经验!\n";
}
}
第 3 步:用 std::map 搭建地图
地图是"地点名 → 描述"的映射,适合 std::map。加地点只需在 buildMap() 里加一行:
std::map<std::string, std::string> buildMap() {
std::map<std::string, std::string> m;
m["村庄"] = "和平之地,可以休息。";
m["森林"] = "野兽出没。";
m["山洞"] = "黑暗的山洞,有宝箱。";
m["城堡"] = "冒险的终点。";
return m;
}
第 4 步:背包系统
背包就是 std::vector<std::string>:push_back 捡、empty 判空、pop_back 用。主循环里三行代码完成"判断 → 使用 → 回血":
// 背包三件套:push_back 捡、empty 判空、pop_back 用
if (!hero.bag.empty()) {
std::cout << "喝下 " << hero.bag.back() << ",+15 HP。\n";
hero.bag.pop_back();
hero.heal(15);
}
第 5 步:主循环与编译运行
主循环负责显示地图、选择地点、触发事件。把第 1~4 步的代码与下面的 main() 存进 rpg.cpp:
int main() {
std::cout << "=== 文本 RPG ===\n";
Player hero("勇者");
std::map<std::string, std::string> places = buildMap();
while (hero.hp > 0) {
std::cout << "\nHP " << hero.hp << "/" << hero.maxHp
<< " Lv." << hero.level << " 背包 " << hero.bag.size() << " 件\n";
for (const auto& [place, desc] : places)
std::cout << "[" << place << "] " << desc << "\n";
std::cout << "去哪? ";
std::string dest;
std::cin >> dest;
if (!places.count(dest)) {
std::cout << "没这地方。\n";
continue;
}
if (dest == "村庄") {
hero.heal(999);
std::cout << "休息一晚,HP 回满。\n";
} else if (dest == "山洞") {
hero.bag.push_back("治疗药水");
std::cout << "宝箱!获得 治疗药水。\n";
} else {
battle(hero, makeMonster(hero.level));
}
if (hero.hp < hero.maxHp / 2 && !hero.bag.empty()) {
std::cout << "喝药?(1 喝 / 0 不喝): ";
int use;
std::cin >> use;
if (use == 1) {
std::cout << "喝下 " << hero.bag.back() << ",+15 HP。\n";
hero.bag.pop_back();
hero.heal(15);
}
}
}
std::cout << "你倒下了……\n";
return 0;
}
g++ -std=c++17 -o rpg rpg.cpp
./rpg
💡 改进方向:① 用 std::map 做技能表;② 商店系统;③ 存档/读档;④ 随机事件,让每次探索不一样。