Qt编写魔塔小游戏

目录

一.游戏大致内容

        1.玩家角色

        2.怪物

        3.可触发的物品

        4.不可触发的物品

        5.地图

        6.(玩家与怪物的技能)

二.实现逻辑

三.具体实现

1.角色类声明

2.怪物类声明

3.地图类声明

4.主界面声明

5.玩家角色的构造

6.怪物的构造

7.地图的构造

8.主界面构造

9.开始按钮的槽函数与绘图事件

10.键盘事件处理

11.初始化地图界面

12.刷新地图界面

13.角色移动处理

14.运算战斗结果

15.边栏角色信息

16.主函数

四.注意事项

        1.头文件

        2.图片与音乐文件

        3.项目整体

五.游戏效果

        1.开始界面(简陋)

        2.第一层(新手大剑)

         3.第二层(开始正常游戏)

        4.后续层

        5.奖励层

        6.最后一层(暂时还没写结束结算)


最近系统学习了Qt的程序界面开发,所以用Qt尝试写了一个简单的魔塔小游戏。

下述括号内内容暂未编写

一.游戏大致内容

        1.玩家角色

                玩家角色拥有:角色名字,血量,攻击,防御,金钱,钥匙,剑,盾,(技能,经验,等级)

                通过玩家角色保存层数

        2.怪物

                怪物拥有:怪物名字,血量,攻击,防御,金钱,(技能,经验,等级)

                怪物名字不方便在代码中使用,所以给怪物添加一个“类型”变量

        3.可触发的物品

                可与玩家互动的物品

                如:血瓶,(蓝瓶),钥匙,宝石,剑,盾,门等

        4.不可触发的物品

                地面,墙等

        5.地图

                初始地图与当前地图

        6.(玩家与怪物的技能)

                (技能拥有等级,加成,蓝耗等属性)

       

二.实现逻辑

        除去暂时不想写的内容,将玩家角色写为一个类,怪物一个类,地图一个类,主界面一个类

        玩家的活动可以写在玩家类中,也可以写在主界面类中——更方便刷新主界面;玩家应有移动,战斗等事件;主界面应有初始化,刷新界面,接收键盘按键事件等功能

        使用Qt中的QLable控件装载地图的各种图标,用QPainter绘制游戏背景,用QMediaPlayer播放游戏背景音乐

        地图用三维数组存储,每一张地图都是一个二维数组,则会将地图分割成网格状,通过多个QLable控件拼接成整张地图界面

三.具体实现

        所有类的成员变量都写成了公有成员,方便在类外部通过该类对象使用这些变量

1.角色类声明

class Hero
{
public:
    Hero(QString);
    ~Hero();
    int blood;          //血量
    int attack;         //攻击力
    int defense;        //防御力
    int money;          //金钱
    int floor;          //楼层
    int b_key;          //蓝钥匙
    int y_key;          //黄钥匙
    bool sword;         //持剑标志
    bool shield;        //持盾标志
    QString name;
};

2.怪物类声明

class Monster
{
public:
    Monster(int);
    ~Monster();
    QString name;
    int blood;			//生命值
    int attack;			//攻击力
    int defense;		//防御力
    int money;          //金钱
    int type;			//类型    8-蓝史莱姆  9-黄史莱姆  10-蝙蝠  11-骷髅 12-法师
};

3.地图类声明

        三维数组若声明未赋值则后续不方便赋值,需使用三重for循环赋值,不方便脑补绘制地图,所以在类中声明时就为其赋值

