前言

最近学校有一个游戏制作的比赛,正好没事干就打算冲一波,规则是两个主题中选择一个,于是选了第二个

在思考制作什么类型的游戏时想到了消除+塔防的组合,于是去搜了一下,好像还真有这样的游戏,是很多年前的一款flash游戏,叫做点击战争(ClickBattle),游玩地址:https://www.newgrounds.com/portal/view/625296(貌似需要挂梯子)

游戏截图:

玩了下感觉还蛮好玩的,所以就做一个类似的吧

我会不定期把部分我认为比较重要的代码发在我的博客上

游戏界面

各种类的定义

class Cat :public Sprite {
public:
    static Cat* create(const char* pszFileName);
    Size msize = Size(42, 42);
    int type = -1;                //猫咪种类
    int id = 0;                    //猫咪id
    Pos pos = Pos(0, 0);        //在网格中的位置

};
class Enemy :public Sprite {
public:
    static Enemy* create(const char* pszFileName);
    enum e_enemyType {

    };
    Sprite* hpbar1 = Sprite::create("bar1.png");
    Sprite* hpbar2 = Sprite::create("bar2.png");
    ProgressTimer* progress;
    int type = -1;                    //敌人种类
    int state = -1;                    //敌人状态
    bool canMove = true;            //可否移动
    Pos attackPos = Pos(-1, -1);        //正在攻击的塔的记录
    Size size = Size(40, 40);
    int hp = 3;
    void fixSize();                    //修正各种属性
    void bloodUpdate(float dt);        //血量监控
};
class Town :public Sprite {
public:
    static Town* create(const char* pszFileName);
    Sprite* hpbar1 = Sprite::create("bar1.png");
    Sprite* hpbar2 = Sprite::create("bar3.png");
    ProgressTimer* progress;

    Pos pos = Pos(0, 0);
    Size size = Size(30, 40);
    int hp = 5;
    int type = -1;

    void initial();                    //初始化
    void bloodUpdate(float dt);        //监控血量
};
class Bullet :public Sprite {
public:
    static Bullet* create(const char* pszFileName);
    enum e_bulletType {
        e_blueBullet, e_redBullet, e_whiteBullet, e_yellowBullet
    };
    int type = -1;
};
class catTD : public cocos2d::Scene {
public:
    static cocos2d::Scene* createScene();
    virtual bool init();
    CREATE_FUNC(catTD);
    virtual bool catOnTouchBegan(Touch* touch, Event* unused_event);
    int gameScore = 0;        //得分
    Vec2 mousePos = Vec2(0, 0);
    Grid lGridArea[8][10];
    Grid rGridArea[4][10];
    vector<Bullet*> bullets;
    vector<Enemy*> enemys;
    vector<ActionInterval*> actions;
    vector<Town*> towns;
    vector<Button*> townBtns;
    Town* townReady = nullptr;        //准备建造的塔
    LabelAtlas* score;                //分数图集
    char* fontToUTF8(const char* font);        //解决中文乱码
    void update(float dt);   //注意参数类型
    void addCat(float dt);                    //增加一列猫咪
    void initScore();        //初始化分数
    void initBg();                    //初始化背景
    void initData();                        //初始化数据
    void removeRound(Pos pos);    //移除相邻同色
    void firstAddCat();                        //初次布置
    void addBullet(Pos pos, int type);        //增加子弹
    void addEnemy(float dt);            //增加敌人
    bool hitEnemy();            //是否击中敌人
    void xMove();                //猫咪向右移动
    void addTown(Pos pos, int type);                //增加建筑
    void addTownBtns();
    void enemyMoveAgain(float dt);            //敌人继续移动
    void enemyAttack(float dt);                //敌人攻击
};

解决中文乱码

char* catTD::fontToUTF8(const char* font) {
    int len = MultiByteToWideChar(CP_ACP, 0, font, -1, NULL, 0);
    wchar_t* wstr = new wchar_t[len + 1];
    memset(wstr, 0, len + 1);
    MultiByteToWideChar(CP_ACP, 0, font, -1, wstr, len);
    len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
    char* str = new char[len + 1];
    memset(str, 0, len + 1);
    WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, len, NULL, NULL);
    if (wstr)delete[] wstr;
    return str;
}

消除区域的初始化

void catTD::firstAddCat() {
    for (int i = 4; i < 8; i++) {
        int number = 0;
        for (int j = 0; j < 10; j++) {
            int color = rand() % 4;
            stringstream ss;
            ss << "cat" << color << ".png";
            auto* cat1 = Cat::create(ss.str().c_str());
            cat1->setPosition(LEFT_GRID_CORNER_X + i * GRID_SIZE + GRID_SIZE / 6, LEFT_GRID_CORNER_Y + GRID_SIZE * number + GRID_SIZE / 6);
            cat1->setAnchorPoint(Vec2(0, 0));
            cat1->setContentSize(cat1->msize);
            cat1->type = color;
            int id = i * 10 + j;
            cat1->setName(to_string(id));
            lGridArea[i][j].isBuild = true;
            addChild(cat1);
            number++;
        }
    }
}

