Jump to content

Calculate world x/y of offset child sprite while parent is rotated


ForgeableSum
 Share

Recommended Posts

Suppose I have a sprite that is the child of another sprite which is the child of the game world:

world > sprite > child

Suppose the sprite is at 100,150 and the child sprite is at 10,20. Were the parent sprite unrotated, getting the world x/y would be trivial:

world x/y = sprite x/y + child x/y. 

However, because the parent sprite is rotated, the coordinates of all children are transformed. And therefore, simply adding the x/y coordinates of the parent to its child won't work as it doesn't take into account the rotation transformation. 

Now, I understand there are sprite.world and sprite.worldPosition properties - these would be perfect, if they actually worked. But as I have pointed out many times on git hub and this forum, reading those values is more or less useless, because they are tied to the game camera somehow. that's right, If you read them at the wrong time, it will give you bizarre coordinates relative to the game camera! So instead of those properties sometimes storing old values (as some would have you believe), they actually store incorrect values (which is much more dangerous). To counteract this, you could call game.stage.updateTransform() each time you read but that is a horrible idea because 1) it is a huge performance suck and 2) it yields inconsistent results. 

Anyway, back to the problem at hand. How do you calculate world coordinates of a child taking into account any rotation transformations that may have occurred on the parent(s)? 

And please no one suggest reading .world or .worldPosition! 

 

 

Link to comment
Share on other sites

You use transformation matrices to calculate the world coordinates of the child object. I believe the relation you have mentioned is still valid even with rotations if you are using homogeneous coordinates (which is the case). You  just apply the rotation transformation matrix and provide your rotation angle (30 cw for example), your parent its world coordinates (100,150) as your translation and your child its local coordinates (10,20) as the input vector. You will get as output your world coordinates after applying your rotation.

https://en.wikipedia.org/wiki/Transformation_matrix#Affine_transformations

Here is a test case to find out what is happening to the matrix/what your are calculating when you rotate the parent:

http://54.87.189.59/example9/

The scene is set like how you described it to be. It will log the parent object before and after giving input. so that everything can be inspected.Then there are two possible inputs:

 1: press A: rotate the parent 30 degrees clockwise without moving the camera.
 2: press D: rotate the parent 30 degrees clockwise and move the camera to right by 10 units (you must refresh first).

Initially:

parent.children[0].world= (110,170)
parent.children[0].worldPosition=(110,170)
parent.children[0].worldTransform={a: 1, b: 0, c: 0, d: 1, tx: 110,ty:170}

The worldTransform matrix is the application of the transformation matrix found in the wiki source. The calculation is:

https://www.symbolab.com/solver/matrix-calculator/\begin{pmatrix}1%260%26100\\ 0%261%26150\\ 0%260%261\end{pmatrix}\cdot\begin{pmatrix}10\\ 20\\ 1\end{pmatrix}

After rotation without camera movement:

parent.children[0].world~= (98,172).
parent.children[0].worldPosition~=(98,172).
parent.children[0].worldTransform~={a: cos(30), b: -sin(30), c: cos(30), d: sin(30), tx: 98,,ty:172}.  

This corresponds to:

https://www.symbolab.com/solver/matrix-calculator/\begin{pmatrix}cos\left(30\right)%26-sin\left(30\right)%26100\\ sin\left(30\right)%26cos\left(30\right)%26150\\ 0%260%261\end{pmatrix}\cdot\begin{pmatrix}10\\ 20\\ 1\end{pmatrix}

After rotation with camera movement:

parent.children[0].world~= (98,172)
parent.children[0].worldPosition~=(88,172)
parent.children[0].worldTransform~={a: cos(30), b: -sin(30), c: cos(30), d: sin(30), tx: 88,ty:172}

your parent world becomes 100-10=90 due to the camera movement (speculation):

https://www.symbolab.com/solver/matrix-calculator/\begin{pmatrix}cos\left(30\right)%26-sin\left(30\right)%2690\\ sin\left(30\right)%26cos\left(30\right)%26150\\ 0%260%261\end{pmatrix}\cdot\begin{pmatrix}10\\ 20\\ 1\end{pmatrix}

Concluding:

It seems like the child object its world property remains the same with or without moving the camera. So based on this I would say you that should take the child.world as a reference if you want the world coordinates of the child?

 

 

Link to comment
Share on other sites

samid, thanks for responding. 

Quote

 

It seems like the child object its world property remains the same with or without moving the camera. So based on this I would say you that should take the child.world as a reference if you want the world coordinates of the child?

 

 

 

What's bizarre is that sometimes .world does exactly as intended. When it fails is when you rapidly move the camera. Which brings me to my main issue with the whole thing: why the hell is world absolute position tied to the camera?

Take a look at this video:

https://streamable.com/z65y6

