您的位置:首页 > 移动开发 > Cocos引擎

知易游戏开发教程cocos2d-x移植版004

2012-11-28 21:27 387 查看
我们知道cocos2d-x是cocos2d-iphone项目的C++移植版本,它拥有跨平台的特性。同时cocos2d-x与cocos2d-iphone保持着高度地同步,这也就从根本上限制住它是一个为手机、平板等设备量身定做的游戏引擎。而对Win32等平台的支持,仅仅是为了方便开发与调试。

如果你正准备开发PC版的游戏,使用那种专为PC设计的引擎才是你正确的选择。虽然在一篇介绍cocos2d-x的文章里出现这样的句子有些扎眼,但我想有些事情还是讲明白的好,任何人的时间都不应该被浪费。

如果你也像我一样是从PC端开发加入到cocos2d-x中的,那么有很多概念是需要注意的。

比如,在PC上我们经常使用的鼠标在手机和平板上变成了手指头。这当然不单单是介质的转变,发生变化的还包括使用习惯。

消失的鼠标经过状态

大家都知道,在PC上,一个按钮通常包含4种状态——普通、鼠标经过、鼠标按下、禁用,而在手机和平板上,一个按钮通常只有3种状态——普通、按下、禁用。

这是很容易理解的。

第一,不管鼠标放在什么地方,它终究是脱离不了显示器屏幕的,而让一个人时刻把手指按在触摸屏上,要困难得多。

第二,鼠标是有按键的,而人手没有按键,想实现移动而脱离点击是有难度的。

所以在手机和平板上,传统意义的鼠标经过状态几乎不存在。

神奇的多点触摸

对于手机和平板用户来说,多点触屏稀松平常。虽然PC也有支持多点输入的外设,但支持这一特性的软件却不常见。对于用惯了PC的人来说,还是鼠标的单点输入更容易接受。

所谓习惯嘛,就是习以为常,时间久了,也就是那么回事儿了。今天我们就来认识认识这个Touch(触摸)。


cocos2d-x的触摸事件处理

在cocos2d-x中,触摸是一种重要的交互手段。虽然我们之前没有明确指出这个触摸事件,但是我们一刻也没有离开它。每一个Menu的触发,其实就是一次触摸。


CCStandardTouchDelegate

准确来说,我们与Touch有过一面之缘。在第2篇中,我们实现了一个点击屏幕后,飞船移动过去的功能。

void GameLayer::ccTouchesEnded(CCSet *pTouches, CCEvent *pEvent)

 {

     CCTouch *touch = (CCTouch *)pTouches->anyObject();

     // 获得触摸点坐标

     CCPoint location = touch->locationInView(touch->view());

     CCPoint convertedLocation = CCDirector::sharedDirector()->convertToGL(location);

     // 让飞船在1秒钟内移动过去

     flight->runAction(CCMoveTo::actionWithDuration(1.0f, ccp(convertedLocation.x, convertedLocation.y)));

 }


但是,想要上面的代码生效,还必须在初始化中开启触摸事件。

1 bool GameLayer::init(void)
2 {
3     ...
4     // accept touch now!
5     setIsTouchEnabled(true);
6     ...
7 }


跟进去看看开启触摸事件的实现。

void CCLayer::setIsTouchEnabled(bool enabled)

 {

     if (m_bIsTouchEnabled != enabled)

     {

         m_bIsTouchEnabled = enabled;

         if (m_bIsRunning)

         {

             if (enabled)

             {

                 this->registerWithTouchDispatcher();

             }

             else

             {

                 // have problems?

                 CCTouchDispatcher::sharedDispatcher()->removeDelegate(this);

             }

         }

     }

 }

 

 void CCLayer::registerWithTouchDispatcher()

 {

     CCTouchDispatcher::sharedDispatcher()->addStandardDelegate(this,0);

 }


上面的代码通过CCTouchDispatcher添加标准代理实现开启触摸的功能。

所谓标准代理就是指你可以接收到所有的消息——开始(Began)、移动(Moved)、结束(Ended)、取消(Cancelled)。触摸点的信息是由CCSet表示一个集合。

与高权限相随的是高灵活性,而要获得高灵活性的代价就是你必须自己来编写额外的代码。

灵活性太高,反而不知从何开始,干脆就不讲了,以后有实际需要的时候再说。不过有一点必须提一下,标准代理类型与苹果的CocoaTouch框架基本一致,所以CocoaTouch的大部分方案在cocos2d-x中也是可行的。