增加一列随机猫咪

void catTD::addCat(float dt) {
    srand(time(NULL));
    for (int i = 0; i < 10; i++) {
        if (!lGridArea[0][i].isBuild) {
            int color = rand() % 4;
            stringstream ss;
            ss << "cat" << color << ".png";
            auto* cat1 = Cat::create(ss.str().c_str());
            cat1->setPosition(LEFT_GRID_CORNER_X + GRID_SIZE / 6, LEFT_GRID_CORNER_Y + GRID_SIZE * i + GRID_SIZE / 6);
            cat1->setAnchorPoint(Vec2(0, 0));
            cat1->setContentSize(cat1->msize);
            cat1->type = color;
            cat1->setName(to_string(i));
            addChild(cat1);
            lGridArea[0][i].isBuild = true;
        }
    }
}

移除相邻同色喵咪

void catTD::removeRound(Pos pos) {
    int id = pos.x * 10 + pos.y;
    Cat* cat = (Cat*)getChildByName(to_string(id));
    int type = cat->type;
    addBullet(pos, cat->type);
    removeChildByName(to_string(id));
    lGridArea[pos.x][pos.y].isBuild = false;
    if (pos.x - 1 >= 0 && lGridArea[pos.x - 1][pos.y].isBuild) {
        int id2 = (pos.x - 1) * 10 + pos.y;
        Cat* cat2 = (Cat*)getChildByName(to_string(id2));
        if (type == cat2->type) {
            removeRound(Pos(pos.x - 1, pos.y));
        }
    }
    if (pos.x + 1 <= 7 && lGridArea[pos.x + 1][pos.y].isBuild) {
        int id2 = (pos.x + 1) * 10 + pos.y;
        Cat* cat2 = (Cat*)getChildByName(to_string(id2));
        if (type == cat2->type) {
            removeRound(Pos(pos.x + 1, pos.y));
        }
    }
    if (pos.y + 1 <= 9 && lGridArea[pos.x][pos.y + 1].isBuild) {
        int id2 = pos.x * 10 + pos.y + 1;
        Cat* cat2 = (Cat*)getChildByName(to_string(id2));
        if (type == cat2->type) {
            removeRound(Pos(pos.x, pos.y + 1));
        }
    }
    if (pos.y - 1 >= 0 && lGridArea[pos.x][pos.y - 1].isBuild) {
        int id2 = pos.x * 10 + pos.y - 1;
        Cat* cat2 = (Cat*)getChildByName(to_string(id2));
        if (type == cat2->type) {
            removeRound(Pos(pos.x, pos.y - 1));
        }
    }
}

增加子弹

void catTD::addBullet(Pos pos, int type) {
    stringstream ss;
    ss << "bullet" << type << ".png";
    auto bullet = Bullet::create(ss.str().c_str());
    bullet->type = type;                        //设定子弹类型
    bullet->setAnchorPoint(Vec2(0, 0));
    Vec2 position = Vec2(LEFT_GRID_CORNER_X + pos.x * GRID_SIZE, LEFT_GRID_CORNER_Y + pos.y * GRID_SIZE);
    bullet->setPosition(position);
    bullets.push_back(bullet);
    addChild(bullet);
    ActionInterval* move = MoveBy::create(4, Vec2(WINDOW_WIDTH, 0));
    bullet->runAction(move);
}

增加敌人

void catTD::addEnemy(float dt) {
    srand(time(NULL));
    int i = rand() % 10;
    auto enemy = Enemy::create("bear.png");
    enemy->setPosition(Vec2(WINDOW_WIDTH, LEFT_GRID_CORNER_Y + i * GRID_SIZE));
    enemy->fixSize();
    enemys.push_back(enemy);
    addChild(enemy);
    ActionInterval* move = MoveTo::create(10, Vec2(FISH_POSITION_X, LEFT_GRID_CORNER_Y + i * GRID_SIZE));
    move->setTag(0);
    enemy->runAction(move);
}

子弹击中敌人

bool catTD::hitEnemy() {
    for (int i = 0; i < enemys.size(); i++) {
        for (int j = 0; j < bullets.size(); j++) {
            if (enemys[i]->getBoundingBox().intersectsRect(bullets[j]->getBoundingBox())) {
                enemys[i]->hp--;
                removeChild(bullets[j]);                    //移除子弹
                bullets.erase(bullets.begin() + j);
                if (enemys[i]->hp == 0) {                    //去世
                    removeChild(enemys[i]);                    //移除敌人
                    enemys.erase(enemys.begin() + i);        //链表去除
                    gameScore++;                                //加分
                    score->setString(to_string(gameScore));
                    return true;
                }
            }
        }
    }
    return true;
}

喵咪向右的移动

