【cocos2D-x学习】3.坐标系

【目标】:调试通过《cocos2D-x权威指南》中实例3.1.4:通过节点控制屏幕中的全体渲染对象

【参考】:

《cocos2D-x权威指南》 (也就是我现在正在学的书,纯入门级不解释)

cocos2D-x源码: cocos2dx\base_nodes\CCNode.cpp

【第一部分】:helloWorld模板的超入门级架构解读心得

作为超级菜鸟,刚拿到模板代码的时候,研究了好久才找到在哪里动笔改。实际上模板架构还是很简单的:

1) main.cpp: _tWinMain 就是 win32程序入口不解释。

对比一般的WIN32程序流程:注册wndclass -> CreateWindow & ShowWindow -> 消息队列循环

可以看到这里的流程如下: CCEGLView::sharedOpenGLView(); - > CCApplication::sharedApplication()->run();

很显然,CCEGLView::sharedOpenGLView() 是创建窗口,而 CCApplication::sharedApplication()->run(); 完成绘制和消息队列功能

2) AppDelegate: tWinMain的开头会调用 AppDelegate app; 构造一个 AppDelegate 的实例,而 AppDelegate 是继承与 CCApplication 的,在CCApplication的初始化代码中,可以看到

CCApplication::CCApplication()
: m_hInstance(NULL)
, m_hAccelTable(NULL)
{
    m_hInstance    = GetModuleHandle(NULL);
    m_nAnimationInterval.QuadPart = 0;
    CC_ASSERT(! sm_pSharedApplication);
    sm_pSharedApplication = this;
}

将全局实例 sm_pSharedApplication 设置为了 AppDelegate app,所以后面调用 CCApplication::sharedApplication()->run() 实际上都是调用的 AppDelegate 的方法。

3) HelloWorldScene : AppDelegate::applicationDidFinishLaunching() 中构造并启动。可以参考其中的调用

【第二部分】:坐标系

1、 源程序无比简单,就是在helloWorld模板下,创建一个父节点CCNode anode,将场景中所有节点作为其子节点添加,然后通过对父节点的操作来达到操纵子节点的目的。

和初始代码相比,只需要将原有的 this->addChild(XXX, z); 改为 aNode->addChild(XXX, z);

2、注意到3.1.4.4 中对整体旋转的处理:

aNode->setPosition(ccp(200, 200)); 
aNode->setRotation(90.0);

只能围绕默认锚点(0,0)进行旋转,比较SB。

既然一个sprite可以通过设置锚点来实现中心旋转,那么作为父节点的CCNode为什么不可以呢?

3、坐标系

参考

http://www.cnblogs.com/pengyingh/articles/2433081.html

http://www.2cto.com/kf/201208/148630.html

http://article.ityran.com/archives/3367

这里有这几个概念,只说明我这个初学者的看法,很可能不正确。

1)世界坐标系:即原 WINDOWS 中的 MM_TEXT。原点左上,正方向为右下。根据参考文档中所述,触摸等事件使用的是该坐标系。

2)GL坐标系:即OPENGL坐标系,右手坐标系,原点左下,正方向为右上前。

【TODO:需要澄清的事实:坐标系的单位?】

在WINDOW上既然是MM_TEXT,那么就应该是以PX为单位了(实测确实如此),不过不知道android和IOS上是以什么为单位的。根据我自己的DEFY的显示情况,应该也是以PX为单位。

(PS:关于android PX DPI DENSITY DIP 可以参考 http://blog.csdn.net/zhuojiuyihu/article/details/7292669

3)节点坐标系:父节点提供给子节点的相对坐标系,自然场景可以参考物理学中的相对坐标系。其情况又可以分为两种:

3.1: 原点和正方向(不考虑锚点)

aNode->convertToWorldSpace(testPoint);
aNode->convertToNodeSpace(testPoint);

不考虑父节点 aNode 的锚点,以aNode的“原设定”左下为原点,“原设定”的右上为正方向。

注意这里指的是“原设定”,这是因为父节点本身可能会旋转,如果发生旋转的话,节点坐标系也会随着旋转。

3.2: 原点和正方向(考虑锚点)

aNode->convertToWorldSpaceAR(testPoint);
aNode->convertToNodeSpaceAR(testPoint);

考虑父节点的锚点,以锚点为原点,“原设定”的右上为正方向。坐标系同样会旋转,不过旋转是以锚点为中心,所以在这个方法中,原点倒是不会随旋转而乱跑。

3.3:单位

对于节点坐标系的单位,又需要特别说明,因为会随着 setScale 而变化。

        cin >> scale;
        aNode->setScale( ((float)scale)/10 );
        CCPoint anchorPoint = aNode->getAnchorPointInPoints();
        aNode->setPosition(ccp(200, 200));
        CCPoint testPoint = ccp(10, 10);
        CCPoint nodePoint = aNode->convertToNodeSpace(testPoint);

 

