BeGear.cs

using System.Collections.Generic;
using System.Linq;
using UnityEngine;

[ExecuteInEditMode]
public class BeGear : MonoBehaviour
{
    #region 委托声明
    public delegate void VoidDelegate();
    public event VoidDelegate onDoSelect;
    public void DoSelect() { if (onDoSelect != null) onDoSelect(); }

    #endregion

    #region 公共变量
    public int Depth = 1;
    public UIAtlas GearWidgetAtlas;
    public string GearSpriteName;
    public string NormalItemSpriteName;
    public string ActiveItemSpriteName;
    public string HintSpriteName;
    public Vector2 GearSize = new Vector2(800, 800);
    public Vector2 ItemSize = new Vector2(750, 120);
    public Vector2 EllipseSize = new Vector2(800, 1600);
    public bool IsUnifyItemScale = false;
    public float NestleAccuracy = 0.01f;
    [Range(50, 200)]
    public int SpacingDistance;

    #endregion

    #region 公共属性
    public UIAtlas CurrentAtlas { get; internal set; }
    public int CurrentSelectIndex { get; internal set; }
    public string CurrentSelectSpriteName { get; internal set; }
    public bool IsAccordingToAtlasToIndex { get; set; }

    #endregion

    #region 私有变量
    Camera m_camera;
    GameObject m_gear;
    GameObject m_itemScroll;
    GameObject m_selectedItem;
    GameObject m_lastHint;
    bool m_isDragging;
    Dictionary<string, UISpriteData> m_currentDictionary = new Dictionary<string, UISpriteData>();
    UIFont m_optionFont;
    Color m_optionFontColor;
    UIFont m_hintFont;
    Color m_hintColor;
    string m_hint;
    Vector2 m_photoSize;
    int m_intervalWithLeft;
    int m_intervalWithIcon;
    bool m_isNeedUpdate;
    bool m_isNeedHint = true;//默认显示提示信息

    #endregion

    #region 私有属性
    Vector3 Location { get { return transform.localPosition; } }
    float Scale { get { return IsUnifyItemScale ? Mathf.Min(m_gear.transform.localScale.x, m_gear.transform.localScale.y) : 1; } }
    float OvalValueA { get { return (EllipseSize.x + ItemSize.x) / 2 * Scale; } }
    float OvalValueB { get { return (EllipseSize.y + ItemSize.y) / 2 * Scale; } }
    float PieceAngle { get { return 90.0f / Mathf.FloorToInt(90 * OvalValueB / (90 * (ItemSize.y + SpacingDistance))); } }
    float QuarterAngle { get { return Mathf.FloorToInt(90 / PieceAngle) * PieceAngle; } }
    float MinAngle { get { return QuarterAngle + PieceAngle; } }
    float MaxAngle { get { return QuarterAngle + PieceAngle * m_currentDictionary.Count; } }
    float AngleAddUp { get; set; }

    #endregion

    #region 系统函数
    void Start()
    {
        //初始化参数
        m_camera = FindObjectOfType<UIRoot>().transform.Find("Camera").GetComponent<Camera>();
        UICamera.currentCamera = m_camera;
        CurrentSelectSpriteName = null;
        CurrentSelectIndex = 0;//默认选中第一个
        //清空子物体
        gameObject.transform.DestroyChildren();
        //重建滚轮
        m_gear = newObject("gear", transform);
        var gearSprite = m_gear.AddComponent<UISprite>();
        gearSprite.atlas = GearWidgetAtlas;
        gearSprite.spriteName = GearSpriteName;
        gearSprite.depth = Depth;
        gearSprite.width = Mathf.FloorToInt(GearSize.x);
        gearSprite.height = Mathf.FloorToInt(GearSize.y);
        gearSprite.autoResizeBoxCollider = true;
        var gearCollider = m_gear.AddComponent<BoxCollider>();
        gearCollider.isTrigger = true;
        gearCollider.size = new Vector3(GearSize.x, GearSize.y, 0);
        //注册滚轮监听
        UIEventListener.Get(m_gear.gameObject).onDragStart = gearOnDragStart;
        UIEventListener.Get(m_gear.gameObject).onDrag = gearOnDrag;
        UIEventListener.Get(m_gear.gameObject).onDragEnd = gearOnDragEnd;
        //重建列表
        m_itemScroll = newObject("itemScroll", transform);
        //重建选项
        optionReset();
    }

    void Update()
    {
        //图集更换处理
        if (m_isNeedUpdate)
        {
            optionReset();
            m_isNeedUpdate = false;
        }
        //拖拽停止后贴靠最近的选项位
        if (m_isDragging) return;
        var residualAngle = AngleAddUp % PieceAngle;
        //小于修正精度则退出
        if (residualAngle < NestleAccuracy || PieceAngle - residualAngle < NestleAccuracy)
        {
            showHint();//修正完毕后显示提示信息
            return;
        }
        if (residualAngle > PieceAngle / 2)
        {//逆时针正数
            chainRotate(residualAngle * 0.1f);
        }
        else
        {//顺时针负数
            chainRotate(-residualAngle * 0.1f);
        }
    }

    #endregion

    #region 调用函数
    public void Init(Vector2 photoSize, int intervalWithLeft, int intervalWithIcon, UIFont optionFont, Color optionFontColor, UIAtlas atlas, Dictionary<string, UISpriteData> dic, int lastSelect, bool isAccordingToAtlasToIndex)
    {
        m_photoSize = photoSize;
        m_intervalWithLeft = intervalWithLeft;
        m_intervalWithIcon = intervalWithIcon;
        m_optionFont = optionFont;
        m_optionFontColor = optionFontColor;
        CurrentAtlas = atlas;
        m_currentDictionary.Clear();
        m_currentDictionary = dic;
        CurrentSelectIndex = lastSelect;
        IsAccordingToAtlasToIndex = isAccordingToAtlasToIndex;
        m_isNeedUpdate = true;
    }

    public void SetHint(UIFont hintFont, Color hintColor, string hint)
    {
        m_hintFont = hintFont;
        m_hintColor = hintColor;
        m_hint = hint;
    }

    #endregion

    #region 计算函数
    Vector3 getScreen(Vector2 location)
    {
        var pos = UICamera.currentCamera.ScreenToWorldPoint(new Vector3(location.x, location.y, 0f));
        return UICamera.currentCamera.transform.InverseTransformPoint(pos);
    }

    Vector3 getTouchAxis(GameObject reference)
    {
        var center = reference.transform.localPosition + Location;
        var touchPos = UICamera.currentTouch.pos;
        var axis = getScreen(touchPos) - center;
        return axis;
    }

    Vector3 getAngleToAxis(float angle)
    {
        var x = Mathf.Sin(angle * Mathf.Deg2Rad);
        var y = Mathf.Cos(angle * Mathf.Deg2Rad);
        return new Vector3(x, y);
    }

    float getAngleBetweenAxis(Vector3 axis1, Vector3 axis2, bool isClockwise = false, bool isJustOneside = false)
    {
        var cross = Vector3.Cross(axis1, axis2);
        var angle = Vector3.Angle(axis1, axis2);
        var round = isJustOneside ? 0 : 360;
        var v = 0.0f;
        if (isClockwise)
        {
            v = cross.z > 0 ? angle : round - angle;
        }
        else
        {
            v = cross.z < 0 ? angle : round - angle;
        }
        return v;
    }

    Vector2 getOvalAxis(Vector2 axis, float a, float b, bool isClockwise = false)
    {
        var cross = Vector3.Cross(axis, Vector2.right);
        var angle = Vector2.Angle(axis, Vector2.right);
        var deg360 = 0.0f;
        if (isClockwise)
        {
            deg360 = cross.z > 0 ? angle : 360 - angle;
        }
        else
        {
            deg360 = cross.z < 0 ? angle : 360 - angle;
        }
        var t = deg360 * Mathf.Deg2Rad;
        var bcost = b * Mathf.Cos(t);
        var asint = a * Mathf.Sin(t);
        var bcostp2 = Mathf.Pow(bcost, 2);
        var asintp2 = Mathf.Pow(asint, 2);
        var sqrt = Mathf.Sqrt(bcostp2 + asintp2);
        var r = a * b / sqrt;
        var x = r * Mathf.Cos(t);
        var y = r * Mathf.Sin(t);
        var v = new Vector2(x, y);
        return v;
    }

    #endregion

    #region 处理函数
    static GameObject newObject(string objectName, Component parent)
    {
        var obj = new GameObject
        {
            name = objectName,
            tag = parent.tag,
            layer = parent.gameObject.layer
        };
        obj.transform.SetParent(parent.transform);
        obj.transform.localScale = Vector3.one;
        obj.transform.localPosition = Vector3.zero;
        obj.transform.rotation = Quaternion.identity;
        obj.transform.localEulerAngles = Vector3.zero;
        return obj;
    }

    GameObject newItem(string itemName)
    {
        var item = newObject(itemName, m_itemScroll.transform);
        var itemSprite = item.AddComponent<UISprite>();
        itemSprite.atlas = GearWidgetAtlas;
        itemSprite.depth = Depth;
        itemSprite.width = Mathf.FloorToInt(ItemSize.x);
        itemSprite.height = Mathf.FloorToInt(ItemSize.y);
        itemSprite.autoResizeBoxCollider = true;
        var itemCollider = item.AddComponent<BoxCollider>();
        itemCollider.isTrigger = true;
        itemCollider.size = new Vector3(itemSprite.width, itemSprite.height, 0);
        return item;
    }

    void newOption(Component parent, int spriteIndex)
    {
        //设置标签
        var label = newObject("label", parent).AddComponent<UILabel>();
        label.depth = Depth + 1;
        label.bitmapFont = m_optionFont;
        var fontSize = m_optionFont.defaultSize;
        label.fontSize = fontSize;
        label.color = m_optionFontColor;
        var currentName = getDictionaryKeyByIndex(m_currentDictionary, spriteIndex);
        label.text = currentName;
        label.width = currentName.Length * fontSize;
        label.height = fontSize;
        //设置图片
        var photo = newObject("photo", parent);
        var photoSprite = photo.AddComponent<UISprite>();
        photoSprite.depth = Depth + 1;
        photoSprite.atlas = CurrentAtlas;
        photoSprite.spriteName = getDictionaryValueByIndex(m_currentDictionary, spriteIndex).name;
        photoSprite.width = Mathf.FloorToInt(m_photoSize.x);
        photoSprite.height = Mathf.FloorToInt(m_photoSize.y);
        photoSprite.autoResizeBoxCollider = true;
        //设置碰撞器
        var photoCollider = photo.AddComponent<BoxCollider>();
        photoCollider.isTrigger = true;
        photoCollider.size = new Vector3(photoSprite.width, photoSprite.height, 0);
        //注册监听
        UIEventListener.Get(photo).onDragStart = optionOnDragStart;
        UIEventListener.Get(photo).onDrag = optionOnDrag;
        UIEventListener.Get(photo).onDragEnd = optionOnDragEnd;
        //微调图标和标签位置
        photo.transform.localPosition = new Vector3(m_intervalWithLeft - (ItemSize.x - photoSprite.width) / 2, 0, 0);
        label.alignment = NGUIText.Alignment.Left;
        var leftPos = photo.transform.localPosition.x + (float)(photoSprite.width + label.width) / 2 + m_intervalWithIcon;
        label.transform.localPosition = new Vector3(leftPos, 0, 0);
    }

    GameObject newHint(Component parent, string hint)
    {
        var obj = newObject("hint", parent);
        obj.transform.localPosition = new Vector3(100, 200);

        var sprite = newObject("backdrop", obj.transform).AddComponent<UISprite>();
        sprite.depth = Depth + 1;
        sprite.atlas = GearWidgetAtlas;
        sprite.spriteName = HintSpriteName;
        sprite.MakePixelPerfect();

        var label = obj.AddComponent<UILabel>();
        label.depth = Depth + 2;
        label.bitmapFont = m_hintFont;
        label.alignment = NGUIText.Alignment.Left;
        label.fontSize = 35;
        label.color = m_hintColor;
        label.text = hint;
        label.width = sprite.width - 55;
        label.height = sprite.height - 55;

        return obj;
    }

    static TKey getDictionaryKeyByIndex<TKey, TValue>(Dictionary<TKey, TValue> dic, int index)
    {
        return dic.Keys.ToArray()[index];
    }

    static TValue getDictionaryValueByIndex<TKey, TValue>(Dictionary<TKey, TValue> dic, int index)
    {
        return dic.Values.ToArray()[index];
    }

    #endregion

    #region 执行函数
    void saveCurrentSelectSpriteName()
    {
        if (IsAccordingToAtlasToIndex)
        {//图集索引模式
            try
            {
                CurrentSelectSpriteName = CurrentAtlas.spriteList[CurrentSelectIndex].name;//尝试读取当前图像名
            }
            catch
            {
                CurrentSelectSpriteName = null;//索引失败
            }
        }
        else
        {//非图集索引模式
            CurrentSelectSpriteName = null;//默认置空
        }
    }

    void selectItem(Transform item)
    {
        var itemSprite = item.GetComponent<UISprite>();
        //判断是否选中
        var isCurrentSelect = false;
        var offset = Mathf.Abs(item.localPosition.y);
        var decisionDistance = SpacingDistance / 2;
        if (offset < decisionDistance) isCurrentSelect = true;
        if (isCurrentSelect)
        {
            itemSprite.spriteName = ActiveItemSpriteName;
            //获取当前选中
            var photo = item.Find("photo");
            if (photo == null) return;
            var optionSprite = photo.GetComponent<UISprite>();
            if (optionSprite == null) return;
            m_selectedItem = item.gameObject;
            CurrentSelectIndex = int.Parse(item.gameObject.name.Split('_')[1]);
            saveCurrentSelectSpriteName();
            DoSelect();//执行委托
        }
        else
        {
            itemSprite.spriteName = NormalItemSpriteName;
        }
    }