void catTD::xMove() {
    for (int i = 7; i >= 0; i--) {
        for (int j = 9; j >= 0; j--) {
            if (lGridArea[i][j].isBuild) {
                int id = i * 10 + j;
                Cat* cat = (Cat*)getChildByName(to_string(id));
                if (cat != nullptr) {
                    for (int k = i + 1; k <= 7; k++) {
                        if (!lGridArea[k][j].isBuild) {
                            lGridArea[i][j].isBuild = false;        //原来的地方变为false
                            cat->setPosition(LEFT_GRID_CORNER_X + GRID_SIZE * k + GRID_SIZE / 6, LEFT_GRID_CORNER_Y + GRID_SIZE * j + GRID_SIZE / 6);        //移动到新位置
                            int id = k * 10 + j;
                            cat->setName(to_string(id));
                            lGridArea[k][j].isBuild = true;        //新位置变为true
                            break;
                        }
                    }
                }
            }
        }
    }
}

增加塔

void catTD::addTown(Pos pos, int type) {
    stringstream ss;
    ss << "town" << type << ".png";
    auto* town = Town::create(ss.str().c_str());
    town->setPosition(RIGHT_GRID_CORNER_X + 10 + pos.x * GRID_SIZE, RIGHT_GRID_CORNER_Y + pos.y * GRID_SIZE);
    town->pos = pos;
    town->initial();
    towns.push_back(town);
    addChild(town);
    rGridArea[pos.x][pos.y].isBuild = true;
}

增加塔的按钮

void catTD::addTownBtns() {
    for (int i = 0; i < 4; i++) {
        stringstream ss;
        ss << "town" << i << ".png";
        auto townBtn = Button::create(ss.str());
        townBtn->setPosition(Vec2(725 + 100 * i, 50));
        townBtn->setScale(30 / townBtn->getContentSize().width, 40 / townBtn->getContentSize().height);
        townBtn->setName(ss.str());
        townBtns.push_back(townBtn);
        townBtn->addTouchEventListener([&](Ref* sender, Widget::TouchEventType type) {

            switch (type) {
                case ui::Widget::TouchEventType::BEGAN:
                    break;
                case ui::Widget::TouchEventType::ENDED:
                    if (townReady == nullptr) {
                        int j;
                        for (j = 0; j < 4; j++) {
                            if (townBtns[j]->getBoundingBox().containsPoint(mousePos)) {        //如果鼠标在按钮内
                                break;
                            }
                        }
                        stringstream s;
                        s << "town" << j << ".png";
                        auto town = Town::create(s.str().c_str());                //获取第几张图
                        townReady = town;
                        townReady->type = j;
                        addChild(townReady);
                    } else {
                        removeChild(townReady);
                        townReady = nullptr;
                    }
                    break;
            }
        });
        addChild(townBtn);
    }
}

敌人的攻击

void catTD::enemyAttack(float dt) {
    for (int i = 0; i < towns.size(); i++) {
        for (int j = 0; j < enemys.size(); j++) {
            if (i == towns.size()) {
                return;
            }
            if (enemys[j]->getBoundingBox().intersectsRect(towns[i]->getBoundingBox())) {
                if (enemys[j]->attackPos.x == -1 && enemys[j]->attackPos.y == -1) {        //如果没有在攻击
                    enemys[j]->attackPos = towns[i]->pos;
                } else {
                    if (enemys[j]->attackPos.x == towns[i]->pos.x && enemys[j]->attackPos.y == towns[i]->pos.y) {    //如果正在攻击的是这座塔
                        towns[i]->hp--;
                        if (towns[i]->hp == 0) {
                            Pos pos = towns[i]->pos;                //记录塔的位置
                            rGridArea[towns[i]->pos.x][towns[i]->pos.y].isBuild = false;    //是否建筑记为否
                            removeChild(towns[i]);
                            towns.erase(towns.begin() + i);
                            for (int k = 0; k < enemys.size(); k++) {        //寻找正在攻击该塔的敌人
                                if (enemys[k]->attackPos.x == pos.x && enemys[k]->attackPos.y == pos.y) {
                                    float second = (enemys[k]->getPosition().x - FISH_POSITION_X) / (WINDOW_WIDTH - FISH_POSITION_X) * 10;    //计算运动时间
                                    ActionInterval* move = MoveTo::create(second, Vec2(FISH_POSITION_X, enemys[k]->getPosition().y));
                                    move->setTag(0);
                                    enemys[k]->runAction(move);
                                    enemys[k]->canMove = true;
                                    enemys[k]->attackPos = Pos(-1, -1);
                                }
                            }
                        }
                    }

                }
            }
        }
    }
}

敌人的再次移动

void catTD::enemyMoveAgain(float dt) {
    for (int i = 0; i < enemys.size(); i++) {
        if (enemys[i]->getActionByTag(0) == nullptr && enemys[i]->canMove) {
            float second = (enemys[i]->getPosition().x - FISH_POSITION_X) / (WINDOW_WIDTH - FISH_POSITION_X) * 10;    //计算运动时间
            ActionInterval* move = MoveTo::create(second, Vec2(FISH_POSITION_X, enemys[i]->getPosition().y));
            enemys[i]->runAction(move);
        }
    }
}

demo演示

Last modification:July 12th, 2020 at 12:59 am