在“入门尝试(四)”中已经对关于玩家的控制做了一个简单的分析。具体涉及到的点不再重复。直接上代码。
一、触屏的实现
1、分析
我希望这个游戏是能够在手机上玩,所以自然我需要实现触屏的效果。初步进行了有关触屏的了解,说需要重写ccTouchBegan、ccTouchMoved、ccTouchEnded等函数。因考虑程序处理过程中的一些传参问题,我采用利用构造函数传递相关参数,并在构造函数中注册触屏事件等。不多说,代码搞上先。
2、实现
(1)添加类MoveController,该类主要实现对棋子移动的控制等。
(2)在MoveController.h头文件中,添加类继承关系、构造函数、触屏相关的函数以及一些变量。其如下:
class MoveController:public CCLayer //因该类有触屏的效果,所以需要继承CCLayer类
public:
MoveController(void);
MoveController(CCTMXTiledMap *map);
~MoveController(void);
private:
/* 触屏事件 */
virtual void registerWithTouchDispatcher();
virtual bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent);
virtual void ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent);
virtual void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent);
private:
CCTMXTiledMap *map;
(3)在MoveController.cpp中,实现其对应函数。在以上定义中,我们暂时先不管触屏的效果。其先写完这些函数,然后再去添加内容。其实现如下:
首先,构造函数:
MoveController::MoveController(CCTMXTiledMap *map)
{
this->map = map;
this->setTouchEnabled(true); //设置是否可以触屏
registerWithTouchDispatcher(); //注册触屏事件
}
然后相关的触屏函数:
void MoveController::registerWithTouchDispatcher()
{
CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, 0, true); //注册触屏事件
}
bool MoveController::ccTouchBegan( CCTouch *pTouch, CCEvent *pEvent )
{
return true;
}
void MoveController::ccTouchMoved( CCTouch *pTouch, CCEvent *pEvent )
{
}
void MoveController::ccTouchEnded( CCTouch *pTouch, CCEvent *pEvent )
{
}
现在,你在ccTouchMoved或者ccTouchEnded函数内部加入你希望响应的操作,然后点击下屏幕瞅瞅是否已经可以有触屏的效果了。
二、图片放大提示效果的实现
1、分析
该效果是希望实现点击要移动的精灵,该精灵会放大,提示玩家正在操作的棋子。再次点击该棋子,图片回复原装,认定为取消对该棋子的选择。该效果的实现,我就是重新做了一张比原图片大的图片,当用户点击屏幕时,我获取用户点击的相关数据(该数据定义在_TouchedInfo这个结构体中,详细定义可参考下文),根据这些数据加载一张新图还是撤销这张新图。
2、实现
(1)在MoveController.h头文件中添加存储点击屏幕的信息的结构体:
/*存储点击的点的相关数据*/
typedef struct _TouchedInfo
{
int firstToucheLocation; //第一次点击时在坐标数组中对应的位置(-1表示空白, 1-21表示对应的坐标下标点)
int currentActiveChessmanCount; //当前活动的棋子数量,0或者1
int secondToucheLocation; //第二次点击时的位置(-1表示点击空白,0表示点击当前活动的棋子,1-21表示点击活动棋子要跳达的位置)
_TouchedInfo()
{
firstToucheLocation = -1;
currentActiveChessmanCount = 0;
secondToucheLocation = -1; //默认点击都是空白位置
}
}*_pTouchedInfo;
(2)在MoveController.h头文件中添加私有变量:
_TouchedInfo touchedInfo; //存储点击的点的相关数据
CCSprite *touchedSprite; //玩家点击精灵之后放大后的图片
(3)MoveController这个类肯定会用到有关其每个坐标点的信息,所以将Chessman中定义的变量pointsInfo作为一个外部变量操作。故在MoveController.cpp文件中添加头文件和变量:
#include "Common.h"
extern struct _PointInfo pointsInfo[21]; //用于存储棋盘中21个坐标点的数组
(4)获取点击屏幕的数据,如是否点击了精灵等,在MoveController.h文件中添加函数声明:
/*
** 功能: 根据传入的点击的点的坐标的信息,存储到变量touchedInfo中
** touchLocation: 点击的点的坐标
** playerIsRed: 当前点击的玩家是否为红方,其操作方(如红方)点击对方(如蓝方)的精灵图片也被认定为点击空白
*/
void GetTouchedPointInfo(CCPoint touchLocation, bool playerIsRed);
/*
** 功能: 获取点击的精灵的大小区域
*/
static CCRect GetRect(CCSprite* pNode);
在MoveController.cpp中实现其声明:
void MoveController::GetTouchedPointInfo(CCPoint touchLocation, bool playerIsRed)
{
if(playerIsRed == true) //如果玩家此时操作
{
/***********判断当前是否有活动棋子******************/
if(touchedInfo.currentActiveChessmanCount == 0) //如果当前活动的棋子个数为0,即玩家处于选择将要移动的棋子的状态
{
touchedInfo.firstToucheLocation = -1; //先假设其点击的位置是空白的地方
/************判断点击的为空白还是棋子精灵********/
for (int i = 0; i < 21; i++)
{
if(pointsInfo[i].currentSprite != NULL && pointsInfo[i].isRed == playerIsRed) //该结点存在精灵并且非对方精灵
{
if(CCRect::CCRectContainsPoint(GetRect(pointsInfo[i].currentSprite), touchLocation))
{
touchedInfo.firstToucheLocation = i + 1; //获取到了其点击的精灵对应下标值
break;
}
}
}
}
else //如果当前有活动棋子,即玩家处于选择将要移动的棋子放置于哪个方向的状态
{
/****判断点击的位置是否为空白、当前活动棋子、下一个可达位置*****/
touchedInfo.secondToucheLocation = -1; //先假设其为空白位置
if(CCRect::CCRectContainsPoint(GetRect(pointsInfo[touchedInfo.firstToucheLocation - 1].currentSprite), touchLocation)) //根据firstToucheLocation,判断点击的是否为当前活动棋子
{
touchedInfo.secondToucheLocation = 0; //如果点击的为活动棋子
}
else //如果点击的不是活动的棋子
{
for (int j = 0; j < 4; j++) //判断是否为可以走下一步的棋子
{
if(CCRect::CCRectContainsPoint(GetRect(touchedPromptImgs[j].touchedSprite), touchLocation))
{
touchedInfo.secondToucheLocation = touchedPromptImgs[j].pointLocation; //如果点击的为下一步可达的棋子
break;
}
}
}
}
}
else
{
touchedInfo.currentActiveChessmanCount = 0;
touchedInfo.firstToucheLocation = -1;
touchedInfo.secondToucheLocation = -1;
}
}
CCRect MoveController::GetRect(CCSprite* pNode)
{
CCRect rc;
rc.origin = pNode->getPosition();
rc.size = pNode->getContentSize();
rc.origin.x -= rc.size.width*0.5;
rc.origin.y -= rc.size.height*0.5;
return rc;
}
(5)根据获取的触屏数据,执行这些数据。因考虑后面的效果,此处实现暂时不写出来,在说完了怎样显示可下位置的提示图片之后一起完成。
三、可下位置图片提示的实现
1、分析
此处就是希望实现(四)中说的2.1.2说的效果。在这个的实现中,自然可以类似于放大图片效果提示一样,加载以及删除提示图片。但是因为考虑到获取坐标的问题,当提示位置如果没有图片不好利用上面已经写出的函数GetRect来判断点击是否在精灵位置上,所以,我采用了在程序初始化的时候初始了四张(最多四个可提示位置)提示图片,先设置位置在(-20, -20)不可见的位置,然后当某个坐标需要提示时,则移动到相应位置。提示完毕后,又移动到(-20, -20)去隐藏。
2、实现
(1)首先在Common.h中定义了一个结构体存放相应提示图片的信息:
typedef struct _PromptImage
{
int pointLocation; //对应的提示图片暂时显示的位置,-1表示不在棋盘21个坐标点上
CCSprite *touchedSprite; //点击之后出现的提示精灵图片对象
_PromptImage()
{
pointLocation = -1;
touchedSprite = NULL;
}
}*_pPromptImage;
(2)回到Chessman.h头文件,添加函数声明:
/*
** 功能: 初始化提示图片
*/
void InitPromptImg(CCTMXTiledMap *map);
在Chessman.cpp中实现函数功能:
void Chessman::InitPromptImg(CCTMXTiledMap *map)
{
for (int i = 0; i < 4; i++)
{
CCSprite *sprite = CCSprite::spriteWithFile("image/prompt.png");
sprite->setPosition(ccp(-20, -20));
map->addChild(sprite);
touchedPromptImgs[i].touchedSprite = sprite;
}
}
为了保存其图片,也在Chessman.cpp中定义了全局变量,(便于在MoveController中做外部变量)
struct _PromptImage touchedPromptImgs[4]; //点击之后出现的四张提示图片
(3)表示头有点晕了,不废话了,直接加代码。在MoveController.h中添加如下声明:
/*
** 功能: 执行点击后的命令
*/
void ExecuteTouchedCommond();
/*
** 功能: 显示提示的图片
** location: 显示图片的位置
*/
void ShowPromptImg(int location);
/*
** 功能: 加入点击棋子之后放大的图片
*/
CCSprite * CreateTouchedSprite(char *path, CCPoint cp);
/*
** 功能: 将四张提示图片隐藏到看不到的坐标点
*/
void RecoverPromptImgsLocation();
/*
** 功能: 将处于firstTouchedLocation的棋子移动到secondTouchedLocation位置
*/
void RemoveOneChessmanSprite(int firstTouchedLocation, int secondTouchedLocation);
/*
** 功能: 在location添加一张棋子精灵的图片
*/
void SetOnePointData(int location, char *path, bool isRed);
(4)已经将其图片移动的代码也敲进去了,在MoveController.cpp中添加如下代码:
extern struct _PromptImage touchedPromptImgs[4]; //点击之后出现的四张提示图片
void MoveController::ExecuteTouchedCommond()
{
if(0 == touchedInfo.currentActiveChessmanCount) //第一次有效点击
{
if(touchedInfo.firstToucheLocation > 0) //点击了精灵
{
touchedInfo.currentActiveChessmanCount = 1;
ShowPromptImg(touchedInfo.firstToucheLocation); //设置点击的棋子周围设置为可移动的图片
touchedSprite = CreateTouchedSprite("image/redTouched.png",
ccp(pointsInfo[touchedInfo.firstToucheLocation - 1].currentSprite->getPositionX(),
pointsInfo[touchedInfo.firstToucheLocation - 1].currentSprite->getPositionY()));
}
}
else
{
if(touchedInfo.secondToucheLocation == -1) //点击空白,不执行代码
{
}
else if(0 == touchedInfo.secondToucheLocation) //其重复点击一个棋子
{
touchedInfo.currentActiveChessmanCount = 0;
touchedSprite->removeFromParentAndCleanup(true);
RecoverPromptImgsLocation();
}
else
{
//点击的是可以下一步可以走的
touchedInfo.currentActiveChessmanCount = 0;
touchedSprite->removeFromParentAndCleanup(true);
RecoverPromptImgsLocation();
RemoveOneChessmanSprite(touchedInfo.firstToucheLocation, touchedInfo.secondToucheLocation);
// playerIsRed = false;
}
}
}
void MoveController::ShowPromptImg(int location)
{
location = location - 1;
for (int i = 0; i < 4; i++)
{
int nearPoint = pointsInfo[location].nearLocations[i] - 1;
if( nearPoint > 0)
{
//表明周围还有邻节点
if(pointsInfo[nearPoint].isNotEmpty == false)
{
//表明那个节点为NULL
touchedPromptImgs[i].touchedSprite->setPosition(pointsInfo[nearPoint].currentPoint);
touchedPromptImgs[i].pointLocation = nearPoint + 1;
}
}
}
}
CCSprite * MoveController::CreateTouchedSprite(char *path, CCPoint cp)
{
CCSprite *playerSprite = CCSprite::create(path);
//playerSprite->setScale(0.2);//缩放
playerSprite->setPosition(cp);
map->addChild(playerSprite);
return playerSprite;
}
void MoveController::RecoverPromptImgsLocation()
{
for (int i = 0; i < 4; i++)
{
touchedPromptImgs[i].touchedSprite->setPosition(ccp(-20, -20));
touchedPromptImgs[i].pointLocation = -1;
}
}
void MoveController::RemoveOneChessmanSprite(int firstTouchedLocation, int secondTouchedLocation)
{
pointsInfo[firstTouchedLocation - 1].currentSprite->removeFromParentAndCleanup(true);
pointsInfo[firstTouchedLocation - 1].currentSprite = NULL;
pointsInfo[firstTouchedLocation - 1].isNotEmpty = false;
if(pointsInfo[firstTouchedLocation - 1].isRed == true)
{
SetOnePointData(secondTouchedLocation, "image/red.png", true);
}
else
{
SetOnePointData(secondTouchedLocation, "image/blue.png", false);
}
}
void MoveController::SetOnePointData(int location, char *path, bool isRed)
{
location = location - 1;
CCSprite *playerSprite = CCSprite::create(path);
//playerSprite->setScale(0.2);//缩放
playerSprite->setPosition(pointsInfo[location].currentPoint);
map->addChild(playerSprite);
//设置参数
pointsInfo[location].currentSprite = playerSprite;
pointsInfo[location].isNotEmpty = true;
pointsInfo[location].isRed = isRed;
}
四、效果执行图