DEV Community

Cover image for Minty's A-Frame Tips & Tricks : Parent Child Relationship
Minty Crisp
Minty Crisp

Posted on

Minty's A-Frame Tips & Tricks : Parent Child Relationship

Hello, my name is Minty Crisp. I am a Web XR developer with a speciality in A-Frame. This is my 2nd post in the series Minty's A-Frame Tips & Tricks where I will share hopefully unique and informative ways of using A-Frame that I have learned on my journey so far.

Check out my portfolio @ MintyCrisp.com to see more of my projects, socials, links and contact info.

If you enjoyed this content or any of my other work, please consider sharing or sponsoring me, so that I may be able to deliver better content more often. You can support me on Ko-fi

And if you are in need of an A-Frame Developer, Debugger or Tutor, I am available. Thank you!


View, follow along & remix these examples on Glitch. Examples start from the far left and move to the right with each subsequent addition. Use your mouse to rotate the view and WASD keys to move the player controller.

The concept of the Parent Child Relationships is pretty basic and is well explained in the A-Frame doc's here -> Building a Basic Scene : Parent and Child Transforms. Essentially, any entity that you attach(a Child) to another(the Parent) will inherit the Position, Rotation and Scale of that Parent as well as any additional Parent's it may have up the chain. This inheritance is constant, meaning any changes to the Parent will update to all the Children.

While this feature doesn't sound all that unique, you can actually accomplish some interesting things with a 3D scene utilizing this relationship. Let's quickly go over some basic examples for each Position, Rotation and Scale before we get to some fun ways we can use it for. Once you understand the logic for one attribute, the rest come easy. This makes it really easy to build complex shapes that stay together whenever moving, but still has access to their own Local settings for dynamic adjustments.

Let's start with Position. The Position's relationship is additive, meaning the Position of the Parent is added to the Child. The Child's Position is it's Local coordinates, while the total (Parent + Child) is it's World coordinates aka where it actually is in scene and not just in reference to it's Parent.

Here we have a Parent entity at the World Position of (-3, 1, -3) along with a single Child at Local Position (0, 1, -1). This means that in comparison to the Parent's Position, the Child will be an extra +1 on the Y axis and -1 on the Z axis. You can see this in action with the Child purple cube being above and behind it's Parent blue cube. So technically, the Child has the World coordinates for it at (-3, 2, -4). You will also see the constant inheritance of the Position in action as we animate the Position of the Parent which moves the Child along with it.

Blue/Purple Cube Position

<!-- Basic Parent Child Position -->
<a-entity
id='parentTest0'
position="-3 1 -3" 
geometry="primitive: box; height: 1; width: 1; depth: 1;"
material="shader: flat; color:purple"

animation__test0="property: object3D.position.y; from: 1; to: 1.5; dur: 3000; delay: 0; loop: true; dir: alternate; easing: linear; elasticity: 400; autoplay: true; enabled: true;"
>
<a-entity
id='childTest0'
position="0 1 -1" 
geometry="primitive: box; height: 1; width: 1; depth: 1;"
material="shader: flat; color:blue"
></a-entity>
</a-entity>
Enter fullscreen mode Exit fullscreen mode

Next we have Rotation. Rotation is also addittive, so any Parent Rotation is added to it's Children. With the Parent's starting Rotation of (45, 0, 0) and the Child's Rotation of (0, 45, 0), the Child is actually starting at the Rotation of (45, 45, 0). With the Parent animating it's own Y axis, you will see the Child's Rotation be continually updated.

Red/Yellow Cube Rotation

<!-- Basic Parent Child Rotation -->
<a-entity
id='parentTest1'
position="-1.5 1 -3"
rotation="45 0 0"
geometry="primitive: box; height: 1; width: 1; depth: 1;"
material="shader: flat; color:red"

animation__test0="property: object3D.rotation.y; from: 0; to: 360; dur: 3000; delay: 0; loop: true; dir: normal; easing: linear; elasticity: 400; autoplay: true; enabled: true;"
>
<a-entity
id='childTest1'
position="0 1 0"
rotation="0 45 0"
geometry="primitive: box; height: 1; width: 1; depth: 1;"
material="shader: flat; color:yellow"
></a-entity>
</a-entity>
Enter fullscreen mode Exit fullscreen mode

And finally we have Scale. Scale is multiplicative, so the Child's Scale is multiplied by the Parent's Scale. In this example, our Parent's Scale is starting at (1, 1.5, 1) with our Child at a Local Scale of (1, 2, 1). Which means that the Child has a World Scale of (1, 3, 1). With this specific example of Scale inheritance and both entities being the same height of 1 meter, our Child becomes 3 meters tall instead of just 2 meters because it is multiplied by the Parent's additional Scale (1.5 x 2 = 3). With the animation playing you see the constant inheritance in play as well adjusting the Child's total height.

Green/Orange Cube Scale

