- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
这是来自 How to make individual anchor points of bezier continuous or non-continuous 的后续问题.请参阅它以获取已接受答案中的相关代码(请注意,我这样做是为了保持这个问题的清洁,因为相关代码很长)。
我正在努力实现以下目标:
最佳答案
我迟到了吗?
Make the bezier curve handles/control points selectable in such a way that the properties (for example continuity) for an individual handle are displayed in the inspector window when selected. Please note I'd like this to be done without making creating game objects for the handles/ control points
Handles.Button
您必须单击一个点来选择它,然后单击并拖动以移动控制点。
Handles.FreeMoveHandle
,但是有一个变量来记住最后点击的句柄的 id,所以我可以识别它。
FreeMoveHandle
,例如,返回其翻译的增量,仅此而已。问题是:你想捕获一个简单的点击,但是如果你只是点击并没有拖动,返回值是
Vector3.zero
这就像你根本没有点击一样。
controlID
的参数。 - 它是每个可交互 GUI 对象的标识符。如果你抑制它,引擎会选择一个,你永远不知道是什么。但是,如果您传递一个 int,则该值将是句柄的 id。但是,如果我通过一个 int 并且它碰巧与我看不到的任何其他 id 冲突?嗯,你可以调用
GUIUtility.GetControlID
获得一个安全的身份证。
EditorGUIUtility.hotControl
相同(即被点击或获得键盘焦点的控件),然后我将这个点的索引保存在
selectedControlPointId
并使用它在 Inspector 中显示自定义属性。
Retain a single method that handles the movement of each point instead of having separate methods for the movement of each point.
smooth
)和选择之类的东西时,它们就变成了不同的野兽,具有不同的逻辑。即使你想做
ControlPoint
一个
struct
(就像我现在所做的那样)并将其作为一个整体传递,您仍然需要指出您要更新的组件,因此约束将适用于其他组件 - 您将始终需要一个“主”字段以避免循环更新(您更改
tangentBack
,这使得
tangentFront
更新,触发
tangentBack
再次更新等等)。
ControlPoint
方法并使其成为
struct
,我无法制作一个逻辑来绘制节点和切线。
using System;
using UnityEngine;
[Serializable]
public struct ControlPoint
{
public Vector2 position;
public Vector2 tangentBack;
public Vector2 tangentFront;
public bool smooth;
static public ControlPoint MovePosition(ControlPoint pt, Vector2 newPos)
{
var newPt = pt;
newPt.position = newPos;
return newPt;
}
static public ControlPoint MoveTangentBack(ControlPoint pt, Vector2 newTanBack)
{
var newPt = pt;
newPt.tangentBack = newTanBack;
if (pt.smooth) newPt.tangentFront = pt.tangentFront.magnitude * -newTanBack.normalized;
return newPt;
}
static public ControlPoint MoveTangentFront(ControlPoint pt, Vector2 newTanFront)
{
var newPt = pt;
newPt.tangentFront = newTanFront;
if (pt.smooth) newPt.tangentBack = pt.tangentBack.magnitude * -newTanFront.normalized;
return newPt;
}
static public ControlPoint WithSmooth(ControlPoint pt, bool smooth)
{
var newPt = pt;
if (smooth != pt.smooth) newPt.tangentBack = -pt.tangentFront;
return newPt;
}
public ControlPoint(Vector2 position, Vector2 tanBack, Vector2 tanFront, bool smooth = false)
{
this.position = position;
this.tangentBack = tanBack;
this.tangentFront = tanFront;
this.smooth = smooth;
}
}
ControlPointDrawer
,因此您添加的其他属性不会隐藏在检查器中。
using System;
using UnityEngine;
using System.Collections.Generic;
[Serializable]
public class Path
{
[SerializeField] List<ControlPoint> _points;
[SerializeField] bool _loop = false;
public Path(Vector2 position)
{
_points = new List<ControlPoint>
{
new ControlPoint(position, -Vector2.one, Vector2.one),
new ControlPoint(position + Vector2.right, -Vector2.one, Vector2.one)
};
}
public bool loop { get { return _loop; } set { _loop = value; } }
public ControlPoint this[int i]
{
get { return _points[(_loop && i == _points.Count) ? 0 : i]; }
set { _points[(_loop && i == _points.Count) ? 0 : i] = value; }
}
public int NumPoints { get { return _points.Count; } }
public int NumSegments { get { return _points.Count - (_loop ? 0 : 1); } }
public ControlPoint InsertPoint(int i, Vector2 position)
{
_points.Insert(i, new ControlPoint(position, -Vector2.one, Vector2.one));
return this[i];
}
public ControlPoint RemovePoint(int i)
{
var item = this[i];
_points.RemoveAt(i);
return item;
}
public Vector2[] GetBezierPointsInSegment(int i)
{
var pointBack = this[i];
var pointFront = this[i + 1];
return new Vector2[4]
{
pointBack.position,
pointBack.position + pointBack.tangentFront,
pointFront.position + pointFront.tangentBack,
pointFront.position
};
}
public ControlPoint MovePoint(int i, Vector2 position)
{
this[i] = ControlPoint.MovePosition(this[i], position);
return this[i];
}
public ControlPoint MoveTangentBack(int i, Vector2 position)
{
this[i] = ControlPoint.MoveTangentBack(this[i], position);
return this[i];
}
public ControlPoint MoveTangentFront(int i, Vector2 position)
{
this[i] = ControlPoint.MoveTangentFront(this[i], position);
return this[i];
}
}
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(PathCreator))]
public class PathCreatorEditor : Editor
{
PathCreator creator;
Path path;
SerializedProperty property;
static int selectedControlPointId = -1;
public override void OnInspectorGUI()
{
serializedObject.Update();
var loopProp = property.FindPropertyRelative("_loop");
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(loopProp);
var ptsProp = property.FindPropertyRelative("_points");
var msg = "Total points in path: " + ptsProp.arraySize + "\n";
if (selectedControlPointId >= 0 && ptsProp.arraySize > 0)
{
EditorGUILayout.HelpBox(msg + "Selected control point: " + selectedControlPointId, MessageType.Info);
EditorGUILayout.Separator();
EditorGUILayout.PropertyField(ptsProp.GetArrayElementAtIndex(selectedControlPointId), true);
}
else
{
EditorGUILayout.HelpBox(msg + "No control points selected", MessageType.Info);
}
if (EditorGUI.EndChangeCheck()) serializedObject.ApplyModifiedProperties();
}
void OnSceneGUI()
{
Input();
Draw();
}
void Input()
{
Event guiEvent = Event.current;
Vector2 mousePos = HandleUtility.GUIPointToWorldRay(guiEvent.mousePosition).origin;
mousePos = creator.transform.InverseTransformPoint(mousePos);
if (guiEvent.type == EventType.MouseDown && guiEvent.button == 0 && guiEvent.shift)
{
Undo.RecordObject(creator, "Insert point");
path.InsertPoint(path.NumPoints, mousePos);
}
else if (guiEvent.type == EventType.MouseDown && guiEvent.button == 0 && guiEvent.control)
{
for (int i = 0; i < path.NumPoints; i++)
{
if (Vector2.Distance(mousePos, path[i].position) <= .25f)
{
Undo.RecordObject(creator, "Remove point");
path.RemovePoint(i);
break;
}
}
}
}
void Draw()
{
Handles.matrix = creator.transform.localToWorldMatrix;
var rot = Quaternion.Inverse(creator.transform.rotation) * Tools.handleRotation;
var snap = Vector2.zero;
Handles.CapFunction cap = Handles.DotHandleCap;
for (int i = 0; i < path.NumPoints; i++)
{
float size;
var pos = path[i].position;
size = HandleUtility.GetHandleSize(pos) * .05f;
Handles.Label(pos, i.ToString());
Handles.color = i == selectedControlPointId ? Handles.selectedColor : Color.red;
int ctrlId = GUIUtility.GetControlID(FocusType.Passive);
Vector2 newPos = Handles.FreeMoveHandle(ctrlId, pos, rot, size, snap, cap);
if (ctrlId == EditorGUIUtility.hotControl) selectedControlPointId = i;
if (pos != newPos)
{
Undo.RecordObject(creator, "Move point position");
path.MovePoint(i, newPos);
}
pos = newPos;
Handles.color = Color.black;
if (path.loop || i != 0)
{
var tanBack = pos + path[i].tangentBack;
Handles.DrawLine(pos, tanBack);
size = HandleUtility.GetHandleSize(tanBack) * .03f;
Vector2 newTanBack = Handles.FreeMoveHandle(tanBack, rot, size, snap, cap);
if (tanBack != newTanBack)
{
Undo.RecordObject(creator, "Move point tangent");
path.MoveTangentBack(i, newTanBack - pos);
}
}
if (path.loop || i != path.NumPoints - 1)
{
var tanFront = pos + path[i].tangentFront;
Handles.DrawLine(pos, tanFront);
size = HandleUtility.GetHandleSize(tanFront) * .03f;
Vector2 newTanFront = Handles.FreeMoveHandle(tanFront, rot, size, snap, cap);
if (tanFront != newTanFront)
{
Undo.RecordObject(creator, "Move point tangent");
path.MoveTangentFront(i, newTanFront - pos);
}
}
}
Repaint();
}
[DrawGizmo(GizmoType.Selected | GizmoType.NonSelected | GizmoType.Pickable)]
static void DrawGizmo(PathCreator creator, GizmoType gizmoType)
{
Gizmos.matrix = creator.transform.localToWorldMatrix;
var path = creator.path;
for (int i = 0; i < path.NumSegments; i++)
{
Vector2[] points = path.GetBezierPointsInSegment(i);
var pts = Handles.MakeBezierPoints(points[0], points[3], points[1], points[2], 30);
Gizmos.color = Color.green;
for (int j = 0; j < pts.Length - 1; j++)
{
Gizmos.DrawLine(pts[j], pts[j + 1]);
}
}
}
void OnEnable()
{
creator = (PathCreator)target;
path = creator.path ?? creator.CreatePath();
property = serializedObject.FindProperty("path");
}
}
OnSceneGui
移动了贝塞尔线的绘图。至
DrawGizmo
,因此即使对象没有被选中,绿线也将可见,并且可以在场景编辑器中拾取它,以便选中它。
关于unity3d - 如何使编辑器句柄可选择以显示属性检查器窗口,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51238340/
你能比较一下属性吗 我想禁用文本框“txtName”。有两种方式 使用javascript,txtName.disabled = true 使用 ASP.NET, 哪种方法更好,为什么? 最佳答案 我
Count 属性 返回一个集合或 Dictionary 对象包含的项目数。只读。 object.Count object 可以是“应用于”列表中列出的任何集合或对
CompareMode 属性 设置并返回在 Dictionary 对象中比较字符串关键字的比较模式。 object.CompareMode[ = compare] 参数
Column 属性 只读属性,返回 TextStream 文件中当前字符位置的列号。 object.Column object 通常是 TextStream 对象的名称。
AvailableSpace 属性 返回指定的驱动器或网络共享对于用户的可用空间大小。 object.AvailableSpace object 应为 Drive 
Attributes 属性 设置或返回文件或文件夹的属性。可读写或只读(与属性有关)。 object.Attributes [= newattributes] 参数 object
AtEndOfStream 属性 如果文件指针位于 TextStream 文件末,则返回 True;否则如果不为只读则返回 False。 object.A
AtEndOfLine 属性 TextStream 文件中,如果文件指针指向行末标记,就返回 True;否则如果不是只读则返回 False。 object.AtEn
RootFolder 属性 返回一个 Folder 对象,表示指定驱动器的根文件夹。只读。 object.RootFolder object 应为 Dr
Path 属性 返回指定文件、文件夹或驱动器的路径。 object.Path object 应为 File、Folder 或 Drive 对象的名称。 说明 对于驱动器,路径不包含根目录。
ParentFolder 属性 返回指定文件或文件夹的父文件夹。只读。 object.ParentFolder object 应为 File 或 Folder 对象的名称。 说明 以下代码
Name 属性 设置或返回指定的文件或文件夹的名称。可读写。 object.Name [= newname] 参数 object 必选项。应为 File 或&
Line 属性 只读属性,返回 TextStream 文件中的当前行号。 object.Line object 通常是 TextStream 对象的名称。 说明 文件刚
Key 属性 在 Dictionary 对象中设置 key。 object.Key(key) = newkey 参数 object 必选项。通常是 Dictionary 
Item 属性 设置或返回 Dictionary 对象中指定的 key 对应的 item,或返回集合中基于指定的 key 的&
IsRootFolder 属性 如果指定的文件夹是根文件夹,返回 True;否则返回 False。 object.IsRootFolder object 应为&n
IsReady 属性 如果指定的驱动器就绪,返回 True;否则返回 False。 object.IsReady object 应为 Drive&nbs
FreeSpace 属性 返回指定的驱动器或网络共享对于用户的可用空间大小。只读。 object.FreeSpace object 应为 Drive 对象的名称。
FileSystem 属性 返回指定的驱动器使用的文件系统的类型。 object.FileSystem object 应为 Drive 对象的名称。 说明 可
Files 属性 返回由指定文件夹中所有 File 对象(包括隐藏文件和系统文件)组成的 Files 集合。 object.Files object&n
我是一名优秀的程序员,十分优秀!