class Map
{
public:
    Map();
    //0-墙1-地板 2-蓝门 3-黄门 4-蓝钥匙 5-黄钥匙 6-小血瓶 7-大血瓶
    //8-蓝史雷姆 9-黄史雷姆 10-蝙蝠 11-骷髅 12-法师 13-红宝石 14-蓝宝石
    //15-英雄 16-上楼 17-下楼 18-剑 19-盾
/* //空地图,用于复制
    {0,0,0,0,0,0,0,0,0,0,0,0,0},
    {0,1,1,1,1,1,1,1,1,1,1,1,0},
    {0,1,1,1,1,1,1,1,1,1,1,1,0},
    {0,1,1,1,1,1,1,1,1,1,1,1,0},
    {0,1,1,1,1,1,1,1,1,1,1,1,0},
    {0,1,1,1,1,1,1,1,1,1,1,1,0},
    {0,1,1,1,1,1,1,1,1,1,1,1,0},
    {0,1,1,1,1,1,1,1,1,1,1,1,0},
    {0,1,1,1,1,1,1,1,1,1,1,1,0},
    {0,1,1,1,1,1,1,1,1,1,1,1,0},
    {0,1,1,1,1,1,1,1,1,1,1,1,0},
    {0,1,1,1,1,1,1,1,1,1,1,1,0},
    {0,0,0,0,0,0,0,0,0,0,0,0,0}
*/
    int map[TOWER_LAYER][ONCE_WIDTH][ONCE_HEIGHT]={
        {
         {0,0,0,0,0,0,0,0,0,0,0,0,0},
         {0,16,5,1,1,1,1,1,1,1,1,14,0},
         {0,5,1,1,1,1,1,1,1,1,1,1,0},
         {0,1,1,1,1,1,1,1,1,1,1,1,0},
         {0,1,1,1,1,1,1,1,1,1,1,1,0},
         {0,1,1,1,1,1,1,1,1,1,1,1,0},
         {0,1,1,1,1,1,18,1,1,1,1,1,0},
         {0,1,1,1,1,1,1,1,1,1,1,1,0},
         {0,1,1,1,1,1,1,1,1,1,1,1,0},
         {0,1,1,1,1,1,1,1,1,1,1,1,0},
         {0,1,1,1,1,1,1,1,1,1,1,1,0},
         {0,6,1,1,1,1,1,1,1,1,1,15,0},
         {0,0,0,0,0,0,0,0,0,0,0,0,0}
        },
        {
          {0,0,0,0,0,0,0,0,0,0,0,0,0},
          {0,16,1,9,8,8,1,1,1,1,1,1,0},
          {0,0,0,0,0,0,0,0,0,0,0,1,0},
          {0,5,1,1,3,1,0,1,5,1,0,1,0},
          {0,1,11,1,0,1,0,14,6,1,0,1,0},
          {0,0,3,0,0,1,0,0,0,3,0,1,0},
          {0,5,1,1,0,1,3,10,11,10,0,1,0},
          {0,1,11,1,0,1,0,0,0,0,0,1,0},
          {0,0,3,0,0,1,1,1,1,1,1,1,0},
          {0,1,1,1,0,0,3,0,0,0,3,0,0},
          {0,6,1,5,0,1,1,1,0,1,10,1,0},
          {0,6,13,5,0,17,15,1,0,9,7,8,0},
          {0,0,0,0,0,0,0,0,0,0,0,0,0}
        },
        {
            {0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,5,14,0,5,7,5,0,1,0,1,6,0},
            {0,1,6,0,7,5,7,0,1,3,10,1,0},
            {0,12,1,0,5,4,5,0,1,0,0,0,0},
            {0,3,0,0,0,1,0,0,1,0,1,12,0},
            {0,1,1,10,1,1,1,9,1,1,1,1,0},
            {0,3,0,0,1,1,1,0,1,0,0,0,0},
            {0,11,1,0,0,1,0,0,1,0,1,6,0},
            {0,1,5,0,1,1,1,0,1,3,12,5,0},
            {0,6,6,0,1,1,1,0,1,0,0,0,0},
            {0,0,0,0,0,1,0,0,9,0,1,1,0},
            {0,17,15,1,1,1,1,0,1,3,1,16,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0}
        },
        {
            {0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,1,5,1,0,1,1,1,0,1,12,1,0},
            {0,6,13,4,0,1,12,1,0,5,1,7,0},
            {0,1,1,1,0,1,1,1,0,1,11,1,0},
            {0,0,3,0,0,0,2,0,0,0,3,0,0},
            {0,1,12,1,3,1,1,1,1,1,1,1,0},
            {0,1,1,1,0,0,0,0,0,0,0,0,0},
            {0,9,1,8,1,1,1,1,1,1,1,1,0},
            {0,3,0,0,3,0,0,0,3,0,0,3,0},
            {0,1,0,9,10,5,0,1,12,1,0,1,0},
            {0,1,0,5,1,5,0,9,6,9,0,15,0},
            {0,16,0,8,8,5,0,1,9,8,0,17,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0}
        },
        {
            {0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,17,1,2,1,1,1,1,1,1,1,1,0},
            {0,15,1,0,1,1,12,1,12,1,1,1,0},
            {0,1,0,0,0,0,0,1,0,0,0,0,0},
            {0,1,0,5,5,0,1,1,1,0,5,12,0},
            {0,1,0,5,5,3,1,1,1,3,1,5,0},
            {0,1,0,0,0,0,1,1,1,0,0,0,0},
            {0,1,0,12,11,0,1,1,1,0,5,7,0},
            {0,1,0,1,1,3,1,1,1,3,1,5,0},
            {0,1,0,0,0,0,1,1,1,0,0,0,0},
            {0,1,0,7,7,0,1,1,1,0,12,19,0},
            {0,16,0,7,14,3,1,1,1,3,12,12,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0}
        },
        {
            {0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,16,0,1,10,3,1,0,1,1,3,1,0},
            {0,1,0,1,1,0,5,0,11,12,0,2,0},
            {0,1,3,11,1,0,1,0,5,5,0,1,0},
            {0,0,0,0,3,0,10,0,5,5,0,1,0},
            {0,5,5,12,12,0,1,0,0,0,0,1,0},
            {0,5,1,1,11,0,1,12,1,1,1,1,0},
            {0,0,11,0,0,0,1,0,0,0,0,12,0},
            {0,1,12,1,1,0,10,0,1,1,1,1,0},
            {0,13,5,7,5,0,1,0,1,0,0,0,0},
            {0,0,0,0,0,0,5,0,1,0,7,14,0},
            {0,17,15,1,1,1,1,0,1,3,1,1,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0}
        },
        {
            {0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,17,15,7,7,7,7,7,7,7,7,7,0},
            {0,1,1,14,14,14,14,14,14,14,14,14,0},
            {0,1,1,13,13,13,13,13,13,13,13,13,0},
            {0,1,1,1,1,1,1,1,1,1,1,1,0},
            {0,1,1,1,1,1,1,1,1,1,1,1,0},
            {0,1,1,1,1,1,1,1,1,1,1,1,0},
            {0,1,1,1,1,1,1,1,1,1,1,1,0},
            {0,1,1,1,1,1,1,1,1,1,1,1,0},
            {0,1,1,1,1,1,1,1,1,1,1,1,0},
            {0,1,1,1,1,1,1,1,1,1,1,1,0},
            {0,1,1,1,1,1,1,1,1,1,1,16,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0}
        },
        {
            {0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,17,15,1,1,1,1,1,1,1,1,1,0},
            {0,1,1,1,1,1,1,1,1,1,1,1,0},
            {0,1,1,1,1,1,1,1,1,1,1,1,0},
            {0,1,1,1,1,1,1,1,1,1,1,1,0},
            {0,1,1,1,1,1,1,1,1,1,1,1,0},
            {0,1,1,1,1,1,1,12,12,12,12,12,0},
            {0,1,1,1,1,1,1,12,12,12,12,12,0},
            {0,1,1,1,1,1,1,12,12,12,12,12,0},
            {0,1,1,1,1,1,1,12,12,12,12,12,0},
            {0,1,1,1,1,1,1,12,12,12,12,12,0},
            {0,1,1,1,1,1,1,12,12,12,12,12,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0}
        }
    };
};

