如何使用自定义编辑器在移动另一条贝塞尔曲线时更新一条贝塞尔曲线

2024-04-08

我正在使用下面的代码创建贝塞尔曲线,我从here http://catlikecoding.com/unity/tutorials/curves-and-splines/。我也做了一个BezierPair具有两条贝塞尔曲线作为子对象的游戏对象。

从下面的各个图像和 BezierPair 中,其中points[0]...points[3]表示为P0...P3:

  1. I want P0当移动一条贝塞尔曲线时,每条贝塞尔曲线的值始终保持不变。换句话说,我希望它们始终一起移动,并且可以选择关闭此移动。
  1. Say P1两条曲线是分开的。我怎样才能使P1每条曲线沿相同方向移动相同距离?
  1. Say P2两条曲线是分开的。我怎样才能使P2一个曲面镜的P2沿着连接线的另一条曲线P0 and P3?请注意,镜像线将从下例中的曲线 1 中获取,因为curve1's P2被感动了。如果curve2's P2移动,则镜像线将从curve2's P0P3.

我不想在运行时执行此操作。所以必须使用自定义编辑器。 我尝试在下面的代码中解决 1. 问题,但是如果不在层次结构窗口中选择 BezierPair,第二条曲线的位置就不会更新

Bezier:

public static class Bezier {

public static Vector3 GetPoint (Vector3 p0, Vector3 p1, Vector3 p2,   Vector3 p3, float t) {
t = Mathf.Clamp01(t);
float oneMinusT = 1f - t;
return
    oneMinusT * oneMinusT * oneMinusT * p0 +
    3f * oneMinusT * oneMinusT * t * p1 +
    3f * oneMinusT * t * t * p2 +
    t * t * t * p3;
}

public static Vector3 GetFirstDerivative (Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t) {
t = Mathf.Clamp01(t);
float oneMinusT = 1f - t;
return
    3f * oneMinusT * oneMinusT * (p1 - p0) +
    6f * oneMinusT * t * (p2 - p1) +
    3f * t * t * (p3 - p2);
}
}

贝塞尔曲线:

[RequireComponent(typeof(LineRenderer))]
public class BezierCurve : MonoBehaviour {

public Vector3[] points;
LineRenderer lr;
public int numPoints = 49;
bool controlPointsChanged = false;

bool isMoving = false;

public void Reset () {
points = new Vector3[] {
    new Vector3(1f, 0f, 0f),
    new Vector3(2f, 0f, 0f),
    new Vector3(3f, 0f, 0f),
    new Vector3(4f, 0f, 0f)
};
}

void Start()    {

lr = GetComponent<LineRenderer> ();
lr.positionCount = 0;
DrawBezierCurve ();

}
public Vector3 GetPoint (float t) {
return transform.TransformPoint(Bezier.GetPoint(points[0], points[1], points[2], points[3], t));
}

public void DrawBezierCurve ()  {
lr = GetComponent<LineRenderer> ();
lr.positionCount = 1;
lr.SetPosition(0, points[0]);

for (int i = 1; i < numPoints+1; i++) {
    float t = i / (float)numPoints;
    lr.positionCount = i+1;
    lr.SetPosition(i, GetPoint(t));
}
}

public Vector3 GetVelocity (float t) {
return transform.TransformPoint(
    Bezier.GetFirstDerivative(points[0], points[1], points[2], points[3], t)) - transform.position;
}

public Vector3 GetDirection (float t) {
return GetVelocity(t).normalized;
}
}

贝塞尔曲线编辑器:

