现在的位置: 首页 > 综合 > 正文

cocos2d-x 坐标研究

2014年10月08日 ⁄ 综合 ⁄ 共 6148字 ⁄ 字号 评论关闭

今天晚上,对cocos2d-x里面的四个表示坐标的方法进行了一下研究,特意做了下笔记,如下:

CCPoint convertToNodeSpace(const CCPoint& worldPoint);
CCPoint convertToWorldSpace(const CCPoint& nodePoint);
CCPoint convertToNodeSpaceAR(const CCPoint& worldPoint);
CCPoint convertToWorldSpaceAR(const CCPoint& nodePoint);

在理解这个之前,要多世界坐标和本地坐标有一定的理解,
GL坐标系Cocos2D以OpenglES为图形库,所以它使用OpenglES坐标系。GL坐标系原点在屏幕左下角,x轴向右,y轴向上。

屏幕坐标系苹果的Quarze2D使用的是不同的坐标系统,原点在屏幕左上角,x轴向右,y轴向下。ios的屏幕触摸事件CCTouch传入的位置信息使用的是该坐标系。因此在cocos2d中对触摸事件做出响应前需要首先把触摸点转化到GL坐标系。可以使用CCDirector的convertToGL来完成这一转化。

世界坐标系也叫做绝对坐标系,cocos2d中的元素是有父子关系的层级结构,我们通过CCNode的position设定元素的位置使用的是相对与其父节点的本地坐标系而非世界坐标系。最后在绘制屏幕的时候cocos2d会把这些元素的本地坐标映射成世界坐标系坐标。世界坐标系和GL坐标系一致,原点在屏幕左下角,

本地坐标系本地坐标系也叫做物体坐标系,是和特定物体相关联的坐标系。每个物体都有它们独立的坐标系,当物体移动或改变方向时,和该物体关联的坐标系将随之移动或改变方向。比如用cocos2d-x创建了个矩形colorLayer:CCRect(10,10,100,100),这是的本地坐标系为以(10,10)为坐标原点,x轴向右,y轴向上。如果创建了一个CCSprite,锚点为(0.5,0.5),位置为(100,100),size为(40,40),这时的本地坐标系为以(80,80)为坐标原点,x轴向右,y轴向上。总之,本地坐标系原点为node的左下角坐标


接下来,convertToNodeSpace:调用CCPoint
point = node1->
convertToNodeSpace(node2->getPosition());

将node2的坐标转化成相对于node1的本地坐标

cocos2d-x 坐标研究 - zjfzjf - zjfzjf

 ,

比如坐标如上图所示,node1的锚点为(0,0),node2的锚点为(1,1),转化之后,node的坐标变成了(-25,-60)

convertToWorldSpace:调用CCPoint
point = node1->
convertToWorldSpace(node2->getPosition());
cocos2d-x 坐标研究 - zjfzjf - zjfzjf

 

是将node的坐标转化成相对于node1的世界坐标,如上图所示:首先将node1的坐标当做世界坐标,然后让node2的坐标位置重置成相对于node1的世界坐标,也就是(15,20)

convertToNodeSpaceAR就是把node1的坐标系原点设置在锚点的位置,这里的锚点是(0,0)所以转化之后的坐标系位置和上面的convertToNodeSpace一样,结果也是一样的,convertToWorldSpaceAR同理


测试:
CCSprite *sprite1 = CCSprite::spriteWithFile("CloseNormal.png");
sprite1->setPosition(ccp(20,40));
sprite1->setAnchorPoint(ccp(0,0));
this->addChild(sprite1);
CCSprite *sprite2 = CCSprite::spriteWithFile("CloseNormal.png");
sprite2->setPosition(ccp(-5,-20));
sprite2->setAnchorPoint(ccp(1,1));
this->addChild(sprite2);
CCPoint point1 = sprite1->convertToNodeSpace(sprite2->getPosition());
CCPoint point2 = sprite1->convertToWorldSpace(sprite2->getPosition());
CCPoint point3 = sprite1->convertToNodeSpaceAR(sprite2->getPosition());
CCPoint point4 = sprite1->convertToWorldSpaceAR(sprite2->getPosition());
CCLog("position = (%f,%f)",point1.x,point1.y);
CCLog("position = (%f,%f)",point2.x,point2.y);
CCLog("position = (%f,%f)",point3.x,point3.y);
CCLog("position = (%f,%f)",point4.x,point4.y);
运行结果:
position = (-25.000000,-60.000000)
position = (15.000000,20.000000)
position = (-25.000000,-60.000000)
position = (15.000000,20.000000)
和预算的一样