4.主界面声明

class Widget : public QWidget
{
    Q_OBJECT
public slots:
    void startGame();                         //开始按钮所实现的自定义槽函数
public:
    Widget(QWidget *parent = 0);
    ~Widget();
    void keyPressEvent(QKeyEvent *event);     //键盘事件重写,上下左右与WASD键的改写
    void paintEvent(QPaintEvent *event);      //绘图事件重写,素材背景为白板,需要绘制背景
    void initMap();                           //初始化游戏地图函数
    void updateMap(int type,int preValue=0);  //刷新游戏地图函数
    void heroMove(int type,int preValue);     //角色移动判断函数
    void heroCombat(int preValue);            //角色战斗判断函数
    void showHeroInfo();                      //显示角色信息函数
private:
    QPushButton *start_bt;        //开始按钮
    bool combate_signal;          //角色战斗标志
    bool restart_signal;          //死亡重开标志
    bool start_background_signal; //更新背景图标志
    QMediaPlayer *bgm;            //播放背景音乐
    QMediaPlaylist *bgmlist;      //背景音乐列表,用于循环播放
    QLabel *mapLabel[13][13];     //当前地图
    QLabel *infoLabel;            //角色信息背景
    QLabel *nameLabel;            //角色名字
    QLabel *moneyValueLabel;      //金币
    QLabel *floorValueLabel;      //楼层
    QLabel *bloodLabel;           //生命值
    QLabel *attackLabel;          //攻击力
    QLabel *defenseLabel;         //防御力
    QLabel *bKeyLabel;            //蓝钥匙
    QLabel *yKeyLabel;            //黄钥匙
    Map *dynamic_map=new Map;     //当前游戏的地图
    Map *static_map=new Map;      //不会去更改的地图,其实也可以用Map::map来调用
    Hero *hero;                   //声明角色
    int heroX;                    //角色的模拟X坐标
    int heroY;                    //角色的模拟Y坐标
    //声明定义怪物
    Monster *blueSlime=new Monster(8);
    Monster *yellowSlime=new Monster(9);
    Monster *smallBat=new Monster(10);
    Monster *skeletonKnight=new Monster(11);
    Monster *exorcist=new Monster(12);
};

5.玩家角色的构造

