This commit is contained in:
SoulliesOfficial
2025-08-22 14:54:40 -04:00
parent 6aa498f6be
commit 70b2a43824
574 changed files with 173713 additions and 1884 deletions

View File

@@ -0,0 +1,48 @@
using System.Collections;
using System.Collections.Generic;
using Michsky.MUIP;
using UnityEngine;
namespace Ichni.UI
{
public class Dropdown : SettingsUIElementBase
{
public List<string> options;
public CustomDropdown dropdown;
public int selectedIndex;
public string selectedOption;
public void SetUp(int initialValue, List<string> options, string title = "")
{
base.SetUp(title);
this.options = options;
this.dropdown.items = new List<CustomDropdown.Item>();
foreach (string option in options)
{
dropdown.items.Add(new CustomDropdown.Item { itemName = option });
}
dropdown.onValueChanged.AddListener(value =>
{
SetValue(value);
updateValueAction?.Invoke();
});
SetValue(initialValue);
}
public int GetOptionIndex(string option)
{
return options.IndexOf(option);
}
public void SetValue(int index)
{
selectedIndex = index;
selectedOption = options[selectedIndex];
dropdown.selectedItemIndex = selectedIndex;
dropdown.UpdateItemLayout();
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: f3060506b42e3df44962829ee40e95d9
guid: 63ac87ec6c134804c9cd877b8820d283
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -1,10 +1,4 @@
using System.Collections;
using System.Collections.Generic;
using I2.Loc;
using Michsky.MUIP;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
namespace Ichni.UI
{

View File

@@ -1,5 +1,7 @@
using System.Collections;
using System.Collections.Generic;
using I2.Loc;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
@@ -8,10 +10,12 @@ namespace Ichni.UI
public class TextButton : SettingsUIElementBase
{
public Button button;
public void SetUp(string title = "")
public TMP_Text buttonText;
public void SetUp(string title, string subTitle, string textContent)
{
base.SetUp(title);
base.SetUp(title, subTitle);
buttonText.GetComponent<Localize>().SetTerm(textContent);
button.onClick.AddListener(() =>
{

View File

@@ -35,18 +35,29 @@ namespace Ichni.UI
public Image avatarMask;
public TMP_Text decorationChapterText0;
public TMP_Text decorationChapterText1;
public TMP_Text progressText;
public TMP_Text outerSongProgressText;
public RectTransform titleRect;
public TMP_Text titleText;
[Title("展开内容")]
[Title("展开内容")]
public RectTransform expansionBackground;
public Image expansionRipple;
public Material rippleMaterial;
public Sequence rippleSequence;
public RectTransform expansionFunctions;
public RectTransform expansionInfos;
public TMP_Text innerSongProgressText;
public TMP_Text fullComboText;
public TMP_Text allPerfectText;
public TMP_Text beatmapProgressText;
public void Initialize(ChapterSelectionUnit chapter)
{
connectedChapter = chapter;
expansionRipple.material = new Material(rippleMaterial);
expandButton.onClick.AddListener(() =>
{
@@ -90,6 +101,14 @@ namespace Ichni.UI
ChapterSelectionManager.instance.chapterSelectionUIPage.FadeOut();
SongSelectionManager.instance.songSelectionUIPage.FadeIn();
});
(int beatmapCount, int finishedSongCount, int finishedBeatmapCount, int fullComboCount, int allPerfectCount) = chapter.GetChapterSaveInfo();
float songProgressPercent = (float)finishedSongCount / chapter.songs.Count * 100f;
outerSongProgressText.text = songProgressPercent.ToString("F2") + "%";
innerSongProgressText.text = songProgressPercent.ToString("F2") + "%";
fullComboText.text = fullComboCount.ToString();
allPerfectText.text = allPerfectCount.ToString();
beatmapProgressText.text = $"{finishedBeatmapCount}/{beatmapCount}";
}
private void Expand()
@@ -122,7 +141,16 @@ namespace Ichni.UI
expandSequence.Join(upperTip.DOFade(1f, 0.3f));
expandSequence.OnStart(() => isDuringAnimation = true);
expandSequence.OnComplete(() => isDuringAnimation = false);
expandSequence.OnComplete(() =>
{
isDuringAnimation = false;
expansionRipple.material.SetFloat("_RippleTime", 0f);
rippleSequence = DOTween.Sequence();
rippleSequence.AppendInterval(5f);
rippleSequence.Append(expansionRipple.material.DOFloat(1, "_RippleTime", 2f));
rippleSequence.SetLoops(-1);
rippleSequence.Play();
});
expandSequence.Play();
}
@@ -131,6 +159,8 @@ namespace Ichni.UI
{
isExpanded = false;
rippleSequence?.Kill(true);
Sequence shrinkSequence = DOTween.Sequence();
shrinkSequence.Append(bottomTip.DOFade(0f, 0.3f));
@@ -147,18 +177,17 @@ namespace Ichni.UI
expansionFunctions.gameObject.SetActive(false);
}));
shrinkSequence.Join(titleRect.DOSizeDelta(new Vector2(322, 100), 0.3f).SetEase(Ease.InQuad));
shrinkSequence.Join(titleRect.DOSizeDelta(new Vector2(322, 100), 0.3f));
shrinkSequence.Append(expansionBackground.DOSizeDelta(new Vector2(322, 826), 0.3f)
.SetEase(Ease.InQuad)
.OnComplete(() =>
{
expansionBackground.gameObject.SetActive(false);
}));
shrinkSequence.Join(DOTween.To(() => layoutElement.preferredWidth,
x => layoutElement.preferredWidth = x, 322, 0.3f).SetEase(Ease.InQuad));
shrinkSequence.Join(avatarMask.rectTransform.DOSizeDelta(new Vector2(322, 826), 0.3f).SetEase(Ease.InQuad));
x => layoutElement.preferredWidth = x, 322, 0.3f));
shrinkSequence.Join(avatarMask.rectTransform.DOSizeDelta(new Vector2(322, 826), 0.3f));
shrinkSequence.OnStart(() => isDuringAnimation = true);
shrinkSequence.OnComplete(() => isDuringAnimation = false);

View File

@@ -15,6 +15,8 @@ namespace Ichni.UI
protected override void Awake()
{
base.Awake();
fadeInStartAction = Initialize;
settingsButton.onClick.AddListener(() =>
{
@@ -23,8 +25,14 @@ namespace Ichni.UI
});
}
private void Start()
private void Initialize()
{
// Clear existing chapters
foreach (Transform child in chapterContainer)
{
Destroy(child.gameObject);
}
foreach (var chapter in ChapterSelectionManager.instance.chapters)
{
ChapterSelectionUI item = Instantiate(chapterSelectionUIPrefab, chapterContainer).GetComponent<ChapterSelectionUI>();

View File

@@ -0,0 +1,122 @@
using System.Collections;
using System.Collections.Generic;
using DG.Tweening;
using TMPro;
using UnityEngine;
namespace Ichni.UI
{
public class TextCatapult : MonoBehaviour
{
[Header("UI 元素")] [Tooltip("第一个文本组件")] [SerializeField]
private TMP_Text textA;
[Tooltip("第二个文本组件")] [SerializeField] private TMP_Text textB;
[Header("动画参数")] [Tooltip("文本在中央停留显示的时间")] [SerializeField]
private float displayDuration = 4f;
[Tooltip("向上移动和淡入淡出的动画时间")] [SerializeField]
private float animationDuration = 1f;
[Tooltip("文本向上移动的距离")] [SerializeField]
private float moveDistance = 200f;
// --- 可选: 如果你想让文字内容循环变化 ---
[Header("可选的文本内容")] [Tooltip("在这里填入你想循环显示的文字列表")] [SerializeField]
private List<string> textContents = new List<string>();
private TMP_Text[] _textElements;
private int _currentTextIndex = 0; // 当前显示在中央的文本的索引 (0 或 1)
private int _contentIndex = 0; // 当前显示内容的索引
private Vector2 _centerPosition;
private Sequence _animationSequence;
void Start()
{
Setup();
// 直接启动第一个动画过渡
AnimateTransition();
}
void OnDestroy()
{
// 安全地杀死所有相关的DoTween动画防止在场景切换或对象销毁时出错
_animationSequence?.Kill();
}
/// <summary>
/// 初始化设置
/// </summary>
private void Setup()
{
_textElements = new[] { textA, textB };
// 记录中心位置
_centerPosition = textA.rectTransform.anchoredPosition;
// 设置 textA 的初始状态
textA.rectTransform.anchoredPosition = _centerPosition;
textA.alpha = 1f;
UpdateTextContent(textA); // 设置初始文字
// 设置 textB 的初始状态 (在下方,透明)
textB.rectTransform.anchoredPosition = _centerPosition - new Vector2(0, moveDistance);
textB.alpha = 0f;
// textB 的内容将在它动画开始前更新,所以这里无需设置
}
/// <summary>
/// 执行一次完整的文本过渡动画,并在完成后自我调用以形成循环
/// </summary>
private void AnimateTransition()
{
// 确定哪个文本要移出,哪个要移入
TMP_Text textToAnimateOut = _textElements[_currentTextIndex];
TMP_Text textToAnimateIn = _textElements[(_currentTextIndex + 1) % 2];
// 创建一个新的动画序列
_animationSequence = DOTween.Sequence();
_animationSequence
.AppendInterval(displayDuration) // 1. 等待当前文本显示4秒
.AppendCallback(() => {
// 2. 在动画开始前,更新即将进入的文本的内容
UpdateTextContent(textToAnimateIn);
})
// 3. 同时执行两个文本的动画
.Append(textToAnimateOut.rectTransform.DOAnchorPosY(_centerPosition.y + moveDistance, animationDuration).SetEase(Ease.OutCubic))
.Join(textToAnimateOut.DOFade(0f, animationDuration).SetEase(Ease.OutCubic))
.Join(textToAnimateIn.rectTransform.DOAnchorPosY(_centerPosition.y, animationDuration).SetEase(Ease.OutCubic))
.Join(textToAnimateIn.DOFade(1f, animationDuration).SetEase(Ease.OutCubic))
.OnComplete(() => {
// 4. 当整个过渡动画完成时
// 将刚刚移出的文本复位到起始位置,为下一次进入做准备
textToAnimateOut.rectTransform.anchoredPosition = _centerPosition - new Vector2(0, moveDistance);
// 切换当前文本的索引,为下个循环做准备
_currentTextIndex = (_currentTextIndex + 1) % 2;
// 启动下一次动画过渡,形成循环
AnimateTransition();
});
_animationSequence.Play();
}
/// <summary>
/// 更新文本内容(如果列表不为空)
/// </summary>
private void UpdateTextContent(TMP_Text textElement)
{
if (textContents == null || textContents.Count == 0)
{
return; // 如果列表为空,则不改变文字
}
textElement.text = textContents[_contentIndex];
_contentIndex = (_contentIndex + 1) % textContents.Count; // 移动到下一个内容索引,并循环
}
}
}

View File

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

View File

@@ -1,67 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Ichni.Menu;
using Ichni.Story;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace Ichni.UI
{
public partial class PrepareUIPage : UIPageBase
{
public TextButton enterGameButton;
public TextButton switchDifficultyButton;
public string songName;
public List<string> difficulties;
public string difficultyName;
private ChapterSelectionUnit chapter;
private SongItemData songItem;
private DifficultyData difficultyData;
}
public partial class PrepareUIPage
{
public void SetUpPrepareUIPage(string songName)
{
chapter = ChapterSelectionManager.instance.currentChapter;
songItem = chapter.songs.FirstOrDefault(s => s.songName == songName);
this.songName = songName;
this.difficulties = new List<string>();
foreach (DifficultyData difficulty in songItem.difficultyDataList)
{
this.difficulties.Add(difficulty.difficultyName);
}
this.difficultyName = difficulties[0];
difficultyData = songItem.difficultyDataList.FirstOrDefault(d => d.difficultyName == difficultyName);
switchDifficultyButton.GetComponentInChildren<TMP_Text>().text = difficultyName + " Lv." + difficultyData.difficultyValue;
switchDifficultyButton.GetComponentInChildren<TMP_Text>().color = difficultyData.color;
}
public void SwitchDifficulty()
{
int currentIndex = difficulties.IndexOf(difficultyName);
int nextIndex = (currentIndex + 1) % difficulties.Count;
difficultyName = difficulties[nextIndex];
difficultyData = songItem.difficultyDataList
.FirstOrDefault(d => d.difficultyName == difficultyName);
switchDifficultyButton.GetComponentInChildren<TMP_Text>().text = difficultyName + " Lv." + difficultyData.difficultyValue;
switchDifficultyButton.GetComponentInChildren<TMP_Text>().color = difficultyData.color;
}
public void EnterGame()
{
InformationTransistor.instance.SetInformation(ChapterSelectionManager.instance.currentChapter, songItem, difficultyData);
MenuAudioManager.instance.audioContainer.StopEvent("PlayPreview");
MenuManager.instance.TestEnterGame();
}
}
}

View File

@@ -1,6 +1,7 @@
using System.Collections;
using System.Collections.Generic;
using Ichni.Menu;
using MoreMountains.Tools;
using UnityEngine;
namespace Ichni.UI
@@ -8,9 +9,10 @@ namespace Ichni.UI
public class GamePlaySettingsWindow : SettingsWindow
{
public TextButton offsetEditorButton;
public Dropdown languageDropdown;
public override void Initialize()
{
offsetEditorButton.SetUp("Menu UI/Settings_OffsetEditor");
offsetEditorButton.SetUp("Menu UI/Settings_OffsetEditor", "", "Menu UI/Settings_Enter");
offsetEditorButton.updateValueAction = () =>
{
gameObject.SetActive(false);
@@ -18,11 +20,18 @@ namespace Ichni.UI
MenuManager.instance.settingsUIPage.offsetEditor.offsetEditingContainer.SetActive(true);
MenuManager.instance.settingsUIPage.offsetEditor.Play();
};
languageDropdown.SetUp(gameSettings.languageIndex, MenuManager.instance.displayLanguageList, "Menu UI/Settings_Language");
languageDropdown.updateValueAction = () =>
{
gameSettings.languageIndex = languageDropdown.selectedIndex;
gameSettings.ApplyLanguage();
};
}
public override void SetValuesFromSettings()
{
languageDropdown.SetValue(gameSettings.languageIndex);
}
}
}

View File

@@ -28,7 +28,7 @@ namespace Ichni.Menu
data.rebindButton.SetUp(data.title, data.subtitle, data.actionName, data.bindingIndex);
}
resetButton.SetUp("MenuUI/Settings_ResetRebinding");
resetButton.SetUp("Menu UI/Settings_ResetRebinding", "", "Menu UI/Settings_Confirm");
resetButton.updateValueAction = ResetAllBindings;
}

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections;
using System.Collections.Generic;
using DG.Tweening;
using UniRx;
using UnityEngine;
using UnityEngine.UI;
@@ -39,10 +40,14 @@ namespace Ichni.Menu
{
arrowSeq.Join(arrow.DOAnchorPosX(-584.5f, 0.2f));
}
arrowSeq.OnComplete(() =>
{
MenuManager.instance.TestEnterGame();
MenuManager.instance.transitionUIPage.FadeIn();
Observable.Timer(TimeSpan.FromSeconds(0.6f)).Subscribe(_ =>
{
MenuManager.instance.TestEnterGame();
});
});
arrowSeq.Play();

View File

@@ -22,8 +22,8 @@ namespace Ichni.Menu
[Title("对齐与动画")]
[SerializeField] public RectTransform centerPoint;
[SerializeField] private float snapSpeed = 5f;
[SerializeField] private float decelerationRate = 0.135f;
[SerializeField] private float snapSpeed = 10f;
[SerializeField] private float decelerationRate = 0.15f;
[Title("平滑度优化")]
[SerializeField] [Range(1f, 20f)]
@@ -263,7 +263,10 @@ namespace Ichni.Menu
selectedTab?.SetSelection(false);
selectedTab = null; // 清除当前选中的Tab
if(isDuringSnap && SnapCoroutine != null) StopCoroutine(SnapCoroutine);
if (isDuringSnap && SnapCoroutine != null)
{
StopCoroutine(SnapCoroutine);
}
SnapCoroutine = SnapToItem(tab.GetComponent<RectTransform>(), false);
@@ -289,8 +292,7 @@ namespace Ichni.Menu
Vector3 closestItemLocalPos = viewport.InverseTransformPoint(targetItem.position);
Vector3 centerPointLocalPos = viewport.InverseTransformPoint(centerPoint.position);
float localOffsetY = centerPointLocalPos.y - closestItemLocalPos.y;
// 【核心修正 #3】吸附动画现在也是通过更新targetPosition来实现
Vector2 finalTargetPosition = content.anchoredPosition + new Vector2(0, localOffsetY);
finalTargetPosition.y = Mathf.Clamp(finalTargetPosition.y, bottomBound, topBound);

View File

@@ -4,6 +4,7 @@ using System.Collections.Generic;
using DG.Tweening;
using Sirenix.OdinInspector;
using TMPro;
using UniRx;
using UnityEngine;
using UnityEngine.Serialization;
using UnityEngine.UI;
@@ -43,8 +44,17 @@ namespace Ichni.Menu
{
if (MenuManager.instance.songSelectionUIPage.songListController.selectedTab == this)
{
// MenuManager.instance.prepareUIPage.SetUpPrepareUIPage(song.songName);
// MenuManager.instance.prepareUIPage.FadeIn();
InformationTransistor.instance.SetInformation(
ChapterSelectionManager.instance.currentChapter,
MenuManager.instance.songSelectionUIPage.selectedSong,
MenuManager.instance.songSelectionUIPage.selectedDifficulty);
MenuAudioManager.instance.audioContainer.PlaySoundFX("EnterToGame");
MenuAudioManager.instance.audioContainer.StopEvent("PlayPreview");
MenuManager.instance.transitionUIPage.FadeIn();
Observable.Timer(TimeSpan.FromSeconds(0.6f)).Subscribe(_ =>
{
MenuManager.instance.TestEnterGame();
});
}
else
{

View File

@@ -8,9 +8,10 @@ namespace Ichni.UI
public class StartUIPage : UIPageBase
{
public AudioContainer audioContainer;
private void Awake()
protected override void Awake()
{
base.Awake();
audioContainer = GetComponent<AudioContainer>();
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 0b91bdc95925cee4eaec293d270c570c
guid: 0d7192179ca0b574496ffe2e3821b1e8
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@@ -0,0 +1,12 @@
using System.Collections;
using System.Collections.Generic;
using Ichni.UI;
using UnityEngine;
namespace Ichni.Menu
{
public class TransitionUIPage : UIPageBase
{
}
}

View File

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