前言
最近学校有一个游戏制作的比赛,正好没事干就打算冲一波,规则是两个主题中选择一个,于是选了第二个
在思考制作什么类型的游戏时想到了消除+塔防的组合,于是去搜了一下,好像还真有这样的游戏,是很多年前的一款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);
}
}
}