[CustomEditor(typeof(BezierCurve))]
public class BezierCurveEditor : Editor {

private BezierCurve curve;
private Transform handleTransform;
private Quaternion handleRotation;

private const int lineSteps = 10;

private const float directionScale = 0.5f;

private void OnSceneGUI () {
curve = target as BezierCurve;
handleTransform = curve.transform;
handleRotation = Tools.pivotRotation == PivotRotation.Local ?
    handleTransform.rotation : Quaternion.identity;

Vector3 p0 = ShowPoint(0);
Vector3 p1 = ShowPoint(1);
Vector3 p2 = ShowPoint(2);
Vector3 p3 = ShowPoint(3);

Handles.color = Color.gray;
Handles.DrawLine(p0, p1);
Handles.DrawLine(p2, p3);
Handles.DrawBezier(p0, p3, p1, p2, Color.white, null, 2f);

curve.DrawBezierCurve ();

if (GUI.changed) {
    curve.DrawBezierCurve ();
    EditorUtility.SetDirty( curve );
    Repaint();
}

}


private void ShowDirections () {
Handles.color = Color.green;
Vector3 point = curve.GetPoint(0f);
Handles.DrawLine(point, point + curve.GetDirection(0f) * directionScale);
for (int i = 1; i <= lineSteps; i++) {
    point = curve.GetPoint(i / (float)lineSteps);
    Handles.DrawLine(point, point + curve.GetDirection(i / (float)lineSteps) * directionScale);
}
}

private Vector3 ShowPoint (int index) {
Vector3 point = handleTransform.TransformPoint(curve.points[index]);
EditorGUI.BeginChangeCheck();
point = Handles.DoPositionHandle(point, handleRotation);
if (EditorGUI.EndChangeCheck()) {
    Undo.RecordObject(curve, "Move Point");
    EditorUtility.SetDirty(curve);
    curve.points[index] = handleTransform.InverseTransformPoint(point);
}
return point;
}
}

贝塞尔曲线对:

public class BezierPair : MonoBehaviour {


public GameObject bez1;
public GameObject bez2;

public void setupCurves()   {
    bez1 = GameObject.Find("Bez1");
    bez2 = GameObject.Find("Bez2");
}
}

贝塞尔曲线对编辑器:

[CustomEditor(typeof(BezierPair))]
public class BezierPairEditor : Editor {

private BezierPair bezPair;

 public override void OnInspectorGUI()
{
    bezPair = target as BezierPair;

    if (bezPair.bez1.GetComponent<BezierCurve>().points[0] != bezPair.bez2.GetComponent<BezierCurve>().points[0])
    {
        Vector3 assignPoint0 = bezPair.bez1.GetComponent<BezierCurve>().points[0];
        bezPair.bez2.GetComponent<BezierCurve>().points[0] = assignPoint0;

    }
     if (GUI.changed)
    {

        EditorUtility.SetDirty(bezPair.bez1);
        EditorUtility.SetDirty(bezPair.bez2);
        Repaint();
    }
}

EDITED:

我认为你不需要你的BezierPair班级。我建议你添加对其他的引用BezierCurve您想要“配对”为公共字段的对象BezierCurve class (paired)。另一条曲线将与该曲线“配对”。一旦配对,可能会受到运动限制。您可以使用 3 个公共布尔字段控制所需的行为behavior1, behavior2 and behavior3.

Note#1: 而不是调用该方法DrawBezierCurve从编辑器中,我添加了[ExecuteInEditMode]到组件类。这样,您就不会混合组件和编辑器之间的职责:BezierCurve 组件在场景上绘制自身,而 BezierCurveEditor 仅管理编辑逻辑,例如应用约束和绘制处理程序。

贝塞尔曲线:

using UnityEngine;

[RequireComponent(typeof(LineRenderer))]
[ExecuteInEditMode] // Makes Update() being called often even in Edit Mode
public class BezierCurve : MonoBehaviour
{

  public Vector3[] points;
  public int numPoints = 50;
  // Curve that is paired with this curve
  public BezierCurve paired;
  public bool behavior1; // check on editor if you desired behavior 1 ON/OFF
  public bool behavior2; // check on editor if you desired behavior 2 ON/OFF
  public bool behavior3; // check on editor if you desired behavior 3 ON/OFF
  LineRenderer lr;

  void Reset()
  {
    points = new Vector3[]
    {
      new Vector3(1f, 0f, 0f),
      new Vector3(2f, 0f, 0f),
      new Vector3(3f, 0f, 0f),
      new Vector3(4f, 0f, 0f)
    };
  }

  void Start()
  {
    lr = GetComponent<LineRenderer>();
  }

  void Update()
  {
    // This component is the only responsible for drawing itself.
    DrawBezierCurve();
  }

  // This method is called whenever a field is changed on Editor
  void OnValidate()
  {
    // This avoids pairing with itself
    if (paired == this) paired = null;
  }

  void DrawBezierCurve()
  {
    lr.positionCount = numPoints;
    for (int i = 0; i < numPoints; i++)
    {
      // This corrects the "strange" extra point you had with your script.
      float t = i / (float)(numPoints - 1);
      lr.SetPosition(i, GetPoint(t));
    }
  }

  public Vector3 GetPoint(float t)
  {
    return transform.TransformPoint(Bezier.GetPoint(points[0], points[1], points[2], points[3], t));
  }

  public Vector3 GetVelocity(float t)
  {
    return transform.TransformPoint(Bezier.GetFirstDerivative(points[0], points[1], points[2], points[3], t)) - transform.position;
  }

  public Vector3 GetDirection(float t)
  {
    return GetVelocity(t).normalized;
  }
}

Note#2:所需的行为被编码在处理程序绘图方法内,因此您可以访问撤消和其他功能。

Note#3: EditorUtility.SetDirty is 被认为是过时的 https://docs.unity3d.com/ScriptReference/EditorUtility.SetDirty.html自 Unity 5.3 起用于将对象标记为脏以进行绘制,以及不应再用于修改场景中的对象. Undo.RecordObject完成工作。

贝塞尔曲线编辑器:

using UnityEngine;
using UnityEditor;

// This attribute allows you to select multiple curves and manipulate them all as a whole on Scene or Inspector
[CustomEditor(typeof(BezierCurve)), CanEditMultipleObjects]
public class BezierCurveEditor : Editor
{
  BezierCurve curve;
  Transform handleTransform;
  Quaternion handleRotation;
  const int lineSteps = 10;
  const float directionScale = 0.5f;

  BezierCurve prevPartner; // Useful later.

  void OnSceneGUI()
  {
    curve = target as BezierCurve;
    if (curve == null) return;
    handleTransform = curve.transform;
    handleRotation = Tools.pivotRotation == PivotRotation.Local ? handleTransform.rotation : Quaternion.identity;

    Vector3 p0 = ShowPoint(0);
    Vector3 p1 = ShowPoint(1);
    Vector3 p2 = ShowPoint(2);
    Vector3 p3 = ShowPoint(3);

    Handles.color = Color.gray;
    Handles.DrawLine(p0, p1);
    Handles.DrawLine(p2, p3);
    Handles.DrawBezier(p0, p3, p1, p2, Color.white, null, 2f);

    // Handles multiple selection
    var sel = Selection.GetFiltered(typeof(BezierCurve), SelectionMode.Editable);
    if (sel.Length == 1)
    {
      // This snippet checks if you just attached or dettached another curve,
      // so it updates the attached member in the other curve too automatically
      if (prevPartner != curve.paired)
      {
        if (prevPartner != null) { prevPartner.paired = null; }
        prevPartner = curve.paired;
      }
    }
    if (curve.paired != null & curve.paired != curve)
    {
      // Pair the curves.
      var partner = curve.paired;
      partner.paired = curve;
      partner.behavior1 = curve.behavior1;
      partner.behavior2 = curve.behavior2;
      partner.behavior3 = curve.behavior3;
    }
  }

