submodule bug fix & Clip extend

移除submodule重复引用的问题,
Clip现在可以保存任意GameElement,且任意指定物体作为父物体加载
This commit is contained in:
SoulliesOfficial
2025-03-02 02:18:28 -05:00
parent 5d775ae687
commit 188cfba84a
39 changed files with 1276 additions and 193 deletions

File diff suppressed because one or more lines are too long

View File

@@ -78,6 +78,8 @@ MonoBehaviour:
type: 3}
emissionColorPicker: {fileID: 8936320662031972394, guid: d00706ed05d0c4a55943214fad99b6cd,
type: 3}
generalSecondaryWindow: {fileID: 6108546402767822149, guid: f3b40a60628cd4583bd8f92cacf1ba3e,
type: 3}
compositeParameterWindow: {fileID: 8976586735561836907, guid: 6d98a93f5b5c14ef5b7b125e407ce17d,
type: 3}
inputFieldUnit: {fileID: 4259592601424320053, guid: 54e97fa10b53c45caa973223dfe8418c,

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: f3b40a60628cd4583bd8f92cacf1ba3e
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -18,11 +18,14 @@ namespace Ichni.Editor
this.buttonText.text = buttonText;
if(!hasTitle) title.gameObject.SetActive(false);
}
public void ApplyFunction(UnityAction function)
{
button.onClick.AddListener(function);
button.onClick.AddListener(connectedBaseElement.Refresh);
if (connectedBaseElement != null)
{
button.onClick.AddListener(connectedBaseElement.Refresh);
}
}
}
}

View File

@@ -17,14 +17,14 @@ namespace Ichni.Editor
public string parameterName;
public UnityAction ApplyParameters;
public void Initialize(IBaseElement baseElement, string title, string parameterName)
public void Initialize(IBaseElement baseElement, string titleText, string parameterName)
{
transform.localScale = Vector3.zero;
this.connectedBaseElement = baseElement;
this.parameterName = parameterName;
this.title.text = title;
unitList = new List<DynamicUICompositeUnit>();
closeButton.onClick.AddListener(Quit);
InitializeWindow(titleText, ApplyParameters);
StartCoroutine(WindowAnim.ShowPanelOnScale(gameObject));
}

View File

@@ -56,7 +56,7 @@ namespace Ichni.Editor
DynamicUIButton button = Object.Instantiate(EditorManager.instance.basePrefabs.button, container.rect)
.GetComponent<DynamicUIButton>();
button.SetText(buttonText, title != "null");
button.Initialize(baseElement, title, "null");
button.Initialize(baseElement, title, string.Empty);
button.ApplyFunction(function);
container.dynamicUIElements.Add(button);
return button;

View File

@@ -11,24 +11,15 @@ namespace Ichni.Editor
public RectTransform WindowRect { get; set; }
public List<DynamicUIContainer> Containers { get; set; }
public void Initialize(GameElement gameElement, string title)
public void Initialize(GameElement gameElement, string titleText)
{
StartCoroutine(WindowAnim.ShowPanelOnScale(gameObject));
WindowRect = windowRect;
Containers = new List<DynamicUIContainer>();
connectedGameElement = gameElement;
this.title.text = title;
closeButton.onClick.AddListener(Quit);
}
public void Quit()
{
Destroy(gameObject);
//StartCoroutine(WindowAnim.HidePanel(gameObject, true));
InitializeWindow(titleText);
}
}
}

View File

@@ -1,18 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GeneralSecondaryWIndow : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}

View File

