Previous: http://www.cocoachina.com/bbs/read.php?tid=203362
- Game Hud
游戏HUD
首先,我们需要调整一下屏幕,毕竟我们创建了一些放置在地图外的物体。这么一块黑色区域确实会让一些人不太喜欢偶尔也会引起一些bug。

把这些代码输入AppDelete.cpp
bool AppDelegate::applicationDidFinishLaunching()
{
……
glview->setDesignResolutionSize(864, 640, kResolutionExactFit);
……
}
```
现在我们就要新建两对类,Tower.h和相应cpp还有GameHud.h及cpp,所以我们将有机会选择不同类型的防御者。(就像我们建立不同类型的敌人那样,一旦你掌握了这个教程你就能自己动手试着完成这一任务)。现在将他们部署在地图上。
取消我们在教程第二部分里的DataModel.h代码注释:
//Vector towers; // We will deal with it later.
```
在Tower.h里面
#pragma once
#include "cocos2d.h"
#include "Creep.h"
class Tower: public Sprite
{
public:
int range;
Sprite* sprite;
Creep* target;
Sprite * selSpriteRange;
Creep* getClosesTarget();
CREATE_FUNC(Tower);
};
class MachineGunTower : public Tower
{
public:
static Tower* tower();
bool virtual init();
void towerLogic(float dt);
};
```
正如你所看到的,在这里我们首先创建了一个基本的类,里面包括它的射程,距离最近目标,并继承自类Sprite
在Tower.cpp里面
#include "Tower.h"
#include "DataModel.h"
Tower* MachineGunTower::tower()
{
Tower* tower = Tower::create();
tower->sprite = Sprite::create("MachineGunTurret.png");
tower->setScale(0.5);
tower->addChild(tower->sprite, 0);
tower->range = 200;
tower->schedule(schedule_selector(towerLogic), 0.2);
return tower;
}
```
就像之前那样。‘MachineGunTower’继承自‘Tower’,并载入图片素材,设置它的比例大小和射程。最后用‘schedule’功能每0.2秒刷新‘towerLogic’功能。
bool MachineGunTower::init()
{
if (!Sprite::init())
{
return false;
}
return true;
}
void MachineGunTower::towerLogic(float dt)
{
// Coming soon…
}
```
取消我们在教程第二部分DataModel.h 里的注释:
//GameHUD* _gameHUDLayer; // We will deal with it later.
```
在GameHUD.h中
#pragma once
#include "cocos2d.h"
USING_NS_CC;
class GameHUD: public Layer
{
public:
Sprite* background;
Sprite* selSpriteRange;
Sprite* selSprite;
Vector movableSprites;
static GameHUD* _sharHUD;
virtual bool init();
static GameHUD* shareHUD();
CREATE_FUNC(GameHUD);
virtual void onEnter();
bool onTouchBegan(Touch *touch, Event *event);
void onTouchMoved(Touch *touch, Event *event);
void onTouchEnded(Touch* touch, Event* event);
};
```
是的。他应该能能够被拖拽到任何地方,也因此我们需要监听事件‘Listener Event’(onTouchXXX)。
然后在GameHUD.cpp里面:
#include "GameHUD .h"
#include "DataModel.h"
GameHUD* GameHUD::_sharHUD;
bool GameHUD::init()
{
if (!Layer::init())
{
return false;
}
Size winSize = CCDirector::getInstance()->getWinSize();
// Draw the background of the game HUD
CCTexture2D::setDefaultAlphaPixelFormat(kCCTexture2DPixelFormat_RGBA8888);
background = Sprite::create("hud.png");
background->setScaleX (2);
background->setAnchorPoint(ccp(0, 0));
this->addChild(background);
//CCTexture2D::setDefaultAlphaPixelFormat(kCCTexture2DPixelFormat_Default);
```
Background
MachineGunTower…
// 读取防御塔的图片素材,我们会把他们绘制在游戏的HUD层上。
Vector p_w_picpath;
p_w_picpath.pushBack(StringMake("MachineGunTurret.png"));
p_w_picpath.pushBack(StringMake("MachineGunTurret.png"));
p_w_picpath.pushBack(StringMake("MachineGunTurret.png"));
p_w_picpath.pushBack(StringMake("MachineGunTurret.png"));
for (int i = 0; i < p_w_picpath.size(); ++i)
{
String* image = p_w_picpath.at(i);
auto *sprite = Sprite::create(image->getCString());
float offsetFraction = ((float)(i + 1)) / (p_w_picpath.size() + 1);
sprite->setScale(0.6);
sprite->setPosition(ccp(winSize.width*offsetFraction, 35));
this->addChild(sprite);
movableSprites.pushBack(sprite);
}
return true;
}
GameHUD* GameHUD::shareHUD()
{
if (_sharHUD == NULL)
{
_sharHUD = GameHUD::create();
}
return _sharHUD;
}
void GameHUD::onEnter()
{
Layer::onEnter();
auto listener = EventListenerTouchOneByOne::create();
listener->setSwallowTouches(true);
listener->onTouchBegan = CC_CALLBACK_2(GameHUD::onTouchBegan, this);
listener->onTouchMoved = CC_CALLBACK_2(GameHUD::onTouchMoved, this);
listener->onTouchEnded = CC_CALLBACK_2(GameHUD::onTouchEnded, this);
auto dispatcher = Director::getInstance()->getEventDispatcher();
dispatcher->addEventListenerWithSceneGraphPriority(listener, this);
}
bool GameHUD::onTouchBegan(Touch *touch, Event *event)
{
Point touchLocation = this->convertToWorldSpace(this->convertTouchToNodeSpace(touch));
Sprite * newSprite = NULL;
for each(Sprite* sprite in this->movableSprites) // C++11
//for (int i = 0; i < movableSprites.size(); i++) // Use this if your VC doesn’t support C++11. 如果你的VC不支持C++11,用这行代码
{
// Sprite* sprite = (Sprite*)(movableSprites.at(i)); // Use this if your VC doesn’t support C++11. 如果你的VC不支持C++11,用这行代码
Rect pos_rect = Rect((sprite->getPositionX()-sprite->getContentSize().width/2), (sprite->getPositionY()-sprite->getContentSize().height/2), sprite->getContentSize().width, sprite->getContentSize().height);
// This determines the area which you can drag our ‘tower’ 这里是决定了你能够将“塔”拖拽的区域。
float xMin = pos_rect.getMinX();
float xMax = pos_rect.getMaxX();
float yMIn = pos_rect.getMinY();
float yMax = pos_rect.getMaxY();
if (pos_rect.containsPoint(touchLocation))
{
DataModel *m = DataModel::getModel();
selSpriteRange = Sprite::create("Range.png");
selSpriteRange->setScale(4);
this->addChild(selSpriteRange, -1);
selSpriteRange->setPosition(sprite->getPosition());
newSprite = Sprite::createWithTexture(sprite->getTexture()); //sprite;
newSprite->setPosition(sprite->getPosition());
newSprite->setScale(0.6);
selSprite = newSprite;
this->addChild(newSprite);
}
}
return true;
}
void GameHUD::onTouchMoved(Touch* touch,Event* event)
{
Point touchLocation = this->convertToWorldSpace(this->convertTouchToNodeSpace(touch));
Point oldTouchLocation = touch->getPreviousLocationInView();
oldTouchLocation = Director::getInstance()->convertToGL(oldTouchLocation);
oldTouchLocation = this->convertToNodeSpace(oldTouchLocation);
Point translation = ccpSub(touchLocation,oldTouchLocation);
if (selSprite)
{
Point newPos = selSprite->getPosition()+translation;
selSprite->setPosition(newPos);
selSpriteRange->setPosition(newPos);
DataModel *m = DataModel::getModel();
Point touchLocationInGameLayer = m->_gameLayer->convertTouchToNodeSpace(touch);
BOOL isBuildable = m->_gameLayer->canBuildOnTilePosition(touchLocationInGameLayer);
if (isBuildable)
{
selSprite->setOpacity(200);
}
else
{
selSprite->setOpacity(50);
}
}
}
```
等一下。你是否注意到了‘bool isBuildable’? 我们接下来应该要为我们的瓷砖设置一些属性。
void GameHUD::onTouchEnded(Touch* touch, Event* event)
{
Point touchLocation = this->convertTouchToNodeSpace(touch);
DataModel *m = DataModel::getModel();
if (selSprite)
{
Rect backgroundRect = Rect(background->getPositionX(),
background->getPositionY(),
background->getContentSize().width,
background->getContentSize().height);
if (!backgroundRect.containsPoint(touchLocation) && m->_gameLayer->canBuildOnTilePosition(touchLocation))
{
Point touchLocationInGameLayer = m->_gameLayer->convertTouchToNodeSpace(touch);
m->_gameLayer->addTower(touchLocationInGameLayer);
}
this->removeChild(selSprite,true);
selSprite = NULL;
this->removeChild(selSpriteRange,true);
selSpriteRange = NULL;
}
}
```
现在,把这些东西扔进TutorialScene.h里面
……
#include "GameHUD .h"
……
class TutorialScene : public cocos2d::Layer
{
public:
……
void addTower(Point pos);
Point tileCoordForPosition(Point position);
bool canBuildOnTilePosition(Point pos);
Point boundLayerPos(Point newPos);
Point position;
GameHUD *gameHUD;
……
private:
…...
};
In TutorialScene.cpp:
在TutorialScene.cpp:
……
#include "Tower.h"
……
Scene* TutorialScene::createScene()
{
……
auto myGameHUD = GameHUD::shareHUD();
m->_gameHUDLayer = myGameHUD;
scene->addChild(myGameHUD, 1);
……
}
```
再一次,我们使用了‘DataModel’来记录‘gameHUDLayer’。
Point TutorialScene::tileCoordForPosition(Point position)
{
int x = position.x / this->_tileMap->getTileSize().width;
int y = ((this->_tileMap->getMapSize().height * this->_tileMap->getTileSize().height) - position.y) / this->_tileMap->getTileSize().height;
return ccp(x, y);
}
```
对int y的公式感到疑惑么?因为坐标原点是放置在地图的左上角的。所以接下来的图片应该能帮助你的理解。
The original coordinate
bool TutorialScene::canBuildOnTilePosition(Point pos)
{
Point towerLoc = this->tileCoordForPosition(pos);
int tileGid = this->_background->getTileGIDAt(towerLoc);
Value props = this->_tileMap->getPropertiesForGID(tileGid);
ValueMap map = props.asValueMap();
int type_int;
if (map.size() == 0)
{
type_int = 0;
}
else
{
type_int = map.at("buildable").asInt();
}
if (1 == type_int)
{
return true;
}
return false;
}
void TutorialScene::addTower(Point pos)
{
DataModel *m = DataModel::getModel();
Tower *target = NULL ;
Point towerLoc = this->tileCoordForPosition(pos);
int tileGid = this->_background->tileGIDAt(towerLoc);
Value props = this->_tileMap->propertiesForGID(tileGid);
ValueMap map = props.asValueMap();
int type_int = map.at("buildable").asInt();
if (1 == type_int)
{
target = MachineGunTower::tower();
target->setPosition(ccp((towerLoc.x * 32) + 16, this->_tileMap->getContentSize().height - (towerLoc.y * 32) - 16));
this->addChild(target,1);
target->setTag(1);
m->towers.pushBack(target);
}
else
{
log("Tile Not Buildable");
}
}
```
检测属性和其相应的值。
Point TutorialScene::boundLayerPos(Point newPos)
{
Size winSize = CCDirector::getInstance()->getWinSize();
Point retval = newPos;
retval.x = MIN(retval.x, 0);
retval.x = MAX(retval.x, _tileMap->getContentSize().width + winSize.width);
retval.y = MIN(0, retval.y);
retval.y = MAX(_tileMap->getContentSize().height + winSize.height, retval.y);
return retval;
}
```
这个功能决定了你能拖拽你的Tower的最大范围。
看看结果如何:
未完待续~(一日一更)
Next:http://www.cocoachina.com/bbs/read.php?tid=203644




