I try to clone a CCNode hierarchy, the problems is I need to reset and set all the variable by type, can I have a way to do that more automatically ?
basiclly what I want to do is :
- store a CCNode* (with some child, for example an image at Pos 10-10, and a Label at Pos 100-50 with the text "Test");
- then clone it, for get a new CCNode* with the same default value and childs.
I need to copy it, because after they will get modify, is like a template of Node, before get custom value.
If you know a simple way to copy, and set all the hierarchy (with correct type also), without big if/else statement for each kind of type, it will help me a lot ^^
thanks
This code clones CCNode and all child CCNodes recursively. You can add other subclasses and other properties to copy.
+ (CCNode*) cloneCCNode:(CCNode*)source
{
CCNode* clone = [CCNode node];
for (CCNode* srcSubnode in source.children) {
CCNode* subnode;
if ([srcSubnode isKindOfClass:[CCSprite class]]) { //only CCSprites are copied, add other subclasses if you need to
CCSprite* srcSprite = (CCSprite*)srcSubnode;
subnode = [CCSprite spriteWithTexture:srcSprite.texture];
((CCSprite*)subnode).displayFrame = srcSprite.displayFrame;
} else {
subnode = [self cloneCCNode:srcSubnode];
}
subnode.rotation = srcSubnode.rotation;
subnode.position = srcSubnode.position;
subnode.anchorPoint = srcSubnode.anchorPoint;
subnode.zOrder = srcSubnode.zOrder;
[clone addChild:subnode];
}
return clone;
}
Related
I want to select rectangle/polyline through scene with mouse click and should be able to print it's name and other property. It's name and other properties are in the graph node. But I dont want to interact graph again.
So when I was drawing rectangle/polyline through graph co-ordinates, I should be able to store some pointer of graph node on rectangle/polyline so when I will click on rectangle/polyline, then through that pointer I can access it's name and other properties.
Question is `Is this possible ?
Among all above parameters, I want to store only _Ptr ( it is basically a pointer, but store as long, while using it will be type cast )
rect = new myRect();
while(true)
{
for(auto iter = verts.begin();iter != verts.end();++iter)
{
// getting co-ordinates of rectangle
QGraphicsRectItem* rectItem = rect->createRect(co-ordinates of rectangle);
rect->_Ptr = iter->_Ptr; // trying to store _crossRefPtr
}
}
myRect.h
class myRect : public QGraphicsRectItem
{
........
QGraphicsRectItem* createRect(QRectF& rect);
}
And when I will click on rectangle on scene through mouse I am doing like this:
if(_scene->selectedItems().count() != 0)
{
foreach(QGraphicsItem* currentItem, _scene->selectedItems())
{
QGraphicsRectItem* rItem = qgraphicsitem_cast<QGraphicsRectItem*>(currentItem);
if(rItem)
{
myRect* r = reinterpret_cast<myRect*>(rItem);
if(r)
{
Instance (rectangle)
Instance* i = reinterpret_cast<Instance*>(r->_boostRefPtr);
qDebug()<< i->Name();
}
}
But aobve way is wrong. Getting run time errors ( Showing Verbose stack trace)
So the question is :
How to store pointer on QGraphicsItem so that, once they get selected,
that pointer will be accessed ?
Use Qt's dynamic properties, check QObject::setProperty. It should do the trick.
But AFAIC, I would have used a double QMap to associate directly <graph_node, QGraphicsItem> AND <QGraphicsItem, graph_node> - so you can search quickly for both associations, and both in O(log2(n)) complexity. You can store this either as a static part of your graph (better) or standalone (not the best idea). Obviously, if your graph is already in O(log2(n)), you don't need this map and you need only the <QGraphicsItem, graph_node> one.
I've realized that CCPhysicsSprite is different in a few ways from CCSprite. For example, you must set the body before you set the position of the sprite. I believe it is one of these differences that is causing an EXC_BAD_ACCESS error when I try destroying the body. I call the scheduleSprite method in the update method.
-(void)scheduleSprite {
if ([testSprite physicsSprite].b2Body != NULL) {
b2Vec2 force = b2Vec2(-5, 10.0 * [testSprite physicsSprite].b2Body->GetMass());
[testSprite physicsSprite].b2Body->ApplyForce(force, [testSprite physicsSprite].b2Body->GetWorldCenter() );
if ([testSprite physicsSprite].position.x < 0) {
world->DestroyBody([testSprite physicsSprite].b2Body);
[testSprite physicsSprite].b2Body = NULL;
}
}
}
I get an EXC_BAD_ACCESS pointing to line
b2Vec2 pos = _b2Body->GetPosition();
in the
-(CGAffineTransform) nodeToParentTransform
method, within the class
CCPhysicsSprite.mm
Thanks.
Despite you destroyed the body, the sprite will keep doing its stuff, so you may remove the sprite from its parent also, something like.-
if ([testSprite physicsSprite].position.x < 0) {
world->DestroyBody([testSprite physicsSprite].b2Body);
[[testSprite physicsSprite] removeFromParentAndCleanup:YES];
}
- (void) killBody:(cpBody*)body
{
cpBodyEachShape_b(body, ^(cpShape *shape) {
cpSpaceRemoveShape( _space, shape );
cpShapeFree(shape);
});
cpSpaceRemoveBody( _space, body );
cpBodyFree(body);//edited to include this line
CCPhysicsSprite* sprite = (__bridge CCPhysicsSprite*) body->data;
[sprite removeFromParentAndCleanup:true];
}
I was getting the same thing, the method above seemed to fix it.
First, remove the shapes attached to the body from the space. I'm using _space as a instance variable pointing to a cpSpace object. Next, remove the body from the space. Finally, remove the sprite from the parent.
When I make a CCPhysicsSprite object, I connect its body to the sprite using body->data. You can see that in the method above.
If you are starting with a CCPhysicsSprite object, you would first get the cpBody object from the sprite, then remove the body and shape as shown above.
I am new to cocos2d game development.I have subclassed a CCNode,and i am looping to add this subclass to the game layer setting the tag for this node.In the m file of this subclass i am using the touches delegate to detect the element.But the console shows that for any object of this node created tag's are the same.
You must initialise tag for each node. Use uniq integer number as tag. Better use enum.
typedef enum
{
kEnemy_Dog = 1000,
kEnemy_Cat,
kEnemy_Rat,
kEnemy_Bat
}EnemyType;
typedef enum
{
kHeroType_Lion = 2000,
kHeroType_Tiger,
kHeroType_Dino,
kEnemy_Eagle
}HeroType;
CCSprite *dog = [CCSprite spriteWithFile:TEX_DOG];
dog.tag = kEnemy_Dog;
[self addChild:dog z:2 tag: kEnemy_Dog];
CCSprite *lion = [CCSprite spriteWithFile:TEX_DOG];
lion.tag = kHeroType_Lion;
[self addChild:dog z:2 tag: kHeroType_Lion];
//to check
if(inSprite.tag == kHeroType_Lion)
I'm following "Learn Cocos2D" and in chapter 4 I'm met with the following directive:
And in the GameLayer init method, add the call to the initSpiders method discussed next, right after scheduleUpdate:
-(id) init {
if ((self=[super init])) {
...
[self scheduleUpdate];
[self initSpiders];
}
return self;
}
I get and ARC error message: no visible #interface for 'GameLayer' declares the selector 'initSpiders'
I get the same message at the line: self resetSpiders
what am i missing? everything builds and runs great up to that point.
This issue derives from the fact that the initSpiders and resetSpiders are not declared in your class interface and are defined in the .m file after the point where they are used.
If they are not missing altogether, you can fix this in either of 2 ways:
move the definition of the initSpiders and resetSpiders methods above your init method and the errors will disappear;
add a declaration for both methods in the #interface of the class.
(If you do both, it will also work)
Check your code to see if the implementation for those methods is available.
Your error appears to be that you haven't followed the next bit of the book too. Completing the next section should allow you to compile your code without warnings like this.
A more complete extract of that section of the book is:
And in the GameScene init method add the call to the initSpiders method discussed next, right after scheduleUpdate:
-(id) init {
if ((self = [super init]))
{
… 96 CHAPTER 4: Your First Game
[self scheduleUpdate];
[self initSpiders];
}
return self;
}
After that a fair bit of code is added to the GameScene class, beginning with the initSpiders method in Listing 4–8, which is creating the spider sprites.
Listing 4–8. For Easier Access, Spider Sprites Are Initialized and Added to a CCArray
-(void) initSpiders
{
CGSize screenSize = [[CCDirector sharedDirector] winSize];
// using a temporary spider sprite is the easiest way to get the image's size
CCSprite* tempSpider = [CCSprite spriteWithFile:#"spider.png"];
float imageWidth = [tempSpider texture].contentSize.width;
// Use as many spiders as can fit next to each other over the whole screen width.
int numSpiders = screenSize.width / imageWidth;
// Initialize the spiders array using alloc.
spiders = [[CCArray alloc] initWithCapacity:numSpiders];
for (int i = 0; i < numSpiders; i++)
{
CCSprite* spider = [CCSprite spriteWithFile:#"spider.png"];
[self addChild:spider z:0 tag:2];
// Also add the spider to the spiders array.
[spiders addObject:spider];
}
// call the method to reposition all spiders
[self resetSpiders];
}
I have one question when infinite background scrolling is done, is the object remain fixed(like doodle in doodle jump, papy in papi jump) or these object really moves.Is only background move or both (background and object )move.plz someone help me.I am searching for this solution for 4/5 days,but can't get the solution.So plz someone help me. And if object does not move how to create such a illusion of object moving.
If you add the object to the same layer as the scrolling background, then it will scroll as the background scrolls.
If your looking for an effect like the hero in doodle jump, you may want to look at having two or more layers in a scene.
Layer 1: Scrolling Background Layer
Layer 2: Sprite layer
SomeScene.m
CCLayer *backgroundLayer = [[CCLayer alloc] init];
CCLayer *spriteLayer= [[CCLayer alloc] init];
[self addChild:backgroundLayer z:0];
[self addChild:spriteLayer z:1];
//Hero stays in one spot regardless of background scrolling.
CCSprite *squidHero = [[CCSprite alloc] initWithFile:#"squid.png"];
[spriteLayer addChild:squidHero];
If you want objects to scroll with the background add it to the background layer:
//Platform moves with background.
CCSprite *bouncePlatform= [[CCSprite alloc] initWithFile:#"bouncePlatform.png"];
[backgroundLayer addChild:bouncePlatform];
Another alternative is to use a CCFollow action. You would code as if the background is static (which it will be) and the player is moving (which it will be), but add a CCFollow action to the player. This essentially moves the camera so that it tracks your player.
You can also modify the classes so that you can get the CCFollow action to follow with an offset (i.e., so the player is not in the middle of the screen) as well as to have a smoothing effect to it, so that when the player moves, the follow action is not jerky. See the below code:
*NOTE I am using cocos2d-x, the c++ port. The methods are similar in cocos2d, and you should be able to modify these to fit the cocos2d syntax. Or search around -- I found these for cocos2d and then ported to c++.
//defines the action to constantly follow the player (in my case, "runner.p_sprite is the sprite pointing to the player)
FollowWithOffset* followAction = FollowWithOffset::create(runner.p_sprite, CCRectZero);
runAction(followAction);
And separately, I have copied the class definition for CCFollow to create my own class, CCFollowWithAction. This also has a smoothing effect (you can look this up more online) so that when the player moves, the actions are not jerky. I modified "initWithTarget," to take into account an offset, and "step," to add a smoothing action. You can see the modifications in the comments below.
bool FollowWithOffset::initWithTarget(CCNode *pFollowedNode, const CCRect& rect/* = CCRectZero*/)
{
CCAssert(pFollowedNode != NULL, "");
pFollowedNode->retain();
m_pobFollowedNode = pFollowedNode;
if (rect.equals(CCRectZero))
{
m_bBoundarySet = false;
}
else
{
m_bBoundarySet = true;
}
m_bBoundaryFullyCovered = false;
CCSize winSize = CCDirector::sharedDirector()->getWinSize();
m_obFullScreenSize = CCPointMake(winSize.width, winSize.height);
//m_obHalfScreenSize = ccpMult(m_obFullScreenSize, 0.5f);
m_obHalfScreenSize = CCPointMake(m_obFullScreenSize.x/2 + RUNNER_FOLLOW_OFFSET_X,
m_obFullScreenSize.y/2 + RUNNER_FOLLOW_OFFSET_Y);
if (m_bBoundarySet)
{
m_fLeftBoundary = -((rect.origin.x+rect.size.width) - m_obFullScreenSize.x);
m_fRightBoundary = -rect.origin.x ;
m_fTopBoundary = -rect.origin.y;
m_fBottomBoundary = -((rect.origin.y+rect.size.height) - m_obFullScreenSize.y);
if(m_fRightBoundary < m_fLeftBoundary)
{
// screen width is larger than world's boundary width
//set both in the middle of the world
m_fRightBoundary = m_fLeftBoundary = (m_fLeftBoundary + m_fRightBoundary) / 2;
}
if(m_fTopBoundary < m_fBottomBoundary)
{
// screen width is larger than world's boundary width
//set both in the middle of the world
m_fTopBoundary = m_fBottomBoundary = (m_fTopBoundary + m_fBottomBoundary) / 2;
}
if( (m_fTopBoundary == m_fBottomBoundary) && (m_fLeftBoundary == m_fRightBoundary) )
{
m_bBoundaryFullyCovered = true;
}
}
return true;
}
void FollowWithOffset::step(float dt)
{
CC_UNUSED_PARAM(dt);
if(m_bBoundarySet){
// whole map fits inside a single screen, no need to modify the position - unless map boundaries are increased
if(m_bBoundaryFullyCovered)
return;
CCPoint tempPos = ccpSub( m_obHalfScreenSize, m_pobFollowedNode->getPosition());
m_pTarget->setPosition(ccp(clampf(tempPos.x, m_fLeftBoundary, m_fRightBoundary),
clampf(tempPos.y, m_fBottomBoundary, m_fTopBoundary)));
}
else{
//custom written code to add in support for a smooth ccfollow action
CCPoint tempPos = ccpSub( m_obHalfScreenSize, m_pobFollowedNode->getPosition());
CCPoint moveVect = ccpMult(ccpSub(tempPos,m_pTarget->getPosition()),0.25); //0.25 is the smooth constant.
CCPoint newPos = ccpAdd(m_pTarget->getPosition(), moveVect);
m_pTarget->setPosition(newPos);
}
}