Dynamic cc.Node creation, Anchor point and positioning issues

Hi,

I will try to keep it simple. The problem is that when you create nodes with Cocos Creator and position them with X or Y values and create child to them, then the anchor point stays where it should.

When you dynamically by code generate nodes (new cc.Node('child')) and change parent’s position (X or Y) it messes up the anchor points completely.

I will try to demonstrate it here:
Creating Nodes with Cocos Creator (no issues):
Animated GIF - Find & Share on GIPHY (video)
Here you see:
Grandparent

  • X Position: -50
  • Height: 500
  • Width: 500

→ Parent (child of Grandparent)

  • X Position: -200
  • Height: 500
  • Width: 100

→ Child (child of Parent)

  • X Position: 0
  • Height: 100
  • Width: 100

All of the nodes have anchor points of 0.5.
The end result is exactly as designed:
06


When creating the same dynamically with new cc.Node('...'):

Without X-Positioning: (all good!):

With Grandparent node X-Positioning -50: (Breaks anchor point between Grandparent and Parent nodes):

With Parent node X-Positioning -200 (Breaks anchor point between Parent and Child nodes)

This seems like a too basic thing to be a bug? What am I doing wrong here while dynamically creating the Nodes?

I’m very sorry for pinging you @slackmoehrle and @jare, but there must be an easy explanation to this and I would like to continue my work.

Thank you so much for your time!

Pasting code here for you to test if you like. It doesn’t matter at what point you place the X-Positioning, it always breaks the anchor points.

const grandParent = new cc.Node('GrandParent');
this.node.addChild(grandParent);

const parent = new cc.Node('Parent');
grandParent.addChild(parent);

const child = new cc.Node('Child');
parent.addChild(child);

grandParent.anchorX = 0.5;
grandParent.anchorY = 0.5;
grandParent.width = 500;
grandParent.height = 500;
grandParent.x = -50;

parent.anchorX = 0.5;
parent.anchorY = 0.5;
parent.width = 100;
parent.height = 500;
parent.x = -200;

child.anchorX = 0.5;
child.anchorY = 0.5;
child.width = 100;
child.height = 100;

const debugNodes = [
	{ node: grandParent, color: cc.Color.RED, lineWidth: 10 },
	{ node: parent, color: cc.Color.GREEN, lineWidth: 5 },
	{ node: child, color: cc.Color.BLUE, lineWidth: 3 },
];

debugNodes.forEach(curr => {
	const drawing = curr.node.addComponent(cc.Graphics);
	drawing.rect(curr.node.x, curr.node.y, curr.node.width, curr.node.height);
	drawing.lineWidth = curr.lineWidth;
	drawing.strokeColor = curr.color;
	drawing.stroke();
});

Moving in X-Axis work properly keeping correct anchor points with:

grandParent.runAction(cc.sequence(
	cc.moveBy(0, -50, 0),
	cc.callFunc(() => {
		cc.log(grandParent.getPositionX()); // -50
		cc.log(parent.getPositionX()); // 0
		cc.log(child.getPositionX()); // 0
	})
));

But it feels stupid to run an action just to tell a node to change position and keep child nodes anchor points?

1 Like

Right, this is not a proper fix. @jare can help. I can test too in the mean time.

1 Like

Thank you @slackmoehrle! I think this seems like a very essential thing to fix if it actually shows to be a bug, since everything is based on cc.Nodes.

@jare I hope my code snippet is clear enough to test and solve this with, otherwise please let me know if I can help you with anything else!

I will ask @pandamicro to check this problem.

1 Like

Thanks!

I have made some more research and have another code snippet for you to prove that it’s acting strange. Now using Actions and moveBy(). moveBy() is obviously not optimal since it’s asynchronous and also not the meant way to move stuff around, but it works a bit better than settings just x and y on Nodes. The whole thing seems to be some kind of asynchronous bug as well. Check the code below!

First example: (actually working)

const grandParent = new cc.Node('GrandParent');
this.node.addChild(grandParent);

const parent = new cc.Node('Parent');
grandParent.addChild(parent);

const child = new cc.Node('Child');
parent.addChild(child);
const child2 = new cc.Node('Child2');
parent.addChild(child2);
const child3 = new cc.Node('Child3');
parent.addChild(child3);

grandParent.anchorX = 0.5;
grandParent.anchorY = 0.5;
grandParent.width = 800;
grandParent.height = 500;

parent.anchorX = 0.5;
parent.anchorY = 0.5;
parent.width = 500;
parent.height = 300;

child.anchorX = 0.5;
child.anchorY = 0.5;
child.width = 100;
child.height = 100;
child2.anchorX = 0.5;
child2.anchorY = 0.5;
child2.width = 100;
child2.height = 100;
child3.anchorX = 0.5;
child3.anchorY = 0.5;
child3.width = 100;
child3.height = 100;

const nodeActions = [{
		node: grandParent,
		action: cc.moveBy(0, cc.v2(-50, 0))
	}, {
		node: parent,
		action: cc.moveBy(0, cc.v2(-150, 0))
	}
];

