- Home /
How do I stop a GUI object from going off screen based on its size?
I have a bit of code that clamps the position of a GUI object from going off screen when dragged. here's a snippet from it:
public void OnDrag(PointerEventData eventData)
{
Vector3 newPos = Vector3.zero;
int deltaX =(int)(eventData.position.x - g_StartPos.x);
deltaX = Mathf.Clamp(deltaX, Mathf.RoundToInt(-Screen.width / 2.9f), Mathf.RoundToInt(Screen.width/2.9f));
newPos.x = deltaX;
int deltaY =(int)(eventData.position.y - g_StartPos.y);
deltaY = Mathf.Clamp(deltaY, Mathf.RoundToInt(-Screen.height /4), Mathf.RoundToInt(Screen.height/1.8f));
newPos.y = deltaY;
myRectTransform.position = new Vector3(g_StartPos.x + newPos.x, g_StartPos.y + newPos.y, g_StartPos.z + newPos.z);
}
This works exactly how I want it to when the GUI object is at its current size, but, In my game I'd like to give the player the option to change the size of the GUI object to his/her liking. So this brings me to my question; How do I stop a GUI object from going off screen based on its size?
I am also attempting to accomplish something along those lines, please post back if you find your solution, and as will I!
Thank you
Answer by unin · Jun 18, 2015 at 02:52 AM
I figured out how to do this but I'm sure there could be a better method than what I'm currently doing. The limitation of the code I see so far is that the guiObject's anchors must be set to the center and I don't think this code will work as intended if your target resolution changes(portrait to landscape). These limitations can be counteracted if you adjust the code to take those factors into account. In my case, it's fine as is. Before using this code you must come up with a target resolution and set up your canvas scaler as such:
Here's my code:
using UnityEngine;
using UnityEngine.EventSystems;
using System.Collections;
public class Example : MonoBehaviour, IDragHandler
{
private RectTransform myRectTransform;
private Vector2 guiSizeHalf;
public Vector2 screenSize;
private Vector2 targetRes;// Set this to whatever your target resolution is.
private Vector2 screenReciprocal;
void Start()
{
myRectTransform = (RectTransform)transform;
DefineScreenValues();
}
//Here we define values about the screen for use in some calculation later on.
void DefineScreenValues()
{
screenSize = new Vector2(Screen.width, Screen.height);
screenReciprocal = new Vector2(1 / screenSize.x, 1 / screenSize.y);
targetRes = new Vector2(720.0f, 1280.0f);
guiSizeHalf = new Vector2(myRectTransform.rect.width * 0.5f, myRectTransform.rect.height * 0.5f);
}
//Here we determine what happens when the mouse/finger starts moving while pressing down on the gameobject.
public void OnDrag(PointerEventData eventData)
{
Vector3 newPos = Vector3.zero;
int deltaX = (int)((eventData.position.x - screenSize.x * 0.5f) * targetRes.x * screenReciprocal.x);
deltaX = (int)(Mathf.Clamp(deltaX, (-targetRes.x * 0.5f + guiSizeHalf.x), (targetRes.x * 0.5f - guiSizeHalf.x)));
newPos.x = deltaX;// deltaX value is assigned to newPos.x
int deltaY = (int)((eventData.position.y - screenSize.y * 0.5f) * targetRes.y * screenReciprocal.y);
deltaY = (int)Mathf.Clamp(deltaY, (-targetRes.y * 0.5f + guiSizeHalf.y), (targetRes.y * 0.5f - guiSizeHalf.y));
newPos.y = deltaY;// deltaY value is assigned to newPos.y
//The position of the dragged object is defined.
myRectTransform.anchoredPosition = new Vector3(newPos.x, newPos.y, newPos.z);
}
}
As stated previously, I'm sure there is a better method so I would love to hear any input on how I can make this code more efficient. Thanks in advance.
It seems like it would be more efficient to use RectTransform.GetWorldCorners(), convert those to screen space and then say that the bottom left can't be less than (0,0) and the top right can't be greater than (res.width, res.height)
Answer by keenanwoodall · Jan 04, 2018 at 06:00 PM
@unin Got my own version working with any pivot after many hours of trying random stuff. I can't post the whole script because it's for work but I here's the gist of it. Just replace the missing references and variables with your own. https://hastebin.com/bagosukiho.go
private void KeepOnScreen ()
{
var refRes = (canvas.transform as RectTransform).sizeDelta;
var goalX = rectTransform.anchoredPosition.x;
var goalY = rectTransform.anchoredPosition.y;
// This works if you change the pivot but don't change the anchors (or the scale).
if (IsTooHigh (refRes))
goalY = (refRes.y / 2f) - (rectTransform.sizeDelta.y * (1f - rectTransform.pivot.y));
if (IsTooLow (refRes))
goalY = -(refRes.y / 2f) + (rectTransform.sizeDelta.y * rectTransform.pivot.y);
if (IsTooFarRight (refRes))
goalX = (refRes.x / 2f) - (rectTransform.sizeDelta.x * (1f - rectTransform.pivot.x));
if (IsTooFarLeft (refRes))
goalX = -(refRes.x / 2f) + (rectTransform.sizeDelta.x * rectTransform.pivot.x);
rectTransform.anchoredPosition = new Vector2 (goalX, goalY);
}
private bool IsTooHigh (Vector2 refRes)
{
return (rectTransform.anchoredPosition.y + (rectTransform.sizeDelta.y * (1f - rectTransform.pivot.y)) > refRes.y / 2f);
}
private bool IsTooLow (Vector2 refRes)
{
return (rectTransform.anchoredPosition.y - (rectTransform.sizeDelta.y * rectTransform.pivot.y) < -refRes.y / 2f);
}
private bool IsTooFarRight (Vector2 refRes)
{
return (rectTransform.anchoredPosition.x + (rectTransform.sizeDelta.x * (1f - rectTransform.pivot.x)) > refRes.x / 2f);
}
private bool IsTooFarLeft (Vector2 refRes)
{
return (rectTransform.anchoredPosition.x - (rectTransform.sizeDelta.x * rectTransform.pivot.x) < -refRes.x / 2f);
}
Answer by nevaran · Jul 05, 2018 at 05:23 AM
I might be late but this is my version of the script(works with Scale with Screen Size scale mode). Its probably not the fastest or most elegant but its easy-ish to understand:
Vector2 pos = Input.mousePosition;
Vector2 percScr;//position of the mouse's screen in percentage
percScr.x = pos.x / Screen.width;
percScr.y = pos.y / Screen.height;
var refRes = (canv.transform as RectTransform).sizeDelta;
pos = refRes * percScr;//translate from screen to canvas pixel position
//avoid going out of screen
if(pos.y > refRes.y - rectBody.sizeDelta.y) pos.y = refRes.y - rectBody.sizeDelta.y;//up
if(pos.y < 0) pos.y = 0;//down
if(pos.x < rectBody.sizeDelta.x) pos.x = rectBody.sizeDelta.x;//left
if(pos.x > refRes.x) pos.x = refRes.x;//right
rectBody.anchoredPosition = pos;
Answer by HallowBlade · Jan 29, 2021 at 03:35 PM
Vector2 mousePosition = new Vector2(Input.mousePosition.x, Input.mousePosition.y);
mousePosition.x = Mathf.Clamp(mousePosition.x, 0 + rectTransform.rect.width / 2, Screen.width - rectTransform.rect.width / 2);
mousePosition.y = Mathf.Clamp(mousePosition.y, 0 + rectTransform.rect.height / 2, Screen.height - rectTransform.rect.height / 2);
transform.position = mousePosition;
Answer by Rezrael · Nov 25, 2020 at 01:12 AM
Anyone know how to get this working for a Context menu that is a VisibleElement variable?
Your answer
Follow this Question
Related Questions
Multiple Cars not working 1 Answer
Distribute terrain in zones 3 Answers
Reproducing the "spin" effect of Flick Kick Football 1 Answer
Turning touch to rotation 1 Answer
Rts touch select unit 2 Answers