@@ -0,0 +1,65 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
namespace Ichni.Editor
{
public class GeneralSecondaryWindow : MovableWindow
{
public RectTransform WindowRect { get; set; }
public List<DynamicUIContainer> Containers { get; set; }
public void Initialize(string titleText, UnityAction closeAction = null)
{
WindowRect = windowRect;
Containers = new List<DynamicUIContainer>();
InitializeWindow(titleText, closeAction);
}
public DynamicUIContainer GenerateContainer(string title)
{
DynamicUIContainer container =
Instantiate(EditorManager.instance.basePrefabs.dynamicUIContainer, WindowRect)
.GetComponent<DynamicUIContainer>();
container.title.text = title;
Containers.Add(container);
return container;
}
public DynamicUIContainer GenerateContainer()
{
DynamicUIContainer container =
Instantiate(EditorManager.instance.basePrefabs.dynamicUIContainer, WindowRect)
.GetComponent<DynamicUIContainer>();
Destroy(container.title.gameObject);
Containers.Add(container);
return container;
}
public DynamicUIInputField GenerateInputField(DynamicUIContainer container,
string title, string defaultText = "") //不与参数绑定的InputField
{
DynamicUIInputField inputField = Instantiate(EditorManager.instance.basePrefabs.inputField, container.rect)
.GetComponent<DynamicUIInputField>();
inputField.Initialize(null, title, string.Empty);
inputField.SetDefaultValue(defaultText);
container.dynamicUIElements.Add(inputField);
return inputField;
}
public DynamicUIButton GenerateButton(DynamicUIContainer container, string buttonText,
UnityAction function, string title = "null")
{
DynamicUIButton button = Instantiate(EditorManager.instance.basePrefabs.button, container.rect)
.GetComponent<DynamicUIButton>();
button.SetText(buttonText, title != "null");
button.Initialize(null, title, string.Empty);
button.ApplyFunction(function);
container.dynamicUIElements.Add(button);
return button;
}
}
}

View File

@@ -1,71 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using Ichni.RhythmGame;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace Ichni.Editor
{
public class ClipManagementWindow : MovableWindow
{
public TMP_InputField clipNameInputField;
public Button applyClipButton;
public void InitializeAsSaveClip()
{
GameElement currentElement = EditorManager.instance.operationManager.currentSelectedElement;
if (currentElement == null)
{
LogWindow.Log("No Game Element selected.", Color.red);
return;
}
if (!OpenWindow())
{
return;
}
InitializeWindow("Save Clip: " + currentElement.elementName);
clipNameInputField.text = currentElement.elementName;
applyClipButton.onClick.RemoveAllListeners();
applyClipButton.onClick.AddListener(() =>
{
EditorManager.instance.projectManager.beatmapClipManager.Save(clipNameInputField.text);
gameObject.SetActive(false);
});
}
public void InitializeAsLoadClip()
{
if (!OpenWindow())
{
return;
}
InitializeWindow("Load Clip");
clipNameInputField.text = "";
applyClipButton.onClick.RemoveAllListeners();
applyClipButton.onClick.AddListener(() =>
{
EditorManager.instance.projectManager.beatmapClipManager.Load(clipNameInputField.text);
gameObject.SetActive(false);
});
}
private bool OpenWindow()
{
if (EditorManager.instance.uiManager.mainPage.toolBar.clipManagementWindow.gameObject.activeSelf)
{
LogWindow.Log("Clip Management Window is already active.");
return false;
}
gameObject.SetActive(true);
return true;
}
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 98c54627a956d42e8af1eb21c396109a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Ichni.RhythmGame;
using Sirenix.OdinInspector;
using UnityEngine;
using UnityEngine.UI;
@@ -18,20 +19,80 @@ namespace Ichni.Editor
public Button clipLoadButton;
[Title("Windows")]
public ClipManagementWindow clipManagementWindow;
public GeneralSecondaryWindow clipManagementWindow;
protected override void Start()
{
base.Start();
saveButton.onClick.AddListener(EditorManager.instance.projectManager.saveManager.Save);
exportButton.onClick.AddListener(EditorManager.instance.projectManager.exportManager.Export);
clipSaveButton.onClick.AddListener(clipManagementWindow.InitializeAsSaveClip);
clipLoadButton.onClick.AddListener(clipManagementWindow.InitializeAsLoadClip);
clipSaveButton.onClick.AddListener(GenerateSaveClipWindow);
clipLoadButton.onClick.AddListener(GenerateLoadClipWindow);
}
}
public partial class ToolBar
{
private void GenerateSaveClipWindow()
{
GameElement currentElement = EditorManager.instance.operationManager.currentSelectedElement;
if (currentElement == null)
{
LogWindow.Log("No Game Element selected.", Color.red);
return;
}
if (clipManagementWindow != null)
{
LogWindow.Log("Clip Management Window already exists.", Color.red);
return;
}
GeneralSecondaryWindow saveClipWindow =
Instantiate(EditorManager.instance.basePrefabs.generalSecondaryWindow,
EditorManager.instance.uiManager.mainPage.mainCanvas.GetComponent<RectTransform>()).GetComponent<GeneralSecondaryWindow>();
clipManagementWindow = saveClipWindow;
saveClipWindow.Initialize("Save Clip: " + currentElement.elementName, () => clipManagementWindow = null);
var container = saveClipWindow.GenerateContainer();
var clipNameInputField = saveClipWindow.GenerateInputField(container, "Clip Name", currentElement.elementName);
var applyClipButton = saveClipWindow.GenerateButton(container, "Apply", () =>
{
EditorManager.instance.projectManager.beatmapClipManager.SaveClip(clipNameInputField.GetValue<string>());
});
}
private void GenerateLoadClipWindow()
{
GameElement currentElement = EditorManager.instance.operationManager.currentSelectedElement;
if (currentElement == null)
{
LogWindow.Log("No Game Element selected.", Color.red);
return;
}
GameElement loadTarget = currentElement == EditorManager.instance ? null : currentElement.parentElement;
if (clipManagementWindow != null)
{
LogWindow.Log("Clip Management Window already exists.", Color.red);
return;
}
GeneralSecondaryWindow loadClipWindow =
Instantiate(EditorManager.instance.basePrefabs.generalSecondaryWindow,
EditorManager.instance.uiManager.mainPage.mainCanvas.GetComponent<RectTransform>()).GetComponent<GeneralSecondaryWindow>();
clipManagementWindow = loadClipWindow;
loadClipWindow.Initialize("Load Clip", () => clipManagementWindow = null);
var container = loadClipWindow.GenerateContainer();
var clipNameInputField = loadClipWindow.GenerateInputField(container, "Clip Name");
var applyClipButton = loadClipWindow.GenerateButton(container, "Apply", () =>
{
EditorManager.instance.projectManager.beatmapClipManager.LoadClip(clipNameInputField.GetValue<string>());
});
}
}
}