    void optionReset()
    {
        AngleAddUp = 0; //重置旋转角
        m_itemScroll.transform.DestroyChildren(); //清空列表
        for (var i = 0; i < m_currentDictionary.Count; i++)
        {
            //创建物件
            var item = newItem("item_" + i);
            item.transform.localPosition = new Vector3(0, 0); //重置物件位置
            //新建选项
            newOption(item.transform, i);
            //注册物件监听
            UIEventListener.Get(item.gameObject).onDragStart = itemOnDragStart;
            UIEventListener.Get(item.gameObject).onDrag = itemOnDrag;
            UIEventListener.Get(item.gameObject).onDragEnd = itemOnDragEnd;
            //隐藏选项
            item.gameObject.SetActive(false);
        }
        //转动物件到合适位置
        chainRotate(CurrentSelectIndex * PieceAngle + MinAngle);
    }

    void chainRotate(float angleOfRotation)
    {
        var addUp = AngleAddUp + angleOfRotation;
        var itemScroll = m_itemScroll.transform;
        var count = itemScroll.childCount;
        var link = Mathf.RoundToInt(addUp) / Mathf.RoundToInt(PieceAngle);
        link = link < 0 ? 0 : link;
        link = link >= count ? count : link;
        AngleAddUp = addUp;
        if (AngleAddUp < MinAngle)
        {
            AngleAddUp = MinAngle;
            return;
        }
        if (AngleAddUp > MaxAngle)
        {
            AngleAddUp = MaxAngle;
            return;
        }
        //物件处理
        var a = OvalValueA;
        var b = OvalValueB;
        for (var i = 0; i < link; i++)
        {
            var item = itemScroll.GetChild(i);
            var angle = AngleAddUp - (i + 1) * PieceAngle;
            if (angle > 180)
            {
                item.transform.localPosition = new Vector3(0, b, 0);
                item.gameObject.SetActive(false);
            }
            else if (angle < PieceAngle)
            {
                item.transform.localPosition = new Vector3(0, -b, 0);
                item.gameObject.SetActive(false);
            }
            else
            {
                item.gameObject.SetActive(true);
                item.transform.localScale = Vector3.one * Scale;
                var srcAxis = getAngleToAxis(angle);
                var trgAxis = getOvalAxis(srcAxis, a, b, true);
                var v = new Vector3(trgAxis.x, trgAxis.y);
                item.transform.localPosition = v;
            }
            selectItem(item.transform); //设置物件选中
        }
        //滚轮处理
        m_gear.transform.localEulerAngles += new Vector3(0, 0, angleOfRotation);
        //提示信息处理
        hideHint();
    }

    void hideHint()
    {
        m_isNeedHint = true;//激活提示信息显示标志
        DestroyObject(m_lastHint);

    }

    void showHint()
    {
        if (!m_isNeedHint || m_selectedItem == null) return;
        m_lastHint = newHint(m_selectedItem.transform, m_hint);
        m_isNeedHint = false;//禁止提示信息显示标志
    }

    #endregion

    #region 滚轮监听事件
    Vector3 m_gearDragOverTouchPos;
    void gearOnDragStart(GameObject go)
    {
        m_isDragging = true;
        m_gearDragOverTouchPos = getTouchAxis(m_gear);
    }

    void gearOnDragEnd(GameObject go)
    {
        m_isDragging = false;
    }

    void gearOnDrag(GameObject go, Vector2 delta)
    {
        var axis = getTouchAxis(m_gear);
        var stepAngle = getAngleBetweenAxis(axis, m_gearDragOverTouchPos, false, true);
        chainRotate(stepAngle);
        m_gearDragOverTouchPos = getTouchAxis(m_gear);
    }

    #endregion

    #region 物件监听事件
    Vector3 m_itemDragOverTouchPos;
    void itemOnDragStart(GameObject go)
    {
        m_isDragging = true;
        m_itemDragOverTouchPos = getTouchAxis(m_gear);
    }

    void itemOnDragEnd(GameObject go)
    {
        m_isDragging = false;
    }

    void itemOnDrag(GameObject go, Vector2 delta)
    {
        var axis = getTouchAxis(m_gear);
        var stepAngle = getAngleBetweenAxis(axis, m_itemDragOverTouchPos, false, true);
        chainRotate(stepAngle);
        m_itemDragOverTouchPos = getTouchAxis(m_gear);
    }

    #endregion

    #region 选项监听事件
    Vector3 m_optionDragOverTouchPos;
    void optionOnDragStart(GameObject go)
    {
        m_isDragging = true;
        m_optionDragOverTouchPos = getTouchAxis(m_gear);
    }

    void optionOnDragEnd(GameObject go)
    {
        m_isDragging = false;
    }

    void optionOnDrag(GameObject go, Vector2 delta)
    {
        var axis = getTouchAxis(m_gear);
        var stepAngle = getAngleBetweenAxis(axis, m_optionDragOverTouchPos, false, true);
        chainRotate(stepAngle);
        m_optionDragOverTouchPos = getTouchAxis(m_gear);
    }

    #endregion
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值