cocos2dx 节点没有显示出来的可能原因

原文地址:http://blog.csdn.net/wangqiuyun/article/details/8008462

作为一个cocos2d的用户,您之前可能遇到这样的情况:您刚刚添加一个额外的精灵,标签,粒子效果或一些其他的节点到场景中—— 但它却显示不出来!

本文试图列举一些可能的原因,让你可以按部就班地验证并且纠正这种错误。您可以使用它作为一个清单,以给你确切的情况提供帮助。收藏这个页面吧,因为我保证你最终将不得不用到它。

-调试技巧
-节点没有作为child添加
-节点的zOrder
-节点的vertexZ
-节点的position
-节点的anchorPoint
-父节点的rotation
-节点没有动画
-节点的visible
-节点的opacity
-节点的scale
-节点自定义的draw
-节点自定义的draw#2(visit)
-节点的blendFunc
-节点的init
-该段有意留为空白


-调试技巧(Debugging Tip)

如果你有一个本应在那却不显示节点,你可以通过暂时把它直接添加到场景对象中,这样就可以排除一大堆可能潜在原因。请确保你把它放置在屏幕上的某个地方。

如果现在该节点显示了,那么你就可以知道其原因是由它其中的一个父节点产生的,你就可以将搜索范围限制到它的父节点。如果节点仍然显示不出来,这就是节点本身的一个问题了,你应该看看你写的用于显示节点的代码,包括您自定义的draw方法,以及你如何初始化节点还有它的资源。

-节点没有作为child添加(Node not added as child)

首先最明显,但也是相当难以捉摸的原因是:你忘了调用:

[csharp] view plain copy
  1. [xxxaddChild:myNewNode];

它发生的频率往往比你想象的要多得多。这是很难发现的,因为无论是cocos2d还是Xcode在这种情况下都不会发出一个警告的。创建一个节点,但不将其添加为其他节点的孩子,这是完全合法的。

另一种难以捉摸的原因是,可能不是你忘了添加节点。您可能确实将其添加为孩子了,但你却忘了添加其父节点。例如,如果你添加一个精灵到一个新层,你可能已经加把精灵添加到层了,但层却没添加到场景。

很少见但仍有可能的是,节点被作为孩子添加了,但却在瞬间将其移除了。这可能是你代码中的逻辑错误了。

孩子:你必须将它们添加到父节点中否则它将不会显示。

-节点的zOrder(Node Z Order)

节点不显示原因之一是,这个节点的zOrder值比一个在前景中大节点的zOrder值要小。然后前景节点绘制时覆盖了zOrder比较小的背景节点,简单明了。

可能更令你挠头的是,如果你从来没有改变过节点的zOrder值,或者任何节点的zOrder值都没有改变,还是出现没显示的问题。你要明白在这种情况下,cocos2d节点的渲染顺序,是按照加点添加为孩子的顺序来的。这意味着较之前添加的节点,最后添加的节点将被最后绘制。目前一切顺利。现在考虑这种做法,当你有几个节点时删除一些并重置游戏。最终,你会碰到以下的情况,当一个节点早于另一个节点添加时,但较早添加的节点却不显示。这是因为之后添加的节点的内容可能相当大,在绘制时覆盖了之前添加的节点。在这种情况下,你可以在同父节点的节点中使用适当的固定的zOrder值来避免这种问题的产生。

要记住的一件事情,ZOrder属性值会从父节点传播到他们的孩子的。举个例子,如果你有三个节点A,B和C,他们的z值A:5、B:-1、C:0,那么B节点的所有的子节点,以及它子节点的子节点都会被首先绘制。接下来是C的所有的子节点,最后才才是A的所有的子节点。如果A的所有的子节的zOrder值都是-1000,而B的子节点的zOrder值是500,这不要紧。在任何时候A的子节点都将出现在B的子节点前面。这对CCSpriteBatchNode来说也是如此,所以为了让你的绘制函数能有序进行,你在早期代码设计时不得不把这个也作为一个情况考虑。

-节点的vertexZ(Node Vertex Z)