View File

@@ -2,6 +2,7 @@ using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Serialization;
using UnityEngine.UI;
@@ -12,13 +13,16 @@ namespace Ichni.Editor
public RectTransform windowRect;
public Button closeButton;
public TMP_Text title;
public UnityAction onCloseWindow;
protected void InitializeWindow(string titleText)
protected void InitializeWindow(string titleText, UnityAction closeAction = null)
{
title.text = titleText;
onCloseWindow = closeAction;
closeButton.onClick.AddListener(() =>
{
gameObject.SetActive(false);
onCloseWindow?.Invoke();
Destroy(gameObject);
});
}
}

View File

@@ -18,7 +18,6 @@ namespace Ichni.RhythmGame
protected override void SetDefaultSubmodules()
{
timeDurationSubmodule = new TimeDurationSubmodule(this);
submoduleList.Add(timeDurationSubmodule);
}
/// <summary>

View File

@@ -38,7 +38,6 @@ namespace Ichni.RhythmGame
protected override void SetDefaultSubmodules()
{
timeDurationSubmodule = new TimeDurationSubmodule(this);
submoduleList.Add(timeDurationSubmodule);
}
protected override void UpdateAnimation(float songTime)

View File

@@ -38,7 +38,6 @@ namespace Ichni.RhythmGame
protected override void SetDefaultSubmodules()
{
timeDurationSubmodule = new TimeDurationSubmodule(this);
submoduleList.Add(timeDurationSubmodule);
}
protected override void UpdateAnimation(float songTime)

View File

@@ -33,7 +33,6 @@ namespace Ichni.RhythmGame
protected override void SetDefaultSubmodules()
{
timeDurationSubmodule = new TimeDurationSubmodule(this);
submoduleList.Add(timeDurationSubmodule);
}
protected override void UpdateAnimation(float songTime)

View File

@@ -41,7 +41,6 @@ namespace Ichni.RhythmGame
protected override void SetDefaultSubmodules()
{
timeDurationSubmodule = new TimeDurationSubmodule(this);
submoduleList.Add(timeDurationSubmodule);
}
protected override void UpdateAnimation(float songTime)

View File

@@ -36,7 +36,6 @@ namespace Ichni.RhythmGame
protected override void SetDefaultSubmodules()
{
timeDurationSubmodule = new TimeDurationSubmodule(this);
submoduleList.Add(timeDurationSubmodule);
}
protected override void UpdateAnimation(float songTime)

View File

@@ -38,7 +38,6 @@ namespace Ichni.RhythmGame
protected override void SetDefaultSubmodules()
{
timeDurationSubmodule = new TimeDurationSubmodule(this);
submoduleList.Add(timeDurationSubmodule);
}
protected override void UpdateAnimation(float songTime)

View File

@@ -55,6 +55,8 @@ namespace Ichni.RhythmGame
this.baseColorDirtyMark = false;
this.emissionColorDirtyMark = false;
(attachedGameElement as IHaveColorSubmodule).colorSubmodule = this;
}
public ColorSubmodule(GameElement attachedGameElement, Color originalBaseColor, bool emissionEnabled,
@@ -71,6 +73,8 @@ namespace Ichni.RhythmGame
this.baseColorDirtyMark = false;
this.emissionColorDirtyMark = false;
(attachedGameElement as IHaveColorSubmodule).colorSubmodule = this;
}
public override void SaveBM()

View File

@@ -34,6 +34,8 @@ namespace Ichni.RhythmGame
effectCollection.Add("Bad", new List<EffectBase>());
effectCollection.Add("Miss", new List<EffectBase>());
}
(attachedGameElement as IHaveEffectSubmodule).effectSubmodule = this;
}
public EffectSubmodule(GameElement attachedGameElement, Dictionary<string, List<EffectBase_BM>> effectList_BM) : base(attachedGameElement)
@@ -49,6 +51,8 @@ namespace Ichni.RhythmGame
}
effectCollection.Add(effect.Key, effectList);
}
(attachedGameElement as IHaveEffectSubmodule).effectSubmodule = this;
}
}

View File

@@ -18,7 +18,8 @@ namespace Ichni.RhythmGame
isOverridingDuration = false;
startTime = -32767;//TODO: 换为-delay
endTime = 32767;//TODO: 换为songLength
(attachedGameElement as IHaveTimeDurationSubmodule).timeDurationSubmodule = this;
}
public TimeDurationSubmodule(GameElement attachedGameElement, bool isOverridingDuration, float startTime, float endTime) : base(attachedGameElement)
@@ -26,6 +27,8 @@ namespace Ichni.RhythmGame
this.isOverridingDuration = isOverridingDuration;
this.startTime = startTime;
this.endTime = endTime;
(attachedGameElement as IHaveTimeDurationSubmodule).timeDurationSubmodule = this;
}
public bool CheckTimeInDuration(float time, float offset = 0.2f)

View File

@@ -25,9 +25,6 @@ namespace Ichni.RhythmGame
{
transformSubmodule = new TransformSubmodule(this);
timeDurationSubmodule = new TimeDurationSubmodule(this);
submoduleList.Add(transformSubmodule);
submoduleList.Add(timeDurationSubmodule);
}
}

View File

@@ -60,7 +60,6 @@ namespace Ichni.RhythmGame
protected override void SetDefaultSubmodules()
{
transformSubmodule = new TransformSubmodule(this);
submoduleList.Add(transformSubmodule);
}
}

View File

@@ -26,7 +26,6 @@ namespace Ichni.RhythmGame
protected override void SetDefaultSubmodules()
{
effectSubmodule = new EffectSubmodule(this);
submoduleList.Add(effectSubmodule);
}
private void Update()

View File

@@ -58,9 +58,6 @@ namespace Ichni.RhythmGame
{
timeDurationSubmodule = new TimeDurationSubmodule(this);
noteJudgeSubmodule = new NoteJudgeSubmodule(this);
submoduleList.Add(timeDurationSubmodule);
submoduleList.Add(noteJudgeSubmodule);
}
private void Update()
@@ -135,27 +132,15 @@ namespace Ichni.RhythmGame
}
public override void SetUpInspector()
{
//我想把时间放在第一层菜单所以
base.SetUpInspector();
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
var container = inspector.GenerateContainer("Element Info");
var nameInputField = inspector.GenerateInputField(this, container, GetType().Name + "'s Name", nameof(elementName));
var guidText = inspector.GenerateParameterText(this, container, "Element GUID", nameof(elementGuid));
var tagsListButton = inspector.GenerateButton(this, container, "Tags List", () =>
{
inspector.GenerateCompositeParameterWindow(this, "Tags List", nameof(tags)).SetAsStringList();
});
var container = inspector.GenerateContainer("Note Info");
var exactJudgeTimeInputField =
inspector.GenerateInputField(this, container, "exactJudgeTime", nameof(exactJudgeTime));
exactJudgeTimeInputField.AddListenerFunction(_ => UpdateNoteInTrack());
var noteScreenPositionText = inspector.GenerateHintText(this, container, () => "Note Screen Position: " + noteScreenPosition);
foreach (var submodule in submoduleList)
{
submodule.SetUpInspector();
}
}
}

View File

@@ -20,7 +20,6 @@ namespace Ichni.RhythmGame
{
base.SetDefaultSubmodules();
effectSubmodule = new EffectSubmodule(this, EffectSubmodule.EffectSubmodulePreset.Note);
submoduleList.Add(effectSubmodule);
}
}
}

View File

@@ -46,10 +46,6 @@ namespace Ichni.RhythmGame
transformSubmodule = new TransformSubmodule(this);
timeDurationSubmodule = new TimeDurationSubmodule(this);
colorSubmodule = new ColorSubmodule(this);
submoduleList.Add(transformSubmodule);
submoduleList.Add(timeDurationSubmodule);
submoduleList.Add(colorSubmodule);
}
}

View File

@@ -47,10 +47,6 @@ namespace Ichni.RhythmGame
transformSubmodule = new TransformSubmodule(this);
timeDurationSubmodule = new TimeDurationSubmodule(this);
colorSubmodule = new ColorSubmodule(this);
submoduleList.Add(transformSubmodule);
submoduleList.Add(timeDurationSubmodule);
submoduleList.Add(colorSubmodule);
}

View File

@@ -34,9 +34,6 @@ namespace Ichni.RhythmGame
trackPathSubmodule = null;
trackTimeSubmodule = null;
trackRendererSubmodule = null;
submoduleList.Add(transformSubmodule);
submoduleList.Add(timeDurationSubmodule);
}
private void Update()

View File

@@ -39,7 +39,6 @@ namespace Ichni.RhythmGame
protected override void SetDefaultSubmodules()
{
timeDurationSubmodule = new TimeDurationSubmodule(this);
submoduleList.Add(timeDurationSubmodule);
}
private void Update()

View File

@@ -33,7 +33,6 @@ namespace Ichni.RhythmGame
protected override void SetDefaultSubmodules()
{
timeDurationSubmodule = new TimeDurationSubmodule(this);
submoduleList.Add(timeDurationSubmodule);
}
public void Update()

View File

@@ -45,7 +45,6 @@ namespace Ichni.RhythmGame
protected override void SetDefaultSubmodules()
{
timeDurationSubmodule = new TimeDurationSubmodule(this);
submoduleList.Add(timeDurationSubmodule);
}
public void Update()

View File

@@ -46,7 +46,6 @@ namespace Ichni.RhythmGame
protected override void SetDefaultSubmodules()
{
transformSubmodule = new TransformSubmodule(this);
submoduleList.Add(transformSubmodule);
}
}

View File

@@ -52,6 +52,7 @@ public class BasePrefabsCollection : SerializedScriptableObject
public GameObject baseColorPicker;
public GameObject emissionColorPicker;
[Title("DynamicUI相关-Composite")]
public GameObject generalSecondaryWindow;
public GameObject compositeParameterWindow;
public GameObject inputFieldUnit;
public GameObject animatedFloatUnit;

