Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

RectTransform position

Discussion in '2D' started by piggybank1974, Dec 29, 2016.

  1. piggybank1974

    piggybank1974

    Joined:
    Dec 15, 2015
    Posts:
    621
    I'm trying to workout clickable area's in a transform(Panel)

    What I would like is 8 boxes contained within the Panel, but I'm having problems with the coordinates system.

    the panel is 292 pixels in width/height and anchored at the bottom left, with a pivot to the bottom left also see screenshot

    I've workout that the value should be around 41.1625F for a third of 292 pixels in screen system, but when I place the Right line, it's way to large.


    Code (CSharp):
    1.  
    2. public class TestComponent : MonoBehaviour
    3. {
    4. RectTransform mTransform;
    5.  
    6. Rect mArea;
    7. void Awake()
    8. {
    9.   mTransform = (RectTransform)this.transform;
    10. }
    11.  
    12.     // Use this for initialization
    13.     void Start()
    14. {
    15.   Single mTop = mTransform.rect.max.y;
    16.   Single mRight = mTransform.rect.max.x;
    17.   Single mBottom = mTransform.rect.min.y;
    18.   Single mLeft = mTransform.rect.min.x;
    19.   Single mWidth = mTransform.rect.width;
    20.   Single mHeight = mTransform.rect.height;
    21.  
    22.   Vector2 mSize = new Vector2((mWidth / 3) * mTransform.localScale.x  , mHeight / 3);
    23.  
    24.   mArea = new Rect(mLeft, mBottom, 41.1625F, mHeight / 3);
    25.  
    26.   Debug.Log("Original: " + mTransform.rect.ToString());
    27.  
    28.   Debug.Log("Area: " + mArea.ToString());
    29.  
    30.   Debug.Log("mSize: " + mSize.ToString());
    31.     }
    32.  
    33.     // Update is called once per frame
    34.     void Update()
    35. {
    36.  
    37.   // Top
    38.   Debug.DrawLine(new Vector3(mArea.min.x, mArea.max.y, 0), new Vector3(mArea.max.x, mArea.max.y, 0), Color.red, 5F);
    39.  
    40.   // Right
    41.   Debug.DrawLine(new Vector3(mArea.max.x, mArea.max.y, 0), new Vector3(mArea.max.x, mArea.min.y, 0), Color.cyan, 5F);
    42.  
    43.   // Bottom
    44.   Debug.DrawLine(new Vector3(mArea.min.x, mArea.min.y, 0), new Vector3(mArea.max.x, mArea.min.y, 0), Color.blue, 5F);
    45.  
    46.   // Left
    47.   Debug.DrawLine(new Vector3(mArea.min.x, mArea.max.y, 0), new Vector3(mArea.min.x, mArea.min.y, 0), Color.green, 5F);
    48.  
    49.     }
    50. }
    can anybody help with this, I'm assuming it's a conversion issue.
     

    Attached Files:

    Last edited: Dec 29, 2016
  2. piggybank1974

    piggybank1974

    Joined:
    Dec 15, 2015
    Posts:
    621
    I've been fiddling while waiting for an answer.

    I noticed that the Camera scale was 0.4230469F

    My camera is set up as follows:

    UI Scale Mode = Scale With Screen Size.
    Reference Resolution = 2560 x 1440 for "Nexus 6"
    Screen Match Mode = Shrink
    Reference Pixel Per Unit = 100

    Now If I hard code the camera Scale value into the

    Code (CSharp):
    1.  
    2.  Vector2 mSize = new Vector2((mWidth / 3) * 0.4230469F, mHeight / 3);
    3.  
    mSize = 41.2, 97.3 ("I have not change height as of yet").

    so this is really close, is there a better way do do this?
     
  3. PGJ

    PGJ

    Joined:
    Jan 21, 2014
    Posts:
    899
    Wouldn't it be easier to use Layout Groups to divide the panel? These will fill the available area and rescale if necessary.
     
  4. piggybank1974

    piggybank1974

    Joined:
    Dec 15, 2015
    Posts:
    621
    Hi PGJ,

    What I'm trying to do is create an 8 Way touch pad if you like, I did want to use buttons as you have to release the button to effectively go to a different direction.

    I've been figuring it out, i'll post back here shortly when I have something to fully show, it works but there might be a better way?
     
  5. PGJ

    PGJ

    Joined:
    Jan 21, 2014
    Posts:
    899
    There's no need for buttons even if you use Layout groups. I usually place Images in the groups and add EventTriggers to them, for example PointerEnter EventTriggers. Works like a charm for virtual touchpads.
     
  6. piggybank1974

    piggybank1974

    Joined:
    Dec 15, 2015
    Posts:
    621
    Here is the working script.

    Code (CSharp):
    1.  
    2. public class JoyPadComponent : MonoBehaviour
    3. {
    4. List<Rect> mAreas;
    5.  
    6. public RectTransform CanvasTransform;
    7. private bool mClickState;
    8. RectTransform mTransform;
    9.  
    10. private Single mScaleX;
    11. private Single mScaleY;
    12.  
    13. void Awake()
    14. {
    15.   mAreas = new List<Rect>();
    16.   mTransform = (RectTransform)this.transform;
    17. }
    18.  
    19. void Start()
    20. {
    21.   mScaleX = CanvasTransform.localScale.x;
    22.   mScaleY = CanvasTransform.localScale.y;
    23.  
    24.   Single mTop = mTransform.rect.max.y;
    25.   Single mRight = mTransform.rect.max.x;
    26.   Single mBottom = mTransform.rect.min.y;
    27.   Single mLeft = mTransform.rect.min.x;
    28.   Single mWidth = mTransform.rect.width;
    29.   Single mHeight = mTransform.rect.height;
    30.  
    31.   Single mTouchWidth = (mWidth / 3) * mScaleX;
    32.   Single mTouchHeight = (mHeight / 3) * mScaleY;
    33.  
    34.    // North West
    35.    mAreas.Add(new Rect(mLeft, mBottom + (mTouchHeight * 2), mTouchWidth, mTouchHeight));
    36.  
    37.    // North
    38.    mAreas.Add(new Rect(mLeft + mTouchWidth, mBottom + (mTouchHeight * 2), mTouchWidth, mTouchHeight));
    39.  
    40.    // North East
    41.    mAreas.Add(new Rect(mLeft + (mTouchWidth * 2), mBottom + (mTouchHeight * 2), mTouchWidth, mTouchHeight));
    42.  
    43.    // West
    44.    mAreas.Add(new Rect(mLeft, mBottom + mTouchHeight, mTouchWidth, mTouchHeight));
    45.  
    46.    // East
    47.    mAreas.Add(new Rect(mLeft + (mTouchWidth * 2), mBottom + mTouchHeight, mTouchWidth, mTouchHeight));
    48.  
    49.    // South West
    50.    mAreas.Add(new Rect(mLeft, mBottom, mTouchWidth, mTouchHeight));
    51.  
    52.    // South
    53.    mAreas.Add(new Rect(mLeft + mTouchWidth, mBottom, mTouchWidth, mTouchHeight));
    54.  
    55.    // South East
    56.    mAreas.Add(new Rect(mLeft + (mTouchWidth * 2), mBottom, mTouchWidth, mTouchHeight));
    57.     }
    58.    
    59.     // Update is called once per frame
    60.     void Update()
    61. {
    62.   if (mAreas.Count > 0)
    63.    for (int A = 0; A < mAreas.Count; A++)
    64.     DrawRectangle(mAreas[A]);
    65.  
    66.   if (Input.GetMouseButtonDown(0) == true)
    67.    mClickState = true;
    68.  
    69.   if (Input.GetMouseButtonUp(0) == true)
    70.    mClickState = false;
    71.  
    72.   if (mClickState == true)
    73.     {
    74.      if (mAreas.Count > 0)
    75.       for (int A = 0; A < mAreas.Count; A++)
    76.       {
    77.        if (mAreas[A].Contains(Input.mousePosition) == true)
    78.         {
    79.          Debug.Log("Hit Area " + A);
    80.         }
    81.       }
    82.     }
    83.     }
    84.  
    85. private void DrawRectangle(Rect area)
    86. {
    87.   // Top
    88.   Debug.DrawLine(new Vector3(area.min.x, area.max.y, 0), new Vector3(area.max.x, area.max.y, 0), Color.red, 5F);
    89.  
    90.   // Right
    91.   Debug.DrawLine(new Vector3(area.max.x, area.max.y, 0), new Vector3(area.max.x, area.min.y, 0), Color.cyan, 5F);
    92.  
    93.   // Bottom
    94.   Debug.DrawLine(new Vector3(area.min.x, area.min.y, 0), new Vector3(area.max.x, area.min.y, 0), Color.blue, 5F);
    95.  
    96.   // Left
    97.   Debug.DrawLine(new Vector3(area.min.x, area.max.y, 0), new Vector3(area.min.x, area.min.y, 0), Color.green, 5F);
    98. }
    99. }
    100.  
    now I've still got stuff to do with it but the basics are there, is there a better way.


    Notes

    If the user clicks/presses the screen in one of the areas, it will be acknowledged, now if the user still holds their finger down and moves way from the area it will ignore the areas, but if the bring it back it will carry on again, there is NO area in the centre as well.

    I currently use a joystick, but users are still struggling with it, this may solve the problem.
     

    Attached Files:

  7. piggybank1974

    piggybank1974

    Joined:
    Dec 15, 2015
    Posts:
    621
    but will the pointer event trigger, constantly fire the event? if so I'm curious now.
     
  8. PGJ

    PGJ

    Joined:
    Jan 21, 2014
    Posts:
    899
    No, but you can hook into; PointerUp, PointerDown, PointerEnter, and PointerExit. The you'll get an event when the finger enters and exits. So you don't really need constant events. Just have a flag that is set to on upon an enter event and set to off once there is an exit event.

    This is of course providing that you are doing a "digital" touch pad. If you want to have "analog" values that changes depending on how far from the center the finger is, then you also need to check for movement of the finger.
     
  9. piggybank1974

    piggybank1974

    Joined:
    Dec 15, 2015
    Posts:
    621
    I thought so, PointerDown will only fire once, I need a constant I'm press this button baby! event.

    The way the game works is that you should be able to go to different directions without, needing to lift off, similar to a real joystick in fact.

    If I'm not happy with it I'll try something else, just trail and error.
     
  10. PGJ

    PGJ

    Joined:
    Jan 21, 2014
    Posts:
    899
    That is why you want to hook into Down and Enter, that way you'll get events even if the user only moves from one image to another without lifting his finger.

    If you absolutely need constant events you can do something like this:
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Events;
    3. using UnityEngine.EventSystems;
    4.  
    5. public class VirtualGamepad : MonoBehaviour
    6. {
    7.     [System.Serializable]
    8.     public class GamepadEvent : UnityEvent { };
    9.  
    10.     public bool up = false;
    11.     public bool down = false;
    12.     public bool right = false;
    13.     public bool left = false;
    14.  
    15.     // You might want diagonal events as well
    16.     public GamepadEvent upEvent;
    17.     public GamepadEvent downEvent;
    18.     public GamepadEvent leftEvent;
    19.     public GamepadEvent rightEvent;
    20.  
    21.     void Update () {
    22.         if (up)
    23.         {
    24.             upEvent.Invoke();
    25.         }
    26.  
    27.         if (down)
    28.         {
    29.             downEvent.Invoke();
    30.         }
    31.  
    32.         if (left)
    33.         {
    34.             leftEvent.Invoke();
    35.         }
    36.  
    37.         if (right)
    38.         {
    39.             rightEvent.Invoke();
    40.         }
    41.     }
    42.  
    43.     /// <summary>
    44.     /// Assign to Pointer Enter and Pointer Down for up, left/up and right/up Image
    45.     /// </summary>
    46.     /// <param name="e"></param>
    47.     public void UpOn(BaseEventData e)
    48.     {
    49.         up = true;
    50.     }
    51.  
    52.     /// <summary>
    53.     /// Assign to Pointer Exit and Pointer Up for up, left/up and right/up Image
    54.     /// </summary>
    55.     /// <param name="e"></param>
    56.     public void UpOff(BaseEventData e)
    57.     {
    58.         up = false;
    59.     }
    60.  
    61.     /// <summary>
    62.     /// Assign to Pointer Enter and Pointer Down for down, left/down and right/down Image
    63.     /// </summary>
    64.     /// <param name="e"></param>
    65.     public void DownOn(BaseEventData e)
    66.     {
    67.         down = true;
    68.     }
    69.  
    70.     /// <summary>
    71.     /// Assign to Pointer Exit and Pointer Up for down, left/down and right/down Image
    72.     /// </summary>
    73.     /// <param name="e"></param>
    74.     public void DownOff(BaseEventData e)
    75.     {
    76.         down = false;
    77.     }
    78.  
    79.     /// <summary>
    80.     /// Assign to Pointer Enter and Pointer Down for left, left/down and left/up Image
    81.     /// </summary>
    82.     /// <param name="e"></param>
    83.     public void LeftOn(BaseEventData e)
    84.     {
    85.         left = true;
    86.     }
    87.  
    88.     /// <summary>
    89.     /// Assign to Pointer Exit and Pointer Up for left, left/down and left/up Image
    90.     /// </summary>
    91.     /// <param name="e"></param>
    92.     public void LeftOff(BaseEventData e)
    93.     {
    94.         left = false;
    95.     }
    96.  
    97.     /// <summary>
    98.     /// Assign to Pointer Enter and Pointer Down for right, right/down and right/up Image
    99.     /// </summary>
    100.     /// <param name="e"></param>
    101.     public void RightOn(BaseEventData e)
    102.     {
    103.         right = true;
    104.     }
    105.  
    106.     /// <summary>
    107.     /// Assign to Pointer Exit and Pointer Up for right, right/down and right/up Image
    108.     /// </summary>
    109.     /// <param name="e"></param>
    110.     public void RightOff(BaseEventData e)
    111.     {
    112.         right = false;
    113.     }
    114. }
    115.  
     
  11. piggybank1974

    piggybank1974

    Joined:
    Dec 15, 2015
    Posts:
    621
    First time I've seen the "UnityEvent" I usually use a delegate/event using .net, I'm assuming this is unity's version of an event system. how does others classes subscribe to that event.

    I've just seen an article Here on the subject, it feels a little disconnected to me, I've not seen how it cleans these event up either e.g. -= method etc.

    are I see AddListener, makes sense.

    Thanks for the advice I'll take it onbaord.


    What I don't really understand with unity is, they use .net, for the language, and ignore a good chunk of what it give you why?

    Why bother with the language in the first place if you don't use its strengths.
     
    Last edited: Dec 31, 2016
  12. eses

    eses

    Joined:
    Feb 26, 2013
    Posts:
    2,637
    @piggybank1974 - "how does others classes subscribe to that even" - It's actually pretty nice, if you consider that you can just add public, serialized unity event class, then put field for it to your class, and you get UI in inspector where you can add / remove subscribers to your event, similar to what you have in Button or EventTrigger component.

    So if one is using UI to setup subscribers, UnityEvent is pretty neat, otherwise I don't know if you get any benefits.
     
  13. piggybank1974

    piggybank1974

    Joined:
    Dec 15, 2015
    Posts:
    621
    Cheers eses,

    That's probably why I've not seen them or use them in that fassion, I've use AddListener though code and though the inspector, but not seen unity events, I don't think the tutorials section has anything on this unless I missed it and skipped though, as I know .net events I tend to prefer and use what I know, and change it later if need be.

    Unity is a fantasic engine to be hold but it does do bonkers things sometimes, things like this do make me wonder if that's the reason for the 23mb(default) compile size, I'm sure in years to come I'll get used to all the different things hopefully, Like I say the more I know the less I feel I know. ;)
     
  14. piggybank1974

    piggybank1974

    Joined:
    Dec 15, 2015
    Posts:
    621
    @PGJ
    @eses

    My idea does have one caveat so far, the positions I have set down, are not local positions but screen positions I moved the position of the panel off the screen side bottom and left, and the position of the rectangle did not change I had to use the anchoredPosition.
     
  15. piggybank1974

    piggybank1974

    Joined:
    Dec 15, 2015
    Posts:
    621
    Here is the demo project, so please tell me what you think?

    I would love to know your thoughts @PGJ and others.
     

    Attached Files:

  16. eses

    eses

    Joined:
    Feb 26, 2013
    Posts:
    2,637
    @piggybank1974 - It's pretty straightforward to find out local position in rectTransforms, once you know it. Manual is little bit short on this info however:

    Code (csharp):
    1.  
    2.   if (canvas.renderMode == RenderMode.WorldSpace)
    3.   RectTransformUtility.ScreenPointToLocalPointInRectangle(rt, Input.mousePosition, Camera.main, out position);
    4.   else
    5.   RectTransformUtility.ScreenPointToLocalPointInRectangle(rt, Input.mousePosition, null, out position);
    6.  
     
  17. eses

    eses

    Joined:
    Feb 26, 2013
    Posts:
    2,637
    @piggybank1974

    Couldn't resist checking your file... been testing/learning joysticks on/off last year.

    I'd keep it simple and just set bool on IPointerDownHandler and IPointerUpHandler's methods.

    That way you can ten just use RectTransform utility to get location in plane, and that way keep code short, also, added benefit is that this works for all canvas types, and with rotated joystick.
     
  18. PGJ

    PGJ

    Joined:
    Jan 21, 2014
    Posts:
    899
    The TouchPadComponent seems to be a nice solution. I agree with @eses comments. I would be a bit concerned by the constant Contains method calls. It would be interesting to run your version and mine in the Profiler to see which is the most efficient one. I'll try to do that later on.

    UnityEvent are pretty nifty, I like that you can have them show up in the Inspector and that they expose all public methods and variables for the selected objects. I've been building a framework with simple, basic components that the user can string together using UnityEvent (or rather I use QuickEvent, which is an improved version of the UnityEvent). I'll be using this framework with my younger students who arent't all that proficient in programming yet.
     
  19. piggybank1974

    piggybank1974

    Joined:
    Dec 15, 2015
    Posts:
    621
    @eses
    @PGJ

    I was thinking of using IPointerDownHandler and IPointerUpHandler, instead of using the update method way I was not sure if it had extra overhead.

    I would of though contains method should be optimised, as your only really comparing points, if this method is not optimised you could easily role your own.

    Currently I use RectTransformUtility.ScreenPointToLocalPointInRectangle, but I've read that sometimes it produces wrong results, I think this happens as the hero goes in the opposite direction as you are dragging, take your finger off the joystick and start again and then all is well.

    I would be interested on how fast PGJ this method is, I will bet its faster than the one I currently have as I don't need to workout angles/distances etc.

    Please let us know here.
     
  20. piggybank1974

    piggybank1974

    Joined:
    Dec 15, 2015
    Posts:
    621
    @eses
    @PGJ

    Everything was working great until I introduced my fire button to and input.mouseposition now does not work as this on gets the first touch if you like, I have recoded it to use IDragHandler, IPointerUpHandler, IPointerDownHandler, but it means you need to slightly move it to get the character to move it's not a press and go solution

    I tried
    Code (CSharp):
    1.  
    2. if (RectTransformUtility.ScreenPointToLocalPointInRectangle(mTransform, Input.mousePosition, Camera.main, out pos) == true)
    3.  
    on it's own with out the IDragHandler but it reports back I'm in the transform even when I'm not got an clues.