Why do we divide by 2 getContentSize? - cocos2d-x

In sample from Cocos2d-x Tutorials when we describe actionMove
CCFiniteTimeAction* actionMove =
CCMoveTo::create( (float)actualDuration,
ccp(0 - target->getContentSize().width/2, actualY) );
we set a point ccp(0 - target->getContentSize().width/2, actualY). If we have target with 20 width then we have point (-10, actualY), and half target must be visible, but it's not so. Why?
Initial target position
target->setPosition(
ccp(winSize.width + (target->getContentSize().width/2),
actualY) );
Here we also divide by 2, but I understand it (winSize.width + any number and target becomes invisible, outside the screen).

Cocos2d-x uses the center of the object as the origin/anchor point, rather than the corner. So if you want half of your object to be visible on the edge of the screen, use:
ccp(0, actualY)
or
ccp(winSize.width)
You add half of the contentSize if you want the object to be completely off the screen.

Related

Jittering lines when scrolling a TMX map in cocos2d-x

I feel like I'm missing some fundamental concept as to why I am getting flickering when moving a tile map around.
I create a layer. In it, I add a TMXTiledMap.
_tileMap = TMXTiledMap::create("TMX/32Map.tmx");
_tileMap->setScale(1.f);
_floorLayer = _tileMap->getLayer("Floor");
this->addChild(_tileMap);
for(const auto& l : _tileMap->getChildren()) {
static_cast<SpriteBatchNode*>(l)->getTexture()->setAliasTexParameters();
}
this->scheduleUpdate();
In the update I move the layer.
Vec2 newPos = this->getPosition();
newPos.x = (newPos.x - 1);
newPos.y = (newPos.y - 1);
this->setPosition(newPos);
I realize I'm not moving it by dt. If I move it by dt I get an overall jumpiness to the whole layer. I understand this is due to how it renders partial pixels. But if I move it by one pixel like above, I get this # looking set of lines on the screen about 64 pixels or so on top and bottom and about 224 pixels from the left and right
That is when the window is 1024x768. If I make a 320x240 window, I don't see the lines and if I make it 640x480 I only see them on the left and right sides right near the edge of the screen.
Ultimately I'd just like to smoothly scroll a tile map around. Any help would be super appreciated, because I just can't seem to get started on this project.
For me working solution was to change CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL in ccConfig.h from 1 to 0. Find ccConfig.h in cocos/base/.

Why are there black lines between my sprites?

I have a single pixel sprite. To this sprite I add four sprites, each a quarter of a square. To offset the sprites, all i do is change their anchor points.
For example:
top right square is at anchor: (0,0);
bottom right : (0,1);
bottom left : (1,1);
top left : (1,0);
I expected the sprite edges to meet perfectly so that it looks like one big square. Instead there are black lines between the edges of each square so it looks like I have placed four squares close together.
I use texture packer to create a sprite sheet, containing the various squares.
Is there some setting in cocos2d-x or some code I must change to get the sprites to align perfectly ?
Edit: This is for cocos2d-x 3.1.1 and higher. Changing the anchor point is necessary and unavoidable.
EDIT: I use sprite frames from a sprite sheet created using TexturePacker. This was the problem. See my answer below.
The problem has something to do with using a sprite sheet (created using TexturePacker) to hold the pieces together. When you place the frames from the sprite sheet together to form a complete image the lines appear.
You can make the black lines disappear by setting the "Extrude" option in Texturepacker to at least 1.
EDIT: For those of you updating sprite positions based on a physics simulation, black lines can be caused by "sub pixel" positions. Try to either move your objects by complete pixels. Or search for answers with "sub pixel" for other solutions.
casting positions calculations to int type didn't help ?
Generally after certain float calculations like multiplying and divide and then complier auto demoting to to int may result in variation of 1px.
for example 26.500123 can be treated as pixel 26 or 27, depending to your casting methodology.
Test Case:
Are you saying you did this ?
auto testNode = Node::create();
auto s1 = Sprite::create("Images/1.png");
s1->cocos2d::Node::setAnchorPoint(Point(1,0));
auto s2 = Sprite::create("Images/2.png");
s2->cocos2d::Node::setAnchorPoint(Point(0,0));
auto s3 = Sprite::create("Images/3.png");
s3->cocos2d::Node::setAnchorPoint(Point(1,1));
auto s4 = Sprite::create("Images/4.png");
s4->cocos2d::Node::setAnchorPoint(Point(0,1));
testNode->addChild(s1);
testNode->addChild(s2);
testNode->addChild(s3);
testNode->addChild(s4);
testNode->setPosition(Point(screenSize.width/2,screenSize.height/2));
this->addChild(testNode);
and you got 1px gap ? i did that same with cocos2dx 3.1
i got this fine lady
Don't change anchorPoint, you'll regret it later. Calculate the correct position for each sprite.
Make sure the position x/y are on pixel boundaries. Casting to int will do the trick though Retina devices allow for 0.5 positions as well due to pixel density being 2x the point resolution.
Point #2 is also why you shouldn't use anchorPoint because you can't cast the position to integers when offsetting the texture with the anchorPoint.