View File

@@ -195,24 +195,24 @@ namespace Ichni
public class BeatmapClipManager
{
public void Save(string clipName)
public void SaveClip(string clipName)
{
LogWindow.Log("Start Saving Clip...");
GameElement selectedElement = EditorManager.instance.operationManager.currentSelectedElement;
if (selectedElement is not ElementFolder folder)
if (selectedElement is null)
{
LogWindow.Log("Please select a folder to save the beatmap clip.", Color.red);
LogWindow.Log("Please select a Game Element to save the beatmap clip.", Color.red);
return;
}
SaveClip(folder, clipName);
_SaveClip(selectedElement, clipName);
LogWindow.Log("Save Clip Complete", Color.green);
}
public void Load(string clipName)
public void LoadClip(string clipName)
{
LogWindow.Log("Start Loading Clip...");
@@ -222,25 +222,25 @@ namespace Ichni
return;
}
LoadClip(clipName);
GameElement selectedElement = EditorManager.instance.operationManager.currentSelectedElement;
if (selectedElement is null)
{
LogWindow.Log("Please select a Game Element to load the beatmap clip.", Color.red);
return;
}
Debug.Log(selectedElement.elementName + " " + selectedElement.elementGuid);
_LoadClip(selectedElement, clipName);
LogWindow.Log("Load Clip Complete", Color.green);
}
public void SaveClip(ElementFolder folder, string clipName)
private void _SaveClip(GameElement element, string clipName)
{
List<BaseElement_BM> clip = new List<BaseElement_BM>();
folder.SaveBM();
folder.matchedBM.attachedElementGuid = Guid.Empty;
clip.Add(folder.matchedBM);
folder.submoduleList.ForEach(s =>
{
s.SaveBM();
clip.Add(s.matchedBM);
});
folder.GetAllGameElementsFromThis(false).ForEach(e =>
element.GetAllGameElementsFromThis().ForEach(e =>
{
e.SaveBM();
clip.Add(e.matchedBM);
@@ -255,7 +255,7 @@ namespace Ichni
ES3.Save("Clip", clip, filePath, ProjectManager.SaveSettings);
}
public void LoadClip(string clipName)
private void _LoadClip(GameElement target, string clipName)
{
List<BaseElement_BM> GetAllAttachedBaseElements(GameElement_BM gameElement, List<BaseElement_BM> clip)
{
@@ -274,9 +274,23 @@ namespace Ichni
string filePath = Application.streamingAssetsPath + "/Clips/" + clipName + ".json";
List<BaseElement_BM> clip = ES3.Load<List<BaseElement_BM>>("Clip", filePath, ProjectManager.SaveSettings);
//对于第一个元素,需要特殊处理,将它放入目标物体的子物体列表中
GameElement_BM first = clip[0] as GameElement_BM;
List<BaseElement_BM> firstAttaches = GetAllAttachedBaseElements(first, clip);
first.elementGuid = Guid.NewGuid();
GameElement_BM.identifier.TryAdd(first.elementGuid, first);
firstAttaches.ForEach(e => { e.attachedElementGuid = first.elementGuid; });
//将目标物体临时存入读存档的Dictionary中
target.SaveBM();
GameElement_BM.identifier.TryAdd(target.elementGuid, target.matchedBM as GameElement_BM);
(target.matchedBM as GameElement_BM).matchedElement = target;
first.attachedElementGuid = target.elementGuid;
foreach (BaseElement_BM element in clip)
for (var index = 1; index < clip.Count; index++)
{
var element = clip[index];
if (element is GameElement_BM gameElement)
{
List<BaseElement_BM> attachedElements = GetAllAttachedBaseElements(gameElement, clip);
@@ -285,8 +299,13 @@ namespace Ichni
attachedElements.ForEach(e => { e.attachedElementGuid = gameElement.elementGuid; });
}
}
first.ExecuteBM();
clip.ForEach(e => e.ExecuteBM());
for (var index = 1; index < clip.Count; index++)
{
clip[index].ExecuteBM();
}
}
}