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

Change array based on direction

Discussion in 'Scripting' started by CasualDutchman, Sep 2, 2018.

  1. CasualDutchman

    CasualDutchman

    Joined:
    Jan 20, 2016
    Posts:
    37
    I have a 2d array of floats and a 2d array of Vector2s. The array of floats needs to be changed based on the array of Vector2s.
    e.g.
    Let's say all of the Vector2s are set to (0.7, 0.7) (normalized upper right)

    This can be a case with the array of floats:
    0, 0, 0
    0, 0, 0
    1, 0, 0

    after the update
    0, 0, 0
    0, 1, 0
    0, 0, 0

    or if the direction is slighty more to the right
    0, 0, 0
    0, 0.7, 0
    0.1, 0.2, 0

    The float has moved based on the directional Vector2
    Of course this is very simple. I want every float to be moved by its own Vector2, but focussing on one is fine for now.
    Think of it as a weather forecasting animation, or a flow of people.

    It would already help if someone could refer me to an article or a paper on the matter.
    Thanks
     
  2. Doug_B

    Doug_B

    Joined:
    Jun 4, 2017
    Posts:
    1,596
    This is a really intriguing problem. :)

    What sort of a solution are you looking for? I'm wondering if there may be 2 ways you could go :-
    1. An OO approach where you could view each location as a 'cell' and have a cell class.
    2. A (potentially) more performant data driven solution using the Unity ECS approach. In truth I am not so knowledgeable on the ECS but it sounds like it could perhaps work quite well here.
     
  3. CasualDutchman

    CasualDutchman

    Joined:
    Jan 20, 2016
    Posts:
    37
    I am looking for a function to just evaluate one point at a time. After that, optimization can be done
     
  4. CubicCBridger

    CubicCBridger

    Joined:
    Apr 19, 2017
    Posts:
    44
    How come 0.1 remains in bottom left if its being moved "slightly more to the right?" Are the Array of vector2's always unit vectors or do you also want to make the floats movement also dependent on the magnitude of its corresponding vector2?

    e.g vector2 = 2,0 so corresponding float moves two cells to the right instead of 1.
     
    Doug_B likes this.
  5. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,194
    This kinda depends on what you want to simulate. Is it a value of 1 that's moving freely around in space, and then you have the surrounding cells represent how close that value is to those? Or is this more like a liquid-simulation-like thing, where the value in each cell flows in a direction?
     
  6. CasualDutchman

    CasualDutchman

    Joined:
    Jan 20, 2016
    Posts:
    37
    For now the magnitude is always between 0 and 1. but if it is lower, a value remains
     
  7. CasualDutchman

    CasualDutchman

    Joined:
    Jan 20, 2016
    Posts:
    37
    More like a liquid simulation thing.
     
  8. Doug_B

    Doug_B

    Joined:
    Jun 4, 2017
    Posts:
    1,596
    In that case, you can use a "pull" style algorithm.

    In other words you have 2 arrays- source and destination. Destination starts out as zero. Now each cell in destination can be (discretely) calculated as the sum of (a) what remains of its own original value plus (b) the sum of all values acquired from its immediate neighbours.

    The fact that this calculation is discrete (it only reads from 'source' and also writes only to a single cell in 'destination') means that the solution could potentially be multi-threaded (should that prove beneficial to do so).
     
  9. CasualDutchman

    CasualDutchman

    Joined:
    Jan 20, 2016
    Posts:
    37
    Had the same idea. Its nice to see I was on the right track.
    I'm now looking for a good function/method/algorithm to put the source to the destination, based on the direction
     
  10. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,194
    Since you're not doing an actual simulation, that's pretty much up to you.

    It sounds like you want two things: drain amount, which is how much of the current volume that's drained, and drain direction, which is where the volume is distributed.

    The drain amount is just the magnitude of your Vector2 array at that position. You want to remove the magnitude from the float array, and leave 1-magnitude behind.

    For the drain direction, it sounds like you want to drain either to 1 or 2 adjacent cells - 1 if the vector is pointing perfectly in at one of the 8 surrounding cells, 2 if it's pointing between two. You can figure out which directions you're draining into by checking the clockwise angle between Vector2.right and the Vector2 array.
     
    CasualDutchman likes this.
  11. Doug_B

    Doug_B

    Joined:
    Jun 4, 2017
    Posts:
    1,596
    I can't claim this to be "good" (I don't currently have access to Unity so no idea if this even compiles :)), but as a first stab at it I'm thinking something like a handler for the 2 arrays (I'll call them maps for want of a better term):
    Code (CSharp):
    1. public class HandlerMap
    2. {
    3.     public void Move()
    4.     {
    5.         Array.Clear(m_targetMap, 0, m_targetMap.Length);
    6.  
    7.         HandlerCell cell = new HandlerCell(m_sourceMap, m_targetMap, m_movement);
    8.         Vector2Int index = Vector2Int.zero;
    9.  
    10.         for (int x = 0; x < m_mapSize; x++)
    11.         {
    12.             for (int y = 0; y < m_mapSize; y++)
    13.             {
    14.                 index.x = x;
    15.                 index.y = y;
    16.                 cell.Pull(index);
    17.             }
    18.         }
    19.     }
    20.  
    21.     public void Start()
    22.     {
    23.         m_sourceMap = m_map1;
    24.         m_targetMap = m_map2;
    25.     }
    26.  
    27.     const int m_mapSize = 10;
    28.  
    29.     float[,] m_map1 = new float[m_mapSize, m_mapSize];
    30.     float[,] m_map2 = new float[m_mapSize, m_mapSize];
    31.     float[,] m_sourceMap;
    32.     float[,] m_targetMap;
    33.  
    34.     Vector2[,] m_movement = new Vector2[m_mapSize, m_mapSize];
    35. }

    And then a handler for a cell (I am assuming that the 'flow' gives both the direction and the drain amount). As mentioned, these cell calculations should be capable of running in parallel if necessary:
    Code (CSharp):
    1. public class HandlerCell
    2. {
    3.     public HandlerCell(float[,] source, float[,] destination, Vector2[,] movement)
    4.     {
    5.         m_mapSize   = m_sourceMap.Length;
    6.         m_sourceMap = source;
    7.         m_targetMap = destination;
    8.         m_movement  = movement;
    9.     }
    10.  
    11.     public void Pull(Vector2Int cellIndex)
    12.     {
    13.         float remains = m_sourceMap[cellIndex.x, cellIndex.y] * (1f - m_movement[cellIndex.x, cellIndex.y].magnitude);
    14.         float pull = 0f;
    15.  
    16.         for (int x = -1; x < 2; x++)
    17.         {
    18.             for (int y = -1; y < 2; y++)
    19.             {
    20.                 if (x == 0 && y == 0) continue;
    21.  
    22.                 if (cellIndex.x + x < 0) continue;
    23.                 if (cellIndex.x + x >= m_mapSize) continue;
    24.  
    25.                 if (cellIndex.y + y < 0) continue;
    26.                 if (cellIndex.y + y >= m_mapSize) continue;
    27.  
    28.                 // Calculate the amount that has drained in from this neighbour.
    29.                 Vector2 directionToUs = new Vector2(x, y);
    30.                 Vector2 angleToUs = Vector2.Angle(
    31.                       m_movement[cellIndex.x + x, cellIndex.y + y]
    32.                     , directionToUs);
    33.  
    34.                 pull +=
    35.                       m_sourceMap[cellIndex.x + x, cellIndex.y + y]
    36.                     * (180f - angleToUs);
    37.             }
    38.         }
    39.  
    40.         m_targetMap[cellIndex.x, cellIndex.y] = remains + pull;
    41.     }
    42.  
    43.     readonly int m_mapSize;
    44.  
    45.     readonly float[,] m_sourceMap;
    46.     float[,] m_targetMap;
    47.  
    48.     readonly Vector2[,] m_movement;
    49. }

    EDIT: as I think about this now, my maths on the "pull" amount is horribly wrong as it will allow some to drain contrary to its indicated direction! :oops:
     
    Last edited: Sep 3, 2018
  12. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,194
    Code (csharp):
    1. pull +=
    2.     m_sourceMap[cellIndex.x + x, cellIndex.y + y]
    3.   * (180f - angleToUs);
    That'll need to be normalized! If something's pointing directly at this cell, it's drawing 180 times the amount at that cell. You're also not taking the flow magnitude at the cell you're pulling from into account.

    It'll also draw something from every cell, as long as it's not pointing exactly away. From the diagrams in the first post, it looks like the flow vector only points at a maximum of two cells.
     
    Doug_B likes this.
  13. Doug_B

    Doug_B

    Joined:
    Jun 4, 2017
    Posts:
    1,596
    Agreed. I think my maths (and logic) leaves a little to be desired there .:)
     
    Last edited: Sep 3, 2018
  14. CasualDutchman

    CasualDutchman

    Joined:
    Jan 20, 2016
    Posts:
    37
    I currently have something, it's not good, but works for now.
    I'm not using a class for a cell, just an array of floats and Vector2s
    This was just for testing, but it really promising.
    It currently just looks at a 3x3 array and calculates the percentage of exchange.
    This percentage is used to calculate the exchange in numbers
    (currently looks at the middle cell of the 3x3 array)
    Code (csharp):
    1.  
    2. for (int y = 0; y < 3; y++) {
    3.             for (int x = 0; x < 3; x++) {
    4.                 if (x == 1 && y == 1)
    5.                     continue;
    6.  
    7.                 Vector2 dir = new Vector2(x, y) - new Vector2(1, 1); // get the direction for every cell
    8.  
    9.                 float angle = Vector2.Angle(direction, dir); //calculate the angle between the direction and cell direction
    10.  
    11.                 if (angle >= 45) //if the angle is more than 45, ignore it
    12.                     continue;
    13.  
    14.                 //if the angle is less than 45, we can use it
    15.  
    16.                 float percent = (45f - angle) / 45f; //percent value beteen 0 - 1
    17.                 //get the percent per cell, this is used to determin the new value for the neighbor cell
    18.  
    19.                 values[x, y] = percent.ToString("F2"); //just debug, this will have the adding function
    20.             }
    21.         }
    22.  
     
  15. Doug_B

    Doug_B

    Joined:
    Jun 4, 2017
    Posts:
    1,596
    Ok, nice. So you are going for the "push" option.

    Also, just thinking about another point I believe Baste alluded to in post#10 : "drain amount, which is how much of the current volume that's drained". How will you handle a movement angle of (absolute) 45 degrees? If the source cell has a value of 1 and it travels exactly along the x axis (let's say angle zero degrees) it will travel at a speed of one unit. How fast should it travel if angled at exactly 45 degrees?

    Addendum: maybe if the 'cells' were logically octagonal, that would resolve this angular speed problem?
     
    Last edited: Sep 3, 2018
  16. CasualDutchman

    CasualDutchman

    Joined:
    Jan 20, 2016
    Posts:
    37
    For now, when the angle is 45 and the magnitude is one, it will just go to that cell fully.
    Normally, the distance at a cell with an offset of (1, 1) is 1.414, but that is ignored.

    it will just travel 1 unit if the magnitude is 1. 0.23 units, if the magniture is 0.23