要获取和选中棋子我们需要要在原来LayerGameMain基础上添加多三个成员变量和六个成员函数。几个函数和变量间依赖性比较高,大体实现功能分为:通过棋子id从数组获取棋子、通过点击选中棋子。
__Array *arrStone;
Sprite *selectBox;
int _selectedId;
Stone* getStoneFromArrById(int id);
int getStoneIdFromRowCol(int row, int col);
bool Screen2Plate(Point &point, int &row, int &col);
Point Plate2Screen(int row, int col);
void selectStone(Touch *pTouch);
这里先直接贴出LayerGameMain.h代码
#ifndef __LAYERGAMEMAIN_H__
#define __LAYERGAMEMAIN_H__
#include "cocos2d.h"
#include "Plate.h"
#include "Stone.h"
/*
cocos2d-x3.14.1版本将大部分类的前缀CC去掉了,不过有部分还是兼容,如果用旧的命名创建对应的类会有警告提示/错误提示
*/
USING_NS_CC;
class LayerGameMain : public Layer
{
public:
static Scene* createScene();
virtual bool init();
CREATE_FUNC(LayerGameMain);
//添加棋盘和棋子
void addPlate();//用于在LayerGameMain上添加棋盘层/精灵
void addStones();//用于在LayerGameMain上添加棋子层/精灵
//触摸事件回调函数
bool touchBeganCallBack(Touch *pTouch, Event *pEvent);
void touchEndedCallBack(Touch *pTouch, Event *pEvent);
//选中棋子相关
Sprite *selectBox;//选中款精灵
int _selectedId;//标记当前选中的棋子id,没有点中为-1
bool Screen2Plate(Point &point, int &row, int &col);//循环棋盘上所有点,通过Plate2Screen获取到棋子中心点,用引用输出中心点
Point Plate2Screen(int row, int col);//获取棋子中心点返回到世界坐标给Screen2Plate
void selectStone(Touch *pTouch);//选中棋子,将选中框精灵移动到选中的棋子上
//获取棋子相关
__Array *arrStone;//用于存放棋子
Stone* getStoneFromArrById(int id);//通过棋子id从arrStone获取对应棋子
int getStoneIdFromRowCol(int row, int col);//通过行列获取棋子id
};
#endif // !__LAYERGAMEMAIN_H__
获取棋子
想要获取棋子就需要先将它们放进一个数组中管理起来,然后通过一些函数方法从数组中取出我们想要的棋子。这里使用的是cocos的数组类,也可以用vector存储。
我们直接上对应代码,因为需要初始化数组,所以先贴出init函数
bool LayerGameMain::init()
{
if (!Layer::init())
{
return false;
}
arrStone = __Array::create();
arrStone->retain();//计数器+1,否则会被回收
selectBox = Sprite::create("selected.png");
selectBox->setVisible(false);
selectBox->setZOrder(1000);//设置显示优先级
this->addChild(selectBox);
this->addPlate();
this->addStones();
//cocos2d-x3.14.1触摸事件,创建监听
EventDispatcher* eventDispatcher = Director::getInstance()->getEventDispatcher();
auto listener = EventListenerTouchOneByOne::create();
listener->setEnabled(true);
listener->setSwallowTouches(true);
listener->onTouchBegan = CC_CALLBACK_2(LayerGameMain::touchBeganCallBack, this);
listener->onTouchEnded = CC_CALLBACK_2(LayerGameMain::touchEndedCallBack, this);
eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
return true;
}
通过id从数组获取棋子函数
Stone* LayerGameMain::getStoneFromArrById(int stoneId)
{
Stone *ret;
ret = nullptr;
if (stoneId > 0)
{
ret = (Stone *)arrStone->getObjectAtIndex(stoneId);
}
return ret;
}
有必要提一下,棋子数组初始化后并没有挂在渲染树上,一帧之后就会被回收,这里涉及到内存管理计数器问题,我们需要在初始化数组时让数组调用retain()函数使计数器+1。
选中棋子
当我们选中棋子时,我们希望看到棋子被选中后有所反馈,那么我们就需要一个选中框精灵重叠在棋子上面以得到我们想要的效果。那么要把选中框重叠在棋子上就必需先知道棋子的行和列,想得到棋子的行列就需要先知道被点击棋子的中心点,想知道棋子的中心点就必需先知道玩家触摸点的位置。
这样我们就可以通过倒着推过去来写我们需要的函数。玩家触摸点的位置可以通过cocos的触摸事件获得,棋子中心点可以通过遍历棋盘上所有的点并通过触点与棋盘点的距离是否小于半径判断是否选中棋盘上的点,如果棋盘上的点存在棋子,那么该点即为棋子中心点,在获得棋子中心点后可以通过棋子自身属性获取棋子的行列位置,通过棋子的行列位置我们可以将其换算成世界坐标,最后这个世界坐标就是我们选中框的位置。
将棋子的相对棋盘坐标转换为世界坐标
Point LayerGameMain::Plate2Screen(int row, int col)//该函数与Stone的getPositionFromPlate类似
{
Point ret = Point(Stone::_offx + col*Stone::_d, Stone::_offy + row*Stone::_d);
return ret;
}
遍历棋盘上所有的点并通过触点与棋盘点的距离判断是否选中该点上的棋子,如果棋盘点上有棋子返回true
bool LayerGameMain::Screen2Plate(Point &point, int &row, int &col)
{
int distance = Stone::_d/2 * Stone::_d/2;//以半径作为距离
for (row = 0; row <= 9; row++)
{
for (col = 0; col <= 8; col++)
{
Point ptCenter = Plate2Screen(row, col);
if (distance > ptCenter.getDistanceSq(point))//获取点距的平方并比较,如果小于半径平方则为能选中中心点上的棋子
{
return true;
}
}
}
return false;
}
通过棋子行列获取棋子id,当行列上没有棋子时返回-1
int LayerGameMain::getStoneIdFromRowCol(int row, int col)
{
int ret = -1;
Ref *obj;
CCARRAY_FOREACH(arrStone,obj)
{
Stone *stone = (Stone *)obj;
if (stone->_row == row && stone->_col == col)
{
ret = stone->_id;
return ret;
}
}
return ret;
}
选棋操作
void LayerGameMain::selectStone(Touch *pTouch)
{
Point point = pTouch->getLocation();
int row, col;
if (!Screen2Plate(point, row, col))//点中范围不在棋盘上直接返回
{
selectBox->setVisible(false);
return;
}
int clickedid = getStoneIdFromRowCol(row, col);
if (clickedid < 0)//没选中棋子直接返回
{
selectBox->setVisible(false);
return;
}
Stone *clickedStone = getStoneFromArrById(clickedid);
selectBox->setPosition(Plate2Screen(row, col));
selectBox->setVisible(true);
_selectedId = clickedid;//记录下棋子id
}
最后贴出修改后LayerGameMain的代码,头文件在上面已经贴出,这里就不再贴出了。
LayerGameMain.cpp
#include "LayerGameMain.h"
Scene* LayerGameMain::createScene()
{
auto ret = Scene::create();
auto layer = LayerGameMain::create();
ret->addChild(layer);
return ret;
}
bool LayerGameMain::init()
{
if (!Layer::init())
{
return false;
}
arrStone = __Array::create();
arrStone->retain();//计数器+1,否则会被回收
selectBox = Sprite::create("selected.png");
selectBox->setVisible(false);
selectBox->setZOrder(1000);//设置显示优先级
this->addChild(selectBox);
this->addPlate();
this->addStones();
//cocos2d-x3.14.1触摸事件,创建监听
EventDispatcher* eventDispatcher = Director::getInstance()->getEventDispatcher();
auto listener = EventListenerTouchOneByOne::create();
listener->setEnabled(true);
listener->setSwallowTouches(true);
listener->onTouchBegan = CC_CALLBACK_2(LayerGameMain::touchBeganCallBack, this);
listener->onTouchEnded = CC_CALLBACK_2(LayerGameMain::touchEndedCallBack, this);
eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
return true;
}
void LayerGameMain::addPlate()
{
Plate *plate = Plate::create();
this->addChild(plate);
}
void LayerGameMain::addStones()
{
int i = 0;
Stone *stone;
for (i = 0; i < 32; i++)
{
stone = Stone::create(i);
arrStone->addObject(stone);//将棋子添加进数组
this->addChild(stone);
}
}
Stone* LayerGameMain::getStoneFromArrById(int stoneId)
{
Stone *ret;
ret = nullptr;
if (stoneId > 0)
{
ret = (Stone *)arrStone->getObjectAtIndex(stoneId);
}
return ret;
}
bool LayerGameMain::touchBeganCallBack(Touch *pTouch, Event *pEvent)
{
return true;
}
void LayerGameMain::touchEndedCallBack(Touch *pTouch, Event *pEvent)
{
selectStone(pTouch);
}
bool LayerGameMain::Screen2Plate(Point &point, int &row, int &col)
{
int distance = Stone::_d/2 * Stone::_d/2;//以半径作为距离
for (row = 0; row <= 9; row++)
{
for (col = 0; col <= 8; col++)
{
Point ptCenter = Plate2Screen(row, col);
if (distance > ptCenter.getDistanceSq(point))//获取点距的平方并比较,如果小于半径平方则为能选中中心点上的棋子
{
return true;
}
}
}
return false;
}
Point LayerGameMain::Plate2Screen(int row, int col)//该函数与Stone的getPositionFromPlate类似
{
Point ret = Point(Stone::_offx + col*Stone::_d, Stone::_offy + row*Stone::_d);
return ret;
}
int LayerGameMain::getStoneIdFromRowCol(int row, int col)
{
int ret = -1;
Ref *obj;
CCARRAY_FOREACH(arrStone,obj)
{
Stone *stone = (Stone *)obj;
if (stone->_row == row && stone->_col == col)
{
ret = stone->_id;
return ret;
}
}
return ret;
}
void LayerGameMain::selectStone(Touch *pTouch)
{
Point point = pTouch->getLocation();
int row, col;
if (!Screen2Plate(point, row, col))//点中范围不在棋盘上直接返回
{
selectBox->setVisible(false);
return;
}
int clickedid = getStoneIdFromRowCol(row, col);
if (clickedid < 0)//没选中棋子直接返回
{
selectBox->setVisible(false);
return;
}
Stone *clickedStone = getStoneFromArrById(clickedid);
selectBox->setPosition(Plate2Screen(row, col));
selectBox->setVisible(true);
_selectedId = clickedid;//记录下棋子id
}
运行效果: