unity脚本收集

This commit is contained in:
Abin
2025-08-01 10:50:57 +08:00
parent ab57544ef5
commit d88804cd63
16 changed files with 1325 additions and 1 deletions
+43
View File
@@ -0,0 +1,43 @@
using UnityEditor;
using UnityEngine;
public class AddParentToSelected : Editor
{
[MenuItem("Tools/Add Parent To Selected Objects")]
public static void AddParentForSelectedObjects()
{
GameObject[] selectedObjects = Selection.gameObjects;
if (selectedObjects.Length == 0)
{
Debug.LogWarning("No objects selected. Please select objects in the Hierarchy.");
return;
}
foreach (GameObject obj in selectedObjects)
{
int siblingIndex = obj.transform.GetSiblingIndex();
Transform parent = obj.transform.parent;
GameObject newParent = new GameObject($"{obj.name}_Root");
Undo.RegisterCreatedObjectUndo(newParent, "Create Parent Object");
if (parent != null)
{
newParent.transform.SetParent(parent);
}
newParent.transform.position = obj.transform.position;
newParent.transform.rotation = obj.transform.rotation;
newParent.transform.localScale = obj.transform.localScale;
newParent.transform.position = obj.transform.position;
Undo.SetTransformParent(obj.transform, newParent.transform, "Set New Parent");
newParent.transform.SetSiblingIndex(siblingIndex);
obj.transform.localRotation = Quaternion.identity;
obj.transform.localScale = Vector3.one;
}
Debug.Log("New parent objects created for selected objects.");
}
}
+52
View File
@@ -0,0 +1,52 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Linq;
public class ChildSort : Editor
{
[MenuItem("Tools/Sort Children By Name")]
private static void SortChildrenByNameMenu()
{
// 获取当前选中的物体
GameObject selected = Selection.activeGameObject;
if (selected == null)
{
Debug.LogError("请先选择一个父物体!");
return;
}
// 获取所有子物体
List<Transform> children = new List<Transform>();
foreach (Transform child in selected.transform)
{
children.Add(child);
}
// 排序逻辑(按名字中的数字)
children = children.OrderBy(child =>
{
string name = child.name;
int number = ExtractNumber(name);
return number;
}).ToList();
// 按排序结果重新设置子物体的层级
for (int i = 0; i < children.Count; i++)
{
children[i].SetSiblingIndex(i);
}
Debug.Log($"已对 {selected.name} 下的子物体按名字排序!");
}
static int ExtractNumber(string name)
{
int number = 0;
string digits = new string(name.Where(char.IsDigit).ToArray());
int.TryParse(digits, out number);
return number;
}
}
+94
View File
@@ -0,0 +1,94 @@
using UnityEngine;
using UnityEditor;
using UnityEngine.Playables;
using System.Collections.Generic;
[ExecuteAlways]
public class EditorTimelinePathTracker : MonoBehaviour
{
public Color pathColor = Color.green;
public int maxPoints = 1000;
private List<Vector3> pathPoints = new List<Vector3>();
private List<float> timeStamps = new List<float>();
private Vector3 lastPosition;
private PlayableDirector director;
private void OnEnable()
{
director = GetComponent<PlayableDirector>();
#if UNITY_EDITOR
EditorApplication.update += EditorUpdate;
#endif
pathPoints.Clear();
timeStamps.Clear();
lastPosition = transform.position;
pathPoints.Add(lastPosition);
timeStamps.Add(GetTimelineTime());
}
private void OnDisable()
{
#if UNITY_EDITOR
EditorApplication.update -= EditorUpdate;
#endif
}
private float GetTimelineTime()
{
if (director != null && director.playableGraph.IsValid())
{
return (float)director.time;
}
return Time.realtimeSinceStartup; // fallback 时间戳
}
private float GetTimelineDuration()
{
return director != null ? (float)director.duration : -1f;
}
private void EditorUpdate()
{
if (Application.isPlaying)
return;
Vector3 currentPosition = transform.position;
float currentTime = GetTimelineTime();
if (Vector3.Distance(currentPosition, lastPosition) > 0.01f)
{
lastPosition = currentPosition;
pathPoints.Add(currentPosition);
timeStamps.Add(currentTime);
if (pathPoints.Count > maxPoints)
{
pathPoints.RemoveAt(0);
timeStamps.RemoveAt(0);
}
SceneView.RepaintAll();
}
}
private void OnDrawGizmos()
{
if (pathPoints == null || pathPoints.Count < 2)
return;
float timelineDuration = GetTimelineDuration();
bool useTimeline = director != null && timelineDuration > 0;
Gizmos.color = pathColor;
for (int i = 1; i < pathPoints.Count; i++)
{
// 若启用Timeline,仅绘制在时间范围内的轨迹
if (useTimeline && (timeStamps[i - 1] > timelineDuration || timeStamps[i] > timelineDuration))
continue;
Gizmos.DrawLine(pathPoints[i - 1], pathPoints[i]);
}
}
}
@@ -0,0 +1,61 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Playables;
using TMPro;
public class NumberScrollPlayableBehaviour : PlayableBehaviour
{
public TMP_Text textMesh;
public int startValue;
public int endValue;
private double clipDuration;
public override void OnGraphStart(Playable playable)
{
clipDuration = playable.GetDuration();
}
public override void OnGraphStop(Playable playable)
{
if (textMesh != null)
{
textMesh.text = endValue.ToString();
}
}
public override void OnBehaviourPlay(Playable playable, FrameData info)
{
if (textMesh != null)
{
textMesh.text = startValue.ToString();
}
}
public override void OnBehaviourPause(Playable playable, FrameData info)
{
if (textMesh != null && playable.GetTime() >= clipDuration)
{
textMesh.text = endValue.ToString();
}
}
public override void ProcessFrame(Playable playable, FrameData info, object playerData)
{
if (textMesh == null)
{
return;
}
double normalizedTime = playable.GetTime() / clipDuration;
float currentValue = Mathf.Lerp(startValue, endValue, (float)normalizedTime);
textMesh.text = Mathf.FloorToInt(currentValue).ToString();
// Ensure the text is set to the end value if the normalized time is 1.0 or more
if (normalizedTime >= 1.0)
{
textMesh.text = endValue.ToString();
}
}
}
+31
View File
@@ -0,0 +1,31 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Playables;
[RequireComponent(typeof(TextMesh))]
public class NumberScrollPlayable : MonoBehaviour, INotificationReceiver
{
public PlayableDirector director;
public void OnNotify(Playable origin, INotification notification, object context)
{
}
void OnEnable()
{
if (director != null)
{
director.Play();
}
}
void Update()
{
if (director != null && director.state != PlayState.Playing)
{
director.Play();
}
}
}
@@ -0,0 +1,37 @@
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
using TMPro;
[System.Serializable]
public class NumberScrollPlayableAsset : PlayableAsset
{
public ExposedReference<TMP_Text> textMesh;
public int startValue;
public int endValue;
public ClipCaps clipCaps
{
get { return ClipCaps.None; }
}
public override Playable CreatePlayable(PlayableGraph graph, GameObject owner)
{
var playable = ScriptPlayable<NumberScrollPlayableBehaviour>.Create(graph);
NumberScrollPlayableBehaviour behaviour = playable.GetBehaviour();
behaviour.textMesh = textMesh.Resolve(graph.GetResolver());
behaviour.startValue = startValue;
behaviour.endValue = endValue;
return playable;
}
}
[System.Serializable]
public class NumberScrollBehaviour : PlayableBehaviour
{
public float startValue;
public float endValue;
}
+14
View File
@@ -0,0 +1,14 @@
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
[TrackColor(0.1f, 0.2f, 0.8f)]
[TrackClipType(typeof(NumberScrollPlayableAsset))]
[TrackBindingType(typeof(TextMesh))]
public class NumberScrollTrack : TrackAsset
{
public override Playable CreateTrackMixer(PlayableGraph graph, GameObject go, int inputCount)
{
return ScriptPlayable<NumberScrollPlayableBehaviour>.Create(graph, inputCount);
}
}
@@ -0,0 +1,96 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode] // 让脚本在编辑器模式下也能执行
public class ParticleAttractionPoint : MonoBehaviour
{
[Header("吸附参数")]
[Tooltip("粒子吸附目标点")]
public Transform attractionPoint; // 吸附目标点
[Tooltip("粒子生成多久后开始进行吸附")]
[Range(0.0f, 100.0f)]
public float delayTime = 0.0f; // 延迟时间
[Tooltip("粒子开始进行吸附计算后的大小变化曲线")]
public AnimationCurve SizeCurve = new AnimationCurve(); // 控制移动的曲线
[Tooltip("是否使用距离比例计算")]
public bool UseDistance = false;
[Header("逐帧计算")]
public float attractionSpeed=50.0f;
[Header("距离比例计算")]
[Range(0.001f, 2.0f)] // 限制时间缩放的最大值和最小值
public float timeScale = 1.0f; // 时间缩放
private ParticleSystem particleSystemComponent;
private ParticleSystem.Particle[] particles;
void OnEnable() // 改用OnEnable替代Start,这样在编辑器中也能初始化
{
particleSystemComponent = GetComponent<ParticleSystem>();
particles = new ParticleSystem.Particle[particleSystemComponent.main.maxParticles];
}
void LateUpdate()
{
if(!particleSystemComponent) return; // 添加空检查
int particleCount = particleSystemComponent.GetParticles(particles);
if(attractionPoint == null) return; // 添加目标点空检查
for (int i = 0; i < particleCount; i++)
{
float lifePercent = 1.0f - (particles[i].remainingLifetime / particles[i].startLifetime);
// 只有当粒子存在时间超过延迟时间才开始移动
if(particles[i].startLifetime - particles[i].remainingLifetime >= delayTime)
{
//暂存粒子位置
Vector3 startPos=particles[i].position;
if (UseDistance)
{
// 位置计算方法1 重新计算剩余的距离比例
float adjustedLifePercent = (particles[i].startLifetime - particles[i].remainingLifetime - delayTime) /
(particles[i].startLifetime - delayTime);
// 根据距离比例计算位置
particles[i].position = Vector3.Lerp(particles[i].position, attractionPoint.position, adjustedLifePercent*timeScale);
} else
{
// 位置计算方法2 朝向吸附点移动
Vector3 direction = (attractionPoint.position - particles[i].position).normalized;
particles[i].position += direction * attractionSpeed * Time.deltaTime;
}
// 修正粒子size
float distancePercent=Vector3.Distance(startPos, particles[i].position) / Vector3.Distance(startPos, attractionPoint.position);
particles[i].startSize = particles[i].startSize * SizeCurve.Evaluate(distancePercent);
// 当粒子到达目标点时立即消失,可能会有大量粒子,引起优化问题
// if(Vector3.Distance(particles[i].position, attractionPoint.position) <= 0.9f)
// {
// particles[i].remainingLifetime = 0.0f;
// }
// // 优化方法1 通过使用SqrMagnitude,可以避免开方运算
// if (Vector3.SqrMagnitude(particles[i].position - attractionPoint.position) <= 0.9f * 0.9f)
// {
// particles[i].remainingLifetime = 0.0f;
// }
// 优化方法2 使用Mathf.Approximately进行比较
if (Mathf.Approximately(Vector3.Distance(particles[i].position, attractionPoint.position), 0.0f) ||
Vector3.Distance(particles[i].position, attractionPoint.position) <= 0.9f)
{
particles[i].remainingLifetime = 0.0f;
}
}
}
particleSystemComponent.SetParticles(particles, particleCount);
}
}
@@ -0,0 +1,106 @@
using UnityEngine;
using UnityEngine.Playables;
using UnityEditor;
using System.Collections.Generic;
using UnityEngine.Timeline;
using System.Linq;
public class TimelineBindingCopier : Editor
{
private const string SourcePlayableDirectorKey = "SourcePlayableDirector"; // EditorPrefs 存储源 PlayableDirector 的键
[MenuItem("CONTEXT/PlayableDirector/Copy Bindings")]
public static void CopyBindings()
{
PlayableDirector source = Selection.activeGameObject?.GetComponent<PlayableDirector>();
if (source == null)
{
Debug.LogError("Please select a PlayableDirector to copy bindings from.");
return;
}
// 保存当前选择的 PlayableDirector 到 EditorPrefs
EditorPrefs.SetString(SourcePlayableDirectorKey, source.gameObject.name);
Debug.Log($"Source PlayableDirector '{source.name}' saved. Now select the target PlayableDirector.");
}
[MenuItem("CONTEXT/PlayableDirector/Paste Bindings")]
public static void PasteBindings()
{
PlayableDirector target = Selection.activeGameObject?.GetComponent<PlayableDirector>();
if (target == null)
{
Debug.LogError("Please select a PlayableDirector to paste bindings to.");
return;
}
string sourceObjectName = EditorPrefs.GetString(SourcePlayableDirectorKey, "");
if (string.IsNullOrEmpty(sourceObjectName))
{
Debug.LogError("No source PlayableDirector saved. Please use 'Copy Bindings' first.");
return;
}
GameObject sourceObject = GameObject.Find(sourceObjectName);
if (sourceObject == null)
{
Debug.LogError($"Source PlayableDirector '{sourceObjectName}' not found. Please copy bindings again.");
return;
}
PlayableDirector source = sourceObject.GetComponent<PlayableDirector>();
if (source == null)
{
Debug.LogError($"Source PlayableDirector '{sourceObjectName}' does not have a PlayableDirector component.");
return;
}
var sourceAsset = source.playableAsset as TimelineAsset;
var targetAsset = target.playableAsset as TimelineAsset;
if (sourceAsset == null || targetAsset == null)
{
Debug.LogError("Source or Target PlayableDirector does not contain a valid Timeline.");
return;
}
// Paste bindings based on track indices
PasteBindingsInternal(source, target, sourceAsset, targetAsset);
}
private static void PasteBindingsInternal(PlayableDirector source, PlayableDirector target, TimelineAsset sourceAsset, TimelineAsset targetAsset)
{
var sourceTracks = sourceAsset.GetOutputTracks().ToArray(); // Convert to array
var targetTracks = targetAsset.GetOutputTracks().ToArray(); // Convert to array
if (sourceTracks.Length != targetTracks.Length)
{
Debug.LogError("Source and Target timelines have different number of tracks.");
return;
}
for (int i = 0; i < sourceTracks.Length; i++)
{
TrackAsset sourceTrack = sourceTracks[i];
TrackAsset targetTrack = targetTracks[i];
if (sourceTrack == null || targetTrack == null)
{
Debug.Log($"Skipping track at index {i} because it is null.");
continue;
}
var sourceBinding = source.GetGenericBinding(sourceTrack);
if (sourceBinding != null)
{
// Set the binding for the target PlayableDirector
target.SetGenericBinding(targetTrack, sourceBinding);
Debug.Log($"Binding pasted for track '{sourceTrack.name}' (Index {i}).");
}
else
{
Debug.Log($"No binding found for track '{sourceTrack.name}' (Index {i}), skipping...");
}
}
}
}
+50
View File
@@ -0,0 +1,50 @@
using UnityEditor;
using UnityEngine;
using System.Linq;
public class RenameSelectedObjects : EditorWindow
{
private string baseName = "NewObject";
[MenuItem("Tools/Rename Selected Objects")]
public static void ShowWindow()
{
GetWindow<RenameSelectedObjects>("Rename Objects");
}
private void OnGUI()
{
GUILayout.Label("Rename Selected Objects", EditorStyles.boldLabel);
baseName = EditorGUILayout.TextField("Base Name", baseName);
if (GUILayout.Button("Rename"))
{
RenameObjects();
}
}
private void RenameObjects()
{
var selectedObjects = Selection.gameObjects;
if (selectedObjects.Length == 0)
{
EditorUtility.DisplayDialog("No Objects Selected", "Please select at least one object in the Hierarchy.", "OK");
return;
}
// Sort selected objects by their sibling index (Hierarchy order)
var sortedObjects = selectedObjects
.OrderBy(obj => obj.transform.GetSiblingIndex())
.ToArray();
Undo.RecordObjects(sortedObjects, "Rename Objects");
for (int i = 0; i < sortedObjects.Length; i++)
{
sortedObjects[i].name = $"{baseName} ({i + 1})";
}
EditorUtility.DisplayDialog("Rename Complete", $"Renamed {sortedObjects.Length} objects.", "OK");
}
}
@@ -0,0 +1,71 @@
using UnityEngine;
using UnityEditor;
public class ReplaceWithPrefabWindow : EditorWindow
{
private GameObject _prefab;
private bool inheritPosition = true;
private bool inheritScale = true;
private bool inheritRotation = true;
[MenuItem("Tools/Replace Selected with Prefab")]
static void ShowWindow()
{
GetWindow<ReplaceWithPrefabWindow>("Replace With Prefab");
}
private void OnGUI()
{
GUILayout.Label("Select Prefab to Replace With", EditorStyles.boldLabel);
_prefab = (GameObject)EditorGUILayout.ObjectField("Prefab", _prefab, typeof(GameObject), false);
inheritPosition = EditorGUILayout.Toggle("Inherit Position", inheritPosition);
inheritRotation = EditorGUILayout.Toggle("Inherit Rotation", inheritRotation);
inheritScale = EditorGUILayout.Toggle("Inherit Scale", inheritScale);
if (GUILayout.Button("Replace"))
{
ReplaceSelectedObjects();
}
}
private void ReplaceSelectedObjects()
{
if (_prefab == null)
{
Debug.LogWarning("No prefab selected.");
return;
}
if (Selection.gameObjects.Length == 0)
{
Debug.LogWarning("No objects selected.");
return;
}
GameObject[] selectedObjects = Selection.gameObjects;
foreach (GameObject selectedObject in selectedObjects)
{
// 创建新对象并记录到撤销历史
GameObject newObject = (GameObject)PrefabUtility.InstantiatePrefab(_prefab);
Undo.RegisterCreatedObjectUndo(newObject, "Replace with Prefab");
newObject.transform.SetParent(selectedObject.transform.parent);
// 继承属性
if (inheritPosition)
newObject.transform.position = selectedObject.transform.position;
if (inheritRotation)
newObject.transform.rotation = selectedObject.transform.rotation;
if (inheritScale)
newObject.transform.localScale = selectedObject.transform.localScale;
// 删除原始对象并记录到撤销历史
Undo.DestroyObjectImmediate(selectedObject);
}
}
}
+192
View File
@@ -0,0 +1,192 @@
using UnityEngine;
/// <summary>
/// 使用 LineRenderer 绘制绳索,通过简化的质点-弹簧系统模拟绳索形态。
/// 支持物理模拟(质量、弹性、阻尼、重力),绳索总长保持不变,
/// 并支持在设定时间内“缓慢拉直”至紧绷状态。
/// </summary>
[RequireComponent(typeof(LineRenderer))]
public class RopeSimulator : MonoBehaviour
{
[Header("绳索控制点(起点与终点)")]
public Transform StartPoint; // 绳索起点,受位置驱动
public Transform EndPoint; // 绳索终点,受位置驱动
[Header("绳索参数")]
public int SegmentCount = 20; // 绳索段数(节点数量 = 段数)
public float Mass = 1f; // 每个中间节点的质量
public float Spring = 50f; // 弹性系数(控制连接的柔软程度)
public float Damping = 5f; // 阻尼系数(用于减缓振荡)
public float Gravity = 9.8f; // 重力加速度(作用于每个节点)
public float TotalLength = 5f; // 绳索总长(约束节点间距)
[Header("拉直开关(true 表示启动拉直动画)")]
public bool Straight = false; // 是否处于拉直状态,true 会触发缓慢收紧过程
[Header("速度衰减")]
[Range(0f, 1f)]
public float SpeedAttenuation = 0.98f; // 控制速度每帧的衰减,防止抖动
[Header("缓慢拉直参数")]
public float TightenDuration = 0.2f; // 拉直动画持续时间(单位:秒)
// 绘制与模拟用的内部变量
private LineRenderer lineRenderer;
private Vector3[] positions; // 当前所有节点的位置
private Vector3[] velocities; // 节点当前速度
private Vector3[] simulatedPositions; // 缓慢拉直前记录的原始模拟位置
// 拉直动画控制变量
private float tightenProgress = 0f; // 拉直进度(0~1
private bool isTightening = false; // 当前是否正在拉直动画中
void Start()
{
// 初始化 LineRenderer
lineRenderer = GetComponent<LineRenderer>();
lineRenderer.positionCount = SegmentCount;
// 初始化节点位置与速度数组
positions = new Vector3[SegmentCount];
velocities = new Vector3[SegmentCount];
simulatedPositions = new Vector3[SegmentCount];
// 初始化所有节点位置(线性插值)
for (int i = 0; i < SegmentCount; i++)
{
float t = (float)i / (SegmentCount - 1);
positions[i] = Vector3.Lerp(StartPoint.position, EndPoint.position, t);
velocities[i] = Vector3.zero;
}
}
void FixedUpdate()
{
// 在固定帧率下执行模拟、约束和绘制
Simulate(Time.fixedDeltaTime);
ApplyConstraints();
DrawLine();
}
/// <summary>
/// 模拟每帧节点的受力与位置更新
/// 包括弹簧力、阻尼、重力,并支持缓慢进入“拉直状态”
/// </summary>
void Simulate(float dt)
{
// 如果开启拉直
if (Straight)
{
// 如果是第一次开始拉直
if (!isTightening)
{
isTightening = true;
tightenProgress = 0f;
// 记录当前模拟位置,用于插值过渡
for (int i = 0; i < SegmentCount; i++)
simulatedPositions[i] = positions[i];
}
// 根据拉直时间推进进度(0~1
if (TightenDuration > 0f)
tightenProgress += dt / TightenDuration;
else
tightenProgress = 1f; // 防止除以0
tightenProgress = Mathf.Clamp01(tightenProgress);
// 插值绘制节点向绷直目标靠拢
for (int i = 0; i < SegmentCount; i++)
{
float t = (float)i / (SegmentCount - 1);
Vector3 tightTarget = Vector3.Lerp(StartPoint.position, EndPoint.position, t);
positions[i] = Vector3.Lerp(simulatedPositions[i], tightTarget, tightenProgress);
velocities[i] = Vector3.zero;
}
return; // 当前为拉直动画阶段,不执行物理模拟
}
else
{
// 退出拉直状态,恢复常规模拟
isTightening = false;
tightenProgress = 0f;
}
// 常规模拟(弹簧 + 阻尼 + 重力)
positions[0] = StartPoint.position;
positions[SegmentCount - 1] = EndPoint.position;
float restLength = TotalLength / (SegmentCount - 1); // 理想段长
for (int i = 1; i < SegmentCount - 1; i++)
{
Vector3 force = Vector3.zero;
// 计算弹簧力(左右节点)
Vector3 left = positions[i - 1];
Vector3 right = positions[i + 1];
Vector3 leftDir = positions[i] - left;
Vector3 rightDir = positions[i] - right;
force += -Spring * (leftDir.normalized * (leftDir.magnitude - restLength));
force += -Spring * (rightDir.normalized * (rightDir.magnitude - restLength));
// 添加阻尼力
force += -velocities[i] * Damping;
// 添加重力
force += Vector3.down * Gravity * Mass;
// 更新速度和位置
Vector3 acceleration = force / Mass;
velocities[i] += acceleration * dt;
velocities[i] *= SpeedAttenuation;
positions[i] += velocities[i] * dt;
}
}
/// <summary>
/// 每帧执行多轮“段长约束修正”,使总绳索长度保持不变
/// </summary>
void ApplyConstraints()
{
if (Straight) return; // 绷直中不做物理约束
float segmentLength = TotalLength / (SegmentCount - 1);
int iterations = 10; // 越多越稳定
for (int k = 0; k < iterations; k++)
{
positions[0] = StartPoint.position;
positions[SegmentCount - 1] = EndPoint.position;
for (int i = 0; i < SegmentCount - 1; i++)
{
Vector3 p1 = positions[i];
Vector3 p2 = positions[i + 1];
Vector3 delta = p2 - p1;
float dist = delta.magnitude;
float error = dist - segmentLength;
Vector3 correction = delta.normalized * (error * 0.5f);
if (i != 0)
positions[i] += correction;
if (i + 1 != SegmentCount - 1)
positions[i + 1] -= correction;
}
}
}
/// <summary>
/// 更新 LineRenderer 可视化节点位置
/// </summary>
void DrawLine()
{
lineRenderer.positionCount = SegmentCount;
lineRenderer.SetPositions(positions);
}
}
+25
View File
@@ -0,0 +1,25 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;
public class ScaleAniChild : MonoBehaviour
{
Vector3 _defaultScale = Vector3.one;
private void Awake()
{
_defaultScale = transform.localScale;
}
public void Hide()
{
transform.localScale = Vector3.zero;
}
public void Show(float delay, float aniLength, AnimationCurve cur)
{
transform.DOKill();
transform.localScale = Vector3.zero;
transform.DOScale(_defaultScale, aniLength).SetEase(cur).SetDelay(delay);
}
}
+85
View File
@@ -0,0 +1,85 @@
using System.IO;
using UnityEditor;
using UnityEngine;
public class ScreenShotWindow : EditorWindow
{
private Camera m_Camera;
private string filePath;
private bool m_IsEnableAlpha = false;
private CameraClearFlags m_CameraClearFlags;
[MenuItem("Tools/ScreenShotWindow")]
private static void Init()
{
ScreenShotWindow window = GetWindowWithRect<ScreenShotWindow>(new Rect(0, 0, 300, 150));
window.titleContent = new GUIContent("屏幕截图");
window.Show();
}
private void OnGUI()
{
EditorGUILayout.Space();
m_Camera = EditorGUILayout.ObjectField("选择摄像机", m_Camera, typeof(Camera), true) as Camera;
if (GUILayout.Button("保存位置"))
{
filePath = EditorUtility.OpenFolderPanel("", "", "");
}
m_IsEnableAlpha = EditorGUILayout.Toggle("是否开启透明通道", m_IsEnableAlpha);
EditorGUILayout.Space();
if (GUILayout.Button("截图"))
{
TakeShot();
}
EditorGUILayout.Space();
if (GUILayout.Button("打开导出文件夹"))
{
if (string.IsNullOrEmpty(filePath))
{
Debug.LogError("<color=red>" + "没有选择截图保存位置" + "</color>");
return;
}
Application.OpenURL("file://" + filePath);
}
}
private void TakeShot()
{
if (m_Camera == null)
{
Debug.LogError("<color=red>" + "没有选择摄像机" + "</color>");
return;
}
if (string.IsNullOrEmpty(filePath))
{
Debug.LogError("<color=red>" + "没有选择截图保存位置" + "</color>");
return;
}
m_CameraClearFlags = m_Camera.clearFlags;
if (m_IsEnableAlpha)
{
m_Camera.clearFlags = CameraClearFlags.Depth;
}
int resolutionX = (int)Handles.GetMainGameViewSize().x;
int resolutionY = (int)Handles.GetMainGameViewSize().y;
RenderTexture rt = new RenderTexture(resolutionX, resolutionY, 24);
m_Camera.targetTexture = rt;
Texture2D screenShot = new Texture2D(resolutionX, resolutionY, TextureFormat.ARGB32, false);
m_Camera.Render();
RenderTexture.active = rt;
screenShot.ReadPixels(new Rect(0, 0, resolutionX, resolutionY), 0, 0);
m_Camera.targetTexture = null;
RenderTexture.active = null;
m_Camera.clearFlags = m_CameraClearFlags;
//Destroy(rt);
byte[] bytes = screenShot.EncodeToPNG();
string fileName = filePath + "/" + $"{System.DateTime.Now:yyyy-MM-dd_HH-mm-ss}" + ".png";
File.WriteAllBytes(fileName, bytes);
Debug.Log("截图成功");
}
}
+334
View File
@@ -0,0 +1,334 @@
/// Credit glennpow
/// Sourced from - http://forum.unity3d.com/threads/free-script-particle-systems-in-ui-screen-space-overlay.406862/
/// *Note - experimental. Currently renders in scene view and not game view.
namespace UnityEngine.UI.Extensions
{
#if UNITY_5_3_OR_NEWER
[ExecuteInEditMode]
[RequireComponent(typeof(CanvasRenderer), typeof(ParticleSystem))]
[AddComponentMenu("UI/Effects/Extensions/UIParticleSystem")]
public class UIParticleSystem : MaskableGraphic
{
[Tooltip("Having this enabled run the system in LateUpdate rather than in Update making it faster but less precise (more clunky)")]
public bool fixedTime = true;
private Transform _transform;
private ParticleSystem pSystem;
private ParticleSystem.Particle[] particles;
private UIVertex[] _quad = new UIVertex[4];
private Vector4 imageUV = Vector4.zero;
private ParticleSystem.TextureSheetAnimationModule textureSheetAnimation;
private int textureSheetAnimationFrames;
private Vector2 textureSheetAnimationFrameSize;
private ParticleSystemRenderer pRenderer;
private Material currentMaterial;
private Texture currentTexture;
#if UNITY_5_5_OR_NEWER
private ParticleSystem.MainModule mainModule;
#endif
public override Texture mainTexture
{
get
{
return currentTexture;
}
}
protected bool Initialize()
{
// initialize members
if (_transform == null)
{
_transform = transform;
}
if (pSystem == null)
{
pSystem = GetComponent<ParticleSystem>();
if (pSystem == null)
{
return false;
}
#if UNITY_5_5_OR_NEWER
mainModule = pSystem.main;
if (pSystem.main.maxParticles > 14000)
{
mainModule.maxParticles = 14000;
}
#else
if (pSystem.maxParticles > 14000)
pSystem.maxParticles = 14000;
#endif
pRenderer = pSystem.GetComponent<ParticleSystemRenderer>();
if (pRenderer != null)
pRenderer.enabled = false;
Shader foundShader = Shader.Find("UI Extensions/Particles/Additive");
Material pMaterial = new Material(foundShader);
if (material == null)
material = pMaterial;
currentMaterial = material;
if (currentMaterial && currentMaterial.HasProperty("_MainTex"))
{
currentTexture = currentMaterial.mainTexture;
if (currentTexture == null)
currentTexture = Texture2D.whiteTexture;
}
material = currentMaterial;
// automatically set scaling
#if UNITY_5_5_OR_NEWER
mainModule.scalingMode = ParticleSystemScalingMode.Hierarchy;
#else
pSystem.scalingMode = ParticleSystemScalingMode.Hierarchy;
#endif
particles = null;
}
#if UNITY_5_5_OR_NEWER
if (particles == null)
particles = new ParticleSystem.Particle[pSystem.main.maxParticles];
#else
if (particles == null)
particles = new ParticleSystem.Particle[pSystem.maxParticles];
#endif
imageUV = new Vector4(0, 0, 1, 1);
// prepare texture sheet animation
textureSheetAnimation = pSystem.textureSheetAnimation;
textureSheetAnimationFrames = 0;
textureSheetAnimationFrameSize = Vector2.zero;
if (textureSheetAnimation.enabled)
{
textureSheetAnimationFrames = textureSheetAnimation.numTilesX * textureSheetAnimation.numTilesY;
textureSheetAnimationFrameSize = new Vector2(1f / textureSheetAnimation.numTilesX, 1f / textureSheetAnimation.numTilesY);
}
return true;
}
protected override void Awake()
{
base.Awake();
if (!Initialize())
enabled = false;
}
protected override void OnPopulateMesh(VertexHelper vh)
{
#if UNITY_EDITOR
if (!Application.isPlaying)
{
if (!Initialize())
{
return;
}
}
#endif
// prepare vertices
vh.Clear();
if (!gameObject.activeInHierarchy)
{
return;
}
Vector2 temp = Vector2.zero;
Vector2 corner1 = Vector2.zero;
Vector2 corner2 = Vector2.zero;
// iterate through current particles
int count = pSystem.GetParticles(particles);
for (int i = 0; i < count; ++i)
{
ParticleSystem.Particle particle = particles[i];
// get particle properties
#if UNITY_5_5_OR_NEWER
Vector2 position = (mainModule.simulationSpace == ParticleSystemSimulationSpace.Local ? particle.position : _transform.InverseTransformPoint(particle.position));
#else
Vector2 position = (pSystem.simulationSpace == ParticleSystemSimulationSpace.Local ? particle.position : _transform.InverseTransformPoint(particle.position));
#endif
float rotation = -particle.rotation * Mathf.Deg2Rad;
float rotation90 = rotation + Mathf.PI / 2;
Color32 color = particle.GetCurrentColor(pSystem);
float size = particle.GetCurrentSize(pSystem) * 0.5f;
// apply scale
#if UNITY_5_5_OR_NEWER
if (mainModule.scalingMode == ParticleSystemScalingMode.Shape)
position /= canvas.scaleFactor;
#else
if (pSystem.scalingMode == ParticleSystemScalingMode.Shape)
position /= canvas.scaleFactor;
#endif
// apply texture sheet animation
Vector4 particleUV = imageUV;
if (textureSheetAnimation.enabled)
{
#if UNITY_5_5_OR_NEWER
float frameProgress = 1 - (particle.remainingLifetime / particle.startLifetime);
if (textureSheetAnimation.frameOverTime.curveMin != null)
{
frameProgress = textureSheetAnimation.frameOverTime.curveMin.Evaluate(1 - (particle.remainingLifetime / particle.startLifetime));
}
else if (textureSheetAnimation.frameOverTime.curve != null)
{
frameProgress = textureSheetAnimation.frameOverTime.curve.Evaluate(1 - (particle.remainingLifetime / particle.startLifetime));
}
else if (textureSheetAnimation.frameOverTime.constant > 0)
{
frameProgress = textureSheetAnimation.frameOverTime.constant - (particle.remainingLifetime / particle.startLifetime);
}
#else
float frameProgress = 1 - (particle.lifetime / particle.startLifetime);
#endif
frameProgress = Mathf.Repeat(frameProgress * textureSheetAnimation.cycleCount, 1);
int frame = 0;
switch (textureSheetAnimation.animation)
{
case ParticleSystemAnimationType.WholeSheet:
frame = Mathf.FloorToInt(frameProgress * textureSheetAnimationFrames);
break;
case ParticleSystemAnimationType.SingleRow:
frame = Mathf.FloorToInt(frameProgress * textureSheetAnimation.numTilesX);
int row = textureSheetAnimation.rowIndex;
// if (textureSheetAnimation.useRandomRow) { // FIXME - is this handled internally by rowIndex?
// row = Random.Range(0, textureSheetAnimation.numTilesY, using: particle.randomSeed);
// }
frame += row * textureSheetAnimation.numTilesX;
break;
}
frame %= textureSheetAnimationFrames;
particleUV.x = (frame % textureSheetAnimation.numTilesX) * textureSheetAnimationFrameSize.x;
particleUV.y = Mathf.FloorToInt(frame / textureSheetAnimation.numTilesX) * textureSheetAnimationFrameSize.y;
particleUV.z = particleUV.x + textureSheetAnimationFrameSize.x;
particleUV.w = particleUV.y + textureSheetAnimationFrameSize.y;
}
temp.x = particleUV.x;
temp.y = particleUV.y;
_quad[0] = UIVertex.simpleVert;
_quad[0].color = color;
_quad[0].uv0 = temp;
temp.x = particleUV.x;
temp.y = particleUV.w;
_quad[1] = UIVertex.simpleVert;
_quad[1].color = color;
_quad[1].uv0 = temp;
temp.x = particleUV.z;
temp.y = particleUV.w;
_quad[2] = UIVertex.simpleVert;
_quad[2].color = color;
_quad[2].uv0 = temp;
temp.x = particleUV.z;
temp.y = particleUV.y;
_quad[3] = UIVertex.simpleVert;
_quad[3].color = color;
_quad[3].uv0 = temp;
if (rotation == 0)
{
// no rotation
corner1.x = position.x - size;
corner1.y = position.y - size;
corner2.x = position.x + size;
corner2.y = position.y + size;
temp.x = corner1.x;
temp.y = corner1.y;
_quad[0].position = temp;
temp.x = corner1.x;
temp.y = corner2.y;
_quad[1].position = temp;
temp.x = corner2.x;
temp.y = corner2.y;
_quad[2].position = temp;
temp.x = corner2.x;
temp.y = corner1.y;
_quad[3].position = temp;
}
else
{
// apply rotation
Vector2 right = new Vector2(Mathf.Cos(rotation), Mathf.Sin(rotation)) * size;
Vector2 up = new Vector2(Mathf.Cos(rotation90), Mathf.Sin(rotation90)) * size;
_quad[0].position = position - right - up;
_quad[1].position = position - right + up;
_quad[2].position = position + right + up;
_quad[3].position = position + right - up;
}
vh.AddUIVertexQuad(_quad);
}
}
void Update()
{
if (!fixedTime && Application.isPlaying)
{
pSystem.Simulate(Time.unscaledDeltaTime, false, false, true);
SetAllDirty();
if ((currentMaterial != null && currentTexture != currentMaterial.mainTexture) ||
(material != null && currentMaterial != null && material.shader != currentMaterial.shader))
{
pSystem = null;
Initialize();
}
}
}
void LateUpdate()
{
if (!Application.isPlaying)
{
SetAllDirty();
}
else
{
if (fixedTime)
{
pSystem.Simulate(Time.unscaledDeltaTime, false, false, true);
SetAllDirty();
if ((currentMaterial != null && currentTexture != currentMaterial.mainTexture) ||
(material != null && currentMaterial != null && material.shader != currentMaterial.shader))
{
pSystem = null;
Initialize();
}
}
}
if (material == currentMaterial)
return;
pSystem = null;
Initialize();
}
}
#endif
}
+34 -1
View File
@@ -1,2 +1,35 @@
1.DependencyOrganizer.cs
脚本需要放在unity工程的Asset/Editor下面,然后菜单工具栏(Tool)下面会出现相关工具按钮
脚本需要放在unity工程的Asset/Editor下面,然后菜单工具栏(Tool)下面会出现相关工具按钮
2.AddParentToSelected.cs
添加父对象脚本
3.PlayableDirectorContextMenu.cs
timeline轨道绑定复制脚本(针对轨道绑定丢失)
4.RenameSelectObjects.cs
批量重命名选中对象脚本
5.ReplaceWithPrefabWindow.cs
预制体替换选中对象(窗口化脚本)
6.DrawPathInEditor.cs
编辑器模式中绘制路径轨迹小工具(未完善)
7.ChildSort.cs
对子对象排序
8.NumberScrollMixerBehaviour.cs NumberScrollPlayable.cs NumberScrollPlayableAsset.cs NumberScrollTrack.cs
timeline中对TMP文本进行数字滚动
9.ParticleAttractionPoint.cs
粒子吸引点脚本(未完善,但可用)
10.RopeSimulator.cs
绳索模拟脚本
11.ScaleAniChild.cs
对当前对象的子Ani节点进行动画脚本
12.UIParticleSystem.cs
粒子ui化脚本