zoukankan      html  css  js  c++  java
  • 关于Cocos2d-x中坐标系的种类和转换

    注意:

    当一个节点有一个子节点的时候,如果移动父节点,子节点也会跟着做相应的移动变化,只要被添加到父节点中,子节点就被绑定了,所以子节点的位置,坐标就会被动地变化。

    当一个节点有一个子节点的时候,如果通过setPosition方法设置子节点的坐标,那么,这时候其实是在以父节点左下角为原点的坐标系中,x轴最长是父节点的宽度,y轴最长是父节点的高度。后面如果有需要可以通过一些坐标的转换的方法转化为世界坐标/UI坐标。

    例如,jewel1是hero的子节点,屏幕分辨率960*640,hero大小200*250

    Point p1, p2,p3;
    auto hero = Sprite::create("s_1.png");
    hero->setPosition(visibleSize.width / 2, visibleSize.height / 2);
    p1=hero->getPosition();
    log("p1=(%f,%f)",p1.x,p1.y);
    addChild(hero);

    auto jewel1 = Sprite::create("jewel1.png");
    jewel1->setAnchorPoint(Vec2(1, 0));
    jewel1->setPosition(hero->getContentSize().width, 0);
    p2 = jewel1->getPosition();
    log("p2=(%f,%f)", p2.x, p2.y);
    p3 = hero->convertToWorldSpace(jewel1->getPosition());
    log("p3=(%f,%f)", p3.x, p3.y);
    hero->addChild(jewel1);

    输出结果

    p1=(480.000000,320.000000)
    p2=(200.000000,0.000000)
    p3=(580.000000,195.000000)

     

    一.UI坐标系

    在进行iOS或者Android界面开发时,它的坐标系规则如下图所示:

    alt

    • 原点坐标(x=0, y=0)位于左上角;
    • X轴从屏幕最左边开始,由左向右渐增;
    • Y轴坐标从屏幕最上方开始,由上向下渐增

    二.Cocos2d-x坐标系

    Cocos2d-x坐标系是这里的重点,也是我们开发时考虑的最多的。由于Cocos2d-x是基于OpenGL和OpenGL ES的。该坐标系的规则如下:

    • 原点坐标(x=0, y=0)位于左下角;
    • X轴从屏幕最左边开始,由左向右渐增;
    • Y轴从屏幕最下方开始,由下向上渐增;

    在Cocos2d-x中的场景,就是使用的该坐标系。

    三.世界坐标系

    世界坐标系也叫绝对坐标系,是游戏开发中建立的概念。它建立了描述其它坐标系所需要的参考标准。我们都可以使用世界坐标系来描述其它坐标系的位置。

    Cocos2d-x中元素是有父子关系的层次结构,通过Node设置位置使用的是相对其父节点的本地坐标系,而非世界坐标系,最后在绘制屏幕的时候,Cocos2d-x会把这些元素的本地节点坐标映射成世界坐标系坐标。世界坐标系和OpenGL坐标系方向一致,原点在屏幕左下角,X轴向右,Y轴向上。

    四.节点坐标系

    节点坐标系也叫相对坐标系,它是与特定节点相关联的坐标系。每个节点都有独立的坐标系,当节点移动或改变方向时,和该节点关联的坐标系(它的子节点)将随之移动或改变方向。比如一个Layer上面有10个精灵,当移动这个Layer的时候,这些精灵也会跟着一起移动的。

    Node节点类中,我们可以对节点进行位置的操作,而这些设置位置使用的就是父节点的节点坐标系。它和OpenGL坐标系方向一致,原点在屏幕左下角,X轴向右,Y轴向上。有的时候,我们需要将节点坐标转换成世界坐标,或者将世界坐标转换成节点坐标。在Node节点类中,就提供了对应的转换函数,下面我们就使用一下这些函数,加深对Cocos2d-x坐标系、世界坐标系和节点坐标系的理解。

     

     

    转换API

    在Cocos2d-x中提供了以下的API用来进行坐标转换。

    /**
    * 将世界坐标转换成节点坐标,忽略锚点的影响;结果是以点为单位。
    */
    Vec2 convertToNodeSpace(const Vec2& worldPoint) const;
    
    /**
    * 将节点坐标转换成世界坐标,忽略锚点的影响;结果是以点为单位。
    */
    Vec2 convertToWorldSpace(const Vec2& nodePoint) const;
    
    /**
    * 将世界坐标转换成节点坐标;结果是以点为单位。
    * 会考虑到锚点的影响
    */
    Vec2 convertToNodeSpaceAR(const Vec2& worldPoint) const;
    
    /**
    * 将节点坐标转换成世界坐标;结果是以点为单位。
    * 会考虑到锚点的影响。
    */
    Vec2 convertToWorldSpaceAR(const Vec2& nodePoint) const;
    
    /**
    * 将Touch对应的点转换成节点坐标,忽略锚点的影响。
    */
    Vec2 convertTouchToNodeSpace(Touch * touch) const;
    
    /**
    * 将Touch对应的点转换成节点坐标,考虑锚点的影响。
    */
    Vec2 convertTouchToNodeSpaceAR(Touch * touch) const;

    好了,世界坐标系转节点坐标系,节点坐标系转世界坐标系,就这么几个函数就能搞定了,剩下的就是实际的应用了。对了,在实际中,一定要考虑到锚点的影响,可能你得到的结果,就是因为锚点的影响,而完全不同的。

     

     

    坐标系变换原理

    上面总结了坐标系之间转换的一些API函数,下面就来看看它们之间到底是如何转换的。看了网上很多人的博客,写的转换原理,写的都不错,就是看的云里雾里的,很多人都配上了坐标图,搞笑的是那些坐标图都是“一副”,也不知道谁抄袭的谁的。

    下面就做一些简单的原理,没有过的图来说明,就是一些简短的文字,按照这些文字说明,你肯定能看的懂的。

     

    1.convertToNodeSpace
    Vec2 newPosition = node1->convertToNodeSpace(node2->getPosition());
    将node2的位置坐标转换成相对于node1左下角顶点的坐标。转换方法:node1和node2位置不变,将坐标轴原点设置为node1的左下角顶点,重新计算node2->getPosition()这个点的坐标即为newPosition。
    当调用以下代码时,返回的是相对于其父节点的节点坐标,当然了,以下代码的实际用处并不大。

    Vec2 newPosition = node1->convertToNodeSpace(node1->getPosition());

    2.convertToNodeSpaceAR
    Vec2 newPosition=node1->convertToNodeSpaceAR(node2->getPosition());
    将node2的位置坐标转换成相对于node1锚点的坐标。转换方法:node1和node2位置不变,将坐标轴原点设置为node1的锚点,重新计算node2->getPosition()这个点的坐标即为newPosition。

    3.convertToWorldSpace
    Vec2 newPosition=node1->convertToWorldSpace(node2->getPosition());
    将node2的位置坐标转换成世界坐标。转换方法:node1的位置不变,世界坐标的坐标轴也不变,以node1的左下角顶点再建立一个坐标系(其实就是本地坐标),将node2->getPosition()这个点设置到新建的坐标系中,以原来的世界坐标系为参考,重新计算node2->getPosition()这个点的坐标即为newPosition。

    4.convertToWorldSpaceAR
    Vec2 newPosition=node1->convertToWorldSpaceAR(node2->getPosition());
    将node2的位置坐标转换成世界坐标。转换方法:node1的位置不变,世界坐标的坐标轴也不变,以node1的锚点再建立一个坐标系,将node2->getPosition()这个点设置到新建的坐标系中,以原来的世界坐标系为参考,重新计算node2->getPosition()这个点的坐标即为newPosition。

    另一个例子

    首先我们添加两个测试精灵(宽:27,高:40)到场景里面:

    Sprite *sprite1 = Sprite::create("player.png");
    sprite1->setPosition(Vec2(20, 40));
    sprite1->setAnchorPoint(Vec2(0, 0));
    this->addChild(sprite1);

    Sprite *sprite2 = Sprite::create("player.png");
    sprite2->setPosition(Vec2(-15, -30));
    sprite2->setAnchorPoint(Vec2(1, 1));
    this->addChild(sprite2);

    然后调试,在场景中大概是下图这样显示(以左下角为坐标原点,从左到右为x方向,从下到上为y方向,废话了:)):

    在cocos2d-x中,每个精灵都有一个锚点,以后对精灵的操作(比如旋转)都会围绕锚点进行,我们暂且可以看作是精灵的中心位置,一般来说有每个方向有三种可能的值:0,0.5,1。上图中红色圆点即为各自的锚点,sprite1 锚点为 (0,0) 左下角,sprite2锚点为(1,1)在右上角。

    现在我们来看看坐标系转换,同样地,我们先写点测试代码:

    Point p1 = sprite2->convertToNodeSpace(sprite1->getPosition());
    Point p2 = sprite2->convertToWorldSpace(sprite1->getPosition());
    Point p3 = sprite2->convertToNodeSpaceAR(sprite1->getPosition());
    Point p4 = sprite2->convertToWorldSpaceAR(sprite1->getPosition());

    接着,再打印出各点的x,y值:
     


    Log("p1:%f,%f", p1.x, p1.y);
    Log("p2:%f,%f", p2.x, p2.y);
    Log("p3:%f,%f", p3.x, p3.y);
    Log("p4:%f,%f", p4.x, p4.y);

    由于cocos2d-x的坐标系(本地坐标系)是以左下角为坐标原点的,所以 sprite1和sprite2的坐标原点在上图的位置分别是(20,40)、(-42,-70),那么很明显的:

    p1就是sprite1锚点相对于sprite2原点来说在sprite2坐标系中的位置,经过对比上图,我们可以得到(20-(-42),40-(-70))即(62,110)

    p2就是sprite1锚点相对于sprite2原点来说在上图坐标系中的位置,这样我们可以计算出sprite1在sprite2坐标系中的位置:(20+(-42),40+(-70)),即(-22,-30)

    p3就是sprite1锚点相对于sprite2锚点来说在sprite2坐标系中的位置,也就是(20-(-15),40-(-30)),即(35,70)

    p4就是sprite1锚点相对于sprite2锚点来说在上图坐标系中的位置,也就是(20+(-15),40+(-30)),即(5,10)

    现在我们可以知道,计算方法都是用sprite1的坐标去加减sprite2的坐标,针对本地坐标系就用减法,针对世界坐标系就用加法。

  • 相关阅读:
    PAT (Advanced Level) 1017. Queueing at Bank (25)
    PAT (Advanced Level) 1016. Phone Bills (25)
    1sting
    八皇后问题
    思维水题
    pigofzhou的巧克力棒
    喵哈哈村的代码传说 第四章 并查集
    简单容器应用
    Codefroces D2. Magic Powder
    喵哈哈村的种花魔法(线段树(区间更新,单点查询),前缀和(单点更新,区间查询))
  • 原文地址:https://www.cnblogs.com/HangZhe/p/5760350.html
Copyright © 2011-2022 走看看