首先贴出链接:
迷宫类中的自动寻路算法autoFindPath的实现
void Dungeon::initialMap()
{
floor=1;
MAX_HP=100;
isPre=false;
isNext=false;
x=11;
y=8;
initialrole();
int num=1;
while(1)
{
if(num==1)
{
int tempmap[12][16]={
15,1,1,3,3,3,1,1,1,1,1,1,1,1,1,1,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
0,8,1,1,1,3,17,1,17,1,1,22,2,5,0,1,
0,1,1,11,1,1,0,1,0,1,1,23,2,20,0,1,
0,0,0,17,0,0,0,1,0,0,0,0,17,0,0,1,
0,20,1,1,1,1,0,1,0,1,3,2,4,1,0,1,
0,20,1,11,1,1,0,1,17,3,2,4,10,1,0,1,
0,0,0,17,0,0,0,1,0,0,0,0,0,0,0,1,
0,1,1,2,1,1,0,1,1,1,1,1,1,1,1,1,
0,8,2,2,2,5,0,0,17,0,0,0,17,0,0,0,
0,8,2,2,2,5,0,20,2,2,0,1,1,4,1,0,
0,2,2,2,2,2,0,2,1,2,0,1,3,10,3,0
};
for(int i=0;i<12;i++)
{
for(int j=0;j<16;j++)
{
map[num-1][i][j]=tempmap[i][j];
}
}
}
if(num==2)
{
int tempmap[12][16]={
14,1,18,1,1,1,1,1,1,1,1,1,1,1,1,0,
1,1,0,0,0,0,19,4,20,4,21,0,0,0,0,0,
1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
1,0,20,20,5,5,0,1,1,1,0,12,5,2,2,0,
1,0,19,20,5,5,16,1,1,1,16,12,5,2,19,0,
1,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,
1,16,5,5,12,5,0,1,1,1,0,12,8,19,2,0,
1,0,5,5,12,19,0,1,1,1,0,12,9,20,22,0,
1,0,0,0,0,0,0,1,1,1,16,12,10,21,23,0,
1,0,19,2,2,2,0,1,1,1,0,0,0,0,0,0,
1,0,5,5,10,10,0,1,1,1,0,2,2,2,2,0,
15,0,5,5,10,12,16,1,1,1,16,2,2,2,2,0
};
for(int i=0;i<12;i++)
{
for(int j=0;j<16;j++)
{
map[num-1][i][j]=tempmap[i][j];
}
}
}
if(num==3)
{
int tempmap[12][16]={
20,1,1,22,0,20,1,1,20,0,1,0,1,1,2,1,
1,1,2,1,0,1,20,20,1,0,1,0,1,2,8,2,
1,2,8,2,0,1,1,21,1,0,1,17,4,4,2,1,
4,1,4,1,0,20,1,1,20,0,1,0,0,0,0,0,
0,0,17,0,0,0,1,1,0,0,1,0,1,1,1,1,
1,1,1,1,1,3,1,1,1,1,3,1,1,1,1,1,
0,0,17,0,0,1,1,1,1,0,1,0,0,0,0,0,
1,1,11,1,0,0,1,1,0,0,1,0,2,2,2,20,
1,4,3,20,0,1,1,1,1,0,1,17,4,4,3,8,
1,1,3,23,0,1,1,1,1,0,1,0,0,0,0,0,
0,0,0,0,0,0,1,1,0,0,3,0,1,1,1,1,
14,1,1,1,1,1,1,1,1,0,1,17,1,1,1,15,
};
for(int i=0;i<12;i++)
{
for(int j=0;j<16;j++)
{
map[num-1][i][j]=tempmap[i][j];
}
}
}
if(num==4)
{
int tempmap[12][16]={
1,21,1,1,0,1,31,32,33,1,0,1,1,1,1,1,
8,2,20,1,0,1,34,35,36,1,0,1,1,1,1,1,
1,3,1,1,0,1,1,1,1,1,0,1,20,1,10,1,
1,1,1,1,0,1,1,1,1,1,0,1,1,11,1,1,
0,0,17,0,0,0,0,18,0,0,0,0,17,0,0,0,
1,1,4,1,17,1,1,3,1,1,1,11,1,1,1,1,
1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,
3,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,
17,0,0,0,17,0,0,0,0,17,0,0,0,0,0,17,
1,0,1,20,4,1,0,1,1,4,1,1,1,1,0,1,
1,0,1,3,1,3,0,1,23,2,8,1,1,1,0,1,
15,0,20,1,20,1,0,1,1,3,1,1,1,1,0,14,
};
for(int i=0;i<12;i++)
{
for(int j=0;j<16;j++)
{
map[num-1][i][j]=tempmap[i][j];
}
}
}
if(num==5)
{
int tempmap[12][16]={
15,0,1,1,3,17,1,0,1,4,1,17,1,1,17,1,
1,0,1,1,1,0,20,0,4,2,4,0,1,1,0,1,
1,17,4,1,1,0,1,0,2,4,2,0,3,3,0,3,
0,0,0,0,17,0,3,0,2,2,2,0,20,20,0,1,
20,1,4,1,1,0,1,0,22,23,19,0,20,20,0,1,
20,1,1,1,4,0,4,0,0,0,0,0,0,0,0,1,
0,0,12,0,0,0,1,1,3,1,1,1,1,1,1,1,
1,1,12,1,1,0,3,0,0,0,0,0,0,0,0,3,
1,1,4,1,1,0,1,0,1,1,1,1,1,1,1,1,
22,23,20,8,10,0,1,0,1,1,1,0,0,0,0,0,
0,0,0,0,0,0,1,0,1,1,1,0,1,1,1,1,
14,1,1,1,1,1,1,0,1,1,1,16,1,1,1,24,
};
for(int i=0;i<12;i++)
{
for(int j=0;j<16;j++)
{
map[num-1][i][j]=tempmap[i][j];
}
}
}
if(num==6)
{
int tempmap[12][16]={
14,0,20,20,2,0,1,1,1,1,1,1,1,1,1,1,
1,0,20,20,2,0,1,0,0,0,0,0,0,0,0,17,
1,0,0,0,4,0,1,0,25,8,2,1,2,25,1,25,
1,17,17,1,1,17,1,0,5,1,2,25,1,2,1,1,
1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,
1,1,1,11,4,1,20,1,1,1,25,25,1,1,1,1,
0,0,0,0,0,0,1,0,0,0,0,0,0,17,0,1,
2,1,1,4,8,0,1,0,10,2,0,0,1,4,0,1,
2,1,11,23,12,0,1,0,9,2,0,0,5,1,0,1,
4,2,1,4,1,0,1,0,8,2,26,2,4,2,0,1,
17,0,0,0,0,0,1,0,0,0,0,1,2,1,0,1,
1,1,3,1,12,1,1,0,1,1,1,19,1,1,0,15,
};
for(int i=0;i<12;i++)
{
for(int j=0;j<16;j++)
{
map[num-1][i][j]=tempmap[i][j];
}
}
}
if(num==7)
{
int tempmap[12][16]={
15,0,27,29,0,22,0,1,1,1,0,10,0,20,0,3,
1,0,8,27,0,23,0,1,1,1,0,4,0,20,0,3,
1,0,8,27,0,4,0,3,0,26,0,3,0,8,0,3,
1,0,2,8,0,2,0,1,0,1,0,1,0,2,0,1,
1,0,2,10,0,1,0,1,0,1,0,1,0,1,0,1,
17,0,25,25,0,17,0,18,0,17,0,17,0,25,0,17,
1,25,1,1,12,1,1,1,1,1,1,1,1,1,1,1,
17,0,25,25,0,17,0,17,0,17,0,17,0,25,0,17,
1,0,2,2,0,1,0,1,0,1,0,1,0,1,0,1,
3,0,8,10,0,2,0,12,0,4,0,3,0,10,0,1,
3,0,8,27,0,2,0,22,0,20,0,4,0,20,0,1,
3,0,8,28,0,2,0,23,0,20,0,10,0,20,0,14,
};
for(int i=0;i<12;i++)
{
for(int j=0;j<16;j++)
{
map[num-1][i][j]=tempmap[i][j];
}
}
}
num++;
if(num>Total_Floor)
{
break;
}
}
}
地牢中每层的地图都不相同,而且每层地图中还有许多游戏角色(物品、怪物、金币等)。而且每层地牢之间是连通的,可以去往下一层地牢,也可以回到上一层地牢,而玩家在地牢中对每层地牢造成的影响都必须保存着,为了实现地牢的这些效果,将地牢抽象为一个3维数组。数组的第3维代表地牢的层数,第1维和第2维表示每层的地牢地图。每层地牢就可以通过第3个维度进行联系,而且每层的地图可以设置为不相同,并且玩家对地牢造成的影响都可以储存在这个3维数组当中。
在实现地牢中每层地图的过程当中,为了方便加载地图资源,将二维数组中的元素与地图贴图资源的名称相对应,比如0代表墙,1代表路,2代表金币,3代表小星星,15代表下一层入口,20代表黄钥匙等等。
地牢中的英雄与怪物都有自身的属性,如生命值、攻击力、防御力等等,将他们都抽象为一个结构体,结构体中存储英雄与怪物的自身属性,修改起来也较为方便。
#ifndef STOREWIDGET_H
#define STOREWIDGET_H
#include
#include
#include
#include
#include
#include
class StoreWidget : public QWidget
{
Q_OBJECT
public:
explicit StoreWidget(QWidget *parent = 0);
int chooseOption; //选择了第几项
int BuyTimes;
QTimer *haveBuyTimer;//购买计时器
bool setChooseEnable;//是否可以购买
bool StoreWinisShow;//商店窗口是否显示
int Currstore_price;//当前价格
int Nextstore_price;//下一次价格
private:
int OptionNum; //有多少个选项
int border_color_it;
QString OptionBoxColor[6]= {"FFFFFF", "CCCCCC", "999999", "666666", "999999", "CCCCCC"};
QTimer *OptionBoxTimer;
QGraphicsView *OptionBox;
QWidget *StoreWin;
QLabel *text1;
QLabel *text2;
QLabel *hpOption;
QLabel *mpOption;
QLabel *atkOption;
QLabel *defOption;
QLabel *exitOption;
private:
void initialStoreWin();//初始化商店界面
void keyPressEvent(QKeyEvent *event);//当前类中的键盘事件与mainwindow中的键盘事件互不影响
void showEvent(QShowEvent *);//重写窗口显示事件,为了使当前窗口获得焦点
public slots:
void OptionBoxborderChanged();
};
#endif // STOREWIDGET_H
void StoreWidget::keyPressEvent(QKeyEvent *event)
{
if(event->key() == Qt::Key_W)
{ //向上
if (chooseOption > 0)
{
chooseOption -= 1;
OptionBox->setGeometry(32, 112 + 48 * chooseOption, 192, 32);
}
else
chooseOption = 0;
}
else if (event->key() == Qt::Key_S)
{ //向下
if (chooseOption <= OptionNum - 2)
{
chooseOption += 1;
OptionBox->setGeometry(32, 112 + 48 * chooseOption, 192, 32);
}
else
chooseOption = OptionNum - 1;
}
else if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter || event->key() == Qt::Key_Space)
{
if (chooseOption != OptionNum - 1)//不是选择的离开商店选项
{
Currstore_price=((BuyTimes) * (BuyTimes) - (BuyTimes) + 2) * 10;
Nextstore_price = ((BuyTimes +1 ) * (BuyTimes +1 ) - (BuyTimes +1 ) + 2) * 10;
haveBuyTimer->start(100);//开启购买(计时器开启后不是马上执行,而是在当前函数执行完毕后才开始执行,
//所以,如果在执行计时器后才改变setChooseEnable的值,那么在
//当前函数体内,setChooseEnable的值就还不会改变
if(!setChooseEnable)//金币不够,则不能购买
{
return;
}
text1->setText(QString::fromStdWString(L"你若给我 ") +
QString::number(Nextstore_price) + QString::fromStdWString(L" 个金币"));
text2->setText(QString::fromStdWString(L"我就可以帮你"));
hpOption->setText(QString::fromStdWString(L"提升 ") +
QString::number(100 * (BuyTimes +1 )) + QString::fromStdWString(L" 点生命"));
mpOption->setText(QString::fromStdWString(L"提升 ") +
QString::number(20 * (BuyTimes +1 )) + QString::fromStdWString(L" 点魔法"));
BuyTimes++;
return;
}
else
{
this->hide();
StoreWinisShow=false;
chooseOption=0;//选项框回到起始位置
OptionBox->setGeometry(32,112,192,32);
return;
}
}
}
主界面的实现
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include //QResizeEvent 是主窗口的变化事件
#include
#include
#include //计时器 QTime 只是用来产生随机数的
#include //视频、音乐类
#include
#include
#include
#include "maze.h"
#include "Dungeon.h"
#include "interface.h"
#include "storewidget.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
/********************** 主界面 ***********************/
private:
int BASIC_WIDTH;//地图窗口宽度
int BASIC_HEIGHT;//地图窗口高度
QPoint windowPos;//窗口位置
QPoint mousePos;//鼠标位置
QPoint dPos;//鼠标移动后的位置
private:
interface *surface;//开始界面
QWidget *MainWidget;//中心窗口
QWidget *MazeWidget[60][60];//地图窗口数组
QWidget *People;//角色
QString character[12];//角色图片字符串
QGridLayout *gLayout_Map;//地图网格布局管理器
QHBoxLayout *hLayout;//主界面水平布局管理器
private:
void initialWindow_Layout();//初始化主窗口
void keyPressEvent(QKeyEvent *event);//键盘事件(用于操作)
void mousePressEvent(QMouseEvent *event);//鼠标按下事件(用于移动主界面)
void mouseMoveEvent(QMouseEvent *event);//鼠标移动事件
private slots:
void ShowWidget();//显示界面
/********************** 迷宫模式 ***********************/
private:
int iNum;//自动寻路显示路径辅助变量
int lastheight;//上一次设置的迷宫高度
int lastwidth;//上一次设置的迷宫宫宽度
int StytleNum;//地图风格数字
bool isPlay;//键盘操作标识(用于限制操作时自动寻路和AI)
bool isShow;//显示地图标识(用于刷新迷宫地图时销毁上次生成的窗口)
bool issurface;//开始界面标识(用于标识开始界面已加载完成)
bool isAIAnimationButton;//AI移动标识
bool isAutoMoveButton;//自动寻路标识
private:
maze m;//迷宫类
QWidget *Controlwidget;//控制界面
QGridLayout *gLayout_Control;//控制界面网格布局
QSpinBox *sp_w;//迷宫大小输入
QSpinBox *sp_h;
QLabel *label_Stytle;//地图风格显示
QComboBox *SelectMapStytle;//地图风格选择
QPushButton *AIAnimationButton;//AI操作
QPushButton *AutoMoveButton;//自动寻路
QPushButton *quitButton;//返回主菜单按钮
QSequentialAnimationGroup *group;//AI操作动画组
QTimer *timer;//定时器
private:
void initialControlWidget();//初始化控制界面
void ShowMaze_Layout();//显示迷宫地图
void hideMaze();//隐藏迷宫地图
void resetMaze();//重置迷宫
private slots:
void moveCharacter();//AI移动时人物图片切换
void CreateMaze_Layout();//创建迷宫
void ShowPath();//自动寻路显示路径
void ShowAnimation();//显示动画
void timeStart();//定时器开启
void MapStytleSet();//设置地图风格
void quit();//返回主菜单
/********************** 地牢模式 ***********************/
private:
Dungeon d;//地牢类
QWidget *infoWidget;//状态显示界面
int moveDirection;//移动方向
int moveNum;
QLabel *Floor;//地牢层数
QLabel *level;//人物状态信息
QLabel *exp;
QLabel *hp;
QLabel *mp;
QLabel *atk;
QLabel *def;
QLabel *score;//金币
QLabel *yellowkeyNum;//钥匙信息
QLabel *purplekeyNum;
QLabel *redkeyNum;
QLabel *CharacterName;
private:
void initialinfoWidget();//初始化状态界面
void initialGetitemWin();//初始化获得物品信息界面
void initialFightWin();//初始化战斗界面
void initialSetNameWin();//初始化设置角色名界面
void ShowDungeon();//显示地牢
void hideDungeon();//隐藏地牢
void updateStatusData();//更新状态数据
void Move();//人物移动
void changeHP(int num);
void QuestionBox(int num);//问号箱
void items(int _itemNum, int _moveDirection);//物品
void doors(int _doorNum, int _moveDirection);
void Monsters(int _MonsterNum,int _moveDirection);
void checkPrefloor();//上一层
void checkNextfloor();//下一层
bool isDynamic(int i,int j);
void soundPlay(int CurrentIndex);
private slots:
void GetitemWinshow();//获得物品信息界面显示
void FightWinshow();//战斗界面显示
void ShowdynamicEffect();//展示动态效果
void OpenStore();//打开商店
void ReturnMainMenuPlay();//返回主菜单
void RestartPlay();//重新开始
void soundStop();//音效停止
void CheckBGMstate();//检查音乐状态
void EnterDungeon();//角色名设置完成,进入地牢
private://获得物品信息栏
QLabel *GetitemsShow;
QTimer *GetitemTimer;
int GetitemNum;
int itemNum;
private://设置角色名界面
QLineEdit *NameEdit;
QPushButton *SetDone;
private://战斗界面
QWidget *FightWidget;
QTimer *FightTimer;
bool isFighting;
QLabel *Monster_pic;//怪物信息标签
QLabel *Monster_name;
QLabel *Monster_hpText;
QLabel *Monster_hp;
QLabel *Monster_atkText;
QLabel *Monster_atk;
QLabel *Monster_defText;
QLabel *Monster_def;
QLabel *Character_pic;//人物信息标签
QLabel *Character_name;
QLabel *Character_hpText;
QLabel *Character_hp;
QLabel *Character_atkText;
QLabel *Character_atk;
QLabel *Character_defText;
QLabel *Character_def;
int MonsterNum;//怪物编号
int fight_period_it;//战斗次数
int fight_end_it;//战斗结束标识
private://商店界面
StoreWidget *Store;
private://按钮
QPushButton *ReturnMainMenu;
QPushButton *Restart;
private://动态效果
QTimer *dynamicEffectTimer;//动态效果计时器
int display_it;
private://音效
QMediaPlayer *BGM;
QTimer *BGMTimer;
QMediaPlaylist *soundlist;
QMediaPlayer *sound;
QTimer *soundTimer;
};
#endif // MAINWINDOW_H
主界面中的窗口结构与使用的布局管理器结构图以在上图给出。当主窗口构造分配内存时,这些窗口也会被分配内存。在实际的游戏执行过程中,会将除主窗口外的所有其它窗口先隐藏起来,即调用窗口的hide函数,当游戏打开时,最先显示的是开始界面窗口,玩家点击不同的接口按钮后,根据玩家选择的模式按钮,显示对应模式下的窗口,同时隐藏开始界面窗口。
玩家选择迷宫模式后,在进度条跑满后,会触发绘图事件,开始界面的背景图片重新绘制为迷宫模式的窗口图片,当玩家输入好迷宫大小点击生成迷宫按钮后,开始界面窗口会隐藏起来,同时地图窗口与迷宫控制界面窗口和主人物窗口会显示,这样就是实现了玩家控制窗口切换的效果。
同样的,玩家选择地牢模式后,在进度条跑满后,会触发绘图事件,开始界面的背景图片重新绘制为地牢模式的窗口图片,当玩家输入角色名后,开始界面的窗口会隐藏,同时地图窗口与地牢人物状态显示窗口和主人物窗口会显示出来。当玩家在地牢中碰到怪物后,地牢战斗窗口也会显示出来,战斗完成后隐藏起来。当玩家进入商店,商店界面窗口也会显示出来,玩家购买完成,点击离开商店选项后,商店界面就会隐藏起来,这样就实现了玩家与游戏之间的交互。
每种游戏模式下都有返回主界面按钮,因为在游戏设计时隐藏了主界面窗口的边框,只有通过开始界面的“离开游戏”选项才能退出游戏,在玩家点击退出游戏后,会有提示对话框,玩家必须点击“确认”按钮才会真的突出游戏,这种设计是为了防止玩家误点而关闭游戏。
迷宫模式下地图风格切换的实现
游戏的BGM以及地牢模式中的音效实现