Files
UmaViewer/Assets/Scripts/UmaViewerUI.cs
T

1604 lines
63 KiB
C#

using Gallop;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Rendering;
using UnityEngine.UI;
//修改(载入通用服装ColorSet相关)(保存当前选中的颜色配置到TXT文件)
using System.Text;
public class UmaViewerUI : MonoBehaviour
{
public static UmaViewerUI Instance;
private UmaViewerMain Main => UmaViewerMain.Instance;
private UmaViewerBuilder Builder => UmaViewerBuilder.Instance;
public CanvasScaler canvasScaler;
[Header("normal models")]
public ScrollRect CharactersList;
public ScrollRect MobCharactersList;
public PageManager MobCharactersPageCtrl;
public ScrollRect CostumeList;
public ScrollRect HeadCostumeList;
public ScrollRect MobCostumeList;
public ScrollRect AnimationSetList;
public ScrollRect AnimationList;
public PageManager AnimationPageCtrl;
[Header("mini models")]
public ScrollRect MiniCharactersList;
public ScrollRect MiniCostumeList;
public ScrollRect MiniAnimationSetList;
public ScrollRect MiniAnimationList;
public PageManager MiniAnimationPageCtrl;
[Header("other")]
public ScrollRect PropList;
public PageManager PropPageCtrl;
public ScrollRect SceneList;
public PageManager ScenePageCtrl;
public ScrollRect MessageScrollRect;
public TextMeshProUGUI MessageText;
public PoseManager PoseManager;
public HandleManager HandleManager;
//修改(载入通用服装ColorSet相关)
public ScrollRect DressColorSetList;
[Header("lists")]
public ScrollRect EmotionList;
public Transform FacialList;
public ScrollRect EarList;
public ScrollRect EyeBrowList;
public ScrollRect EyeList;
public ScrollRect MouthList;
public ScrollRect OtherList;
public ScrollRect LiveList;
public ScrollRect LiveSoundList;
public ScrollRect LiveCharaSoundList;
public ScrollRect NormalSoundList;
public ScrollRect NormalSubSoundList;
public PageManager NormalSoundCtrl;
[Header("jukebox ui resources")]
public Button JukeboxButton;
public JukeboxSelectionView JukeboxPanel;
private GameObject JukeboxSoundPanel;
private ScrollRect JukeboxSoundList;
private Image JukeboxSelectedJacket;
private GameObject JukeboxSelectedPlaceholder;
private Text JukeboxSelectedInfo;
private Toggle JukeboxShortVersionToggle;
private Toggle JukeboxGameSizeVersionToggle;
private UmaUIContainer JukeboxMusicItemPrefab;
private JukeboxMusicSelect JukeboxSelectedItem;
private MasterJukeboxMusicData.JukeboxMusicData JukeboxSelectedMusic;
private bool JukeboxUseGameSize;
private bool JukeboxListLoaded;
private bool JukeboxListLoading;
[Header("pose mode")]
public Transform HandlesPanel;
[Header("live settings")]
public Sprite CharaIconDefault;
public Sprite CostumeIconDefault;
public LiveCharacterSelect CurrentSeletChara;
public GameObject LiveSelectPrefab;
public GameObject LiveSelectPannel;
public GameObject SelectCharacterPannel;
public ScrollRect LiveSelectList;
public Image LiveSelectImage;
public Text LiveSelectInfoText;
private LiveEntry currentLive;
[Header("settings")]
public UISettingsCamera CameraSettings;
public UISettingsModel ModelSettings;
public UISettingsSound AudioSettings;
public UISettingsAnimation AnimationSettings;
public UISettingsScreenshot ScreenshotSettings;
public UISettingsAssets AssetSettings;
public UISettingsOther OtherSettings;
public List<GameObject> TogglablePanels = new List<GameObject>();
public List<GameObject> TogglableFacials = new List<GameObject>();
[Header("prefabs")]
public GameObject UmaContainerPrefab;
public GameObject UmaContainerCostumePrefab;
public GameObject UmaContainerLivePrefab;
public GameObject UmaContainerSliderPrefab;
public UmaUIContainer UmaContainerTogglePrefab;
public Color UIColor1, UIColor2;
public string CurrentHeadCostumeId = string.Empty;
[Header("Live")]
public bool LiveTime = false;
public bool isRecordVMD;
public bool isRequireStage = true;
//修改(Live演出的显隐控制和位置变换相关)
public bool isControlMode = true;
public Dropdown LiveRecoedToggle;
public int LiveMode = 1;
public FaceDrivenKeyTarget currentFaceDrivenKeyTarget;
private void Awake()
{
Instance = this;
}
private void Start()
{
OtherSettings.ApplySettings();
SetupJukeboxSoundUI();
CameraSettings.AAModeDropdown.SetValueWithoutNotify(Config.Instance.AntiAliasing);
AssetSettings.LoadedAssetsClear();
UmaAssetManager.OnLoadedBundleUpdate += AssetSettings.LoadedAssetsAdd;
UmaAssetManager.OnLoadedBundleRemove += AssetSettings.LoadedAssetsRemove;
UmaAssetManager.OnLoadedBundleClear += AssetSettings.LoadedAssetsClear;
PoseManager.LoadLocalPoseFiles();
#if UNITY_ANDROID && !UNITY_EDITOR
canvasScaler.referenceResolution = new Vector2(1280, 720);
#endif
StartCoroutine(ApplyGraphicsSettings());
}
private void OnDestroy()
{
UmaAssetManager.OnLoadedBundleUpdate -= AssetSettings.LoadedAssetsAdd;
UmaAssetManager.OnLoadedBundleRemove -= AssetSettings.LoadedAssetsRemove;
UmaAssetManager.OnLoadedBundleClear -= AssetSettings.LoadedAssetsClear;
}
private void Update()
{
if (Builder.CurrentAudioSources.Count > 0 && Builder.CurrentAudioSources[0])
{
AudioSource MianSource = Builder.CurrentAudioSources[0];
if (MianSource.clip)
{
AudioSettings.UpdateTrack(MianSource);
}
}
var umaContainer = Builder.CurrentUMAContainer;
if (umaContainer != null && umaContainer.OverrideController != null)
{
AnimationSettings.UpdateAnimationInfo(umaContainer);
}
}
/// <summary>Some settings may not be saved when set in Start()</summary>
public IEnumerator ApplyGraphicsSettings()
{
yield return 0;
CameraSettings.AAModeDropdown.SetValueWithoutNotify(Config.Instance.AntiAliasing);
GraphicsSettings.renderPipelineAsset = Config.Instance.Region == Region.Global ? null : UmaViewerMain.Instance.DefaultRenderPipeline;
RuntimeGraphicsSettings.Apply();
}
public void HighlightChildImage(Transform mainObject, UmaUIContainer child)
{
foreach (var t in mainObject.GetComponentsInChildren<UmaUIContainer>())
{
if (t.transform.parent != mainObject) continue;
t.ToggleImage.enabled = (t == child);
}
}
public void LoadModelPanels()
{
var containerG = Instantiate(UmaContainerPrefab, AnimationSetList.content).GetComponent<UmaUIContainer>();
containerG.Name = containerG.name = "Generic";
containerG.Button.onClick.AddListener(() =>
{
HighlightChildImage(AnimationSetList.content, containerG);
ListAnimations(-1, false);
});
var containerT = Instantiate(UmaContainerPrefab, AnimationSetList.content).GetComponent<UmaUIContainer>();
containerT.Name = containerT.name = "Tail";
containerT.Button.onClick.AddListener(() =>
{
HighlightChildImage(AnimationSetList.content, containerT);
ListAnimations(-2, false);
});
var containerE = Instantiate(UmaContainerPrefab, AnimationSetList.content).GetComponent<UmaUIContainer>();
containerE.Name = containerE.name = "Ear";
containerE.Button.onClick.AddListener(() =>
{
HighlightChildImage(AnimationSetList.content, containerE);
ListAnimations(-3, false);
});
foreach (var chara in Main.Characters.OrderBy(c => c.Id))
{
var charaInstance = chara;
var container3 = Instantiate(UmaContainerPrefab,CharactersList.content).GetComponent<UmaUIContainer>();
UIPressScaleFeedback.AddTo(container3.gameObject);
container3.UseLocalizedText();
container3.Name = container3.name = chara.Id + " " + chara.GetName();
container3.Button.onClick.AddListener(() =>
{
HighlightChildImage(CharactersList.content, container3);
ListCostumes(charaInstance, false);
});
if (chara.Icon)
{
container3.Image.sprite = (chara.Icon == null ? CharaIconDefault : chara.Icon);
container3.Image.enabled = true;
}
var container4 = Instantiate(UmaContainerPrefab,AnimationSetList.content).GetComponent<UmaUIContainer>();
container4.UseLocalizedText();
container4.Name = container4.name = chara.Id + " " + chara.GetName();
container4.Button.onClick.AddListener(() =>
{
HighlightChildImage(AnimationSetList.content, container4);
ListAnimations(charaInstance.Id, false);
});
if (chara.Icon)
{
container4.Image.sprite = (chara.Icon == null ? CharaIconDefault : chara.Icon);
container4.Image.enabled = true;
}
}
var pageentrys = new List<PageManager.Entry>();
foreach (var chara in Main.MobCharacters.OrderBy(c => c.Id))
{
var charaInstance = chara;
var pageentry = new PageManager.Entry();
pageentry.Name = chara.GetName();
pageentry.UseLocalizedText = true;
pageentry.EnablePressScaleFeedback = true;
pageentry.OnClick = (container) =>
{
HighlightChildImage(MobCharactersList.content, container);
ListCostumes(charaInstance, false);
};
if (chara.Icon)
{
pageentry.Sprite = chara.Icon;
}
pageentrys.Add(pageentry);
}
MobCharactersPageCtrl.Initialize(pageentrys, MobCharactersList);
}
public void LoadMiniModelPanels()
{
var container1 = Instantiate(UmaContainerPrefab, MiniAnimationSetList.content).GetComponent<UmaUIContainer>();
container1.Name = container1.name = "General";
container1.Button.onClick.AddListener(() =>
{
HighlightChildImage(MiniAnimationSetList.content, container1);
ListAnimations(-1, true);
});
foreach (var chara in Main.Characters.OrderBy(c => c.Id))
{
var charaInstance = chara;
var container2 = Instantiate(UmaContainerPrefab,MiniCharactersList.content).GetComponent<UmaUIContainer>();
UIPressScaleFeedback.AddTo(container2.gameObject);
container2.UseLocalizedText();
container2.Name = container2.name = chara.Id + " " + chara.GetName();
container2.Button.onClick.AddListener(() =>
{
HighlightChildImage(MiniCharactersList.content, container2);
ListCostumes(charaInstance, true);
});
if (chara.Icon)
{
container2.Image.sprite = chara.Icon;
container2.Image.enabled = true;
}
var container3 = Instantiate(UmaContainerPrefab, MiniAnimationSetList.content).GetComponent<UmaUIContainer>();
container3.UseLocalizedText();
container3.Name = container3.name = chara.Id + " " + chara.GetName();
container3.Button.onClick.AddListener(() =>
{
HighlightChildImage(MiniAnimationSetList.content, container3);
ListAnimations(charaInstance.Id, true);
});
if (chara.Icon)
{
container3.Image.sprite = chara.Icon;
container3.Image.enabled = true;
}
}
}
public void LoadFacialPanels(FaceDrivenKeyTarget target)
{
currentFaceDrivenKeyTarget = target;
if(FacialList)
foreach (UmaUIContainer ui in FacialList.GetComponentsInChildren<UmaUIContainer>(true))
{
Destroy(ui.gameObject);
}
if (target == null) return;
List<FacialMorph> targetMorph = new List<FacialMorph>(target.OtherMorphs);
Action<GameObject, FaceDrivenKeyTarget, List<FacialMorph>, ScrollRect> action = (UmaContainerSliderPrefab, target, targetMorph, TargetList) =>
{
foreach (FacialMorph morph in targetMorph)
{
if (morph is FacialOtherMorph extraMorph)
{
foreach (var prop in extraMorph.BindProperties)
{
var container = Instantiate(UmaContainerSliderPrefab, TargetList.content).GetComponent<UmaUIContainer>();
container.Name = $"{morph.name}({prop.Type})";
container.Slider.value = prop.Value;
switch (prop.Type)
{
case BindProperty.BindType.Shader:
case BindProperty.BindType.Texture:
container.Slider.maxValue = 1;
container.Slider.minValue = 0;
break;
case BindProperty.BindType.Select:
container.Slider.wholeNumbers = true;
container.Slider.maxValue = prop.BindPrefabs.Count - 1;
container.Slider.minValue = 0;
break;
case BindProperty.BindType.EyeSelect:
container.Slider.wholeNumbers = true;
container.Slider.maxValue = prop.BindPrefabs.Count / 2 - 1;
container.Slider.minValue = 0;
break;
case BindProperty.BindType.Enable:
case BindProperty.BindType.TearSelect:
case BindProperty.BindType.TearSide:
container.Slider.wholeNumbers = true;
container.Slider.maxValue = 1;
container.Slider.minValue = 0;
break;
case BindProperty.BindType.TearWeight:
container.Slider.maxValue = 1;
container.Slider.minValue = 0;
break;
case BindProperty.BindType.TearSpeed:
container.Slider.maxValue = 2;
container.Slider.minValue = 0;
break;
}
container.Slider.onValueChanged.AddListener((a) =>
{
target.ChangeMorphWeight(prop, a, morph);
});
}
}
else
{
var container = Instantiate(UmaContainerSliderPrefab, TargetList.content).GetComponent<UmaUIContainer>();
container.Name = $"{morph.name}({morph.tag})";
container.Slider.value = morph.weight;
container.Slider.maxValue = 1;
container.Slider.minValue = morph.tag.Contains("Range") ? -1 : 0;
container.Slider.onValueChanged.AddListener((a) => { target.ChangeMorphWeight(morph, a); });
}
}
};
action(UmaContainerSliderPrefab, target, target.EarMorphs, EarList);
action(UmaContainerSliderPrefab, target, target.EyeBrowMorphs, EyeBrowList);
action(UmaContainerSliderPrefab, target, target.EyeMorphs, EyeList);
action(UmaContainerSliderPrefab, target, target.MouthMorphs, MouthList);
action(UmaContainerSliderPrefab, target, targetMorph, OtherList);
}
//修改(Mini角色表情滑条修复)
public void LoadFacialPanelsMini(Material Eye, Material MayuL, Material MayuR, Material Mouth)
{
currentFaceDrivenKeyTarget = null;
foreach (UmaUIContainer ui in FacialList.GetComponentsInChildren<UmaUIContainer>(true))
{
Destroy(ui.gameObject);
}
var action = new Action<ScrollRect, string, Material, int>((list, name, mat, type) =>
{
if (mat == null) return;
var container = Instantiate(UmaContainerSliderPrefab, list.content).GetComponent<UmaUIContainer>();
container.Name = name;
container.Slider.wholeNumbers = true;
container.Slider.value = 0;
if (type == 0)
container.Slider.maxValue = 18;
else if (type == 1)
container.Slider.maxValue = 14;
else if (type == 2)
container.Slider.maxValue = 14;
else if (type == 3)
container.Slider.maxValue = 8;
container.Slider.minValue = 0;
container.Slider.onValueChanged.AddListener((a) =>
{
var val = Convert.ToInt32(a);
if (type == 0)
{
mat.mainTextureOffset = new Vector2(0.25f * (val % 4), -0.125f * (val / 4));
}
else if (type == 1)
{
var vector = mat.GetVector("_UVOffset");
mat.SetVector("_UVOffset", new Vector4(0.25f * (val % 4), -0.125f * (val / 4), vector.z, vector.w));
}
else if (type == 2)
{
var vector = mat.GetVector("_UVOffset");
mat.SetVector("_UVOffset", new Vector4(vector.x, vector.y, 0.25f * (val % 4), -0.125f * (val / 4)));
}
else if (type == 3)
{
mat.mainTextureOffset = new Vector2(0.25f * (val % 4), -0.25f * (val / 4));
}
});
});
action(EyeList, "Eye_L_Select", Eye, 2);
action(EyeList, "Eye_R_Select", Eye, 1);
action(MouthList, "Mouth_Select", Mouth, 0);
action(EyeBrowList, "Mayu_L_Select", MayuL, 3);
action(EyeBrowList, "Mayu_R_Select", MayuR, 3);
}
public void UpdateFacialPanels()
{
if (currentFaceDrivenKeyTarget)
LoadFacialPanels(currentFaceDrivenKeyTarget);
}
public void LoadEmotionPanels(FaceEmotionKeyTarget target)
{
if (EmotionList)
foreach (UmaUIContainer ui in EmotionList.content.GetComponentsInChildren<UmaUIContainer>())
{
Destroy(ui.gameObject);
}
if (target == null) return;
foreach (var emotion in target.FaceEmotionKey)
{
if (emotion.label == "Base")
{
continue;
}
var container = Instantiate(UmaContainerSliderPrefab, EmotionList.content).GetComponent<UmaUIContainer>();
container.Name = emotion.label;
container.Slider.value = emotion.weight;
container.Slider.maxValue = 1;
container.Slider.minValue = 0;
container.Slider.onValueChanged.AddListener((a) => { target.ChangeEmotionWeight(emotion, a); });
}
}
public void LoadLivePanels()
{
Action<LiveEntry, GameObject, ScrollRect, UnityAction> createContainer = (live, containerPrefab, parent, onClickAction) =>
{
var container = Instantiate(containerPrefab, parent.content).GetComponent<UmaUIContainer>();
container.Name = " " + live.MusicId + " " + live.SongName;
container.Image.sprite = live.Icon;
container.Image.enabled = container.Image.sprite;
container.Button.onClick.AddListener(() =>
{
HighlightChildImage(parent.content, container);
onClickAction.Invoke();
});
};
foreach (var live in Main.Lives.OrderBy(c => c.MusicId))
{
createContainer(live, UmaContainerLivePrefab, LiveList, () => ShowLiveSelectPanel(live));
createContainer(live, UmaContainerLivePrefab, LiveSoundList, () => ListLiveSounds(live.MusicId));
}
}
public void LoadNormalSoundPanels()
{
NormalSoundCtrl.Clear();
var pageentrys = new List<PageManager.Entry>();
foreach (var entry in Main.AbSounds.Where(a => (!a.Name.Contains("live")) && a.Name.EndsWith("awb")))
{
var pageentry = new PageManager.Entry();
pageentry.Name = Path.GetFileNameWithoutExtension(entry.Name);
pageentry.OnClick = (container) =>
{
HighlightChildImage(NormalSoundList.content, container);
ListNormalSubSounds(entry);
};
pageentrys.Add(pageentry);
}
NormalSoundCtrl.Initialize(pageentrys, NormalSoundList);
}
private void SetupJukeboxSoundUI()
{
if (JukeboxButton == null || JukeboxPanel == null || JukeboxPanel.SongItemPrefab == null)
{
Debug.LogWarning(
"[UmaViewerUI] Assign JukeboxButton, JukeboxPanel and its SongItemPrefab in the Inspector.");
return;
}
if (JukeboxPanel.CloseButton == null ||
JukeboxPanel.ShortVersionToggle == null ||
JukeboxPanel.GameSizeVersionToggle == null ||
JukeboxPanel.SelectedJacket == null ||
JukeboxPanel.SelectedInfo == null ||
JukeboxPanel.SongList == null ||
JukeboxPanel.SongList.content == null ||
JukeboxPanel.SongItemPrefab.Button == null ||
JukeboxPanel.SongItemPrefab.Image == null)
{
Debug.LogWarning("[UmaViewerUI] JukeboxPanel has missing Inspector references.");
return;
}
JukeboxSoundPanel = JukeboxPanel.gameObject;
JukeboxSoundList = JukeboxPanel.SongList;
JukeboxSelectedJacket = JukeboxPanel.SelectedJacket;
JukeboxSelectedPlaceholder = JukeboxPanel.SelectedJacketPlaceholder;
JukeboxSelectedInfo = JukeboxPanel.SelectedInfo;
JukeboxShortVersionToggle = JukeboxPanel.ShortVersionToggle;
JukeboxGameSizeVersionToggle = JukeboxPanel.GameSizeVersionToggle;
JukeboxMusicItemPrefab = JukeboxPanel.SongItemPrefab;
JukeboxButton.onClick.AddListener(OpenJukeboxSoundPanel);
JukeboxPanel.CloseButton.onClick.AddListener(CloseJukeboxSoundPanel);
JukeboxShortVersionToggle.onValueChanged.AddListener(OnJukeboxShortVersionChanged);
JukeboxGameSizeVersionToggle.onValueChanged.AddListener(OnJukeboxGameSizeVersionChanged);
JukeboxShortVersionToggle.SetIsOnWithoutNotify(true);
JukeboxGameSizeVersionToggle.SetIsOnWithoutNotify(false);
JukeboxSoundPanel.SetActive(false);
RefreshJukeboxVersionUI();
}
private void OpenJukeboxSoundPanel()
{
JukeboxButton.interactable = false;
JukeboxSoundPanel.transform.SetAsLastSibling();
JukeboxSoundPanel.SetActive(true);
LoadJukeboxSoundPanels();
}
private void CloseJukeboxSoundPanel()
{
if (JukeboxSoundPanel != null)
JukeboxSoundPanel.SetActive(false);
if (JukeboxButton != null)
JukeboxButton.interactable = true;
}
private IEnumerator PopulateJukeboxGrid(List<MasterJukeboxMusicData.JukeboxMusicData> musicEntries)
{
for (int i = JukeboxSoundList.content.childCount - 1; i >= 0; i--)
Destroy(JukeboxSoundList.content.GetChild(i).gameObject);
for (int i = 0; i < musicEntries.Count; i++)
{
if (JukeboxSoundPanel == null || JukeboxSoundList == null)
yield break;
MasterJukeboxMusicData.JukeboxMusicData music = musicEntries[i];
Sprite jacket = null;
try
{
jacket = Builder.LoadLiveIcon(music.MusicId);
}
catch (Exception ex)
{
Debug.LogWarning($"[UmaViewerUI] Could not load jacket for music {music.MusicId}: {ex.Message}");
}
JukeboxMusicSelect item = CreateJukeboxGridItem(music, jacket);
if (JukeboxSelectedMusic == null)
SelectJukeboxMusic(item, false);
// AssetBundle loading is synchronous. Yield in small batches so opening
// a large regional jukebox does not freeze the UI for one long frame.
if ((i + 1) % 8 == 0)
yield return null;
}
JukeboxSoundList.verticalNormalizedPosition = 1f;
JukeboxListLoaded = true;
JukeboxListLoading = false;
if (musicEntries.Count == 0)
{
JukeboxSelectedInfo.text = "No playable music found";
}
}
private JukeboxMusicSelect CreateJukeboxGridItem(
MasterJukeboxMusicData.JukeboxMusicData music,
Sprite jacket)
{
UmaUIContainer view = Instantiate(JukeboxMusicItemPrefab, JukeboxSoundList.content);
JukeboxMusicSelect item = view.GetComponent<JukeboxMusicSelect>();
if (item == null)
item = view.gameObject.AddComponent<JukeboxMusicSelect>();
string title = JukeboxUtil.ResolveSongTitle(music, UmaDatabaseController.Instance.LiveData);
item.Initialize(this, view, music, jacket, title);
return item;
}
public void SelectJukeboxMusic(JukeboxMusicSelect item, bool playImmediately = true)
{
if (item == null || item.Music == null)
return;
if (JukeboxSelectedItem != null)
JukeboxSelectedItem.SetSelected(false);
JukeboxSelectedItem = item;
JukeboxSelectedItem.SetSelected(true);
JukeboxSelectedMusic = item.Music;
JukeboxSelectedJacket.sprite = item.Jacket;
JukeboxSelectedJacket.enabled = item.Jacket != null;
if (JukeboxSelectedPlaceholder != null)
JukeboxSelectedPlaceholder.SetActive(item.Jacket == null);
if (JukeboxUseGameSize && !item.Music.HasGameSizeAudio)
JukeboxUseGameSize = false;
else if (!JukeboxUseGameSize && !item.Music.HasShortAudio && item.Music.HasGameSizeAudio)
JukeboxUseGameSize = true;
RefreshJukeboxVersionUI();
if (playImmediately)
PlaySelectedJukeboxMusic();
}
private void SelectJukeboxVersion(bool useGameSize)
{
if (JukeboxSelectedMusic != null)
{
if (useGameSize && !JukeboxSelectedMusic.HasGameSizeAudio)
return;
if (!useGameSize && !JukeboxSelectedMusic.HasShortAudio)
return;
}
JukeboxUseGameSize = useGameSize;
RefreshJukeboxVersionUI();
PlaySelectedJukeboxMusic();
}
private void OnJukeboxShortVersionChanged(bool isOn)
{
if (!isOn)
{
if (!JukeboxGameSizeVersionToggle.isOn)
JukeboxShortVersionToggle.SetIsOnWithoutNotify(true);
return;
}
JukeboxGameSizeVersionToggle.SetIsOnWithoutNotify(false);
SelectJukeboxVersion(false);
}
private void OnJukeboxGameSizeVersionChanged(bool isOn)
{
if (!isOn)
{
if (!JukeboxShortVersionToggle.isOn)
JukeboxGameSizeVersionToggle.SetIsOnWithoutNotify(true);
return;
}
JukeboxShortVersionToggle.SetIsOnWithoutNotify(false);
SelectJukeboxVersion(true);
}
private void RefreshJukeboxVersionUI()
{
bool hasShort = JukeboxSelectedMusic == null || JukeboxSelectedMusic.HasShortAudio;
bool hasGameSize = JukeboxSelectedMusic == null || JukeboxSelectedMusic.HasGameSizeAudio;
if (JukeboxShortVersionToggle != null)
{
JukeboxShortVersionToggle.interactable = hasShort;
JukeboxShortVersionToggle.SetIsOnWithoutNotify(!JukeboxUseGameSize);
}
if (JukeboxGameSizeVersionToggle != null)
{
JukeboxGameSizeVersionToggle.interactable = hasGameSize;
JukeboxGameSizeVersionToggle.SetIsOnWithoutNotify(JukeboxUseGameSize);
}
if (JukeboxSelectedMusic != null && JukeboxSelectedInfo != null)
{
string title = JukeboxUtil.ResolveSongTitle(
JukeboxSelectedMusic,
UmaDatabaseController.Instance.LiveData);
string version = JukeboxUseGameSize ? "GAME SIZE Ver." : "SHORT Ver.";
JukeboxSelectedInfo.text =
$"{title}\nMUSIC ID {JukeboxSelectedMusic.MusicId}\n{version}";
}
}
private void PlaySelectedJukeboxMusic()
{
if (JukeboxSelectedMusic != null)
HomeJukeboxController.Instance.SetupMusicAndPlay(
JukeboxSelectedMusic.MusicId,
JukeboxUseGameSize);
}
public void LoadJukeboxSoundPanels()
{
if (JukeboxSoundList == null || JukeboxListLoaded || JukeboxListLoading)
return;
try
{
MasterJukeboxMusicData master = UmaDatabaseController.Instance.JukeboxMusicData;
if (master == null)
{
ShowMessage("Jukebox master data is not available.", UIMessageType.Warning);
return;
}
var musicEntries = new List<MasterJukeboxMusicData.JukeboxMusicData>();
foreach (MasterJukeboxMusicData.JukeboxMusicData music in master.GetAll())
{
if (!music.TryGetPreferredAudio(out _, out _))
continue;
musicEntries.Add(music);
}
JukeboxListLoading = true;
StartCoroutine(PopulateJukeboxGrid(musicEntries));
}
catch (Exception ex)
{
JukeboxListLoading = false;
Debug.LogError($"[UmaViewerUI] Could not load Jukebox UI: {ex}");
ShowMessage($"Could not load Jukebox: {ex.Message}", UIMessageType.Error);
}
}
public void LoadPropPanel()
{
var pageentrys = new List<PageManager.Entry>();
foreach (var prop in Main.AbList
.Where(a => IsPreviewableProp(a.Key))
.OrderBy(a => a.Key, StringComparer.OrdinalIgnoreCase))
{
var pageentry = new PageManager.Entry();
pageentry.Name = Path.GetFileName(prop.Key);
pageentry.OnClick = (container) =>
{
HighlightChildImage(PropList.content, container);
Builder.LoadProp(prop.Value);
};
pageentrys.Add(pageentry);
}
PropPageCtrl.Initialize(pageentrys, PropList);
}
private static bool IsPreviewableProp(string path)
{
if (string.IsNullOrEmpty(path) || path.IndexOf("clothes", StringComparison.OrdinalIgnoreCase) >= 0)
return false;
string fileName = Path.GetFileName(path);
if (!fileName.StartsWith("pfb_", StringComparison.OrdinalIgnoreCase))
return false;
// Legacy assets use pfb_chr_prop; newer hand-held assets may be filed under
// prop/item/accessory directories with different prefab names.
return path.IndexOf("/prop/", StringComparison.OrdinalIgnoreCase) >= 0 ||
path.IndexOf("/props/", StringComparison.OrdinalIgnoreCase) >= 0 ||
path.IndexOf("/item/", StringComparison.OrdinalIgnoreCase) >= 0 ||
path.IndexOf("/accessory/", StringComparison.OrdinalIgnoreCase) >= 0 ||
fileName.IndexOf("prop", StringComparison.OrdinalIgnoreCase) >= 0 ||
fileName.IndexOf("item", StringComparison.OrdinalIgnoreCase) >= 0 ||
fileName.IndexOf("accessory", StringComparison.OrdinalIgnoreCase) >= 0;
}
public void LoadMapPanel()
{
var pageentrys = new List<PageManager.Entry>();
foreach (var scene in Main.AbList.Where(a => ((a.Key.StartsWith("3d/env") && Path.GetFileName(a.Key).StartsWith("pfb_")) || a.Key.StartsWith("cutt/cutt_son") && Path.GetFileName(a.Key).StartsWith("cutt_son"))))
{
var pageentry = new PageManager.Entry();
pageentry.Name = Path.GetFileName(scene.Key);
pageentry.FontSize = 19;
pageentry.OnClick = (container) =>
{
HighlightChildImage(SceneList.content, container);
Builder.LoadProp(scene.Value);
};
pageentrys.Add(pageentry);
}
ScenePageCtrl.Initialize(pageentrys, SceneList);
}
public void PlayLive()
{
if (currentLive == null) return;
PoseManager.SetPoseMode(false);
var selectlist = LiveSelectList.content.GetComponentsInChildren<LiveCharacterSelect>();
if (selectlist != null)
{
LiveTime = true;
ModelSettings.SetEyeTrackingEnable(false);
Builder.LoadLive(currentLive, new List<LiveCharacterSelect>(selectlist));
LiveSelectPannel.SetActive(false);
}
}
void ShowLiveSelectPanel(LiveEntry entry, bool loadPreview = true)
{
LiveSelectPannel.SetActive(true);
if (loadPreview) Builder.loadLivePreviewSound(entry.MusicId);
bool keepSelections = currentLive == entry;
CurrentSeletChara = null;
if (!keepSelections)
{
for (int i = LiveSelectList.content.childCount - 1; i >= 0; i--)
{
var child = LiveSelectList.content.GetChild(i);
child.SetParent(null);
Destroy(child.gameObject);
}
}
currentLive = entry;
LiveSelectImage.sprite = entry.Icon;
int memberCount = entry.MemberCount;
if (LiveMode == 0)
{
memberCount = Main.AbMotions.Count(a => a.Name.StartsWith($"3d/motion/live/body/son{entry.MusicId}") && Path.GetFileName(a.Name).Split('_').Length == 4);
LiveSelectInfoText.text = $"{entry.SongName}\nMain Members Count: {memberCount}";
}
else
{
LiveSelectInfoText.text = $"{entry.SongName}\nMembers Count: {memberCount}";
}
var selections = new List<LiveCharacterSelect>(LiveSelectList.content.GetComponentsInChildren<LiveCharacterSelect>());
for (int i = selections.Count - 1; i >= memberCount; i--)
{
selections[i].transform.SetParent(null);
Destroy(selections[i].gameObject);
selections.RemoveAt(i);
}
for (int i = selections.Count; i < memberCount; i++)
{
var chara = Instantiate(LiveSelectPrefab, LiveSelectList.content).GetComponent<LiveCharacterSelect>();
chara.IndexText.text = (i + 1).ToString();
chara.GetComponent<Button>().onClick.AddListener(() =>
{
chara.SelectChara(this);
foreach (var t in LiveSelectList.content.GetComponentsInChildren<LiveCharacterSelect>())
{
t.GetComponentInChildren<Text>().color = (t == chara ? (LiveMode == 1 ? Color.green : Color.white) : Color.black);
}
});
selections.Add(chara);
}
foreach (var selection in selections)
selection.GetComponentInChildren<Text>().color = Color.black;
if (!LiveAutoSelection.FillMissing(Main, entry, selections, CostumeIconDefault))
ShowMessage("Cannot fill the live selection: no available character-owned winning costume matches an empty slot.", UIMessageType.Warning);
}
void ListCostumes(CharaEntry chara, bool mini)
{
ScrollRect costumeList = mini ? MiniCostumeList : chara.IsMob ? MobCostumeList : CostumeList;
for (int i = costumeList.content.childCount - 1; i >= 0; i--)
{
Destroy(costumeList.content.GetChild(i).gameObject);
}
Action<CharaEntry> action = delegate (CharaEntry achara)
{
string nameVar = mini ? $"pfb_mbdy{achara.Id}" : $"pfb_bdy{achara.Id}";
string bodyPath = mini ? UmaDatabaseController.MiniBodyPath : UmaDatabaseController.BodyPath;
foreach (var entry in Main.AbChara.Where(a => a.Name.StartsWith(bodyPath) && !a.Name.Contains("clothes") && a.Name.Contains(nameVar)))
{
var container = Instantiate(UmaContainerCostumePrefab, costumeList.content).GetComponent<UmaUIContainer>();
UIPressScaleFeedback.AddTo(container.gameObject);
string[] split = entry.Name.Split('_');
string costumeId = split[split.Length - 1];
var dressdata = Main.Costumes.FirstOrDefault(a => (a.CharaId == achara.Id && a.BodyTypeSub == int.Parse(costumeId)));
container.Name = container.name = GetCostumeName(costumeId, (dressdata == null ? costumeId : dressdata.DressName));
container.Image.sprite = (dressdata == null ? CostumeIconDefault : dressdata.Icon);
container.Image.enabled = true;
container.Id = costumeId;
container.Button.onClick.AddListener(() =>
{
if (LiveSelectPannel.activeInHierarchy && CurrentSeletChara)
{
CurrentSeletChara.SetValue(achara, costumeId, container.Image.sprite, CurrentHeadCostumeId);
}
else
{
HighlightChildImage(costumeList.content, container);
var list = new List<UmaDatabaseEntry>();
list.AddRange(Main.AbChara.Where(a => a.Name.StartsWith(UmaDatabaseController.BodyPath) && a.Name.Contains(chara.Id.ToString())));
list.AddRange(Main.AbChara.Where(a => a.Name.StartsWith(UmaDatabaseController.HeadPath) && a.Name.Contains(chara.Id.ToString())));
var tail_path = getCharaTailPath(chara);
if (!string.IsNullOrEmpty(tail_path))
{
list.AddRange(Main.AbChara.Where(a => a.Name.StartsWith(tail_path)));
}
list.Add(Main.AbList["3d/animator/drivenkeylocator"]);
list.Add(Main.AbList[$"3d/motion/event/body/chara/chr{achara.Id}_00/anm_eve_chr{achara.Id}_00_idle01_loop"]);
Builder.UnloadUma();
//UmaAssetManager.UnloadAllBundle(true);
UmaAssetManager.PreLoadAndRun(list , delegate {
StartCoroutine(Builder.LoadUma(achara, costumeId, mini, CurrentHeadCostumeId));
});
//StartCoroutine(Builder.LoadUma(achara, costumeId, mini));
}
});
}
};
if (!chara.IsMob)
{
action.Invoke(chara);
}
//Common costumes
List<string> costumes = new List<string>();
var nameVar = mini ? "pfb_mbdy0" : $"pfb_bdy0";
string bodyPath = mini ? UmaDatabaseController.MiniBodyPath : UmaDatabaseController.BodyPath;
foreach (var entry in Main.AbChara.Where(a => a.Name.StartsWith(bodyPath) && !a.Name.Contains("/clothes/") && a.Name.Contains(nameVar)))
{
string id = Path.GetFileName(entry.Name);
string[] split = id.Split('_');
if (split.Length < 4) continue; //prevents error for mini umas
id = split[1].Substring(mini ? 4 : 3) + "_" + split[2] + "_" + split[3];
if (!costumes.Contains(id))
{
costumes.Add(id);
string costumeId = id;
var container = Instantiate(UmaContainerCostumePrefab, costumeList.content).GetComponent<UmaUIContainer>();
UIPressScaleFeedback.AddTo(container.gameObject);
var data = costumeId.Split('_');
var bodytype = int.Parse(data[0]);
var bodysub = int.Parse(data[1]);
var dressdata = Main.Costumes.FirstOrDefault(a => a.BodyType == bodytype && a.BodyTypeSub == bodysub);
container.Name = container.name = GetCostumeName(id, (dressdata == null ? id : dressdata.DressName));
container.Image.sprite = (dressdata == null ? CostumeIconDefault : dressdata.Icon);
container.Image.enabled = true;
container.Id = costumeId;
container.Button.onClick.AddListener(() =>
{
if (LiveSelectPannel.activeInHierarchy && CurrentSeletChara)
{
CurrentSeletChara.SetValue(chara, costumeId, container.Image.sprite, CurrentHeadCostumeId);
}
else
{
HighlightChildImage(costumeList.content, container);
var list = new List<UmaDatabaseEntry>();
list.AddRange(Main.AbChara.Where(a => a.Name.StartsWith(UmaDatabaseController.BodyPath) && a.Name.Contains(chara.Id.ToString())));
list.AddRange(Main.AbChara.Where(a => a.Name.StartsWith(UmaDatabaseController.HeadPath) && a.Name.Contains(chara.Id.ToString())));
var tail_path = getCharaTailPath(chara);
if (!string.IsNullOrEmpty(tail_path))
{
list.AddRange(Main.AbChara.Where(a => a.Name.StartsWith(tail_path)));
}
list.Add(Main.AbList["3d/animator/drivenkeylocator"]);
var motion_path = $"3d/motion/event/body/chara/chr{chara.Id}_00/anm_eve_chr{chara.Id}_00_idle01_loop";
if (!chara.IsMob && !ModelSettings.IsHeadFix && Main.AbList.TryGetValue(motion_path, out var motion_entry))
{
list.Add(motion_entry);
}
Builder.UnloadUma();
//UmaAssetManager.UnloadAllBundle(true);
UmaAssetManager.PreLoadAndRun(list, delegate { StartCoroutine(Builder.LoadUma(chara, costumeId, mini, CurrentHeadCostumeId)); });
//StartCoroutine(Builder.LoadUma(achara, costumeId, mini));
}
});
}
}
if(!chara.IsMob && !mini)
{
for (int i = HeadCostumeList.content.childCount - 1; i >= 0; i--)
{
Destroy(HeadCostumeList.content.GetChild(i).gameObject);
}
CurrentHeadCostumeId = string.Empty;
nameVar = $"/pfb_chr{chara.Id}_";
foreach (var entry in Main.AbChara.Where(a => a.Name.StartsWith($"{UmaDatabaseController.HeadPath}chr{chara.Id}_") && !a.Name.Contains("clothes") && a.Name.Contains(nameVar)))
{
var container = Instantiate(UmaContainerCostumePrefab, HeadCostumeList.content).GetComponent<UmaUIContainer>();
UIPressScaleFeedback.AddTo(container.gameObject);
string[] split = entry.Name.Split('_');
string head_costumeId = split[split.Length - 1];
var dressdata = Main.Costumes.FirstOrDefault(a => (a.CharaId == chara.Id && a.BodyTypeSub == int.Parse(head_costumeId)));
container.Name = container.name = GetCostumeName(head_costumeId, (dressdata == null ? head_costumeId : dressdata.DressName));
container.Image.sprite = (dressdata == null ? CostumeIconDefault : dressdata.Icon);
container.Image.enabled = true;
container.Id = head_costumeId;
container.Button.onClick.AddListener(() =>
{
CurrentHeadCostumeId = head_costumeId;
if (!LiveSelectPannel.activeInHierarchy || !CurrentSeletChara)
{
HighlightChildImage(HeadCostumeList.content, container);
}
bool hasBreak = false;
foreach (var t in CostumeList.GetComponentsInChildren<UmaUIContainer>())
{
if (t.ToggleImage.enabled)
{
hasBreak = true;
t.Button.onClick.Invoke();
break;
}
else if(CurrentSeletChara && CurrentSeletChara.CostumeId == t.Id)
{
hasBreak = true;
t.Button.onClick.Invoke();
break;
}
}
if (!hasBreak)
{
//if no costume selected, select first costume
if (CostumeList.content.childCount > 0)
{
CostumeList.content.GetChild(0).GetComponent<UmaUIContainer>().Button.onClick.Invoke();
}
}
});
}
}
}
void ListLiveSounds(int songid)
{
for (int i = LiveCharaSoundList.content.childCount - 1; i >= 0; i--)
{
Destroy(LiveCharaSoundList.content.GetChild(i).gameObject);
}
string nameVar = $"snd_bgm_live_{songid}_chara";
foreach (var entry in Main.AbSounds.Where(a => a.Name.Contains(nameVar) && a.Name.EndsWith("awb")))
{
var container = Instantiate(UmaContainerPrefab, LiveCharaSoundList.content).GetComponent<UmaUIContainer>();
string[] split = Path.GetFileNameWithoutExtension(entry.Name).Split('_');
string name = $"{split[split.Length - 2]} {getCharaName(split[split.Length - 2])}";
container.Name = name;
var icon = Main.Characters.Find(a => a.Id.ToString().Equals(split[split.Length - 2]));
if (icon != null)
{
container.Image.sprite = icon.Icon;
container.Image.enabled = true;
}
container.Button.onClick.AddListener(() =>
{
HighlightChildImage(LiveCharaSoundList.content, container);
Builder.LoadLiveSound(songid, entry);
});
}
}
void ListNormalSubSounds(UmaDatabaseEntry awb)
{
for (int i = NormalSubSoundList.content.childCount - 1; i >= 0; i--)
{
Destroy(NormalSubSoundList.content.GetChild(i).gameObject);
}
var subSounds = UmaViewerBuilder.LoadAudio(awb);
foreach (var sound in subSounds)
{
var container = Instantiate(UmaContainerPrefab, NormalSubSoundList.content).GetComponent<UmaUIContainer>();
container.Name = $"Clip_{subSounds.IndexOf(sound)}";
container.Button.onClick.AddListener(() =>
{
HighlightChildImage(NormalSubSoundList.content, container);
Builder.SetLastAudio(awb, subSounds.IndexOf(sound));
Builder.PlaySound(awb, subSounds.IndexOf(sound));
});
}
}
void ListAnimations(int umaId, bool mini)
{
ScrollRect animationList = mini ? MiniAnimationList : AnimationList;
PageManager pageManager = mini ? MiniAnimationPageCtrl : AnimationPageCtrl;
for (int i = animationList.content.childCount - 1; i >= 0; i--)
{
Destroy(animationList.content.GetChild(i).gameObject);
}
var filteredList = Main.AbMotions.Where(a => !a.Name.Contains($"mirror")
&& (mini ? a.Name.Contains($"mini") : !a.Name.Contains($"mini"))
&& !a.Name.Contains($"facial")
&& !a.Name.Contains($"_cam")
&& !a.Name.EndsWith($"_s")
&& !a.Name.EndsWith($"_e")
);
if (umaId == -1)
{
var pageentrys = new List<PageManager.Entry>();
foreach (var entry in filteredList.Where(a => (a.Name.Contains($"/type0") || a.Name.Contains($"/type99") || a.Name.Contains($"anm_sty_")) && !a.Name.Contains($"/tail") && !a.Name.EndsWith($"_pos") && !a.Name.Contains($"prop") && !a.Name.EndsWith($"_pose") && !a.Name.Contains($"_defaultmotion")))
{
var pageentry = new PageManager.Entry();
pageentry.Name = Path.GetFileName(entry.Name);
pageentry.FontSize = 20;
pageentry.OnClick = (container) =>
{
HighlightChildImage(animationList.content, container);
(Builder.CurrentUMAContainer)?.LoadAnimation(entry);
LoadedAnimation();
};
pageentrys.Add(pageentry);
}
pageManager.Initialize(pageentrys, animationList);
}
else if (umaId == -2)
{
foreach (var entry in filteredList.Where(a => a.Name.Contains($"/tail")))
{
CreateAnimationSelectionPanel(entry, animationList.content);
}
pageManager.ResetCtrl();
}
else if (umaId == -3)
{
foreach (var entry in Main.AbMotions.Where(a => a.Name.Contains($"type00_ear") && !a.Name.EndsWith("driven") && !a.Name.Contains("touch")))
{
CreateAnimationSelectionPanel(entry, animationList.content);
}
pageManager.ResetCtrl();
}
else
{
//Common animations
foreach (var entry in filteredList.Where(a => a.Name.Contains($"chara/chr{umaId}") && !a.Name.Contains("pose")))
{
CreateAnimationSelectionPanel(entry, animationList.content);
}
//Skill animations
foreach (var entry in filteredList.Where(a => a.Name.Contains($"card/body/crd{umaId}")))
{
CreateAnimationSelectionPanel(entry, animationList.content);
}
pageManager.ResetCtrl();
}
}
void CreateAnimationSelectionPanel(UmaDatabaseEntry entry, Transform parent)
{
var container = Instantiate(UmaContainerPrefab, parent).GetComponent<UmaUIContainer>();
container.Name = container.name = Path.GetFileName(entry.Name);
container.FontSize = 20;
container.Button.onClick.AddListener(() =>
{
HighlightChildImage(parent, container);
(Builder.CurrentUMAContainer)?.LoadAnimation(entry);
LoadedAnimation();
});
}
string getCharaName(string id)
{
var entry = Main.Characters.FirstOrDefault(a => a.Id.ToString().Equals(id));
return (entry == null) ? id.ToString() : entry.GetName();
}
public string getCharaTailPath(CharaEntry chara)
{
int tailId = 0;
if (chara.IsMob)
{
tailId = 1;
}
else
{
var charaData = UmaDatabaseController.ReadCharaData(chara);
tailId = Convert.ToInt32(charaData["tail_model_id"]);
}
if (tailId <= 0)
{
return string.Empty;
}
var tailName = $"tail{tailId.ToString().PadLeft(4, '0')}_00";
var tailPath = $"3d/chara/tail/{tailName}/";
var tailPfb = tailPath + $"pfb_{tailName}";
return tailPfb;
}
public static string GetCostumeName(string costumeId, string defaultname)
{
switch (costumeId)
{
// case "00":return "Default";
case "90":
return "Upgraded";
case "0001_00_01":
return "Race Shorts";
case "0001_00_02":
return "Race Bloomers";
case "0002_00_00":
return "School Short Sleeves";
case "0002_00_03":
return "School Short Sleeves Big Belly";
case "0002_01_00":
return "School Long Sleeves";
case "0002_01_03":
return "School Long Sleeves Big Belly";
case "0003_00_01":
return "Tracksuit Shorts";
case "0003_00_02":
return "Tracksuit Bloomers";
case "0003_01_01":
return "Tracksuit Long Pants";
case "0003_01_02":
return "Tracksuit Rolled Up Pants";
case "0004_00_00":
return "Swimsuit";
case "0004_01_00":
return "Towel";
default:
return (defaultname == "00") ? "Default" : defaultname;
}
}
public void LoadedAnimation()
{
var container = Builder.CurrentUMAContainer;
if (!container || !container.UmaAnimator) return;
AnimationSettings.ProgressSlider.SetValueWithoutNotify(0);
// Reset settings by Panel
container.UmaAnimator.speed = AnimationSettings.SpeedSlider.value;
Builder.AnimationCameraAnimator.speed = AnimationSettings.SpeedSlider.value;
if (container.UmaFaceAnimator)
container.UmaFaceAnimator.speed = AnimationSettings.SpeedSlider.value;
}
/// <summary> Toggles one object ON and all others from UI.TogglablePanels list OFF </summary>
public void ToggleUIPanel(GameObject go)
{
if (go.activeSelf || !TogglablePanels.Contains(go))
{
go.SetActive(!go.activeSelf);
return;
}
foreach (var panel in TogglablePanels)
{
panel.SetActive(panel == go);
}
}
public void ToggleUIFacial(GameObject go)
{
if (go.activeSelf || !TogglableFacials.Contains(go))
{
go.SetActive(!go.activeSelf);
return;
}
foreach (var panel in TogglableFacials)
{
panel.SetActive(panel == go);
}
}
public void RecordVMD()
{
var container = Builder.CurrentUMAContainer;
var camera = Builder.AnimationCamera;
var buttonText = AnimationSettings.VMDButton.GetComponentInChildren<TextMeshProUGUI>();
if (!container || container.IsMini)
{
buttonText.text = string.Format("<color=#FF0000>{0}</color>", "Need Normal UMA");
return;
}
var rootbone = container.transform.Find("Position");
if (rootbone.gameObject.TryGetComponent(out UnityHumanoidVMDRecorder recorder))
{
if (recorder.IsRecording)
{
if (camera.enabled)
{
var cameraRecorder = camera.GetComponent<UnityCameraVMDRecorder>();
if (cameraRecorder != null)
{
cameraRecorder.StopRecording();
cameraRecorder.SaveVMD();
}
}
recorder.StopRecording();
buttonText.text = "Saving";
recorder.SaveVMD(container.name, Config.Instance.VmdKeyReductionLevel);
buttonText.text = "Record VMD";
ShowMessage($"VMD is saved in {Path.GetFullPath(Application.dataPath + UnityHumanoidVMDRecorder.FileSavePath)}", UIMessageType.Success);
}
}
else
{
var newRecorder = rootbone.gameObject.AddComponent<UnityHumanoidVMDRecorder>();
newRecorder.Initialize();
if (!newRecorder.IsRecording)
{
if (camera.enabled)
{
var cameraRecorder = camera.gameObject.AddComponent<UnityCameraVMDRecorder>();
cameraRecorder.Initialize();
cameraRecorder.StartRecording();
}
newRecorder.StartRecording();
buttonText.text = "Recording...";
}
}
}
public void UpdateLiveMode(int val)
{
LiveMode = val;
if (LiveSelectPannel.activeSelf && currentLive != null)
ShowLiveSelectPanel(currentLive, false);
}
public void SetLiveRecordMode(bool val) { isRecordVMD = val; }
public void SetLiveRequireStage(bool val) { isRequireStage = val; }
//修改(Live演出的显隐控制和位置变换相关)
public void SetLiveControlMode(bool val) { isControlMode = val; }
public void ShowMessage(string msg, UIMessageType type)
{
if (!MessageText) return;
MessageText.text += type switch
{
UIMessageType.Error => string.Format("<color=red>{0}</color>\n", msg),
UIMessageType.Warning => string.Format("<color=yellow>{0}</color>\n", msg),
UIMessageType.Success => string.Format("<color=green>{0}</color>\n", msg),
UIMessageType.Close => string.Format("<color=green>{0}</color>\n", msg),
_ => $"{msg}\n",
};
MessageScrollRect.gameObject.SetActive(type != UIMessageType.Close);
LayoutRebuilder.ForceRebuildLayoutImmediate(MessageScrollRect.content);
MessageScrollRect.verticalNormalizedPosition = 0;
}
public void ToggleVisible(GameObject go)
{
go.SetActive(!go.activeSelf);
}
//修改(载入通用服装ColorSet相关)(颜色组UI)
public static string CurrentSelectedColorSetId;
public void LoadColorSetButtons()
{
ClearColorSetButtons();
var uma = Builder.CurrentUMAContainer;
if (uma == null || string.IsNullOrEmpty(uma.VarCostumeIdShort)) return;
var parts = uma.VarCostumeIdShort.Split('_');
if (parts.Length < 2) return;
if (!int.TryParse(parts[0], out int bodyType)) return;
if (!int.TryParse(parts[1], out int bodyTypeSub)) return;
if (bodyType == 7 || bodyType == 8)
{
var colorSetIds = UmaDatabaseController.Instance.CharaDressColor
.Select(row => row["id"].ToString());
foreach (var id in colorSetIds)
{
var buttonObj = Instantiate(UmaContainerPrefab, DressColorSetList.content);
var container = buttonObj.GetComponent<UmaUIContainer>();
container.Name = id;
var buttonText = buttonObj.GetComponentInChildren<Text>();
if (buttonText != null) buttonText.text = id;
container.Button.onClick.AddListener(() =>
{
HighlightChildImage(DressColorSetList.content, container);
CurrentSelectedColorSetId = id;
Builder.CurrentUMAContainer?.RefreshDressColor();
});
if (container.ToggleImage != null)
container.ToggleImage.enabled = (id == CurrentSelectedColorSetId);
}
return;
}
var dressRow = UmaDatabaseController.Instance.DressData.FirstOrDefault(row =>
int.Parse(row["body_type"].ToString()) == bodyType &&
int.Parse(row["body_type_sub"].ToString()) == bodyTypeSub);
if (dressRow == null || dressRow["body_setting"].ToString() != "4") return;
string dressId = dressRow["id"].ToString();
var colorSetRows = UmaDatabaseController.Instance.CharaDressColor
.Where(row => row["dress_id"].ToString() == dressId);
foreach (var row in colorSetRows)
{
string id = row["id"].ToString();
var buttonObj = Instantiate(UmaContainerPrefab, DressColorSetList.content);
var container = buttonObj.GetComponent<UmaUIContainer>();
container.Name = id;
var buttonText = buttonObj.GetComponentInChildren<Text>();
if (buttonText != null) buttonText.text = id;
container.Button.onClick.AddListener(() =>
{
HighlightChildImage(DressColorSetList.content, container);
CurrentSelectedColorSetId = id;
Builder.CurrentUMAContainer?.RefreshDressColor();
});
if (container.ToggleImage != null)
container.ToggleImage.enabled = (id == CurrentSelectedColorSetId);
}
}
//修改(载入通用服装ColorSet相关)(清理所有颜色组按钮)
public void ClearColorSetButtons()
{
foreach (Transform child in DressColorSetList.content)
{
Destroy(child.gameObject);
}
}
//修改(载入通用服装ColorSet相关)(解析颜色字符串)
private static bool TryParseHexColor(
string hex,
out Tuple<byte, byte, byte> rgb255,
out Tuple<float, float, float> rgbNormalized)
{
rgb255 = null;
rgbNormalized = null;
if (string.IsNullOrWhiteSpace(hex) || !hex.StartsWith("#") || hex.Length != 7)
return false;
byte r, g, b;
bool okR = byte.TryParse(hex.Substring(1, 2), System.Globalization.NumberStyles.HexNumber, null, out r);
bool okG = byte.TryParse(hex.Substring(3, 2), System.Globalization.NumberStyles.HexNumber, null, out g);
bool okB = byte.TryParse(hex.Substring(5, 2), System.Globalization.NumberStyles.HexNumber, null, out b);
if (!okR || !okG || !okB)
return false;
rgb255 = Tuple.Create(r, g, b);
rgbNormalized = Tuple.Create(r / 255f, g / 255f, b / 255f);
return true;
}
//修改(载入通用服装ColorSet相关)(保存当前选中的颜色配置到TXT文件)
public static void SaveCurrentColorSetRow()
{
if (string.IsNullOrEmpty(CurrentSelectedColorSetId))
{
UmaViewerUI.Instance.ShowMessage("No color set ID selected!", UIMessageType.Error);
return;
}
var charaColor = UmaDatabaseController.Instance.CharaDressColor
.FirstOrDefault(row => row["id"].ToString() == CurrentSelectedColorSetId);
if (charaColor == null)
{
UmaViewerUI.Instance.ShowMessage($"Color set ID {CurrentSelectedColorSetId} not found!", UIMessageType.Error);
return;
}
#if UNITY_ANDROID && !UNITY_EDITOR
string fileDirectory = Application.persistentDataPath + "/../ColorCode/";
#else
string fileDirectory = Application.dataPath + "/../ColorCode/";
#endif
Directory.CreateDirectory(fileDirectory);
string idValue = charaColor["id"]?.ToString() ?? "unknown";
string fileName = $"{idValue}.txt";
string fullPath = Path.GetFullPath(Path.Combine(fileDirectory, fileName));
List<string> lines = new List<string>();
foreach (var column in charaColor.Table.Columns)
{
string columnName = column.ToString();
string value = charaColor[columnName]?.ToString() ?? "";
if (TryParseHexColor(value, out var rgb255, out var rgbNormalized))
{
string prefix = (columnName + ":").PadRight(17);
lines.Add(
$"{prefix}\t{value}\t" +
$"({rgb255.Item1,3}, {rgb255.Item2,3}, {rgb255.Item3,3})\t" +
$"({rgbNormalized.Item1:0.####}, {rgbNormalized.Item2:0.####}, {rgbNormalized.Item3:0.####})"
);
}
else
{
lines.Add($"{columnName}: {value}");
}
}
string content = string.Join("\n", lines);
File.WriteAllText(fullPath, content, Encoding.UTF8);
UmaViewerUI.Instance.ShowMessage($"ColorSet data saved: {fullPath}", UIMessageType.Success);
}
}