Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Question What is the principle of transform?

Discussion in 'Scripting' started by NeverMore_, Nov 29, 2023.

  1. NeverMore_

    NeverMore_

    Joined:
    Apr 16, 2021
    Posts:
    7
    I have a requirement to simulate the hierarchical relationship of transform in a thread. Finally, the gameobject will be generated in the ui thread based on the simulated data.


    In unity, Transform has localposition, localrotation, and localscale attributes. When setting parent to Transform, localposition, localrotation, and localscale will change accordingly. What is this change?

    My guess is to convert the local information of the current Transform into a matrix, and then use its original parent's localtoworldmatrix to act on the matrix to get its information in the world. Then use the new parent's worldtolocalmatrix to act on the world matrix to get the final matrix, from which localposition, localrotation and localscale are extracted.

    But currently I have implemented a Transform logic based on this idea. It is ok when there are only two layers in the parent-child hierarchy. After three layers, there is a big deviation from the one created with gameobject.

    This is my demo, what is the problem?
     

    Attached Files:

    Last edited: Nov 29, 2023
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,287
    Nothing like THAT is gonna be happening!!!

    For one, there is no UI thread that you have access to.

    For two, 100% of EVERY GameObject will always be created on the main thread where your script runs.

    Transform hierarchal relationships are simply a cascading series of matrix transforms.

    https://www.gamedev.net/tutorials/p...s/making-a-game-engine-transformations-r3566/
     
  3. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,845
    I think that your "Scale" extension method has a flaw. It should only consider the top left 3x3 matrix and not the full 4d columns to extract the scale. A single TRS matrix has the bottom row all zeros (except for the last one which is 1). However when you have combined matrices those values usually are not 0 as they get influenced by the translation of the parent(s). Though those should not be considered when extracting the scale. Rotation and scale only happens in the 3x3 part of the matrix. The 4th dimension is just added for the homogeneous coordinates in order to do translations as well.

    @Kurt-Dekker I think with UI thread he actually means the main thread. In most systems the UI thread is the main thread of the application and a lot of systems have the same restriction as Unity. That's why there's usually some kind of "runOnUiThread" mechanism. Unity doesn't really have that out of the box but you can roll your own "runOnMainThread" quite easily.
     
    angrypenguin likes this.
  4. NeverMore_

    NeverMore_

    Joined:
    Apr 16, 2021
    Posts:
    7
    The ui thread I'm talking about is the main thread. I also understand that the change of transformation is the change of matrix. It's just that when I implemented it through this set of theories, the results were not consistent with the unified one. So I'm not sure which stage the problem is.

    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Text;
    5. using System.Threading.Tasks;
    6. using UnityEngine;
    7.  
    8.  
    9. public class VirtualTransform
    10. {
    11.     private VirtualTransform _parent;
    12.     private Vector3 _localPosition = Vector3.zero;
    13.     private Quaternion _localRotation = Quaternion.identity;
    14.     private Vector3 _localScale = Vector3.one;
    15.  
    16.     public List<VirtualTransform> Children { get; } = new List<VirtualTransform>();
    17.  
    18.     public VirtualTransform Parent
    19.     {
    20.         get => _parent; set
    21.         {
    22.             if (_parent == value) return;
    23.             if (_parent != null)
    24.                 _parent.Children.Remove(this);
    25.             if (value == null)
    26.             {
    27.                 ApplyMatrix(_parent.LocalToWorldMatrix * Matrix);
    28.                 _parent = null;
    29.                 return;
    30.             }
    31.  
    32.             var worldMatrix = Matrix;
    33.             if (_parent != null)
    34.                 worldMatrix = _parent.LocalToWorldMatrix * worldMatrix;
    35.             _parent = value;
    36.             _parent.Children.Add(this);
    37.             var matrix = _parent.WorldToLocalMatrix * worldMatrix;
    38.             ApplyMatrix(matrix);
    39.         }
    40.     }
    41.  
    42.     void ApplyMatrix(Matrix4x4 matrix)
    43.     {
    44.         _localPosition = matrix.Position();
    45.         _localRotation = matrix.Rotation();
    46.         _localScale = matrix.Scale();
    47.     }
    48.  
    49.     public Vector3 LocalPosition { get => _localPosition; set => _localPosition = value; }
    50.     public Quaternion LocalRotation { get => _localRotation; set => _localRotation = value; }
    51.     public Vector3 LocalScale { get => _localScale; set => _localScale = value; }
    52.  
    53.     public Vector3 Position
    54.     {
    55.         get
    56.         {
    57.             if (_parent == null)
    58.                 return _localPosition;
    59.             return _parent.LocalToWorldMatrix.MultiplyPoint(_localPosition);
    60.         }
    61.         set
    62.         {
    63.             if (_parent == null)
    64.                 _localPosition = value;
    65.             else
    66.                 _localPosition = _parent.WorldToLocalMatrix.MultiplyPoint(value);
    67.         }
    68.     }
    69.     public Quaternion Rotation
    70.     {
    71.         get
    72.         {
    73.             if (_parent == null)
    74.                 return _localRotation;
    75.             return (_parent.LocalToWorldMatrix * Matrix4x4.Rotate(_localRotation)).Rotation();
    76.         }
    77.         set
    78.         {
    79.             if (_parent == null)
    80.                 _localRotation = value;
    81.             else
    82.                 _localRotation = (_parent.WorldToLocalMatrix * Matrix4x4.Rotate(value)).Rotation();
    83.         }
    84.     }
    85.     public Vector3 Scale
    86.     {
    87.         get
    88.         {
    89.             if (_parent == null)
    90.                 return _localScale;
    91.             return (_parent.LocalToWorldMatrix * Matrix4x4.Scale(_localScale)).Scale();
    92.         }
    93.     }
    94.  
    95.     Matrix4x4 Matrix => Matrix4x4.TRS(LocalPosition, LocalRotation, LocalScale);
    96.  
    97.     public Matrix4x4 LocalToWorldMatrix
    98.     {
    99.         get
    100.         {
    101.  
    102.             var parent = Parent;
    103.             var matrix = Matrix;
    104.             while (parent != null)
    105.             {
    106.                 matrix = parent.Matrix * matrix;
    107.                 parent = parent.Parent;
    108.             }
    109.             return matrix;
    110.         }
    111.     }
    112.     public Matrix4x4 WorldToLocalMatrix => LocalToWorldMatrix.inverse;
    113.  
    114.  
    115.     #region create unity gameobject
    116.     public void Create()
    117.     {
    118.         Create(null, this);
    119.     }
    120.  
    121.     static GameObject CreateBasic()
    122.     {
    123.         var obj = GameObject.CreatePrimitive(PrimitiveType.Cube);
    124.         obj.name = "Virtual_" + obj.name;
    125.         //var obj = new GameObject();
    126.         //obj.AddComponent<MeshFilter>();
    127.         //obj.AddComponent<MeshRenderer>();
    128.         return obj;
    129.     }
    130.  
    131.     static void Create(Transform root, VirtualTransform @object)
    132.     {
    133.         var obj = CreateBasic();
    134.         obj.transform.parent = root;
    135.         obj.transform.localPosition = @object.LocalPosition;
    136.         obj.transform.localRotation = @object.LocalRotation;
    137.         obj.transform.localScale = @object.LocalScale;
    138.  
    139.         for (int i = 0; i < @object.Children.Count; i++)
    140.         {
    141.             var child = @object.Children[i];
    142.             Create(obj.transform, child);
    143.         }
    144.     }
    145.     #endregion
    146. }
    147.  
    148.  
    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Text;
    5. using UnityEngine;
    6.  
    7.  
    8. public static class MatrixExtension
    9. {
    10.     public static Vector3 Position(this Matrix4x4 matrix)
    11.     {
    12.         return new Vector3(matrix.m03, matrix.m13, matrix.m23);
    13.     }
    14.  
    15.     public static Vector3 Scale(this Matrix4x4 matrix)
    16.     {
    17.         Vector3 scale;
    18.         scale.x = new Vector4(matrix.m00, matrix.m10, matrix.m20, matrix.m30).magnitude;
    19.         scale.y = new Vector4(matrix.m01, matrix.m11, matrix.m21, matrix.m31).magnitude;
    20.         scale.z = new Vector4(matrix.m02, matrix.m12, matrix.m22, matrix.m32).magnitude;
    21.         return scale;
    22.     }
    23.  
    24.     public static Quaternion Rotation(this Matrix4x4 matrix)
    25.     {
    26.         return Quaternion.LookRotation(matrix.GetColumn(2), matrix.GetColumn(1));
    27.     }
    28.  
    29. }
    30.  
    31.  
    You can download the zip package to view the test project.
     
  5. halley

    halley

    Joined:
    Aug 26, 2013
    Posts:
    2,279
    I don't see why you're simulating all this, rather than just constructing the hierarchy. Probably some academia nonsense.

    Each matrix for all objects represents the local position, rotation, scale. You can generate that matrix with the Matrix4x4.TRS() method. When you want to know the world space position, rotation, and scale, Transform will multiply them in the same sequence they appear in the hierarchy branch. When Unity is traversing through the entire hierarchy, it does this and caches the results, so you always have access to the world space; and conversely when you set the world space values, it calculates the inverse matrix, so it can update the local space matrix too.

    As Bunny points out, once you try to combine non-uniform scale and any rotation, the matrix can end up having muddy values outside the classical TRS matrix. This is one reason the Transform calls the function to get the approximate world scale "lossy scale."
     
    DragonCoder likes this.
  6. NeverMore_

    NeverMore_

    Joined:
    Apr 16, 2021
    Posts:
    7
    We are making a game with UGC. Players may create a lot of things, there may be many levels, and the mesh will be very large. Loading the user's works again will become extremely time-consuming, causing the game to freeze. Of course, coroutines can solve some problems, but when we load user works, there will still be intensive operations, so consider directly putting them into sub-threads to perform operations, get the final results, and then create gameobjects in the main thread, which will solve the lag problem.
     
  7. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    758
    I've had experience in user created worlds and any freeze you experience when loading the worlds may be a result of instantiating lots of objects with colliders that need to be added to the physics engine's internal data structures. My workaround was to instantiate the objects with their colliders previously disabled and then gradually enabling them over several frames. Doing this meant that I could load the worlds instantly but the downside was that the player was briefly a ghost. :)
     
  8. NeverMore_

    NeverMore_

    Joined:
    Apr 16, 2021
    Posts:
    7
    Our current main lag is caused by the huge amount of mesh and hierarchical structures. We mainly do construction production, and there is no demand for collisions and the like for the time being.
     
  9. DragonCoder

    DragonCoder

    Joined:
    Jul 3, 2015
    Posts:
    1,635
    You should have started the thread with this info ;)

    Rolling out your own threading is extremely rarely a good idea in Unity.
    Have you heard of the Job System and Burst compiler? Unity has an extremely performanct and very easily parallelizable system for mass processing data.
    That should be the go-to for processing massive stuff at runtime or during loading steps.
     
    Kurt-Dekker likes this.