我刚才所描述cocos2d使用可以用z轴调整节点层次。但有时,如在上面描述的例子中,这可能会有太多的限制。你可能会想,在游戏中使用CCSpriteBatchNode作为“层”,但仍然能够很轻松地改变任何Batch Node上的精灵的绘制顺序。为了实现这一目标,开发者需要,开启CCGLView的深度缓冲选项(默认情况下禁用)。从此你可以使用vertexZ值改变OpenGL认为节点实际所在的“深度”。这是全局适用的,因此任何节点的vertexZ值设-100,将被绘制在任何一个的在不同层次结构中拥有更大的vertexZ值的后面。

目前一切顺利。所有注意事项就是安排z轴。然而,有一个额外的警告:如果两个节点具有相同的父节点和相同的vertexZ值,那么安排z轴只有一个会产生效果。在这种情况下,安排z轴仍然有效,如果z轴也相同,则添加的顺序会确定节点的绘制顺序。对于vertexZ值这种问题往往也比较简单的,你现在必须全局地考虑所有的vertexZ值,并使z轴几乎没有影响。然而,如果你不习惯使用vertexZ值,你可能会发现自己会你疯狂地修改普通的z值,以处理两个完全不相关的在两个完全不相关的分支上的节点的排列顺序问题。考虑周全啊!

另一方面预防说明:iPhone4S和iPad 3内部发生了变化,在这种情况下,如果两个节点使用相同vertexZ值并且启用了深度缓冲,实际z轴会出现冲突。您可能会留意到两个或两个以上的节点三角形重叠,甚至闪烁仿佛节点在移动。由于这是很难处理的,所以使vertexZ值基本上意味着你必须确保,任何场景上的节点都不能用相同的vertexZ值,否则z轴冲突的视觉假象就会依旧。

z/绘制顺序:这就是它工作原理!

-节点的position(Node position)

当然,你的节点都应该有一个位置以保持它在屏幕上可视。因此,检查你实际使用的适当的坐标。

一些开发人员不理解的点与像素坐标系,所以他们可能会尝试使用Retina像素位置作为他们的节点位置,如900×600。即使在Retina设备上,这也会把节点放置在屏幕外面,因为位置用点来表达的,而不是像素。Retina和Non-Retina设备都是480×320点,iPad上是1024×768点。

如果你正在使用的API,里面是以像素为单位,而不是点的话,这就要特别留意了。例如,cocos2d本身有一些瓦砖地图函数返回的是像素坐标,而不是点的坐标。再有就是OpenGL ES的代码,它希望你使用的是像素坐标而不是点坐标。

坐标转换也可能会产生问题,如果你使用的是Box2D并且使用不正确的像素-米的比率。一个经常会犯的错误是,尝试使用CC_CONTENT_SCALE_FACTOR()方法在Box2D进行位置的转换,以适应Retina 分辨率,这是没有必要的。为什么呢?见上文:你的工作只用点的坐标,box2d的本身是完全无视实际的屏幕分辨率。

因为动作是基于时间的,所以它们也可能会非常棘手。指定一个非常短的时间,它的效果也是瞬间的。因此,节点使用CCMoveBy基本上是“瞬移”从A移到了B。还有可能,特别是写的不好的自定义操作,可能是一个问题,但更常见的是简单地运行把两个或两个以上CCMoveTo/By在同一时间作用在同一节点上。混乱就出现了。

最后,一个节点的位置是相对于它的父节点位置来说的。如果父节点(和他们的父节点们)都是CCNode,CCScene或CCLayer对象(和一些其它的)并且所有父节点位置都是CGPointZero(0×0),那么您的节点的位置就是等于实际的屏幕坐标。在这种情况下,450×300的位置会将您的节点放置在屏幕的靠右上角。但是,如果父节点的位置不是CGPointZero,那么该父节点的位置将会被加到您的节点的位置上。因此,如果父节点被放置在100×100上,那么你的节点位置450×300会被抵消重设,最后定位在屏幕550×400上。

现在,如果你认为大多数节点都不是按父节点位置抵消重设,而是从父节点左下角的纹理开始显示,这样问题会变得更加复杂。这是cocos2d选择的一个很不幸(不是故意)的设计。如果父节点放置于200×200,使用一个纹理来显示(精灵,标签或其它的)并且纹理的尺寸为150×150,那么在子节点就会被放置在125×125(200×200减(150×150除以2))。这是假设默认anchorPoint是0.5×0.5(因此除以2)保持不变。如果父节点不使用纹理来显示(其的contentSize属性全是零)的话,这种情况就不会放生。

