Set position relative to scene in Cocos2d - c++

I'm trying to use a DrawNode in cocos2d which is a child of a Sprite. The Sprite's position and rotation influences the child DrawNode's position and rotation as you would expect.
I'm trying to set the DrawNode's position and rotation to be not affected by it's parent (the Sprite).
I've tried changing self->addChild(self->drawNode) to self->getScene()->addChild(self->drawNode) which would work perfectly (if it worked), but I receive an error saying self->getScene() returns a nullptr. And this happens because it has not yet been added to any scene.
How can I position the DrawNode relative to the scene/world?
For example:
this->drawNode()->drawLine(Point::ZERO, Point(0, 100), Color4F::RED)
draw's a line going straight up from the sprite.
I am aiming for it to draw a line straight up from the bottom right of the window.

If you want to access the scene before adding the sprite to it, the current running scene can always be accessed from the director.
auto scene = Director::getInstance()->getRunningScene();
scene->addChild(draw_node);

Related

How to add a sprite that is always on the screen in Cocos2d?

I'm doing a platformer game using cocos2d-x v3 in c++, where the maps are usually very large, the visible screen follows the object through the map.
Let's say I want to show a sprite in the top right corner of the screen and it would be in this position even when the screen is following the object.
Using the object position doesn't do it.
Is there a way to show a sprite or whatever in the screen and it would be in the screen even when the screen is moving?
Ps. I'm super noob in game development
As it's written here, you whould use convertToWorldSpace
convertToWorldSpace converts on-node coords to SCREEN coordinates.convertToWorldSpace will always return SCREEN position of our sprite, might be very useful if you want to capture taps on your sprite but need to move/scale your layer.
Generally, the parent node call this method with the child node position, return the world’s postion of child’s as a result. It seems make no sense calling this method if the caller isn’t the parent…
So, as you can read,
Point point = node1->convertToWorldSpace(node2->getPosition());
the above code will convert the node2‘s coordinates to the coordinates on the screen.
For example if the anchor position of node1 is which will be the bottom left corner of the node1, but not necessarily on the screen. This will convert the position of the node2 which is to the screen coordinate of the point relative to node1 ).
Or if you wish, you can get position relative to scenes' anchor points with function convertToWorldSpaceAR.
So there are some assumptions that will have to be made to answer this question. I am assuming that you are using a Follow action on your layer that contains your map. Check here for example. Something like:
// If your playerNode is the node you want to follow, pass it to the create function.
auto cameraFollowAction = Follow:create(playerNode);
// running the action on the layer that has the game/map on it
mapLayer->runAction(cameraFollowAction);
The code above will cause the viewport to "move" to where the player is in world position. So following the player on your map that's bigger than the current viewport. What I did for my in-game menu/hud is add the Hud onto a different layer and add it to the root of the main game scene. The scene that does not have the follow action running on it. Something like below.
// Hud inherits from layer and has all the elements you need on it.
auto inGameHud = HudLayer::create();
// Add the map/game layer to the root of main game scene
this->addChild(mapLayer, 0);
// Add the hud to the root layer
this->addChild(inGameHud, 1);
The code above assumes 'this' to be your MainGameScene. This restricts the Follow action from scrolling the element off the screen. Your element will be on the screen no matter where in World space your scene currently is.
Let me know if this is clear enough. I can help you out more if you get stuck.
I've managed to do it using a Parallax Node, and using the velocity which the sprite goes to Vec2(0,0), this way it stays always on the same spot in the screen.
You can always just put that sprite into different node / layer that everything else is. That way moving this layer / node won't move the sprite

Centering view on a moving position in SFML

I want to use sf::View in SFML in order to change the position of the view, such that the player sprite is always in the center of the screen. Thus I want to write a function which allows me to input a set of coordinates and thus center the screen around those coordinates. In addition I want to be able to set a limit to this, such that when the player reaches the side of the map, that axis of the camera stops following the player, as it has reached a "limit". How do I achieve this?
Thank you in advance.
The function you need is called sf::RenderWindow::setView .
Do something like this:
sf::RenderWindow window (sf::VideoMode(800,600),"Test");
sf::View view ();
view.setCenter (/*Set Center here*/);
window.setView (view);

Cocos2d - removeChild by location

