Cocos2d-x 3.0坐标系详解
Cocos2d-x坐标系和OpenGL坐标系相同,都是起源于笛卡尔坐标系。
笛卡尔坐标系
笛卡尔坐标系中定义右手系原点在左下角,x向右,y向上,z向外,OpenGL坐标系为笛卡尔右手系。
屏幕坐标系和Cocos2d坐标系
标准屏幕坐标系使用和OpenGL不同的坐标系,而Cocos2d则使用和OpenGL相同的坐标系。
iOS, Android, Windows Phone等在开发应用时使用的是标准屏幕坐标系,原点为屏幕左上角,x向右,y向下。
Cocos2d坐标系和OpenGL坐标系一样,原点为屏幕左下角,x向右,y向上。
在开发中,我们经常会提到两个比较抽象的概念-世界坐标系和本地坐标系。这两个概念可以帮助我们更好的理解节点在Cocos2d坐标系中的位置以及对应关系。
世界坐标系(World Coordinate) VS 本地坐标系(Node Local)
世界坐标系也叫做绝对坐标系,是游戏开发中建立的概念。因此,“世界”指游戏世界。cocos2d中的元素是有父子关系的层级结构,我们通过Node的setPosition设定元素的位置使用的是相对与其父节点的本地坐标系而非世界坐标系。最后在绘制屏幕的时候cocos2d会把这些元素的本地坐标映射成世界坐标系坐标。
本地坐标系也叫相对坐标系,是和节点相关联的坐标系。每个节点都有独立的坐标系,当节点移动或改变方向时,和该节点关联的坐标系将随之移动或改变方向。
锚点(Anchor Point)
将一个节点添加到父节点里面时,需要设置其在父节点上的位置,本质上是设置节点的锚点在父节点坐标系上的位置。
- Anchor Point的两个参数都在0~1之间。它们表示的并不是像素点,而是乘数因子。(0.5, 0.5)表示Anchor Point位于节点长度乘0.5和宽度乘0.5的地方,即节点的中心
- 在Cocos2d-x中Layer的Anchor Point为默认值(0, 0),其他Node的默认值为(0.5, 0.5)。
我们用以下代码为例,使用默认Anchor Point值,将红色层放在屏幕左下角,绿色层添加到红色层上:
CCDirector* pDirector = CCDirector::sharedDirector();
CCPoint visibleOrigin = pDirector->getVisibleOrigin();
CCSize visibleSize = pDirector->getVisibleSize();
auto red = CCLayerColor::create(ccc4(255, 100, 100, 128), visibleSize.width/2, visibleSize.height/2);
auto green = CCLayerColor::create(ccc4(100, 255, 100, 128), visibleSize.width/4, visibleSize.height/4);
red->addChild(green);
this->addChild(red, 0);
printf("red Position:%f--%f Anchor:%f--%f\n",red->getPosition().x,red->getPosition().y,red->getAnchorPoint().x,red->getAnchorPoint().y);
printf("green Position:%f--%f Anchor:%f--%f\n",green->getPosition().x,green->getPosition().y,green->getAnchorPoint().x,green->getAnchorPoint().y);
1)输出:
red Position:0.000000--0.000000 Anchor:0.500000--0.500000
green Position:0.000000--0.000000 Anchor:0.500000--0.500000
显示:
2)加上语句:
red->ignoreAnchorPointForPosition(false);
red->setAnchorPoint(ccp(0.5f,0.5f));
输出:
red Position:0.000000--0.000000 Anchor:0.500000--0.500000
green Position:0.000000--0.000000 Anchor:0.500000--0.500000
显示:
3)加上语句:
red->setPosition(ccp(visibleSize.width/2 + visibleOrigin.x, visibleSize.height/2 + visibleOrigin.y));
输出:
red Position:240.000000--160.000000 Anchor:0.500000--0.500000
green Position:0.000000--0.000000 Anchor:0.500000--0.500000
显示:
4)加上语句:
green->ignoreAnchorPointForPosition(false);
green->setAnchorPoint(ccp(1, 1));
输出:
red Position:240.000000--160.000000 Anchor:0.500000--0.500000
green Position:0.000000--0.000000 Anchor:1.000000--1.000000
显示:
忽略锚点(Ignore Anchor Point)
Ignore Anchor Point全称是ignoreAnchorPointForPosition,作用是将锚点固定在一个地方。
如果设置其值为true,则图片资源的Anchor Pont固定为左下角,否则即为所设置的位置。
我们用以下代码为例,将两个层的ignoreAnchorPointForPosition设为true,并将绿色的层添加到红色的层上:
CCDirector* pDirector = CCDirector::sharedDirector();
CCPoint visibleOrigin = pDirector->getVisibleOrigin();
CCSize visibleSize = pDirector->getVisibleSize();
auto red = CCLayerColor::create(ccc4(255, 100, 100, 128), visibleSize.width/2, visibleSize.height/2);
red->ignoreAnchorPointForPosition(true);
red->setAnchorPoint(ccp(0.5f,0.5f));
red->setPosition(ccp(visibleSize.width/2 + visibleOrigin.x, visibleSize.height/2 + visibleOrigin.y));
auto green = CCLayerColor::create(ccc4(100, 255, 100, 128), visibleSize.width/4, visibleSize.height/4);
green->ignoreAnchorPointForPosition(true);
green->setAnchorPoint(ccp(1, 1));
red->addChild(green);
this->addChild(red, 0);
printf("red Position:%f--%f Anchor:%f--%f\n",red->getPosition().x,red->getPosition().y,red->getAnchorPoint().x,red->getAnchorPoint().y);
printf("green Position:%f--%f Anchor:%f--%f\n",green->getPosition().x,green->getPosition().y,green->getAnchorPoint().x,green->getAnchorPoint().y);
输出:
red Position:240.000000--160.000000 Anchor:0.500000--0.500000
green Position:0.000000--0.000000 Anchor:1.000000--1.000000
显示:
触摸点(Touch position)
所以在处理触摸事件时需要用重写以下四个函数:
virtual bool onTouchBegan(Touch *touch, Event * event);
virtual void onTouchEnded(Touch *touch, Event * event);
virtual void onTouchCancelled(Touch *touch, Event * event);
virtual void onTouchMoved(Touch *touch, Event * event);
在函数中获取到touch,我们在设计游戏逻辑时需要用到触摸点在Cocos2d坐标系中的位置,就需要将touch的坐标转换成OpenGL坐标系中的点坐标。
Touch position是屏幕坐标系中的点,OpenGL position是Cocos2d-x用到的OpenGL坐标系上的点坐标。通常我们在开发中会使用两个接口getLocation()和getLocationInView()来进行相应坐标转换工作。
在开发中一般使用getLocation()获取触摸点的GL坐标,而getLocation()内部实现是通过调用Director::getInstance()->convertToGL(_point);返回GL坐标。
// returns the current touch location in OpenGL coordinates
CCPoint CCTouch::getLocation() const
{
return CCDirector::sharedDirector()->convertToGL(m_point);
}
此外,关于世界坐标系和本地坐标系的相互转换,在Node中定义了以下四个常用的坐标变换的相关方法。
// 把世界坐标转换到当前节点的本地坐标系中
Point convertToNodeSpace(const Point& worldPoint) const;
// 把基于当前节点的本地坐标系下的坐标转换到世界坐标系中
Point convertToWorldSpace(const Point& nodePoint) const;
// 把世界坐标转换到当前节点的基于Anchor Point的本地坐标系中
Point convertToNodeSpaceAR(const Point& worldPoint) const;
//把基于当前节点Anchor Point的本地坐标系下的坐标转换到世界坐标系中
Point convertToWorldSpaceAR(const Point& nodePoint) const;
下面通过一个例子来说明这四个方法的理解和作用:
auto *sprite1 = CCSprite::create("ccb/btn-b-0.png");//btn-b-0.png 100*100
sprite1->setPosition(ccp(20,40));
this->addChild(sprite1); //此时添加到的是世界坐标系,也就是OpenGL坐标系
CCSize s = sprite1->getContentSize();
CCPoint p = sprite1->getPosition();
CCPoint ap = sprite1->getAnchorPoint();
CCRect rect = CCRectMake(p.x - ap.x * s.width , p.y - ap.y * s.height,s.width, s.height);
auto *sprite2 = CCSprite::create("ccb/btn-b-1.png");
sprite2->setPosition(ccp(-5,-20));
this->addChild(sprite2); //此时添加到的是世界坐标系,也就是OpenGL坐标系
//将 sprite2 这个节点的坐标ccp(-5,-20) 转换为 sprite1节点下的本地(节点)坐标系统的位置坐标
CCPoint point1 = sprite1->convertToNodeSpace(sprite2->getPosition());
CCPoint point2 = sprite1->convertToNodeSpaceAR(sprite2->getPosition());
//将 sprite2 这个节点的坐标ccp(-5,-20) 转换为 sprite1节点下的世界坐标系统的位置坐标
CCPoint point3 = sprite1->convertToWorldSpace(sprite2->getPosition());
CCPoint point4 = sprite1->convertToWorldSpaceAR(sprite2->getPosition());
printf("point1 = (%.0f,%.0f) \n", point1.x,point1.y);
printf("point2 = (%.0f,%.0f) \n",point2.x,point2.y);
printf("point3 = (%.0f,%.0f) \n",point3.x,point3.y);
printf("point4 = (%.0f,%.0f) \n",point4.x,point4.y);
先给出输出结果,再来说明是怎么转化的:
point1 = (0,-35)
point2 = (-25,-60)
point3 = (-10,-5)
point4 = (15,20)
先拿point1和point2来说说没有AR和有AR的区别:
CCPoint point1 = sprite1->convertToNodeSpace(sprite2->getPosition());
CCPoint point2 = sprite1->convertToNodeSpaceAR(sprite2->getPosition());
point1和point2都是把sprite2的世界坐标转化为节点sprite1的本地坐标。区别在于他们基于的sprite1的本地坐标系(节点坐标系)的位置不一样。节点的本地坐标系默认的原点位置在节点的左下角位置,即图1的X1'-A1'-Y1'。用convertToNodeSpace来转换的时候基于的sprite1的本地坐标系的原点就是在sprite1的左下角的位置(图1的X1'-A1'-Y1'),而用convertToNodeSpace来转换的时候基于的sprite1的本地坐标系的原点在sprite1的锚点的位置(图1的X1-A1-Y1),因为我们知道一般节点的锚点在(0.5,0.5)的位置。上述代码中并没有设置sprit1的锚点,所以sprite1的默认为(0.5,0.5)。结论:没有AR后缀在转化的时候,实际上是以参照物的左下角,向上,向右来建立本地坐标系的,而有AR后缀在转化的时候,以参照物的锚点,向上向右来建立本地坐标系的。
再来说说世界坐标转本地坐标的计算方法:
point1 = A2-A1‘ = (-5,-20) - (-5,15)=(0,-35)
point2 = A2-A1 = (-5,-20)-(20,40)=(-25,-60)
结论:作为函数参数的世界坐标 - 参照物本地坐标系原点
再来说说convertToWorldSpace的计算方法(节点坐标转世界坐标)
Point3 = A2+A1'=(-5,-20)+(-5,15)=(-10,-5)
point4 = A2+A1 =(-5,-20)+(20,40)=(15,20)
结论:节点坐标 + 参照物本地坐标系原点
在上面如果把sprite1的锚点修改成:
sprite1->setAnchorPoint(ccp(0,0));
则此时锚点就在左下角的位置(如图2),那么输出结果就是:
point1 = (-25,-60)
point2 = (-25,-60)
point3 = (15,20)
point4 = (15,20)
另外,这里没有提到sprite2的锚点对转换的结果的影响,因为转换结果和它没有半毛钱关系。
Cocos2d-x 3.0坐标系详解相关推荐
- 地理坐标系、投影坐标系详解
地理坐标系.投影坐标系详解 1.基本概念 2.地理坐标系 2.1 地球的三级逼近 2.1.1大地水准面 2.1.2地球椭球体 2.1.3大地基准面 2.2地理坐标 3.投影坐标系 3.1投影 3.2我 ...
- [转]地理投影,常用坐标系详解、WGS84、WGS84 Web墨卡托、WGS84 UTM、北京54坐标系、西安80坐标系、CGCS2000坐标系...
转自:http://www.rivermap.cn/docs/show-1829.html 常用坐标系详解 (一)WGS84坐标系 WGS-84坐标系(World Geodetic System一19 ...
- vue-cli3.0配置详解
这次给大家带来vue-cli3.0配置详解,使用vue-cli3.0配置的注意事项有哪些,下面就是实战案例,一起来看一下. 新建项目 1 2 3 4 5 6 7 8 # 安装 npm install ...
- [转载]AxureRP 7.0部件详解(一)
转载]AxureRP 7.0部件详解(一) 本文为Axure RT7.0教程,本章主要介绍menu菜单.table表格.Tree Widget 树部件三个部件,后续将持续更新...... Menu 菜 ...
- Android消息传递之EventBus 3.0使用详解
前言: 前面两篇不仅学习了子线程与UI主线程之间的通信方式,也学习了如何实现组件之间通信,基于前面的知识我们今天来分析一下EventBus是如何管理事件总线的,EventBus到底是不是最佳方案?学习 ...
- linux rpm安装zabbix,CentOS 7上安装Zabbix Server 3.0 图文详解
CentOS 7上安装Zabbix Server 3.0 图文详解 1.查看系统信息. cat /etc/RedHat-release CentOS Linux release 7.0.1406 (C ...
- Less(v3.9.0)使用详解—变量
该系列: Less(v3.9.0)使用详解--基本使用 Less(v3.9.0)使用详解--变量 Less(v3.9.0)使用详解--嵌套和父选择器& Less(v3.9.0)使用详解--ex ...
- Hadoop3.2.0使用详解
Hadoop3.2.0使用详解 1.概述 Hadoop3已经发布很久了,迭代集成的一些新特性也是很有用的.截止本篇博客书写为止,Hadoop发布了3.2.0.接下来,笔者就为大家分享一下在使用Hado ...
- mvc jquery ajax分页实例,jQuery Ajax自定义分页组件(jquery.loehpagerv1.0)实例详解,mvcpagerajax分页...
jQuery Ajax自定义分页组件(jquery.loehpagerv1.0)实例详解,mvcpagerajax分页 简单的两个步骤即可实现分页功能 //回调里面进行业务处理 function lo ...
最新文章
- 小白都能看懂的神经网络教程:从原理到优化如此简单
- bzoj 2436: [Noi2011]Noi嘉年华
- 女主计算机的学霸,又一部青春网剧开机,学霸女主牵手计算机大神,另类爱情精彩上演...
- Python字符串前加f、r、b、u的不同用法
- android 上下收缩动画,Android 带有弹出收缩动画的扇形菜单实例
- 什么叫“职业年金”,与养老保险有什么关系?
- linux下查看某软件是否已安装, ubuntu安装deb包
- Android保存之SharedPreferences
- 面向对象 —— 类设计(十二)—— 全局变量和成员变量
- Windows和Linux hosts 文件位置
- 网站正在建设中_网站建设中如何设计更能吸引用户
- cad2019菜单栏怎么调出来_AutoCAD2019怎么把工具栏放左右两边两侧工具栏调出来
- Python爬虫实战四之抓取淘宝MM照片
- vue中实现 楼层效果
- Control Egress TCP Traffic
- mkdir: Permission denied: user=root, access=WRITE, inode=/lcy/test5.txt:hdfs:hdfs:drwxr-xr-x
- IE与Firefox火狐的CSS兼容大全
- redis.clients.jedis.exceptions.JedisDataException: WRONGTYPE Operation against a key holding the wro
- 增值税发票生成EXCEL——调用百度开发接口
- 看完 Python3.10 的新特性,我决定仍不更新