一:题目分析
1.功能描述
几乎所有的RPG游戏(一种源自《龙与地下城》的游戏类型)在进入游戏时都会让用户自己来创建自己喜欢的角色。本次上机要求编写一个简化的创建游戏角色的程序。
2.游戏角色应有的属性
本题目要求的游戏角色应有以下属性:名字、性别、种族、职业、力量、敏捷、体力、智力、智慧、生命值和魔法值。
名字:不超过50个字符。
性别:可以选择男性和女性。
种族:一共可选五个种族,人类、精灵、兽人、矮人和元素。
职业:可选六种职业,狂战士、圣骑士、刺客、猎手、祭司和巫师。
其余属性均为整数。
本题目要求首先用户输入角色姓名,然后由用户选择角色性别,然后由用户选择种族,然后选择职业,然后自动分配力量、敏捷、体力、智力和智慧属性,并计算生命值和魔法值。
生命值=体力*20。
魔法值=(智力+智慧)*10。
3.职业限制
很多职业会限制某些种族选择,例如兽人不能就职圣骑士等等,种族和职业的限制表如下:
种族/职业 | 狂战士 | 圣骑士 | 刺客 | 猎手 | 祭司 | 巫师 |
人类 | 允许 | 允许 | 允许 | 允许 | 允许 | 允许 |
精灵 | 不允许 | 不允许 | 允许 | 允许 | 允许 | 允许 |
兽人 | 允许 | 不允许 | 不允许 | 允许 | 允许 | 不允许 |
矮人 | 允许 | 允许 | 不允许 | 不允许 | 允许 | 不允许 |
元素 | 不允许 | 不允许 | 不允许 | 不允许 | 允许 | 允许 |
所以在要求用户选择职业时,输出信息里面只能有用户所选择种族可以就职的职业。
4.初始属性
本题目要求力量、敏捷、体力、智力和智慧要求是随机值(利用随机数函数来取得随机数),但是五项属性的总和应该是100,并且应该和职业相关。例如狂战士的体力和力量就要比较高,而巫师需要较高的智力,而祭司则需要较高的智慧。各职业初始属性的大致比例应遵从下表:
职业/属性 | 力量 | 敏捷 | 体力 | 智力 | 智慧 |
狂战士 | 40 | 20 | 30 | 5 | 5 |
圣骑士 | 25 | 15 | 30 | 20 | 10 |
刺客 | 20 | 35 | 20 | 15 | 10 |
猎手 | 15 | 40 | 15 | 10 | 20 |
祭司 | 15 | 20 | 15 | 35 | 15 |
巫师 | 10 | 20 | 10 | 20 | 40 |
例如,前面示意图中的祭司的初始属性,大致满足该比例,但是应该是随机的。
然后利用属性值计算生命值和魔法值。
5.显示信息
最后向用户显示该角色的所有信息,然后询问用户是否满意,如用户不满意则重新创建,若用户满意则程序结束,并将用户创建角色的相关信息写入文件保存。
二:类图设计
三:程序实现
#include<iostream> //输入输出流头文件
#include<stdlib.h> //标准库头文件
#include<fstream> //操作文件读入写出头文件
#include<ctime> //随机数头文件
#include<string> //使用字符串头文件
using namespace std; //使用命名空间std
int profession_choice; //选择职业序号数字
class role //角色基类
{
protected:
char name[50]; //名字
string sex; //性别
public:
void getrole(); //得到角色基本信息函数
friend class output; //友元类,用于输出角色信息
friend class File; //友元类,将角色信息保存在文档中
};
void role::getrole() // 得到角色基本信息
{
cout<<"输入您游戏角色的姓名:"<<endl;
cin>>name; //输入姓名
int a; //选择角色性别序号
cout<<"选择您游戏角色的性别(0 男性,1 女性):"<<endl;
cin>>a;
if(a==0)
sex="男性";
else if(a==1)
sex="女性";
else
{
cout<<"输入错误,请重新输入"<<endl;
getrole();
}
}
class roce:public role //role派生类,记录角色的种族和职业
{
protected:
string race; //种族
string profession; //职业
public:
void getrace(); //得到种族和职业函数
friend class output; //友元类,用于输出角色信息
friend class File; //友元类,将角色信息保存在文档中
};
void roce::getrace() //得到种族和职业函数
{
int a;
cout<<"选择您游戏角色的种族(0 人类,1 精灵,2 兽人,3 矮人,4 元素):"<<endl;
cin>>a; //选择角色种族序号
if(a==0)
{ race="人类";
cout<<"选择您的职业(0 狂战士,1 圣骑士,2 刺客,3 猎手,4 祭祀,5 巫师):"<<endl;
}
else if(a==1)
{ race="精灵";
cout<<"选择您的职业(2 刺客,3 猎手,4 祭祀,5 巫师):"<<endl;
}
else if(a==2)
{ race="兽人";
cout<<"选择您的职业(0 狂战士,3 猎手,4 祭祀):"<<endl;
}
else if(a==3)
{ race="矮人";
cout<<"选择您的职业(0 狂战士,1 圣骑士,4 祭祀):"<<endl;
}
else if(a==4)
{ race="元素";
cout<<"选择您的职业(4 祭祀,5 巫师):"<<endl;
}
else
{ //输入数字有误时重新调用函数
cout<<"输入有误请输入正确数字"<<endl;
getrace();
}
cin>>profession_choice; //选择职业序号数字
//根据数字为职业赋值
switch(profession_choice)
{ case 0:profession ="狂战士";break;
case 1:profession = "圣骑士";break;
case 2: profession = "刺客";break;
case 3:profession= "猎手";break;
case 4:profession = "祭司";break;
case 5:profession= "巫师";break;
default:getrace();
}
}
class skill:public roce //roce派生类skill记录角色的属性
{
protected:
int strength; //力量
int agility; //敏捷
int physical; //体力
int intellective; //智力
int wisdom; //智慧
int HP; //生命值
int MP; //魔法值
public:
void getskill(); //获得属性函数
void getrandom(int a,int b,int c,int d,int e); //生成角色属性随机数函数
friend class output; //友元类,用于输出角色信息
friend class File; //友元类,将角色信息保存在文档中
};
void skill::getrandom(int a,int b,int c,int d,int e) //随机生成每项属性的值
{
int sum; //5个属性的总和
srand((unsigned)time(NULL)); //产生随机种子
do
{
strength =a+( rand() % 5); //力量
agility =b+ (rand() % 5); //敏捷
physical =c+( rand() % 5); // 体力
intellective =d+(rand() % 5); //智力
wisdom = e+(rand()%5); //智慧
sum = strength + agility + physical + intellective+wisdom;
} while (sum<100);
HP = physical * 20; // 生命值
MP = (wisdom + intellective) * 10; //魔法值
}
void skill::getskill() //根据选择的职业,向 getrandom传各个职业的大致比例而生成随机数
{
if (profession_choice == 0)
getrandom(40, 20, 30, 5, 5);//狂战士
if (profession_choice == 1)
getrandom(25, 15, 30, 20, 10);//圣骑士
if (profession_choice == 2)
getrandom(20, 35, 20, 15, 10);//刺客
if (profession_choice == 3)
getrandom(15, 40, 15, 10, 20);//猎手
if (profession_choice == 4)
getrandom(15, 20, 15, 35, 15);//祭司
if (profession_choice == 5)
getrandom(10, 20, 10, 20, 40);//巫师
}
class output //输出角色属性
{
public:
void show(role &, roce &, skill &);//访问友元类
};
void output::show(role &t1, roce &t2,skill &t3)
{
cout <<"==================================== "<< endl;
cout <<"姓名:"<< t1.name << endl;
cout << "==================================== " << endl;
cout << "性别:" << t1.sex << endl;
cout << "==================================== "<< endl;
cout << "种族:" << t2.race << endl;
cout << "==================================== " << endl;
cout <<" 职业:" << t2.profession << endl;
cout << "===================================="<< endl;
cout << "力量:" << t3.strength << endl;
cout << " ====================================" << endl;
cout << "敏捷: "<< t3.agility << endl;
cout << "==================================== " << endl;
cout << "体力:" << t3.physical << endl;
cout << "==================================== " << endl;
cout << "智力: "<< t3.intellective << endl;
cout << "==================================== " << endl;
cout <<" 智慧:" << t3.wisdom << endl;
cout << "==================================== " << endl;
cout << "生命值:" << t3.HP << endl;
cout << " ====================================" << endl;
cout << "魔法值:" << t3.MP << endl;
cout << "==================================== " << endl;
}
//将角色信息保存在文档中
class File
{
public:
void file(role &, roce &, skill &);
};
void File::file(role &t1, roce &t2, skill &t3)
{
ofstream outfile;
outfile.open("data.txt", ios::out);
cout << ""<<endl;
outfile <<"姓名:" << t1.name <<endl;
cout << ""<<endl;
outfile << "性别:" << t1.sex <<endl;
cout << ""<<endl;
outfile<<" 种族:" << t2.race <<endl;
cout << ""<<endl;
outfile <<" 职业:" << t2.profession <<endl;
cout << ""<<endl;
outfile <<" 力量:" << t3.strength <<endl;
cout << ""<<endl;
outfile <<" 敏捷:" << t3.agility <<endl;
cout << ""<<endl;
outfile << "体力:" << t3.physical <<endl;
cout << ""<<endl;
outfile << "智力:"<<t3.intellective<<endl;
cout << ""<<endl;
outfile << "智慧: "<< t3.wisdom <<endl;
cout << ""<<endl;
outfile << "生命值: "<< t3.HP << endl;
cout << ""<<endl;
outfile << "法力值: "<< t3.MP << endl;
cout << ""<<endl;
}
//主函数
int main()
{ role player; //角色基类对象
roce player_race; //角色派生类对象
skill player_ski; //属性类对象
output player_show; //输出类
File test;
int i;
do
{
player.getrole(); //调用获得名字,性别信息函数
player_race.getrace(); //调用得到 种族,职业信息函数
player_ski.getskill(); //调用获得角色属性函数
player_show.show(player, player_race, player_ski); //输出信息
cout << endl;
cout << "是否继续生成角色游戏?"<< endl;
cout << "0.退出游戏 1.继续生成" << endl;
cin >> i;
} while (i);
test.file(player, player_race, player_ski); //将角色信息保存在文档中
return 0;
}
四:调试、测试及运行结果
4.1调试
Profession_choice//职业选择序号
要被继承类的方法函数调用,不能设置为私有,派生类不能访问。
然后我将这个变量改为受保护的,程序没有问题,可以与运行。
但是运行结果还会出现角色属性随机数值都有问题(如下图)。
通过调试发现这个变量应设为全局变量而解决随机数的问题。
4.2测试
4.2.1:测试代码以及截图(创建角色姓名以及性别)
class role //角色基类
{
private:
char name[50]; //名字
string sex; //性别
public:
void getrole(); //得到角色基本信息函数
friend class output; //友元类,用于输出角色信息
friend class File; //友元类,将角色信息保存在文档中
};
void role::getrole() // 得到角色基本信息
{
cout<<"输入您游戏角色的姓名:"<<endl;
cin>>name; //输入姓名
int a; //选择角色性别序号
cout<<"选择您游戏角色的性别(0 男性,1 女性):"<<endl;
cin>>a;
if(a==0)
sex="男性";
else if(a==1)
sex="女性";
else
{
cout<<"输入错误,请重新输入"<<endl;
getrole();
}
}
class output //输出角色属性
{
public:
void show(role &);//访问友元类
};
void output::show(role &t1)
{ cout <<"==================================== "<< endl;
cout <<"姓名:"<< t1.name << endl;
cout << "==================================== " << endl;
cout << "性别:" << t1.sex << endl;
}
int main()
{
role player; //角色基类对象
player.getrole(); //调用获得名字,性别信息函数
output player_show; //输出类
player_show.show(player); //输出信息
return 0;
}
4.2.2:测试代码以及截图(获得角色种族和职业):
class roce //role派生类,记录角色的种族和职业
{
private:
string race; //种族
string profession; //职业
protected:
int profession_choice; //选择职业序号数字
public:
void getrace(); //得到种族和职业函数
friend class output; //友元类,用于输出角色信息
friend class File; //友元类,将角色信息保存在文档中
};
void roce::getrace() //得到种族和职业函数
{
int a;
cout<<"选择您游戏角色的种族(0 人类,1 精灵,2 兽人,3 矮人,4 元素):"<<endl;
cin>>a; //选择角色种族序号
if(a==0)
{ race="人类";
cout<<"选择您的职业(0 狂战士,1 圣骑士,2 刺客,3 猎手,4 祭祀,5 巫师):"<<endl;
}
else if(a==1)
{ race="精灵";
cout<<"选择您的职业(2 刺客,3 猎手,4 祭祀,5 巫师):"<<endl;
}
else if(a==2)
{ race="兽人";
cout<<"选择您的职业(0 狂战士,3 猎手,4 祭祀):"<<endl;
}
else if(a==3)
{ race="矮人";
cout<<"选择您的职业(0 狂战士,1 圣骑士,4 祭祀):"<<endl;
}
else if(a==4)
{ race="元素";
cout<<"选择您的职业(4 祭祀,5 巫师):"<<endl;
}
else
{ //输入数字有误时重新调用函数
cout<<"输入有误请输入正确数字"<<endl;
getrace();
}
cin>>profession_choice; //选择职业序号数字
//根据数字为职业赋值
switch(profession_choice)
{ case 0:profession ="狂战士";break;
case 1:profession = "圣骑士";break;
case 2: profession = "刺客";break;
case 3:profession= "猎手";break;
case 4:profession = "祭司";break;
case 5:profession= "巫师";break;
default:getrace();
}
}
class output //输出角色属性
{
public:
void show(roce &);//访问友元类
};
void output::show(roce &t2)
{
cout << "==================================== "<< endl;
cout << "种族:" << t2.race << endl;
cout << "==================================== " << endl;
cout <<" 职业:" << t2.profession << endl;
}
int main()
{
roce player_race; //角色派生类对象
player_race.getrace(); //调用得到 种族,职业信息函数
output player_show; //输出类
player_show.show(player_race); //输出信息
return 0;
}
4.2.2:测试代码以及截图(获得角色属性):
class skill //roce派生类skill记录角色的属性
{
private:
int strength; //力量
int agility; //敏捷
int physical; //体力
int intellective; //智力
int wisdom; //智慧
int HP; //生命值
int MP; //魔法值
int profession_choice;
public:
void getskill(); //获得属性函数
void getrandom(int a,int b,int c,int d,int e); //生成角色属性随机数函数
friend class output; //友元类,用于输出角色信息
friend class File; //友元类,将角色信息保存在文档中
};
void skill::getrandom(int a,int b,int c,int d,int e) //随机生成每项属性的值
{
int sum; //5个属性的总和
srand((unsigned)time(NULL)); //产生随机种子
do
{
strength =a+( rand() % 5); //力量
agility =b+ (rand() % 5); //敏捷
physical =c+( rand() % 5); // 体力
intellective =d+(rand() % 5); //智力
wisdom = e+(rand()%5); //智慧
sum = strength + agility + physical + intellective+wisdom;
} while (sum<100);
HP = physical * 20; // 生命值
MP = (wisdom + intellective) * 10; //魔法值
}
void skill::getskill() //根据选择的职业,向 getrandom传各个职业的大致比例而生成随机数
{cin>>profession_choice;
if (profession_choice == 0)
getrandom(40, 20, 30, 5, 5);//狂战士
if (profession_choice == 1)
getrandom(25, 15, 30, 20, 10);//圣骑士
if (profession_choice == 2)
getrandom(20, 35, 20, 15, 10);//刺客
if (profession_choice == 3)
getrandom(15, 40, 15, 10, 20);//猎手
if (profession_choice == 4)
getrandom(15, 20, 15, 35, 15);//祭司
if (profession_choice == 5)
getrandom(10, 20, 10, 20, 40);//巫师
}
class output //输出角色属性
{
public:
void show(skill &);//访问友元类
};
void output::show(skill &t3)
{
cout << "===================================="<< endl;
cout << "力量:" << t3.strength << endl;
cout << " ====================================" << endl;
cout << "敏捷: "<< t3.agility << endl;
cout << "==================================== " << endl;
cout << "体力:" << t3.physical << endl;
cout << "==================================== " << endl;
cout << "智力: "<< t3.intellective << endl;
cout << "==================================== " << endl;
cout <<" 智慧:" << t3.wisdom << endl;
cout << "==================================== " << endl;
cout << "生命值:" << t3.HP << endl;
cout << " ====================================" << endl;
cout << "魔法值:" << t3.MP << endl;
cout << "==================================== " << endl;
}
int main()
{
skill player_ski; //属性类对象
output player_show; //输出类
player_ski.getskill(); //调用获得角色属性函数
player_show.show( player_ski); //输出信息
}
4.3运行结果
五:总结
在本次上机实验中,巩固了面向对象程序设计的类继承,友元类的使用和基类成员在派生类中的访问对象,让程序有良好的封装性,以及随机数的产生以及文件的使用。
最开始我写了程序的大体框架,在文件以及随机数按比例产生角色属性的地方有点卡顿,后来在查阅资料中(参考了https://blog.csdn.net/weixin_40933169/article/details/82953738的博客)有了一点灵感,也进行了类的修改,让程序更好的封装。整体设计完成后,我的随机数生成的属性值有好几个零,后来发现是类的成员变量的属性问题,需要把角色的职业选择设置成全局变量,解决了属性值的问题。
保护继承的特点:保护基类的公有成员和保护成员在派生类中都成了保护成员,其私有成员仍为基类私有。