This is the mini map view of my game. In the mini map, you can see the bullets that are fired and the units firing them (represented as blue and purple squares). Notice when you move the camera around rapidly, the bullets start shooting from camera.x and camera.y ... this might be hard to spot if you're not looking for it, because most times the bullets start where they should. It happens a few times in the first 5 seconds of the video. those bullets are supposed to shoot from where the muzzle flash sprite is (which would be right on top of the parent sprite, the blue and purple squares). The muzzle flashes are children of the unit sprites. So I'm reading the bullet start position with muzzleFlashSprite.world x/y. 

This appears to only happen with rapid camera movement. But I'm making an RTS, so rapid camera movement is the norm. Here's richard's explanation for this:

https://github.com/photonstorm/phaser/issues/2703

I just find it difficult to believe that I need to update the transforms of the entire display list just to read the world positions of a single child sprite. I have tried that and it does work most of the time, but it is a huge performance suck, so not a viable option. 

I know there is a mathematical solution to all this but I don't quite understand it from your post, samid. Perhaps you could write it in a simple format, like a javascript function in which I pass the rotations and child/parent coordinates? Although I don't pretend to know the math, I don't see why this couldn't be solved mathematically with just those paramaters (coordinates and rotations). 

 

 

 

Link to comment
Share on other sites

@feudalwars You could try to apply Phaser.Point.rotate to your objects just like @Miltonsuggested. I am still new to JS and Phaser and I cannot think of anything that might solve the problem mathematically. I do have some ideas that I came up with while looking at the game (might not apply/already attempted) :

-Reduce your camera movement speed until you cannot reproduce the problem and see if that is considerable?
-Before the bullet is visible in the world, check if it is within a certain distance of its spawner/parent, if not destroy it (won't fix the cause).
-Invisibly spawn the bullet directly from the character vehicle its centre so that you wont deal with child objects/offsets, but only make it visible after it has travelled a certain distance/ certain time (after leaving muzzle) (Still might not fix the cause).


Good luck!

 

Link to comment
Share on other sites

Holy crap. I had attempted to use Phaser.Point.rotate before to solve this problem but wrote it off because it didn't work. Now looking over my code again I discovered my error: I neglected to take into account the rotation of the parent sprite. So the actual hierarchy is:

world > sprite (unit) > sprite (gun) > sprite (muzzleflash). 

Therefore, you can't simply rotate the point around the gun sprite. You also need to add the rotation of the unit sprite (which has its own rotation):

Phaser.Point.rotate(unit.x, unit.y, (unit.rotation + gunSprite.rotation));

That's it! So point.rotate does work, you just need to add in the rotations of any parent sprites (including parents of parents). All the way until you reach the root of the display list, game.world or game.stage. 

 

Link to comment
Share on other sites

11 hours ago, feudalwars said:

What's bizarre is that sometimes .world does exactly as intended. When it fails is when you rapidly move the camera. Which brings me to my main issue with the whole thing: why the hell is world absolute position tied to the camera?

It's because camera movement is actually world movement (in the opposite direction). It is confusing.

 

Link to comment
Share on other sites

On 4/8/2017 at 1:29 PM, samid737 said:

@feudalwars You could try to apply Phaser.Point.rotate to your objects just like @Miltonsuggested. I am still new to JS and Phaser and I cannot think of anything that might solve the problem mathematically. I do have some ideas that I came up with while looking at the game (might not apply/already attempted) :

-Reduce your camera movement speed until you cannot reproduce the problem and see if that is considerable?
-Before the bullet is visible in the world, check if it is within a certain distance of its spawner/parent, if not destroy it (won't fix the cause).
-Invisibly spawn the bullet directly from the character vehicle its centre so that you wont deal with child objects/offsets, but only make it visible after it has travelled a certain distance/ certain time (after leaving muzzle) (Still might not fix the cause).


Good luck!

 

 

On 4/8/2017 at 10:32 PM, samme said:

It's because camera movement is actually world movement (in the opposite direction). It is confusing.

 

Okay, point.rotate works great but if and only if the gun sprite is not offset. Here's a simplified version of my code.

muzzleFlashPosition = new Phaser.Point(); 

muzzleFlashPosition.x = unit.x + muzzleFlashSprite.x;
muzzleFlashPosition.y = unit.y + muzzleFlashSprite.y;

gunRotation = (unit.rotation + gunSprite.rotation);
muzzleFlashPosition.rotate(unit.x + gunSprite.x, unit.y + gunSprite.y, gunRotation);

 

With the above code, the following scenario gives me the correct muzzle world position:

world (0,0) > sprite (unit) (500,900) > sprite (gun) (0,0) > sprite (muzzleflash) (30,0).

But this does not:

world (0,0) > sprite (unit) (500,900) > sprite (gun) (-60,0) > sprite (muzzleflash) (30,0).

There must be a particular order I am supposed to rotate the points and/or add relative x/y values. I've tried many different combinations, but can't seem to find one that works with the gunSprite having its own offset (so its anchor does not start as the same spot as its parent). 

 

Link to comment
Share on other sites

 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...