-节点的anchorPoint(Node anchorPoint)

和position还有rotation密切相关的是anchorPoint属性。对于大多数节点来说,它的默认值是0.5×0.5,这意味着不管节点显示什么图像都会以节点位置为中心放置。 anchorPoint也是节点旋转的参考点。

正因为如此,anchorPoint往往被错误地视为一种(容易)方式用以修改节点的位置或旋转角度。我听说过,有些开发人员根本不明白,--anchorPoint是一个范围在0.0到1.0的乘数。所以,如果你笨拙地使用大值的anchorPoint,不仅会影响到节点的位置,甚至于它所有的子节点,从长远来看,当它涉及到节点定位时,这很可能变成一个大问题。某种程度上,改变太多的anchorPoint会导致节点位置和它的可视化表示(精灵,标签,等)不同步。

另一个要考虑的是,修改anchorPoint也会改变节点旋转的中心点。这种情况下围绕它们的父节点的中心旋转的子节点,实际上可能围绕一个远偏移的中心点旋转。这意味着,父节点即使是很小地改变一下rotation属性也可能把它们的子节点移出屏幕外。

-父节点的rotation(Parent rotation)

如上文所暗示这是很隐蔽的。如果您的节点刚好放置在其父节点位置的偏移量上,请明确父节点的旋转属性也会影响您的节点的位置。

例如,为简单起见让我们假设,我们有一个父节点不使用纹理,它被放置于100×100。您的节点的位置为300×0,然后弥补了它的绝对位置是400×0。但是这只是在你父节点的rotation属性为0时是这样。让我们进一步假设父节点的rotation属性为90,180或270——这也会使你的节点围绕父节点的位置旋转。因此,您的节点所得到的绝对位置将是100×500,-200×100和100x-200。所有这些位置将会让你的节点移出屏幕外。

因为继承副节点的属性,如位置,参与的父节点越多,积累的偏移量和偏移位置或旋转量就越大,效果就越明显。

-节点没有动画(Node not animated)

你可能会期待您的节点移动到屏幕上的。或者以任何其他方式出现。但它却没发生。

如果它不是一个很复杂的问题,那么它可能很简单,如忘了运行动作。或者他们不止一次运行,这也同样糟糕。还有可能就是过早中断它们。

如果您给你的节点手写动画,它可能仅仅是遗忘了安排一个更新的选择器。或无意终止了它。例如在节点上调用了pauseSchedulerAndActions。

-节点的visible(Node visible)

听起来很明显,节点的visible属性肯定应该是YES的。但你不得不面对的事实是一个节点的visible属性,是会受它的父节点影响的。因此,如果节点的任何一个父节点的visible属性设置为NO了,那么该节点将不可见。

其实真正意义上,子节点的visible属性在调试器中读取的出来的仍然是YES,因为这个值是不会从父传递下来的。如果父节点的visible属性设置为NO,父节点只是简单地停止其子女调用draw方法。

-节点的opacity(Node opacity)

使用CCFadeOut动作是相当普遍的。因此,简单地认为你的节点的opacity属性是0(或接近零),这并不令人惊讶。如果你有一个逻辑错误,这就有可能会发生。

调用可能涉及到的stopAllActions或stopActionByTag,因为你可能会在一个不幸的时间点停止淡入/淡出动作。

-节点的scale(Node scale)

同样也可以说这样的情况和CCScaleTo动作有关。如果您的节点的scale值是非常小的(也许是低于0.1),节点也就可能是不可见的。

同样,一个逻辑错误如在不恰当的时间停止了动作,可能也是节点不显示原因。或者是用户输入的问题,如你允许用户通过手势对节点缩放,但却没有检查比例系数值可能会变成0的情况。现在,如果你继续这个比例系数值作为比例的乘数,你的节点最终可能永远无法恢复从比例为零中恢复过来。应为任何乘以与零的结果都是零。

