Qt 魔塔小游戏

魔塔小游戏

思路构建

刚学习了qt,所以想写一点小时候玩的游戏,魔塔游戏简单,正合适新手练习。在我们写项目之前,就需要先构思如何写,就相当于写作文,一样的意思,现有准备工作,才可以有条不紊的写代码,而且写出来方便查看。

1、魔塔是一个2D游戏,只有一个地图,任务在其中行走,所以我们可以选择使用数组来表示地图,使用QLabel 来显示图片,这样地图就做好了。
2、定义英雄类,其中有英雄的人物信息。
3、定义怪物类,其中有怪物的信息,不同的怪物设置一个属性来区分。
4、实现按键事件,控制人物行走,人物行走,其实就是展示人物的label进行移动
5、分析人物遇见不同情况而不同的处理
6、写人物战斗算法,计算经验,血量等等属性
7、人物移动,收取物品,击杀怪物,实时显示

代码实现

定义地图

魔塔共50层,每一层是一个平面,就相当于我们的X,Y坐标系。所以可以定义一个三维数组,来存储不同楼层的排布。

// 定义一个三维数组
// 这个是自定义地图,定义了一个专门的类来存储地图,而这个是原始地图,并不会被修改,
// 游戏正常进行的地图由其他类成员来操作,这样防止重新开始游戏后初始化地图被修改。
int Map::map[50][13][13] =
{
    {
        //关卡1
        {0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,16,1,9,8,9,1,1,1,1,1,1,0},
        {0,0,0,0,0,0,0,0,0,0,0,1,0},
        {0,6,1,1,3,1,0,13,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,18,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,5,1,1,0,1,10,1,0},
        {0,6,17,5,0,1,15,1,0,9,7,9,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0}
    },
    { //关卡2
      {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,18,1,0,5,4,5,0,1,0,0,0,0},
      {0,3,0,0,0,1,0,0,1,0,1,13,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,18,5,0},
      {0,6,13,0,1,1,1,0,1,0,0,0,0},
      {0,0,0,0,0,1,0,0,8,0,1,1,0},
      {0,19,15,1,1,1,1,0,1,0,1,16,0},
      {0,0,0,0,0,0,0,0,0,0,0,0,0}
    }
};
//在这里我将素材图片和数组中的值一一对应,这样方便显示地图。
0-1-地板
2-蓝门
3-黄门
4-蓝钥匙
5-黄钥匙
6-红血瓶
7-蓝血瓶
8-大史雷姆
9-小史雷姆
10-小蝙蝠
11-骷髅战士
13-红宝石
14-蓝宝石
15-英雄
16-上楼
17-法杖
18-大法师
19-下楼

// 这里我是完全按照老板游戏设置的地图,所以我只写了两层,如果大家有时间,可以继续往下写,我这里只是一个初版,大家可以继续完善。

定义好地图数组之后,我们则需要按照数组值用图片将地图绘制出来。我自己初步定义一个图片值为 60 *60 ,游戏地图是长13个,宽13个。还有右边需要显示英雄信息, 60 * (13 * 60)。因此可以初步设置游戏界面大小是 14 * 60 , 13 * 50。

游戏管理类头文件,具体功能后续会详细介绍。

mainwindow.h
// 游戏主类
class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

protected:
    // 重载按键事件
    void keyPressEvent(QKeyEvent *event) override;

private:
    Ui::MainWindow *ui;

    // 定义地图
    int map[50][13][13];
    // 记录是否重新开始
    bool isStart = true;
    // 记录是否在战斗中,如果是战斗中则不能进行移动
    bool isCombat = false;

    // 记录角色的行列
    int heroX = 0;
    int heroY = 0;
    // 定义地图label,展示当前层的地图图片
    QLabel* labels[13][13];
    // 定义角色信息label
    QLabel *infoLabel;
    // 角色名字
    QLabel *nameLabel;
    // 金币
    QLabel *moneyLabel;
    // 金币显示
    QLabel *moneyValueLabel;
    // 楼层
    QLabel *floorLabel;
    // 楼层显示
    QLabel *floorValueLabel;
    // 生命值
    QLabel *bloodLabel;
    // 攻击力
    QLabel *attackLabel;
    // 防御力
    QLabel *defenseLabel;
    // 蓝钥匙
    QLabel *bKeyLabel;
    // 黄钥匙
    QLabel *yKeyLabel;


    // 定义英雄
    Hero *hero = new Hero();
    //初始化 9小史莱姆
    Monster *smallSlime = new Monster("小史莱姆", 20, 20, 20, 20, 9);

    //初始化 8大史莱姆
    Monster *bigSlime = new Monster("大史莱姆", 40, 40, 40, 40, 8);

    //初始化 10 小蝙蝠
    Monster *smallBat = new Monster("小蝙蝠", 60, 60, 60, 60, 10);

    //初始化 11 骷髅战士
    Monster *skeletonKnight = new Monster("骷髅战士", 70, 70, 70, 70, 11);

    //初始化 18 大法师
    Monster *exorcist = new Monster("大法师", 80, 80, 80, 80, 18);

    // 初始化地图
    void initMap();

    // 修改地图
    // type: 0-上,1-下,2-左,3-右
    void updateMap(int type,int preValue = 0);

    // 角色行动
    void heroMove(int type,int preValue);

    // 角色战斗
    void heroCombat(int prevalue);

    // 展示英雄信息
    void showHeroInfo();

};