<!-- Basic Parent Child Scale -->
<a-entity
id='parentTest2'
position="0 1 -3"
scale='1 1.5 1'
geometry="primitive: box; height: 1; width: 1; depth: 1;"
material="shader: flat; color:green"

animation__test0="property: object3D.scale.y; from: 1.5; to: 2; dur: 3000; delay: 0; loop: true; dir: alternate; easing: linear; elasticity: 400; autoplay: true; enabled: true;"
>
<a-entity
id='childTest2'
position="0 0.5 -1"
scale='1 2 1'
geometry="primitive: box; height: 1; width: 1; depth: 1;"
material="shader: flat; color:orange"
></a-entity>
</a-entity>
Enter fullscreen mode Exit fullscreen mode

This all works pretty straight forward when you have a single complex shape composed of many smaller entites. As for example, think of building a car comprised of a rectangle cube frame Parent and 4 cylinder tire Children. When you move the car's frame, you want to keep the tires in the same Position related to the frame. When you turn the car, you want to Rotate the tires in addition to to their normal turning controls. Same with Scale when you want to transform this car into a hot wheel or monster truck.


Advanced Inheritance

You can play with these inheritance rules a few different ways for more interesting results such as updating both the Parent and Child dynamically, having a long chain of Parents as well as having using the Parent as a Null controller.

Let's check out an example of updating both the Parent and Child's inherited properties at the same time. In this example we are rotating both the Parent and Child's X axis exactly the same from 0 to 360 repeating, but since the Child inherits the Parent's Rotation it rotates around a Rotation. Akin to a planetary body.

Red/Green Cube Rotation

<!-- Parent Child Simultaneous Updates -->
<a-entity
id='parentTest3'
position="1.5 1 -3"
rotation="0 0 0"
geometry="primitive: box; height: 1; width: 1; depth: 1;"
material="shader: flat; color:red"

animation__test0="property: object3D.rotation.x; from: 0; to: 360; dur: 3000; delay: 0; loop: true; dir: normal; easing: linear; elasticity: 400; autoplay: true; enabled: true;"
>
<a-entity
id='childTest3'
position="0 1.5 0"
rotation="0 0 0"
geometry="primitive: box; height: 1; width: 1; depth: 1;"
material="shader: flat; color:green"

animation__test0="property: object3D.rotation.x; from: 0; to: 360; dur: 3000; delay: 0; loop: true; dir: normal; easing: linear; elasticity: 400; autoplay: true; enabled: true;"
></a-entity>
</a-entity>
Enter fullscreen mode Exit fullscreen mode

Next let's see the same example, but with a longer Parent chain all doing the same Rotation animation. Now we are rotating around a Rotation, which is itself is rotating. Tongue twister alert!

Red/Green/Blue Cube Rotation

<!-- Parent Child Chain -->
<a-entity
id='parentTest4'
position="3 1 -3"
rotation="0 0 0"
geometry="primitive: box; height: 1; width: 1; depth: 1;"
material="shader: flat; color:red"

animation__test0="property: object3D.rotation.x; from: 0; to: 360; dur: 3000; delay: 0; loop: true; dir: normal; easing: linear; elasticity: 400; autoplay: true; enabled: true;"
>
<a-entity
id='parentChildTest4'
position="0 1.5 0"
rotation="0 0 0"
geometry="primitive: box; height: 1; width: 1; depth: 1;"
material="shader: flat; color:green"

animation__test0="property: object3D.rotation.x; from: 0; to: 360; dur: 3000; delay: 0; loop: true; dir: normal; easing: linear; elasticity: 400; autoplay: true; enabled: true;"
>
<a-entity
id='childTest4'
position="0 1.5 0"
rotation="0 0 0"
geometry="primitive: box; height: 1; width: 1; depth: 1;"
material="shader: flat; color:blue"

animation__test0="property: object3D.rotation.x; from: 0; to: 360; dur: 3000; delay: 0; loop: true; dir: normal; easing: linear; elasticity: 400; autoplay: true; enabled: true;"
></a-entity>
</a-entity></a-entity>
Enter fullscreen mode Exit fullscreen mode

And 1 more of the same concept, but let's try animating a different axis on each entity.

Red/Green/Blue Cube Rotation 2

<!-- Parent Child Chain 2 -->
<a-entity
id='parentTest5'
position="5 1 -3"
rotation="0 0 0"
geometry="primitive: box; height: 1; width: 1; depth: 1;"
material="shader: flat; color:red"

animation__test0="property: object3D.rotation.x; from: 0; to: 360; dur: 3000; delay: 0; loop: true; dir: normal; easing: linear; elasticity: 400; autoplay: true; enabled: true;"
>
<a-entity
id='parentChildTest5'
position="0 1.5 0"
rotation="0 0 0"
geometry="primitive: box; height: 1; width: 1; depth: 1;"
material="shader: flat; color:green"

