In Unity, how to detect if window is being resized and if window has stopped resizing - unity3d

I wanted my UI to not resize when user is still resizing the game (holding click in the window border) and only when the user has released the mouse the resize event will trigger.
I have tried to achieve it on Unity but so far I only able to detect windows size change, which my script checked every 0.5 second and if detected change it will resize the UI. But of course resizing everything caused a heavy lag, so resizing every 0.5 second is not a good option but resizing every 1 second is not a good idea either because 1 second is considered too long.
The question might be too broad but I have specified the problem as small as possible, how do I detect if user is still resizing the window? And how do I detect if user has stopped resizing the window (stop holding click at window border)?

You can't tell when someone stops dragging a window, unless you want to code a low level solution for ever desktop environment and every operating system.
Here's what worked for me with any MonoBehavior class, using the OnRectTransformDimensionsChange event:
public class StackOverflow : MonoBehaviour
{
private const float TimeBetweenScreenChangeCalculations = 0.5f;
private float _lastScreenChangeCalculationTime = 0;
private void Awake()
{
_lastScreenChangeCalculationTime = Time.time;
}
private void OnRectTransformDimensionsChange()
{
if (Time.time - _lastScreenChangeCalculationTime < TimeBetweenScreenChangeCalculations)
return;
_lastScreenChangeCalculationTime = Time.time;
Debug.Log($"Window dimensions changed to {Screen.width}x{Screen.height}");
}
}

I have some good news - sort of.
When the user resizes the window on Mac or PC,
Unity will AUTOMATICALLY re-layout everything.
BUT in fact ONLY when the user is "finished" resizing the Mac/PC window.
I believe that is what the OP is asking for - so the good news, what the OP is asking for is quite automatic.
However. A huge problem in Unity is Unity does not smoothly resize elements as the user is dragging the mouse to expand the Mac/PC window.
I have never found a solution to that problem. (A poor solution often mentioned is to check the size of the window every frame; that seems to be about the only approach.)
Again interestingly, what the OP mentions
" ..and if window has stopped resizing .."
is automatically done in Unity; in fact do nothing to achieve that.

I needed something like this for re generating a line chart, but as it has too many elements, it would be heavy to do it on every update, so I came up with this, which for me worked well:
public class ToRunOnResize : MonoBehaviour
{
private float screenWidth;
private bool screenStartedResizing = false;
private int updateCounter = 0;
private int numberOfUpdatesToRunXFunction = 15; // The number of frames you want your function to run after, (usually 60 per second, so 15 would be .25 seconds)
void Start()
{
screenWidth = Screen.width; // Identifies the screen width
}
private void Update()
{
if (Screen.width != screenWidth) // This will be run and repeated while you resize your screen
{
updateCounter = 0; // This will set 0 on every update, so the counter is reset until you release the resizing.
screenStartedResizing = true; // This lets the application know when to start counting the # of updates after you stopped resizing.
screenWidth = Screen.width;
}
if (screenStartedResizing)
{
updateCounter += 1; // This will count the updates until it gets to the numberOfUpdatesToRunXFunction
}
if (updateCounter == numberOfUpdatesToRunXFunction && screenStartedResizing)
{ // Finally we make the counter stop and run the code, in my case I use it for re-rendering a line chart.
screenStartedResizing = false;
// my re-rendering code...
// my re-rendering code...
}
}
}

Related

Unity Dynamic UI number selection with plus and minus buttons

