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

[C#] How to properly and efficiently display triangles over selected units?

Discussion in 'Scripting' started by amzin7000, Jun 2, 2016.

  1. amzin7000

    amzin7000

    Joined:
    Nov 16, 2014
    Posts:
    48
    I have a simple system in my 2d game that allows the player to select units, similar many other games.
    I decided that the best way for the player to determine what units are selected is to simply draw triangles over each unit.

    I decided to have a UI graphic component do the drawing, and it works flawlessly. But especially when you moving the camera, you can notice lag between the position of the unit and the triangle. here's what i have so far:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3. using System.Collections.Generic;
    4.  
    5. public class drawUnitSelections : Graphic{
    6.     public List<Vector2> points;
    7.  
    8.     protected override void OnPopulateMesh(VertexHelper vh){
    9.         Color32 color32 = color;
    10.         vh.Clear();
    11.      
    12.         //remove empties from list
    13.         for(int i=0;i<points.Count;i++){
    14.             vh.AddVert(points[i], color32, new Vector2(0f, 0f));
    15.             vh.AddVert(points[i]+new Vector2(7,10), color32, new Vector2(0f, 1f));
    16.             vh.AddVert(points[i]+new Vector2(-7,10), color32, new Vector2(1f, 1f));
    17.          
    18.             vh.AddTriangle(i*3, i*3+1, i*3+2);
    19.         }
    20.     }
    21.  
    22.     void Update(){
    23.      
    24.         if(Application.isPlaying&&Time.frameCount%1==0){
    25.             points.Clear();
    26.             for(int i=UnitManager.main.selectedUnits.Count-1;i>=0;i--){
    27.                 Vector3 point = UnitManager.main.selectedUnits[i].transform.position;
    28.                 points.Add(Camera.main.WorldToScreenPoint(point+new Vector3(0,1.2f)));
    29.             }
    30.             SetAllDirty();//causes the mesh to be redrawn a frame or two later, causing the lag
    31.         }
    32.     }
    33. }

    OnPopulateMesh doesn't seem to update on its own, even when i change the points, so i discovered that adding a SetAllDirty() causes OnPopulateMesh to be run again, but a frame too late, which causes lag.

    The documentation on graphic doesn't help at all. In fact it doesn't even note the existence of OnPopulateMesh, or any other functions that could be useful. Thanks for the source @LeftyRighty

    So what should I do? and is doing it like this the right way? (cause looping through all the selected units seems slow to me, but I don't see any other way of doing this)

    I would greatly appreciate any suggestions. thanks.
     
    Last edited: Jun 2, 2016
  2. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    amzin7000 likes this.
  3. amzin7000

    amzin7000

    Joined:
    Nov 16, 2014
    Posts:
    48
  4. amzin7000

    amzin7000

    Joined:
    Nov 16, 2014
    Posts:
    48
  5. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    Couldn't you just put a little arrow graphic as a child object of the unit and turn it on and off when they're selected? Seems like it would be much faster than rebuilding a dynamic mesh every frame.
     
    amzin7000 likes this.
  6. ThermalFusion

    ThermalFusion

    Joined:
    May 1, 2011
    Posts:
    906
    Are you sure you're updating the UI after any camera movement has been done and not after. Just to make sure that's not where the lagging behind is coming from.
     
    amzin7000 likes this.
  7. tawdry

    tawdry

    Joined:
    Sep 3, 2014
    Posts:
    1,356
    Makeswiftwings makes a good point or even instead of a arrow use a triangle would be a lot more efficient than the way you are trying to do it.
     
    amzin7000 likes this.
  8. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    I just throw on an extra sprite and make it active or inactive as needed.

    If you need to upload mesh data instantly you can always use UploadMeshData
     
    amzin7000 likes this.
  9. cjdev

    cjdev

    Joined:
    Oct 30, 2012
    Posts:
    42
    I don't have any experience with VertexHelper but isn't the idea to use FillMesh to fill an actual Mesh object? If that's the case you can also try using MarkDynamic to improve performance if you're updating every frame.
     
    amzin7000 likes this.
  10. amzin7000

    amzin7000

    Joined:
    Nov 16, 2014
    Posts:
    48
    Sorry for not replying, I've been a bit preoccupied by work and sleep.

    @makeshiftwings I could but then I have to make an object pool, and I would have to update the position of every arrow to stay above my units.

    @ThermalFusion I believe the way my scripts are set up it should update right after the camera, but yeah I'll try updating it directly.

    @tawdry I am using triangles. The Script generates small triangle meshes over every unit.

    @BoredMormon I'm not sure about the performance concerns behind that, but I'll look into it.

    @cjdev Thanks, I didn't know about MarkDynamic. It should help me improve performance a lot. I wouldn't need a pool of objects, and could simply regenerate my mesh on every update and after the camera moves.

    Thanks a lot guys, the Unity community does not disappoint.

    Cheers! :)
     
  11. tawdry

    tawdry

    Joined:
    Sep 3, 2014
    Posts:
    1,356
    @makeshiftwings I could but then I have to make an object pool, and I would have to update the position of every arrow to stay above my units.

    The arrow/triangle would not exactly take up much memory so why would you need a pool. create all units with the object as a child and its position would automatically change when the units does No code required.
     
  12. amzin7000

    amzin7000

    Joined:
    Nov 16, 2014
    Posts:
    48
    My units also rotate and scale, so I would need to have a script update them whichever way i decide to do it.
    In the end I don't think it matters too much what i choose to do now that i've scrapped my drawUnitSelections script.
     
  13. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    When you make one object a child of another object, it inherits its position, rotation, and scale automatically.
     
  14. amzin7000

    amzin7000

    Joined:
    Nov 16, 2014
    Posts:
    48
    I meant that my units scale and rotate every frame.
     
  15. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    It automatically inherits the position, rotation, and scale of its parent when the parent's values change. That's what parent and child objects are for. If you make a sword a child of a character, the sword automatically moves with the characters as the character moves/rotates/scales.
     
  16. amzin7000

    amzin7000

    Joined:
    Nov 16, 2014
    Posts:
    48
    Thanks, but I would like triangles the same size and distance over every unit (just like the drawUnitSelections script does). The arrows should stay the same distance above all units, not rotate with them or scale with them as they rotate or scale.

    That forces me to update their position every frame. If the triangles were children of each unit, i would have to do some simple math rectifying their rotation and position relative to the unit every frame. Between these two choices the simpler one seems like simply updating each triangle's position.
     
  17. Errorsatz

    Errorsatz

    Joined:
    Aug 8, 2012
    Posts:
    555
    You could structure units as:

    UnitRoot
    -- UnitBody

    Where UnitRoot has nothing visible and doesn't rotate/scale, only moves. Then you can attach things to the root or body, depending on the behavior you want.