废话不多说,先看第一篇代码!
using DG.Tweening;
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
public class RotationChart3D : MonoBehaviour
{
public int n; // 使用的角色数量
public float spacing = 1; // 角色之间的间距
float c; // 圆的周长
float r; // 圆的半径
float ang; // 每个角色的角度
float moveang; // 移动的角度增量
List<GameObject> list = new List<GameObject>(); // 角色对象列表
List<Transform> sortlist = new List<Transform>(); // 排序后的角色对象列表
void Start()
{
c = (1 + spacing) * n; // 计算圆的周长
r = c / (2 * Mathf.PI); // 计算圆的半径
ang = 2 * Mathf.PI / n; // 计算每个角色的角度
for (int i = 0; i < n; i++)
{
// 实例化角色对象,加载对应资源文件
list.Add(Instantiate(Resources.Load<GameObject>("Role/" + i), transform));
float x = Mathf.Sin(i * ang) * r; // 根据角度计算x坐标
float z = Mathf.Cos(i * ang) * r; // 根据角度计算z坐标
list[i].transform.localPosition = new Vector3(x, 0, z); // 设置角色对象的位置
list[i].AddComponent<PlayerMove>(); // 向角色对象添加PlayerMove组件
sortlist.Add(list[i].transform); // 将角色对象的transform添加到排序列表中
}
}
public void Move(float dis)
{
moveang += dis / r; // 根据移动距离计算角度增量
for (int i = 0; i < list.Count; i++)
{
float x = Mathf.Sin(i * ang + moveang) * r; // 根据角度和角度增量计算x坐标
float z = Mathf.Cos(i * ang + moveang) * r; // 根据角度和角度增量计算z坐标
list[i].transform.localPosition = new Vector3(x, 0, z); // 更新角色对象的位置
}
}
public void Inertia(float endspeed)
{
float time = Mathf.Abs(endspeed) / 1; // 计算惯性运动的时间
DOTween.To((float a) =>
{
Move(a); // 根据角度增量移动角色对象
}, endspeed, 0, time).OnComplete(() =>
{
Align(); // 惯性运动结束后进行对齐操作
});
}
public void Align()
{
sortlist.Sort((a, b) =>
{
if (a.localPosition.z < b.localPosition.z) // 比较z坐标大小进行排序
{
return 1;
}
else if (a.localPosition.z == b.localPosition.z)
{
return 0;
}
else
{
return -1;
}
});
float alignang = Mathf.Atan(sortlist[0].localPosition.x / sortlist[0].localPosition.z); // 计算对齐的角度
float aligndis = alignang * r; // 计算对齐的距离
float time = Mathf.Abs(aligndis) / 1; // 计算对齐的时间
DOTween.To((float a) =>
{
for (int i = 0; i < list.Count; i++)
{
float x = Mathf.Sin(i * ang + a) * r; // 根据角度计算x坐标
float z = Mathf.Cos(i * ang + a) * r; // 根据角度计算z坐标
list[i].transform.localPosition = new Vector3(x, 0, z); // 更新角色对象的位置
}
}, moveang, moveang - alignang, time); // 动态更新位置,实现对齐效果
}
}
注意事项:1.这篇代码挂在空对象身上就可以
2.命名空间上的using DG.Tweening;这个是DoTween插件,可到网上自行下载
3.Resources目录下如果你有文件夹,路径一定要对
接下来看第二篇代码,这是给每一个预制体(游戏角色)挂的脚本(这里不用手动挂,在第一篇代码中实例化角色的时候会动态给角色添加脚本)。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMove : MonoBehaviour
{
Camera cam; // 相机对象
Vector3 pos; // 鼠标点击位置
void Start()
{
cam = Camera.main; // 获取主摄像机对象
}
private void OnMouseDown() // 鼠标点击自身
{
float z = cam.WorldToScreenPoint(transform.position).z; // 将角色对象的世界位置转换为屏幕坐标,并获取z坐标
pos = cam.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, z)); // 将鼠标点击位置转换为世界坐标
}
private void OnMouseDrag() // 鼠标拖拽自身
{
float z = cam.WorldToScreenPoint(transform.position).z; // 将角色对象的世界位置转换为屏幕坐标,并获取z坐标
Vector3 pos1 = cam.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, z)); // 将鼠标当前位置转换为世界坐标
float dis = pos1.x - pos.x; // 计算鼠标移动距离
transform.parent.GetComponent<RotationChart3D>().Move(dis); // 调用父对象(RotationChart3D)中的Move方法,传入移动距离
pos = pos1; // 更新鼠标位置
}
private void OnMouseUp() // 鼠标抬起
{
float z = cam.WorldToScreenPoint(transform.position).z; // 将角色对象的世界位置转换为屏幕坐标,并获取z坐标
Vector3 pos1 = cam.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, z)); // 将鼠标当前位置转换为世界坐标
float dis = pos1.x - pos.x; // 计算鼠标移动距离
transform.parent.GetComponent<RotationChart3D>().Inertia(dis); // 调用父对象(RotationChart3D)中的Inertia方法,传入移动距离
pos = pos1; // 更新鼠标位置
}
}
注意事项:游戏对象如何想要触发OnMonseDown(所有鼠标在物体身上触发的效果),身上一定要有一个碰撞器组件。游戏对象的话,常用Capsule Collider 胶囊碰撞器就可以。
效果展示: