Search Unity

Question Get data from a list.

Discussion in 'Visual Scripting' started by Samazure, Apr 26, 2022.

  1. Samazure

    Samazure

    Joined:
    Mar 31, 2013
    Posts:
    7
    Hello everyone.

    Basically I'm using the Cast() method of an object's box collider and it requires an array of raycasthit to store the hit information. I can create that without any issue.

    I want to do different things depending on the tag of the collided object.

    The problem is the Cast() method only returns an int with the amount of collisions found, and if I try to connect the array again later it shows to be null.

    My assumption is that because there is not an arrow (the flow connections) between the first use of the array and when I need to use it, so it uses the version before it is updated and when the first item is still null.

    As a note, I am familiar with C# code but am having a hard time converting certain things to this node system. In C# what I want to achieve would be something like this:
    RaycastHit2d[] hits = new RaycastHit2d[1];
    if (GetComponent<BoxCollider2d>().Cast(Vector2.Right, hits, 1.0f) > 0) {
    // This is the part I can't emulate because it says hits[0].transform is null
    if (hits[0].transform.tag == "testTag") {
    // Do something
    } else {
    // Do something else
    }
    }


    Thanks for your help.
     
  2. munchmo

    munchmo

    Joined:
    May 20, 2019
    Posts:
    85
    How are you storing the cast results? What kind of variable(s) are you using? Can you share the graph you're working on?
     
  3. marcuslelus

    marcuslelus

    Joined:
    Jun 18, 2018
    Posts:
    67
    I had some free time, so, it's not good enough for my taste, but it seems to be working just fine :)
    Things you should look into:
    • ValueOutput can take a callback (line 43)
    • To keep a state in a graph, you should use the IGraphElementWithData interface (line 6, 8 and 81)
    • I didn't have to store everything in data, but it makes it easier to read in the Cast method.
    Hopefully it's clear enough, but what I had in mind was a way to add tags in the left panel inspector, and for each tag, have a trigger output, as to mimic your if/else behaviour. I might do it tomorrow for fun tho...

    Code (CSharp):
    1. using Unity.VisualScripting;
    2. using UnityEngine;
    3.  
    4. [UnitTitle("Box Collider 2D Cast")]
    5. [UnitCategory("Custom/Collider2D")]
    6. public class BoxCollider2DUnit : Unit, IGraphElementWithData
    7. {
    8.     private sealed class Data : IGraphElementData
    9.     {
    10.         public BoxCollider2D origin;
    11.         public Vector2 direction;
    12.         public float distance;
    13.         public bool ignoreSiblings;
    14.         public string tag;
    15.         public RaycastHit2D[] hits = new RaycastHit2D[1];
    16.     }
    17.  
    18.     [DoNotSerialize] public ControlInput cast { get; private set; }
    19.  
    20.     [DoNotSerialize] public ControlOutput hitEvent { get; private set; }
    21.     [DoNotSerialize] public ControlOutput missEvent { get; private set; }
    22.  
    23.     [DoNotSerialize] public ValueInput origin { get; private set; }
    24.     [DoNotSerialize] public ValueInput distance { get; private set; }
    25.     [DoNotSerialize] public ValueInput direction { get; private set; }
    26.     [DoNotSerialize] public ValueInput ignoreSiblings { get; private set; }
    27.     [DoNotSerialize] public ValueInput tag { get; private set; }
    28.     [DoNotSerialize] public ValueOutput hit { get; private set; }
    29.  
    30.  
    31.     protected override void Definition()
    32.     {
    33.         cast = ControlInput(nameof(cast), Cast);
    34.         hitEvent = ControlOutput(nameof(hitEvent));
    35.         missEvent = ControlOutput(nameof(missEvent));
    36.  
    37.         tag = ValueInput(nameof(tag), string.Empty);
    38.         origin = ValueInput(typeof(BoxCollider2D), nameof(origin));
    39.         distance = ValueInput(nameof(distance), 1f);
    40.         direction = ValueInput(nameof(direction), Vector2.right);
    41.         ignoreSiblings = ValueInput(nameof(ignoreSiblings), true);
    42.  
    43.         hit = ValueOutput(nameof(hit), GetValue);
    44.  
    45.         Succession(cast, hitEvent);
    46.         Succession(cast, missEvent);
    47.     }
    48.  
    49.     private Transform GetValue(Flow flow)
    50.     {
    51.         Data data = GetData(flow);
    52.         return data.hits[0].transform;
    53.     }
    54.  
    55.     private ControlOutput Cast(Flow flow)
    56.     {
    57.         Data data = GetData(flow);
    58.  
    59.         if (data.origin.Cast(data.direction, data.hits, data.distance, data.ignoreSiblings) > 0)
    60.         {
    61.             if (data.hits[0].transform.CompareTag(data.tag))
    62.             {
    63.                 return hitEvent;
    64.             }
    65.         }
    66.  
    67.         return missEvent;
    68.     }
    69.  
    70.     private Data GetData(Flow flow)
    71.     {
    72.         var data = flow.stack.GetElementData<Data>(this);
    73.         data.distance = flow.GetValue<float>(distance);
    74.         data.origin = flow.GetValue<BoxCollider2D>(origin);
    75.         data.direction = flow.GetValue<Vector2>(direction);
    76.         data.ignoreSiblings = flow.GetValue<bool>(ignoreSiblings);
    77.         data.tag = flow.GetValue<string>(tag);
    78.         return data;
    79.     }
    80.  
    81.     public IGraphElementData CreateData() => new Data();
    82. }
     
  4. Samazure

    Samazure

    Joined:
    Mar 31, 2013
    Posts:
    7
    Ok, so a bit more about what I tried.

    I use a subgraph matching to each direction input to move.
    SubgraphApplied.png

    It basically takes a vector that is both direction and distance, it checks if the object would collide with anything in said direction/distance, and if it doesn't, it moves. The inside of the subgraph looks like this:
    MovementSubgraph.png

    Right now my test scene has the player object (white rectangle), some walls (red rectangles), and something to get (the yellow circle).
    SceneOverview.png

    My intent is to set a tag to the walls so they stop the player's movement (like it currently does) and the ball can be collected.

    What I tried so far looks like this:
    ChangeAttempts.png

    Mostly what I changed is that now I get the first item in the list I created, extract its transform to then get its tag, then compare it to a "Wall" tag; if it is a wall, it doesn't move and shows a debug message, otherwise, it moves.

    The problem is, when I try to run it, it seems the list is not updated and does not have (at least yet) the RaycastHit2D information. Here is a picture of me running it, when I'm getting the transform out of the RaycastHit2D it seems to be null.
    NullError.png

    I assume the problem is because there is no "Arrow flow" going through the list, therefore when I call it, it gives its state as newly created with an empty RaycastHit2D.

    Any idea what would help?
     
  5. munchmo

    munchmo

    Joined:
    May 20, 2019
    Posts:
    85
    What is the actual error you're getting from the Component Get Tag node there? Is it the usual NullReferenceException?

    Actually, I think I know where your issue is, you're using the results connector wrong. Instead, you should create a new, empty list (Get rid of that Create you're feeding into the list. save the list in a variable for easy access maybe?) to feed into results. Then use that same list (first item, maybe?) to get the tag.

    What's happening is you create a raycast hit with no information as the first item in the list. Then you're using that same list to output the results of the Cast, so any hits from that are going to be the second (third, fourth, etc) items in the list.
     
    Last edited: May 3, 2022
  6. marcuslelus

    marcuslelus

    Joined:
    Jun 18, 2018
    Posts:
    67
    It would make sense and in most cases, I would say that is usually the right thing to do. But I tried using his graph and it didn't work.

    This is not true though. You can check the Collider2D documentation on Cast, it will not resize the results array and overrides every value it needs to in it. This is mostly to avoid allocation, meaning you would declare the array outside the function, with a defined size, and reuse it when you need it.

    The real issue is that you're creating a list, but BoxCollider2D takes an array. When you pass a list into a ValueInput of type Array, UVS knows how to convert it (yourList.ToArray()). Since ToArray() returns a new instance of an array, you lost the reference.

    So, I would simply suggest a Reflected Unit / Extension Method like this:

    Code (CSharp):
    1. [IncludeInSettings(true)]
    2. public static class BoxCollider2DExtension
    3. {
    4.     static RaycastHit2D[] _hits = new RaycastHit2D[1];
    5.  
    6.     public static Transform CustomCast(this BoxCollider2D coll, Vector2 direction, int distance = 1)
    7.     {
    8.         var hit = coll.Cast(direction, _hits, distance);
    9.         return hit > 0 ? _hits[0].transform : null;
    10.     }
    11. }
    And use it like this (btw, this has been tested):
    Untitled.png
     
  7. marcuslelus

    marcuslelus

    Joined:
    Jun 18, 2018
    Posts:
    67
    Also, if you're not into writing your custom nodes, you can also use Bolt Addon Community. It's pretty easy to install and it comes with arrays (and a bunch of other cool stuff). Here's how you would use it (again, tested)
    Untitled2.png

    Note that you need to set your array in a variable, otherwise you lose the reference. you can also see that the type for my variable is List<RaycastHit2D>.
     
  8. PanthenEye

    PanthenEye

    Joined:
    Oct 14, 2013
    Posts:
    2,075
    Take note that Bolt Community Addons are not maintained anymore. Some stuff already seems to be breaking in newer versions of UVS. And when a graph contains a custom node that doesn't compile (say in case Unity make an API change) then the whole graph becomes broken and inaccessible until the faulty node is fixed. And if you want to later remove the Addons, then all graphs containing nodes from addons will break (unless removed manually everywhere).
     
    Last edited: May 7, 2022
    marcuslelus likes this.
  9. marcuslelus

    marcuslelus

    Joined:
    Jun 18, 2018
    Posts:
    67
    Do we know why it's not maintained anymore? Should we just fork it and keep it alive elsewhere?
     
  10. PanthenEye

    PanthenEye

    Joined:
    Oct 14, 2013
    Posts:
    2,075
    Reality.Stop() and JasonJonesLASM were the main maintainers. Since the Unity acquisition, Jason was the main driving force behind all the updates on the addons, especially UVS compatibility. But it seems that Jason has left the community altogether and doesn't code anymore, his last commit was October last year. He's posted some ads for providing graphic design services and such and he's also left the official Bolt/UVS Discord server in which he was active previously.

    And Reality.Stop() shared on Discord that Unity contacted them and asked if they can merge the more useful parts of addons into UVS. I think the conversation was mainly about reroute nodes and perhaps collapse node selection to subgraph functionality. But they definitely won't port all the addons content. I doubt defined events, return events, the arrays implementation and many others will make the cut.

    Reality.Stop() also mentioned that UVS' development direction since the cancellation of Bolt 2 didn't align with his goals and use cases and since Unity asked to integrate some of the addons, he's basically done with supporting them going forward. He was also not enthusiastic about the need to refactor the entire addons library in relation to the upcoming UVS 1.8 API which will be completely new.

    So the creator and the main maintainer of the addons are officially out of the game. There are no active maintainers left.
     
    Last edited: May 9, 2022
  11. marcuslelus

    marcuslelus

    Joined:
    Jun 18, 2018
    Posts:
    67
    Then I do have another unrelated question... Who are you? It seems you're everywhere even on the old Bolt forum. As for me, I use UVS at work, I also developed my own fully working reflected-driven Node System a while ago using the Graph Tool Foundation (GraphView and stuff), so when the new 1.8 version comes, I feel like I might have quite the skills to start a new Community Addons.. Anyway, just a thought!
     
  12. PanthenEye

    PanthenEye

    Joined:
    Oct 14, 2013
    Posts:
    2,075
    Just a random OG Bolt user and Unity based freelancer who's been around for a while. I was heavily invested in Bolt 2's development and testing before that got cancelled and I've stuck around for updates on the state of UVS in hopes it'll outgrow its Bolt 1 roots. Hasn't happened so far, but the new runtime and GTF migration look hopeful.

    I'm not using visual scripting anymore. It has too many workflow and performance issues for my liking, and it's hard to debug. For now, I'm sticking around, since I like the concept of visual scripting for many reasons and I do want to use it for my client projects in the future.

    But I'm not sure when any of the promised improvements from two years ago will actually materialize, so I'm reluctant to invest deeply into the platform. Got burned with Bolt 2 already. So I'm looking forward to GTF more than UVS' GTF migration since I won't be dependent on their timelines and can comfortably write my own tooling that's on the same level as all other Unity graph based tools on the front end. Maybe next year.

    That's quite the programming acumen you have there, definitely enough for new and improved Community Addons. I'm sure the community would appreciate it a lot, especially if you can revive Manual Event and Defined Events. Since I'm not sure I'll use UVS in the future, I'm reluctant to commit to a project like that and perhaps it's better left to more focused developers. I'm more of a generalist.
     
    Starpaq2 likes this.
  13. Trindenberg

    Trindenberg

    Joined:
    Dec 3, 2017
    Posts:
    398
    I was writing various nodes in 1.8. Actually fairly easy upgrading to it. Not sure what the current status is with 1.8 though eg. Is it being worked on, dropped, delayed, no idea.
     
  14. Trindenberg

    Trindenberg

    Joined:
    Dec 3, 2017
    Posts:
    398
    Manual event was my fav. The original design on addons i simplified got rid of the nodebutton issues, no idea who uses or needs it though. Trying to push myself back into everything though.