CCTargetedTouchDelegate

相较标准代理来说,目标代理的功能自然是弱一些,但使用起来要简便得多。

使用目标代理有两个显著的好处:

1.你不用跟CCSet打交道,调度程序会替你做好拆分,每次使用时你会得到单一的Touch事件。

2.你可以通过让ccTouchBegan返回true来“认领”一个触摸事件。被“认领”的触摸事件只会被分发给“认领”它的代理对象。这样你就可以从多点触摸的检测苦海中解脱出来。

我们来实战演练一下。假设我们接到一个任务,要求在屏幕上显示一个精灵,这个精灵是一个循环播放的序列帧动画。当我们按在精灵上的时候可以拖动它,如果点在精灵外面,那就不能拖动。

首先分析下我们接到的任务。显示一个循环播放的序列帧动画精灵,这个没有难点,我们之前已经做过多次了。按在精灵上的时候可以拖动它,按在外面就不能拖动,这一条如果使用标准代理对象实现,必然要添加大量的判断和状态,显然目标代理是我们应该优先考虑的。

因此,我们需要的是一个符合目标代理的精灵,所以这是一个多重继承的类,它有2个父类——CCSprite和CCTargetedTouchDelegate。

1 class KillingLight : public CCSprite, public CCTargetedTouchDelegate
2 {
3 public:
4     KillingLight(void);
5     virtual ~KillingLight(void);
6 }


要接收触摸事件必须先进行注册,当不再需要的时候要进行反注册。我们重写CCNode的onEnter和onExit函数来完成触摸事件的注册与反注册。

void KillingLight::onEnter(void)

 {

     CCSprite::onEnter();

     // 我们希望独占被“认领”的触摸事件,所以第3个参数传入true

     CCTouchDispatcher::sharedDispatcher()->addTargetedDelegate(this, 0, true);

 }

 

 void KillingLight::onExit(void)

 {

     CCTouchDispatcher::sharedDispatcher()->removeDelegate(this);

     CCSprite::onExit();

 }


在目标代理对象中,只有先“认领”触摸事件,才能使用。所以我们要重写CCTargetedTouchDelegate的ccTouchBegan函数。

因为任务要求只有点在精灵上面时才能拖动,所以我们需要增加判断触摸点是否在精灵上的判断。

1 CCRect KillingLight::rect(void)
 2 {
 3     // 后面的比较是以锚点为标准的
 4     const CCSize& s = getTextureRect().size;
 5     const CCPoint& p = this->getAnchorPointInPixels();
 6     return CCRectMake(-p.x, -p.y, s.width, s.height);
 7 }
 8 
 9 bool KillingLight::containsTouchLocation(CCTouch *touch)
10 {
11     return CCRect::CCRectContainsPoint(rect(), convertTouchToNodeSpaceAR(touch));
12 }
13 
14 bool KillingLight::ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent)
15 {
16     if (! containsTouchLocation(pTouch))
17         return false;
18 
19     return true;
20 }


接着,我们重写CCTargetedTouchDelegate的ccTouchMoved函数,来实现拖动功能。

void KillingLight::ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent)

 {

     CCPoint touchPoint = pTouch->locationInView(pTouch->view());

     touchPoint = CCDirector::sharedDirector()->convertToGL(touchPoint);

     setPosition(touchPoint);

 }


为了便于使用,我们再添加一个静态成员函数KillingLightWithBatchNode来创建KillingLight对象。

1 KillingLight* KillingLight::KillingLightWithBatchNode(cocos2d::CCSpriteBatchNode *batchNode, const cocos2d::CCRect& rect)
 2 {
 3     KillingLight *pobSprite = new KillingLight();
 4     if (pobSprite && pobSprite->initWithBatchNode(batchNode, rect))
 5     {
 6         pobSprite->autorelease();
 7         return pobSprite;
 8     }
 9     CC_SAFE_DELETE(pobSprite);
10     return NULL;
11 }


这样,一个支持点击拖动的精灵类就完成了,你可以像使用CCSprite那样使用它。



如果你希望让这个类再完美一些,比如只响应第一个按上的触摸事件以免多点抢一个精灵等等,你可以参考TouchesTest中的Paddle类。

代码下载:

http://dl.dbank.com/c0jczeqp57

在处理触摸事件的时候,最需要注意的是坐标转换。这包括很多层面,是屏幕坐标系还是GL坐标系,是世界坐标还是局部坐标,还有坐标是相对哪个点计算的等等。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: