Files
MinFt/Client/Assets/Editor/CampTool/CampToolNewEditor.cs
Liubing\LB 3d8d4d18f3 first commit
先修复一下,错误的场景

删除不必要的钓场资产

修复into场景

update:更新meta文件,修复报错

update:修复资源

修复第一章节建造

修复建造第三章

update:删除多余内容

update:更新README.md

update:还原图标

update:更新README

update:更新配置
2026-04-28 02:17:22 +08:00

461 lines
18 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.Audio;
[CustomEditor(typeof(CampToolNew))]
public class CampToolNewEditor : Editor
{
CampToolNew Temp;
Vector2 scrollPosition;
private Vector2 outerScrollPosition;
private Vector2[] innerScrollPositions;
public override void OnInspectorGUI()
{
//DrawDefaultInspector();
Temp = (CampToolNew)target;
// 显示文件夹路径
EditorGUILayout.LabelField("保存路径: " + Temp.folderPath);
if (GUILayout.Button("选择"))
{
// Temp.folderPath = EditorUtility.OpenFolderPanel("选择文件夹", Temp.folderPath, "");
var absPath = EditorUtility.OpenFolderPanel("选择文件夹", Temp.folderPath, "");
Temp.folderPath = absPath.Substring(absPath.IndexOf("Assets/"));
EditorUtility.SetDirty(Temp);
EditorSceneManager.SaveOpenScenes();
}
EditorGUILayout.PropertyField(serializedObject.FindProperty("dynamic"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("defaultAudioClip"));
EditorGUILayout.BeginVertical("box");
EditorGUILayout.Space();
GUIStyle labelStyle = new GUIStyle(GUI.skin.label);
labelStyle.normal.textColor = Color.red; // Set the color you want
labelStyle.fontStyle = FontStyle.BoldAndItalic;
labelStyle.fontSize = 20;
labelStyle.wordWrap = true;
string labelText = "模版动画中每一个轨道,在动画最后一帧必须有关键帧,因为拿每一条轨道的最后一帧时间播放音效";
float labelHeight = labelStyle.CalcHeight(new GUIContent(labelText), EditorGUIUtility.currentViewWidth - 40);
EditorGUILayout.LabelField(labelText, labelStyle, GUILayout.Height(labelHeight));
EditorGUILayout.LabelField("生成初始动画", EditorStyles.boldLabel);
OnInspectorGUIStepsDatas();
EditorGUILayout.EndScrollView();
EditorGUILayout.PropertyField(serializedObject.FindProperty("targetLength"), true);
if (GUILayout.Button("Apply"))
{
Play();
}
EditorGUILayout.Space();
EditorGUILayout.EndVertical();
//间隔
EditorGUILayout.Space(10);
EditorGUILayout.BeginVertical("box");
EditorGUILayout.LabelField("后续修改音效", EditorStyles.boldLabel);
//EditorGUILayout.PropertyField(serializedObject.FindProperty("audioChangeDatas"));
OnInspectorGUIAudioChangeDatas();
// 结束外部滚动视图
EditorGUILayout.EndScrollView();
if (GUILayout.Button("ChangeAudio"))
{
ChangeAudio();
}
EditorGUILayout.EndVertical();
serializedObject.ApplyModifiedProperties();
}
void OnSceneGUI()
{
}
void OnInspectorGUIStepsDatas()
{
// 获取列表属性
SerializedProperty stepsProperty = serializedObject.FindProperty("steps");
// 计算列表元素的总高度,包括展开的子属性
float totalHeight = 0f;
for (int i = 0; i < stepsProperty.arraySize; i++)
{
SerializedProperty element = stepsProperty.GetArrayElementAtIndex(i);
totalHeight += EditorGUI.GetPropertyHeight(element, true) + EditorGUIUtility.standardVerticalSpacing;
}
// 设置 ScrollView 的高度,最小高度为 100最大高度为 400
float scrollViewHeight = Mathf.Clamp(totalHeight, 100, 400);
scrollViewHeight += 60;
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition, GUILayout.Height(scrollViewHeight));
// 获取列表属性
Event e = Event.current;
if (e.type == EventType.DragUpdated || e.type == EventType.DragPerform)
{
Rect dropArea = new Rect(scrollPosition.x, scrollPosition.y, 300, scrollViewHeight);
if (dropArea.Contains(e.mousePosition))
{
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
if (e.type == EventType.DragPerform)
{
DragAndDrop.AcceptDrag();
foreach (var obj in DragAndDrop.objectReferences)
{
if (obj is AnimationClip)
{
stepsProperty.arraySize++;
SerializedProperty newElement = stepsProperty.GetArrayElementAtIndex(stepsProperty.arraySize - 1);
SerializedProperty ani = newElement.FindPropertyRelative("ani");
SerializedProperty audioClip = newElement.FindPropertyRelative("audioClip");
SerializedProperty timeAniOffset = newElement.FindPropertyRelative("timeAniOffset");
ani.objectReferenceValue = obj;
audioClip.objectReferenceValue = Temp.defaultAudioClip;
timeAniOffset.floatValue = 0.1f;
}
}
}
e.Use();
}
}
EditorGUILayout.PropertyField(stepsProperty);
}
void OnInspectorGUIAudioChangeDatas()
{
// 计算外部列表元素的总高度,包括展开的子属性
SerializedProperty stepsProperty = serializedObject.FindProperty("audioChangeDatas");
float totalOuterHeight = 0f;
for (int i = 0; i < stepsProperty.arraySize; i++)
{
SerializedProperty element = stepsProperty.GetArrayElementAtIndex(i);
totalOuterHeight += EditorGUI.GetPropertyHeight(element, true) + EditorGUIUtility.standardVerticalSpacing;
}
// 设置外部 ScrollView 的高度,最小高度为 100最大高度为 400
float outerScrollViewHeight = Mathf.Clamp(totalOuterHeight, 100, 400);
outerScrollViewHeight += 60;
// 开始外部滚动视图
outerScrollPosition = EditorGUILayout.BeginScrollView(outerScrollPosition, GUILayout.Height(outerScrollViewHeight));
// 获取列表属性
Event e = Event.current;
if (e.type == EventType.DragUpdated || e.type == EventType.DragPerform)
{
Rect dropArea = new Rect(outerScrollPosition.x, outerScrollPosition.y, 300, outerScrollViewHeight);
if (dropArea.Contains(e.mousePosition))
{
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
if (e.type == EventType.DragPerform)
{
DragAndDrop.AcceptDrag();
if (stepsProperty.arraySize == 0)
{
stepsProperty.arraySize++;
}
SerializedProperty audioChangeDatasSubRoot = stepsProperty.GetArrayElementAtIndex(stepsProperty.arraySize - 1);
SerializedProperty audioChangeDatasSub = audioChangeDatasSubRoot.FindPropertyRelative("audioChangeDatas");
foreach (var obj in DragAndDrop.objectReferences)
{
if (obj is AnimationClip)
{
//AnimationClip clip = (AnimationClip)obj;
Debug.Log("AnimationClip :" + obj.name);
audioChangeDatasSub.arraySize++;
SerializedProperty newElement = audioChangeDatasSub.GetArrayElementAtIndex(audioChangeDatasSub.arraySize - 1);
SerializedProperty ani = newElement.FindPropertyRelative("ani");
SerializedProperty audioClip = newElement.FindPropertyRelative("audioClip");
ani.objectReferenceValue = obj;
audioClip.objectReferenceValue = Temp.defaultAudioClip;
}
}
}
e.Use();
}
}
EditorGUILayout.PropertyField(stepsProperty);
}
public void Play()
{
for (int n = 0; n < Temp.steps.Length; n++)
{
StepRootData step = Temp.steps[n];
Transform root2 = step.StepRoot;
Transform root1 = root2.parent;
string path1 = root1.name;
string path2 = root2.name;
//创建新的AnimationClip
AnimationClip newAni = new AnimationClip();
if (string.IsNullOrEmpty(step.newAniName))
{
newAni.name = $"{path1}_{path2}";
}
else
{
newAni.name = step.newAniName;
}
List<int> timeList = new List<int>();
EditorCurveBinding[] bindings = AnimationUtility.GetCurveBindings(step.ani);
int count = root2.childCount;
float bindingTime = 0f;
for (int j = 0; j < bindings.Length; j++)
{
var binding = bindings[j];
AnimationCurve curve = AnimationUtility.GetEditorCurve(step.ani, binding);
// 获取关键帧的数量
int keyCount = curve.keys.Length;
// 移动关键帧的位置
for (int k = keyCount - 1; k >= 0; k--)
{
Keyframe key = curve.keys[k];
key.time += 0.02f;
if (bindingTime < key.time)
{
bindingTime = key.time;
}
curve.MoveKey(k, key);
}
for (int i = 0; i < count; i++)
{
// string trackName = $"{path1}/{path2}/{i + 1}/AnimRoot";
string trackName = $"{path1}/{path2}/{i + 1:D2}/AnimRoot";
EditorCurveBinding trackBinding = new EditorCurveBinding
{
type = binding.type,
path = trackName,
propertyName = binding.propertyName
};
// 将轨道添加到Clip中
AnimationUtility.SetEditorCurve(newAni, trackBinding, curve);
if (j == 0 && step.audioClip != null)
{
int keyTime = Mathf.RoundToInt(bindingTime * 10000);
if (!timeList.Contains(keyTime))
{
timeList.Add(keyTime);
}
}
// 移动关键帧的位置
for (int k = keyCount - 1; k >= 0; k--)
{
Keyframe key = curve.keys[k];
key.time += step.timeAniOffset;
if (bindingTime < key.time)
{
bindingTime = key.time;
}
curve.MoveKey(k, key);
}
}
}
if (step.audioClip != null)
{
SetBindingAudio(timeList, newAni, step.audioClip);
}
if (Temp.targetLength > 0)
{
// Get the original length of the clip
float originalLength = newAni.length;
if (originalLength <= 0)
{
Debug.LogWarning($"Clip '{newAni.name}' has no length to scale.");
continue;
}
// Calculate the new speed to scale the animation
float scaleFactor = originalLength / Temp.targetLength;
// Scale the animation clip speed by adjusting its frame rates
ScaleClip(newAni, scaleFactor);
}
AssetDatabase.CreateAsset(newAni, $"{Temp.folderPath}/{newAni.name}.anim");
}
//保存 Temp
AssetDatabase.SaveAssets();
}
private string _mixerPath = "Assets/ABPackage/pkgFirstPackage/Audio/AudioMixer.mixer";
void SetBindingAudio(List<int> timeList, AnimationClip newAni, AudioClip audioClip)
{
List<EditorCurveBinding> bindingAudio = new List<EditorCurveBinding>();
List<AnimationCurve> curveAudio = new List<AnimationCurve>();
int audioIndex = 0;
Keyframe keyframe = new Keyframe(0, 0, Mathf.Infinity, Mathf.Infinity);
string AudioRoot = "Audio";
Transform AudioTran = Temp.dynamic.Find(AudioRoot);
if (AudioTran == null)
{
GameObject go = new GameObject(AudioRoot);
go.transform.SetParent(Temp.dynamic);
AudioTran = go.transform;
}
int maxTime = 0;
AudioMixer mixer = AssetDatabase.LoadAssetAtPath<AudioMixer>(_mixerPath);
for (int j = 0; j < timeList.Count; j++)
{
audioIndex %= 3;
Transform tran2 = AudioTran.Find($"{newAni.name}_{audioIndex}");
AudioSource aud;
if (tran2)
{
aud = tran2.GetComponent<AudioSource>();
}
else
{
var go = new GameObject($"{newAni.name}_{audioIndex}", typeof(AudioSource));
go.transform.SetParent(AudioTran);
aud = go.GetComponent<AudioSource>();
go.SetActive(false);
}
aud.clip = audioClip;
aud.outputAudioMixerGroup = mixer.FindMatchingGroups("Fishing")[0];
if (bindingAudio.Count <= audioIndex)
{
bindingAudio.Add(new EditorCurveBinding
{
type = typeof(GameObject),
path = $"{AudioRoot}/{newAni.name}_{audioIndex}",
propertyName = "m_IsActive"
});
}
if (curveAudio.Count <= audioIndex)
{
curveAudio.Add(new AnimationCurve());
curveAudio[audioIndex].AddKey(keyframe);
}
if (maxTime < timeList[j])
{
maxTime = timeList[j];
}
float time = timeList[j] / 10000f;
Keyframe key2 = new Keyframe(time - 0.02f, 0, Mathf.Infinity, Mathf.Infinity);
Keyframe key3 = new Keyframe(time, 1, Mathf.Infinity, Mathf.Infinity);
curveAudio[audioIndex].AddKey(key2);
curveAudio[audioIndex].AddKey(key3);
audioIndex++;
}
for (int i = 0; i < bindingAudio.Count; i++)
{
if (newAni == null || bindingAudio[i] == null || curveAudio[i] == null)
continue;
float time = maxTime / 10000f;
Keyframe key = new Keyframe(time, 0);
curveAudio[i].AddKey(key);
int lastKey = curveAudio[i].length - 1;
curveAudio[i].MoveKey(lastKey, key);
AnimationUtility.SetEditorCurve(newAni, bindingAudio[i], curveAudio[i]);
}
}
public void ChangeAudio()
{
for (int n = 0; n < Temp.audioChangeDatas.Length; n++)
{
AudioChangeDataRoot dataRoot = Temp.audioChangeDatas[n];
var audioChangeDatas = dataRoot.audioChangeDatas;
for (int i = 0; i < audioChangeDatas.Length; i++)
{
try
{
AudioChangeData data = audioChangeDatas[i];
AnimationClip newAni = data.ani;
EditorCurveBinding[] bindings = AnimationUtility.GetCurveBindings(data.ani);
List<int> timeList = new List<int>();
for (int j = 0; j < bindings.Length; j++)
{
var binding = bindings[j];
if (binding.path.Contains($"Audio/{newAni.name}"))
{
continue;
}
AnimationCurve curve = AnimationUtility.GetEditorCurve(data.ani, binding);
// 获取关键帧的数量
int keyCount = curve.keys.Length;
Keyframe key = curve.keys[keyCount - 1];
int keyTime = Mathf.RoundToInt(key.time * 10000);
if (!timeList.Contains(keyTime))
{
timeList.Add(keyTime);
}
}
SetBindingAudio(timeList, newAni, data.audioClip);
EditorUtility.SetDirty(data.ani);
}
catch (System.Exception e)
{
Debug.LogError($"Error processing audio change data at index {i} in root '{dataRoot.name}': {e.Message}");
return;
}
}
}
AssetDatabase.SaveAssets();
}
private void ScaleClip(AnimationClip clip, float scaleFactor)
{
// Debug.Log($"<color=red>{scaleFactor} for {clip}</color>");
// Scale all curves in the clip
foreach (var binding in AnimationUtility.GetCurveBindings(clip))
{
var curve = AnimationUtility.GetEditorCurve(clip, binding);
var curveKeyFrames = curve.keys;
// Adjust keyframe times
for (int i = 0; i < curveKeyFrames.Length; i++)
{
// Debug.Log($" before: {curveKeyFrames[i].time}");
// var t = curveKeyFrames[i].time / scaleFactor;
// curve.keys[i].time = t;
curveKeyFrames[i].time /= scaleFactor;
// Debug.Log($" after: {curveKeyFrames[i].time}");
// Debug.Log($" after t: {t}");
}
curve.keys = curveKeyFrames;
AnimationUtility.SetEditorCurve(clip, binding, curve);
}
foreach (var binding in AnimationUtility.GetObjectReferenceCurveBindings(clip))
{
var curve = AnimationUtility.GetEditorCurve(clip, binding);
for (int i = 0; i < curve.keys.Length; i++)
{
curve.keys[i].time /= scaleFactor;
}
AnimationUtility.SetEditorCurve(clip, binding, curve);
}
// Scale events
foreach (var animEvent in AnimationUtility.GetAnimationEvents(clip))
{
animEvent.time /= scaleFactor;
}
// Apply changes back to the clip
AnimationUtility.SetAnimationEvents(clip, AnimationUtility.GetAnimationEvents(clip));
Debug.Log($"Scaled clip '{clip.name}' to {clip.length / scaleFactor} seconds.");
}
}