Hero::Hero(QString name_):name(name_)
{
    //根据传入角色名生成指定角色
    if(name.compare("英雄")){
        blood=200;
        attack=20;
        defense=15;
    }
    else if(name.compare("勇者")){
        blood=100;
        attack=16;
        defense=10;
    }
    else if(name.compare("村民")){
        blood=60;
        attack=10;
        defense=8;
    }
    else {
        blood=40;
        attack=10;
        defense=5;
    }
    money=0;
    floor=0;
    b_key=0;
    y_key=0;
}

6.怪物的构造

Monster::Monster(int type){
    this->type=type;
    //根据传入类型生成指定小怪
    switch (type){
    case 8:
        blood=30;
        attack=12;
        defense=5;
        money=2;
        name="黄史莱姆";
        break;
    case 9:
        blood=50;
        attack=13;
        defense=5;
        money=3;
        name="蓝史莱姆";
        break;
    case 10:
        blood=50;
        attack=15;
        defense=8;
        money=5;
        name="蝙蝠";
        break;
    case 11:
        blood=100;
        attack=18;
        defense=12;
        money=20;
        name="骷髅";
        break;
    case 12:
        blood=200;
        attack=26;
        defense=10;
        money=30;
        name="法师";
        break;
    default:
        break;
    }
}

7.地图的构造

        因为地图数组在声明时就赋值了,所以不需要重写地图的构造函数

8.主界面构造

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    this->grabKeyboard();//设置接收键盘事件
    this->setFixedSize((ONCE_WIDTH+1.5)*60,ONCE_HEIGHT*60);
    //背景音乐
    bgm=new QMediaPlayer();
    bgmlist=new QMediaPlaylist();//用QMediaPlaylist装入背景音乐,使其能循环播放
    bgmlist->addMedia(QUrl("qrc:/bgm/morning.mp3"));
    bgmlist->setPlaybackMode(QMediaPlaylist::Loop);
    bgm->setPlaylist(bgmlist);
    bgm->setVolume(30);//设置音量大小
    bgm->play();
    //开始按钮
    start_bt=new QPushButton("start game");
    start_bt->setFixedSize(140,50);
    start_bt->setFont(QFont("KaiTi",16));
    start_bt->setStyleSheet("QPushButton{ color:rgb(255,255,255);}"
                            "QPushButton{ selection-color:rgb(181,181,181);}"
                            "QPushButton{ border-radius:18px;}"
                            "QPushButton{ background-color:rgb(0,128,255);}");
    start_bt->move(this->width()/2-70,this->height()/2-25);
    start_bt->setParent(this);
    //初始化各个标志
    restart_signal=false;
    combate_signal=false;
    start_background_signal=false;
    //定义角色并初始化其内容
    hero=new Hero("英雄");
    heroX=0;
    heroY=0;
    hero->sword=false;
    hero->shield=false;
    //for循环初始化地图lable
    for(int i=0;i<ONCE_WIDTH;i++){
        for(int j=0;j<ONCE_HEIGHT;j++){
            mapLabel[i][j]=new QLabel(this);
            mapLabel[i][j]->setFixedSize(60,60);
        }
    }
    connect(start_bt,SIGNAL(clicked(bool)),this,SLOT(startGame()));
}

9.开始按钮的槽函数与绘图事件

//开始按钮点击后隐藏按钮,跟新地图,立即刷新绘图事件,显示角色信息
void Widget::startGame(){
    start_background_signal=true;
    start_bt->hide();
    repaint();
    initMap();
    showHeroInfo();
}
//绘图事件,绘制窗口背景图
void Widget::paintEvent(QPaintEvent *event){
    QPainter p(this);
    event->accept();
    //进入游戏后背景改为全游戏地面背景
    if(start_background_signal==true){
        for(int i=0;i<ONCE_HEIGHT;i++){
            for(int j=0;j<ONCE_WIDTH+1;j++){
                p.drawPixmap(j*60,i*60,60,60,QPixmap(":/image/1.png"));
            }
        }
    }
    //未进入游戏设置开始背景
    else{
        p.drawPixmap(0,0,60*(ONCE_WIDTH+1.5),60*ONCE_HEIGHT,QPixmap(":/image/back.jpeg"));
    }
}

10.键盘事件处理

