先修复一下,错误的场景 删除不必要的钓场资产 修复into场景 update:更新meta文件,修复报错 update:修复资源 修复第一章节建造 修复建造第三章 update:删除多余内容 update:更新README.md update:还原图标 update:更新README update:更新配置
461 lines
18 KiB
C#
461 lines
18 KiB
C#
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.");
|
||
}
|
||
}
|