#Value type equivalent to array or list suitable for use in structs that will be used to store state

1 messages · Page 1 of 1 (latest)

deft plinth
#

I need recommendations for a Value type equivalent to array or list suitable for use in structs that will be used to store world state per tick of a game.
I have quite a few child transforms, of which, I'd like to store their rotation and position for syncing purposes over a network. I could do it via hardcoding each transforms position and rotation variable, but I'm wondering if there's a better way, such as storing them in a value type collection, suitable for use in structs.

hollow kelp
#

you would need to make your own struct, you can't make it variable-length.

#

oh actually, i think there is one

#

there's Span, but not quite sure how it works internally. might not be what you want

coarse hazel
#

There's a couple options here, one of which is a fixed-size buffer, but that requires unsafe code.

One simple option is essentially a stupid struct with numbered values

struct MyFixedList {
  public MyData data0;
  public MyData data1;
  // Etc
}```

Obviously you'd need a maximum count hardcoded here and you can make some helper accessors and such.
#

You can also use a fixed size NativeArray

astral trout
#

Is the issue here that you need it to be a value type because your networking solution only supports serializing value types? Because otherwise, you should be able to use arrays and lists just fine in structs.

Otherwise, you can use Unity's native collections (i.e. NativeList etc), which is a value type with unmanaged memory backing it, but that comes with a few caveats around using it without leaking memory or crashing your game.

deft plinth
deft plinth
deft plinth
# astral trout Is the issue here that you need it to be a value type because your networking so...

I'm not sure if I should be using list or array as the struct i'm implementing is for quick usage and will be dumped immediately after access.
Here's an example:

public struct PlayerStatePayload : IPayLoad
{
    private int tick;
    private byte objectID;
    public Vector3 position;
    public uint rotation;
    public Vector3 weaponPosition;
    public uint weaponRotation;
    public Vector3 velocity;
    public Vector3 angularVelocity;
    public Span<Vector3> childTransformPositions;

    public int Tick { get => tick; set => this.tick = value; }

    public byte ObjectID { get => objectID; set => this.objectID = value; }

    public Quaternion GetRot()
    {
        Quaternion rot = Quaternion.identity;
        QuaternionCompressor.DecompressQuaternion(ref rot, rotation);
        return rot;
    }

    public Quaternion GetEmitterRot()
    {
        Quaternion rot = Quaternion.identity;
        QuaternionCompressor.DecompressQuaternion(ref rot, weaponRotation);
        return rot;
    }

    public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
    {
        serializer.SerializeValue(ref objectID);
        serializer.SerializeValue(ref position);
        serializer.SerializeValue(ref rotation);
        serializer.SerializeValue(ref velocity);
        serializer.SerializeValue(ref angularVelocity);
        serializer.SerializeValue(ref weaponRotation);
        serializer.SerializeValue(ref weaponPosition);
    }
}
#

It's mainly going to be used for player prediction and reconciliation

deft plinth
coarse hazel
deft plinth
coarse hazel
coarse hazel
#

it depends 😜

#

It depends on the shape of your data, how big this "fixed" length is, how sparse the array is, the access patterns, how frequently it changes and needs to be synced, etc.

deft plinth
#

Maybe i should simply hardcode all these variables in the struct

grizzled sail
#

Just to include other options, you can also use explicit struct layout and field offset to make space for a fixed number of elements and use unsafe to index into it. (not that I necessarily recommend you do it this way)

[StructLayout(LayoutKind.Explicit, Size = 64)]
public struct MyStruct
{
    [FieldOffset(0)]
    public int Number;
    
    [FieldOffset(4)]
    public Vector3 Vector;
    
    [FieldOffset(16)]
    private Vector3 childPositions;
    
    public unsafe ref Vector3 this[int index]
    {
        get
        {
            if (index < 0 || index >= 4)
                throw new ArgumentOutOfRangeException(nameof(index));

            fixed(Vector3* position = &childPositions)
            {
                return ref UnsafeUtility.ArrayElementAsRef<Vector3>(position, index);
            }
        }
    }
}
#

This is similar to fixed buffer, but that only supports built-in primitives, not structs like Vector3.