cocos2d -CCLabelTTF is not printing more then once - cocos2d-iphone

i am losing my mind !!
i have done this so many times and now its just not working !
in my init method i have this label :
teamLabel = [CCLabelTTF labelWithString:#"WAITING..." fontName:#"Marker Felt" fontSize:32];
teamLabel.position = ccp( 150,100);
teamLabel.color = ccc3(150, 50, 80);
[self addChild:teamLabel];
[teamLabel setVisible:YES];
then i call a function which i know it has been called(NSLOG) and there i am trying to print something in all kind of ways:
[teamLabel setString:#"ran"];
[teamLabel setString:[NSString stringWithFormat:#"%ig", (int) (ran)]]; //ran int==5;
the function is fired, but i can still see the first string from the init in my label without a change..
EDIT ::
it now prints a red big square which is in the size of my word. if the word is small i get small red square etc...
whats that ?!?
thanks .

I once came upon similar behavior, and it ended being due to lack of memory to allocate the TTF texture.
Perhaps you have been growing your game/app resource utilization and you are now seeing the same behavior.
Try disabling a number of these other resources, or create the TTF labels only. You can also try moving the creation of these TTFs out of the init method, to check whether there might be a load order/priority problem.
Cheers,

Related

Why doesn`t Cosos2d ClippingNode work properly?

I try to use ClippingNode for my Cocos2d project, but due to some unknown reason it doesn`t work the proper way neither on Iphone, nor Android. Here is the code being used. The stencil is Label with string "7". Are there any mistakes or is it simply a Cocos2d problem?
auto colors = Sprite::create("colors.png");
colors->setContentSize(Size(nodeSize.width * 1.25, nodeSize.height * 1.25));
colors->setPosition(recordNumLbl->getPosition());
colors->setName("recordNum");
auto cropNode = ClippingNode::create(recordNumLbl);
cropNode->setGlobalZOrder(11);
cropNode->setName("cropNode");
cropNode->addChild(colors);
this->addChild(cropNode);
You can see the result I get on the first image, and what I try to get on the second one. Any help is highly appreciated!
https://i.stack.imgur.com/fZ9LX.png
https://i.stack.imgur.com/xH1hp.png
The global Z value needs to be exactly the same for any children of a clipping node. So, for the example you posted, you would need to set this:
colors->setGlobalZOrder(11);
Also, make sure the stencil you use (recordNumLbl?) is also set to a global Z of 11.

Crash after QGraphicsScene::removeItem() with custom item class

I am populating a QGraphicsScene with instances of a custom item class (inherting QGraphicsPathItem). At some point during runtime, I try to remove an item (plus its children) from the scene by calling:
delete pItem;
This automatically calls QGraphicsScene::removeItem(), however it also leads to a crash in the class QGraphicsSceneFindItemBspTreeVisitor during the next repaint.
TL;DR: The solution is to ensure that QGraphicsItem::prepareGeometryChange() gets called before the item's removal from the scene.
The problem is that during the item removal from the scene, the scene internal index was not properly updated, resulting in the crash upon the next attempt of drawing the scene.
Since in my case, I use a custom subclass from QGraphicsPathItem, I simply put the call to QGraphicsItem::prepareGeometryChange() into its destructor since I am not manually removing the item from the scene (via QGraphicsScene::removeItem()), but instead I simply call delete pItem; which in return triggers the item's destructor as well as removeItem() later on.
I ran into the same issue using PySide2.
Disabling BSP indexing (as mentioned here) does work for me and is most likely the actual solution to the problem. But is a sub-optimal one, because the scene that I am working with can get arbitrarily large. I also tried to call prepareGeometryChange before removing the item, and while that did seem to work for a while, the error re-appeared just a few weeks later.
What worked for me (so far) is manually removing all child items before removing the item itself...
To that end, I am overwriting the QGraphicsScene::removeItem method in Python:
class GraphicsScene(QtWidgets.QGraphicsScene):
def removeItem(self, item: QtWidgets.QGraphicsItem) -> None:
for child_item in item.childItems():
super().removeItem(child_item)
super().removeItem(item)
Note that this will not quite work the same in C++ because QGraphicsScene::removeItem is not a virtual method, so you will probably have to add your own method removeItemSafely or whatever.
Disclaimer: Other methods have worked for me as well ... until they didn't. I have not seen a crash in QGraphicsSceneFindItemBspTreeVisitor::visit since introducing this workaround, but that does not mean that this is actually the solution. Use at your own risk.
I had this issue and it was a real pain to fix it. Besides the crash, I was also having "guost" items appearing on the screen.
I was changing the boundingRect size 2x inside a custom updateGeometry() method that updates the boundingbox and shape caches of the item.
I was initializing the boundig rectangle as QRectf():
boundingBox = QRectF();
... then doing some processing (and taking the opportunity to do some clean ups in unneeded objects from the scene).
And finally setting the value of the boundingRect to its new size:
boundingBox = polygon.boundingRect();
Calling prepareGeometryChange() in the beggining, alone, didn't solve the issue since I was changing it's size twice.
The solution was to remove the first attribution.
It seems the issue lasting for long time today and there are open bugs also.
But it seems to have a workaround, which I could find it useful and after hours of debugging and reading and investigations I have found it here:
https://forum.qt.io/topic/71316/qgraphicsscenefinditembsptreevisitor-visit-crashes-due-to-an-obsolete-paintevent-after-qgraphicsscene-removeitem/17
Some other tips and tricks regarding Graphics Scene here:
https://tech-artists.org/t/qt-properly-removing-qgraphicitems/3063/6

To extract one char from label that are made from CClabelBMFont

I am trying to show slowly one char from string ,like novel game.
To do it,at first I wrote the code as follows using CCLabelBMFont to extract one char from string.
string str = "I like an apple";
CCLabelBMFont *label = CCLabelBMFont::create(str.c_str(), "font.fnt");
CCSprite *spr = (CCSprite*)label1->getChildByTag(0);
spr->setPosition(ccp(100, 100));
this->addChild(spr);
I want to show spr that are extracted from CCLabelBMFont in GameScene(this)
But I am getting the error as follows
CCAssert( child->m_pParent == NULL, "child already added. It can't be added again");
Why did such the error appear ? and How should I do?
Look at the method you are using and the error you get. You use getChildByTag(...) to get your sprite. So what you get is a child node of your CCLabelBMFont, meaning it has a parent. Your error says that this sprite cannot be added again as a child, because it already has a parent.
I can't think of a straigthforward and sure way to achieve what you want, but here are some suggestions you may try :
Adding each letter as a seperate label - to be honest, this is really dumb as it unnecessarily multiplies your code and would use too much memory.
Making the children of your label invisible, and them make them visible one by one. When you create the label, iterate through its children and call setVisible(false) on them. Then schedule a call in which you will call setVisible(true) on subsequent children.
Possibly creating subclass of LabelBMFont and/or creating a custom action may be the most flexible way, but propably not the easiest.
Let me know if anything is not clear!

Cocos2d and SpriteBatchNode: cannot identify which sprite frame is causing an Assertion to fail

I have already asked something similar but I can't figure out properly how to debug this. That's the question.
I added some Exceptions handler (catches all Objective-C) exceptions and that's the result of what I see:
The problem is with the setTexture method and it fails at the assertion verifying whether the texture name that needs to be displayed is the same as the one in the current sprite batch node.
This happens when trying to replace one scene with another but doesn't happen all the times. It has to do with the new scene as I tried to "isolate" the problem by calling the replace from a different part of the game and it still does give trouble.
In the game scene I have a couple of sprite sheets and sprite batch nodes but as I don't manage to isolate a sprite sheet id I cannot understand which sprite frame is giving me the problem, as well as I don't understand why this happens only sometimes.
I would like to:
understand which sprite frame name gives me the AssertionFailure
understand to which sprite sheet it belongs
This should help me to understand if it is a naming problem or if this has to do with something else.
Hoping not to be too lame with this question..
EDIT: I tried the answer but I am not able to read the 'fileName' information, here is what the debugger says "Summary unavailable":
That's how I create the filename property:
/** TMP: Bug solving filename */
#property (copy) NSString *fileName;
-(id) initWithTexture:(CCTexture2D*)texture rectInPixels:(CGRect)rect rotated:(BOOL)rotated offset:(CGPoint)offset originalSize:(CGSize)originalSize
{
if( (self=[super init]) )
{
self.fileName = [NSString stringWithFormat:#"GLUINT texture name: %i", texture.name];
self.texture = texture;
rectInPixels_ = rect;
rect_ = CC_RECT_PIXELS_TO_POINTS( rect );
offsetInPixels_ = offset;
offset_ = CC_POINT_PIXELS_TO_POINTS( offsetInPixels_ );
originalSizeInPixels_ = originalSize;
originalSize_ = CC_SIZE_PIXELS_TO_POINTS( originalSizeInPixels_ );
rotated_ = rotated;
}
return self;
}
-(id) initWithTextureFilename:(NSString *)filename rectInPixels:(CGRect)rect rotated:(BOOL)rotated offset:(CGPoint)offset originalSize:(CGSize)originalSize
{
if( (self=[super init]) )
{
self.fileName = fileName; //TMP
texture_ = nil;
textureFilename_ = [filename copy];
rectInPixels_ = rect;
rect_ = CC_RECT_PIXELS_TO_POINTS( rect );
offsetInPixels_ = offset;
offset_ = CC_POINT_PIXELS_TO_POINTS( offsetInPixels_ );
originalSizeInPixels_ = originalSize;
originalSize_ = CC_SIZE_PIXELS_TO_POINTS( originalSizeInPixels_ );
rotated_ = rotated;
}
return self;
}
In such cases logging is your friend. Every time you create a CCAnimate action (or CCAnimation) you should log something like this:
// during 'create sprite frames from sprite frame names' loop
NSLog(#"adding sprite frame name for CCAnimate: %#", spriteFrameName);
// after CCAnimate was created
NSLog(#"creating CCAnimate %# with sprite frames: %#, animate, arrayOfSpriteFrames);
You will probably want to add more detail, like which sprite frame names were added to that CCAnimate. You may also have to add additional logging if you cache CCAnimations and reuse them later (log each CCAnimation when reused).
Now when you receive that error you should select the [CCSprite setDisplayFrame:] method in the call stack. The debugger will then show you the value for the CCSpriteFrame it wants to set. Look for the pointer value, it will read something like 0x254fb22e.
Search for that value in your log, this will bring you back to one of the "creating CCAnimate.." logs. From the log lines above you can see the sprite frame names it contains. Since you also logged the 'arrayOfSpriteFrames' you can get their pointer value, compare it with the pointer value of the CCSpriteFrame that caused the assertion.
When you have a match, and it's the fourth item in the sprite frame array, just look up the name of the fourth sprite frame name added to the CCAnimate.
There may be a quicker way to do this depending on which information is available in the debugger (and how well versed you are in debugging), but this is one approach that will definitely lead you to the offending sprite frame name in relatively short time.
Note that the pointer values are not unique - it's possible that a different CCAnimate was created with the same pointer value. Especially if there's a high frequence of CCAnimate playing and stopping it can happen that another CCAnimate is allocated at the exact same memory location of a previous one. So be wary if the results don't seem to match up. A quick way to find out is to test on different devices or Simulator vs. Device, as pointer values and allocation strategies vary.
You don't need to log which sprite frame name belongs to which texture atlas. Simply open the plist of each atlas and search for the sprite frame name. The plist is an XML file.
Tip: common cause for this problem can be having the same sprite frame name in two different texture atlases - cocos2d may use either texture when you request a sprite frame with a duplicate name.
Another Tip: if the logging seems daunting, I'd simply do the following:
open source code for CCSpriteFrame class
add a NSString* property 'filename' with 'copy' attribute
every time you create a CCSpriteFrame object, assign the filename to the CCSpriteFrame
Now whenever you see a CCSpriteFrame in the debugger, it'll show you the associated filename in the debugger view.
ok , looks like you have been there before ... Comment the first NSAssert, uncomment the if block. Then put a break point on the newly uncommented NSAssert. Execution will pause BEFORE the exception, and you should be to inspect the iVars of each class instance in the call stack (at least i can with AppCode, hope xCode allows that). Maybe you will get enough of a hint to figure out which texture/animation/batchnode is giving you a hard time.
Also ... fyi. Whenever i start a project with cocos2d (any version) , i apply some 'standard' fixes to it so that my life will be easier for debugging. A standard 'patch' is to add a name to CCNode, which i set to some meaningful value : always. Also i override the descpription method to return MY name. Assigning meaningful names has helped me many times, especially when walking down (or up) the node hierarchy to figure out a bug. I also nuke many of the NSAsserts and whenever possible (especially in ctors), return nil and log an error message. As per Stefen's suggestion, if a cocos2d ctor returns me a nil, i spew out an error log message. I can then break on that log statement and drill-down the issue.

runAction do not works for sprites coming from CCSpriteBatchNode

I am using Cocos2d 2.1rc0.
I have this project that was working perfectly when I was not using CCSpriteBatchNode. Then I decided to use batch nodes to reduce draw calls and my problems started.
A lot of stuff is not working well. reorderChild is one. Another one is runAction and without runAction Cocos is useless.
This is an example of a method that works without batchNodes and do not work with it.
// move/rotate all objects
for (int i=0; i<[allObjects count]; i++) {
Card *object = [allObjects objectAtIndex:i];
[object stopAllActions];
CGPoint center = object.position;
center.x = center.x + 100;
center.y = center.y - 200;
CCMoveTo *moveAction = [CCMoveTo actionWithDuration:0.3f position:ccp(center.x, center.y)];
CCRotateTo *rotateAction = [CCRotateTo actionWithDuration:0.3 angle:0.0f];
CCSpawn *action = [CCSpawn actions:moveAction, rotateAction, nil];
[object runAction:[CCSequence actions: action,
[CCDelayTime actionWithDuration:0.1f],
nil]];
}
Exactly nothing happens.
I have tried to eliminate the CCSpanw and use runAction directly just with move and nothing works. If I use regular sprites, it works.
Objects in that array derive from a CCSprite based class.
Is there any workaround?
the solution is to cast the class to the object extracted from the array...
instead of
Card *object = [allObjects objectAtIndex:i];
this
Card *object = (Card *)[allObjects objectAtIndex:i];
After double-checking in a clean project that this isn't a weird side-effect of some kind, I have to say there's something fishy about your project. Hard to tell what, though.
What I did: create a sprite-batch, add a sprite to it, also store it in an array. In a scheduled method I'm receiving the sprite from the array (not casting) and run your action sequence posted above. It works fine, as expected.
The casting should not make any difference. Batched or non-batched sprite should not make any difference either.
If it does, something really weird is going on. After all the card object is the same with or without casting. If it were not actually running the runAction method, you'd be receiving an "unrecognized selector sent to instance" error. But that's not the case.
Try again without casting, after rebooting your device, your machine, cleaning the project in Xcode and rebuilding. Also test in debug and release configurations. I've had the weirdest issues that were gone after doing one of the above, and definitely all of the above. If that doesn't fix things, you can be sure it's a problem with the code (memory leak is my alltime favorite) or the project settings (ie uncommon compiler optimizations sometimes can have side-effects).
Step into the runAction method if it really doesn't run the action - I'm sure it will add the action to the action manager. Try with and without casting to see if there really is a different code path taken. I doubt it.