cocos2dx 获取 子节点相对于父节点的坐标
在探讨如何使用 Cocos2d-x 获取子节点相对于父节点的坐标之前,我们需要先明确父节点和子节点的概念,只有清晰掌握这些概念,才能顺利进行后续操作。
1. Cocos2d-x 的层次结构与节点特性
1.1 层次结构
Cocos2d-x 采用场景、层、精灵的层次结构来组织游戏元素,这种层次结构对应着游戏的渲染层次,游戏元素可以组织成树形结构,即渲染树。
1.2 节点抽象
Cocos2d-x 将渲染树上的每一个游戏元素抽象为一个节点(Node)。所有游戏元素都继承自 Node,因此它们都具备 Node 所提供的特性。
1.3 Node 的特性与功能
Node 定义了可绘制对象的通用特性,包括位置、缩放、可见性、旋转角度等。节点的基本功能如下:
- 包含其他 Node 对象。
- 接受各种事件与回调函数,例如定时器事件。
- 运行动作。
渲染树中包含多个子节点的 Node 类似于一张画布,允许其他画布以不同顺序叠加覆盖在上面。一旦改变该节点的缩放和旋转等特性,会影响该节点及其子节点的显示效果。例如,将节点的旋转角度设置为 90 度,节点会旋转 90 度,但其子节点的相对位置和角度保持不变。绘图顺序通过 localZOrder 属性描述,这与 addChild 中的 localZOrder 参数相同。
2. Cocos2d-x 中的坐标系
2.1 绘图坐标系
绘图坐标系是最常见的坐标系,与 OpenGL 采用的坐标系相同,以左下角为原点,向右为 x 轴正方向,向上为 y 轴正方向。在 Cocos2d-x 中,所有绘图相关的操作都使用绘图坐标系,例如游戏元素中的 Position 和 AnchorPoint 等属性。
2.2 纹理坐标系
纹理坐标系以左上角为原点,向右为 x 轴正方向,向下为 y 轴正方向。在 Cocos2d-x 中,只有从纹理中截取部分矩形时才使用该坐标系,例如 Sprite 的 TextureRect 属性。
3. 绘图属性
3.1 Size _contentSize
该属性用于获取或设置节点的内容大小。任何节点都需要确定其内容大小,以便进行图形变换。对于精灵,_contentSize 是其纹理显示部分的大小;对于层或场景等全屏大型节点,_contentSize 是屏幕大小。
3.2 Point _anchorPoint 与 Point _position
_anchorPoint:用于设置锚点,以精确控制节点的位置和变换。_anchorPoint的两个参量 x 和 y 的取值通常在 0 到 1 之间,表示锚点相对于节点长宽的位置。例如,节点左下角作为锚点时值为 (0, 0),中心作为锚点时值为 (0.5, 0.5),右下角作为锚点时值为 (1, 0)。精灵的_anchorPoint默认值为 (0.5, 0.5),其他节点的默认值为 (0, 0)。_position:用于设置节点的位置。由于_position指的是锚点在父节点中的坐标值,节点的显示位置通常与锚点有关。当多个节点嵌套时,每个节点的坐标系原点位于其内容的左下角,因此锚点在父节点中的坐标实际上是相对于父节点左下角的坐标值。对于场景或层等大型节点,其_ignoreAnchorPointForPosition属性为true,此时引擎会认为_anchorPoint永远为 (0, 0);而其他节点的该属性为false,锚点不会被忽略。
3.3 float Rotation
该属性用于获取或设置节点的旋转角度。节点以自己的锚点为中心顺时针旋转一定量,单位为角度,旋转角度可以是任意实数。
3.4 float _scaleX、float _scaleY 与 float _scaleZ
这些属性用于获取或设置节点的缩放比例。节点以锚点为中心进行缩放,Scale 的值代表整体缩放比例,_scaleX 和 _scaleY 分别代表 X 方向和 Y 方向的缩放比例。默认情况下,这三个属性的值都是 1,表示节点不被缩放。如果设置 Scale 属性,_scaleX 和 _scaleY 会随之变为相同的值,当然也可以给 _scaleX 和 _scaleY 设置不同的值,此时 Scale 属性的值就没有意义了。
3.5 bool _visible
该属性用于获取或设置节点的可见性。当 _visible 为 true 时,节点会被显示,反之则不会显示。在节点不显示时,也不会调用绘图方法(visit 与 draw)。该属性的访问器方法如下:
/**
* Sets whether the node is visible
*
* The default value is true, a node is default to visible
*
* @param visible true if the node is visible, false if the node is hidden.
*/
virtual void setVisible(bool visible);
/**
* Determines if the node is visible
*
* @see `setVisible(bool)`
*
* @return true if the node is visible, false if the node is hidden.
*/
virtual bool isVisible() const;
3.6 float _skewX 与 float _skewY
这两个属性用于获取或设置斜切角度。节点以锚点为中心,平行 x 轴或 y 轴方向作一定角度的变形,_skewX 为平行 x 轴顺时针的变形,_skewY 为平行 y 轴逆时针的变形,单位为角度,默认值为 0,表示节点没有斜切变形。
3.7 int _tag
该属性用于获取或设置节点的标号。在 Cocos2d-x 中,_tag 类似于标识符,用于快速从节点的所有子节点中找出所需节点。添加到同一节点的所有 Node 中,不能有两个节点的 _tag 相同,否则会给定位带来麻烦。与 _tag 相关的方法有 getTag、removeChildByTag 等。
3.8 void *_userData
该属性用于获取或设置与节点相关的额外信息。_userData 为 void* 类型,可以用来保存任何数据。
4. 定时器事件
利用场景、层和精灵等游戏元素可以构建游戏框架,但此时游戏是静止的。在游戏中,游戏状态会随时间变化,同时需要定时进行逻辑判断,因此引入了定时器的概念。定时器是以一定时间间隔连续引发游戏事件的工具,是使游戏动态变化的关键。
Cocos2d-x 提供了两种实现定时机制的方式:使用 update 方法和使用 schedule 方法。
4.1 update 定时器
update 是 Node 的刷新事件,该方法在每帧绘制之前都会被触发一次。由于绘图帧率有限,且每次更新最终会反映到画面上,所以每帧之间刷新一次足以满足大部分游戏逻辑处理的要求。例如,某些游戏的碰撞检测机制可以通过 update 事件实现。
Node 默认未启用 update 事件,要启用定时器,需要调用 scheduleUpdate 方法,并重载 update 方法以执行自定义代码。若要停止定时器,可以使用 unscheduleUpdate 方法。
4.2 schedule 定时器
schedule 方法可以实现以一定时间间隔连续调用某个函数。由于引擎的调度机制,这里的时间间隔必须大于两帧的间隔,否则两帧期间的多次调用会被合并成一次调用。因此,schedule 定时器通常用于间隔较长的定时调用,一般事件间隔应在 0.1 秒以上。在实际开发中,许多定时操作都通过 schedule 定时器实现。
以下以示例说明如何使用 schedule 定时器:
// 启用定时器
this->schedule(schedule_selector(PlayGame::timeRunning), 1.0f);
// 定时器回调函数
void PlayGame::timeRunning(float dt)
{
if (second <= 0)
{
second = 0;
pLabelTime->setString(toTimeMode(second));
this->unschedule(schedule_selector(PlayGame::timeRunning));
return;
}
second--;
pLabelTime->setString(toTimeMode(second));
}
Node 的 schedule 方法接受一个函数指针并启动定时器,利用 schedule 方法的不同重载可以指定触发间隔与延时。schedule_selector 是一个将指定函数转换为函数指针的宏,用于创建 schedule 方法所需的函数指针。传入该宏的函数应包含一个 float 参数,表示距离前一次触发事件的时间间隔。
定时器机制是 Cocos2d-x 调度机制的基础,Cocos2d-x 的动作机制实际上也依赖定时器实现。由于 Cocos2d-x 的调度是纯粹的串行机制,所有函数都运行在同一个线程,避免了并行程序的诸多问题,大大简化了编程的复杂性。
5. 其他与流程控制相关的事件
这些事件的默认实现通常负责处理定时器和动作的启用与暂停,因此在重载方法时必须调用父类的方法。例如,在游戏层开始时可以设置事件监听器。