mirror of
https://github.com/RHeavenStudio/HeavenStudio.git
synced 2025-06-12 13:47:38 +02:00
Game Overlays (#280)
* add accuracy display * temp BG for show * separate overlays prefab make proper shader for star effects * aim shakiness display * implement testing skill star * fully functional skill star * separate section display from editor * fully separate chart section display from timeline * add section to overlays * fix nullreference issues * start game layout settings * add game settings script * fix nonfunctioning scoring * invert y position logic on timing bar * add perfect challenge functionality * fix section not showing up in editor add perfect challenge option * add timing display minimal mode * Update PerfectAndPractice.png * show gismo for minigame bounds in editor * add ability to disable overlays in editor * prepare medals add new timing display graphic * hide screen preview * per-axis camera control added per request * section medals basic functionality * add medal get animations * fix bug with perfect icons * visual enhancements * adjust look of timing display minmode address audio ducking issues(?) * prepare overlay lyt editor add viewport pan, rotate, scale adjust audio setting * add layout editor UI elements * dynamic overlay creation * fix default single timing disp * set up overlay settings controls * start UI events * runtime uuid for component reference * layout editor affects overlay elements * show overlay element previews while editing * advanced audio settings * fix bug in drop-down creation * fallback defaults for the new stuff * fix textbox & overlay visibility bugs
This commit is contained in:
54
Assets/Scripts/UI/Overlays/ChartSectionDisplay.cs
Normal file
54
Assets/Scripts/UI/Overlays/ChartSectionDisplay.cs
Normal file
@ -0,0 +1,54 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
using UnityEngine.UI;
|
||||
using TMPro;
|
||||
|
||||
using HeavenStudio.Editor;
|
||||
|
||||
namespace HeavenStudio.Common
|
||||
{
|
||||
public class ChartSectionDisplay : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private TMP_Text SectionText;
|
||||
[SerializeField] private Slider SectionProgress;
|
||||
|
||||
// Start is called before the first frame update
|
||||
void Start()
|
||||
{
|
||||
GameManager.instance.onSectionChange += OnSectionChange;
|
||||
GameManager.instance.onBeatChanged += OnBeatChanged;
|
||||
gameObject.SetActive(GameManager.instance.currentSection != null);
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
SectionProgress.value = GameManager.instance.sectionProgress;
|
||||
}
|
||||
|
||||
public void OnBeatChanged(float beat)
|
||||
{
|
||||
gameObject.SetActive(GameManager.instance.currentSection != null);
|
||||
SectionProgress.value = GameManager.instance.sectionProgress;
|
||||
}
|
||||
|
||||
public void OnSectionChange(DynamicBeatmap.ChartSection section)
|
||||
{
|
||||
if (section != null)
|
||||
{
|
||||
gameObject.SetActive(true);
|
||||
SectionText.text = section.sectionName;
|
||||
SectionProgress.value = GameManager.instance.sectionProgress;
|
||||
|
||||
if (PersistentDataManager.gameSettings.perfectChallengeType == PersistentDataManager.PerfectChallengeType.Off) return;
|
||||
if (!OverlaysManager.OverlaysEnabled) return;
|
||||
if (section.startPerfect && GoForAPerfect.instance != null && GoForAPerfect.instance.perfect && !GoForAPerfect.instance.gameObject.activeSelf)
|
||||
{
|
||||
GoForAPerfect.instance.Enable(section.beat);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Scripts/UI/Overlays/ChartSectionDisplay.cs.meta
Normal file
11
Assets/Scripts/UI/Overlays/ChartSectionDisplay.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 52530e94ebdc2e343a3aa77a9dfd8e71
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
123
Assets/Scripts/UI/Overlays/GoForAPerfect.cs
Normal file
123
Assets/Scripts/UI/Overlays/GoForAPerfect.cs
Normal file
@ -0,0 +1,123 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
using HeavenStudio.Util;
|
||||
|
||||
namespace HeavenStudio.Common
|
||||
{
|
||||
public class GoForAPerfect : MonoBehaviour
|
||||
{
|
||||
public static GoForAPerfect instance { get; set; }
|
||||
|
||||
[SerializeField] Animator texAnim;
|
||||
[SerializeField] Animator pAnim;
|
||||
|
||||
private bool active = false;
|
||||
private bool hiddenActive = false;
|
||||
|
||||
public bool perfect;
|
||||
|
||||
Conductor cond;
|
||||
float lastReportedBeat = 0f;
|
||||
float currentBeat = 0f;
|
||||
long currentBlink = 0;
|
||||
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
instance = this;
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
perfect = true;
|
||||
cond = Conductor.instance;
|
||||
}
|
||||
|
||||
private void Update() {
|
||||
gameObject.SetActive(hiddenActive);
|
||||
if (!active) return;
|
||||
if (!OverlaysManager.OverlaysEnabled) return;
|
||||
if (cond == null || !cond.isPlaying) return;
|
||||
|
||||
if (cond.ReportBeat(ref lastReportedBeat))
|
||||
{
|
||||
currentBeat = lastReportedBeat;
|
||||
if (currentBlink != 0)
|
||||
{
|
||||
currentBlink++;
|
||||
if (currentBlink % 2 == 0)
|
||||
{
|
||||
texAnim.Play("GoForAPerfect_Blink", -1, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
texAnim.Play("GoForAPerfect_Blink2", -1, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
currentBlink++;
|
||||
}
|
||||
}
|
||||
else if (cond.songPositionInBeats < lastReportedBeat)
|
||||
{
|
||||
lastReportedBeat = Mathf.Round(cond.songPositionInBeats);
|
||||
}
|
||||
}
|
||||
|
||||
public void Hit()
|
||||
{
|
||||
if (!active) return;
|
||||
if (!OverlaysManager.OverlaysEnabled) return;
|
||||
pAnim.Play("PerfectIcon_Hit", 0, 0);
|
||||
}
|
||||
|
||||
public void Miss()
|
||||
{
|
||||
perfect = false;
|
||||
if (!active) return;
|
||||
SetInactive();
|
||||
if (!OverlaysManager.OverlaysEnabled)
|
||||
{
|
||||
hiddenActive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
GameProfiler.instance.perfect = false;
|
||||
|
||||
texAnim.Play("GoForAPerfect_Miss");
|
||||
pAnim.Play("PerfectIcon_Miss", -1, 0);
|
||||
Jukebox.PlayOneShot("perfectMiss");
|
||||
}
|
||||
|
||||
public void Enable(double startBeat)
|
||||
{
|
||||
SetActive();
|
||||
gameObject.SetActive(true);
|
||||
pAnim.gameObject.SetActive(true);
|
||||
texAnim.gameObject.SetActive(true);
|
||||
texAnim.Play("GoForAPerfect_Idle");
|
||||
|
||||
currentBlink = 0;
|
||||
}
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
SetInactive();
|
||||
gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
public void SetActive()
|
||||
{
|
||||
hiddenActive = true;
|
||||
active = true;
|
||||
}
|
||||
public void SetInactive()
|
||||
{
|
||||
active = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
11
Assets/Scripts/UI/Overlays/GoForAPerfect.cs.meta
Normal file
11
Assets/Scripts/UI/Overlays/GoForAPerfect.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9e5c7679bb002f04980d7554b101f24b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
284
Assets/Scripts/UI/Overlays/OverlaysManager.cs
Normal file
284
Assets/Scripts/UI/Overlays/OverlaysManager.cs
Normal file
@ -0,0 +1,284 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
|
||||
namespace HeavenStudio.Common
|
||||
{
|
||||
public class OverlaysManager : MonoBehaviour
|
||||
{
|
||||
public static OverlaysManager instance { get; private set; }
|
||||
public static bool OverlaysEnabled;
|
||||
|
||||
const float WIDTH_SPAN = 10f;
|
||||
const float HEIGHT_SPAN = 10f * (9f / 16f);
|
||||
|
||||
[Header("Prefabs")]
|
||||
[SerializeField] GameObject TimingDisplayPrefab;
|
||||
[SerializeField] GameObject SkillStarPrefab;
|
||||
[SerializeField] GameObject ChartSectionPrefab;
|
||||
|
||||
[Header("Components")]
|
||||
[SerializeField] Transform ComponentHolder;
|
||||
|
||||
List<OverlaysManager.OverlayOption> lytElements = new List<OverlaysManager.OverlayOption>();
|
||||
|
||||
// Start is called before the first frame update
|
||||
void Start()
|
||||
{
|
||||
instance = this;
|
||||
RefreshOverlaysLayout();
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void TogleOverlaysVisibility(bool visible)
|
||||
{
|
||||
OverlaysEnabled = visible;
|
||||
RepositionElements();
|
||||
}
|
||||
|
||||
public void RefreshOverlaysLayout()
|
||||
{
|
||||
if (PersistentDataManager.gameSettings.timingDisplayComponents == null || PersistentDataManager.gameSettings.timingDisplayComponents.Count == 0)
|
||||
{
|
||||
PersistentDataManager.gameSettings.timingDisplayComponents = new List<TimingDisplayComponent>()
|
||||
{
|
||||
TimingDisplayComponent.CreateDefaultDual()
|
||||
};
|
||||
}
|
||||
if (PersistentDataManager.gameSettings.skillStarComponents == null || PersistentDataManager.gameSettings.skillStarComponents.Count == 0)
|
||||
{
|
||||
PersistentDataManager.gameSettings.skillStarComponents = new List<SkillStarComponent>()
|
||||
{
|
||||
SkillStarComponent.CreateDefault()
|
||||
};
|
||||
}
|
||||
if (PersistentDataManager.gameSettings.sectionComponents == null || PersistentDataManager.gameSettings.sectionComponents.Count == 0)
|
||||
{
|
||||
PersistentDataManager.gameSettings.sectionComponents = new List<SectionComponent>()
|
||||
{
|
||||
SectionComponent.CreateDefault()
|
||||
};
|
||||
}
|
||||
|
||||
lytElements = new List<OverlaysManager.OverlayOption>();
|
||||
foreach (var c in PersistentDataManager.gameSettings.timingDisplayComponents) { lytElements.Add(c); }
|
||||
foreach (var c in PersistentDataManager.gameSettings.skillStarComponents) { lytElements.Add(c); }
|
||||
foreach (var c in PersistentDataManager.gameSettings.sectionComponents) { lytElements.Add(c); }
|
||||
|
||||
foreach (Transform child in ComponentHolder.transform)
|
||||
{
|
||||
Destroy(child.gameObject);
|
||||
}
|
||||
|
||||
foreach (var c in lytElements)
|
||||
{
|
||||
if (c is TimingDisplayComponent) {
|
||||
Debug.Log("TimingDisplayComponent");
|
||||
c.CreateElement(TimingDisplayPrefab, ComponentHolder);
|
||||
}
|
||||
else if (c is SkillStarComponent) {
|
||||
Debug.Log("SkillStarComponent");
|
||||
c.CreateElement(SkillStarPrefab, ComponentHolder);
|
||||
}
|
||||
else if (c is SectionComponent) {
|
||||
Debug.Log("SectionComponent");
|
||||
c.CreateElement(ChartSectionPrefab, ComponentHolder);
|
||||
}
|
||||
c.PositionElement();
|
||||
}
|
||||
}
|
||||
|
||||
void RepositionElements()
|
||||
{
|
||||
lytElements = new List<OverlaysManager.OverlayOption>();
|
||||
foreach (var c in PersistentDataManager.gameSettings.timingDisplayComponents) { lytElements.Add(c); }
|
||||
foreach (var c in PersistentDataManager.gameSettings.skillStarComponents) { lytElements.Add(c); }
|
||||
foreach (var c in PersistentDataManager.gameSettings.sectionComponents) { lytElements.Add(c); }
|
||||
foreach (var c in lytElements)
|
||||
{
|
||||
c.PositionElement();
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class TimingDisplayComponent : OverlayOption
|
||||
{
|
||||
public enum TimingDisplayType
|
||||
{
|
||||
Dual,
|
||||
Single,
|
||||
}
|
||||
|
||||
[NonSerialized] GameObject go2;
|
||||
[SerializeField] public TimingDisplayType tdType;
|
||||
|
||||
public TimingDisplayComponent(TimingDisplayType type, bool enable, Vector2 position, float scale, float rotation)
|
||||
{
|
||||
tdType = type;
|
||||
this.enable = enable;
|
||||
this.position = position;
|
||||
this.scale = scale;
|
||||
this.rotation = rotation;
|
||||
}
|
||||
|
||||
public override void CreateElement(GameObject prefab, Transform holder)
|
||||
{
|
||||
if (go == null) go = Instantiate(prefab, holder);
|
||||
if (go2 == null) go2 = Instantiate(prefab, holder);
|
||||
}
|
||||
|
||||
public override void PositionElement()
|
||||
{
|
||||
if (go != null)
|
||||
{
|
||||
switch (tdType)
|
||||
{
|
||||
case TimingDisplayType.Dual:
|
||||
go.transform.localPosition = position * new Vector2(WIDTH_SPAN, HEIGHT_SPAN) * new Vector2(-1, 1);
|
||||
go.transform.localScale = Vector3.one * scale;
|
||||
go.transform.localRotation = Quaternion.Euler(0, 0, -rotation);
|
||||
|
||||
go2.transform.localPosition = position * new Vector2(WIDTH_SPAN, HEIGHT_SPAN);
|
||||
go2.transform.localScale = Vector3.one * scale;
|
||||
go2.transform.localRotation = Quaternion.Euler(0, 0, rotation);
|
||||
|
||||
go.SetActive(enable && OverlaysManager.OverlaysEnabled);
|
||||
go2.SetActive(enable && OverlaysManager.OverlaysEnabled);
|
||||
break;
|
||||
case TimingDisplayType.Single:
|
||||
go.transform.localPosition = position * new Vector2(WIDTH_SPAN, HEIGHT_SPAN);
|
||||
go.transform.localScale = Vector3.one * scale;
|
||||
go.transform.localRotation = Quaternion.Euler(0, 0, rotation);
|
||||
go.SetActive(enable && OverlaysManager.OverlaysEnabled);
|
||||
go2.SetActive(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void EnablePreview() {}
|
||||
public override void DisablePreview() {}
|
||||
|
||||
public static TimingDisplayComponent CreateDefaultDual()
|
||||
{
|
||||
return new TimingDisplayComponent(TimingDisplayType.Dual, true, new Vector2(-0.84f, 0), 1f, 0f);
|
||||
}
|
||||
|
||||
public static TimingDisplayComponent CreateDefaultSingle()
|
||||
{
|
||||
return new TimingDisplayComponent(TimingDisplayType.Single, true, new Vector2(0, -0.8f), 1f, 90f);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class SkillStarComponent : OverlayOption
|
||||
{
|
||||
public SkillStarComponent(bool enable, Vector2 position, float scale, float rotation)
|
||||
{
|
||||
this.enable = enable;
|
||||
this.position = position;
|
||||
this.scale = scale;
|
||||
this.rotation = rotation;
|
||||
}
|
||||
|
||||
public override void PositionElement()
|
||||
{
|
||||
if (go != null)
|
||||
{
|
||||
go.transform.localPosition = position * new Vector2(WIDTH_SPAN, HEIGHT_SPAN);
|
||||
go.transform.localScale = Vector3.one * scale;
|
||||
go.transform.localRotation = Quaternion.Euler(0, 0, rotation);
|
||||
go.SetActive(enable && OverlaysManager.OverlaysEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
public override void EnablePreview() { SkillStarManager.instance?.DoStarPreview(); }
|
||||
public override void DisablePreview() { SkillStarManager.instance.ResetStarPreview(); }
|
||||
|
||||
public static SkillStarComponent CreateDefault()
|
||||
{
|
||||
return new SkillStarComponent(true, new Vector2(0.75f, -0.7f), 1f, 0f);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class SectionComponent : OverlayOption
|
||||
{
|
||||
public SectionComponent(bool enable, Vector2 position, float scale, float rotation)
|
||||
{
|
||||
this.enable = enable;
|
||||
this.position = position;
|
||||
this.scale = scale;
|
||||
this.rotation = rotation;
|
||||
}
|
||||
|
||||
public override void PositionElement()
|
||||
{
|
||||
if (go != null)
|
||||
{
|
||||
go.transform.localPosition = position * new Vector2(WIDTH_SPAN, HEIGHT_SPAN);
|
||||
go.transform.localScale = Vector3.one * scale;
|
||||
go.transform.localRotation = Quaternion.Euler(0, 0, rotation);
|
||||
go.SetActive(enable && OverlaysManager.OverlaysEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
public override void EnablePreview()
|
||||
{
|
||||
if (go != null)
|
||||
{
|
||||
go.GetComponent<Image>().enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
public override void DisablePreview()
|
||||
{
|
||||
if (go != null)
|
||||
{
|
||||
go.GetComponent<Image>().enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
public static SectionComponent CreateDefault()
|
||||
{
|
||||
return new SectionComponent(true, new Vector2(0.7f, 0.765f), 1f, 0f);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public abstract class OverlayOption
|
||||
{
|
||||
static long uuidCounter = 0;
|
||||
[NonSerialized] protected GameObject go;
|
||||
[NonSerialized] public long uuid = GenerateUUID();
|
||||
[SerializeField] public bool enable;
|
||||
[SerializeField] public Vector2 position;
|
||||
[SerializeField] public float scale;
|
||||
[SerializeField] public float rotation;
|
||||
|
||||
static long GenerateUUID()
|
||||
{
|
||||
return uuidCounter++;
|
||||
}
|
||||
|
||||
public virtual void CreateElement(GameObject prefab, Transform holder)
|
||||
{
|
||||
if (go == null)
|
||||
go = Instantiate(prefab, holder);
|
||||
}
|
||||
|
||||
public abstract void PositionElement();
|
||||
public abstract void EnablePreview();
|
||||
public abstract void DisablePreview();
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Scripts/UI/Overlays/OverlaysManager.cs.meta
Normal file
11
Assets/Scripts/UI/Overlays/OverlaysManager.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6c9513d62ced05840a5db89f2a529a71
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
77
Assets/Scripts/UI/Overlays/SectionMedalsManager.cs
Normal file
77
Assets/Scripts/UI/Overlays/SectionMedalsManager.cs
Normal file
@ -0,0 +1,77 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
using HeavenStudio.Util;
|
||||
using HeavenStudio.Games;
|
||||
|
||||
namespace HeavenStudio.Common
|
||||
{
|
||||
public class SectionMedalsManager : MonoBehaviour
|
||||
{
|
||||
public static SectionMedalsManager instance { get; private set; }
|
||||
|
||||
[SerializeField] GameObject MedalsHolder;
|
||||
[SerializeField] GameObject MedalOkPrefab;
|
||||
[SerializeField] GameObject MedalMissPrefab;
|
||||
|
||||
Conductor cond;
|
||||
bool isMedalsStarted = false;
|
||||
bool isMedalsEligible = true;
|
||||
|
||||
// Start is called before the first frame update
|
||||
void Start()
|
||||
{
|
||||
instance = this;
|
||||
cond = Conductor.instance;
|
||||
GameManager.instance.onSectionChange += OnSectionChange;
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void MakeIneligible()
|
||||
{
|
||||
isMedalsEligible = false;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
isMedalsStarted = false;
|
||||
isMedalsEligible = true;
|
||||
foreach (Transform child in MedalsHolder.transform)
|
||||
{
|
||||
Destroy(child.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnSectionChange(DynamicBeatmap.ChartSection section)
|
||||
{
|
||||
if (section == null) return;
|
||||
Debug.Log(PersistentDataManager.gameSettings.isMedalOn);
|
||||
if (PersistentDataManager.gameSettings.isMedalOn && !isMedalsStarted)
|
||||
{
|
||||
isMedalsStarted = true;
|
||||
isMedalsEligible = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
GameObject medal = Instantiate(isMedalsEligible ? MedalOkPrefab : MedalMissPrefab, MedalsHolder.transform);
|
||||
medal.SetActive(true);
|
||||
isMedalsEligible = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnRemixEnd()
|
||||
{
|
||||
if (PersistentDataManager.gameSettings.isMedalOn && isMedalsStarted)
|
||||
{
|
||||
GameObject medal = Instantiate(isMedalsEligible ? MedalOkPrefab : MedalMissPrefab, MedalsHolder.transform);
|
||||
medal.SetActive(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Scripts/UI/Overlays/SectionMedalsManager.cs.meta
Normal file
11
Assets/Scripts/UI/Overlays/SectionMedalsManager.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 93e2705ea532cf24d8c161d8762aeccc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
123
Assets/Scripts/UI/Overlays/SkillStarManager.cs
Normal file
123
Assets/Scripts/UI/Overlays/SkillStarManager.cs
Normal file
@ -0,0 +1,123 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
using HeavenStudio.Util;
|
||||
using HeavenStudio.Games;
|
||||
|
||||
namespace HeavenStudio.Common
|
||||
{
|
||||
public class SkillStarManager : MonoBehaviour
|
||||
{
|
||||
public enum StarState
|
||||
{
|
||||
None,
|
||||
In,
|
||||
Collected,
|
||||
Out
|
||||
}
|
||||
public static SkillStarManager instance { get; private set; }
|
||||
[SerializeField] private Animator starAnim;
|
||||
[SerializeField] private ParticleSystem starParticle;
|
||||
|
||||
public float StarTargetTime { get { return starStart + starLength; } }
|
||||
public bool IsEligible { get; private set; }
|
||||
|
||||
float starStart = float.MaxValue;
|
||||
float starLength = float.MaxValue;
|
||||
StarState state = StarState.None;
|
||||
Conductor cond;
|
||||
|
||||
// Start is called before the first frame update
|
||||
void Start()
|
||||
{
|
||||
instance = this;
|
||||
cond = Conductor.instance;
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
if (cond.songPositionInBeatsAsDouble > starStart && state == StarState.In)
|
||||
{
|
||||
double offset = cond.SecsToBeats(Minigame.AceStartTime()-1, cond.GetBpmAtBeat(StarTargetTime));
|
||||
if (cond.songPositionInBeatsAsDouble <= starStart + starLength + offset)
|
||||
starAnim.DoScaledAnimation("StarIn", starStart, starLength + (float)offset);
|
||||
else
|
||||
starAnim.Play("StarIn", -1, 1f);
|
||||
|
||||
offset = cond.SecsToBeats(Minigame.AceEndTime()-1, cond.GetBpmAtBeat(StarTargetTime));
|
||||
if (cond.songPositionInBeatsAsDouble > starStart + starLength + offset)
|
||||
KillStar();
|
||||
}
|
||||
}
|
||||
|
||||
public void DoStarPreview()
|
||||
{
|
||||
if (starAnim == null) return;
|
||||
starAnim.Play("StarJust", -1, 0.5f);
|
||||
starAnim.speed = 0f;
|
||||
}
|
||||
|
||||
public void ResetStarPreview()
|
||||
{
|
||||
if (starAnim == null) return;
|
||||
starAnim.Play("NoPose", -1, 0f);
|
||||
starAnim.speed = 1f;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
IsEligible = false;
|
||||
cond = Conductor.instance;
|
||||
state = StarState.None;
|
||||
starAnim.Play("NoPose", -1, 0f);
|
||||
starAnim.speed = 1f;
|
||||
starStart = float.MaxValue;
|
||||
starLength = float.MaxValue;
|
||||
}
|
||||
|
||||
public void DoStarIn(float beat, float length)
|
||||
{
|
||||
if (!OverlaysManager.OverlaysEnabled) return;
|
||||
IsEligible = true;
|
||||
state = StarState.In;
|
||||
starStart = beat;
|
||||
starLength = length;
|
||||
|
||||
TimingAccuracyDisplay.instance.StartStarFlash();
|
||||
|
||||
starAnim.DoScaledAnimation("StarIn", beat, length);
|
||||
}
|
||||
|
||||
public bool DoStarJust()
|
||||
{
|
||||
if (state == StarState.In &&
|
||||
cond.songPositionInBeatsAsDouble >= StarTargetTime + cond.SecsToBeats(Minigame.AceStartTime()-1, cond.GetBpmAtBeat(StarTargetTime)) &&
|
||||
cond.songPositionInBeatsAsDouble <= StarTargetTime + cond.SecsToBeats(Minigame.AceEndTime()-1, cond.GetBpmAtBeat(StarTargetTime))
|
||||
)
|
||||
{
|
||||
state = StarState.Collected;
|
||||
starAnim.Play("StarJust", -1, 0f);
|
||||
starParticle.Play();
|
||||
Jukebox.PlayOneShot("skillStar");
|
||||
|
||||
TimingAccuracyDisplay.instance.StopStarFlash();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void KillStar()
|
||||
{
|
||||
if (state == StarState.In && cond.songPositionInBeatsAsDouble >= starStart + starLength*0.5f || !cond.isPlaying)
|
||||
{
|
||||
IsEligible = false;
|
||||
state = StarState.Out;
|
||||
starAnim.Play("NoPose", -1, 0f);
|
||||
|
||||
TimingAccuracyDisplay.instance.StopStarFlash();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Scripts/UI/Overlays/SkillStarManager.cs.meta
Normal file
11
Assets/Scripts/UI/Overlays/SkillStarManager.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0521855ace8165a44b98e3d0022da114
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
172
Assets/Scripts/UI/Overlays/TimingAccuracyDisplay.cs
Normal file
172
Assets/Scripts/UI/Overlays/TimingAccuracyDisplay.cs
Normal file
@ -0,0 +1,172 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
using HeavenStudio.Games;
|
||||
|
||||
namespace HeavenStudio.Common
|
||||
{
|
||||
public class TimingAccuracyDisplay : MonoBehaviour
|
||||
{
|
||||
public enum Rating
|
||||
{
|
||||
NG,
|
||||
OK,
|
||||
Just
|
||||
}
|
||||
|
||||
public static TimingAccuracyDisplay instance { get; private set; }
|
||||
|
||||
[SerializeField] GameObject NG;
|
||||
[SerializeField] GameObject OK;
|
||||
[SerializeField] GameObject Just;
|
||||
[SerializeField] GameObject MinimalJust;
|
||||
[SerializeField] GameObject MinimalOK;
|
||||
[SerializeField] GameObject MinimalNG;
|
||||
|
||||
[SerializeField] Animator MetreAnim;
|
||||
|
||||
[SerializeField] Transform arrowTransform;
|
||||
[SerializeField] Transform barTransform;
|
||||
[SerializeField] Transform barJustTransform;
|
||||
[SerializeField] Transform barOKTransform;
|
||||
[SerializeField] Transform barNGTransform;
|
||||
|
||||
float targetArrowPos = 0f;
|
||||
|
||||
// Start is called before the first frame update
|
||||
void Start()
|
||||
{
|
||||
instance = this;
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
arrowTransform.localPosition = Vector3.Lerp(arrowTransform.localPosition, new Vector3(0, targetArrowPos, 0), 4f * Time.deltaTime);
|
||||
}
|
||||
|
||||
public void ResetArrow()
|
||||
{
|
||||
targetArrowPos = 0f;
|
||||
arrowTransform.localPosition = Vector3.zero;
|
||||
StopStarFlash();
|
||||
|
||||
NG.GetComponent<ParticleSystem>().Stop();
|
||||
OK.GetComponent<ParticleSystem>().Stop();
|
||||
Just.GetComponent<ParticleSystem>().Stop();
|
||||
MinimalNG.GetComponent<ParticleSystem>().Stop();
|
||||
MinimalOK.GetComponent<ParticleSystem>().Stop();
|
||||
MinimalJust.GetComponent<ParticleSystem>().Stop();
|
||||
}
|
||||
|
||||
public void StartStarFlash()
|
||||
{
|
||||
MetreAnim.Play("StarWarn", -1, 0f);
|
||||
}
|
||||
|
||||
public void StopStarFlash()
|
||||
{
|
||||
MetreAnim.Play("NoPose", -1, 0f);
|
||||
}
|
||||
|
||||
public void MakeAccuracyVfx(double time, bool late = false)
|
||||
{
|
||||
if (!OverlaysManager.OverlaysEnabled) return;
|
||||
GameObject it;
|
||||
Rating type = Rating.NG;
|
||||
|
||||
// centre of the transfor would be "perfect ace"
|
||||
// move the object up or down the bar depending on hit time
|
||||
// use bar's scale Y for now, we're waiting for proper assets
|
||||
|
||||
// this probably doesn't work
|
||||
float frac = 0f;
|
||||
float y = barTransform.position.y;
|
||||
|
||||
// SetArrowPos(time);
|
||||
|
||||
// no Clamp() because double
|
||||
time = System.Math.Max(Minigame.EarlyTime(), System.Math.Min(Minigame.EndTime(), time));
|
||||
|
||||
if (time >= Minigame.AceStartTime() && time <= Minigame.AceEndTime())
|
||||
{
|
||||
type = Rating.Just;
|
||||
frac = (float)((time - Minigame.AceStartTime()) / (Minigame.AceEndTime() - Minigame.AceStartTime()));
|
||||
y = barJustTransform.localScale.y * frac - (barJustTransform.localScale.y * 0.5f);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (time > 1.0)
|
||||
{
|
||||
// goes "down"
|
||||
if (time <= Minigame.LateTime())
|
||||
{
|
||||
type = Rating.OK;
|
||||
frac = (float)((time - Minigame.AceEndTime()) / (Minigame.LateTime() - Minigame.AceEndTime()));
|
||||
y = ((barOKTransform.localScale.y - barJustTransform.localScale.y) * frac) + barJustTransform.localScale.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
type = Rating.NG;
|
||||
frac = (float)((time - Minigame.LateTime()) / (Minigame.EndTime() - Minigame.LateTime()));
|
||||
y = ((barNGTransform.localScale.y - barOKTransform.localScale.y) * frac) + barOKTransform.localScale.y;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// goes "up"
|
||||
if (time >= Minigame.PerfectTime())
|
||||
{
|
||||
type = Rating.OK;
|
||||
frac = (float)((time - Minigame.PerfectTime()) / (Minigame.AceStartTime() - Minigame.PerfectTime()));
|
||||
y = ((barOKTransform.localScale.y - barJustTransform.localScale.y) * -frac) - barJustTransform.localScale.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
type = Rating.NG;
|
||||
frac = (float)((time - Minigame.EarlyTime()) / (Minigame.PerfectTime() - Minigame.EarlyTime()));
|
||||
y = ((barNGTransform.localScale.y - barOKTransform.localScale.y) * -frac) - barOKTransform.localScale.y;
|
||||
}
|
||||
}
|
||||
y *= -0.5f;
|
||||
}
|
||||
|
||||
targetArrowPos = (targetArrowPos + y) * 0.5f;
|
||||
|
||||
if (PersistentDataManager.gameSettings.timingDisplayMinMode)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case Rating.OK:
|
||||
it = MinimalOK;
|
||||
break;
|
||||
case Rating.Just:
|
||||
it = MinimalJust;
|
||||
break;
|
||||
default:
|
||||
it = MinimalNG;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case Rating.OK:
|
||||
it = OK;
|
||||
break;
|
||||
case Rating.Just:
|
||||
it = Just;
|
||||
break;
|
||||
default:
|
||||
it = NG;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
it.transform.position = barTransform.position + new Vector3(0, barTransform.localScale.y * y, 0);
|
||||
it.GetComponent<ParticleSystem>().Play();
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Scripts/UI/Overlays/TimingAccuracyDisplay.cs.meta
Normal file
11
Assets/Scripts/UI/Overlays/TimingAccuracyDisplay.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a91127b0615e4d341bcde412b3e92f93
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user