Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Netcode: How to have the Ghost Authoring Component sync the Scale

Discussion in 'NetCode for ECS' started by MaverickROM, Jan 5, 2020.

  1. MaverickROM

    MaverickROM

    Joined:
    Mar 19, 2019
    Posts:
    11
    Hi guys,

    I have a prefab that is being instantiated and managed on the server and synchronised to the client. A custom component, rotation and transform are all successfully syncing however the scale (CompositeScale in this case) is not being synced so the client side isn't changing this when rendering.

    I saw a note in the change log regarding a fixed bug, and another in the doco about how to tell the generator how to sync components you don't have source code for but have no clue how to make this happen.

    From the change log (https://docs.unity3d.com/Packages/com.unity.entities@0.4/changelog/CHANGELOG.html):
    [0.0.12-preview.33] - 2019-05-24
    • Fixed bug where entities with manually specified CompositeScale were not updated by TRSLocalToWorldSystem.

    From the doco (https://docs.unity3d.com/Packages/com.unity.netcode@0.0/manual/ghost-snapshots.html) - I don't where where I would add this override code:
    As well as adding attributes, you can override the defaults for components which you do not have source access to by creating an entry to GhostAuthoringComponentEditor.GhostDefaultOverrides.

    Any help would be greatly appreciated.

    Cheers.
     
  2. MaverickROM

    MaverickROM

    Joined:
    Mar 19, 2019
    Posts:
    11
    Okay, I've worked it out. In case anyone else has a need for this or something similar, I used the DOTS Sample as a reference; see this folder: https://github.com/Unity-Technologi...04f734/Assets/Unity.Sample.Game/GameBootstrap

    Basically, I:
    • Created a float4x4 implementation of GhostSnapshotValue (as per the sample's GhostAngleValue.cs)
    • Created an associated template text file (as per GhostSnapshotAngleValue.txt)
    • Created a bootstrap inheriting from ClientServerBootstrap (as per GameBootstrap.cs)
    I wanted the scale to apply to all CompositeScale components so I added a GhostDefaultOverrides in the bootstrap, referencing the Value field. I also added my float4x4 handler to the GameSpecificTypes. I can choose not to pass the scale around where I don't want it (which is actually in most cases) by turning off all the checkbox options for Server / Interpolated Client / Predicted Client - then no code is generated for the CompositeScale.

    My implementation looks like this - feel free to constructively comment if you have any feedback:

    Bootstrap.cs
    Code (CSharp):
    1.  
    2. using Unity.NetCode;
    3.  
    4. #if UNITY_EDITOR
    5. using Unity.NetCode.Editor;
    6. using Unity.Transforms;
    7. using UnityEditor;
    8. #endif
    9.  
    10. public class Bootstrap : ClientServerBootstrap
    11. {
    12. #if UNITY_EDITOR
    13.     [InitializeOnLoadMethod]
    14.     static void SetupGhostDefaults()
    15.     {
    16.         GhostAuthoringComponentEditor.DefaultRootPath = "/Scripts";
    17.         GhostAuthoringComponentEditor.DefaultUpdateSystemPrefix = "Client/Generated/";
    18.         GhostAuthoringComponentEditor.DefaultSerializerPrefix = "Server/Generated/";
    19.         GhostAuthoringComponentEditor.DefaultSnapshotDataPrefix = "Mixed/Generated/";
    20.  
    21.         var compositeScaleComponent = new GhostAuthoringComponent.GhostComponent
    22.         {
    23.             name = "Unity.Transforms.CompositeScale",
    24.             interpolatedClient = true,
    25.             predictedClient = true,
    26.             server = true,
    27.             sendDataTo = GhostAuthoringComponent.ClientSendType.All,
    28.             fields = new GhostAuthoringComponent.GhostComponentField[]
    29.             {
    30.                 new GhostAuthoringComponent.GhostComponentField
    31.                 {
    32.                     name = "Value",
    33.                     quantization = 1000,
    34.                     interpolate = true,
    35.                     Field = typeof(CompositeScale).GetField("Value")
    36.                 }
    37.             }
    38.         };
    39.  
    40.         GhostAuthoringComponentEditor.GhostDefaultOverrides.Add("Unity.Transforms.CompositeScale", compositeScaleComponent);
    41.  
    42.         GhostSnapshotValue.GameSpecificTypes.Add(new GhostSnapshotFloat4x4Value());
    43.     }
    44. #endif
    45. }
    46.  


    GhostSnapshotFloat4x4Value.cs

    Code (CSharp):
    1.  
    2. using System;
    3. #if UNITY_EDITOR
    4. using System.Collections.Generic;
    5. using System.Reflection;
    6. using Unity.Mathematics;
    7. using Unity.NetCode;
    8. #endif
    9.  
    10. // Optional attribute can be used if you're serializing any other float4x4 (see comment in CanProcess below)
    11. public class GhostFloat4x4ValueAttribute : Attribute
    12. {
    13. }
    14.  
    15. #if UNITY_EDITOR
    16. class GhostSnapshotFloat4x4Value : GhostSnapshotValue
    17. {
    18.     public override void AddImports(HashSet<string> imports)
    19.     {
    20.         imports.Add("Unity.Mathematics");
    21.     }
    22.  
    23.     public override bool SupportsQuantization => true;
    24.  
    25.     public override bool CanProcess(FieldInfo field, string componentName, string fieldName)
    26.     {
    27.         return field.FieldType == typeof(float4x4); // && field.GetCustomAttribute<GhostFloat4x4ValueAttribute>() != null; - from the DOTS Sample - not desired for my scenario
    28.     }
    29.     public override bool CanProcess(Type type, string componentName, string fieldName)
    30.     {
    31.         throw new NotImplementedException();
    32.     }
    33.  
    34.     public override string GetTemplatePath(int quantization)
    35.     {
    36.         if (quantization < 1)
    37.             throw new NotImplementedException("Unquantized float4x4 values not supported");
    38.         return "Assets/Scripts/Mixed/Components/Editor/GhostSnapshotFloat4x4Value.txt";
    39.     }
    40. }
    41.  
    42. #endif
    43.  

    GhostSnapshotFloat4x4Value.txt

    Code (CSharp):
    1.  
    2. public struct GhostSnapshotData
    3. {
    4.     #region __GHOST_FIELD__
    5.     private int __GHOST_FIELD_NAME__C0_X;
    6.     private int __GHOST_FIELD_NAME__C0_Y;
    7.     private int __GHOST_FIELD_NAME__C0_Z;
    8.     private int __GHOST_FIELD_NAME__C0_W;
    9.  
    10.     private int __GHOST_FIELD_NAME__C1_X;
    11.     private int __GHOST_FIELD_NAME__C1_Y;
    12.     private int __GHOST_FIELD_NAME__C1_Z;
    13.     private int __GHOST_FIELD_NAME__C1_W;
    14.  
    15.     private int __GHOST_FIELD_NAME__C2_X;
    16.     private int __GHOST_FIELD_NAME__C2_Y;
    17.     private int __GHOST_FIELD_NAME__C2_Z;
    18.     private int __GHOST_FIELD_NAME__C2_W;
    19.  
    20.     private int __GHOST_FIELD_NAME__C3_X;
    21.     private int __GHOST_FIELD_NAME__C3_Y;
    22.     private int __GHOST_FIELD_NAME__C3_Z;
    23.     private int __GHOST_FIELD_NAME__C3_W;
    24.     #endregion
    25.  
    26.     #region __GHOST_FIELD_GET_SET__
    27.     public float4x4 Get__GHOST_FIELD_NAME__(GhostDeserializerState deserializerState)
    28.     {
    29.         return Get__GHOST_FIELD_NAME__();
    30.     }
    31.     public float4x4 Get__GHOST_FIELD_NAME__()
    32.     {
    33.         var c0 = new float4(__GHOST_FIELD_NAME__C0_X * __GHOST_DEQUANTIZE_SCALE__, __GHOST_FIELD_NAME__C0_Y * __GHOST_DEQUANTIZE_SCALE__, __GHOST_FIELD_NAME__C0_Z * __GHOST_DEQUANTIZE_SCALE__, __GHOST_FIELD_NAME__C0_W * __GHOST_DEQUANTIZE_SCALE__);
    34.         var c1 = new float4(__GHOST_FIELD_NAME__C1_X * __GHOST_DEQUANTIZE_SCALE__, __GHOST_FIELD_NAME__C1_Y * __GHOST_DEQUANTIZE_SCALE__, __GHOST_FIELD_NAME__C1_Z * __GHOST_DEQUANTIZE_SCALE__, __GHOST_FIELD_NAME__C1_W * __GHOST_DEQUANTIZE_SCALE__);
    35.         var c2 = new float4(__GHOST_FIELD_NAME__C2_X * __GHOST_DEQUANTIZE_SCALE__, __GHOST_FIELD_NAME__C2_Y * __GHOST_DEQUANTIZE_SCALE__, __GHOST_FIELD_NAME__C2_Z * __GHOST_DEQUANTIZE_SCALE__, __GHOST_FIELD_NAME__C2_W * __GHOST_DEQUANTIZE_SCALE__);
    36.         var c3 = new float4(__GHOST_FIELD_NAME__C3_X * __GHOST_DEQUANTIZE_SCALE__, __GHOST_FIELD_NAME__C3_Y * __GHOST_DEQUANTIZE_SCALE__, __GHOST_FIELD_NAME__C3_Z * __GHOST_DEQUANTIZE_SCALE__, __GHOST_FIELD_NAME__C3_W * __GHOST_DEQUANTIZE_SCALE__);
    37.         return new float4x4(c0, c1, c2, c3);
    38.     }
    39.     public void Set__GHOST_FIELD_NAME__(float4x4 val, GhostSerializerState serializerState)
    40.     {
    41.         Set__GHOST_FIELD_NAME__(val);
    42.     }
    43.     public void Set__GHOST_FIELD_NAME__(float4x4 val)
    44.     {
    45.         __GHOST_FIELD_NAME__C0_X = (int)(val.c0.x * __GHOST_QUANTIZE_SCALE__);
    46.         __GHOST_FIELD_NAME__C0_Y = (int)(val.c0.y * __GHOST_QUANTIZE_SCALE__);
    47.         __GHOST_FIELD_NAME__C0_Z = (int)(val.c0.z * __GHOST_QUANTIZE_SCALE__);
    48.         __GHOST_FIELD_NAME__C0_W = (int)(val.c0.w * __GHOST_QUANTIZE_SCALE__);
    49.  
    50.         __GHOST_FIELD_NAME__C1_X = (int)(val.c1.x * __GHOST_QUANTIZE_SCALE__);
    51.         __GHOST_FIELD_NAME__C1_Y = (int)(val.c1.y * __GHOST_QUANTIZE_SCALE__);
    52.         __GHOST_FIELD_NAME__C1_Z = (int)(val.c1.z * __GHOST_QUANTIZE_SCALE__);
    53.         __GHOST_FIELD_NAME__C1_W = (int)(val.c1.w * __GHOST_QUANTIZE_SCALE__);
    54.  
    55.         __GHOST_FIELD_NAME__C2_X = (int)(val.c2.x * __GHOST_QUANTIZE_SCALE__);
    56.         __GHOST_FIELD_NAME__C2_Y = (int)(val.c2.y * __GHOST_QUANTIZE_SCALE__);
    57.         __GHOST_FIELD_NAME__C2_Z = (int)(val.c2.z * __GHOST_QUANTIZE_SCALE__);
    58.         __GHOST_FIELD_NAME__C2_W = (int)(val.c2.w * __GHOST_QUANTIZE_SCALE__);
    59.  
    60.         __GHOST_FIELD_NAME__C3_X = (int)(val.c3.x * __GHOST_QUANTIZE_SCALE__);
    61.         __GHOST_FIELD_NAME__C3_Y = (int)(val.c3.y * __GHOST_QUANTIZE_SCALE__);
    62.         __GHOST_FIELD_NAME__C3_Z = (int)(val.c3.z * __GHOST_QUANTIZE_SCALE__);
    63.         __GHOST_FIELD_NAME__C3_W = (int)(val.c3.w * __GHOST_QUANTIZE_SCALE__);
    64.     }
    65.     #endregion
    66.  
    67.     public void PredictDelta(uint tick, ref GhostSnapshotData baseline1, ref GhostSnapshotData baseline2)
    68.     {
    69.         var predictor = new GhostDeltaPredictor(tick, this.tick, baseline1.tick, baseline2.tick);
    70.         #region __GHOST_PREDICT__
    71.         __GHOST_FIELD_NAME__C0_X = predictor.PredictInt(__GHOST_FIELD_NAME__C0_X, baseline1.__GHOST_FIELD_NAME__C0_X, baseline2.__GHOST_FIELD_NAME__C0_X);
    72.         __GHOST_FIELD_NAME__C0_Y = predictor.PredictInt(__GHOST_FIELD_NAME__C0_Y, baseline1.__GHOST_FIELD_NAME__C0_Y, baseline2.__GHOST_FIELD_NAME__C0_Y);
    73.         __GHOST_FIELD_NAME__C0_Z = predictor.PredictInt(__GHOST_FIELD_NAME__C0_Z, baseline1.__GHOST_FIELD_NAME__C0_Z, baseline2.__GHOST_FIELD_NAME__C0_Z);
    74.         __GHOST_FIELD_NAME__C0_W = predictor.PredictInt(__GHOST_FIELD_NAME__C0_W, baseline1.__GHOST_FIELD_NAME__C0_W, baseline2.__GHOST_FIELD_NAME__C0_W);
    75.  
    76.         __GHOST_FIELD_NAME__C1_X = predictor.PredictInt(__GHOST_FIELD_NAME__C1_X, baseline1.__GHOST_FIELD_NAME__C1_X, baseline2.__GHOST_FIELD_NAME__C1_X);
    77.         __GHOST_FIELD_NAME__C1_Y = predictor.PredictInt(__GHOST_FIELD_NAME__C1_Y, baseline1.__GHOST_FIELD_NAME__C1_Y, baseline2.__GHOST_FIELD_NAME__C1_Y);
    78.         __GHOST_FIELD_NAME__C1_Z = predictor.PredictInt(__GHOST_FIELD_NAME__C1_Z, baseline1.__GHOST_FIELD_NAME__C1_Z, baseline2.__GHOST_FIELD_NAME__C1_Z);
    79.         __GHOST_FIELD_NAME__C1_W = predictor.PredictInt(__GHOST_FIELD_NAME__C1_W, baseline1.__GHOST_FIELD_NAME__C1_W, baseline2.__GHOST_FIELD_NAME__C1_W);
    80.  
    81.         __GHOST_FIELD_NAME__C2_X = predictor.PredictInt(__GHOST_FIELD_NAME__C2_X, baseline1.__GHOST_FIELD_NAME__C2_X, baseline2.__GHOST_FIELD_NAME__C2_X);
    82.         __GHOST_FIELD_NAME__C2_Y = predictor.PredictInt(__GHOST_FIELD_NAME__C2_Y, baseline1.__GHOST_FIELD_NAME__C2_Y, baseline2.__GHOST_FIELD_NAME__C2_Y);
    83.         __GHOST_FIELD_NAME__C2_Z = predictor.PredictInt(__GHOST_FIELD_NAME__C2_Z, baseline1.__GHOST_FIELD_NAME__C2_Z, baseline2.__GHOST_FIELD_NAME__C2_Z);
    84.         __GHOST_FIELD_NAME__C2_W = predictor.PredictInt(__GHOST_FIELD_NAME__C2_W, baseline1.__GHOST_FIELD_NAME__C2_W, baseline2.__GHOST_FIELD_NAME__C2_W);
    85.  
    86.         __GHOST_FIELD_NAME__C3_X = predictor.PredictInt(__GHOST_FIELD_NAME__C3_X, baseline1.__GHOST_FIELD_NAME__C3_X, baseline2.__GHOST_FIELD_NAME__C3_X);
    87.         __GHOST_FIELD_NAME__C3_Y = predictor.PredictInt(__GHOST_FIELD_NAME__C3_Y, baseline1.__GHOST_FIELD_NAME__C3_Y, baseline2.__GHOST_FIELD_NAME__C3_Y);
    88.         __GHOST_FIELD_NAME__C3_Z = predictor.PredictInt(__GHOST_FIELD_NAME__C3_Z, baseline1.__GHOST_FIELD_NAME__C3_Z, baseline2.__GHOST_FIELD_NAME__C3_Z);
    89.         __GHOST_FIELD_NAME__C3_W = predictor.PredictInt(__GHOST_FIELD_NAME__C3_W, baseline1.__GHOST_FIELD_NAME__C3_W, baseline2.__GHOST_FIELD_NAME__C3_W);
    90.         #endregion
    91.     }
    92.  
    93.     public void Serialize(int networkId, ref GhostSnapshotData baseline, DataStreamWriter writer, NetworkCompressionModel compressionModel)
    94.     {
    95.         #region __GHOST_CALCULATE_CHANGE_MASK_ZERO__
    96.         changeMask__GHOST_MASK_BATCH__ = (
    97.             __GHOST_FIELD_NAME__C0_X != baseline.__GHOST_FIELD_NAME__C0_X ||
    98.             __GHOST_FIELD_NAME__C0_Y != baseline.__GHOST_FIELD_NAME__C0_Y ||
    99.             __GHOST_FIELD_NAME__C0_Z != baseline.__GHOST_FIELD_NAME__C0_Z ||
    100.             __GHOST_FIELD_NAME__C0_W != baseline.__GHOST_FIELD_NAME__C0_W ||
    101.  
    102.             __GHOST_FIELD_NAME__C1_X != baseline.__GHOST_FIELD_NAME__C1_X ||
    103.             __GHOST_FIELD_NAME__C1_Y != baseline.__GHOST_FIELD_NAME__C1_Y ||
    104.             __GHOST_FIELD_NAME__C1_Z != baseline.__GHOST_FIELD_NAME__C1_Z ||
    105.             __GHOST_FIELD_NAME__C1_W != baseline.__GHOST_FIELD_NAME__C1_W ||
    106.  
    107.             __GHOST_FIELD_NAME__C2_X != baseline.__GHOST_FIELD_NAME__C2_X ||
    108.             __GHOST_FIELD_NAME__C2_Y != baseline.__GHOST_FIELD_NAME__C2_Y ||
    109.             __GHOST_FIELD_NAME__C2_Z != baseline.__GHOST_FIELD_NAME__C2_Z ||
    110.             __GHOST_FIELD_NAME__C2_W != baseline.__GHOST_FIELD_NAME__C2_W ||
    111.  
    112.             __GHOST_FIELD_NAME__C3_X != baseline.__GHOST_FIELD_NAME__C3_X ||
    113.             __GHOST_FIELD_NAME__C3_Y != baseline.__GHOST_FIELD_NAME__C3_Y ||
    114.             __GHOST_FIELD_NAME__C3_Z != baseline.__GHOST_FIELD_NAME__C3_Z ||
    115.             __GHOST_FIELD_NAME__C3_W != baseline.__GHOST_FIELD_NAME__C3_W
    116.         ) ? 1u : 0;
    117.         #endregion
    118.         #region __GHOST_CALCULATE_CHANGE_MASK__
    119.         changeMask__GHOST_MASK_BATCH__ |= (
    120.             __GHOST_FIELD_NAME__C0_X != baseline.__GHOST_FIELD_NAME__C0_X ||
    121.             __GHOST_FIELD_NAME__C0_Y != baseline.__GHOST_FIELD_NAME__C0_Y ||
    122.             __GHOST_FIELD_NAME__C0_Z != baseline.__GHOST_FIELD_NAME__C0_Z ||
    123.             __GHOST_FIELD_NAME__C0_W != baseline.__GHOST_FIELD_NAME__C0_W ||
    124.  
    125.             __GHOST_FIELD_NAME__C1_X != baseline.__GHOST_FIELD_NAME__C1_X ||
    126.             __GHOST_FIELD_NAME__C1_Y != baseline.__GHOST_FIELD_NAME__C1_Y ||
    127.             __GHOST_FIELD_NAME__C1_Z != baseline.__GHOST_FIELD_NAME__C1_Z ||
    128.             __GHOST_FIELD_NAME__C1_W != baseline.__GHOST_FIELD_NAME__C1_W ||
    129.  
    130.             __GHOST_FIELD_NAME__C2_X != baseline.__GHOST_FIELD_NAME__C2_X ||
    131.             __GHOST_FIELD_NAME__C2_Y != baseline.__GHOST_FIELD_NAME__C2_Y ||
    132.             __GHOST_FIELD_NAME__C2_Z != baseline.__GHOST_FIELD_NAME__C2_Z ||
    133.             __GHOST_FIELD_NAME__C2_W != baseline.__GHOST_FIELD_NAME__C2_W ||
    134.  
    135.             __GHOST_FIELD_NAME__C3_X != baseline.__GHOST_FIELD_NAME__C3_X ||
    136.             __GHOST_FIELD_NAME__C3_Y != baseline.__GHOST_FIELD_NAME__C3_Y ||
    137.             __GHOST_FIELD_NAME__C3_Z != baseline.__GHOST_FIELD_NAME__C3_Z ||
    138.             __GHOST_FIELD_NAME__C3_W != baseline.__GHOST_FIELD_NAME__C3_W
    139.         ) ? (1u<<__GHOST_MASK_INDEX__) : 0;
    140.         #endregion
    141.         #region __GHOST_WRITE__
    142.         if ((changeMask__GHOST_MASK_BATCH__ & (1 << __GHOST_MASK_INDEX__)) != 0)
    143.             writer.WritePackedIntDelta(__GHOST_FIELD_NAME__C0_X, baseline.__GHOST_FIELD_NAME__C0_X, compressionModel);
    144.             writer.WritePackedIntDelta(__GHOST_FIELD_NAME__C0_Y, baseline.__GHOST_FIELD_NAME__C0_Y, compressionModel);
    145.             writer.WritePackedIntDelta(__GHOST_FIELD_NAME__C0_Z, baseline.__GHOST_FIELD_NAME__C0_Z, compressionModel);
    146.             writer.WritePackedIntDelta(__GHOST_FIELD_NAME__C0_W, baseline.__GHOST_FIELD_NAME__C0_W, compressionModel);
    147.  
    148.             writer.WritePackedIntDelta(__GHOST_FIELD_NAME__C1_X, baseline.__GHOST_FIELD_NAME__C1_X, compressionModel);
    149.             writer.WritePackedIntDelta(__GHOST_FIELD_NAME__C1_Y, baseline.__GHOST_FIELD_NAME__C1_Y, compressionModel);
    150.             writer.WritePackedIntDelta(__GHOST_FIELD_NAME__C1_Z, baseline.__GHOST_FIELD_NAME__C1_Z, compressionModel);
    151.             writer.WritePackedIntDelta(__GHOST_FIELD_NAME__C1_W, baseline.__GHOST_FIELD_NAME__C1_W, compressionModel);
    152.  
    153.             writer.WritePackedIntDelta(__GHOST_FIELD_NAME__C2_X, baseline.__GHOST_FIELD_NAME__C2_X, compressionModel);
    154.             writer.WritePackedIntDelta(__GHOST_FIELD_NAME__C2_Y, baseline.__GHOST_FIELD_NAME__C2_Y, compressionModel);
    155.             writer.WritePackedIntDelta(__GHOST_FIELD_NAME__C2_Z, baseline.__GHOST_FIELD_NAME__C2_Z, compressionModel);
    156.             writer.WritePackedIntDelta(__GHOST_FIELD_NAME__C2_W, baseline.__GHOST_FIELD_NAME__C2_W, compressionModel);
    157.  
    158.             writer.WritePackedIntDelta(__GHOST_FIELD_NAME__C3_X, baseline.__GHOST_FIELD_NAME__C3_X, compressionModel);
    159.             writer.WritePackedIntDelta(__GHOST_FIELD_NAME__C3_Y, baseline.__GHOST_FIELD_NAME__C3_Y, compressionModel);
    160.             writer.WritePackedIntDelta(__GHOST_FIELD_NAME__C3_Z, baseline.__GHOST_FIELD_NAME__C3_Z, compressionModel);
    161.             writer.WritePackedIntDelta(__GHOST_FIELD_NAME__C3_W, baseline.__GHOST_FIELD_NAME__C3_W, compressionModel);
    162.         #endregion
    163.     }
    164.  
    165.     public void Deserialize(uint tick, ref GhostSnapshotData baseline, DataStreamReader reader, ref DataStreamReader.Context ctx,
    166.         NetworkCompressionModel compressionModel)
    167.     {
    168.         #region __GHOST_READ__
    169.         if ((changeMask__GHOST_MASK_BATCH__ & (1 << __GHOST_MASK_INDEX__)) != 0)
    170.         {
    171.             __GHOST_FIELD_NAME__C0_X = reader.ReadPackedIntDelta(ref ctx, baseline.__GHOST_FIELD_NAME__C0_X, compressionModel);
    172.             __GHOST_FIELD_NAME__C0_Y = reader.ReadPackedIntDelta(ref ctx, baseline.__GHOST_FIELD_NAME__C0_Y, compressionModel);
    173.             __GHOST_FIELD_NAME__C0_Z = reader.ReadPackedIntDelta(ref ctx, baseline.__GHOST_FIELD_NAME__C0_Z, compressionModel);
    174.             __GHOST_FIELD_NAME__C0_W = reader.ReadPackedIntDelta(ref ctx, baseline.__GHOST_FIELD_NAME__C0_W, compressionModel);
    175.  
    176.             __GHOST_FIELD_NAME__C1_X = reader.ReadPackedIntDelta(ref ctx, baseline.__GHOST_FIELD_NAME__C1_X, compressionModel);
    177.             __GHOST_FIELD_NAME__C1_Y = reader.ReadPackedIntDelta(ref ctx, baseline.__GHOST_FIELD_NAME__C1_Y, compressionModel);
    178.             __GHOST_FIELD_NAME__C1_Z = reader.ReadPackedIntDelta(ref ctx, baseline.__GHOST_FIELD_NAME__C1_Z, compressionModel);
    179.             __GHOST_FIELD_NAME__C1_W = reader.ReadPackedIntDelta(ref ctx, baseline.__GHOST_FIELD_NAME__C1_W, compressionModel);
    180.  
    181.             __GHOST_FIELD_NAME__C2_X = reader.ReadPackedIntDelta(ref ctx, baseline.__GHOST_FIELD_NAME__C2_X, compressionModel);
    182.             __GHOST_FIELD_NAME__C2_Y = reader.ReadPackedIntDelta(ref ctx, baseline.__GHOST_FIELD_NAME__C2_Y, compressionModel);
    183.             __GHOST_FIELD_NAME__C2_Z = reader.ReadPackedIntDelta(ref ctx, baseline.__GHOST_FIELD_NAME__C2_Z, compressionModel);
    184.             __GHOST_FIELD_NAME__C2_W = reader.ReadPackedIntDelta(ref ctx, baseline.__GHOST_FIELD_NAME__C2_W, compressionModel);
    185.  
    186.             __GHOST_FIELD_NAME__C3_X = reader.ReadPackedIntDelta(ref ctx, baseline.__GHOST_FIELD_NAME__C3_X, compressionModel);
    187.             __GHOST_FIELD_NAME__C3_Y = reader.ReadPackedIntDelta(ref ctx, baseline.__GHOST_FIELD_NAME__C3_Y, compressionModel);
    188.             __GHOST_FIELD_NAME__C3_Z = reader.ReadPackedIntDelta(ref ctx, baseline.__GHOST_FIELD_NAME__C3_Z, compressionModel);
    189.             __GHOST_FIELD_NAME__C3_W = reader.ReadPackedIntDelta(ref ctx, baseline.__GHOST_FIELD_NAME__C3_W, compressionModel);
    190.         }
    191.         else
    192.         {
    193.             __GHOST_FIELD_NAME__C0_X = baseline.__GHOST_FIELD_NAME__C0_X;
    194.             __GHOST_FIELD_NAME__C0_Y = baseline.__GHOST_FIELD_NAME__C0_Y;
    195.             __GHOST_FIELD_NAME__C0_Z = baseline.__GHOST_FIELD_NAME__C0_Z;
    196.             __GHOST_FIELD_NAME__C0_W = baseline.__GHOST_FIELD_NAME__C0_W;
    197.  
    198.             __GHOST_FIELD_NAME__C1_X = baseline.__GHOST_FIELD_NAME__C1_X;
    199.             __GHOST_FIELD_NAME__C1_Y = baseline.__GHOST_FIELD_NAME__C1_Y;
    200.             __GHOST_FIELD_NAME__C1_Z = baseline.__GHOST_FIELD_NAME__C1_Z;
    201.             __GHOST_FIELD_NAME__C1_W = baseline.__GHOST_FIELD_NAME__C1_W;
    202.  
    203.             __GHOST_FIELD_NAME__C2_X = baseline.__GHOST_FIELD_NAME__C2_X;
    204.             __GHOST_FIELD_NAME__C2_Y = baseline.__GHOST_FIELD_NAME__C2_Y;
    205.             __GHOST_FIELD_NAME__C2_Z = baseline.__GHOST_FIELD_NAME__C2_Z;
    206.             __GHOST_FIELD_NAME__C2_W = baseline.__GHOST_FIELD_NAME__C2_W;
    207.  
    208.             __GHOST_FIELD_NAME__C3_X = baseline.__GHOST_FIELD_NAME__C3_X;
    209.             __GHOST_FIELD_NAME__C3_Y = baseline.__GHOST_FIELD_NAME__C3_Y;
    210.             __GHOST_FIELD_NAME__C3_Z = baseline.__GHOST_FIELD_NAME__C3_Z;
    211.             __GHOST_FIELD_NAME__C3_W = baseline.__GHOST_FIELD_NAME__C3_W;
    212.         }
    213.         #endregion
    214.     }
    215.     public void Interpolate(ref GhostSnapshotData target, float factor)
    216.     {
    217.         #region __GHOST_INTERPOLATE__
    218.         var thisValue = GetCompositeScaleValue();
    219.         var targetValue = target.GetCompositeScaleValue();
    220.         var c0 = math.lerp(thisValue.c0, targetValue.c0, factor);
    221.         var c1 = math.lerp(thisValue.c1, targetValue.c1, factor);
    222.         var c2 = math.lerp(thisValue.c2, targetValue.c2, factor);
    223.         var c3 = math.lerp(thisValue.c3, targetValue.c3, factor);
    224.  
    225.         Set__GHOST_FIELD_NAME__(new float4x4(c0, c1, c2, c3));
    226.         #endregion
    227.     }
    228. }
    229.  
     
    edalbeci likes this.
  3. timjohansson

    timjohansson

    Unity Technologies

    Joined:
    Jul 13, 2016
    Posts:
    473
    The implementation looks good to me.

    You don't have to put your SetupGhostDefaults in any special class, the method is called by unity thanks to the [InitializeOnLoadMethod] attribute - not because of the class it is in. The only reason we are extending ClientServerBootstrap in DotsSample is that we are actually changing the bootstrap process and it seemed like a reasonable place to put the initialization code in.

    I usually do GhostAuthoringComponentEditor.GhostDefaultOverrides.Add(compositeScaleComponent.name, compositeScaleComponent); instead of using the hardcoded string in two places since they always have to match.
     
    MaverickROM likes this.
  4. MaverickROM

    MaverickROM

    Joined:
    Mar 19, 2019
    Posts:
    11
  5. edalbeci

    edalbeci

    Joined:
    Jan 21, 2018
    Posts:
    36
    Thanks @MaverickROM! This is perfect.

    Just to be more explicit about how to use (I fumbled around, and maybe my fumbling isn't correct):

    1. I created a prefab and changed the prefab's transform to have non-standard scaling (1.01 scaling for the x axis, as shown below).
    2. After clicking "Update Component List," the Ghost Authoring Component adds a Composite Scale component, pictured below:
    3. Copy the scripts into /Scripts/Mixed/Components/Editor/, and hit "Update component list".
     
    MaverickROM likes this.