设为首页 收藏本站 662p开源网
开启辅助访问 切换到宽版

QQ登录

只需一步,快速开始

新浪微博登陆

只需一步, 快速开始

切换风格 立即注册 找回密码

安卓教程网

发表于 2014-5-24 09:40:23 | 显示全部楼层 |阅读模式
更多
出来,分别有每个级别提供的初始短语TileView,和目标短语TargetView。初步接触了cocos2d-x的基本概念和基础用法。这篇博客将会基本实现游戏的逻辑,完成游戏的主体部分。采用以下步骤:
使TileView可拖动
捕获TileView停止移动的事件
分析TileView是否放在正确的位置上
创建与原来Layer区分的层,放置按钮菜单和分数等等。
添加计时和分数
现在开始,继续cocos2d-x之道!
1) 拖放TileView
在TileView的initWithLetter函数中,其实还有一部分工作没完成。现在要实现拖放效果,就得让TileView处理手势事件,在函数的最后添加下面代码
CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(tile,0,true);通过CCDirector类,添加TileView为手势处理代理。要处理手势事件,TileView还需要继承CCTargetedTouchDelegate,如代码所示class TileView: public CCNode, public CCTargetedTouchDelegate然后在类里添加以下变量int xOffset;  int yOffset;  bool isControl;以及重写CCTargetedTouchDelegate继承来的函数bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent);  void ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent);  void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent);  void ccTouchCancelled(CCTouch *pTouch, CCEvent *pEvent);ccTouchBegan是点击开始,ccTouchMoved是手指在屏幕上移动,ccTouchEnded是手指离开屏幕以及ccTouchCancelled是被其他情况打断
实现这几个重要函数前,先把isControl初始化,同样是在initWithLetter的最后添加
tile->isControl = false;这个变量的作用在后面再解释,先把非常重要的手势事件给实现bool TileView::ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent){  if(!containTouchLocation(pTouch)){    return false;  }  isControl = true;  this->setZOrder(999);  CCPoint point = pTouch->getLocationInView();  CCPoint touchPoint = CCDirector::sharedDirector()->convertToGL(point);  this->xOffset = touchPoint.x - this->getPositionX();  this->yOffset = touchPoint.y - this->getPositionY();  return true;}void TileView::ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent){  if(!isControl){    return;  }  CCPoint point = pTouch->getLocationInView();  CCPoint touchPoint = CCDirector::sharedDirector()->convertToGL(point);  float x = touchPoint.x - this->xOffset;  float y = touchPoint.y - this->yOffset;  this->setPosition(ccp(x,y));}void TileView::ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent){  CCPoint point = pTouch->getLocationInView();  CCPoint touchPoint = CCDirector::sharedDirector()->convertToGL(point);  if(isControl){    isControl = false;  }        }void TileView::ccTouchCancelled(CCTouch *pTouch, CCEvent *pEvent){  ccTouchEnded(pTouch, pEvent);}一步步来解释,在ccTouchBegan中,containTouchLocation是判断手指的落点是否在TileView内,只有落点是在TileView内,才进行处理,否则直接返回。然后设置isControl为true,且把拖动的TileView置顶,使用xOffset和yOffset来记录触摸点坐标和TileView坐标的偏移量。在每个手势事件函数里,都有convertToGL这个函数,它的作用是把触摸的坐标转化成游戏世界里的坐标(非常重要)。
ccTouchMoved就是在拖动过程中,不断的改变TileView的坐标,记得把偏移量减掉。
ccTouchEnded就是手指离开屏幕了,把isControl重新设为false。可以看出,isControl的作用就是控制在整个手势过程中,标记TileView是否被拖动着。当ccTouchEnded或者ccTouchCancelled后,就需要复原。
运行一下,是否可以拖动了?神奇吧。