animation__test0="property: object3D.rotation.y; from: 0; to: 360; dur: 3000; delay: 0; loop: true; dir: normal; easing: linear; elasticity: 400; autoplay: true; enabled: true;"
>
<a-entity
id='childTest5'
position="0 1.5 0"
rotation="0 0 0"
geometry="primitive: box; height: 1; width: 1; depth: 1;"
material="shader: flat; color:blue"

animation__test0="property: object3D.rotation.z; from: 0; to: 360; dur: 3000; delay: 0; loop: true; dir: normal; easing: linear; elasticity: 400; autoplay: true; enabled: true;"
></a-entity>
</a-entity></a-entity>
Enter fullscreen mode Exit fullscreen mode

Null Controller

All of the previous examples help illustrate how each layer acts from Parent downward to each Child ending with the final Child being animated via everything above it. Which brings me to my final twist on the Parent & Child relationship. That would be making a Null Controller.

What is a Null Controller? It is an invisible Parent which grants the same power of inheritance to it's Children. This can be used a bunch of ways to better control your entites and make them act uniquely. Can be used as a Rig, Offset, Layouts, etc...

Everyone of the previous examples showcases the features for a Rig, Offset and Layout, except with a Null Controller Rig, they wouldn't have any geometry. But they are good visualizations for what it is doing in relationship to their children. I made an exact copy of each example for which I converted all Parents to a Null Controller by removing their geometry and material. To help showcase these updated examples without having to alter each individual entity's Position, I added them all to a single Null Controller rig which allowed me to move/rotate the entire set off to the right to not conflict with the base examples.

<!-- Null Controller Examples -->
<a-entity
id='allNull'
position="3 0 3" 
rotation="0 -90 0" 
>

<!-- All Examples as Null Controllers -->

</a-entity>
Enter fullscreen mode Exit fullscreen mode

Rig

Using it as a Rig allows you to control the entire set of entities in a unified manner. So in our car example idea with a Parent frame and 4 Child tires, the Parent with a frame geometry will work okay as a Rig, but if we parent all 5 of them under a unified Null Controller, then we can still have them all move in tandum while being able to update the frame seperately without affecting the tires. So if we want to simulate the minor flucuations of the car's shocks up and down which moves independant of the tires position or if the car is actually a tank and needs to rotate to aim, it can do so without actually turning the vehicle's chasis.

Offset (Anchor Point)

Using it for Offsets allows you to adjust the natural Anchor Point of the Child object. Anchor Points for built in primitives are set to the center of it's volume and are used as the starting point to Position the object, Rotate the object and Scale the object. Having a Null Controller Offset allows you to move this Anchor Point anywhere as you would be able to in a 3D modeling program.

So when you spawn a cube that is 1 meter wide x 1 meter tall x 1 meter deep at the Position (0, 0, 0), the cube's volume will extend from -0.5 to 0.5 in each direction. Which means if your floor is set to 0, it will clip through and you will only see the top half. If you wanted the cube to sit on the floor, normally you would need to set the Y Position to 0.5 aka half of the object's height of 1. You can avoid this by having a Parent Null Controller which is set to the natural (0, 0, 0) Position and have the Child be the Offset Position of (0, 0.5, 0).

When you spin a cube that is 1x1x1, it will rotate from center point of (0.5, 0.5, 0.5). If you wanted to rotate the object like a pendulum or a moon, then you need to offset that Anchor Point of the Child and rotate the Parent Null Controller.

Same thing when you Scale a cube, it will Scale outwards in all directions from the center. But what if you wanted it grow 1 direction such as a flat cube that grows into a pillar at floor level. This can be done by Offseting the Anchor Point with a Null Controller and animating the Null Controller Scale.

Layouts

Using it for Layouts lets you make easy circle, sphere, grids, etc... without extra math. Like if you want to have a set of image frames surrounding the user 360 degrees aligned with the floor. You would build the previous pendulum/moon example which extends the Anchor Point outwards and when your rotate the Parent Null Controller, it will move in a perfect circle around the user. You could have a bunch of Null Controller w/ Frame objects each with their own rotation to surround the user completely which all face the center.


Things can get complicated fast as you expand your entities to hold more, but grants you the power to control each object as you see fit. There is a lot that can be done with the power of the Parent and Child Relationship.

One extra tidbit to note is that when you are dealing with the ability to click on a Child, that click will by default bubble upwards through each of the Parents. This can cause issues if not managed correctly, so if you are interested in making an interactive multi-entity element, learn more about the Event Loop. There is a good overview here -> An Interactive Guide to JavaScript Events.

That's it for this edition of Minty's A-Frame Tips & Tricks : Parent Child Relationship.

If you would like to see more of my projects or get in contact with me, check out my portfolio @ MintyCrisp.com.

And if you enjoyed this content or any of my other work, please consider sharing or sponsoring me, so that I may be able to deliver better content more often. You can support me on Ko-fi

Thank you for reading!

Top comments (0)