这里在将sprite1的锚点设置成(0.5,0.5),对convertToNodeSpaceAR和convertToWorldSpaceAR进行了进一步的测试
sprite1->setAnchorPoint(ccp(0.5,0.5));
sprite1->setPosition(ccp(100,100));
CCPoint point5 = sprite1->convertToNodeSpaceAR(sprite2->getPosition());
CCPoint point6 = sprite1->convertToWorldSpaceAR(sprite2->getPosition());
CCLog("position = (%f,%f)",point5.x,point5.y);
CCLog("position = (%f,%f)",point6.x,point5.y);

运算结果:
size = (40.000000,40.000000)
position = (-105.000000,-120.000000)
position = (95.000000,80.000000)
分析:重置的sprite1的坐标为(100,100),锚点为(0.5,0.5)所以对于convertToNodeSpaceAR和convertToWorldSpaceAR这两个方法的坐标系为原点(100,100),所以用convertToNodeSpaceAR转化之后的坐标为(-105,-120)用convertToWorldSpaceAR化之后的坐标为(95,80),和运算结果一样

   1  GL坐标系

  Cocos2D以OpenglES为图形库,所以它使用OpenglES坐标系。GL坐标系原点在屏幕左下角,x轴向右,y轴向上。

  2   屏幕坐标系

  苹果的Quarze2D使用的是不同的坐标系统,原点在屏幕左上角,x轴向右,y轴向下。ios的屏幕触摸事件CCTouch传入的位置信息使用的是该坐标系。因此在cocos2d中对触摸事件做出响应前需要首先把触摸点转化到GL坐标系。可以使用CCDirector的convertToGL来完成这一转化。

  3  世界坐标系

  世界坐标系也叫做绝对坐标系,是游戏开发中的概念,它建立了描述其他坐标系所需要的参考框架。我们能够用世界坐标系来描述其他坐标系的位置,而不能用更大的,外部的坐标系来描述世界坐标系。cocos2d中的元素是有父子关系的层级结构,我们通过CCNode的position设定元素的位置使用的是相对与其父节点的本地坐标系而非世界坐标系。最后在绘制屏幕的时候cocos2d会把这些元素的本地坐标映射成世界坐标系坐标。世界坐标系和GL坐标系一致,原点在屏幕左下角,x轴向右,y轴向上。

  4  本地坐标系

  本地坐标系也叫做物体坐标系,是和特定物体相关联的坐标系。每个物体都有它们独立的坐标系,当物体移动或改变方向时,和该物体关联的坐标系将随之移动或改变方向。例如坐出租车的时候对驾驶员说“向左转”,我们使用的是车的物体坐标系,“前”、“后”、“左”、“右”只有在物体坐标系中才有意义。但如果我们说“向东开”,我们使用的就是世界坐标系了,无论是车内还是车外的人都知道应该向什么方向开。CCNode的position使用的就是父节点的本地坐标系,它和GL坐标系也是一致的,x轴向右,y轴向上,原点在父节点的左下角。如果父节点是场景树中的顶层节点,那么它使用的本地坐标系就和世界坐标系重合了。在CCNode对象中有几个方便的函数可以做坐标转换:convertToWorldSpace方法可以把基于当前节点的本地坐标系下的坐标转换到世界坐标系中。convertToNodeSpace方法可以把世界坐标转换到当前节点的本地坐标系中。注意这些方法转换的是基于当前节点的坐标,而一个节点的position所使用的坐标是基于它父节点的本地坐标,因此我们要把node的位置转换到世界坐标系中应该调用父节点的convertToWorldSpace函数