void Widget::keyPressEvent(QKeyEvent *event){
    if(!combate_signal){
        int type=4;             //记录移动类型(0-上 1-下 2-左 3-右)
        int preValue=0;         //记录走到的位置有什么东西
        switch (event->key()){
        case Qt::Key_Up:
        case Qt::Key_W:
            type=0;
            preValue=dynamic_map->map[hero->floor][heroX-1][heroY];
            heroMove(type,preValue);
            break;
        case Qt::Key_Down:
        case Qt::Key_S:
            type=1;
            preValue=dynamic_map->map[hero->floor][heroX+1][heroY];
            heroMove(type,preValue);
            break;
        case Qt::Key_Left:
        case Qt::Key_A:
            type=2;
            preValue=dynamic_map->map[hero->floor][heroX][heroY-1];
            heroMove(type,preValue);
            break;
        case Qt::Key_Right:
        case Qt::Key_D:
            type = 3;
            preValue=dynamic_map->map[hero->floor][heroX][heroY+1];
            heroMove(type,preValue);
            break;
        default:
            break;
        }
        showHeroInfo();//刷新角色信息的显示
    }
}

11.初始化地图界面

void Widget::initMap(){
    if(restart_signal){
        //将不变的地图赋予可变地图
        for(int k=0;k<TOWER_LAYER;k++){
            for(int i=0;i<ONCE_WIDTH;i++){
                for(int j=0;j<ONCE_HEIGHT;j++){
                    dynamic_map->map[k][i][j]=static_map->map[k][i][j];
                }
            }
        }
        //判断角色死亡,是否重新开始。
        restart_signal=false;
    }
    //绘制当前地图
    for(int i=0;i<ONCE_HEIGHT;i++){
        for(int j=0;j<ONCE_WIDTH;j++){
            //每次更新一层,需要刷新当前层的图片
            mapLabel[i][j]->clear();
            mapLabel[i][j]->move(j*60,i*60);
            mapLabel[i][j]->setPixmap(QPixmap(tr(":/image/%1.png").arg(dynamic_map->map[hero->floor][i][j])));
            mapLabel[i][j]->setScaledContents(true);
            mapLabel[i][j]->show();
            //检索角色位置
            if(dynamic_map->map[hero->floor][i][j]==15){
                heroX=i;
                heroY=j;
            }
        }
    }
    //计分板背景
    infoLabel=new QLabel(this);
    infoLabel->setFixedSize(60*1.5,ONCE_HEIGHT*60);
    infoLabel->setPixmap(QPixmap(tr(":/image/20.jpeg")));
    infoLabel->setScaledContents(true);
    infoLabel->move(ONCE_WIDTH*60,0);
    infoLabel->show();
    //设置字体
    QPalette palette;
    palette.setColor(QPalette::WindowText,Qt::black);
    QFont font;
    font.setPointSize(12);
    font.setBold(true);
    //角色名字
    nameLabel=new QLabel(this);
    nameLabel->setFixedSize(60*1.5,50);
    nameLabel->move(ONCE_WIDTH*60,0);
    nameLabel->setPalette(palette);
    nameLabel->setFont(font);
    nameLabel->show();
    //金币显示
    moneyValueLabel=new QLabel(this);
    moneyValueLabel->setFixedSize(60*1.5,50);
    moneyValueLabel->move(ONCE_WIDTH*60,60*1.5);
    moneyValueLabel->setPalette(palette);
    moneyValueLabel->setFont(font);
    moneyValueLabel->show();
    //楼层显示
    floorValueLabel=new QLabel(this);
    floorValueLabel->setFixedSize(60*1.5,50);
    floorValueLabel->move(ONCE_WIDTH*60,60*3);
    floorValueLabel->setPalette(palette);
    floorValueLabel->setFont(font);
    floorValueLabel->show();
    //生命值
    bloodLabel=new QLabel(this);
    bloodLabel->setFixedSize(60*1.5,50);
    bloodLabel->move(ONCE_WIDTH*60,60*4.5);
    bloodLabel->setPalette(palette);
    bloodLabel->setFont(font);
    bloodLabel->show();
    //攻击力
    attackLabel=new QLabel(this);
    attackLabel->setFixedSize(60*1.5,50);
    attackLabel->move(ONCE_WIDTH*60,60*6);
    attackLabel->setPalette(palette);
    attackLabel->setFont(font);
    attackLabel->show();
    //防御力
    defenseLabel=new QLabel(this);
    defenseLabel->setFixedSize(60*1.5,50);
    defenseLabel->move(ONCE_WIDTH*60,60*7.5);
    defenseLabel->setPalette(palette);
    defenseLabel->setFont(font);
    defenseLabel->show();
    //黄钥匙
    yKeyLabel=new QLabel(this);
    yKeyLabel->setFixedSize(60*1.5,50);
    yKeyLabel->move(ONCE_WIDTH*60,60*9);
    yKeyLabel->setPalette(palette);
    yKeyLabel->setFont(font);
    yKeyLabel->show();
    //蓝钥匙
    bKeyLabel=new QLabel(this);
    bKeyLabel->setFixedSize(60*1.5,50);
    bKeyLabel->move(ONCE_WIDTH*60,60*10.5);
    bKeyLabel->setPalette(palette);
    bKeyLabel->setFont(font);
    bKeyLabel->show();
}