Scale, Position & Rotate Parent object to make child object take up entire stage

Using the first photo below, let's say:
The red outline is the stage bounds
The gray box is a Sprite on the stage.
The green box is a child of the gray box and has a rotation set.
both display object are anchored at the top-left corner (0,0).
I'd like to rotate, scale, and position the gray box, so the green box fills the stage bounds (the green box and stage have the same aspect ratio).
I can negate the rotation easily enough
parent.rotation = -child.rotation
But the scale and position are proving tricky (because of the rotation). I could use some assistance with the Math involved to calculate the scale and position.
This is what I had tried but didn't produce the results I expected:
gray.scaleX = stage.stageWidth / green.width;
gray.scaleY = gray.scaleX;
gray.x = -green.x;
gray.y = -green.y;
gray.rotation = -green.rotation;
I'm not terribly experienced with Transformation matrices but assume I will need to go that route.
Here is an .fla sample what I'm working with:
SampleFile
You can use this answer: https://stackoverflow.com/a/15789937/1627055 to get some basics. First, you are in need to rotate around the top left corner of the green rectangle, so you use green.x and green.y as center point coordinates. But in between you also need to scale the gray rectangle so that the green rectangle's dimensions get equal to stage. With uniform scaling you don't have to worry about distortion, because if a gray rectangle is scaled uniformly, then a green rectangle will remain a rectangle. If the green rectangle's aspect ratio will be different than what you want it to be, you'd better scale the green rectangle prior to performing this trick. So, you need to first transpose the matrix to offset the center point, then you need to add rotation and scale, then you need to transpose it away. Try this set of code:
var green:Sprite; // your green rect. The code is executed within gray rect
var gr:Number=green.rotation*Math.PI/180; // radians
var gs:Number=stage.stageWidth/green.width; // get scale ratio
var alreadyTurned:Boolean; // if we have already applied the rotation+scale
function turn():void {
if (alreadyTurned) return;
var mat:flash.geom.Matrix=this.transform.matrix;
mat.scale(gs,gs);
mat.translate(-gs*green.x,-gs*green.y);
mat.rotate(-1*gr);
this.transform.matrix=mat;
alreadyTurned=true;
}
Sorry, didn't have time to test, so errors might exist. If yes, try swapping scale, translate and rotate, you pretty much need this set of operations to make it work.
For posterity, here is what I ended up using. I create a sprite/movieClip inside the child (green) box and gave it an instance name of "innerObj" (making it the actually content).
var tmpRectangle:Rectangle = new Rectangle(greenChild.x, greenChild.y, greenChild.innerObj.width * greenChild.scaleX, greenChild.innerObj.height * greenChild.scaleY);
//temporary reset
grayParent.transform.matrix = new Matrix();
var gs:Number=stage.stageHeight/(tmpRectangle.height); // get scale ratio
var mat:Matrix=grayParent.transform.matrix;
mat.scale(gs,gs);
mat.translate(-gs * tmpRectangle.x, -gs * tmpRectangle.y);
mat.rotate( -greenChild.rotation * Math.PI / 180);
grayParent.transform.matrix = mat;
If the registration point of the green box is at one of it's corners (let's say top left), and in order to be displayed this way it has a rotation increased, then the solution is very simple: apply this rotation with negative sign to the parent (if it's 56, add -56 to parent's). This way the child will be with rotation 0 and parent -> -56;
But if there is no rotation applied to the green box, there is almost no solution to your problem, because of wrong registration point. There is no true way to actually determine if the box is somehow rotated or not. And this is why - imagine you have rotated the green box at 90 degrees, but changed it's registration point and thus it has no property for rotation. How could the script understand that this is not it's normal position, but it's flipped? Even if you get the bounds, you will see that it's a regular rectangle, but nobody know which side is it's regular positioned one.
So the short answer is - make the registration point properly, and use rotation in order to display it like in the first image. Then add negative rotation to the parent, and its all good :)
Edit:
I'm uploading an image so I can explain my idea better:
 
As you can see, I've created a green object inside the grey one, and the graphics INSIDE are rotated. The green object itself, has rotation of 0, and origin point - top left.
#Vesper - I don't think that the matrix will fix anything in this situation (remember that the green object has rotation of 0).
Otherwise I agree, that the matrix will do a pretty job, but there are many ways to do it :)

How do I correctly translate my map after scaling?

