主界面添加,许多bug修复。祝贺黑盒1如期开始
This commit is contained in:
@@ -5,6 +5,7 @@ using System.IO;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.Audio;
|
||||
|
||||
public class BgmPlaybackManager : MonoBehaviour
|
||||
{
|
||||
@@ -12,6 +13,7 @@ public class BgmPlaybackManager : MonoBehaviour
|
||||
|
||||
[Header("Audio")]
|
||||
public AudioSource audioSource;
|
||||
public AudioMixerGroup outputMixerGroup;
|
||||
public AudioClip clip;
|
||||
[Tooltip("Optional playlist. If empty, will load all clips in Resources/BGM.")]
|
||||
public List<AudioClip> playlist = new List<AudioClip>();
|
||||
@@ -49,6 +51,9 @@ public class BgmPlaybackManager : MonoBehaviour
|
||||
if (audioSource == null)
|
||||
audioSource = gameObject.AddComponent<AudioSource>();
|
||||
|
||||
if (outputMixerGroup != null)
|
||||
audioSource.outputAudioMixerGroup = outputMixerGroup;
|
||||
|
||||
audioSource.playOnAwake = false;
|
||||
audioSource.loop = loop;
|
||||
|
||||
@@ -96,6 +101,9 @@ public class BgmPlaybackManager : MonoBehaviour
|
||||
if (audioSource == null)
|
||||
audioSource = gameObject.AddComponent<AudioSource>();
|
||||
|
||||
if (outputMixerGroup != null)
|
||||
audioSource.outputAudioMixerGroup = outputMixerGroup;
|
||||
|
||||
audioSource.loop = loop;
|
||||
|
||||
if (audioSource.clip == null && clip != null)
|
||||
|
||||
@@ -4,6 +4,7 @@ using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using TMPro;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.Audio;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
[ExecuteAlways]
|
||||
@@ -61,6 +62,9 @@ public class BgmUiBinder : MonoBehaviour
|
||||
public Vector2 pointerEndPos = new Vector2(195.3f, -87.05f);
|
||||
public Vector2 spectrumPosition = new Vector2(-238.9f, -111.5f);
|
||||
|
||||
[Header("Audio")]
|
||||
public AudioMixerGroup outputMixerGroup;
|
||||
|
||||
private readonly List<TMP_Text> timeTmps = new List<TMP_Text>();
|
||||
private readonly List<Text> timeTexts = new List<Text>();
|
||||
|
||||
@@ -104,6 +108,8 @@ public class BgmUiBinder : MonoBehaviour
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
var mgr = BgmPlaybackManager.EnsureInstance();
|
||||
if (outputMixerGroup != null)
|
||||
mgr.outputMixerGroup = outputMixerGroup;
|
||||
mgr.EnsurePlaying();
|
||||
}
|
||||
}
|
||||
@@ -129,6 +135,11 @@ public class BgmUiBinder : MonoBehaviour
|
||||
|
||||
Canvas sceneCanvas = FindSceneCanvas();
|
||||
var controller = Object.FindAnyObjectByType<btmandtopController>();
|
||||
if (controller != null && controller.bgmMixerGroup != null)
|
||||
{
|
||||
outputMixerGroup = controller.bgmMixerGroup;
|
||||
}
|
||||
|
||||
if (controller != null && controller.musicPicRoot != null)
|
||||
{
|
||||
SetMusicRoot(controller.musicPicRoot, sceneCanvas);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
@@ -6,6 +6,9 @@ using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEngine.UI;
|
||||
using TMPro;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
|
||||
public class pressStart : MonoBehaviour
|
||||
{
|
||||
@@ -24,6 +27,28 @@ public class pressStart : MonoBehaviour
|
||||
// UI text to show status
|
||||
public Text statusText;
|
||||
public Text warningText;
|
||||
|
||||
[Header("New UI Buttons")]
|
||||
public Button startButton;
|
||||
public Button helpButton;
|
||||
public Button closeButton;
|
||||
public Button exitButton;
|
||||
public GameObject helpContentObject;
|
||||
|
||||
[Header("Fade Settings")]
|
||||
public CanvasGroup mainCanvasGroup;
|
||||
public float fadeDelay = 0f;
|
||||
public float fadeDuration = 1f;
|
||||
|
||||
[Header("TMP Flashing Settings")]
|
||||
public TextMeshProUGUI flashingText;
|
||||
public float flashInterval = 0.5f;
|
||||
|
||||
[Header("Start Sequence Settings")]
|
||||
public Volume globalVolume;
|
||||
public Image fadeOutImage;
|
||||
public float startButtonDelay = 6f; // 6秒后才允许点击 Start
|
||||
|
||||
// polling
|
||||
private Coroutine pollCoroutine;
|
||||
private float pollInterval = 3f;
|
||||
@@ -40,6 +65,147 @@ public class pressStart : MonoBehaviour
|
||||
|
||||
// Ensure status text shows idle
|
||||
UpdateStatus("Press Enter to continue.", Color.green);
|
||||
|
||||
// Bind buttons
|
||||
if (startButton != null)
|
||||
{
|
||||
startButton.onClick.AddListener(OnStartButtonClick);
|
||||
}
|
||||
if (helpButton != null)
|
||||
{
|
||||
helpButton.onClick.AddListener(OnHelpButtonClick);
|
||||
}
|
||||
if (closeButton != null)
|
||||
{
|
||||
closeButton.onClick.AddListener(OnCloseButtonClick);
|
||||
}
|
||||
if (exitButton != null)
|
||||
{
|
||||
exitButton.onClick.AddListener(OnExitButtonClick);
|
||||
}
|
||||
if (helpContentObject != null)
|
||||
{
|
||||
helpContentObject.SetActive(false);
|
||||
}
|
||||
|
||||
// Initialize fadeOutImage
|
||||
if (fadeOutImage != null)
|
||||
{
|
||||
Color c = fadeOutImage.color;
|
||||
c.a = 0f;
|
||||
fadeOutImage.color = c;
|
||||
fadeOutImage.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
// Initialize Global Volume Bloom intensity to 0
|
||||
if (globalVolume != null && globalVolume.profile.TryGet<Bloom>(out var bloom))
|
||||
{
|
||||
bloom.intensity.Override(0f);
|
||||
}
|
||||
|
||||
// Initialize CanvasGroup and start fade-in
|
||||
if (mainCanvasGroup != null)
|
||||
{
|
||||
mainCanvasGroup.alpha = 0f;
|
||||
StartCoroutine(FadeInMainCanvasGroup());
|
||||
}
|
||||
|
||||
// Start flashing logic
|
||||
if (flashingText != null)
|
||||
{
|
||||
StartCoroutine(FlashTMPText());
|
||||
}
|
||||
|
||||
// Start button delay logic
|
||||
if (startButton != null)
|
||||
{
|
||||
StartCoroutine(StartButtonActivationDelay());
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator StartButtonActivationDelay()
|
||||
{
|
||||
if (startButton == null) yield break;
|
||||
|
||||
// 初始禁用按钮
|
||||
startButton.interactable = false;
|
||||
|
||||
// 等待指定时间 (使用 unscaledTime 以防 timeScale 为 0)
|
||||
yield return new WaitForSecondsRealtime(startButtonDelay);
|
||||
|
||||
// 启用按钮
|
||||
startButton.interactable = true;
|
||||
Debug.Log("[pressStart] Start button is now active.");
|
||||
}
|
||||
|
||||
private IEnumerator FlashTMPText()
|
||||
{
|
||||
if (flashingText == null) yield break;
|
||||
|
||||
float elapsed = 0f;
|
||||
while (true)
|
||||
{
|
||||
elapsed += Time.deltaTime;
|
||||
// 使用 Mathf.PingPong 在 0 到 1 之间往复
|
||||
// 除以 flashInterval 可以控制循环的速度
|
||||
float alpha = Mathf.PingPong(elapsed / flashInterval, 1f);
|
||||
|
||||
Color color = flashingText.color;
|
||||
color.a = alpha;
|
||||
flashingText.color = color;
|
||||
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator FadeInMainCanvasGroup()
|
||||
{
|
||||
if (fadeDelay > 0)
|
||||
yield return new WaitForSeconds(fadeDelay);
|
||||
|
||||
float elapsed = 0f;
|
||||
while (elapsed < fadeDuration)
|
||||
{
|
||||
elapsed += Time.deltaTime;
|
||||
mainCanvasGroup.alpha = Mathf.Lerp(0f, 1f, elapsed / fadeDuration);
|
||||
yield return null;
|
||||
}
|
||||
mainCanvasGroup.alpha = 1f;
|
||||
}
|
||||
|
||||
private void OnStartButtonClick()
|
||||
{
|
||||
Debug.Log("[pressStart] Start button clicked.");
|
||||
HandleGameStartLogic();
|
||||
}
|
||||
|
||||
private void OnHelpButtonClick()
|
||||
{
|
||||
if (helpContentObject != null)
|
||||
{
|
||||
bool isActive = helpContentObject.activeSelf;
|
||||
helpContentObject.SetActive(!isActive);
|
||||
Debug.Log("[pressStart] Help button clicked. Help content active: " + !isActive);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCloseButtonClick()
|
||||
{
|
||||
if (helpContentObject != null)
|
||||
{
|
||||
helpContentObject.SetActive(false);
|
||||
Debug.Log("[pressStart] Close button clicked. Help content hidden.");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnExitButtonClick()
|
||||
{
|
||||
Debug.Log("[pressStart] Exit button clicked. Application quitting.");
|
||||
#if UNITY_EDITOR
|
||||
UnityEditor.EditorApplication.isPlaying = false;
|
||||
#else
|
||||
Application.Quit();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
@@ -51,32 +217,82 @@ public class pressStart : MonoBehaviour
|
||||
if (!(Input.GetKeyDown(KeyCode.Return) || Input.GetKeyDown(KeyCode.KeypadEnter)))
|
||||
return;
|
||||
|
||||
// 如果按钮未激活 (还在倒计时),不允许通过键盘进入
|
||||
if (startButton != null && !startButton.interactable)
|
||||
{
|
||||
Debug.Log("[pressStart] Start request ignored. Button is not active yet.");
|
||||
return;
|
||||
}
|
||||
|
||||
HandleGameStartLogic();
|
||||
}
|
||||
|
||||
private void HandleGameStartLogic()
|
||||
{
|
||||
bool requestTestMode = Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift);
|
||||
if (requestTestMode)
|
||||
{
|
||||
Debug.Log("[pressStart] Enter+Shift detected, starting test mode polling.");
|
||||
Debug.Log("[pressStart] Enter+Shift or button start requested, starting test mode polling.");
|
||||
GameConfig.testMode = true;
|
||||
if (pollCoroutine != null) StopCoroutine(pollCoroutine);
|
||||
pollCoroutine = StartCoroutine(PollForBanflagAndProceed());
|
||||
return;
|
||||
}
|
||||
|
||||
if (check_enterNextScene)
|
||||
{
|
||||
Debug.Log("[pressStart] Second input detected, loading UI_UI scene. Forcing Time.timeScale = 1.");
|
||||
Time.timeScale = 1f; // Ensure time is running for next scene
|
||||
SceneManager.LoadScene("UI_UI");
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.Log("[pressStart] First input detected, setting check_enterNextScene = true");
|
||||
// 修改为点击一次即可开始进入游戏序列
|
||||
Debug.Log("[pressStart] Start requested, initiating transition sequence.");
|
||||
GameConfig.testMode = false;
|
||||
|
||||
if (warningText != null)
|
||||
{
|
||||
warningText.gameObject.SetActive(true);
|
||||
warningText.text = WARNING_TEXT_CONTENT;
|
||||
}
|
||||
check_enterNextScene = true;
|
||||
|
||||
// 启动进入游戏的异步序列
|
||||
StartCoroutine(StartGameTransitionSequence());
|
||||
}
|
||||
|
||||
private IEnumerator StartGameTransitionSequence()
|
||||
{
|
||||
// 1. 0.5秒内将 Global Volume 的 Bloom Intensity 从 0 变到 3
|
||||
if (globalVolume != null && globalVolume.profile.TryGet<Bloom>(out var bloom))
|
||||
{
|
||||
float elapsed = 0f;
|
||||
float duration = 0.5f;
|
||||
float startVal = bloom.intensity.value;
|
||||
float targetVal = 3f;
|
||||
while (elapsed < duration)
|
||||
{
|
||||
elapsed += Time.deltaTime;
|
||||
bloom.intensity.Override(Mathf.Lerp(startVal, targetVal, elapsed / duration));
|
||||
yield return null;
|
||||
}
|
||||
bloom.intensity.Override(targetVal);
|
||||
}
|
||||
|
||||
// 2. 结束变 3 后,将 Image 的不透明度在 0.5 秒内从 0 变到 255 (alpha = 1)
|
||||
if (fadeOutImage != null)
|
||||
{
|
||||
fadeOutImage.gameObject.SetActive(true);
|
||||
float elapsed = 0f;
|
||||
float duration = 0.5f;
|
||||
Color color = fadeOutImage.color;
|
||||
while (elapsed < duration)
|
||||
{
|
||||
elapsed += Time.deltaTime;
|
||||
color.a = Mathf.Lerp(0f, 1f, elapsed / duration);
|
||||
fadeOutImage.color = color;
|
||||
yield return null;
|
||||
}
|
||||
color.a = 1f;
|
||||
fadeOutImage.color = color;
|
||||
}
|
||||
|
||||
// 3. 切换场景
|
||||
Debug.Log("[pressStart] Transition sequence complete. Loading UI_UI scene.");
|
||||
Time.timeScale = 1f;
|
||||
SceneManager.LoadScene("UI_UI");
|
||||
}
|
||||
|
||||
private IEnumerator PollForBanflagAndProceed()
|
||||
|
||||
@@ -9,8 +9,11 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using DG.Tweening;
|
||||
using JetBrains.Annotations;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using Bansonic;
|
||||
|
||||
class UI_Panel_Character : MonoBehaviour
|
||||
{
|
||||
[SerializeField] float anim_Time = 0.5f;
|
||||
@@ -22,6 +25,11 @@ class UI_Panel_Character : MonoBehaviour
|
||||
[SerializeField] Image Image_Character_Illustration;
|
||||
[SerializeField] Image Image_Character_Illustration_BG;
|
||||
|
||||
[Header("buttons")]
|
||||
public Button change_thisHero_skin;
|
||||
public Button thisHero_detail;
|
||||
public Button confirm_thisHero;
|
||||
|
||||
[Header("Data Paths")]
|
||||
[Tooltip("Path relative to Resources folder for Editor mode")]
|
||||
public string editorResourcePath = "so/ally";
|
||||
@@ -37,36 +45,98 @@ class UI_Panel_Character : MonoBehaviour
|
||||
gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
private Vector2 originalIllustrationPos;
|
||||
private Vector2 originalIllustrationBGPos;
|
||||
private bool hasCachedPositions = false;
|
||||
private const string SAVED_HERO_ID_KEY = "SelectedMainHeroID";
|
||||
|
||||
void Start()
|
||||
{
|
||||
if (Image_Character_Illustration.transform.parent.TryGetComponent(out CanvasGroup canvasGroup))
|
||||
UnityEngine.Debug.Log($"[UI_Panel_Character] Start called. Illustration: {Image_Character_Illustration}");
|
||||
|
||||
// 初始化按钮监听
|
||||
if (change_thisHero_skin != null)
|
||||
change_thisHero_skin.onClick.AddListener(() => gNotice.warning.display("此版本未开放皮肤切换功能"));
|
||||
|
||||
if (thisHero_detail != null)
|
||||
thisHero_detail.onClick.AddListener(() => gNotice.warning.display("此版本未开放详情查看功能"));
|
||||
|
||||
if (confirm_thisHero != null)
|
||||
confirm_thisHero.onClick.AddListener(OnConfirmHeroClicked);
|
||||
|
||||
if (Image_Character_Illustration != null)
|
||||
{
|
||||
canvasGroup.alpha = 0;
|
||||
}
|
||||
else if(canvasGroup == null)
|
||||
{
|
||||
// Documentation text normalized.
|
||||
return;
|
||||
// 设置为图示的数值:Pos X: 376, Pos Y: -306
|
||||
Image_Character_Illustration.rectTransform.anchoredPosition = new Vector2(376, -306);
|
||||
|
||||
originalIllustrationPos = Image_Character_Illustration.rectTransform.anchoredPosition;
|
||||
if (Image_Character_Illustration.transform.parent.TryGetComponent(out CanvasGroup canvasGroup))
|
||||
{
|
||||
canvasGroup.alpha = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (Image_Character_Illustration_BG != null)
|
||||
{
|
||||
// 设置运行时 X 值为 376
|
||||
Vector2 pos = Image_Character_Illustration_BG.rectTransform.anchoredPosition;
|
||||
pos.x = 376;
|
||||
Image_Character_Illustration_BG.rectTransform.anchoredPosition = pos;
|
||||
|
||||
originalIllustrationBGPos = Image_Character_Illustration_BG.rectTransform.anchoredPosition;
|
||||
}
|
||||
hasCachedPositions = true;
|
||||
|
||||
foreach (var item in ui_Anim)
|
||||
{
|
||||
item.speed = Anim_Speed;
|
||||
if (item != null) item.speed = Anim_Speed;
|
||||
}
|
||||
content_Character_Slot.Get_Childrens_Component<UI_Button_Character_Head_Slot>(true, true);
|
||||
|
||||
if (content_Character_Slot != null)
|
||||
content_Character_Slot.Get_Childrens_Component<UI_Button_Character_Head_Slot>(true, true);
|
||||
|
||||
// Load AllyHero_SO data
|
||||
string path = Application.isEditor ? editorResourcePath : runtimeResourcePath;
|
||||
var loadedData = Resources.LoadAll<AllyHero_SO>(path);
|
||||
allyHeroList = new List<AllyHero_SO>(loadedData);
|
||||
|
||||
UnityEngine.Debug.Log($"[UI_Panel_Character] Loaded {allyHeroList.Count} heroes from {path}");
|
||||
|
||||
// Ensure consistent order with UI_Panel_Main
|
||||
allyHeroList.Sort((a, b) => a.ally_heroID.CompareTo(b.ally_heroID));
|
||||
|
||||
// 读取保存的角色 ID
|
||||
int savedHeroID = PlayerPrefs.GetInt(SAVED_HERO_ID_KEY, -1);
|
||||
int initialIndex = 0;
|
||||
|
||||
for (int i = 0; i < allyHeroList.Count; i++)
|
||||
{
|
||||
var obj = Instantiate(button_Character_Slot_Prefab, content_Character_Slot);
|
||||
|
||||
// Register UI sounds for the newly instantiated button
|
||||
UISystemBootstrap.RegisterHierarchy(obj.gameObject);
|
||||
|
||||
var data = allyHeroList[i];
|
||||
obj.image.sprite = data.ally_hero_squareProfile;
|
||||
|
||||
// 如果 ID 匹配,设置初始索引
|
||||
if (savedHeroID != -1 && data.ally_heroID == savedHeroID)
|
||||
{
|
||||
initialIndex = i;
|
||||
}
|
||||
|
||||
if (obj.image != null)
|
||||
{
|
||||
obj.image.sprite = data.ally_heroSelectIcon;
|
||||
obj.image.preserveAspect = true; // 保持比例,防止拉伸变形
|
||||
obj.image.type = Image.Type.Simple; // 确保是普通显示模式
|
||||
|
||||
// 确保 RectTransform 撑满父物体并居中
|
||||
obj.image.rectTransform.anchorMin = Vector2.zero;
|
||||
obj.image.rectTransform.anchorMax = Vector2.one;
|
||||
obj.image.rectTransform.sizeDelta = Vector2.zero;
|
||||
obj.image.rectTransform.anchoredPosition = Vector2.zero;
|
||||
}
|
||||
|
||||
if (obj.TryGetComponent(out UI_Button_Character_Head_Slot head_Slot))
|
||||
{
|
||||
head_Slot.index = i;
|
||||
@@ -74,12 +144,52 @@ class UI_Panel_Character : MonoBehaviour
|
||||
obj.onClick.AddListener(() => Set_Character_Index(capturedIndex));
|
||||
}
|
||||
}
|
||||
|
||||
// 应用初始索引(来自保存的 ID 或默认 0)
|
||||
UI_Panel_Main.Singleton.Character_Index = initialIndex;
|
||||
|
||||
// Initial character display
|
||||
Set_Character();
|
||||
}
|
||||
|
||||
public void Set_Character_Index(int index)
|
||||
{
|
||||
if (UI_Panel_Main.Singleton.Character_Index == index) return;
|
||||
UI_Panel_Main.Singleton.Character_Index = index;
|
||||
Set_Character();
|
||||
|
||||
// 人性化逻辑:点击头像即自动设为看板并保存,并显示提示内容
|
||||
SaveCurrentCharacter(true);
|
||||
}
|
||||
|
||||
private void OnConfirmHeroClicked()
|
||||
{
|
||||
if (SaveCurrentCharacter(true))
|
||||
{
|
||||
// 确认按钮逻辑:保存后禁用自身物体(隐藏面板)
|
||||
gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
private bool SaveCurrentCharacter(bool showNotice)
|
||||
{
|
||||
int currentIndex = UI_Panel_Main.Singleton.Character_Index;
|
||||
if (allyHeroList != null && currentIndex >= 0 && currentIndex < allyHeroList.Count)
|
||||
{
|
||||
int heroID = allyHeroList[currentIndex].ally_heroID;
|
||||
PlayerPrefs.SetInt(SAVED_HERO_ID_KEY, heroID);
|
||||
PlayerPrefs.Save();
|
||||
|
||||
if (showNotice)
|
||||
{
|
||||
gNotice.alarm.display($"已将 {allyHeroList[currentIndex].ally_heroName} 设置为记录对象。");
|
||||
}
|
||||
|
||||
// 同步更新主面板的看板图
|
||||
UI_Panel_Main.Singleton.Character_Index = currentIndex;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private void OnEnable()
|
||||
{
|
||||
@@ -102,27 +212,40 @@ class UI_Panel_Character : MonoBehaviour
|
||||
int index = UI_Panel_Main.Singleton.Character_Index;
|
||||
if (allyHeroList == null || index < 0 || index >= allyHeroList.Count) return;
|
||||
|
||||
var data = allyHeroList[index];
|
||||
var pos1 = Image_Character_Illustration.rectTransform.anchoredPosition;
|
||||
Image_Character_Illustration.sprite = data.ally_hero_HD_image;
|
||||
DOTween.To(value =>
|
||||
{ Image_Character_Illustration.rectTransform.anchoredPosition = new Vector2(value, pos1.y); },
|
||||
startValue: Image_Character_Illustration.rectTransform.anchoredPosition.x - 200,
|
||||
pos1.x, anim_Time);
|
||||
// Image_Character_Illustration.SetNativeSize(); // 注释掉以保持自适应而不改变大小
|
||||
var pos = Image_Character_Illustration_BG.rectTransform.anchoredPosition;
|
||||
Image_Character_Illustration_BG.sprite = data.ally_hero_HD_image;
|
||||
DOTween.To(value =>
|
||||
{ Image_Character_Illustration_BG.rectTransform.anchoredPosition = new Vector2(value, pos.y); },
|
||||
startValue: Image_Character_Illustration_BG.rectTransform.anchoredPosition.x - 200,
|
||||
pos.x, anim_Time);
|
||||
// Image_Character_Illustration_BG.SetNativeSize(); // 注释掉以保持自适应而不改变大小
|
||||
Image_Character_Illustration_BG.transform.localScale = new(2.7f, 2.7f, 2.7f);
|
||||
// 停止之前的 Tween 以防冲突
|
||||
Image_Character_Illustration.rectTransform.DOKill();
|
||||
Image_Character_Illustration_BG.rectTransform.DOKill();
|
||||
text_Character_Name.DOKill();
|
||||
|
||||
var data = allyHeroList[index];
|
||||
|
||||
// 使用初始缓存的坐标,防止频繁点击导致的偏移累积
|
||||
Vector2 targetPos1 = hasCachedPositions ? originalIllustrationPos : Image_Character_Illustration.rectTransform.anchoredPosition;
|
||||
Image_Character_Illustration.sprite = data.ally_hero_HD_image;
|
||||
|
||||
// 动画:从左侧 200 像素滑入到目标位置
|
||||
float startX1 = targetPos1.x - 200;
|
||||
Image_Character_Illustration.rectTransform.anchoredPosition = new Vector2(startX1, targetPos1.y);
|
||||
Image_Character_Illustration.rectTransform.DOAnchorPos(targetPos1, anim_Time)
|
||||
.SetEase(Ease.OutCubic)
|
||||
.SetLink(Image_Character_Illustration.gameObject);
|
||||
|
||||
Vector2 targetPosBG = hasCachedPositions ? originalIllustrationBGPos : Image_Character_Illustration_BG.rectTransform.anchoredPosition;
|
||||
Image_Character_Illustration_BG.sprite = data.ally_hero_HD_image;
|
||||
|
||||
float startXBG = targetPosBG.x - 200;
|
||||
Image_Character_Illustration_BG.rectTransform.anchoredPosition = new Vector2(startXBG, targetPosBG.y);
|
||||
Image_Character_Illustration_BG.rectTransform.DOAnchorPos(targetPosBG, anim_Time)
|
||||
.SetEase(Ease.OutCubic)
|
||||
.SetLink(Image_Character_Illustration_BG.gameObject);
|
||||
|
||||
Image_Character_Illustration_BG.transform.localScale = Vector3.one;
|
||||
|
||||
string text = data.ally_heroName;
|
||||
var t = DOTween.To(() => string.Empty, value => text_Character_Name.text = value, text, anim_Time).SetEase(Ease.Linear);
|
||||
t.SetOptions(true);
|
||||
text_Character_Name.text = string.Empty;
|
||||
text_Character_Name.DOText(text, anim_Time)
|
||||
.SetEase(Ease.Linear)
|
||||
.SetLink(text_Character_Name.gameObject);
|
||||
}
|
||||
IEnumerator C_Character_Illustration_Anim(CanvasGroup canvasGroup, Action onHide)
|
||||
{
|
||||
|
||||
@@ -270,6 +270,17 @@ public class UI_Panel_Main : Singleton_Mono<UI_Panel_Main>
|
||||
|
||||
yield return null;
|
||||
|
||||
// 读取保存的角色 ID 并初始化索引
|
||||
int savedHeroID = PlayerPrefs.GetInt("SelectedMainHeroID", -1);
|
||||
if (savedHeroID != -1)
|
||||
{
|
||||
int foundIndex = data_List.FindIndex(h => h.ally_heroID == savedHeroID);
|
||||
if (foundIndex != -1)
|
||||
{
|
||||
character_Index = foundIndex;
|
||||
}
|
||||
}
|
||||
|
||||
Character_Index = character_Index;
|
||||
if(Character_Index >= 0 && Character_Index < data_List.Count)
|
||||
Set_Character_Illustration(Get_Character_Image_Data(Character_Index).ally_hero_HD_image);
|
||||
|
||||
@@ -258,7 +258,7 @@ MonoBehaviour:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 0
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 0
|
||||
m_OnCullStateChanged:
|
||||
@@ -4732,6 +4732,7 @@ GameObject:
|
||||
- component: {fileID: 8671031786275937763}
|
||||
- component: {fileID: 5224241407313794548}
|
||||
- component: {fileID: 61231020966361337}
|
||||
- component: {fileID: 3093056817030257919}
|
||||
- component: {fileID: 2510911207525280717}
|
||||
m_Layer: 5
|
||||
m_Name: btm and top
|
||||
@@ -4854,6 +4855,7 @@ MonoBehaviour:
|
||||
guideSprites: []
|
||||
button_Music: {fileID: 5472029888669800656}
|
||||
musicPicRoot: {fileID: 0}
|
||||
bgmMixerGroup: {fileID: -1352833484795664833, guid: 1ec6b149cf654494d8d97065d8f2ccac, type: 2}
|
||||
showMusicPicInUI: 1
|
||||
uiSceneName: UI_UI
|
||||
musicPicFadeTime: 0.25
|
||||
@@ -4884,6 +4886,18 @@ MonoBehaviour:
|
||||
- sceneName:
|
||||
backTargetScene:
|
||||
depth: 4
|
||||
--- !u!114 &3093056817030257919
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5054239668136224504}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 76ce63b908338a84c8a3aa071b90c46b, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
--- !u!114 &2510911207525280717
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -4,6 +4,7 @@ using UnityEngine.Events;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.EventSystems;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.Audio;
|
||||
using Bansonic;
|
||||
|
||||
public class btmandtopController : MonoBehaviour, ICancelHandler
|
||||
@@ -40,6 +41,7 @@ public class btmandtopController : MonoBehaviour, ICancelHandler
|
||||
public Button button_Music;
|
||||
[Header("MusicPic")]
|
||||
public RectTransform musicPicRoot;
|
||||
public AudioMixerGroup bgmMixerGroup;
|
||||
public bool showMusicPicInUI = false;
|
||||
public string uiSceneName = "UI_UI";
|
||||
public float musicPicFadeTime = 0.25f;
|
||||
|
||||
@@ -1,9 +1,59 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
public class Music_Mgr : Singleton_Mono<Music_Mgr>
|
||||
{
|
||||
private void OnEnable()
|
||||
{
|
||||
SceneManager.sceneLoaded += OnSceneLoaded;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
SceneManager.sceneLoaded -= OnSceneLoaded;
|
||||
}
|
||||
|
||||
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
|
||||
{
|
||||
// 现在这两个场景是不合规的,需要停止播放
|
||||
bool isDisallowedScene = scene.name == "Main_main" || scene.name == "gamePlay_gamePlay";
|
||||
|
||||
if (isDisallowedScene)
|
||||
{
|
||||
// 如果是不合规场景且正在播放,则停止
|
||||
if (AudioSource != null && AudioSource.isPlaying)
|
||||
{
|
||||
AudioSource.Stop();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果是合规场景(其他所有场景)且当前没在播放,则尝试播放
|
||||
if (AudioSource != null && !AudioSource.isPlaying)
|
||||
{
|
||||
if (Music_Cur != null)
|
||||
{
|
||||
AudioSource.Play();
|
||||
}
|
||||
else if (Music_Data_List != null && Music_Data_List.Count > 0)
|
||||
{
|
||||
Play(Music_Data_List[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 切换场景后,如果存在播放器UI,确保其显示正确的数据
|
||||
if (Music_Cur != null)
|
||||
{
|
||||
var players = Object.FindObjectsByType<MusicPlayer>(FindObjectsInactive.Include, FindObjectsSortMode.None);
|
||||
foreach (var player in players)
|
||||
{
|
||||
player.Refresh(Music_Cur);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string Get_Time_Max(AudioClip clip)
|
||||
{
|
||||
@@ -68,9 +118,33 @@ public class Music_Mgr : Singleton_Mono<Music_Mgr>
|
||||
}
|
||||
public void Play(Music_Data data)
|
||||
{
|
||||
if (data == null) return;
|
||||
Music_Cur = data;
|
||||
AudioSource.clip = data.Clip;
|
||||
AudioSource.Play();
|
||||
|
||||
// 刷新所有正在显示的 MusicPlayer UI
|
||||
var players = Object.FindObjectsByType<MusicPlayer>(FindObjectsInactive.Include, FindObjectsSortMode.None);
|
||||
foreach (var player in players)
|
||||
{
|
||||
player.Refresh(data);
|
||||
}
|
||||
}
|
||||
|
||||
public void Next()
|
||||
{
|
||||
if (Music_Data_List == null || Music_Data_List.Count == 0) return;
|
||||
int index = Music_Data_List.IndexOf(Music_Cur);
|
||||
index = (index + 1) % Music_Data_List.Count;
|
||||
Play(Music_Data_List[index]);
|
||||
}
|
||||
|
||||
public void Last()
|
||||
{
|
||||
if (Music_Data_List == null || Music_Data_List.Count == 0) return;
|
||||
int index = Music_Data_List.IndexOf(Music_Cur);
|
||||
index = (index - 1 + Music_Data_List.Count) % Music_Data_List.Count;
|
||||
Play(Music_Data_List[index]);
|
||||
}
|
||||
protected override bool Is_Singleton_Auto()
|
||||
{
|
||||
|
||||
@@ -60,6 +60,7 @@ public class UI_Panel_Music : MonoBehaviour
|
||||
foreach (var item in music_Data_List)
|
||||
{
|
||||
var slot = Instantiate(prefab_Music_Slot, group_Music.transform);
|
||||
UISystemBootstrap.RegisterHierarchy(slot.gameObject);
|
||||
slot.Refresh(item);
|
||||
music_Slot_List.Add(slot);
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public class NoteSpawner : MonoBehaviour
|
||||
|
||||
// Documentation text normalized.
|
||||
[Tooltip("Multiplier applied to visual fall speed. Changing this will automatically adjust spawn timing so notes still arrive at their original beat times.")]
|
||||
[Range(0.8f, 1.25f)]
|
||||
[Range(0.5f, 2f)]
|
||||
public float speedMultiplier = 1f;
|
||||
|
||||
[Header("Calibration")]
|
||||
@@ -78,8 +78,8 @@ public class NoteSpawner : MonoBehaviour
|
||||
public GameObject animations;
|
||||
|
||||
// optional: constants for runtime clamping (kept for internal use)
|
||||
private const float SpeedMultiplierMin = 0.8f;
|
||||
private const float SpeedMultiplierMax = 1.25f;
|
||||
private const float SpeedMultiplierMin = 0.5f;
|
||||
private const float SpeedMultiplierMax = 2f;
|
||||
|
||||
public NoteJudgeConfig judgeConfig; // Documentation text normalized.
|
||||
private Beatmap beatmap;
|
||||
@@ -408,8 +408,8 @@ public class NoteSpawner : MonoBehaviour
|
||||
// pass realtime hit time (startTime + note.time) and delay 0, include globalHitDelay
|
||||
holdNote.Setup(holdNoteId, noteData.trackIndex, noteSpeed, baseHit, 0f, false, scheduledEndTime, noteData.color, key, "start", travelTime, judgeConfig, noteData);
|
||||
|
||||
// compute visual scale for hold segments so pieces visually connect across speedMultiplier changes
|
||||
float visualScale = 0.9f * sm + 0.1f; // linear fit: f(1)=1, f(2)=1.9
|
||||
// 使用流速倍率 sm 直接进行缩放,确保长条音符在 0.5-2.0 范围内依然能完美衔接
|
||||
float visualScale = sm;
|
||||
holdNote.ApplyVisualScale(visualScale);
|
||||
// inform hold note of visual speed so it can adapt judgement windows if needed
|
||||
holdNote.visualSpeedMultiplier = sm;
|
||||
@@ -440,8 +440,8 @@ public class NoteSpawner : MonoBehaviour
|
||||
// Documentation text normalized.
|
||||
holdSeg.Setup(holdNoteId, noteData.trackIndex, noteSpeed, baseHit, segmentDelay, false, scheduledEndTime, noteData.color, key, "middle", travelTime, judgeConfig, noteData);
|
||||
|
||||
// apply same visual scale so middle pieces visually connect
|
||||
float visualScaleMid = 0.9f * sm + 0.1f;
|
||||
// 应用与开始段一致的流速缩放
|
||||
float visualScaleMid = sm;
|
||||
holdSeg.ApplyVisualScale(visualScaleMid);
|
||||
holdSeg.visualSpeedMultiplier = sm;
|
||||
|
||||
@@ -472,8 +472,8 @@ public class NoteSpawner : MonoBehaviour
|
||||
{
|
||||
holdEnd.Setup(holdNoteId, noteData.trackIndex, noteSpeed, baseHit, endDelay, true, scheduledEndTime, noteData.color, key, "end", travelTime, judgeConfig, noteData);
|
||||
|
||||
// apply visual scale to end piece as well
|
||||
float visualScaleEnd = 0.9f * sm + 0.1f;
|
||||
// 同样应用流速缩放
|
||||
float visualScaleEnd = sm;
|
||||
holdEnd.ApplyVisualScale(visualScaleEnd);
|
||||
holdEnd.visualSpeedMultiplier = sm;
|
||||
|
||||
|
||||
@@ -1,8 +1,19 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.Audio;
|
||||
|
||||
public class audioMerger : MonoBehaviour
|
||||
{
|
||||
[Header("Audio Mixer")]
|
||||
public AudioMixer mainMixer;
|
||||
|
||||
[Header("Exposed Parameters")]
|
||||
public string masterParam = "Master_Vol";
|
||||
public string noteHitParam = "noteHit_sfx_Vol";
|
||||
public string musicInGameParam = "inGameMusic_Vol";
|
||||
public string musicOutGameParam = "outGameMusic_Vol";
|
||||
public string uiInterationParam = "button_sfx_Vol";
|
||||
|
||||
[Header("sv object")]
|
||||
public GameObject sv_audioSettings;
|
||||
[Header("Inspector")]
|
||||
@@ -25,11 +36,27 @@ public class audioMerger : MonoBehaviour
|
||||
public Text uiInteration_text;
|
||||
public Text cvVolumn_text;
|
||||
|
||||
// PlayerPrefs key for storing the toggle state
|
||||
// PlayerPrefs keys
|
||||
const string PlayerPrefKey_EnableGlobalMute = "EnableGlobalMute";
|
||||
const string Key_MainVol = "Volume_Main";
|
||||
const string Key_NoteHitVol = "Volume_NoteHit";
|
||||
const string Key_MusicInGameVol = "Volume_MusicInGame";
|
||||
const string Key_MusicOutGameVol = "Volume_MusicOutGame";
|
||||
const string Key_UiVol = "Volume_UI";
|
||||
|
||||
// Start is called once before the first execution of Update after the MonoBehaviour is created
|
||||
void Start()
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
// Ensure state is correct whenever the UI is shown
|
||||
Initialize();
|
||||
}
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
// Load saved state (default: false)
|
||||
bool isOn = PlayerPrefs.GetInt(PlayerPrefKey_EnableGlobalMute, 0) == 1;
|
||||
@@ -37,7 +64,6 @@ public class audioMerger : MonoBehaviour
|
||||
// Apply to toggle (if available) and sv_audioSettings
|
||||
if (enableGlobalMute_toggle != null)
|
||||
{
|
||||
// Prevent accidentally invoking listeners while initializing
|
||||
enableGlobalMute_toggle.onValueChanged.RemoveAllListeners();
|
||||
enableGlobalMute_toggle.isOn = isOn;
|
||||
enableGlobalMute_toggle.onValueChanged.AddListener(OnEnableGlobalMuteChanged);
|
||||
@@ -45,9 +71,73 @@ public class audioMerger : MonoBehaviour
|
||||
|
||||
if (sv_audioSettings != null)
|
||||
{
|
||||
// Documentation text normalized.
|
||||
sv_audioSettings.SetActive(!isOn);
|
||||
}
|
||||
|
||||
InitializeSliders();
|
||||
}
|
||||
|
||||
void InitializeSliders()
|
||||
{
|
||||
// Initialize 5 main sliders
|
||||
SetupSlider(mainVolumn_slider, mainVolumn_text, Key_MainVol, masterParam);
|
||||
SetupSlider(noteHit_slider, noteHit_text, Key_NoteHitVol, noteHitParam);
|
||||
SetupSlider(musicInGame_slider, musicInGame_text, Key_MusicInGameVol, musicInGameParam);
|
||||
SetupSlider(musicOutGame_slider, musicOutGame_text, Key_MusicOutGameVol, musicOutGameParam);
|
||||
SetupSlider(uiInteration_slider, uiInteration_text, Key_UiVol, uiInterationParam);
|
||||
}
|
||||
|
||||
void SetupSlider(Slider slider, Text text, string prefKey, string mixerParam)
|
||||
{
|
||||
if (slider == null) return;
|
||||
|
||||
// Ensure slider range is 0 to 1
|
||||
slider.minValue = 0f;
|
||||
slider.maxValue = 1f;
|
||||
|
||||
// Load saved value (default 1.0f for 100% / 0dB)
|
||||
float savedVal = PlayerPrefs.GetFloat(prefKey, 1.0f);
|
||||
slider.value = savedVal;
|
||||
|
||||
// Update display and mixer initially
|
||||
UpdateSliderEffect(slider.value, text, mixerParam);
|
||||
|
||||
// Add listener
|
||||
slider.onValueChanged.RemoveAllListeners();
|
||||
slider.onValueChanged.AddListener((val) => {
|
||||
UpdateSliderEffect(val, text, mixerParam);
|
||||
PlayerPrefs.SetFloat(prefKey, val);
|
||||
PlayerPrefs.Save();
|
||||
});
|
||||
}
|
||||
|
||||
void UpdateSliderEffect(float value, Text text, string mixerParam)
|
||||
{
|
||||
// 1. Update Text (Linear percentage)
|
||||
if (text != null)
|
||||
{
|
||||
text.text = Mathf.RoundToInt(value * 100f).ToString() + "%";
|
||||
}
|
||||
|
||||
// 2. Update Mixer (Logarithmic)
|
||||
if (mainMixer != null && !string.IsNullOrEmpty(mixerParam))
|
||||
{
|
||||
// Check Global Mute state from PlayerPrefs directly for robustness
|
||||
bool isMuted = PlayerPrefs.GetInt(PlayerPrefKey_EnableGlobalMute, 0) == 1;
|
||||
|
||||
// If this is Master and Global Mute is ON, don't update mixer to anything other than -80dB
|
||||
if (mixerParam == masterParam && isMuted)
|
||||
{
|
||||
mainMixer.SetFloat(mixerParam, -80f);
|
||||
return;
|
||||
}
|
||||
|
||||
// Normal volume application
|
||||
float clampedValue = Mathf.Clamp(value, 0.0001f, 1.0f);
|
||||
float dB = Mathf.Log10(clampedValue) * 20f;
|
||||
|
||||
mainMixer.SetFloat(mixerParam, dB);
|
||||
}
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
@@ -56,25 +146,47 @@ public class audioMerger : MonoBehaviour
|
||||
{
|
||||
enableGlobalMute_toggle.onValueChanged.RemoveListener(OnEnableGlobalMuteChanged);
|
||||
}
|
||||
|
||||
// Remove listeners from sliders to be safe
|
||||
RemoveSliderListeners(mainVolumn_slider);
|
||||
RemoveSliderListeners(noteHit_slider);
|
||||
RemoveSliderListeners(musicInGame_slider);
|
||||
RemoveSliderListeners(musicOutGame_slider);
|
||||
RemoveSliderListeners(uiInteration_slider);
|
||||
}
|
||||
|
||||
void RemoveSliderListeners(Slider slider)
|
||||
{
|
||||
if (slider != null) slider.onValueChanged.RemoveAllListeners();
|
||||
}
|
||||
|
||||
// Listener called when the toggle value changes
|
||||
void OnEnableGlobalMuteChanged(bool isOn)
|
||||
{
|
||||
// Save to PlayerPrefs
|
||||
// 1. Save state
|
||||
PlayerPrefs.SetInt(PlayerPrefKey_EnableGlobalMute, isOn ? 1 : 0);
|
||||
PlayerPrefs.Save();
|
||||
|
||||
// Documentation text normalized.
|
||||
// 2. UI visual feedback
|
||||
if (sv_audioSettings != null)
|
||||
{
|
||||
sv_audioSettings.SetActive(!isOn);
|
||||
}
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
|
||||
// 3. Apply mute/unmute to Master
|
||||
if (mainMixer != null)
|
||||
{
|
||||
if (isOn)
|
||||
{
|
||||
// Mute: set to -80dB
|
||||
mainMixer.SetFloat(masterParam, -80f);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unmute: restore from PlayerPrefs (Key_MainVol)
|
||||
float savedVal = PlayerPrefs.GetFloat(Key_MainVol, 1.0f);
|
||||
UpdateSliderEffect(savedVal, mainVolumn_text, masterParam);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,8 +34,8 @@ public class controllerSettings : MonoBehaviour
|
||||
private bool previousKeyValid = false;
|
||||
|
||||
// slider range
|
||||
private const float NoteSpeedMin = 0.8f;
|
||||
private const float NoteSpeedMax = 1.25f;
|
||||
private const float NoteSpeedMin = 0.5f;
|
||||
private const float NoteSpeedMax = 2f;
|
||||
private const string NoteSpeedPrefKey = "noteSpeedMultiplier";
|
||||
|
||||
// continuous change
|
||||
@@ -441,7 +441,9 @@ public class controllerSettings : MonoBehaviour
|
||||
{
|
||||
if (noteSpeedMultipler_valueText != null)
|
||||
{
|
||||
noteSpeedMultipler_valueText.text = value.ToString("F3");
|
||||
// 将 0.5-2.0 映射为 50%-200% 的百分比显示
|
||||
int percentage = Mathf.RoundToInt(value * 100f);
|
||||
noteSpeedMultipler_valueText.text = $"{percentage}%";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user