devNotes 8-12-15 Controller Radial Menu – ScreenShots

dfgngnfg6

gfngfndgf

efeqefqe4

 

Screenshot_102.5556_

Screenshot

using UnityEngine;
using System.Collections;
using UnityEngine.Events;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine.EventSystems;

public delegate void HapticPulseEventHandler(ushort strength);

[ExecuteInEditMode] //Lets us set up buttons from inspector option
public class RadialMenu : MonoBehaviour
{
    #region Variables
    public List<RadialMenuButton> buttons;
    public GameObject buttonPrefab;
    [Range(0f, 1f)]
    public float buttonThickness = 0.5f;
    public Color buttonColor = Color.white;
    public float offsetDistance = 1;
    [Range(0, 359)]
    public float offsetRotation;
    public bool rotateIcons;
    public float iconMargin;
    public bool isShown;
    public bool hideOnRelease;
    public bool executeOnUnclick;
    [Range(0, 1599)]
    public ushort baseHapticStrength;

    public event HapticPulseEventHandler FireHapticPulse;

    //Has to be public to keep state from editor -> play mode?
    public List<GameObject> menuButtons;

    private int currentHover = -1;
    private int currentPress = -1;
    #endregion

    #region Unity Methods

    private void Awake()
    {
        if (Application.isPlaying)
        {
            if (!isShown)
            {
                transform.localScale = Vector3.zero;
            }
            RegenerateButtons();
        }
    }

    private void Update()
    {
        //Keep track of pressed button and constantly invoke Hold event
        if (currentPress != -1)
        {
            buttons[currentPress].OnHold.Invoke();
        }
    }

    #endregion

    #region Interaction

    //Turns and Angle and Event type into a button action
    private void InteractButton(float angle, ButtonEvent evt) //Can't pass ExecuteEvents as parameter? Unity gives error
    {
        //Get button ID from angle
        float buttonAngle = 360f / buttons.Count; //Each button is an arc with this angle
        angle = mod((angle + offsetRotation), 360); //Offset the touch coordinate with our offset

        int buttonID = (int)mod(((angle + (buttonAngle / 2f)) / buttonAngle), buttons.Count); //Convert angle into ButtonID (This is the magic)
        var pointer = new PointerEventData(EventSystem.current); //Create a new EventSystem (UI) Event

        //If we changed buttons while moving, un-hover and un-click the last button we were on
        if (currentHover != buttonID && currentHover != -1)
        {
            ExecuteEvents.Execute(menuButtons[currentHover], pointer, ExecuteEvents.pointerUpHandler);
            ExecuteEvents.Execute(menuButtons[currentHover], pointer, ExecuteEvents.pointerExitHandler);
            buttons[currentHover].OnHoverExit.Invoke();
            if (executeOnUnclick && currentPress != -1)
            {
                ExecuteEvents.Execute(menuButtons[buttonID], pointer, ExecuteEvents.pointerDownHandler);
                AttempHapticPulse ((ushort)(baseHapticStrength * 1.666f));
            }
        }
        if (evt == ButtonEvent.click) //Click button if click, and keep track of current press (executes button action)
        {
            ExecuteEvents.Execute(menuButtons[buttonID], pointer, ExecuteEvents.pointerDownHandler);
            currentPress = buttonID;
            if (!executeOnUnclick)
            {
                buttons[buttonID].OnClick.Invoke ();
                AttempHapticPulse ((ushort)(baseHapticStrength * 2.5f));
            }
        }
        else if (evt == ButtonEvent.unclick) //Clear press id to stop invoking OnHold method (hide menu)
        {
            ExecuteEvents.Execute(menuButtons[buttonID], pointer, ExecuteEvents.pointerUpHandler);
            currentPress = -1;

            if (executeOnUnclick)
            {
                AttempHapticPulse ((ushort)(baseHapticStrength * 2.5f));
                buttons[buttonID].OnClick.Invoke ();
            }
        }
        else if (evt == ButtonEvent.hoverOn && currentHover != buttonID) // Show hover UI event (darken button etc). Show menu
        {
            ExecuteEvents.Execute(menuButtons[buttonID], pointer, ExecuteEvents.pointerEnterHandler);
            buttons[buttonID].OnHoverEnter.Invoke();
            AttempHapticPulse (baseHapticStrength);
        }
        currentHover = buttonID; //Set current hover ID, need this to un-hover if selected button changes
    }

    /*
    * Public methods to call Interact
    */

    public void HoverButton(float angle)
    {
        InteractButton(angle, ButtonEvent.hoverOn);
    }

    public void ClickButton(float angle)
    {
        InteractButton(angle, ButtonEvent.click);
    }

    public void UnClickButton(float angle)
    {
        InteractButton(angle, ButtonEvent.unclick);
    }

    public void ToggleMenu()
    {
        if (isShown)
        {
            HideMenu(true);
        }
        else
        {
            ShowMenu();
        }
    }

    public void StopTouching()
    {
        if (currentHover != -1)
        {
            var pointer = new PointerEventData(EventSystem.current);
            ExecuteEvents.Execute(menuButtons[currentHover], pointer, ExecuteEvents.pointerExitHandler);
            buttons[currentHover].OnHoverExit.Invoke();
            currentHover = -1;
        }
    }

    /*
    * Public methods to Show/Hide menu
    */
    public void ShowMenu()
    {
        if (!isShown)
        {
            isShown = true;
            StopCoroutine("TweenMenuScale");
            StartCoroutine("TweenMenuScale", isShown);
        }
    }