I'm creating a camera in canvas like the one in Super Smash Bros, where the center of the camera follows the center point of all players and scales to encompass all the players.
I have it set up to find the distance between the 2 players, and if it's larger than the canvas size, the camera scale lowers to decrease the size of the blocks, player sprites, etc.
ctx.scale(cameraS,cameraS);
ctx.translate(-(cameraX*cameraS)+(CANVAS_WIDTH/2),-(cameraY*cameraS)+(CANVAS_HEIGHT/2));
These are what scale and move the drawn images to a position relative to the screen.
This is the actual game using the code and as you can tell, the scaling and moving of the images is slightly incorrect, but I'm not sure why!
https://dl.dropboxusercontent.com/u/51784213/Conjugate/index.html
For reference, the red dot is the position centered between both players. The lines show the dead center of the actual canvas. When scaling is 1(no scaling at all), the red dot is completely centered as it should be. When the scaling starts to decrease, the red dot begins to move off center in weird directions.
For the code to be working correctly, the dot should be centered at all times, even during the scaling process!
Transformations are applied in the reverse order; so you are first translating and then scaling. This means that for a point (x, y), after the current transformation, you get
(
(x + CANVAS_WIDTH/2 - cameraX*cameraS) * cameraS,
(y + CANVAS_HEIGHT/2 - cameraY*cameraS) * cameraS
)
What's actually needed here is the canvas be translated by scaled (cameraX, cameraY) and then be offset by actual (CANVAS_WIDTH/2, CANVAS_HEIGHT/2), so that (cameraX, cameraY) is at center of the visible canvas.
Or rather, the transformation needed here for a point (x, y) is
(
(x - cameraX) * cameraS + CANVAS_WIDTH/2,
(y - cameraY) * cameraS + CANVAS_HEIGHT/2
)
Hence, the code becomes, if you choose to apply translate first,
ctx.scale(cameraS,cameraS);
ctx.translate(-cameraX+CANVAS_WIDTH/(2*cameraS),-cameraY+CANVAS_HEIGHT/(2*cameraS));
Or, if you choose to apply scaling first
ctx.translate(-cameraX*cameraS + CANVAS_WIDTH/2, -cameraY*cameraS + CANVAS_HEIGHT/2);
ctx.scale(cameraS, cameraS);
Working JSFiddle.

Difference between defining sprite's x, y coordinates, and painting object in some location

I have a task:
I need to place about 100 sprites on one canvas (with prepared grid on it). I need to place them as invisible (circles) stones, on the board, and make visible only on mouseover.
The problem I come across is following, I can't place those objects accurately into the nodes on the grid.
E.g.
if I define stones (it's just a sprite, as I said earlier) this way:
var stone:StoneSprite = new StoneSprite();
stone.x = this.x + 2*cellWidth;
stone.graphics.beginFill( 0x000000 );
stone.graphics.drawCircle(stone.x , this.y + cellWidth, cellWidth/3 );
stone.graphics.endFill();
rawChildren.addChild(stone);
They don't sit on the node...
See image:
http://img.skitch.com/20091014-kuhfyjeg1g5qmrbyxbcerp4aya.png
And if I do it this way:
var stone:StoneSprite = new StoneSprite();
stone.graphics.beginFill( 0x000000 );
  stone.graphics.drawCircle(this.x + 2*cellWidth , this.y + cellWidth, cellWidth/3 );
  stone.graphics.endFill();
rawChildren.addChild(stone);
The stone is displayed correctly in the grid node... See image 2:
http://img.skitch.com/20091014-f595tksjxramt98s7yfye591bh.png
So I wonder what is the difference between these 2 approaches.
Also, I think I need to pass correct coordinates to the stone class... In case I would like to change some properties of the stone object. E.g. visibility, or radius.
Could you please suggest, what's wrong in defining coordinates as stone.x, stone.y
How to fix the problem with incorrect positioning.
Would really appreciate ideas about the problem, I am trying to solve for so long :(
Assume x & y are 30 and cellWidth is 30.
First Example:
stone.x = 30 + 60; //90
drawCircle(90, 60, 10);
This means if you were to draw a rectangle around your circle, it would be at [170,50]. (x,y).
Second Example:
stone.x = 0;
drawCircle(90, 60, 10)
This means the rectangle around your circle is at [80,50];
In the first example, you are moving the sprite to position x==90. Then drawing a circle whose center is at x==90 inside the sprite. So relative to this, you're at x==180. But because a circle's x,y coords are the center, subtract 10 for the radius to get the boundary x position.
In the second example, the sprite defaults to position x==0 relative to this and you're drawing the circle inside the sprite at position x==90. (therefore it begins at x==80).
I am not sure what's causing the issue - might be some padding induced by the container - can't say without testing. But I believe that adding a Sprite (say board) to canvas.rawChildren and using it as the parent for the grid and stones would fix the issue.