Emmanuel Salva Cruz

Independent Game Developer

ScrollRect and Unity UI

by

in

The new Unity UI is great. There are just some things that I wished were on by default. With a list of buttons, Unity’s ScrollRect does not automatically scroll to the selected button if the button is out of bounds.

I never found a quick way or a component to emulate it, so I turned to scripting. I found one on the Unity forums for centering the selected object (Unity Forum Link), but that doesn’t seem to be the one I wanted.

So after pounding my head on the keyboard for three days, I finally figured it out.

It scrolls on the edges when the selected button is out of bounds on the mask.

For setting up the scroll list, I suggest watching this Live Session: Unity Live Training Link

And here’s the code. Just add it to the object with the ScrollRect:

[code lang=”csharp”]

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;

[RequireComponent( typeof( ScrollRect ) )]
public class ScrollToSelected : MonoBehaviour {

public float scrollSpeed = 10f;

ScrollRect m_ScrollRect;
RectTransform m_RectTransform;
RectTransform m_ContentRectTransform;
RectTransform m_SelectedRectTransform;

void Awake() {
m_ScrollRect = GetComponent<ScrollRect>();
m_RectTransform = GetComponent<RectTransform>();
m_ContentRectTransform = m_ScrollRect.content;
}

void Update() {
UpdateScrollToSelected();
}

void UpdateScrollToSelected() {

// grab the current selected from the eventsystem
GameObject selected = EventSystem.current.currentSelectedGameObject;

if ( selected == null ) {
return;
}
if ( selected.transform.parent != m_ContentRectTransform.transform ) {
return;
}

m_SelectedRectTransform = selected.GetComponent<RectTransform>();

// math stuff
Vector3 selectedDifference = m_RectTransform.localPosition – m_SelectedRectTransform.localPosition;
float contentHeightDifference = ( m_ContentRectTransform.rect.height – m_RectTransform.rect.height );

float selectedPosition = ( m_ContentRectTransform.rect.height – selectedDifference.y );
float currentScrollRectPosition = m_ScrollRect.normalizedPosition.y * contentHeightDifference;
float above = currentScrollRectPosition – ( m_SelectedRectTransform.rect.height / 2 ) + m_RectTransform.rect.height;
float below = currentScrollRectPosition + ( m_SelectedRectTransform.rect.height / 2 );

// check if selected is out of bounds
if ( selectedPosition > above ) {
float step = selectedPosition – above;
float newY = currentScrollRectPosition + step;
float newNormalizedY = newY / contentHeightDifference;
m_ScrollRect.normalizedPosition = Vector2.Lerp( m_ScrollRect.normalizedPosition, new Vector2( 0, newNormalizedY ), scrollSpeed * Time.deltaTime );
} else if ( selectedPosition < below ) {
float step = selectedPosition – below;
float newY = currentScrollRectPosition + step;
float newNormalizedY = newY / contentHeightDifference;
m_ScrollRect.normalizedPosition = Vector2.Lerp( m_ScrollRect.normalizedPosition, new Vector2( 0, newNormalizedY ), scrollSpeed * Time.deltaTime );
}

}

}

[/code]

Note that it only automatically scrolls vertically. You might want to extend it if you want it to work horizontally.

And that’s it. Let me know if you found a better way of doing it, or if there’s a simple way of doing it, like a hidden component I missed.

Edit: You can download the unityPackage here: Download