23 OCTOBER 2016 Edited on 23 OCTOBER 2016

Carousel Slider In Unity 3D

In this tutorial im going to give you a class that draws a sliding carousel within the given rect. I think it could be very useful in your games so if you were looking for something like this, read this post to the end.

Hi, this is my second post and i hope it could be useful to you. Using this component is really easy and i'll show you how to do it step by step. First im going to give you the C# code and then i will explain how it works.

using UnityEngine;
using System.Collections;

public class Carousel : MonoBehaviour
{
    private static int _nearestIndex = 0;

    private static float _minMoveTreshold = 20f;
    private static float _newScroll = 0;
    private static AnimationCurve _miniTransitionCurve;
    public static bool Draw(Rect rect,ref float scroll,out int selectedIndex, float growMultiplyer,float slidesDistance,Texture2D[] slides,float size,float transitionTime,bool startTransition)
    {
        CheckMoves();
        selectedIndex = _nearestIndex;
        if (_miniTransitionCurve != null && _miniTransitionCurve.Evaluate(Time.time) < 0.90f)
        {
            if (Input.touchCount < 1)
            {
                scroll = Mathf.Lerp(scroll, _newScroll, _miniTransitionCurve.Evaluate(Time.time));
            }
        }
        if (startTransition)
        {
            _miniTransitionCurve = AnimationCurve.Linear(Time.time, 0, Time.time + transitionTime, 1);
            _miniTransitionCurve.preWrapMode = WrapMode.Clamp;
            _miniTransitionCurve.postWrapMode = WrapMode.Clamp;
        }
        GUI.BeginGroup(rect);
        {
            GUI.BeginGroup(new Rect(-scroll, rect.height / 2 - size * growMultiplyer, int.MaxValue, size * growMultiplyer * 2));
            {
                float minDistance = float.MaxValue;
                for (int i = 0; i < slides.Length; i++)
                {
                    float relativePosition = rect.width / 2 - size / 2 + i * (size + slidesDistance) + size / 2 - scroll;
                    //float grow = 1 + Mathf.Sin(AngleRadian(relativePosition, rect.width)) * growMultiplyer; //Smooth
                    float grow = 1 + Ratio(relativePosition, rect.width) * (growMultiplyer - 1); //Linear
                    GUI.DrawTexture(new Rect(rect.width / 2 - size * grow / 2 + i * (size + slidesDistance), size * growMultiplyer - size * grow / 2, size * grow, size * grow), slides[i]);
                    if (GUI.Button(new Rect(rect.width / 2 - size * grow / 2 + i * (size + slidesDistance), size * growMultiplyer - size * grow / 2, size * grow, size * grow), "", GUIStyle.none))
                    {
                        if (!_moved)
                        {
                            selectedIndex = i;
                            return true;
                        }
                    }

                    float distance = Mathf.Abs(relativePosition - rect.width / 2);
                    if (distance < minDistance)
                    {
                        minDistance = distance;
                        _nearestIndex = i;
                        _newScroll = scroll + (relativePosition - rect.width / 2);
                    }
                }
            }
            GUI.EndGroup();
        }
        GUI.EndGroup();
        return false;
    }

    private static Vector2 _startPosition;

    private static bool _moved = false;
    private static void CheckMoves()
    {
        if (Input.touchCount > 0)
        {
            if (Input.touches[0].phase == TouchPhase.Began)
            {
                _moved = false;
                _startPosition = Input.touches[0].position;
            }

            if (Input.touches[0].phase == TouchPhase.Moved || Input.touches[0].phase == TouchPhase.Ended || Input.touches[0].phase == TouchPhase.Canceled)
            {
                if (Vector2.Distance(Input.touches[0].position, _startPosition) > _minMoveTreshold)
                {
                    _moved = true;
                }
            }
        }
    }

    private static float AngleRadian(float pos, float length)
    {
        float ratio = pos / length;
        return Mathf.PI * ratio;
    }

    private static float Ratio(float pos, float length)
    {
        if (pos <= length / 2)
            return pos / (length / 2);
        else
            return (length - pos) / (length / 2);
    }
}

CheckMoves function checks if we are scrolling or selecting a slide, if we do move our hand and our touch movement was bigger than the threshold so we are scrolling. As you can see the Draw function is static so you dont need to attach this component to an existing game object. this function does all the work for you and all you have to is to place it in an OnGUI() method where you want to draw a carousel. if you didnt catch what i just said here is the code :

Carousel.Draw(rect,ref scroll,out selectedIndex, growMultiplyer, slidesDistance, slides, size, transitionTime, startTransition)

You if you dont place it in OnGUI function, it will cause errors. You should also implement a function to handle the input in your own script and then you pass the scroll value to draw function

My method of doing it

I used the basic math to do it, many may think this is a bad idea. but it's awsome B-) my idea was something like this Carousel Idea I implemented two functions Ratio and AngleRadian to do the scroll and resizes in both linear and curved style. But thats not the end of it. for scrolling the carousel you should implement your own input handling function. For those who need help in this part, here is an example for changing the scroll.

//Carousel Scroll
private float _scroll;
private float _startPosition;
private float _startScroll;
private void HandleInput(int min, int max)
{
    //Touch
    if (Input.touchCount > 0)
    {
        Touch touch = Input.touches[0];
        if (touch.phase == TouchPhase.Began)
        {
            _startPosition = touch.position.x;
            _startScroll = _scroll;
        }
        if (touch.phase == TouchPhase.Moved || touch.phase == TouchPhase.Stationary || touch.phase == TouchPhase.Ended || touch.phase == TouchPhase.Canceled)
        {
            _scroll = _startScroll - (touch.position.x - _startPosition) * 2;
        }
    }

    //Keyboard
    if (Input.GetKey(KeyCode.LeftArrow))
        _scroll -= 20;
    if (Input.GetKey(KeyCode.RightArrow))
        _scroll += 20;
    _scroll = Mathf.Clamp(_scroll, min, max);
}

you just have to put this in Update function. there you go. if you had any problems or questions  just comment in the form below.  Good luck.