I have a number on CCSprites on screen which have CCNodes placed upon them, and added as children. How can I remove the CCNode child based on touch location (or touched sprite location)?
I have attempted creating a custom method (see below) but am unsure how to fill it in.
-(void)removeChild:(CCNode*)node location:(CGPoint)location cleanup:(BOOL)cleanup;
Is it possible to specify location with the built in method?
[self removeChild:(CCNode *)[node at location] cleanup:(BOOL)cleanup]
You can get local rect of your sprite by sending boundingBox message. Then just test your touch position with CGRectContainsPoint function. The only thing you should remember, that you need convert your coordinates(for example, if your sprites are children of some node, then their rects will be relative to it's (0.f, 0.f) coordinate, not to the scene (0.f, 0.f)). Also you should convert your touch location with sending convertToGL: message to CCDirector shared instance.

Why could a custom QGraphicsItem cause the scene or view to move unless boundingRect is empty?

My Qt 4.8.1 C++ class to draw a crosshair subclasses QGraphicsItem and implements its paint() and boundingRect() methods.
The paint() method consists of two drawLine() and one drawText() calls. The origin is centered.
boundingRect() also respects these coordinates (-,-,+,+) and also the half-pen-width in each direction.
When creating, moving (or positioning) and then adding the object to the (visible) scene, the view shifts a few pixels so that the scene contents move slightly to the bottom right.
If the boundingRect() returns an empty QRectF(), this shift does not happen.
additional elements do not cause further movement of the scene.
a prior update() of the scene does not change the behaviour
a larger boundingRect seems to increase movement
changing the view's transform does not change the behaviour
itemChange() is called numerous times but does not seem to provide a clue (in my eyes):
Crosshair::Crosshair being created, id= "1" at QPointF(63.6, 88.487) scenePos= QPointF(33.6, 58.487)
Crosshair::Crosshair position QPointF(63.6, 88.487)
Crosshair::itemChange ItemFlagsChange QVariant(uint, 1)
Crosshair::itemChange ItemFlagsHaveChanged QVariant(uint, 1)
Crosshair::itemChange ItemFlagsChange QVariant(uint, 33)
Crosshair::itemChange ItemFlagsHaveChanged QVariant(uint, 33)
Crosshair::itemChange ItemFlagsChange QVariant(uint, 2081)
Crosshair::itemChange ItemFlagsHaveChanged QVariant(uint, 2081)
[...]
Crosshair::itemChange ItemPositionChange QVariant(QPointF, QPointF(63.6, 88.487) )
Crosshair::itemChange ItemPositionHasChanged QVariant(QPointF, QPointF(63.6, 88.487) )
Crosshair::itemChange ItemSceneChange QVariant(QGraphicsScene*, )
Crosshair::itemChange ItemSceneHasChanged QVariant(QGraphicsScene*, )
[...]
Crosshair::boundingRect 20 3 QRectF(-11.5,-11.5 21.5x21.5)
Crosshair::boundingRect 20 3 QRectF(-11.5,-11.5 21.5x21.5)
Crosshair::itemChange ItemVisibleChange QVariant(bool, true)
Crosshair::itemChange ItemVisibleHasChanged QVariant(bool, true)
Crosshair::boundingRect 20 3 QRectF(-11.5,-11.5 21.5x21.5)
Crosshair::boundingRect 20 3 QRectF(-11.5,-11.5 21.5x21.5)
...
I might be able to create a minimal example tomorrow, but maybe someone is able to guess my mistake even from this general description of the problem.
Alternatively, hints towards further debugging are of course very welcome!
Edit
Thanks Riateche for the advice.
Some further debug outputs reveal that the sceneRect() indeed is changed, it expands exactly the size of the Crosshair's bounding rect, e.g. from QRectF(0,0 1680x636) to QRectF(-11.5,-11.5 1691.5x647.5). The scene's itemsBoundingRect stays the same.
Now I failed to mention previously that the view has been scaled/transformed with fitInView(scene()->itemsBoundingRect(), Qt::KeepAspectRatio); and noticed that the shift indeed does not occur when the scene is viewed at 100% (e.g. resetTransform()).
But I still do not understand how/why the sceneRect() changes when I am adding an element within the boundingRect(). It must have to do with my custom implementation, because adding an ellipse does not cause a shift of the scene/view.
Ok, here we go: I also failed to mention that I use setFlag(QGraphicsItem::ItemIgnoresTransformations); so that the crosshair stays the same size regardless of scale. This apparently causes the boundingrect to "stay" at the scene's origin when added, and ignores the setPos().
To be continued ...
A scene has a bounding rectangle which is by default the smallest rect containing all items. If you add something to a scene outside of this rectangle, the bounding rect will be expanded.
A view respects the scene's rect. If the scene's rect is smaller than the view's viewport size, the view's scrollbars get hidden and the scene contents appear in the center of the viewport. So, if you add an item, scene's bounding rect changes and all contents get shifted. If the viewport is smaller than bounding rect then scrollbars appear.
If you want to avoid this behavior, you can use QGraphicsView::setSceneRect or QGraphicsScene::setSceneRect to fix the rect position. Note that everything outside of the set rectangle will be unavailable and invisible in the view.
Also consider switching to QGraphicsPathItem (and QGraphicsScene::addPath). It can be used to draw crosshairs.
I was trying whether the problem could be worked around with
QRectF save = scene->sceneRect();
scene->addItem( myGraphicsItem );
scene->setSceneRect( save );
as Riateche suggests on a different note, and it works.
However, I unfortunately still don't fully understand the cause of this.
Here's what the documentation has to say about this flag:
QGraphicsItem::ItemIgnoresTransformations
0x20
The item ignores
inherited transformations (i.e., its position is still anchored to its
parent, but the parent or view rotation, zoom or shear transformations
are ignored). This flag is useful for keeping text label items
horizontal and unscaled, so they will still be readable if the view is
transformed. When set, the item's view geometry and scene geometry
will be maintained separately. You must call deviceTransform() to map
coordinates and detect collisions in the view. By default, this flag
is disabled. This flag was introduced in Qt 4.3. Note: With this flag
set you can still scale the item itself, and that scale transformation
will influence the item's children.
Here is some possibly related information:
https://bugreports.qt-project.org/browse/QTBUG-19039
Issue with fitInView of QGraphicsView when ItemIgnoresTransformations is on

ccSpriteBatchNode child bounding box transformation

I'm using cocos2d 1.0.1.
I've created a CCSpriteBatchNode, it includes a CCSprite (let's name it parentLayer) which includes some X number of childs(CCSprites).
The problem is - when I rotate parentLayer all sprites (childs) correctly displayed, however bounding boxes are at the same place (where they've been before rotation), so world coordinates of those sprites won't be changed.
Off course, all of the above works great without CCSpriteBatchNode. But, I would like to use batch node due to amount of sprites involved.
The question is, is there any way to update bounding boxes & child positions correspondingly?
How many sprites are we talking about? I would just go with a fast enumeration call to rotate each one individually. I nave never noticed a performance hit when doing this, have you?
CCArray *listOfChildren = [parentLayer children];
for (CCSprite *sprite in listOfChildren) {
[sprite setRotation:someValue];
}