-节点自定义的draw(Node custom draw)

一种简单但容易被忽略的情况是,当为了实现自定义(或调试)绘制方法而重写一个节点的draw方法时,忘记调用超类的draw:

copy

    -(void)draw
  1. {
  2. //callsuperimplementationtodrawnode(ifdesired)
  3. [superdraw];
  4. //customdrawcodehere
  5. }

调用超类的方法并不总是必要的。例如,一个CCNode不绘制任何东西时。在其他情况下,你简单认为超类不会自己绘制自己,因为你用你自己的方法绘制它。但有时不调用超类的draw,意味着你除了自定义的draw代码绘制的东西(可能并不是你想象那样的)以外,你看不到任何东西。

-节点自定义的draw#2(visit)(Node custom draw #2 (visit))

另一个重要的事情是你必须知道节点绘制的顺序。首先,节点所有的zOrder值为负的子节点,然后绘制节点其本身,其次绘制所有zOrder值为0或更大的子节点。这意味着,如果你想使用OpenGL(以ccDrawCircle(..)为例)绘制的东西突出在所有子节点之上,你只能在draw方法实现,并看子节点的zOrder值是否是-1或者更小。否则,子节点将简单地被绘制覆盖住你用OpenGL绘制的东西。

为了避免这种制顺序问题,或其他繁琐的解决方法,如对你全部的子节点自定义绘制方法,最好的代替办法是重写的visit方法:

copy

    void)visit
  1. //callsuperimplementationtodrawselfandallchildreninorder
  2. [supervisit];
  3. //customdrawcodeheredrawsovernodeandallofitschildren
  4. 现在你写在visit方法后面的OpenGL绘制代码会被绘制在节点(本身)和它所有的子节点之上。visit方法根据子节点的zOrder值来访问它们的draw方法和visit方法。因此,一旦超类visit返回,那么所有节点包括自身将会被绘制,这样你就能在它们之上绘制了。

    当然,忘记调用超类的visit,会导致你什么都没有绘制,除了您自定义的绘制代码以外。但也许这正是你想要的吗?

    -节点的blendFunc(Node blend func)

    对于一个不可见的节点来说还有一种罕见的可能就是以一个不恰当的方式修改了节点的blendFunc属性。一些混合方法组合可能会导致您的节点变成不可见的。大多数只会简单地导致某种形式的畸变,如矩形,没有起始端,黑色图像内容或者背景图像轮廓闪闪发光。这里真正的问题是两个纹理格式和CCGLView呈现格式影响了blendFunc的结果。那么是什么导致可能一个精灵或者一个app与另一个精灵(或标签)或其它有不同的CCGLView设置的app无法一起正常工作呢。

    -节点的init(Node init)

    最后的,但不是不重要的,那么多的开发人员不假思索地使用继承自cocos2d节点类的子类,他们最终不得不遇到一个问题,我称之为“坏初始化”。例如你可能以这种方式继承CCSprite子类,在最后才调用超类的初始化,这样肯定没有任何纹理被初始化。如果它不是彻底的崩溃,那么精灵也是简单地只显示部分。如果你从cocos2d节点继承了子类,而它只却没显示出来,那么请检查您的初始化函数!逐步检查初始化代码,包括cocos2d的代码,看看它是否使用了它不应该使用的方式。当使用继承子类来代替简单地使用节点类时,这一切很容易使cocos2d跳过重要的代码。

    -该段有意留为空白(This Paragraph Intentionally Left Blank)

    如果你知道任何其他原因会导致一个节点不显示在屏幕上(或您预期的位置上),请给我留下了评论。我将它添加到帖子里。就是这段有意留为空白的地方!

    相关文章

        本文实践自 RayWenderlich、Ali Hafizji 的文章《...
    Cocos-code-ide使用入门学习地点:杭州滨江邮箱:appdevzw@1...
    第一次開始用手游引擎挺激动!!!进入正题。下载资源1:从C...
        Cocos2d-x是一款强大的基于OpenGLES的跨平台游戏开发...
    1.  来源 QuickV3sample项目中的2048样例游戏,以及最近《...
       Cocos2d-x3.x已经支持使用CMake来进行构建了,这里尝试...