Unity使用Dotween实现一个快捷的指令圆盘
玩方舟生存进化时总是用到这个圆盘功能,最近在用Blender时也有这个功能,心里觉得用在某些游戏上确实挺好用的,草草写了一个功能,主要在数学上有点麻烦,应该可以再改进运算方法
using DG.Tweening;
using UnityEngine;
using System.Collections.Generic;
using UnityEngine.InputSystem;
using Sirenix.OdinInspector;
using System.Linq;
using JetBrains.Annotations;
using Extraerrestrial.Tools;
using UnityEngine.Profiling;
namespace Extraerrestrial.Gameplay
{
public class CommandDial : MonoBehaviour
{
public Sequence CommandDialIn;
public float AnimationDuration = 0.5f;
public AnimationCurve AnimationCurve;
public GameObject CommandItemPrefab;
public float Distance;
public int CommandNumbers;
protected List<GameObject> _list;
protected GameObject _root;
public void Start()
{
CommandDialIn = DOTween.Sequence();
InputManager.Instance.CallCommandDial.performed += OnCallDial;
InputManager.Instance.CallCommandDial.canceled += OnHideDial;
}
public void Update()
{
ChooseDial();
}
public void ChooseDial()
{
Vector2 mouseWorldPos = ARTools.GetMouseWorldPosition();
if (_root == null || _list == null || _list.Count == 0) return;
Vector2 rootPos = _root.transform.position;
Vector2 dir = mouseWorldPos - rootPos;
int selectedIndex=GetPointSector(dir, CommandNumbers);
// 响应选中的指令项
for (int i = 0; i < _list.Count; i++)
{
var item = _list[i];
if (item == null) continue;
// 这里可以高亮选中的item,或做其他反应
if (i == selectedIndex)
{
item.GetComponent<SpriteRenderer>().color = Color.yellow;
}
else
{
item.GetComponent<SpriteRenderer>().color = Color.white;
}
}
}
public void OnCallDial(InputAction.CallbackContext callbackContext)
{
CallCommandDial();
}
public void OnHideDial(InputAction.CallbackContext callbackContext)
{
HideCommandDial();
}
public void CallCommandDial()
{
Vector2 mouseWorldPos = ARTools.GetMouseWorldPosition();
GameObject root = new GameObject("CommandDialRoot");
_root = root;
root.transform.position = mouseWorldPos;
_list = new List<GameObject>();
for (int i = 0; i < CommandNumbers; i++)
{
GameObject CommandItem = Instantiate(CommandItemPrefab, root.transform);
CommandItem.transform.position = root.transform.position;
_list.Add(CommandItem);
}
float degree = 360f / CommandNumbers;
CommandDialIn.Kill();
CommandDialIn = DOTween.Sequence();
int j = 0;
foreach (var item in _list)
{
if (item == null) continue;
float currentDegree = degree * j++;
CommandDialIn.Join((item.GetComponent<Transform>().DOMove(item.GetComponent<Transform>().position + Quaternion.Euler(new Vector3(0, 0, -currentDegree)) * Vector3.up * Distance, AnimationDuration))).SetEase(AnimationCurve).SetAutoKill(false).Pause();
}
CommandDialIn.Play();
}
public void HideCommandDial()
{
CommandDialIn.OnRewind(null);
CommandDialIn.OnRewind(() =>
{
Destroy(_list[0].transform.parent.gameObject);
_root = null;
}).PlayBackwards();
}
public void OnDestroy()
{
InputManager.Instance.CallCommandDial.performed -= OnCallDial;
InputManager.Instance.CallCommandDial.canceled -= OnHideDial;
}
public int GetPointSector(Vector2 targetVector, int n)
{
// 1. 基础参数计算(核心不变)
float totalAngle = 360f;
float anglePerPoint = totalAngle / n; // 每个顶点的中心夹角x
float sectorHalfAngle = anglePerPoint / 2; // 领域半角x/2
// 2. 将目标向量转换为极角(以Y轴正方向为0°,顺时针为正,范围[0,360))
float targetPolarAngle = NormalizeAngle(GetPolarAngle(targetVector));
KeyValuePair<float,float> zeroRange= new KeyValuePair<float,float>(90-sectorHalfAngle,90+sectorHalfAngle);
// 3. 遍历所有顶点(0~n-1),计算每个顶点的角度区间
for (int i = 0; i < n; i++)
{
// 核心修正:0号顶点=90°,i号顶点=90° + i*每个顶点的夹角(顺时针排列)
float pointCenterAngle = NormalizeAngle(90f + i * anglePerPoint);
float sectorStart = NormalizeAngle(zeroRange.Key - i * anglePerPoint);
float sectorEnd = NormalizeAngle(zeroRange.Value - i * anglePerPoint);
// 计算当前顶点的领域区间(处理跨0°的边界情况)
if((zeroRange.Key - i * anglePerPoint)* (zeroRange.Value - i * anglePerPoint)<0)
{
if(zeroRange.Value-i*anglePerPoint>targetPolarAngle||NormalizeAngle(zeroRange.Key-i*anglePerPoint)<targetPolarAngle)
{
return i;
}
}
else
{
// 4. 判断目标角度是否在当前顶点的领域内
if (targetPolarAngle >= sectorStart && targetPolarAngle <=sectorEnd)
{
return i;
}
}
}
}
private float GetPolarAngle(Vector2 vector)
{
return Mathf.Atan2(vector.y, vector.x) * Mathf.Rad2Deg; // 转换为以Y轴为0°,顺时针为正
}
private float NormalizeAngle(float angle)
{
angle = angle % 360f;
if (angle < 0)
{
angle += 360f;
}
return angle;
}
}
}
浙公网安备 33010602011771号