利用unity自带的LineRenderer组件创建/编辑曲线.
[Editor – BezierEditor.cs]
文件放在”Assets/Editor”目录下.
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(BezierCurves))]
public class BezierEditor : Editor
{
/// <summary>
/// Menu
/// </summary>
[MenuItem("[ MYTOOLS ]/创建Bezier曲线")]
static void CreateBezier(){
var go = new GameObject ("BezierCurves", typeof(LineRenderer), typeof(BezierCurves));
var bc = go.GetComponent<BezierCurves> ();
bc.Init ();
bc.RefreshLineRenderer(bc.CalCurvesPoints ());
bc.controllerList = bc.BezierToControllers ();
}
//变量
BezierCurves bc; //曲线类
/// <summary>
/// Raises the enable event.
/// </summary>
void OnEnable(){
bc = target as BezierCurves;
bc.controllerList = bc.BezierToControllers ();
}
/// <summary>
/// 重构Inspector面板
/// </summary>
public override void OnInspectorGUI (){
serializedObject.Update ();
GUI.backgroundColor = Color.green;
GUI.color = Color.yellow;
//曲线段数
SerializedProperty cs = serializedObject.FindProperty ("curvesSegments");
bc.curvesSegments = EditorGUILayout.IntSlider ("[ 顶点间隔距离 ]", bc.curvesSegments, 1, 30);
if (cs.intValue != bc.curvesSegments) {
bc.RefreshLineRenderer(bc.CalCurvesPoints());
}
//曲线是否闭合
SerializedProperty close = serializedObject.FindProperty ("isClosed");
bc.isClosed = EditorGUILayout.Toggle ("[ 是否是闭合曲线 ]", bc.isClosed);
if (close.boolValue != bc.isClosed) {
bc.RefreshLineRenderer (bc.CalCurvesPoints ());
}
//添加顶点
if (GUILayout.Button ("[ 添加点]")) {
bc.AddBezierPoint();
bc.controllerList = bc.BezierToControllers ();
bc.RefreshLineRenderer (bc.CalCurvesPoints ());
}
GUI.backgroundColor = Color.red;
GUI.color = Color.yellow;
//删除顶点
if(GUILayout.Button("[ 删除点 ]")){
bc.DeleteBezierPoint ();
bc.controllerList = bc.BezierToControllers ();
bc.RefreshLineRenderer (bc.CalCurvesPoints ());
}
Undo.RecordObject (bc, "操纵器");
Undo.RecordObject (bc._line, "线");
serializedObject.ApplyModifiedProperties ();
}
/// <summary>
/// 控制器
/// </summary>
public void OnSceneGUI(){
EditorGUI.BeginChangeCheck();
for (int i = 0; i < bc.controllerList.Count; i++) {
bc.controllerList [i].OnBeginChangeCheck ();
}
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject (bc, "bc.controllerList");
for (int i = 0; i < bc.controllerList.Count; i++) {
bc.controllerList [i].OnEndChangeCheck ();
}
bc.RefreshLineRenderer (bc.CalCurvesPoints ());
Undo.RecordObject (bc, "操纵器");
Undo.RecordObject (bc._line, "线");
}
}
}
[曲线实例 – BezierCurves.cs]
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
[System.Serializable]
public class BezierCurves : MonoBehaviour
{
//单例化
static public BezierCurves Instance;
//变量
[SerializeField] public List<Bezier> bezierPointsList; //用来保存Bezier数据
[SerializeField] public LineRenderer _line; //绘制线组件
public List<BezierController> controllerList; //保存控制器
public int curvesSegments = 10; //曲线点的段数
public bool isClosed = false; //曲线是否是闭合的?
//初始化
void Awake () {
Instance = this;
//获取LineRenderer组件
_line = GetComponent<LineRenderer> ();
}
//初始化
public void Init(){
_line = GetComponent<LineRenderer> ();
controllerList = new List<BezierController> ();
bezierPointsList = new List<Bezier>();
//创建默认的点
bezierPointsList.Add(new Bezier( Vector3.zero, Vector2.one *5F));
bezierPointsList.Add(new Bezier( Vector3.right*10, Vector2.one *5F));
}
public void RefreshLineRenderer(Vector3[] curvesPoints){
_line.numPositions = curvesPoints.Length;
_line.SetPositions (curvesPoints);
}
/// <summary>
/// 添加一个Bezier点
/// </summary>
/// <param name="ctrllerList">Ctrller list.</param>
public Bezier AddBezierPoint(){
int count = bezierPointsList.Count;
Bezier bezier = new Bezier (
bezierPointsList [ count- 1].point - bezierPointsList [count - 2].point + bezierPointsList [count - 1].point,
Vector2.one*5F);
bezierPointsList.Add( bezier );
return bezier;
}
/// <summary>
/// 删除最后一个Bezier点
/// </summary>
public void DeleteBezierPoint(){
if (bezierPointsList.Count <= 2)
return;
bezierPointsList.RemoveAt (bezierPointsList.Count - 1);
}
/// <summary>
/// 计算曲线点
/// </summary>
/// <returns>The curves points.</returns>
/// <param name="ctrllerList">Ctrller list.</param>
/// <param name="isClosed">If set to <c>true</c> is closed.</param>
public Vector3[] CalCurvesPoints(){
//存放curves点
List<Vector3> curves = new List<Vector3>();
//是否是结束点?
bool isEndPoint=false;
//计算曲线点
for (int i = 0; i < bezierPointsList.Count-1; i++) {
//是否最后一个顶点?
if (i >= bezierPointsList.Count - 2 && !isClosed) {
isEndPoint = true;
} else {
isEndPoint = false;
}
//计算曲线点
curves.AddRange (CalCurvesPoints (bezierPointsList [i].point, bezierPointsList [i].cp2,
bezierPointsList [i + 1].cp1, bezierPointsList [i + 1].point, isEndPoint));
}
//如果是闭合曲线 -- 添加一排点
if(isClosed){
int count = bezierPointsList.Count;
curves.AddRange (CalCurvesPoints (bezierPointsList [count-1].point, bezierPointsList [count-1].cp2,
bezierPointsList [0].cp1, bezierPointsList [0].point, true));
}
return curves.ToArray ();
}
/// <summary>
/// 数据转换 -- BezierCurvesController to Bezier
/// </summary>
/// <returns>The to datas.</returns>
/// <param name="ctrllerList">Ctrller list.</param>
public List<Bezier> ControllersToBezier(List<BezierController> ctrllerList){
List<Bezier> bezierPointsList = new List<Bezier>();
//转存数据
for (int i = 0; i < ctrllerList.Count; i++) {
bezierPointsList.Add (new Bezier (
ctrllerList [i].ctrller.point, ctrllerList [i].ctrller.cp1, ctrllerList [i].ctrller.cp2));
}
return bezierPointsList;
}
/// <summary>
/// 数据转换 -- Bezier to BezierCurvesController
/// </summary>
/// <returns>The to controllers.</returns>
/// <param name="bezier">Bezier.</param>
public List<BezierController> BezierToControllers(){
List<BezierController> list = new List<BezierController> ();
//转存数据
for (int i = 0; i < bezierPointsList.Count; i++) {
list.Add (new BezierController (bezierPointsList[i]));
}
return list;
}
/***************************************************************
* 三阶Bezier曲线公式 :
* B(t) = P0(1-t)³ + 3P₁t(1-t)² + 3P₂t²(1-t) + P₃t³, t∈[0,1]
****************************************************************/
public Vector3 CalCurvesPoints( Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t){
var v = p0 * Mathf.Pow ((1 - t), 3) + 3 * p1 * t * Mathf.Pow (1 - t, 2) + 3 * p2 * Mathf.Pow (t, 2) * (1 - t) + p3 * Mathf.Pow (t, 3);
return v;
}
public List<Vector3> CalCurvesPoints(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, bool isEndPoint){
int endPoint; //起始点/结束点+1
//是否结束点?
if (isEndPoint) endPoint = 1;
else endPoint = 0;
List<Vector3> list = new List<Vector3> ();
float t = 0; //时间
float incr = 1F / (float)curvesSegments; //递增数
//计算两点间所有曲线点
for (int i = 0; i < curvesSegments + endPoint; i++) {
list.Add (CalCurvesPoints (p0, p1, p2, p3, t));
t += incr;
}
return list;
}
}
[曲线控制器 – CustomDrawCurves.cs]
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
public class CustomDrawCurves : MonoBehaviour
{
public float minSpace = 1F; //曲线的最小间距
private Vector2 _startPos, _currentPos, _lastPos; //起始/结束点//上一次拖动的位置
private List<Vector2> _list; //记录经过点
void Start () {
_list = new List<Vector2> ();
}
void Update () {
if (Input.GetMouseButtonDown (0)) {
_startPos = _currentPos = Input.mousePosition;
OnButtonDown ();
}
if (Input.GetMouseButton (0)) {
_currentPos = Input.mousePosition;
OnDrag ();
}
if (Input.GetMouseButtonUp (0)) {
OnButtonUp ();
}
}
void OnButtonDown(){
_list.Clear ();
_list.Add (GetScreenToWorld(_startPos));
}
void OnDrag(){
if (GetDistance (_currentPos, _lastPos, minSpace)) {
_lastPos = _currentPos;
_list.Add (GetScreenToWorld(_currentPos));
}
}
void OnButtonUp(){
print (_list.Count);
// CreatePolygon (_list);
var go = new GameObject("ccccc",typeof(PolygonCollider2D));
var col = go.GetComponent<PolygonCollider2D> ();
col.points = _list.ToArray();
// _list.Clear ();
}
//PRIVATE-----------------------------------------------------------------
//计算 -- 两点距离是否小于最小值
private bool GetDistance( Vector2 p1, Vector2 p2, float minDistance){
if (Vector2.Distance (p1, p2) > minDistance)
return true;
return false;
}
//获取点位置 -- 屏幕To世界坐标
private Vector2 GetScreenToWorld(Vector2 pos){
return Camera.main.ScreenToWorldPoint(pos);
}
//创建模型
private void CreatePolygon(List<Vector3> points){
var go = new GameObject ("Stroe", typeof(MeshRenderer), typeof(MeshFilter));
var mesh = go.GetComponent<MeshFilter> ();
mesh.mesh.SetVertices (points);
}
//PRIVATE-----------------------------------------------------------------
//TESTCODE--------------------------------------------------------------
#if UNITY_EDITOR
void OnDrawGizmos(){
//Draw lines
if (_list!=null && _list.Count > 0) {
for (int i = 0; i < _list.Count; i++) {
if (i != _list.Count - 1)
Gizmos.DrawLine (_list [i], _list [i+1]);
Gizmos.DrawWireSphere (_list [i], 1);
}
}
}
#endif
//TESTCODE--------------------------------------------------------------
}