12.刷新地图界面

void Widget::updateMap(int type,int preValue){
    dynamic_map->map[hero->floor][heroX][heroY]=1;//先前角色所在地置为地面
    mapLabel[heroX][heroY]->setPixmap(QPixmap(tr(":/image/%1.png").arg(dynamic_map->map[hero->floor][heroX][heroY])));
    switch(type){
    case 0:
        heroX-=1;
        break;
    case 1:
        heroX+=1;
        break;
    case 2:
        heroY-=1;
        break;
    case 3:
        heroY+=1;
        break;
    default:
        break;
    }
    //角色移动后位置显示角色
    dynamic_map->map[hero->floor][heroX][heroY]=15;
    mapLabel[heroX][heroY]->setPixmap(QPixmap(tr(":/image/15.png")));
    //攻击怪物,实现图像闪烁
    if(preValue==8||preValue==9||preValue==10||preValue==11||preValue==12){
        //战斗中,记录战斗状态
        combate_signal=true;
        //定时器显示怪物图片
        QTimer *timer1=new QTimer(this);
        connect(timer1,&QTimer::timeout,this,[=](){
            mapLabel[heroX][heroY]->setPixmap(QPixmap(tr(":/image/%1.png").arg(preValue)));
        });
        //定时器显示角色图片
        QTimer *timer2=new QTimer(this);
        connect(timer2,&QTimer::timeout,this,[=](){
            mapLabel[heroX][heroY]->setPixmap(QPixmap(tr(":/image/15.png")));
        });
        timer1->start(100);
        QThread::msleep(50);//线程实现短暂休眠
        timer2->start(100);
        //500毫秒定时器,定时器结束,闪烁结束
        QTimer *timer=new QTimer(this);
        connect(timer,&QTimer::timeout,this,[=](){
            timer1->stop();
            timer2->stop();
            timer->stop();
            combate_signal=false;
            //如果角色战斗失败,则显示怪物图片
            if(hero->blood==0){
                mapLabel[heroX][heroY]->setPixmap(QPixmap(tr(":/image/%1.png").arg(preValue)));
            }
            //英雄胜利,则显示角色图片
            else{
                dynamic_map->map[hero->floor][heroX][heroY]=15;
                mapLabel[heroX][heroY]->setPixmap(QPixmap(tr(":/image/15.png")));
            }
        });
        timer->start(500);
    }
}

13.角色移动处理

void Widget::heroMove(int type,int preValue){
    if(hero->sword){
        hero->attack+=12;
        hero->sword=false;
    }
    if(hero->shield){
        hero->defense+=8;
        hero->shield=false;
    }
    if(preValue==0){//墙
        return;
    }
    else if(preValue==1){//空地
        updateMap(type);
    }
    else if(preValue==3&&hero->y_key>0){//黄门
        updateMap(type);
        hero->y_key--;
    }
    else if(preValue==5){//黄钥匙
        updateMap(type);
        hero->y_key++;
    }
    else if(preValue==2&&hero->b_key>0){//蓝门
        updateMap(type);
        hero->b_key--;
    }
    else if(preValue==4){//蓝钥匙
        updateMap(type);
        hero->b_key++;
    }
    else if(preValue==6){//小血瓶
        updateMap(type);
        hero->blood+=30;
    }
    else if(preValue==7){//大血瓶
        updateMap(type);
        hero->blood+=80;
    }
    else if(preValue==13){//红宝石
        updateMap(type);
        hero->attack+=3;
    }
    else if(preValue==14){//蓝宝石
        updateMap(type);
        hero->defense+=2;
    }
    else if(preValue==16){//上楼
        hero->floor+=1;
        initMap();
    }
    else if(preValue==17){//下楼
        hero->floor-=1;
        initMap();
    }
    else if(preValue==18){//剑
        updateMap(type);
        hero->sword=true;
    }
    else if(preValue==19){//盾
        updateMap(type);
        hero->shield=true;
    }
    else if(preValue==8||preValue==9||preValue==10||preValue==11||preValue==12){//怪物
        heroCombat(preValue);
        updateMap(type,preValue);
        showHeroInfo();
        // 碰见怪物,攻击结束之后判断血量
        if(hero->blood==0){
            int select=QMessageBox::information(this,tr("游戏结束"),tr("是否重新开始"),QMessageBox::Yes|QMessageBox::No);
            if(select==QMessageBox::Yes){
                //重新开始
                hero=new Hero("英雄");
                restart_signal=true;
                initMap();
            }
            else{
                this->close();
            }
        }
    }
}

