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
}