该项目有4个ui组成,其中mainwindow作为项目程序入口,窗口跳转流程为:mainwindow->gamegraph->area_prairrie->playerstastus。
主要游戏功能装载在gamegraph和area_prairrie两个ui中,而该两个ui中,主要功能函数分别装载在gamegraph.h、monster中。
对于RPG游戏,person类主要存储了关于游戏玩家的个人属性,包括攻击力、防御力、HP、LV等基础属性以及装备、称号等高级属性。而monster类则存储了怪物的基础属性、战斗模式、击杀奖励等属性及方法。area_prairrie类则存储一个区域的所有怪物对象,及区域内一些游戏性功能。
首先是开始游戏画面,这里可以选择开始游戏,继续游戏和结束游戏三个选项,点击开始/继续游戏后进入下一个界面——主游戏界面。
在主游戏界面下,玩家可以选择恢复生命值、前往武器或防具店强化自身属性、出城打怪、保存游戏及退出游戏等功能,如下图所示:
武器、防具点及旅店与常规游戏一样,这里不再介绍,我们直接看出城即战斗界面:
如上图所示,出城后,我们可以查看自身状态,点击原处刷怪或继续前进则可以进入战斗状态,战斗过程在本游戏中直接跳过,在下方直接输出战斗结果,如下图所示:
战斗结束后可以选择是否继续前进,需要注意的是,为了区分难度,这里每一次继续前进遇到的怪物难度都会逐渐增加,直到遇到BOSS,而如果玩家在与怪物对战过程中失败,则游戏结束,如下图所示:
而如果不继续前进,则可以持续和当前难度水平怪物战斗提升等级,积攒金币,通过购买装备提升自己从而击败怪物,在这过程中,玩家可以随时查看自身状态,如下图所示:
本游戏中,与怪物战斗采用最简单的攻防相抵造成伤害。算法封装在Monster类中,代码如下:
void monster::battle(person &you)
{
while (you.HP_now > 0 && HP >0)
{
if(you.DEF+you.have[2] < ATK)
you.HP_now -= (ATK - you.DEF-you.have[2]);
if(you.ATK +you.have[1] > DEF)
HP -= (you.ATK +you.have[1]- DEF);
else
{
QMessageBox box;
box.setText("怪物防御力太高了,你打不动它,于是逃跑了!");
box.exec();
return ;
}
cout << "你的当前生命值为:" << you.HP_now << endl;
cout << "怪物的当前生命值为:" << HP << endl;
}
if (you.HP_now <= 0)
{
you.gameover();
}
if (HP<=0)
{
cout << "恭喜你击败了怪物" << endl;
cout << "你获得了金币,同时升级了" << endl;
you.LVUP(1);
you.show_stastus();
you.getmoney(money);
}
}
void monster::BOSS_battle(person& you)
{
int MAX=HP;
while (you.HP_now > 0 && HP > 0)
{
if(you.DEF+you.have[2] < ATK)
you.HP_now -= (ATK - you.DEF-you.have[2]);
if(you.ATK +you.have[1] > DEF)
HP -= (you.ATK +you.have[1]- DEF);
if (HP * 2 <= MAX)
ATK = ATK * 1.2;
if (HP * 10 <= MAX)
ATK = ATK * 1.25;
cout << "你的当前生命值为:" << you.HP_now << endl;
cout << "怪物的当前生命值为:" << HP << endl;
}
if (you.HP_now <= 0)
{
you.gameover();
}
if (HP <= 0)
{
you.LVUP(5);
you.show_stastus();
you.getmoney(money);
}
}
从上述代码中可以看到,游戏采用回合制,每次怪物对玩家造成的伤害=怪物的攻击力-玩家的防御力,而玩家对怪物造成的伤害同理,当有一方生命值先扣为0以下时,另一方获胜。而对于BOSS战,为了提升游戏难度,BOSS战中,Boss的战斗方式多了一个当BOSS血量降至一半以下时,BOSS造成的伤害提升,当血量降至10%以下时,BOSS攻击力再次提升。
在RPG游戏中,玩家的属性在各个游戏环节中均要用到,因此这一大属性的传递是游戏设计过程中的一大重点。
在本游戏中,玩家游戏属性通过一个类对象来存储,而该类对象在第一次开始游戏时初始化,在以后则使用该对象的值进行各游戏功能。在这个过程中,如何在各个窗体之间传递该类对象值则是重中之重。在本游戏中,玩家属性通过QVariant包装,然后通过信号触发SLOT槽函数,将该包装属性传递给下一子窗体,从而实现了类对象的不同窗体间的传递功能。
具体算法如下:
首先对类对象做一个宏定义
Q_DECLARE_METATYPE(person)
接着在窗口构造函数中创建子窗口对象的一个指针变量,然后通过connect函数链接
area =new area_prairie(this);
connect(this, SIGNAL(send_data(QVariant)), area, SLOT(setvalue(QVariant))); // send the message of player to area_prairie
然后在对应窗口跳转功能处发送信号
QVariant variance;
variance.setValue(Player);
emit send_data(variance);//发出信号
接着在子窗口处接受该variance包装,接受函数为connect函数参数内的SLOT函数(定义在子窗体)。
void area_prairie::setvalue(QVariant data)
{
person player ;
player = data.value();
}
如上便完成了类对象在不同窗体间的传递。而子窗体要将数据传回父窗体算法也大致相同,只是大部分定义需在父窗体定义,如connect函数如下:
connect(area, SIGNAL(send_data(QVariant)),this,SLOT(setvalue(QVariant)) ); //receive the message of player from area_prairie
send_data信号在子窗口定义,SLOT函数在父窗口定义,如法炮制即可。