14.运算战斗结果

void Widget::heroCombat(int preValue){
    int change_blood;       //战斗后减少的血量
    int monster_blood;      //当前怪物血量
    int hero_atk_def;       //角色与怪物的攻防差
    int monster_atk_def;    //怪物与角色的攻防余数
    switch (preValue){
    case 8:
        monster_blood=blueSlime->blood;
        hero_atk_def=hero->attack-blueSlime->defense;
        monster_atk_def=blueSlime->attack-hero->defense;
        hero->money+=blueSlime->money;
        break;
    case 9:
        monster_blood=yellowSlime->blood;
        hero_atk_def=hero->attack-yellowSlime->defense;
        monster_atk_def=yellowSlime->attack-hero->defense;
        hero->money+=yellowSlime->money;
        break;
    case 10:
        monster_blood=smallBat->blood;
        hero_atk_def=hero->attack-smallBat->defense;
        monster_atk_def=smallBat->attack-hero->defense;
        hero->money+=smallBat->money;
        break;
    case 11:
        monster_blood=skeletonKnight->blood;
        hero_atk_def=hero->attack-skeletonKnight->defense;
        monster_atk_def=skeletonKnight->attack-hero->defense;
        hero->money+=skeletonKnight->money;
        break;
    case 12:
        monster_blood=exorcist->blood;
        hero_atk_def=hero->attack-exorcist->defense;
        monster_atk_def=exorcist->attack-hero->defense;
        hero->money+=exorcist->money;
        break;
    default:
        break;
    }
    //角色防御高于怪物攻击
    if(monster_atk_def<=1){
         monster_atk_def=1;
     }
    //角色防御低于怪物攻击过多
    else if(monster_atk_def>10){
        monster_atk_def=10;
    }
    //角色攻击低于怪物防御
    if(hero_atk_def<0){
        change_blood=hero->blood;
    }
    else{
        //角色攻击高于怪物防御过多
        if(hero_atk_def>monster_blood){
            hero_atk_def=monster_blood;
        }
        change_blood=(monster_blood/hero_atk_def)*monster_atk_def;
    }
    //将要改变的血量高于原始血量
    if(change_blood>hero->blood){
        change_blood=hero->blood;
    }
    hero->blood-=change_blood;
}

15.边栏角色信息

void Widget::showHeroInfo(){
    QString text;
    //角色名字
    text.append("角色名字\n");
    text.append(hero->name);
    nameLabel->setText(text);
    nameLabel->setAlignment(Qt::AlignCenter);//居中显示
    text.clear();//用完清空,方便下次使用
    //金币
    text.append("金币\n");
    text.append(QString::number(hero->money));
    moneyValueLabel->setText(text);
    moneyValueLabel->setAlignment(Qt::AlignCenter);
    text.clear();
    //楼层
    text.append("楼层\n");
    text.append(QString::number(hero->floor));
    floorValueLabel->setText(text);
    floorValueLabel->setAlignment(Qt::AlignCenter);
    text.clear();
    //生命值
    text.append("生命值\n");
    text.append(QString::number(hero->blood));
    bloodLabel->setText(text);
    bloodLabel->setAlignment(Qt::AlignCenter);
    text.clear();
    //攻击力
    text.append("攻击力\n");
    text.append(QString::number(hero->attack));
    attackLabel->setText(text);
    attackLabel->setAlignment(Qt::AlignCenter);
    text.clear();
    //防御力
    text.append("防御力\n");
    text.append(QString::number(hero->defense));
    defenseLabel->setText(text);
    defenseLabel->setAlignment(Qt::AlignCenter);
    text.clear();
    //蓝钥匙
    text.append("蓝钥匙\n");
    text.append(QString::number(hero->b_key));
    bKeyLabel->setText(text);
    bKeyLabel->setAlignment(Qt::AlignCenter);
    text.clear();
    //黄钥匙
    text.append("黄钥匙\n");
    text.append(QString::number(hero->y_key));
    yKeyLabel->setText(text);
    yKeyLabel->setAlignment(Qt::AlignCenter);
    text.clear();
}

16.主函数

        所有需要操作的都写在了主界面中,不需要更改Qt默认生成的主函数

四.注意事项

        1.头文件

                主界面所需的头文件

#include <QWidget>
#include <QMessageBox>
#include <QLabel>
#include <QKeyEvent>
#include <QTimer>
#include <QThread>
#include <QPushButton>
#include <QPainter>
#include <QPoint>
#include <QMediaPlayer>
#include <QMediaPlaylist>
#include <QString>

