How to position ScrollRect to another item?

I’m trying the 4.6 beta and I have a masked ScrollRect with buttons inside. Since I prefer using Gamepad I noticed that for some reason the ScrollRect fails to jump to the currently selected object. The same happens when using keyboard. So I guess we need to do it manually and I was hoping there would be a ScrollRect.CenterOn(GameObject item) function, but there isn’t. I know about ScrollRect.verticalNormalizedPosition and ScrollRect.horizontalNormalizedPosition but I’m unsure how I can take my objects position to get a usable value out of to assign it. Ideally, the selected object would be in the perfect center of the ScrollRect. A long time ago I used NGUI and it supported that, but I can’t figure it out with uGUI. Does anyone know how to do it?

Many thanks in advance!

Try setting the normalize position to where it would reveal the transform

    public ScrollRect scroll;

    void ShowInTabControl(){
        float normalizePosition = (float)transform.GetSiblingIndex() / (float)scroll.content.transform.childCount;
        scroll.verticalNormalizedPosition = 1-normalizePosition;
    }
6 Likes

That wont center it exactly… you’ll need to know how many items are possible to be visible at once and do a little shifting/math on your normalizePosition to do that.

Thanks dave, I got it working. Your code didn’t work by itself until I did some trial and error with it. Here is what I ended up using. I’m sure it’s quiet terrible, but it seems to work for me (it doesn’t really center anything but it moves the position good enough)

I use this script on the ScrollRect

  private ScrollRect scroll;
  private RectTransform scrollTransform;

  void Start()
  {
  scroll = GetComponent<ScrollRect>();
  scrollTransform = GetComponent<RectTransform>();
  }

  public void CenterToItem(RectTransform obj)
  {
  float normalizePosition = scrollTransform.anchorMin.y - obj.anchoredPosition.y;
  normalizePosition += (float)obj.transform.GetSiblingIndex() / (float)scroll.content.transform.childCount;
  normalizePosition /= 1000f;
  normalizePosition = Mathf.Clamp01(1 - normalizePosition);
  scroll.verticalNormalizedPosition = normalizePosition;
  Debug.Log(normalizePosition);
  }

I call the function from another script on my button that has the event interface implemented. Because my button is a child of another object I pass the parents RectTransform to it since for some reason only that appears to store the proper position that I needed. The call on my button is made like so:

  public void OnSelect(BaseEventData eventData)
  {
  if (scrollCenterScript != null)
  scrollCenterScript.CenterToItem(rTransform.parent.GetComponent<RectTransform>());
  }

Thank you for the help. Of course improvements are always welcome if anyone has them :slight_smile:

2 Likes

Hi,

Thanks for this script, its given me a good start, but I’m having a little problem in centering my item, it ends up being further and further out as I step through my items. I’m using this technique as a level select along a map. (Think Super Mario). I want to centre the scrollrect on one of my levels, but they aren’t equally spaced out, so I’m not sure this:

normalizePosition +=(float)obj.transform.GetSiblingIndex()/(float)scroll.content.transform.childCount;

Will do what I need. Is there a way to modify this script so that I can centre on any item, regardless of the even spacing?

Thanks

[Edit]

Sorry, centering is broken for big lists, fixing it right now. Back soon!

Ok, now it is fixed. There seems to be a bit of an offset error. Can seem to find it now but (for me) it not an issue right now.


Hi,
I am working on this very same thing. I have made a script that moves the scroll rect content so that an item is shown centered. It uses the target item, content and mask RectTransforms so it ignores child count and child hierarchy, meaning you can center on a child of a child of a child of content and the position “should” be correct.

I am not very good at math so this script posibly look like trash to someone that knows, but it is my trash and my love for it is unconditional… until Unity implement this by themselves :smile:.

Important thing, in my script, I am assuming that content childs have their pivot point placed up-left. Results my vary if different. Also I am using GoKit for animations, if you are not I have left commented a direct assignment of the new position so you can uncomment it and comment the GoTo animation.

//_sr.normalizedPosition = normalizedPosition;
  Go.to(_sr, AnimTime, new GoTweenConfig().vector2Prop("normalizedPosition", normalizedPosition));

http://pastebin.com/FecBh4mh

  1. Add the script to a scroll rect
  2. Assign the mask RectTransform used to calculate the visible area. It can be a different object. It will use it to know which position is the center.
  3. Configure properties as needed :slight_smile:
  4. Call CenterOnItem(RectTransform target) to center the scroll rect in a certain item