parent.children.forEach((c, index) => {
	nodeActions.push({
		node: c,
		action: cc.moveBy(0, cc.v2(100 * index, 0))
	});
})

// Run all Moving actions that are collected in the array at once
nodeActions.forEach(item => item.node.runAction(item.action));

const debugNodes = [
	{ node: grandParent, color: cc.Color.RED, lineWidth: 10 },
	{ node: parent, color: cc.Color.GREEN, lineWidth: 5 },
	{ node: child, color: cc.Color.BLUE, lineWidth: 3 },
	{ node: child2, color: cc.Color.CYAN, lineWidth: 3 },
	{ node: child3, color: cc.Color.YELLOW, lineWidth: 3 },
];

debugNodes.forEach(curr => {
	const drawing = curr.node.addComponent(cc.Graphics);
	drawing.rect(curr.node.x, curr.node.y, curr.node.width, curr.node.height);
	drawing.lineWidth = curr.lineWidth;
	drawing.strokeColor = curr.color;
	drawing.stroke();
});

Result:


Second example: (Causing wrong positions).
Just by adding some before the rects are drawn:

this.node.runAction(
	cc.sequence(
		cc.delayTime(0),
		cc.callFunc(() => {
			const debugNodes = [
				{ node: grandParent, color: cc.Color.RED, lineWidth: 10 },
				{ node: parent, color: cc.Color.GREEN, lineWidth: 5 },
				{ node: child, color: cc.Color.BLUE, lineWidth: 3 },
				{ node: child2, color: cc.Color.CYAN, lineWidth: 3 },
				{ node: child3, color: cc.Color.YELLOW, lineWidth: 3 },
			];

			debugNodes.forEach(curr => {
				const drawing = curr.node.addComponent(cc.Graphics);
				drawing.rect(curr.node.x, curr.node.y, curr.node.width, curr.node.height);
				drawing.lineWidth = curr.lineWidth;
				drawing.strokeColor = curr.color;
				drawing.stroke();
			});
		})
	)
);

or

setTimeout(() => {
	const debugNodes = [
		{ node: grandParent, color: cc.Color.RED, lineWidth: 10 },
		{ node: parent, color: cc.Color.GREEN, lineWidth: 5 },
		{ node: child, color: cc.Color.BLUE, lineWidth: 3 },
		{ node: child2, color: cc.Color.CYAN, lineWidth: 3 },
		{ node: child3, color: cc.Color.YELLOW, lineWidth: 3 },
	];

	debugNodes.forEach(curr => {
		const drawing = curr.node.addComponent(cc.Graphics);
		drawing.rect(curr.node.x, curr.node.y, curr.node.width, curr.node.height);
		drawing.lineWidth = curr.lineWidth;
		drawing.strokeColor = curr.color;
		drawing.stroke();
	});
}, 1); // If you run with 0ms timeout it actually works!

Resulting in:

@pandamicro hope this help to get you going on this one. I would assume this is quite a critical thing to fix, so let me know if I can help more and in what ways. Thank you!

@pandamicro @jare @slackmoehrle
I created a CC project for you to download and instantly test this out.

In this example I use the setTimeout() as described in the post before this one. I can use up to 3ms timeout and still have it working. If I go to 4ms it breaks. In the demo project it’s now set to 10ms by default.

node-bug-test.zip (267.8 KB)

Here is the same project but using node.x = position - they way it should be used I assume. By using it this way I cannot figure out any workaround to get it to work properly in any way. Probably because the x-positioning is completely synchronous.
node-bug-test (x-pos).zip (268.3 KB)

@Ronsku I was trying all this afternoon to understand what’s happening, finally I got it. The problem is just how you draw the debug nodes.

As you put graphics component to the node, it inherit the transform from the node, so you shouldn’t draw the rect from its node’s x & y position, it’s already in the transform.

When you put Grandparent node X-Positioning -50, actually your code is drawing at -100, and Parent & Child is drawing at -50, that why there is a gap.
When you put Parent node X-Positioning -200, it’s drawing at -450 (-200 - 200 - 50)

So the solution is to change your debug nodes code as the following:

debugNodes.forEach(curr => {
    const drawing = curr.node.addComponent(cc.Graphics);
    drawing.rect(
        0, 0, 
        curr.node.width, curr.node.height);
    drawing.lineWidth = curr.lineWidth;
    drawing.strokeColor = curr.color;
    drawing.stroke();
});

Haven’t been looking into your action samples yet, but I assume they share the same issue, please try the modification and see the result. If it doesn’t solve your problem, just let me know

4 Likes

@pandamicro You’re a hero! …and i feel awkward!

This makes complete sense and works EXACTLY as intended! I guess I got completely blinded by the positioning and thought that my drawing method was accurate.

I feel very relieved having the solution after my “code-blindness”. Thank you! :star::star::star::star::star: :slight_smile:

I’m also sorry for wasting your time, because it’s definitely not your job resolving my brain farts. :confounded:
You’re all doing an amazing job with this game engine!

No problem, everybody is welcome to submit potential issues, and you have given really complete issue description and demos, it’s a good example for how to provide informations about problems.

2 Likes