Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question Examples of using DynamicBuffer in Aspects?

Discussion in 'Entity Component System' started by jwvanderbeck, May 20, 2023.

  1. jwvanderbeck

    jwvanderbeck

    Joined:
    Dec 4, 2014
    Posts:
    820
    Are there any examples of using and modifying DynamicBuffers in an Aspect? I dug through all the ECS Samples in Github but none that I could find use buffers in an Aspect.
     
  2. jwvanderbeck

    jwvanderbeck

    Joined:
    Dec 4, 2014
    Posts:
    820
    Here is the code I'm struggling with. I know this doesn't work (Rider even tells me it won't) but this is what I'm trying to do:

    Code (CSharp):
    1.     public readonly partial struct CreateCommandAspect : IAspect
    2.     {
    3.         public readonly Entity entity;
    4.  
    5.         public readonly DynamicBuffer<GenericCommand> commands;
    6.  
    7.         // for efficiency in creating command components, commands
    8.         // are stored in reverse order, with the last command in the list being the first to be executed
    9.         // and the first in the list being the last to be executed
    10.        
    11.         public GenericCommandData PopCommand(EntityCommandBuffer ecb)
    12.         {
    13.             if (commands.Length == 0)
    14.                 return new GenericCommandData { command = CommandType.Idle };
    15.            
    16.             var command = commands[0];
    17.             commands.RemoveAt(0);
    18.             return command.commandData;
    19.         }
    20.         public void PushCommand(GenericCommandData command, EntityCommandBuffer ecb)
    21.         {
    22.             // pushing a new command to the list needs to push it to the front of the list and
    23.             // move everything else down
    24.             commands.Insert(0, new GenericCommand {commandData = command});
    25.         }
    26.        
    27.        
    28.        
    29.         // methods to validate a given command can be successfully created
    30.     }
    31.  
    I assumed I could make these inserts/removes using an ECB so I set it up to let me pass that in, but I can't find any such methods to be used through the ECB.

    Trying to make the changes as I have in this code of course just silently fails because it is working on a copy of the buffer.
     
  3. print_helloworld

    print_helloworld

    Joined:
    Nov 14, 2016
    Posts:
    231
    I believe you also need to return the ecb if youre modifying it outside of its original created scope, in this case the method would be static with an out parameter for `GenericCommandData`
     
  4. jwvanderbeck

    jwvanderbeck

    Joined:
    Dec 4, 2014
    Posts:
    820
    That isn't the issue. The issue is that the ECB doesn't actually have any methods for inserting or removing elements. Those methods are on the buffer itself, but when used in an Aspect the buffer is ReadOnly
     
  5. Wobbers

    Wobbers

    Joined:
    Dec 31, 2017
    Posts:
    55
    DynamicBuffers are just pointers to their underlying data which lies somewhere else, so the readonly modifier doesn't really mean anything as far as I know. Your code should work fine, at least with the newest entities version. There is a weird entry in the changelog for 1.0.8 that I don't understand, but might be related, in case you are using an older version:
    Quickly tested it with entities 1.0.10, and it adds a total of 4 entries to the buffer per frame:
    Code (CSharp):
    1. using UnityEngine;
    2. using Unity.Entities;
    3.  
    4. public struct MyBuffer : IBufferElementData { public int Value; }
    5.  
    6. public readonly partial struct MyBufferAspect : IAspect
    7. {
    8.     public readonly DynamicBuffer<MyBuffer> TheBuffer;
    9.  
    10.     public void ModifyBuffer()
    11.     {
    12.         // Rider warning: "Possibly impure struct method called on readonly variable: struct value always copied before invocation"
    13.         var lengthBefore = TheBuffer.Length;
    14.         TheBuffer.Add(new MyBuffer { Value = 5 });
    15.         TheBuffer.Add(new MyBuffer { Value = 3 });
    16.         TheBuffer.RemoveAt(0);
    17.         Debug.Log($"Change on RO Buffer: {lengthBefore} -> {TheBuffer.Length}");
    18.      
    19.         // No warning
    20.         lengthBefore = TheBuffer.Length;
    21.         var bufferRw = TheBuffer;
    22.         bufferRw.Add(new MyBuffer { Value = 5 });
    23.         bufferRw.Add(new MyBuffer { Value = 3 });
    24.         bufferRw.RemoveAt(0);
    25.         Debug.Log($"Change on copied Buffer: {lengthBefore} -> {TheBuffer.Length}");
    26.     }
    27. }
    28.  
    29. partial struct MyBufferJob : IJobEntity
    30. {
    31.     void Execute(MyBufferAspect aspect)
    32.     {
    33.         // Rider warning: "Possibly impure struct method called on readonly variable: struct value always copied before invocation"
    34.         var lengthBefore = aspect.TheBuffer.Length;
    35.         aspect.TheBuffer.Add(new MyBuffer { Value = 7 });
    36.         aspect.TheBuffer.Add(new MyBuffer { Value = 9 });
    37.         aspect.TheBuffer.RemoveAt(1);
    38.         Debug.Log($"Change in Job: {lengthBefore} -> {aspect.TheBuffer.Length}");
    39.  
    40.         // No warning
    41.         var noWarningsBuffer = aspect.TheBuffer;
    42.         lengthBefore = aspect.TheBuffer.Length;
    43.         noWarningsBuffer.Add(new MyBuffer { Value = 7 });
    44.         noWarningsBuffer.Add(new MyBuffer { Value = 9 });
    45.         noWarningsBuffer.RemoveAt(1);
    46.         Debug.Log($"Change in Job (copied buffer): {lengthBefore} -> {aspect.TheBuffer.Length}");
    47.     }
    48. }
    49.  
    50. partial struct TestMyBufferSystem : ISystem
    51. {
    52.     public void OnCreate(ref SystemState state)
    53.     {
    54.         state.EntityManager.CreateEntity(typeof(MyBuffer));
    55.     }
    56.  
    57.     public void OnUpdate(ref SystemState state)
    58.     {
    59.         foreach (var aspect in SystemAPI.Query<MyBufferAspect>())
    60.         {
    61.             aspect.ModifyBuffer();
    62.         }
    63.  
    64.         new MyBufferJob().Schedule();
    65.     }
    66. }
    67.  
     
    Neiist likes this.