曲线视觉编辑器,初步
This commit is contained in:
@@ -0,0 +1,587 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using Sirenix.OdinInspector;
|
||||
using System.Linq;
|
||||
using UnityEngine.InputSystem;
|
||||
using Michsky.MUIP;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace Ichni.Editor
|
||||
{
|
||||
[RequireComponent(typeof(RawImage))]
|
||||
public class KeyframeVisualizer : MonoBehaviour
|
||||
{
|
||||
public AnimationCurve curve;
|
||||
public float timeRange = 10f;
|
||||
public float valueRange = 10f;
|
||||
public float keyframeSize = 0.2f;
|
||||
public float tangentLength = 1f;
|
||||
public Color curveColor = Color.green;
|
||||
public Color keyframeColor = Color.red;
|
||||
public Color tangentColor = Color.blue;
|
||||
public int segments = 5;
|
||||
|
||||
[Header("Curve Preview")]
|
||||
public RawImage curveRawImage;
|
||||
public Vector2Int curveTextureSize = new Vector2Int(256, 128);
|
||||
|
||||
[System.Serializable]
|
||||
public struct KeyframeImageInfo
|
||||
{
|
||||
public Image keyImage;
|
||||
public Image inTangentImage;
|
||||
public Image outTangentImage;
|
||||
}
|
||||
|
||||
public List<KeyframeImageInfo> keyframeImages = new List<KeyframeImageInfo>();
|
||||
|
||||
// 合并min/max到类成员,便于全局一致使用
|
||||
private float minTime, maxTime, minValue = 0f, maxValue = 1f;
|
||||
|
||||
private void UpdateCurveRange()
|
||||
{
|
||||
if (curve == null || curve.length < 2)
|
||||
{
|
||||
minTime = maxTime = valueRange = timeRange = 0;
|
||||
minValue = 0f;
|
||||
maxValue = 1f;
|
||||
return;
|
||||
}
|
||||
minTime = 0f;
|
||||
maxTime = 1f;
|
||||
minValue = 0f; // 固定下界为0
|
||||
maxValue = 1f; // 固定上界为1
|
||||
valueRange = Mathf.Max(0.0001f, maxValue - minValue);
|
||||
timeRange = Mathf.Max(0.0001f, maxTime - minTime);
|
||||
}
|
||||
|
||||
[Button("Draw Curve To RawImage")]
|
||||
public void DrawCurveToRawImage()
|
||||
{
|
||||
UpdateCurveRange();
|
||||
if (curveRawImage == null || curve == null || curve.length < 2) return;
|
||||
|
||||
int texWidth = curveTextureSize.x;
|
||||
int texHeight = curveTextureSize.y;
|
||||
Texture2D tex = new Texture2D(texWidth, texHeight, TextureFormat.ARGB32, false);
|
||||
tex.filterMode = FilterMode.Point;
|
||||
tex.wrapMode = TextureWrapMode.Clamp;
|
||||
|
||||
// 清空
|
||||
for (int i = 0; i < texWidth; i++)
|
||||
for (int j = 0; j < texHeight; j++)
|
||||
tex.SetPixel(i, j, new Color(0, 0, 0, 0));
|
||||
|
||||
// === 新增:绘制网格 ===
|
||||
int gridXCount = 8;
|
||||
int gridYCount = 4;
|
||||
Color gridColor = new Color(0.3f, 0.3f, 0.3f, 0.7f);
|
||||
for (int gx = 1; gx < gridXCount; gx++)
|
||||
{
|
||||
int x = gx * texWidth / gridXCount;
|
||||
DrawVerticalLineOnTexture(tex, x, 0, texHeight - 1, gridColor);
|
||||
}
|
||||
for (int gy = 1; gy < gridYCount; gy++)
|
||||
{
|
||||
int y = gy * texHeight / gridYCount;
|
||||
DrawHorizontalLineOnTexture(tex, 0, texWidth - 1, y, gridColor);
|
||||
}
|
||||
|
||||
// === 新增:绘制边框 ===
|
||||
Color borderColor = Color.white;
|
||||
DrawHorizontalLineOnTexture(tex, 0, texWidth - 1, 0, borderColor);
|
||||
DrawHorizontalLineOnTexture(tex, 0, texWidth - 1, texHeight - 1, borderColor);
|
||||
DrawVerticalLineOnTexture(tex, 0, 0, texHeight - 1, borderColor);
|
||||
DrawVerticalLineOnTexture(tex, texWidth - 1, 0, texHeight - 1, borderColor);
|
||||
|
||||
int lastY = -1;
|
||||
for (int x = 0; x < texWidth; x++)
|
||||
{
|
||||
float t = (float)x / (texWidth - 1);
|
||||
float time = Mathf.Lerp(minTime, maxTime, t);
|
||||
float value = curve.Evaluate(time);
|
||||
float normY = (value - minValue) / valueRange;
|
||||
int y = (int)(normY * (texHeight - 1));
|
||||
|
||||
bool outOfRange = y < 0 || y >= texHeight;
|
||||
int drawY = y;
|
||||
|
||||
Color drawColor = outOfRange
|
||||
? new Color(1f - curveColor.r, 1f - curveColor.g, 1f - curveColor.b, 1f)
|
||||
: curveColor;
|
||||
|
||||
if (lastY >= 0)
|
||||
{
|
||||
int y0 = lastY;
|
||||
int y1 = y;
|
||||
int x0 = x - 1;
|
||||
int x1 = x;
|
||||
// === 修正:补全断点 ===
|
||||
if (x0 >= 0 && x0 < texWidth && x1 >= 0 && x1 < texWidth)
|
||||
{
|
||||
if (Mathf.Abs(y1 - y0) > 1)
|
||||
{
|
||||
int step = y1 > y0 ? 1 : -1;
|
||||
for (int yyy = y0; yyy != y1; yyy += step)
|
||||
{
|
||||
if (yyy >= 0 && yyy < texHeight)
|
||||
tex.SetPixel(x0, yyy, curveColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
int dy = Mathf.Abs(y1 - y0);
|
||||
int sy = y0 < y1 ? 1 : -1;
|
||||
int err = dy / 2;
|
||||
int yy = y0;
|
||||
for (int xx = x0; xx <= x1; xx++)
|
||||
{
|
||||
bool segOutOfRange = yy < 0 || yy >= texHeight;
|
||||
Color segColor = segOutOfRange
|
||||
? new Color(1f - curveColor.r, 1f - curveColor.g, 1f - curveColor.b, 1f)
|
||||
: curveColor;
|
||||
if (xx >= 0 && xx < texWidth)
|
||||
{
|
||||
for (int j = lastY; j < yy; j++) tex.SetPixel(x0, j, curveColor);
|
||||
for (int j = lastY; j > yy; j--) tex.SetPixel(x0, j, curveColor);
|
||||
tex.SetPixel(xx, yy, segColor);
|
||||
}
|
||||
err -= dy;
|
||||
if (err < 0)
|
||||
{
|
||||
yy += sy;
|
||||
err += (x1 - x0);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (x >= 0 && x < texWidth)
|
||||
tex.SetPixel(x, drawY, drawColor);
|
||||
|
||||
lastY = y;
|
||||
}
|
||||
|
||||
foreach (var key in curve.keys)
|
||||
{
|
||||
float tangentScale = (maxTime - minTime) * 0.08f;
|
||||
if (!float.IsInfinity(key.inTangent))
|
||||
{
|
||||
float t0 = key.time;
|
||||
float v0 = key.value;
|
||||
float t1 = t0 - tangentScale;
|
||||
float v1 = v0 - key.inTangent * tangentScale;
|
||||
int x0 = (int)(((t0 - minTime) / timeRange) * (texWidth - 1));
|
||||
int y0 = (int)(((v0 - minValue) / valueRange) * (texHeight - 1));
|
||||
int x1 = (int)(((t1 - minTime) / timeRange) * (texWidth - 1));
|
||||
int y1 = (int)(((v1 - minValue) / valueRange) * (texHeight - 1));
|
||||
DrawLineOnTexture(tex, x0, y0, x1, y1, tangentColor);
|
||||
}
|
||||
if (!float.IsInfinity(key.outTangent))
|
||||
{
|
||||
float t0 = key.time;
|
||||
float v0 = key.value;
|
||||
float t1 = t0 + tangentScale;
|
||||
float v1 = v0 + key.outTangent * tangentScale;
|
||||
int x0 = (int)(((t0 - minTime) / timeRange) * (texWidth - 1));
|
||||
int y0 = (int)(((v0 - minValue) / valueRange) * (texHeight - 1));
|
||||
int x1 = (int)(((t1 - minTime) / timeRange) * (texWidth - 1));
|
||||
int y1 = (int)(((v1 - minValue) / valueRange) * (texHeight - 1));
|
||||
DrawLineOnTexture(tex, x0, y0, x1, y1, tangentColor * 0.8f);
|
||||
}
|
||||
}
|
||||
|
||||
tex.Apply();
|
||||
curveRawImage.texture = tex;
|
||||
}
|
||||
|
||||
private void DrawLineOnTexture(Texture2D tex, int x0, int y0, int x1, int y1, Color color)
|
||||
{
|
||||
int dx = Mathf.Abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
|
||||
int dy = Mathf.Abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
|
||||
int err = (dx > dy ? dx : -dy) / 2, e2;
|
||||
int lastY = y0;
|
||||
while (true)
|
||||
{
|
||||
for (int j = lastY; j < y0; j++) tex.SetPixel(x0, j, color);
|
||||
for (int j = lastY; j > y0; j--) tex.SetPixel(x0, j, color);
|
||||
tex.SetPixel(x0, y0, color);
|
||||
lastY = y0;
|
||||
if (x0 == x1 && y0 == y1) break;
|
||||
e2 = err;
|
||||
if (e2 > -dx) { err -= dy; x0 += sx; }
|
||||
if (e2 < dy) { err += dx; y0 += sy; }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取关键帧在RawImage中的localPosition(以RawImage中心为原点)
|
||||
/// </summary>
|
||||
public Vector2 GetKeyframeLocalPositionInRawImage(Keyframe key, float scale = 1f)
|
||||
{
|
||||
UpdateCurveRange();
|
||||
if (curveRawImage == null || curve == null || curve.length < 2)
|
||||
return Vector2.zero;
|
||||
|
||||
// 这里直接使用固定的minValue/maxValue
|
||||
float normX = (key.time - minTime) / timeRange;
|
||||
float normY = (key.value - 0f) / Mathf.Max(0.0001f, 1f - 0f);
|
||||
|
||||
float px = normX * curveTextureSize.x;
|
||||
float py = normY * curveTextureSize.y;
|
||||
|
||||
Vector2 localPos = new Vector2(
|
||||
px - curveTextureSize.x * 0.5f,
|
||||
py - curveTextureSize.y * 0.5f
|
||||
);
|
||||
return localPos * scale;
|
||||
}
|
||||
|
||||
[Button("Create Keyframe Images")]
|
||||
public void CreateKeyframeImages()
|
||||
{
|
||||
UpdateCurveRange();
|
||||
if (curveRawImage == null || curve == null || curve.length < 1) return;
|
||||
|
||||
for (int i = curveRawImage.transform.childCount - 1; i >= 0; i--)
|
||||
{
|
||||
var child = curveRawImage.transform.GetChild(i);
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying)
|
||||
DestroyImmediate(child.gameObject);
|
||||
else
|
||||
#endif
|
||||
Destroy(child.gameObject);
|
||||
}
|
||||
keyframeImages.Clear();
|
||||
|
||||
for (int i = 0; i < curve.length; i++)
|
||||
{
|
||||
Keyframe key = curve.keys[i];
|
||||
Vector2 localPos = GetKeyframeLocalPositionInRawImage(key);
|
||||
|
||||
GameObject go = new GameObject($"KeyframeImage_{i}", typeof(RectTransform), typeof(Image));
|
||||
go.transform.SetParent(curveRawImage.transform, false);
|
||||
|
||||
RectTransform rt = go.GetComponent<RectTransform>();
|
||||
rt.sizeDelta = Vector2.one * 16f;
|
||||
rt.anchoredPosition = localPos;
|
||||
|
||||
Image img = go.GetComponent<Image>();
|
||||
img.color = keyframeColor;
|
||||
img.raycastTarget = false;
|
||||
PointMover pointMover = go.AddComponent<PointMover>();
|
||||
pointMover.parent = this;
|
||||
pointMover.keyIndex = i;
|
||||
|
||||
Image inImg = null;
|
||||
if (!float.IsInfinity(key.inTangent))
|
||||
{
|
||||
Vector2 tangentLocalPos = GetTangentLocalPosition(key, true);
|
||||
GameObject tgo = new GameObject($"TangentInImage_{i}", typeof(RectTransform), typeof(Image));
|
||||
tgo.transform.SetParent(curveRawImage.transform, false);
|
||||
RectTransform trt = tgo.GetComponent<RectTransform>();
|
||||
trt.sizeDelta = Vector2.one * 10f;
|
||||
trt.anchoredPosition = tangentLocalPos;
|
||||
inImg = tgo.GetComponent<Image>();
|
||||
inImg.color = tangentColor;
|
||||
inImg.raycastTarget = false;
|
||||
PointMover pointMover1 = tgo.AddComponent<PointMover>();
|
||||
pointMover1.IO = 1;
|
||||
pointMover1.parent = this;
|
||||
pointMover1.keyIndex = i;
|
||||
pointMover.subpointMover.Add(pointMover1);
|
||||
}
|
||||
|
||||
Image outImg = null;
|
||||
if (!float.IsInfinity(key.outTangent))
|
||||
{
|
||||
Vector2 tangentLocalPos = GetTangentLocalPosition(key, false);
|
||||
GameObject tgo = new GameObject($"TangentOutImage_{i}", typeof(RectTransform), typeof(Image));
|
||||
tgo.transform.SetParent(curveRawImage.transform, false);
|
||||
RectTransform trt = tgo.GetComponent<RectTransform>();
|
||||
trt.sizeDelta = Vector2.one * 10f;
|
||||
trt.anchoredPosition = tangentLocalPos;
|
||||
outImg = tgo.GetComponent<Image>();
|
||||
outImg.color = tangentColor * 0.8f;
|
||||
outImg.raycastTarget = false;
|
||||
PointMover pointMover2 = tgo.AddComponent<PointMover>();
|
||||
pointMover2.IO = -1;
|
||||
pointMover2.parent = this;
|
||||
pointMover2.keyIndex = i;
|
||||
pointMover.subpointMover.Add(pointMover2);
|
||||
}
|
||||
|
||||
keyframeImages.Add(new KeyframeImageInfo
|
||||
{
|
||||
keyImage = img,
|
||||
inTangentImage = inImg,
|
||||
outTangentImage = outImg
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 修复后的切线位置计算方法
|
||||
private Vector2 GetTangentLocalPosition(Keyframe key, bool isIn)
|
||||
{
|
||||
Vector2 keyLocalPos = GetKeyframeLocalPositionInRawImage(key);
|
||||
float tangent = isIn ? key.inTangent : key.outTangent;
|
||||
|
||||
// 计算像素/单位转换比例
|
||||
float pixelsPerTimeUnit = curveTextureSize.x / timeRange;
|
||||
float pixelsPerValueUnit = curveTextureSize.y / valueRange;
|
||||
|
||||
// 处理无穷斜率的情况(垂直切线)
|
||||
if (float.IsInfinity(tangent))
|
||||
{
|
||||
return keyLocalPos + new Vector2(
|
||||
0,
|
||||
isIn ? -tangentLength * 20f : tangentLength * 20f
|
||||
);
|
||||
}
|
||||
|
||||
// 计算方向向量
|
||||
Vector2 direction = new Vector2(
|
||||
isIn ? -1f : 1f,
|
||||
tangent * (pixelsPerValueUnit / pixelsPerTimeUnit)
|
||||
).normalized;
|
||||
|
||||
// 动态计算像素长度
|
||||
float dynamicLength = tangentLength * Mathf.Min(curveTextureSize.x, curveTextureSize.y) * 0.1f;
|
||||
|
||||
return keyLocalPos + direction * dynamicLength;
|
||||
}
|
||||
[Button("Update Curve From Images")]
|
||||
public void UpdateCurveFromImages()
|
||||
{
|
||||
UpdateCurveRange();
|
||||
if (curve == null || keyframeImages == null || keyframeImages.Count < curve.length) return;
|
||||
|
||||
Keyframe[] newKeys = new Keyframe[curve.length];
|
||||
for (int i = 0; i < curve.length; i++)
|
||||
{
|
||||
RectTransform rt = keyframeImages[i].keyImage.rectTransform;
|
||||
Vector2 localPos = rt.anchoredPosition;
|
||||
float px = localPos.x + curveTextureSize.x * 0.5f;
|
||||
float py = localPos.y + curveTextureSize.y * 0.5f;
|
||||
float normX = px / curveTextureSize.x;
|
||||
float normY = py / curveTextureSize.y;
|
||||
float time = Mathf.Lerp(minTime, maxTime, normX);
|
||||
float value = Mathf.Clamp01(0f + normY * Mathf.Max(0.0001f, 1f - 0f));
|
||||
|
||||
float inTangent = curve.keys[i].inTangent;
|
||||
float outTangent = curve.keys[i].outTangent;
|
||||
float timeScale = timeRange / curveTextureSize.x;
|
||||
float valueScale = valueRange / curveTextureSize.y;
|
||||
|
||||
// 修复切线斜率计算
|
||||
if (keyframeImages[i].inTangentImage != null)
|
||||
{
|
||||
Vector2 inLocalPos = keyframeImages[i].inTangentImage.rectTransform.anchoredPosition;
|
||||
Vector2 keyLocalPos = keyframeImages[i].keyImage.rectTransform.anchoredPosition;
|
||||
|
||||
// 使用像素坐标差计算真实斜率
|
||||
float dx = (keyLocalPos.x - inLocalPos.x) * timeScale;
|
||||
float dy = (keyLocalPos.y - inLocalPos.y) * valueScale;
|
||||
|
||||
if (Mathf.Abs(dx) > 0.001f)
|
||||
inTangent = dy / dx;
|
||||
}
|
||||
|
||||
if (keyframeImages[i].outTangentImage != null)
|
||||
{
|
||||
Vector2 outLocalPos = keyframeImages[i].outTangentImage.rectTransform.anchoredPosition;
|
||||
Vector2 keyLocalPos = keyframeImages[i].keyImage.rectTransform.anchoredPosition;
|
||||
|
||||
// 使用像素坐标差计算真实斜率
|
||||
float dx = (outLocalPos.x - keyLocalPos.x) * timeScale;
|
||||
float dy = (outLocalPos.y - keyLocalPos.y) * valueScale;
|
||||
|
||||
if (Mathf.Abs(dx) > 0.001f)
|
||||
outTangent = dy / dx;
|
||||
}
|
||||
|
||||
Keyframe newKey = new Keyframe(time, value, inTangent, outTangent);
|
||||
newKeys[i] = newKey;
|
||||
}
|
||||
curve.keys = newKeys;
|
||||
}
|
||||
|
||||
[Button("Refresh Keyframe Images Position")]
|
||||
public void RefreshKeyframeImagesPosition()
|
||||
{
|
||||
UpdateCurveRange();
|
||||
if (curve == null || keyframeImages == null || keyframeImages.Count < curve.length) return;
|
||||
|
||||
for (int i = 0; i < curve.length; i++)
|
||||
{
|
||||
Keyframe key = curve.keys[i];
|
||||
var info = keyframeImages[i];
|
||||
|
||||
if (info.keyImage != null)
|
||||
{
|
||||
Vector2 localPos = GetKeyframeLocalPositionInRawImage(key);
|
||||
info.keyImage.rectTransform.anchoredPosition = localPos;
|
||||
}
|
||||
|
||||
if (info.inTangentImage != null && !float.IsInfinity(key.inTangent))
|
||||
{
|
||||
Vector2 tangentLocalPos = GetTangentLocalPosition(key, true);
|
||||
info.inTangentImage.rectTransform.anchoredPosition = tangentLocalPos;
|
||||
}
|
||||
|
||||
if (info.outTangentImage != null && !float.IsInfinity(key.outTangent))
|
||||
{
|
||||
Vector2 tangentLocalPos = GetTangentLocalPosition(key, false);
|
||||
info.outTangentImage.rectTransform.anchoredPosition = tangentLocalPos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public CompositeParameterWindow compositeParameterWindow;
|
||||
public void UpadteValue()
|
||||
{
|
||||
compositeParameterWindow.connectedBaseElement.GetType().GetField(compositeParameterWindow.parameterName).SetValue(compositeParameterWindow.connectedBaseElement, curve);
|
||||
}
|
||||
|
||||
// === 新增:绘制水平线辅助方法 ===
|
||||
private void DrawHorizontalLineOnTexture(Texture2D tex, int x0, int x1, int y, Color color)
|
||||
{
|
||||
int width = tex.width;
|
||||
int height = tex.height;
|
||||
if (y < 0 || y >= height) return;
|
||||
int minX = Mathf.Clamp(Mathf.Min(x0, x1), 0, width - 1);
|
||||
int maxX = Mathf.Clamp(Mathf.Max(x0, x1), 0, width - 1);
|
||||
for (int x = minX; x <= maxX; x++)
|
||||
tex.SetPixel(x, y, color);
|
||||
}
|
||||
|
||||
// === 新增:绘制垂直线辅助方法 ===
|
||||
private void DrawVerticalLineOnTexture(Texture2D tex, int x, int y0, int y1, Color color)
|
||||
{
|
||||
int width = tex.width;
|
||||
int height = tex.height;
|
||||
if (x < 0 || x >= width) return;
|
||||
int minY = Mathf.Clamp(Mathf.Min(y0, y1), 0, height - 1);
|
||||
int maxY = Mathf.Clamp(Mathf.Max(y0, y1), 0, height - 1);
|
||||
for (int y = minY; y <= maxY; y++)
|
||||
tex.SetPixel(x, y, color);
|
||||
}
|
||||
}
|
||||
|
||||
public class PointMover : MonoBehaviour
|
||||
{
|
||||
|
||||
public RectTransform rectTransform;
|
||||
public KeyframeVisualizer parent;
|
||||
public int keyIndex = -1; // 记录所属关键帧索引
|
||||
public PointMover parentPointMover;
|
||||
public List<PointMover> subpointMover = new();
|
||||
public bool Pressed;
|
||||
public int IO = 0;//0关键帧 1in -1out
|
||||
|
||||
private Dictionary<PointMover, Vector2> initialOffsets; // 存储切线点初始偏移
|
||||
private Vector2 startPosition; // 拖拽开始位置
|
||||
|
||||
private void Start()
|
||||
{
|
||||
rectTransform = gameObject.GetComponent<RectTransform>();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (RectTransformUtility.RectangleContainsScreenPoint(rectTransform, Mouse.current.position.ReadValue()))
|
||||
{
|
||||
if (Mouse.current.leftButton.wasPressedThisFrame)
|
||||
{
|
||||
StartCoroutine(Moving());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator Moving()
|
||||
{
|
||||
var windowDragger = parent.compositeParameterWindow.GetComponent<WindowDragger>();
|
||||
if (windowDragger != null)
|
||||
{
|
||||
windowDragger.Lock = true;
|
||||
}
|
||||
|
||||
// 获取边界尺寸
|
||||
float halfWidth = parent.curveTextureSize.x * 0.5f;
|
||||
float halfHeight = parent.curveTextureSize.y * 0.5f;
|
||||
|
||||
// 如果是切线点,记录初始位置和方向
|
||||
Vector2 initialPosition = rectTransform.anchoredPosition;
|
||||
Vector2 initialDirection = Vector2.zero;
|
||||
if (IO != 0)
|
||||
{
|
||||
PointMover keyPoint = parentPointMover != null ? parentPointMover : this;
|
||||
Vector2 keyPos = keyPoint.rectTransform.anchoredPosition;
|
||||
initialDirection = (initialPosition - keyPos).normalized;
|
||||
}
|
||||
|
||||
while (Mouse.current.leftButton.isPressed)
|
||||
{
|
||||
Vector2 mouseDelta = Mouse.current.delta.ReadValue();
|
||||
Vector2 newPos = rectTransform.anchoredPosition + mouseDelta;
|
||||
|
||||
// 边界约束
|
||||
newPos.x = Mathf.Clamp(newPos.x, -halfWidth, halfWidth);
|
||||
newPos.y = Mathf.Clamp(newPos.y, -halfHeight, halfHeight);
|
||||
|
||||
// 如果是切线点,约束移动方向
|
||||
if (IO != 0 && initialDirection != Vector2.zero)
|
||||
{
|
||||
Vector2 keyPos = parentPointMover.rectTransform.anchoredPosition;
|
||||
Vector2 toNewPos = newPos - keyPos;
|
||||
|
||||
// 计算与初始方向的夹角
|
||||
float angle = Vector2.Angle(initialDirection, toNewPos);
|
||||
|
||||
// 如果偏离初始方向超过45度,修正方向
|
||||
if (angle > 45f)
|
||||
{
|
||||
// 投影到初始方向
|
||||
float dot = Vector2.Dot(toNewPos, initialDirection);
|
||||
newPos = keyPos + initialDirection * Mathf.Sign(dot) * toNewPos.magnitude;
|
||||
}
|
||||
}
|
||||
|
||||
rectTransform.anchoredPosition = newPos;
|
||||
|
||||
// 如果是关键帧点,同时移动切线点
|
||||
if (IO == 0)
|
||||
{
|
||||
foreach (PointMover tangentPoint in subpointMover)
|
||||
{
|
||||
if (tangentPoint != null)
|
||||
{
|
||||
tangentPoint.rectTransform.anchoredPosition += mouseDelta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
if (windowDragger != null)
|
||||
{
|
||||
windowDragger.Lock = false;
|
||||
}
|
||||
UpdateParentCurve();
|
||||
}
|
||||
|
||||
public void UpdateParentCurve()
|
||||
{
|
||||
parent.UpdateCurveFromImages();
|
||||
parent.DrawCurveToRawImage();
|
||||
parent.UpadteValue();
|
||||
parent.CreateKeyframeImages();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5e4743327f32eb24e86090ec474ac91a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user