#include "map.h"
#include "hero.h"
#include "monster.h"

                玩家类和怪物类只需要QString的头文件

        2.图片与音乐文件

                素材图片最好是.png格式的,背景图片任何类型都可以

                背音乐文件一般为.wav格式,若想要使用其他格式则需要安装插件

                当音乐文件放在项目.qrc文件下时,写音乐路径时必须在分号前写上"qrc"

                如:QUrl("qrc:/bgm/morning.mp3");

                当文件未放入项目中时,该路径必须是绝对路径

        3.项目整体

五.游戏效果

        1.开始界面(简陋)

        2.第一层(新手大剑)

         3.第二层(开始正常游戏)

        4.后续层

        5.奖励层

        6.最后一层(暂时还没写结束结算)

  • 17
    点赞
  • 72
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: CocosCreator是一款功能强大的游戏开发引擎,而魔塔游戏源码则是指使用CocosCreator开发的一款魔塔游戏的源代码。 CocosCreator魔塔游戏源码通常包含游戏的各个模块,包括角色控制、战斗系统、地图生成以及游戏UI等。在这个源码中,开发者可以看到一套完整的魔塔游戏框架和逻辑。 角色控制是魔塔游戏源码中的重要部分。开发者可以看到玩家角色的移动、攻击以及技能释放等代码。通过阅读源码,开发者可以了解到游戏角色的行为逻辑,比如怪物移动的规则、装备系统的实现等。 另外,战斗系统是魔塔游戏源码中的关键内容。开发者可以通过查看源码,了解到游戏的攻击和防御计算方式、技能释放和效果表现等细节。这对于学习游戏战斗系统的设计与实现非常有帮助。 地图生成也是源码中的一个重要模块。通过阅读源码,开发者可以了解到地图生成的算法、随机事件的处理、宝箱、商店等游戏元素的放置和生成等细节。 游戏的UI设计也是魔塔游戏源码中不可缺少的一部分。开发者可以通过查看源码,了解到游戏UI的布局、按钮点击事件的处理、弹窗的实现等细节。 总之,通过研究CocosCreator魔塔游戏源码,开发者可以学习到游戏开发中常用的算法和设计思路,同时也可以培养自己的开发能力和创造力。在学习和借鉴源码的基础上,开发者还可以根据自身需求进行二次开发和优化,创作出属于自己的魔塔游戏作品。 ### 回答2: CocosCreator魔塔游戏源码是指使用CocosCreator开发魔塔游戏的程序代码。CocosCreator是一款基于Javascript的游戏开发引擎,通过它可以轻松创建多平台的游戏魔塔游戏源码通常包含了游戏的一些基本组成部分,如场景、角色、道具、怪物等。它们通过CocosCreator的节点和组件系统进行组织和管理。在源码中,开发者可以了解到游戏的逻辑、界面设计以及各个功能模块的实现方式。 通过使用CocosCreator魔塔游戏源码,开发者可以学习到如何实现一个完整的魔塔游戏。他们可以了解到游戏场景的搭建,通过编辑器创建地图和设置场景元素。他们可以学习到如何处理游戏角色的移动、攻击和受伤等操作,并实现相应的动画效果。另外,他们还可以了解到如何处理游戏道具的使用和怪物的生成与AI行为等。 魔塔游戏源码可以帮助开发者迅速上手游戏开发,节省大量的开发时间。通过对源码的学习和理解,开发者可以根据自己的需要进行二次开发和扩展,实现独具创意的魔塔游戏。 ### 回答3: CocosCreator魔塔游戏源码是基于CocosCreator游戏引擎开发的一种魔塔游戏的代码。魔塔游戏是一种以策略和冒险为主题的角色扮演游戏,玩家需要在迷宫中探索、寻找宝物、打败敌人,最终达到最深层并击败最终BOSS。 CocosCreator魔塔游戏源码包含了游戏的基本逻辑、界面设计、角色控制、敌人AI、道具系统等各个方面的代码实现。通过阅读源码,我们可以了解到游戏是如何实现地图的生成与呈现、角色的移动和战斗、敌人的行为规则、道具的获取和使用等功能。同时,源码还提供了一些基本的游戏美术资源,如角色、地图瓷砖、敌人等,可以用于快速搭建游戏。 在实际使用源码的时候,我们可以根据自己的需求进行修改和扩展,比如添加新的关卡、设计新的敌人、增加新的道具等。同时,我们也可以根据源码学习到CocosCreator游戏开发的一些基本技巧与规范,例如场景管理、碰撞检测、UI设计等。 总之,CocosCreator魔塔游戏源码是一个可以让我们了解和学习魔塔游戏开发的实际案例,可以通过对源码的学习和修改,快速搭建自己的游戏项目,并且还可以帮助我们掌握CocosCreator游戏引擎的使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值