绘制地图


MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    // 接受按键事件
    this->grabKeyboard();

    // 设置窗口大小
    this->setFixedSize(14 * 60, 13 * 60);
    // 初始化地图
    initMap();
}


// 初始化地图
void MainWindow::initMap()
{
    if(isStart)
    {
        // 将初始化地图防止在游戏类成员地图中,之后玩家操作,实时变换地图,操作的是这个成员变量地图
        for(unsigned long k = 0 ; k < sizeof(Map::map) / sizeof(Map::map[0]) ; k++)
        {
            for(unsigned long i = 0 ; i < sizeof(Map::map[0]) / sizeof(Map::map[0][0]) ; i++)
            {
                for(unsigned long j = 0 ; j < sizeof(Map::map[0][0]) / sizeof(Map::map[0][0][0]) ; j++)
                {
                    map[k][i][j] = Map::map[k][i][j];
                }
            }
        }
        // 用于英雄死亡,是否重新开始。
        isStart = false;
    }
    // 绘制地图
    for(unsigned long i = 0 ; i < sizeof(map[0]) / sizeof(map[0][0]) ; i++)
    {
        for(unsigned long j = 0 ; j < sizeof(map[0][0]) / sizeof(map[0][0][0]) ; j++)
        {
            // 每次更新一层,则需要刷新当前层的图片
            labels[i][j] = new QLabel(this);
            labels[i][j]->setFixedSize(60,60);
            labels[i][j]->setPixmap(QPixmap(tr(":/%1.jpg").arg(map[hero->heroFloor][i][j])));
            // 让图片在label中填充
            labels[i][j]->setScaledContents(true);
            // 设置label位置
            labels[i][j]->move(j * 60,i * 60);
            labels[i][j]->show();

            // 检索到角色位置
            if(map[hero->heroFloor][i][j] == 15)
            {
                heroX = i;
                heroY = j;
            }
        }
    }
    // 绘制计分板
    infoLabel = new QLabel(this);
    // 设置label大小
    infoLabel->setFixedSize(60,13 * 60);
    infoLabel->setPixmap(QPixmap(":/info.jpg"));
    // 让图片在label中填充
    infoLabel->setScaledContents(true);
    infoLabel->move(13 * 60, 0);
    infoLabel->show();

    // 显示英雄信息label
    // 设置字体颜色
    QPalette palette;
    palette.setColor(QPalette::WindowText,Qt::white);
    // 设置字体大小
    QFont font;
    font.setPointSize(10);
    // 角色名字
    nameLabel = new QLabel(this);
    nameLabel->move(13 * 60 + 15, 100);
    nameLabel->setPalette(palette);
    nameLabel->setFont(font);
    nameLabel->show();
    // 金币
    moneyLabel = new QLabel(this);
    moneyLabel->move(13 * 60 + 13, 145);
    moneyLabel->setPalette(palette);
    moneyLabel->setFont(font);
    moneyLabel->show();
    // 金币显示
    moneyValueLabel = new QLabel(this);
    moneyValueLabel->move(13 * 60 + 23, 175);
    moneyValueLabel->setPalette(palette);
    moneyValueLabel->setFont(font);
    moneyValueLabel->show();
    // 楼层
    floorLabel = new QLabel(this);
    floorLabel->move(13 * 60 + 13, 198);
    floorLabel->setPalette(palette);
    floorLabel->setFont(font);
    floorLabel->show();
    // 楼层显示
    floorValueLabel = new QLabel(this);
    floorValueLabel->move(13 * 60 + 23, 225);
    floorValueLabel->setPalette(palette);
    floorValueLabel->setFont(font);
    floorValueLabel->show();
    // 生命值
    bloodLabel = new QLabel(this);
    bloodLabel->move(13 * 60 + 13, 355);
    bloodLabel->setPalette(palette);
    bloodLabel->setFont(font);
    bloodLabel->show();
    // 攻击力
    attackLabel = new QLabel(this);
    attackLabel->move(13 * 60 + 18, 510);
    attackLabel->setPalette(palette);
    attackLabel->setFont(font);
    attackLabel->show();
    // 防御力
    defenseLabel = new QLabel(this);
    defenseLabel->move(13 * 60 + 18, 560);
    defenseLabel->setPalette(palette);
    defenseLabel->setFont(font);
    defenseLabel->show();
    // 黄钥匙
    yKeyLabel = new QLabel(this);
    yKeyLabel->move(13 * 60 + 23, 680);
    yKeyLabel->setPalette(palette);
    yKeyLabel->setFont(font);
    yKeyLabel->show();
    // 蓝钥匙
    bKeyLabel = new QLabel(this);
    bKeyLabel->move(13 * 60 + 23, 750);
    bKeyLabel->setPalette(palette);
    bKeyLabel->setFont(font);
    bKeyLabel->show();

}
定义英雄类
// 英雄类
// 英雄类的各个属性将展示在右边面板
class Hero
{
public:
    Hero();
    ~Hero();