while programming a game in Unity, I had troubles with incrementing this new feature: a dynamic UI number selection with plus and minus buttons.
What I want precisely:
1. three buttons, one blank in the middle displays the number, two with plus and minus signs which, when clicked, increment or decrement number by 1. WORKS OK!
image of what I did
2. (this is where it gets tricky) When user presses for more than, say, .2s, then it increments the value of the central button pretty fast as long as the user is still pressing on the button. Would avoid player from pressing hundred times the button because it increments only by 1.
3. Eventually add an acceleration phase of the increase (at the start increases by 3/s for example and at the max by 20/s or something like that)
Some help on this would be really great, thanks for those who will take time answering :)
edit: found somebody who asked same question on another post -->https://stackoverflow.com/questions/22816917/faster-increments-with-longer-uibutton-hold-duration?rq=1 but I don't understand a single inch of code...(doesn't look like c#) ;( help!
Are you using the button's OnClick() Event? It only calls the function once even if the user is holding down the key.
If you are not sure how to configure it. Here is a tutorial, you can use.
https://vionixstudio.com/2022/05/21/making-a-simple-button-in-unity/
Also the middle button can be a text field.
edit: found the solution myself. The principal issue I encountered when trying to make the button was a way to know if the button was being pressed (a bool variable). I already knew when it was clicked and when it was released with the OnPointerUp and OnPointerDown methods in the event trigger component. I then found the Input.GetButton() funct, which returns true if the button passed in parameter (here the mouse's left click) is pressed.
Here's the code (I didn't make an acceleration phase 'cause I was bored but it can be done pretty easily once you've got the Input.GetButton statement):
public class PlusAndMinus : MonoBehaviour
{
[SerializeField] private GameManager gameManagerScript;
[SerializeField] private int amount;
private bool isPressedForLong=false;
[SerializeField] private float IncreasePerSecWhenPressingLonger;
public void PointerDown()
{
if (!gameManagerScript.isGameActive)
{
StartCoroutine(PointerDownCoroutine());
}
}
private IEnumerator PointerDownCoroutine()
{
yield return new WaitForSeconds(.1f);#.2f might work better
if (Input.GetMouseButton(0))
{
isPressedForLong = true;
}
}
public void PointerUp()
{
if (!gameManagerScript.isGameActive)
{
isPressedForLong = false;
gameManagerScript.UpdateNumberOfBalls("Expected", amount);
}
}
private void Update()
{
if (isPressedForLong)
{
gameManagerScript.UpdateNumberOfBalls("Expected", amount * IncreasePerSecWhenPressingLonger * Time.deltaTime);
}
}
}
The PointerDown event in the event trigger component calls the PointerDown function in the script (same for PointerUp).
The value of "amount" var is set to 1 for the plus button and -1 for the minus button.
The two if statements checking if game is not active are for my game's use but aren't necessary.
Finally, the gameManagerScript.UpdateNumberOfBalls("expected",amount); statements call a function that updates the text in the middle by the amount specified. Here's my code ("expected" argument is for my game's use):
#inside the function
else if (startOrEndOrExpected == "Expected")
{
if (!((numberOfBallsExpected +amount) < 0) & !((numberOfBallsExpected +amount) > numberOfBallsThisLevel))
{
if (Math.Round(numberOfBallsExpected + amount) != Math.Round(numberOfBallsExpected))
{
AudioManager.Play("PlusAndMinus");
}
numberOfBallsExpected += amount;
numberOfBallsExpectedText.text = Math.Round(numberOfBallsExpected).ToString();
}
}
0 and numberOfBallsThisLevel are the boundaries of the number displayed.
Second if statement avoids the button click sound to be played every frame when the user presses for long on the button (only plays when number increments or decrements by 1 or more).
Hope this helps!

Unity jagged mouse input detection