    public RadialMenuButton GetButton(int id)
    {
        if (id < buttons.Count)
        {
            return buttons[id];
        }
        return null;
    }

    public void HideMenu(bool force)
    {
        if (isShown && (hideOnRelease || force))
        {
            isShown = false;
            StopCoroutine("TweenMenuScale");
            StartCoroutine("TweenMenuScale", isShown);
        }
    }

    //Simple tweening for menu, scales linearly from 0 to 1 and 1 to 0
    private IEnumerator TweenMenuScale(bool show)
    {
        float targetScale = 0;
        Vector3 Dir = -1 * Vector3.one;
        if (show)
        {
            targetScale = 1;
            Dir = Vector3.one;
        }
        int i = 0; //Sanity check for infinite loops
        while (i < 250 && ((show && transform.localScale.x < targetScale) || (!show && transform.localScale.x > targetScale)))
        {
            transform.localScale += Dir * Time.deltaTime * 4f; //Tweening function - currently 0.25 second linear
            yield return true;
            i++;
        }
        transform.localScale = Dir * targetScale;
        StopCoroutine("TweenMenuScale");
    }

    private void AttempHapticPulse(ushort strength)
    {
        if (strength > 0 && FireHapticPulse != null)
        {
            FireHapticPulse (strength);
        }
    }

    #endregion

    #region Generation

    //Creates all the button Arcs and populates them with desired icons
    public void RegenerateButtons()
    {
        RemoveAllButtons();
        for (int i = 0; i < buttons.Count; i++)
        {
            // Initial placement/instantiation
            GameObject newButton = Instantiate(buttonPrefab);
            newButton.transform.SetParent(transform);
            newButton.transform.localScale = Vector3.one;
            newButton.GetComponent<RectTransform>().offsetMax = Vector2.zero;
            newButton.GetComponent<RectTransform>().offsetMin = Vector2.zero;

            //Setup button arc
            UICircle circle = newButton.GetComponent<UICircle>();
            if (buttonThickness == 1)
            {
                circle.fill = true;
            }
            else
            {
                circle.thickness = (int)(buttonThickness * (GetComponent<RectTransform>().rect.width / 2f));
            }
            int fillPerc = (int)(100f / buttons.Count);
            circle.fillPercent = fillPerc;
            circle.color = buttonColor;

            //Final placement/rotation
            float angle = ((360 / buttons.Count) * i) + offsetRotation;
            newButton.transform.localEulerAngles = new Vector3(0, 0, angle);
            newButton.layer = 4; //UI Layer
            newButton.transform.localPosition = Vector3.zero;
            if (circle.fillPercent < 55)
            {
                float angleRad = (angle * Mathf.PI) / 180f;
                Vector2 angleVector = new Vector2(-Mathf.Cos(angleRad), -Mathf.Sin(angleRad));
                newButton.transform.localPosition += (Vector3)angleVector * offsetDistance;
            }

            //Place and populate Button Icon
            GameObject buttonIcon = newButton.GetComponentInChildren<RadialButtonIcon>().gameObject;
            if (buttons[i].ButtonIcon == null)
            {
                buttonIcon.SetActive(false);
            }
            else
            {
                buttonIcon.GetComponent<Image>().sprite = buttons[i].ButtonIcon;
                buttonIcon.transform.localPosition = new Vector2(-1 * ((newButton.GetComponent<RectTransform>().rect.width / 2f) - (circle.thickness / 2f)), 0);
                //Min icon size from thickness and arc
                float scale1 = Mathf.Abs(circle.thickness);
                float R = Mathf.Abs(buttonIcon.transform.localPosition.x);
                float bAngle = (359f * circle.fillPercent * 0.01f * Mathf.PI) / 180f;
                float scale2 = (R * 2 * Mathf.Sin(bAngle / 2f));
                if (circle.fillPercent > 24) //Scale calc doesn't work for > 90 degrees
                {
                    scale2 = float.MaxValue;
                }

                float iconScale = Mathf.Min(scale1, scale2) - iconMargin;
                buttonIcon.GetComponent<RectTransform>().sizeDelta = new Vector2(iconScale, iconScale);
                //Rotate icons all vertically if desired
                if (!rotateIcons)
                {
                    buttonIcon.transform.eulerAngles = GetComponentInParent<Canvas>().transform.eulerAngles;
                }
            }
            menuButtons.Add(newButton);

        }
    }

    public void AddButton(RadialMenuButton newButton)
    {
        buttons.Add(newButton);
        RegenerateButtons();
    }

    private void RemoveAllButtons()
    {
        if (menuButtons == null)
        {
            menuButtons = new List<GameObject>();
        }
        for (int i = 0; i < menuButtons.Count; i++)
        {
            DestroyImmediate(menuButtons[i]);
        }
        menuButtons = new List<GameObject>();
    }

    #endregion

    #region Utility

    private float mod(float a, float b)
    {
        return a - b * Mathf.Floor(a / b);
    }

    #endregion
}

[System.Serializable]
public class RadialMenuButton
{
    public Sprite ButtonIcon;

    public UnityEvent OnClick;
    public UnityEvent OnHold;
    public UnityEvent OnHoverEnter;
    public UnityEvent OnHoverExit;
}

public enum ButtonEvent
{
    hoverOn,
    hoverOff,
    click,
    unclick
}

using UnityEngine;
using System.Collections;

public class ExampleClass : MonoBehaviour {
    void OnMouseDown() {
        Application.CaptureScreenshot("Screenshot.png");
    }
}

 

1-Cruise-North-Pole-50-Years-of-Victory