5

I wrote a C# Script in Unity, which detects all visible objects. Now I would like to output them from left to right, as how they are positioned in the scene. Each objects has a number and yet the script outputs the objects by ascending order.

My idea:

I thought about a for-loop which goes along the horizontal field of view. First calculating the horizontal FOV by:

private static float horizontalFOV() {
    float radAngle = Camera.main.fieldOfView * Mathf.Deg2Rad;
    float radHFOV = 2 * Mathf.Atan(Mathf.Tan(radAngle / 2) * Camera.main.aspect);
    float hFOV = Mathf.Rad2Deg * radHFOV;

    return hFOV;
}

And creating the loop by:

public static string OutputVisibleRenderers (List<Renderer> renderers) {
    float hFov = horizontalFOV ();

    if (null == renderers)
        throw new System.ArgumentNullException ("renderers are null");

    for (int i = 0; i < hFov; i++) {
        foreach (var renderer in renderers) {
                if (IsVisible (renderer)) {
                myList.Add (renderer.name);
            }
        }
    }
    string itemsInOneLine = string.Join ("-", myList.ToArray());
    myList.Clear ();
    print (itemsInOneLine);
    print ("--------------------------------------------------");
    return itemsInOneLine;
}

But unfortunally this doesn't work. So how could I read all objects from left to right?

Anup Kumar Gupta
  • 361
  • 5
  • 14
Viktoria
  • 533
  • 2
  • 7
  • 24
  • Are you getting an error or is `renderers` empty or are they not getting added to the list? – jhhoff02 Jul 27 '17 at 17:49
  • More information on how it "doesn't work" would be helpful. – hatchet - done with SOverflow Jul 27 '17 at 17:56
  • Sorry, I meant the list is created, but all names (which are numbers) are added by ascending order. And I want them to be added by "position" order, from left to right. – Viktoria Jul 27 '17 at 18:09
  • You need to sort them, you can use each object's `transform.position` as the value to sort on. – Draco18s no longer trusts SE Jul 27 '17 at 19:15
  • @Draco18s how may I realize it? – Viktoria Jul 27 '17 at 19:22
  • I don't use Unity, so sorry if I'm missing something, but it seems that although in the outer `for` loop You look from left to right, You don't use this information inside the loop. The inner loop just checks if each renderer is visible and adds it to the list, so they probably appear in the output list in the order they are put in the `renderers` collection. – Lukasz M Jul 27 '17 at 19:27
  • In order to sort the objects from left to right, You should probably calculate their on-screen coordinates and then use them for sorting. To calculate this, You can use WorldToScreenPoint method (https://docs.unity3d.com/ScriptReference/Camera.WorldToScreenPoint.html). I've found some suggestions that may be helpful here: http://answers.unity3d.com/questions/49943/is-there-an-easy-way-to-get-on-screen-render-size.html. To sort it, You can use SortedList structure `https://msdn.microsoft.com/en-us/library/ms132319(v=vs.110).aspx`. – Lukasz M Jul 27 '17 at 19:34

2 Answers2

2

I don't use Unity, so sorry if I'm missing something, but it seems that although in the outer for loop You look from left to right, You don't use this information inside the loop. The inner loop just checks if each renderer is visible and adds it to the list, so they probably appear in the output list in the order they are put in the renderers collection.

In order to sort the objects from left to right, You should probably calculate their on-screen coordinates and then use them for sorting. To calculate this, You can use Camera.WorldToScreenPoint method. I've found some suggestions that may be helpful here: http://answers.unity3d.com/questions/49943/is-there-an-easy-way-to-get-on-screen-render-size.html.

To sort the objects, You can use SortedList class described here: https://msdn.microsoft.com/en-us/library/ms132319(v=vs.110).aspx. After calculating on-screen coordinates for each object, just add it to the list with on-screen X coordinate as the key and the list will automatically keep the objects ordered.

Lukasz M
  • 5,635
  • 2
  • 22
  • 29
0

Supposing renderers is a List, you could use the List.Sort method and use the dot product to sort the objects from left to right.

I've made a script that works here, give it a try

    public System.Collections.Generic.List<Renderer> renderers ;

    private void Start()
    {
        var sortedRenderers = new System.Collections.Generic.List<Renderer>( renderers ) ;
        sortedRenderers.Sort( SortLeftToRight ) ;

        for( int i = 0 ; i < sortedRenderers.Count ; ++i )
            Debug.Log( sortedRenderers[i].name ) ;

    }
    private int SortLeftToRight( Renderer r1, Renderer r2 )
    {
        Transform camTransform = Camera.main.GetComponent<Transform>(); // You can optimize if you cache the transform of the camera
        Vector3 axis = camTransform.right ; 
        Transform t1 = r1.gameObject.GetComponent<Transform>();
        Transform t2 = r2.gameObject.GetComponent<Transform>();
        float dot1 = Vector3.Dot( axis, t1.position - camTransform.position );
        float dot2 = Vector3.Dot( axis, t2.position - camTransform.position );

        if( Mathf.Abs( dot1 - dot2 ) < 0.001f )
            return 0 ;

        return dot1 < dot2 ? -1 : 1 ;
    }

SortRenderers

Useful links :

Hellium
  • 7,206
  • 2
  • 19
  • 49
  • In my code, I don't check if the renderer is visible. Check this question to filter the renderer if they are visible : https://stackoverflow.com/questions/26196/filtering-collections-in-c-sharp – Hellium Jul 28 '17 at 18:20