  // Constraints for a curve attached to back
  // The trick here is making the object being inspected the "master" and the attached object is adjusted to it.
  // This way, you avoid the conflict of one object trying to move the other.
  // [ExecuteInEditMode] on component class makes it posible to have real-time drawing while editing.
  // If you were calling DrawBezierCurve from here, you would only see updates on the other curve when you select it
  Vector3 ShowPoint(int index)
  {
    var thisPts = curve.points;
    Vector3 point = handleTransform.TransformPoint(thisPts[index]);
    EditorGUI.BeginChangeCheck();
    point = Handles.DoPositionHandle(point, handleRotation);
    if (EditorGUI.EndChangeCheck())
    {
      if (curve.paired != null && curve.paired != curve)
      {
        Undo.RecordObjects(new Object[] { curve, curve.paired }, "Move Point " + index.ToString());
        var pairPts = curve.paired.points;
        var pairTransform = curve.paired.transform;
        switch (index)
        {
          case 0:
            {
              if (curve.behavior1)
              {
                pairPts[0] = pairTransform.InverseTransformPoint(point);
              }
              break;
            }
          case 1:
            {
              if (curve.behavior2)
              {
                var p1 = handleTransform.TransformPoint(thisPts[1]);
                pairPts[1] += pairTransform.InverseTransformVector(point - p1);
              }
              break;
            }
          case 2:
            {
              if (curve.behavior3)
              {
                var p0 = handleTransform.TransformPoint(thisPts[0]);
                var p3 = handleTransform.TransformPoint(thisPts[3]);
                var reflect = Vector3.Reflect(p3 - point, (p3 - p0).normalized);
                pairPts[2] = pairTransform.InverseTransformPoint(p3 + reflect);
              }
              break;
            }
          default:
            break;
        }
      }
      else
      {
        Undo.RecordObject(curve, "Move Point " + index.ToString());
      }
      thisPts[index] = handleTransform.InverseTransformPoint(point);
    }
    return point;
  }
}

为了使其正常工作,请参考一BezierCurve通过检查器到另一个配对字段,并设置开/关您想要的行为。

Tips: 修改属性LineRenderer获得很酷的渐变或宽度变化(如画笔描边)。如果您有一个尖点节点并希望它看起来连续,请增加End Cap Vertices在线渲染器上。使用Sprites-Default作为材质,用于 2D。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用自定义编辑器在移动另一条贝塞尔曲线时更新一条贝塞尔曲线 的相关文章

  • Volatile.Read 和 Volatile.Write 背后的逻辑是什么?

    来自 MSDN Volatile Read 读取字段的值 在需要它的系统上 插入一个 阻止处理器重新排序内存的内存屏障 操作如下 如果在该方法之后出现读或写 代码 处理器无法移动它before这个方法 and Volatile Write
  • WebClient.DownloadDataAsync 冻结了我的 UI

    我在 Form 构造函数中的 InitializeComponent 之后有以下代码 using WebClient client new WebClient client DownloadDataCompleted new Downloa
  • 如何使用 ASP.NET MVC 编辑多选列表?

    我想编辑一个如下所示的对象 我希望用 UsersGrossList 中的一个或多个用户填充 UsersSelectedList 使用 mvc 中的标准编辑视图 我只得到映射的字符串和布尔值 下面未显示 我在 google 上找到的许多示例都
  • 是否可以从 C++ 应用程序调用 C# 应用程序?

    我是一名编程学生 现在我已经上了两门 C 课程 这个学期我将参加我的第一门 C 课程 出于好奇 是否可以从 C 应用程序调用 C 应用程序 如果是的话 是否还可以检查运行该程序的计算机是否具有 NET框架 我只是很好奇 我想如果可能的话 这
  • 无法将 std::min 传递给函数,std::min 的副本有效

    Passing std min函数无法编译 我复制了 libcpp 声明std min进入我的源文件并且它可以工作 std 版本有什么问题 clang 和 gcc 也会发生同样的情况 在 Godbolt 上测试 https godbolt
  • C free() 是如何工作的? [复制]

    这个问题在这里已经有答案了 可能的重复 malloc 和 free 如何工作 https stackoverflow com questions 1119134 how malloc and free work include
  • 进程退出后 POSIX 名称信号量不会释放

    我正在尝试使用 POSIX 命名信号量进行跨进程同步 我注意到进程死亡或退出后 信号量仍然被系统打开 在进程 打开它 死亡或退出后是否有办法使其关闭 释放 早期的讨论在这里 当将信号量递减至零的进程崩溃时 如何恢复信号量 https sta
  • 将 OpenCV Mat 转换为数组(可能是 NSArray)