2) 放下TileView
拖动TileView后,总会有放下的一刻。当放下TileView时,我们就要判断它是否被放在正确的目标TargetView上。这些判断需要MainScene去处理,所以得建立一个TileDropDelegate
class TileDropDelegate{public:  virtual void dropToPoint(TileView * tile,cocos2d::CCPoint point) = 0;};用MainScene继承这个类class MainScene : public cocos2d::CCLayer, public TileDropDelegate
实现dropToPoint方法
void MainScene::dropToPoint(TileView * tile,CCPoint point){  TargetView * target = NULL;  CCObject * tv;  CCARRAY_FOREACH(pTargets,tv){    TargetView* pTarget = (TargetView*) tv;    if(pTarget->containPoint(point)){      target = pTarget;      break;    }  }}遍历存放TargetView的pTargets,检查是否点point在TargetView的frame里,如果发现TileView最后是落在TargetView上,则可以进行结果判断,继续在dropToPoint函数的尾部添加if(target != NULL){    if(target->getLetter() == tile->getLetter()){      // 1 单词匹配    }else{      // 2 单词不匹配    }  }1.检查拖动的TileView所代表的字母与TargetView的字母(不可见)是匹配
2.两个字母是不匹配的,需要进行后续工作
实现了回调函数,还得设置进去给TileView,在dealRandomAnagram中的 tile->randomize(); 这句后面添加
tile->setTileDropDelegate(this);记得在TileView类里添加setTileDropDelegate函数。
然后,就进行字母匹配后处理,需要将TileView覆盖到TargetView上,
void MainScene::placeTile(TileView * tile,TargetView * target){  tile->setMatch(true);  target->setMatch(true);  tile->removeTouchDelegate();  CCActionInterval * actionTo = CCMoveTo::create(0.35,ccp(target->getPositionX(),target->getPositionY()));  tile->runAction(actionTo);  tile->setRotation(0);  target->setVisible(false);}代码中把tile和target设置成已匹配,去掉tile的拖放代理,使用CCMoveTo把tile移动到target位置上,并摆正
在1 单词匹配后添加以下语句
placeTile(tile,target);在2 单词不匹配后添加tile->randomize();      CCActionInterval * actionTo = CCMoveTo::create(0.35,        ccp(tile->getPositionX() + Common::random(-20,20),        tile->getPositionY() + Common::random(-20,30)));      tile->runAction(actionTo);主要作用是把tile偏离target,表示两者不匹配,如图所示

有了检测匹配函数,就可以判断是否成功完成游戏。在MainScene中添加
void MainScene::checkForSuccess(){  CCObject * tv;  CCARRAY_FOREACH(pTargets,tv){    TargetView* pTarget = (TargetView*) tv;    if(pTarget->getIsMatch() == false){      return;    }  }    CCLog("Game Over!");}遍历target,如果全部都匹配了,表示游戏成功,在dropToPoint最后添加checkForSuccess();运行游戏,就会发现当所有字母都匹配了,就能打印出Game Over消息
3) 创建HUD层
这是一种游戏设计理念,就是把游戏一些界面上的东西分离到HUD层上,这层的内容主要作用是辅助游戏内容,比如倒计时,按钮,结束界面等等
首先,我们来创建倒计时,类名为StopWatchView,头文件如下
class StopWatchView : public CCLabelAtlas{public:  StopWatchView(){}  static StopWatchView * initView();  void setSecond(int seconds);  CREATE_FUNC(StopWatchView);};主体部分为setSecond是把秒数拆分成mm:ss形式void StopWatchView::setSecond(int second){  char strTime[50];  float minutes = second / 60;  minutes =  (minutes > 0.0) ? floor(minutes + 0.5) : ceil(minutes - 0.5);   int minu = (int)minutes;  int sec = second % 60;  sprintf(strTime," %02d:%02d",minu,sec);  this->setString(strTime);}下面创建HUDView,头文件如下class HUDView : public CCLayer{public:  // Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone  virtual bool init();    // implement the "static node()" method manually  CREATE_FUNC(HUDView);private:  StopWatchView * pWatch;  CounterPointView * pPointView;  CCMenuItemImage * pButton;  CCMenu * pMenu;  CCLabelTTF * pLabel;public:  friend class MainScene;};其中,CounterPointView, CCmenuItemImage, CCMenu, CCLabelTTF这些是以后使用。friend class MainScene是让MainScene可以访问HUDView的成员变量。
HUDView.cpp 内只有一个函数,如
bool HUDView::init(){  if(! CCLayer::init()){    return false;  }  pWatch = StopWatchView::initView();  pWatch->setSecond(0);  pWatch->setPosition(ccp(Common::getCameraWith()*0.5,    Common::getCameraHeight() * 0.95));  pWatch->setScale(0.7);  this->addChild(pWatch);      return true;}把StopWatchView添加上去。
然后需要把HUDView添加到MainScene中,所以在MainScene.h里添加
HUDView * pHUD;添加这个成员变量后,就在MainScene的init函数里,this->addChild(bg)这句后面添加pHUD = HUDView::create();this->addChild(pHUD);然后运行游戏,可以看到如图所示

使用艺术字体
StopWatchView * StopWatchView::initView(){  StopWatchView * p = (StopWatchView*)(CCLabelAtlas::create("11","fonts/tuffy_bold_italic-charMap.plist"));  return p;}加载艺术字体后,如图所示

4) 添加Level计时器
在MainScene.h中添加
int mTimeLeft;然后在MainScene 中 添加启动计时器,如void MainScene::startStopWatch(){  mTimeLeft = pLevel->mTimeToSovle;  pHUD->pWatch->setSecond(mTimeLeft);  schedule(schedule_selector(MainScene::downTime),1.0f);}使用schedule方法,每隔一秒执行一次downTime。有启动计时,就得停止计时void MainScene::stopStopWatch(){  unschedule(schedule_selector(MainScene::downTime));}unschedule后就不会再计时,下面实现downTime函数void MainScene::downTime(float dt){  mTimeLeft --;  pHUD->pWatch->setSecond(mTimeLeft);  if(mTimeLeft == 0){    stopStopWatch();  }}内容比较简单,就是实现mTimeLeft的递减和设置给TimeWatchView,并且在归0时停止计时
启动计时的地方在dealRandomAnagram函数中,在最后处添加
startStopWatch();如图所示,游戏开始时就进行倒计时

当然,游戏结束时别忘了要停止计时,在checkForSuccess的最后添加
stopStopWatch();5)添加计分
游戏中计分是很重要的设定,有了计分,才有成就感。下面就开始为游戏添加计分功能
新增Data类,用来存放分数
class Data : public CCObject{public:  Data();  virtual ~Data(){}  void setPoint(int point);  int getPoint();  void addPoint(int point);  void subtractPoint(int point);  private:  int mPoints;};函数的实现如下Data::Data(){  mPoints = 0;}void Data::setPoint(int point){  if(point > 0){    mPoints = point;  }else{    mPoints = 0;  }}int Data::getPoint(){  return mPoints;}void Data::addPoint(int point){  mPoints += point;}void Data::subtractPoint(int point){  mPoints -= point;}当玩家拖放TileView,放到正确的位置上后,就添加分数,如果放错,则扣分。
在MainScene.h中添加变量
Data * pData;
在MainScene的init函数中,申请变量
pData = new Data();
在dropToPoint中,如果匹配成功,则
pData->addPoint(pLevel->mPointPerTile);
如果匹配失败,则减一半的分
pData->subtractPoint(pLevel->mPointPerTile / 2);6)在HUD层添加分数显示
分数应该实时显示给玩家看,需要显示则需要使用Label,所以新增CounterPointView
class CounterPointView: public CCLabelAtlas{public:  static CounterPointView* initWithValue(int v);  void countTo(int p, float duration);  void updateValueBy(float dt);  void setValue(int v);  CREATE_FUNC(CounterPointView);private:  CounterPointView(){};  int value;  float mDelta;  int mEndValue;  float mValueDelta;};
value是指当前的分数值, mEndValue是新的分数值,下面先来初始化
CounterPointView* CounterPointView::initWithValue(int v){  CounterPointView * p = (CounterPointView*)(CCLabelAtlas::create("11","fonts/tuffy_bold_italic-charmap.plist"));  char strPoint[10];  sprintf(strPoint,"%d",v);  p->setString(strPoint);  p->value = v;  p->setScale(0.5);  return p;}CCLabelAtlas类是能快速渲染的类,用来做变化的数字最合适(FPS的数字就是使用了CCLabelAtlas)void CounterPointView::setValue(int v){  this->value = v;  char strPoint[10];  sprintf(strPoint,"%d",v);  setString(strPoint);}设置分数void CounterPointView::updateValueBy(float dt){  this->value += (int)mValueDelta;  if((int)mValueDelta > 0){    if(this->value > mEndValue){      this->value = mEndValue;      return;    }  }else{    if(this->value < mEndValue){      this->value = mEndValue;      return;    }  }  setValue(value);  schedule(schedule_selector(CounterPointView::updateValueBy),mDelta);}updateValueBy是递增(或递减)分数,每次都加上(或减去)mValueDelta,直到mEndValue,不然的话,每次都执行schedule函数void CounterPointView::countTo(int to, float duration){  this->mDelta = duration / (abs(to - value) + 1);  if(mDelta < 0.05) mDelta = (float)0.05;  mEndValue = to;  unscheduleAllSelectors();  if(to - this->value > 0){    mValueDelta = 1;    updateValueBy(1);  }else{    mValueDelta = -1;    updateValueBy(-1);  }}countTo函数是对外的接口,目的是在duration时间内达到to值,首先是计算间隔mDelta,然后是unscheduleAllSelectors,接着就开始递增(或递减)了。
在HUDView中添加变量CounterPointView后,在init函数中添加
pPointView = CounterPointView::initWithValue(0);pPointView->setColor(ccc3(97,25,9));pPointView->setPosition(ccp(Common::getCameraWith()*0.05,Common::getCameraHeight() * 0.9));this->addChild(pPointView);
则可以如图所示,看到分数

回到MainScene.cpp中,在 pData->addPoint(pLevel->mPointPerTile); 这句后面添加
pHUD->pPointView->countTo(pData->getPoint(),1.5);在 pData->subtractPoint(pLevel->mPointPerTile / 2 ); 这句后面添加pHUD->pPointView->countTo(pData->getPoint(),0.75);如图所示,当摆放正确后,分数递增,摆放错误则分数递减

7)结语
第二篇就到此结束了,因为时间实在是不够用,所以写得很慢而且很懒散,几乎是了了几句(掩面)。从文字上看,这实在不是一篇好教程,不过我觉得从中可以学习的是制作游戏过程的思路。
在开发过程中,不断的往主框架里添加新东西,然后让整个游戏变得充实丰满。主要的游戏逻辑在这篇已经完成了,剩下的就是一些优化方面的东西,还有就是添加一些炫的效果等。
使用cocos2d-x开发会很方便,也很有code的感觉,但是C++实在是门不容易驾驭的语言,要非常注意指针和内存方面的使用。不过也会收益匪浅,起码在code方面会提升。最后,放上AnagramPuzzle完整版的 代码 ,欢迎大家拍砖。

来自群组: 安卓新人开发群

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册 新浪微博登陆

x
  
发表于 2014-5-24 09:47:01 | 显示全部楼层
顶起出售广告位
发表于 2014-6-18 18:15:34 | 显示全部楼层
纯粹路过,没任何兴趣,仅仅是看在老用户份上回复一下
发表于 2014-7-12 00:32:22 | 显示全部楼层
看看如何先
发表于 2014-7-23 16:59:07 | 显示全部楼层
大人,此事必有蹊跷!

新浪微博达人勋

高级会员

1805

积分

602

威望

0

金钱

Rank: 6Rank: 6

积分
1805
发表于 2014-7-28 18:13:30 | 显示全部楼层
在撸一遍。。。

新浪微博达人勋

高级会员

1838

积分

607

威望

3

金钱

Rank: 6Rank: 6

积分
1838
发表于 2014-7-28 22:39:54 | 显示全部楼层
OMG!介是啥东东!!!
发表于 2014-8-27 11:43:32 | 显示全部楼层
顶起出售广告位

新浪微博达人勋

高级会员

897

积分

298

威望

3

金钱

Rank: 4

积分
897
发表于 2014-11-1 10:09:16 | 显示全部楼层
众里寻他千百度,蓦然回首在这里!
发表于 2014-11-5 09:46:31 | 显示全部楼层
回个帖子,下班咯~
您需要登录后才可以回帖 登录 | 立即注册 新浪微博登陆

本版积分规则

Archiver|手机版|小黑屋|安卓教程网 ( 粤ICP备12089784号  

GMT+8, 2020-2-21 07:54 , Processed in 1.272353 second(s), 59 queries .

Powered by Discuz X3.1

© 2001-2013 IOS教程网

快速回复 返回顶部 返回列表