If you find a bug or have a better implementation please let me know.

1 Like

I have fixed a small bug that caused bad centering when the target item was deep in the content hierarchy.
Updated script:

Hi John,

Thank you for sharing your code. I’ll have a go with you (newly) modified version and let you know how it goes :slight_smile:

Hi John,

Just tried this out, and unfortunately it doesn’t work out for me. I always end up near the end of the scroll rect. With the item I’m choosing, I should be close to the centre. Any idea what I might be doing wrong?

Well thats bad, sorry. I have tested the script in both directions and with different content structures and thought it worked quite well. I’m sure at somepoint I messed up with the anchors and alignments and whatnot :stuck_out_tongue:

Ok, let’s get to it. I have little info about your problem so excuse me for the interrogation:

  • Do you have an empty project with the same problem? (so I can directly test the error)
  • Are you using a mask?
  • Is it a mask?
  • Is it a child (at any depth) of the scrollrect object and a parent (at any depth) of the content object
  • At wich depth is the item you want to center?
  • Is it a direct child of content?
  • Is it deep into the content hierarchy?
  • What is the content anchor position?
  • What is the item anchor position?

Thanks!

Hi,

Started writing some detail to this before, but the forum stopped working for me…

Anyway:

  • No, but I can possibly create one
  • I have a RectTransform that is set to the size of the canvas (which is in world space and is the size of the screen) that I’ve allocated to the Mask variable
  • Doesn’t seem to make any difference if it has the mask component on it or not
  • I’ve attached a screenshot of my hierarchy, I’m after centring on any of the Level_{0}_{1} game objects, so not very deep.
  • The Level game objects have their anchors top left (as you required)
  • Their positions will vary depending where they are on the map. I’ve attached another screenshot of what the map looks like. There is a 3D environment that will be visible as well (which is in the Environment gameobject in the hierarchy)

1853115--118815--Capture.PNG

Well, I did not test it in world space, the script was thought for 2D scrolling. I will test it with a simple example. I am using the mask to calculate an offset from the screen center, but with your setup the mask tweak does not work since the mask and the content are not on the same plane.

I really like your idea for a map :). By the way, the pivot alignment should only cause a different offset and only in a very small amount so you can test different alignments if you want to.

2 questions:

  • What is the dragable object?
  • Why is the mask inside the content? ← this is curiosity.

All the examples I have tested where like this:
1853451--118841--ScrollRectStructure.png

The Dragable object is there so there is a transparent image all the way along for the raycasting to hit, so it can fully scroll. I found otherwise, I had to click on one of the level numbers instead.

Mask is there because I didn’t know where else to put it. I’ll set up my scene hierarchy like your as see if that fixes things for me.

I did not understand you when we talked about your map. The script I posted is intended for 2D maps, mostly because I did not think of a 3D map when doing it.

This script does not work with this kind of scrollrect. I am doing a few tests to see if I can get something working in 3D.

ahh, I see. I was assuming the same thing would work, as the items are still in a scroll rect, but the position it chooses for each of them is way off. E.g. looking at my picture above, I’m trying to centre on Level 4, but it ends up beyond the end of the content.

Well, I use a lot of screen position jumbo mambo to calculate the scroll rect displacement so it does not work very well with 3D scroll rects.

But since I was interested I made a little project to toy a bit with this and ended up with something funny:

  • Take an item position in the screen
  • Calculate the direction in the screen for that item to reach the center of the screen
  • Adjust the scroll rect horizontal and vertical position in that direction
  • Repeat until target item is whithin a threshold from the center of the screen
  • It work
  • Laugh like a maniac! MWAHAHAHAhahahahahahaha

It is not a very professional work and the code is a bit ugly but since it works and I think it does something similar to what you want, there is no harm it checking it :slight_smile:
https://drive.google.com/file/d/0B2e1MCu4wYC2cUJvcXVuMmNWQ28/view?usp=sharing

See how it goes. Notes:

  • It centers onto the target pivot point
  • No need for mask
  • Scrollbars are there for me to manually moveit and test it, can be deleted
  • Drag does not work, I did not need it, I had the scroll bars :stuck_out_tongue:

Good luck

1 Like

Fantastic! works perfectly, even with drag!

Thank you so much for the help!

Just a lucky shoot, glad it worked for you. There is a lot to improve in that script so have fun :stuck_out_tongue:

yeah, it needs a little tweaking, but fundamentally its sound. I get a little jerky bit as it approaches the item which I can fix. Thanks again