当setscale(1) 时输出结果是:(-190, -190); 当 setScale(0.5) 时,输出结果是 (-380, -380),也就是说坐标系的单位值也变成了原来的0.5倍。

另外需要注意的是,setPosition 使用的显然不是节点坐标系,由于这里锚点设置的是默认的原点(0,0), 所以无论输入什么 scale, 其左下角原点在世界坐标系上的位置都是固定的。

这里和锚点牵扯的比较厉害,下面研究了锚点再来看一个稍微复杂一些的例子。

【第三部分:锚点】

名称还是挺形象的,我理解就是图像转换的中心点。需要注意的是,和原点并不是一个概念。

1、设置锚点的方法

第二部分提到书上 3.1.4.4 这个示例只是按照默认锚点旋转,对于图像旋转而言,并不是很好用。

书上有这样一句话:“只有CCNode节点使用贴图的情况下,锚点才有意义。”

不过在某些情况下,一个本身无贴图的CCNode节点,可能是作为几个有贴图的子节点的集中管理器出现的,那么提供一个锚点来进行操作还是挺有意义的。所以还是要尝试启用这个功能。现在我们的代码中的 aNode 就是这样一个CCNode,那么下面要为他设置锚点,然后再进行旋转。

如果我们直接使用一般节点的锚点设置方法是无效的:

        aNode->setAnchorPoint(ccp(0.5,0.5));
        aNode->setRotation(90.0);

会发现界面完全没有动。

查阅CCNode.cpp 源代码,可以看到

CCAffineTransform CCNode::nodeToParentTransform(void)
{
    if (m_bIsTransformDirty) 
    {

        // Translate values
        float x = m_tPosition.x;
        float y = m_tPosition.y;

        if (m_bIgnoreAnchorPointForPosition) 
        {
            x += m_tAnchorPointInPoints.x;
            y += m_tAnchorPointInPoints.y;
        }

        // Rotation values
        float c = 1, s = 0;
        if (m_fRotation) 
        {
            float radians = -CC_DEGREES_TO_RADIANS(m_fRotation);
            c = cosf(radians);
            s = sinf(radians);
        }

        bool needsSkewMatrix = ( m_fSkewX || m_fSkewY );


        // optimization:
        // inline anchor point calculation if skew is not needed
        if (! needsSkewMatrix && !m_tAnchorPointInPoints.equals(CCPointZero))
        {
            x += c * -m_tAnchorPointInPoints.x * m_fScaleX + -s * -m_tAnchorPointInPoints.y * m_fScaleY;
            y += s * -m_tAnchorPointInPoints.x * m_fScaleX +  c * -m_tAnchorPointInPoints.y * m_fScaleY;
        }


可以看到,在实现旋转的时候,关键的几个变量是 m_bIgnoreAnchorPointForPosition 和 m_tAnchorPointInPoints。

m_bIgnoreAnchorPointForPosition 可以通过 ignoreAnchorPointForPosition(true) 来打开,

至于 m_tAnchorPointInPoints 则需要再设置setAnchorPoint "之前"调用 setContentSize(size) 来设置 size,这是因为 m_tAnchorPointInPoints 是根据 setAnchorPoint 设置进去的相对值和 ContentSize 的值相乘得到的,而一个没有设置贴图的 CCNode , 其contentSize 是 0, 所以如果直接设置 setAnchorPoint, 会发现用 getAnchorPointInPoints 查出来的锚点值仍然是0。

具体来说,代码如下:

        CCSize size = CCDirector::sharedDirector()->getWinSize();
        aNode->ignoreAnchorPointForPosition(true);
        aNode->setContentSize(size);    //这里的aNode实际装载的是整个界面,所以size也是整个winSize
        aNode->setAnchorPoint(ccp(0.5,0.5));
        aNode->setRotation(90.0);

这样就可以实现旋转了。

可以看到这样设置锚点还挺麻烦的,肯定不是最好的方法,我看到后面scene节点和layer节点设置要容易的多,可能是更好的办法。

2. 锚点、旋转和节点坐标系

缩放和坐标系前面讨论过了,这里主要讨论旋转。实际上,节点坐标系是随着节点一起旋转的。只看一个例子,基本上就可以理解了:

        aNode->ignoreAnchorPointForPosition(true);
        aNode->setContentSize(size);
        aNode->setAnchorPoint(ccp(0.5,0.5));

        CCPoint anchorPoint = aNode->getAnchorPointInPoints();
        cout << "x = " << anchorPoint.x  << "; y = " << anchorPoint.y << endl;
        aNode->setPosition(ccp(200, 200));
        aNode->setRotation(90.0);

        CCPoint testPoint = ccp(10, 10);
        CCPoint nodePoint = aNode->convertToNodeSpace(testPoint);
        cout << "node point x = "<< nodePoint.x << "; y = " << nodePoint.y << endl;

输出的结果是 x = 590; y = -270 (辅助信息:eglView->setFrameSize(480, 320);) 容易看到坐标轴实际上随着图片转动了

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值