    我的 C C 技能很生疏 OpenCV 的文档也相当晦涩难懂 有没有办法获得cv Mat data属性转换为数组 NSArray 我想将其序列化为 JSON 我知道我可以使用 FileStorage 实用程序转换为 YAML XML 但这不
  • C++ 将联合强制转换为其成员类型之一

    以下对我来说似乎完全符合逻辑 但不是有效的 C 联合不能隐式转换为其成员类型之一 有人知道为什么不这样做的充分理由吗 union u int i char c function f int i int main u v v i 6 f v
  • 静态类与类的实例

    我有一个静态类 用于访问我的公共属性 整个应用程序的全局属性 和我在应用程序运行期间使用的方法 例如 我在静态类中设置了一些属性 并且在应用程序运行时我可以从属性中获取值 但我可以使用单例模式创建非静态类并以相同的方式使用它 问题 对于我的
  • 如何使用 Roslyn 通过扩展方法、静态类中的方法以及带有 ref/out 参数的方法来访问调用

    我正在致力于创建一个开源项目 用于创建 NET UML 序列图 该项目利用名为 js sequence diagrams 的 javascript 库 我不确定 Roslyn 是适合这项工作的工具 但我想我应该尝试一下 所以我整理了一些概念
  • 在 C# 中何时使用 ArrayList 而不是 array[]?

    我经常使用一个ArrayList而不是 正常 array 当我使用时 我感觉好像我在作弊 或懒惰 ArrayList 什么时候可以使用ArrayList在数组上 数组是强类型的 并且可以很好地用作参数 如果您知道集合的长度并且它是固定的 则
  • 子目录中的头文件(例如 gtk/gtk.h 与 gtk-2.0/gtk/gtk.h)

    我正在尝试使用 GTK 构建一个 hello world 其中包括以下行 include
  • Resharper:IEnumerable 的可能多重枚举

    我正在使用新的 Resharper 版本 6 在我的代码中的几个地方 它给一些文本加了下划线 并警告我可能存在IEnumerable 可能的多重枚举 我理解这意味着什么 并在适当的情况下采纳了建议 但在某些情况下 我不确定这实际上是一个大问
  • doxygen c++:记录由“using”声明公开的私有继承成员

    作为一个例子 我有以下课程 class A public void methodOne class B private A public Brief description using A methodOne 我还没有找到强制 doxyge
  • 使用 xslt 将 xml 转换为 xsl-fo 时动态创建超链接?

    我想使用 xsl 文件在 PDF 报告中创建标题 如果源文件包含超链接 则应将其呈现为超链接 否则呈现为纯文本 例如 我的 xml 如下所示 a href http google com target blank This is the h
  • Linq.Select() 中的嵌套表达式方法调用

    I use Select i gt new T 每次手动点击数据库后将我的实体对象转换为 DTO 对象 以下是一些示例实体和 DTOS 用户实体 public partial class User public int Id get set
  • printf或iostream如何指定点后的最大位数

    字符串采用什么格式printf or iomanip我应该使用 iostream 中的运算符以以下格式打印浮点数 125 0 gt 125 125 1 gt 125 1 125 12312 gt 125 12 1 12345 gt 1 12
  • 使用 C# 动态创建按钮并按预定义的顺序放置它们

    NET 4 5 C 创建 Windows 窗体 我想动态创建和添加按钮并为其分配单击事件 但希望它们以特定的方式动态放置 就像图像一样 我的问题是如何以上述方式动态放置按钮 即 4x4 格式 一行 4 个按钮 4 列 但行数不受限制 是否可
  • C++ Boost ASIO 简单的周期性定时器?

    我想要一个非常简单的周期性计时器每 50 毫秒调用我的代码 我可以创建一个始终休眠 50 毫秒的线程 但这很痛苦 我可以开始研究用于制作计时器的 Linux API 但它不可移植 I d like使用升压 我只是不确定这是否可能 boost

随机推荐