    // 姓名
    QString heroName = "勇者";
    // 血量
    int heroBlood = 100;
    //攻击力
    int heroAttack = 20;
    //防御力
    int heroDefense = 20;
    //金钱
    int heroMoney = 0;
    //楼层
    int heroFloor = 0;
    //蓝钥匙
    int heroBKey = 0;
    //黄钥匙
    int heroYKey = 0;

};
游戏类展示英雄信息
// 展示英雄信息
void MainWindow::showHeroInfo()
{
    // 角色名字
    nameLabel->setText(hero->heroName);
    // 金币
    moneyLabel->setText("金币");
    // 金币显示
    moneyValueLabel->setText(QString::number(hero->heroMoney));
    // 楼层
    floorLabel->setText("楼层");
    // 楼层显示
    floorValueLabel->setText(QString::number(hero->heroFloor));
    // 生命值
    bloodLabel->setText(QString::number(hero->heroBlood));
    // 攻击力
    attackLabel->setText(QString::number(hero->heroAttack));
    // 防御力
    defenseLabel->setText(QString::number(hero->heroDefense));
    // 蓝钥匙
    bKeyLabel->setText(QString::number(hero->heroBKey));
    // 黄钥匙
    yKeyLabel->setText(QString::number(hero->heroYKey));

}
定义怪物类
// 怪物类,不同的怪物有不同的参数属性
class Monster
{
public:
    Monster(QString name,int boold,int attack ,int defense ,int money ,int type);
    ~Monster();


    QString name;			//姓名
    int boold;				//生命值
    int attack;				//攻击力
    int defense;			//防御力
    int money;				//金钱
    int type;				//类型 怪物类型  8大史莱姆  9小史莱姆  10小蝙蝠  11骷髅战士 18大法师

};
实现按键事件
// 重载按键事件,实现角色行动
void MainWindow::keyPressEvent(QKeyEvent *event)
{
    if(!isCombat)
    {
        // 记录类型
        int type = 4;
        // 记录即将行走的位置是什么
        int preValue = 0;
        switch (event->key())
        {
        case Qt::Key_Up: // 上
        case Qt::Key_W:
            type = 0;
            preValue = map[hero->heroFloor][heroX - 1][heroY];
            heroMove(type,preValue);
            break;
        case Qt::Key_Down:  // 下
        case Qt::Key_S:
            type = 1;
            preValue = map[hero->heroFloor][heroX + 1][heroY];
            heroMove(type,preValue);
            break;
        case Qt::Key_Left:  // 左
        case Qt::Key_A:
            type = 2;
            preValue = map[hero->heroFloor][heroX][heroY - 1];
            heroMove(type,preValue);
            break;
        case Qt::Key_Right:  // 右
        case Qt::Key_D:
            type = 3;
            preValue = map[hero->heroFloor][heroX][heroY + 1];
            heroMove(type,preValue);
            break;
        default:
            break;
        }
        //显示信息
        showHeroInfo();
    }

}
处理英雄行走
// 角色移动
void MainWindow::heroMove(int type, int preValue)
{
    if(preValue == 0) // 墙
    {
        return;
    }
    else if(preValue == 1) // 空地
    {
        updateMap(type);
    }
    else if(preValue == 3 && hero->heroYKey > 0)  // 黄门
    {
        updateMap(type);
        hero->heroYKey--;
    }
    else if(preValue == 5) // 黄钥匙
    {
        updateMap(type);
        hero->heroYKey++;
    }
    else if(preValue == 2 && hero->heroBKey > 0)  // 蓝门
    {
        updateMap(type);
        hero->heroBKey--;
    }
    else if(preValue == 4) // 黄钥匙
    {
        updateMap(type);
        hero->heroBKey++;
    }
    else if(preValue == 6) // 红血瓶
    {
        updateMap(type);
        hero->heroBlood += 100;
    }
    else if(preValue == 7) // 蓝血瓶
    {
        updateMap(type);
        hero->heroBlood += 200;
    }
    else if(preValue == 13)  // 红宝石
    {
        updateMap(type);
        hero->heroAttack += 10;
    }
    else if(preValue == 14)  // 蓝宝石
    {
        updateMap(type);
        hero->heroDefense += 10;
    }
    else if(preValue == 16)  // 上楼
    {
        hero->heroFloor++;
        initMap();
    }
    else if(preValue == 19)  // 下楼
    {
        hero->heroFloor--;
        initMap();
    }
    else if(preValue == 8 || preValue == 9 || preValue == 10 || preValue == 11 || preValue == 18) // 怪物
    {
		// 英雄战斗
        heroCombat(preValue);
        // 实时修改地图
        updateMap(type,preValue);
        // 碰见怪物,攻击结束之后判断血量
        if(hero->heroBlood <= 0)
        {
            // 英雄血量为空,判断失败
            int select = QMessageBox::information(this,tr("游戏结束"),tr("是否重新开始?"),QMessageBox::Yes | QMessageBox::No);
            if(select == QMessageBox::Yes)
            {
                // 重置英雄
                hero = new Hero();
                // 重新开始
                isStart = true;
                // 重置图像
                initMap();
            }
            else
            {
                this->close();
            }
        }
    }
}
人物战斗
// 角色战斗
// 角色战斗计算公式自己定义
void MainWindow::heroCombat(int prevalue)
{
    switch (prevalue) {
    case 8:
        hero->heroBlood -= (bigSlime->boold - (hero->heroAttack - bigSlime->defense)) - hero->heroDefense;
        hero->heroMoney += bigSlime->money;
        break;
    case 9:
        hero->heroBlood -= (smallSlime->boold - (hero->heroAttack - smallSlime->defense)) - hero->heroDefense;
        hero->heroMoney += smallSlime->money;
        break;
    case 10:
        hero->heroBlood -= (smallBat->boold - (hero->heroAttack - smallBat->defense)) - hero->heroDefense;
        hero->heroMoney += smallBat->money;
        break;
    case 11:
        hero->heroBlood -= (skeletonKnight->boold - (hero->heroAttack - skeletonKnight->defense)) - hero->heroDefense;
        hero->heroMoney += skeletonKnight->money;
        break;
    case 18:
        hero->heroBlood -= (exorcist->boold - (hero->heroAttack - exorcist->defense)) - hero->heroDefense;
        hero->heroMoney += exorcist->money;
        break;
    default:
        break;
    }
}
修改图片

人物运动时,当前位置图片和目标位置图片需要修改,目标位置变成英雄,当前位置变成空地。

// 修改地图
void MainWindow::updateMap(int type,int preValue)
{
    // 修改地图,英雄向四周行动,则英雄原来的地方变成空地,下一个地方变成英雄
    map[hero->heroFloor][heroX][heroY] = 1;
    labels[heroX][heroY]->setPixmap(QPixmap(tr(":/%1.jpg").arg(map[hero->heroFloor][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;
    }

    // 角色移动后下一个位置显示英雄
    map[hero->heroFloor][heroX][heroY] = 15;
    labels[heroX][heroY]->setPixmap(QPixmap(tr(":15.jpg")));

    // 攻击怪物,实现图像闪烁
    if(preValue == 8 || preValue == 9 || preValue == 10 || preValue == 11 || preValue == 18)
    {

        // 战斗中,记录战斗状态,在
        isCombat = true;
        // 定时器显示怪物图片
        QTimer *timer1 = new QTimer(this);
        connect(timer1,&QTimer::timeout,this,[=](){
            labels[heroX][heroY]->setPixmap(QPixmap(tr(":/%1.jpg").arg(preValue)));
        });
        // 定时器显示英雄图片
        QTimer *timer2 = new QTimer(this);
        connect(timer2,&QTimer::timeout,this,[=](){
            labels[heroX][heroY]->setPixmap(QPixmap(tr(":15.jpg")));
        });
        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();
            isCombat = false;
            // 如果英雄战斗失败,则显示怪物图片
            if(hero->heroBlood <= 0)
            {
                labels[heroX][heroY]->setPixmap(QPixmap(tr(":/%1.jpg").arg(preValue)));
            }
            // 英雄胜利,则显示英雄图片
            else
            {
                map[hero->heroFloor][heroX][heroY] = 15;
                labels[heroX][heroY]->setPixmap(QPixmap(tr(":15.jpg")));
            }
        });
        timer->start(500);
    }
}

至此,游戏基本功能已经实现。目前只是游戏初版,可以自己逐步优化。
游戏效果如下。

魔塔展示

你知道的越多,你不知道的越多,下期再见。

  • 9
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答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游戏引擎的使用。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值