I wrote a script that writes Input.mousePosition into a file on every frame. The idea is that I want to identify which button on screen the player is trying to click before actually clicking, from the speed of the mouse basically. However, I ran into data like this:
(1113.0, 835.0, 0.0)
(1113.0, 835.0, 0.0)
(1113.0, 835.0, 0.0)
(1126.0, 835.0, 0.0)
Basically on one frame the x position is one value, a couple of frames later it's changed, but in the middle there is no gradation. While my mouse movement was continuous, if I'm to believe Unity, in the example above I hovered on 1 pixel for 3 frames then jumped 13 pixels to the right in one frame. Why is this? Is there any code to get the actual frame by frame position of the mouse?
EDIT:
Vector2 _lastPosition;
StreamWriter _mouseData;
// Start is called before the first frame update
void Start()
{
_mouseData = new StreamWriter(File.Open("sdata.txt", FileMode.Create));
}
// Update is called once per frame
void FixedUpdate()
{
_mouseData.WriteLine(Input.mousePosition.ToString());
if (Input.GetMouseButtonDown(0))
{
_mouseData.WriteLine("CLICK\n\n");
}
_lastPosition = Input.mousePosition;
}
void OnDestroy()
{
_mouseData.Close();
}
EDIT 2:
I changed the code to the following:
void FixedUpdate()
{
_mouseData.WriteLine(Vector2.SqrMagnitude(new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y"))));
if (Input.GetMouseButtonDown(0))
{
_mouseData.WriteLine("CLICK\n\n");
}
}
Now I'm still getting output that's 50% 0-es and non-0 values are sprinkled in on every second row. Exceptions: a few rows where actual values are supposed to be still contain random 0-es. Now, I'm not super concerned about getting less frequent than 1/frame data, but there's no way to distinguish between these false 0-es and actual 0-es when the mouse is not moving, which is an issue.
I cannot find out from your question but I am guessing that you use the FixedUpdate() method which is unreliable in this situation. Update() is advised to use for calls that you want to execute once per frame.
EDIT:
Also, note that it is recommended that you set your application's framerate to a realistic number since it is unlimited by default (on desktop) and your app could be running with so many FPS that it is faster than how often you can sample your mouse input.
You can set the framerate using: Application.targetFrameRate = 60;
Aside from this problem it is generally a good idea to set your framerate to save yourself some headaches. (This is specifically true if you develop for mobile platforms and test on your desktop.)

Active a Unity animation if certain conditions are met?

I'm developing a 2d game in Unity and I ran into a problem. When trying to create animation transitions for one of my animations for some reason Unity instantly or very quickly deactivates my animation(like less then a second after I activated it). However when I don't have a transition conected to the animation the animation works fine. However I need to be able to transition the gameobject back to the state it was in before the animation after a certain condtion is met and so I cant leave the animation running. Does anyone know how I can solve this problem?
Ive tried switching the way I activate the animations to see if it might be related to this by trying both a bool and trigger as the condition that determines when a transition should happen but the same problem arrises
THis is a rough outline of how I activate the animation
public class InteractControl : MonoBehaviour, IPooledObject
{
public static float timeLeft = 10f;
public void OnObjectSpawn()
{
anim = GetComponent<Animator>();
}
// Change this method to just be a temporary obstacle
void Update()
{
timeLeft -= Time.deltaTime;
if (timeLeft > 0)
{
anim.enabled = true;
}
if (timeLeft < 0)
{
anim.SetTrigger("FollowAd");
}
}
}
Ad Follow with transition:
Ad Follow without transition:
Transition Settings:
The animation should activate if timeLeft is bigger than 0 and deactivate as soon as it is smaller then 0 and return the the gameobject to the state it was in before the animation started. Right now if Ad Follow_ has no transition the animation works when called upon but doesn't deactivate. if Ad Follow_ has an empty state connected to it to which it should transition should
anim.SetTrigger("FollowAd");
be called like shown above it works for less then a second or not at all and then switches straight back to the default state. It should only switch back when timeLeft < 0.
You can create a public RuntimeAnimatorController animCon for your required animation. When your conditions are met. You can do anim.runtimeanimatorcontroller = animCon;.
And When the condition is false do this anim.runtimeanimatorcontroller = null;. Its not the best way but its quick and better then most.

Only scroll in one direction

I have a infinite vertical scrolling bar in unity and I want to suddenly limit the scrolling (in one direction only) when reaching a (variable) threshold.
public GameObject MyScrollRectContent;
public float limit = 300;
void Update () {
if(MyScrollRectContent.transform.localPosition.y >= limit){
//make it ONLY possible to scroll backwards or not beyond the limit and stop elasticity
}
Any ideas how to limit the infinite scrolling?
The simplest thing would be to use the existing ScrollRect. It allows scroll in a defined rectangle. You would have to simulate infinity by setting a really big size in one direction, or find a way to seamlessly reset the position if the user goes too far (difficult to do without seeing the jump but might be possible depending on your content).
If this is not an acceptable solution:
To limit the scrolling, you can just set the position to the limit if the user goes too high:
void Update ()
{
if(MyScrollRectContent.transform.localPosition.y >= limit)
{
var currentPos = MyScrollRectContent.transform.localPosition;
MyScrollRectContent.transform.localPosition = new Vector3(currentPos.x, limit, currentPos.z);
}
}
Now if on top of that you want elasticity, it is a bit more tricky: Somewhere, your script must currently set MyScrollRectContent.transform.localPosition. Instead of doing that, set a targetYPosition. You can imagine this as a point that moves, on which the scrollrect is linked by an elastic. This targetYPosition is contrained by your limit. Then in your update you make the ScrollRectContent follow.
public void OnUserInput(float y) // I don't know how you actually do this. this is an example
{
_targetYposition = y;
if(_targetYposition > limit)
{
_targetYPosition = limit;
}
}
private void Update()
{
var currentPos = MyScrollRectContent.transform.localPosition;
var y =
Vector3.Lerp(currentPos.y,
_targetYPosition, Time.deltaTime*_speed);
MyScrollRectContent.transform.localPosition = new Vector3(currentPos.x, y, currentPos.z);
}
Note that this is a simple example aiming at hinting you in the right direction. You'll have to adapt it to your code. The elasticity will likely not look as you want either, so you might want to modify the Update function to create other effects

visual lag of the moving picture in Unity3D. How to make it smoother?

I make a game in Unity3D (+2D ToolKit) for iOS.
It is 2d runner/platformer with side view. So on screen is moving background and obstacles. And we control up/down the main character to overcome obstacles.
The problem is that the moving picture has a visual lag. In other words, it moves jerkily or creates the tail area of the previous frames on iOS-devices. In XCode the number of frames is 30 and over.
I have objects 'plain' with the following script:
public class Move: Monobehavior
{
private float x;
public float Speed = 128.0f;
void Start()
{
x = transform.position.x;
}
void Update()
{
x += Time.DeltaTime *Speed;
Vector3 N = transform.position;
N.x = mathf.FloorInt(x);
transform.position = N;
}
}
The question is how to make the move of background smoother, without jerks and flicker on screen while playing? Maybe the problem is in framerate parameter.
Can anybody help me to find a solution?
I'd say it's the use of the FloorInt function which will move the background only in steps of 1 which is rather not smooth. It should get better when you comment that line out. Do you have any special reason why you are doing the FloorInt there?
The use of floor will definitely hurt your performance. Not only is it one more thing to calculate, but it is actually removing fidelity by removing decimals. This will definalty make the movement look 'jerky'. Also, update is not always called on the same time inteval, depending on what else is happening during that frame, so using Time.delaTime is highly recommended. Another thing, you do not need to set variables for x and Vector3 N, when you can update the transoms position like the code below. And if you have to, you sill only need to create one variable to update, and set your position to it. The code below just updates the players x position at a given rate, based on the amount of time that has passes since the last update. There should be no 'jerky' movement. (Unless you have a serious framerate drop);
public class Move: Monobehavior
{
public float Speed = 128.0f;
void Update()
{
transform.position =
(transform.position.x + (speed*Time.DeltaTime),
transform.position.y,
transform.position.z);
}
}