[node.parent convertToWorldSpace:[node position]]。几乎所有的游戏引擎都会使用本地坐标系而非世界坐标系来指定元素的位置,这样做的好处是当计算物体运动的时候使用同一本地坐标系的元素可以作为一个子系统独立计算,最后再加上坐标系的运动即可,这是物理研究中常用的思路。例如一个在行驶的车厢内上下跳动的人,我们只需要在每帧绘制的时候计算他在车厢坐标系中的位置,然后加上车的位置就可以计算出人在世界坐标系中的位置,如果使用单一的世界坐标系,人的运动轨迹就变复杂了。

  5  锚点

  每一个CCNode都有一个锚点(anchor point),锚点指定了texture上和所在节点原点(也就是position所表示的点)重合的点的位置,因此只有在节点使用了texture的情况下,锚点才有意义。锚点的默认值是(0.5, 0.5),它表示的并不是一个像素点,而是一个乘数因子。(0.5, 0.5) 表示锚点位于texture长度乘以0.5和宽度乘以0.5的地方,即texture的中心。改变锚点的值并不会改变节点的位置(position),虽然可能看起来节点的图像位置发生了变化,其实变化的只是texture相对于position的位置,相当于你在移动节点里面的texture,而非节点本身。如果把锚点设置成(0,0),texture的左下角就会和节点的position点重合,这可能使得元素定位更为方便,但会影响到元素的缩放和旋转等一系列变换,所以不推荐这么做。因此在锚点为默认值(0.5,0.5)的情况下要把一个精灵放置到屏幕底部中央,应该如下设置position

  [plain] view plaincopy CGSize screenSize = [[CCDirector sharedDirector] winSize];

  float imageHeight = player.contentSize.height;

  player.position = CGPointMake(screenSize.width / 2, imageHeight / 2);

      6  仿射变换

  一般来说游戏中会大量使用旋转,缩放,平移等仿射变换(所谓仿射变换是指在线性变换的基础上加上平移,平移不是线性变换)。2D计算机图形学中的仿射变换通常是通过和3x3齐次矩阵相乘来实现的。cocos2d中的仿射变换使用了Quartz 2D中的CGAffineTransform类来表示:

[cpp] view plaincopy struct CGAffineTransform {

  CGFloat a;

  CGFloat b;

  CGFloat c;

  CGFloat d;

  CGFloat tx;

  CGFloat ty;

  };

  typedef struct CGAffineTransform CGAffineTransform;

  CGAffineTransform类表示的齐次矩阵如下,由于变换矩阵最后一列总是[0,0,1],所以被省略了:

  


\

 

  由于cocos2d的绘制使用了OpenglES,因此CGAffineTransform只是用来表示2D仿射变换,最终还是要转化成OpenglES的4x4变换矩阵(Opengl是3D的世界,因此它接受的齐次矩阵是4x4的)。转换工作由CGAffineToGL(const CGAffineTransform *t, GLfloat *m)来完成,Opengl的变换矩阵以一维数组表示,它和CGAffineTransform的映射关系如下:

  [plain] view plaincopy | m[0] m[4] m[8] m[12] | | m11 m21 m31 m41 | | a c 0 tx |

  | m[1] m[5] m[9] m[13] | | m12 m22 m32 m42 | | b d 0 ty |

  | m[2] m[6] m[10] m[14] | <=> | m13 m23 m33 m43 | <=> | 0 0 1 0 |

  | m[3] m[7] m[11] m[15] | | m14 m24 m34 m44 | | 0 0 0 1 |

  PS关于3x3齐次矩阵:

  对于2D平面上的点[x,y],2x2的变换矩阵(右乘)表示的是线性变换,不包含平移。这很容易理解:根据矩阵乘法的性质,零向量总是变换成零向量,因此任何能用矩阵乘法表达的变换都不包含平移。事实上,平移是矩阵加法,而不是矩阵乘法。齐次坐标系的引入是一种数学上的技巧,通过合并矩阵运算中的乘法和加法,使得变换矩阵可以处理平移。所谓齐次坐标就是将一个原本是n维的向量用一个n+1维向量来表示。2D向量[x,y]的齐次表示就是[x,y,h],h叫做齐次因子,可以是任意非零值,因此一个向量的齐次表示是不唯一的,比如齐次坐标[8,4,2]、[4,2,1]表示的都是2D点[4,2]。h=0通常可以理解为无穷远点,通过计算不难发现,[x,y,0]乘以3x3平移变换矩阵,平移无效。换句话说,h分量有开关3x3矩阵的平移部分的功能。这个现象是非常有用的,尽管数学表示上相同,从几何意义上来说向量和点是完全不同的:
向量只表示方向,没有位置,而点表示位置,[x,y]可以代表向量也可以代表点(当表示点的时候,其实指的是相对于原点的位移)。点可以平移但向量不可以,因此从几何意义上讲,[x,y,h],当h≠0时表示的是点,当h=0时表示的是向量。

抱歉!评论已关闭.