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:
minenice55
2023-03-10 23:51:22 -05:00
committed by GitHub
parent 11d4abf1eb
commit d5ae99d8ba
136 changed files with 131638 additions and 1043 deletions

View File

@ -280,9 +280,11 @@ namespace HeavenStudio
public float GetBpmAtBeat(float beat)
{
var chart = GameManager.instance.Beatmap;
if (chart.tempoChanges.Count == 0)
return chart.bpm;
float bpm = chart.bpm;
foreach (DynamicBeatmap.TempoChange t in GameManager.instance.Beatmap.tempoChanges)
foreach (DynamicBeatmap.TempoChange t in chart.tempoChanges)
{
if (t.beat > beat)
{
@ -303,7 +305,7 @@ namespace HeavenStudio
float lastTempoChangeBeat = 0f;
foreach (DynamicBeatmap.TempoChange t in GameManager.instance.Beatmap.tempoChanges)
foreach (DynamicBeatmap.TempoChange t in chart.tempoChanges)
{
if (t.beat > beat)
{

View File

@ -12,9 +12,16 @@ namespace HeavenStudio
public static GameCamera instance { get; private set; }
public new Camera camera;
public enum CameraAxis
{
All,
X,
Y,
Z
}
private List<DynamicBeatmap.DynamicEntity> positionEvents = new List<DynamicBeatmap.DynamicEntity>();
private List<DynamicBeatmap.DynamicEntity> rotationEvents = new List<DynamicBeatmap.DynamicEntity>();
private List<DynamicBeatmap.DynamicEntity> scaleEvents = new List<DynamicBeatmap.DynamicEntity>();
private List<DynamicBeatmap.DynamicEntity> shakeEvents = new List<DynamicBeatmap.DynamicEntity>();
/**
@ -22,25 +29,20 @@ namespace HeavenStudio
**/
public static Vector3 defaultPosition = new Vector3(0, 0, -10);
public static Vector3 defaultRotEluer = new Vector3(0, 0, 0);
public static Vector3 defaultScale = new Vector3(16, 9, 1);
public static Vector3 defaultShake = new Vector3(0, 0, 0);
/**
camera's current transformation
TODO: stretching (the scale param) not working, will need help with this cause I don't understand Unity's camera
**/
private static Vector3 position;
private static Vector3 rotEluer;
private static Vector3 scale;
private static Vector3 shakeResult;
/**
camera's last transformation
TODO: stretching (the scaleLast param) not working, will need help with this cause I don't understand Unity's camera
**/
private static Vector3 positionLast;
private static Vector3 rotEluerLast;
private static Vector3 scaleLast;
private static Vector3 shakeLast;
/**
@ -71,7 +73,6 @@ namespace HeavenStudio
positionLast = defaultPosition;
rotEluerLast = defaultRotEluer;
scaleLast = defaultScale;
}
public void OnBeatChanged(float beat)
@ -81,7 +82,6 @@ namespace HeavenStudio
positionLast = defaultPosition;
rotEluerLast = defaultRotEluer;
scaleLast = defaultScale;
// this entire thing is a mess redo it later
//pos
@ -97,10 +97,6 @@ namespace HeavenStudio
shakeEvents = EventCaller.GetAllInGameManagerList("vfx", new string[] { "screen shake" });
//scale (TODO)
// scaleEvents = EventCaller.GetAllInGameManagerList("vfx", new string[] { "scale camera" });
UpdateCameraTranslate();
UpdateCameraRotate();
SetShakeIntensity();
@ -115,7 +111,6 @@ namespace HeavenStudio
Camera cam = GetCamera();
cam.transform.localPosition = position + additionalPosition + shakeResult;
cam.transform.eulerAngles = rotEluer + additionalRotEluer;
cam.transform.localScale = Vector3.Scale(scale, additionalScale);
}
private void UpdateCameraTranslate()
@ -126,14 +121,42 @@ namespace HeavenStudio
if (prog >= 0f)
{
EasingFunction.Function func = EasingFunction.GetEasingFunction((EasingFunction.Ease) e["ease"]);
float dx = func(positionLast.x, e["valA"], Mathf.Min(prog, 1f));
float dy = func(positionLast.y, e["valB"], Mathf.Min(prog, 1f));
float dz = func(positionLast.z, -e["valC"], Mathf.Min(prog, 1f));
position = new Vector3(dx, dy, dz);
switch (e["axis"])
{
case (int) CameraAxis.X:
position.x = func(positionLast.x, e["valA"], Mathf.Min(prog, 1f));
break;
case (int) CameraAxis.Y:
position.y = func(positionLast.y, e["valB"], Mathf.Min(prog, 1f));
break;
case (int) CameraAxis.Z:
position.z = func(positionLast.z, -e["valC"], Mathf.Min(prog, 1f));
break;
default:
float dx = func(positionLast.x, e["valA"], Mathf.Min(prog, 1f));
float dy = func(positionLast.y, e["valB"], Mathf.Min(prog, 1f));
float dz = func(positionLast.z, -e["valC"], Mathf.Min(prog, 1f));
position = new Vector3(dx, dy, dz);
break;
}
}
if (prog > 1f)
{
positionLast = new Vector3(e["valA"], e["valB"], -e["valC"]);
switch (e["axis"])
{
case (int) CameraAxis.X:
positionLast.x = e["valA"];
break;
case (int) CameraAxis.Y:
positionLast.y = e["valB"];
break;
case (int) CameraAxis.Z:
positionLast.z = -e["valC"];
break;
default:
positionLast = new Vector3(e["valA"], e["valB"], -e["valC"]);
break;
}
}
}
}
@ -146,14 +169,42 @@ namespace HeavenStudio
if (prog >= 0f)
{
EasingFunction.Function func = EasingFunction.GetEasingFunction((EasingFunction.Ease) e["ease"]);
float dx = func(rotEluerLast.x, e["valA"], Mathf.Min(prog, 1f));
float dy = func(rotEluerLast.y, e["valB"], Mathf.Min(prog, 1f));
float dz = func(-rotEluerLast.z, e["valC"], Mathf.Min(prog, 1f));
rotEluer = new Vector3(dx, dy, dz); //I'm stupid and forgot to negate the rotation gfd 😢
switch (e["axis"])
{
case (int) CameraAxis.X:
rotEluer.x = func(rotEluerLast.x, e["valA"], Mathf.Min(prog, 1f));
break;
case (int) CameraAxis.Y:
rotEluer.y = func(rotEluerLast.y, e["valB"], Mathf.Min(prog, 1f));
break;
case (int) CameraAxis.Z:
rotEluer.z = func(rotEluerLast.z, -e["valC"], Mathf.Min(prog, 1f));
break;
default:
float dx = func(rotEluerLast.x, e["valA"], Mathf.Min(prog, 1f));
float dy = func(rotEluerLast.y, e["valB"], Mathf.Min(prog, 1f));
float dz = func(rotEluerLast.z, -e["valC"], Mathf.Min(prog, 1f));
rotEluer = new Vector3(dx, dy, dz); //I'm stupid and forgot to negate the rotation gfd 😢
break;
}
}
if (prog > 1f)
{
switch (e["axis"])
{
case (int) CameraAxis.X:
rotEluerLast.x = e["valA"];
break;
case (int) CameraAxis.Y:
rotEluerLast.y = e["valB"];
break;
case (int) CameraAxis.Z:
rotEluerLast.z = -e["valC"];
break;
default:
rotEluerLast = new Vector3(e["valA"], e["valB"], -e["valC"]);
break;
}
rotEluerLast = new Vector3(e["valA"], e["valB"], -e["valC"]);
}
}
@ -180,7 +231,6 @@ namespace HeavenStudio
{
position = defaultPosition;
rotEluer = defaultRotEluer;
scale = defaultScale;
shakeResult = defaultShake;
}
@ -188,7 +238,6 @@ namespace HeavenStudio
{
additionalPosition = new Vector3(0, 0, 0);
additionalRotEluer = new Vector3(0, 0, 0);
additionalScale = new Vector3(1, 1, 1);
}
public static Camera GetCamera()

View File

@ -7,6 +7,7 @@ using UnityEngine;
using Starpelly;
using Newtonsoft.Json;
using HeavenStudio.Games;
using HeavenStudio.Common;
namespace HeavenStudio
{
@ -21,7 +22,7 @@ namespace HeavenStudio
[Header("Components")]
public string txt;
public string ext;
public Camera GameCamera, CursorCam, OverlayCamera;
public Camera GameCamera, CursorCam, OverlayCamera, StaticCamera;
public GameObject GameLetterbox;
public CircleCursor CircleCursor;
[HideInInspector] public GameObject GamesHolder;
@ -44,11 +45,7 @@ namespace HeavenStudio
public bool autoplay;
public bool canInput = true;
public DynamicBeatmap.ChartSection currentSection, nextSection;
public float sectionProgress { get {
if (currentSection == null) return 0;
if (nextSection == null) return (Conductor.instance.songPositionInBeats - currentSection.beat) / (endBeat - currentSection.beat);
return (Conductor.instance.songPositionInBeats - currentSection.beat) / (nextSection.beat - currentSection.beat);
}}
public float sectionProgress { get; private set; }
public event Action<float> onBeatChanged;
public event Action<DynamicBeatmap.ChartSection> onSectionChange;
@ -88,6 +85,7 @@ namespace HeavenStudio
return totalPlayerAccuracy / totalInputs;
}
}
bool skillStarCollected = false;
private void Awake()
{
@ -125,8 +123,23 @@ namespace HeavenStudio
Conductor.instance.SetVolume(Beatmap.musicVolume);
Conductor.instance.firstBeatOffset = Beatmap.firstBeatOffset;
GameObject textbox = Instantiate(Resources.Load<GameObject>("Prefabs/Common/Textbox"));
textbox.name = "Textbox";
// note: serialize this shit in the inspector //
GameObject textbox = Instantiate(Resources.Load<GameObject>("Prefabs/Common/Textbox"));
textbox.name = "Textbox";
GameObject overlays = Instantiate(Resources.Load<GameObject>("Prefabs/Common/Overlays"));
overlays.name = "Overlays";
GameObject timingDisp = Instantiate(Resources.Load<GameObject>("Prefabs/Common/Overlays/TimingAccuracy"));
timingDisp.name = "TimingDisplay";
GameObject skillStarDisp = Instantiate(Resources.Load<GameObject>("Prefabs/Common/Overlays/SkillStar"));
skillStarDisp.name = "SkillStar";
GoForAPerfect.instance.Disable();
/////
if (txt != null && ext != null)
{
LoadRemix(txt, ext);
@ -206,12 +219,25 @@ namespace HeavenStudio
}
}
public void ScoreInputAccuracy(double accuracy, bool late, double weight = 1)
public void ScoreInputAccuracy(double accuracy, bool late, double time, double weight = 1, bool doDisplay = true)
{
totalInputs += weight;
totalPlayerAccuracy += accuracy * weight;
if (accuracy < Minigame.rankOkThreshold && weight > 0)
{
SkillStarManager.instance.KillStar();
}
if (SkillStarManager.instance.IsEligible && !skillStarCollected && accuracy >= 1f)
{
if (SkillStarManager.instance.DoStarJust())
skillStarCollected = true;
}
// push the hit event to the timing display
if (doDisplay)
TimingAccuracyDisplay.instance.MakeAccuracyVfx(time, late);
}
public void SeekAheadAndPreload(double start, float seekTime = 8f)
@ -292,7 +318,6 @@ namespace HeavenStudio
}
}
// LateUpdate works a bit better(?) but causes some bugs (like issues with bop animations).
private void Update()
{
PlayerInput.UpdateInputControllers();
@ -301,16 +326,16 @@ namespace HeavenStudio
return;
if (!Conductor.instance.isPlaying)
return;
Conductor cond = Conductor.instance;
List<float> entities = Beatmap.entities.Select(c => c.beat).ToList();
List<float> tempoChanges = Beatmap.tempoChanges.Select(c => c.beat).ToList();
if (currentTempoEvent < Beatmap.tempoChanges.Count && currentTempoEvent >= 0)
{
if (Conductor.instance.songPositionInBeatsAsDouble >= tempoChanges[currentTempoEvent])
if (cond.songPositionInBeatsAsDouble >= tempoChanges[currentTempoEvent])
{
Conductor.instance.SetBpm(Beatmap.tempoChanges[currentTempoEvent].tempo);
cond.SetBpm(Beatmap.tempoChanges[currentTempoEvent].tempo);
currentTempoEvent++;
}
}
@ -318,9 +343,9 @@ namespace HeavenStudio
List<float> volumeChanges = Beatmap.volumeChanges.Select(c => c.beat).ToList();
if (currentVolumeEvent < Beatmap.volumeChanges.Count && currentVolumeEvent >= 0)
{
if (Conductor.instance.songPositionInBeatsAsDouble >= volumeChanges[currentVolumeEvent])
if (cond.songPositionInBeatsAsDouble >= volumeChanges[currentVolumeEvent])
{
Conductor.instance.SetVolume(Beatmap.volumeChanges[currentVolumeEvent].volume);
cond.SetVolume(Beatmap.volumeChanges[currentVolumeEvent].volume);
currentVolumeEvent++;
}
}
@ -328,7 +353,7 @@ namespace HeavenStudio
List<float> chartSections = Beatmap.beatmapSections.Select(c => c.beat).ToList();
if (currentSectionEvent < Beatmap.beatmapSections.Count && currentSectionEvent >= 0)
{
if (Conductor.instance.songPositionInBeatsAsDouble >= chartSections[currentSectionEvent])
if (cond.songPositionInBeatsAsDouble >= chartSections[currentSectionEvent])
{
Debug.Log("Section " + Beatmap.beatmapSections[currentSectionEvent].sectionName + " started");
currentSection = Beatmap.beatmapSections[currentSectionEvent];
@ -343,13 +368,13 @@ namespace HeavenStudio
float seekTime = 8f;
//seek ahead to preload games that have assetbundles
SeekAheadAndPreload(Conductor.instance.songPositionInBeatsAsDouble, seekTime);
SeekAheadAndPreload(cond.songPositionInBeatsAsDouble, seekTime);
SeekAheadAndDoPreEvent(Conductor.instance.songPositionInBeatsAsDouble);
if (currentEvent < Beatmap.entities.Count && currentEvent >= 0)
{
if (Conductor.instance.songPositionInBeatsAsDouble >= entities[currentEvent])
if (cond.songPositionInBeatsAsDouble >= entities[currentEvent])
{
// allows for multiple events on the same beat to be executed on the same frame, so no more 1-frame delay
var entitiesAtSameBeat = Beatmap.entities.FindAll(c => c.beat == Beatmap.entities[currentEvent].beat && !EventCaller.FXOnlyGames().Contains(EventCaller.instance.GetMinigame(c.datamodel.Split('/')[0])));
@ -380,10 +405,26 @@ namespace HeavenStudio
// Thank you to @shshwdr for bring this to my attention
currentEvent++;
}
// currentEvent += gameManagerEntities.Count;
}
}
if (currentSection == null)
{
sectionProgress = 0;
}
else
{
float currectSectionStart = (float)cond.GetSongPosFromBeat(currentSection.beat);
if (nextSection == null)
sectionProgress = (cond.songPosition - currectSectionStart) / ((float)cond.GetSongPosFromBeat(endBeat) - currectSectionStart);
else
sectionProgress = (cond.songPosition - currectSectionStart) / ((float)cond.GetSongPosFromBeat(nextSection.beat) - currectSectionStart);
}
}
private void LateUpdate() {
OverlaysManager.instance.TogleOverlaysVisibility(Editor.Editor.instance == null || Editor.Editor.instance.fullscreen || ((PersistentDataManager.gameSettings.overlaysInEditor) && (!Editor.Editor.instance.fullscreen)) || HeavenStudio.Editor.GameSettings.InPreview);
}
public void ToggleInputs(bool inputs)
@ -402,6 +443,15 @@ namespace HeavenStudio
totalInputs = 0;
totalPlayerAccuracy = 0;
TimingAccuracyDisplay.instance.ResetArrow();
SkillStarManager.instance.Reset();
skillStarCollected = false;
GoForAPerfect.instance.perfect = true;
GoForAPerfect.instance.Disable();
SectionMedalsManager.instance.Reset();
StartCoroutine(PlayCo(beat));
onBeatChanged?.Invoke(beat);
}
@ -441,6 +491,12 @@ namespace HeavenStudio
onBeatChanged?.Invoke(beat);
KillAllSounds();
// I feel like I should standardize the names
SkillStarManager.instance.KillStar();
TimingAccuracyDisplay.instance.StopStarFlash();
GoForAPerfect.instance.Disable();
SectionMedalsManager.instance.OnRemixEnd();
Debug.Log($"Average input offset for playthrough: {averageInputOffset}ms");
Debug.Log($"Accuracy for playthrough: {(PlayerAccuracy * 100) : 0.00}");

View File

@ -3,12 +3,13 @@ using System.Collections.Generic;
using UnityEngine;
using HeavenStudio.Util;
using HeavenStudio.Common;
namespace HeavenStudio.Games
{
public class Minigame : MonoBehaviour
{
public static float earlyTime = 0.07f, perfectTime = 0.04f, aceEarlyTime = 0.01f, aceLateTime = 0.01f, lateTime = 0.04f, endTime = 0.07f;
public static double earlyTime = 0.07f, perfectTime = 0.04f, aceEarlyTime = 0.01f, aceLateTime = 0.01f, lateTime = 0.04f, endTime = 0.07f;
public static float rankHiThreshold = 0.8f, rankOkThreshold = 0.6f;
[SerializeField] public SoundSequence.SequenceKeyValue[] SoundSequences;
@ -150,37 +151,38 @@ namespace HeavenStudio.Games
}
// now should fix the fast bpm problem
public static float EarlyTime()
public static double EarlyTime()
{
return 1f - ScaleTimingMargin(earlyTime);
return 1f - earlyTime;
}
public static float PerfectTime()
public static double PerfectTime()
{
return 1f - ScaleTimingMargin(perfectTime);
return 1f - perfectTime;
}
public static float LateTime()
public static double LateTime()
{
return 1f + ScaleTimingMargin(lateTime);
return 1f + lateTime;
}
public static float EndTime()
public static double EndTime()
{
return 1f + ScaleTimingMargin(endTime);
return 1f + endTime;
}
public static float AceStartTime()
public static double AceStartTime()
{
return 1f - ScaleTimingMargin(aceEarlyTime);
return 1f - aceEarlyTime;
}
public static float AceEndTime()
public static double AceEndTime()
{
return 1f + ScaleTimingMargin(aceLateTime);
return 1f + aceLateTime;
}
//scales timing windows to the BPM in an ""intelligent"" manner
// DEPRECATED: scales timing windows to the BPM in an ""intelligent"" manner
// only left for historical reasons
static float ScaleTimingMargin(float f)
{
float bpm = Conductor.instance.songBpm * Conductor.instance.musicSource.pitch;
@ -253,7 +255,12 @@ namespace HeavenStudio.Games
public void ScoreMiss(double weight = 1f)
{
GameManager.instance.ScoreInputAccuracy(0, true, weight);
GameManager.instance.ScoreInputAccuracy(0, true, EndTime(), weight, false);
if (weight > 0)
{
GoForAPerfect.instance.Miss();
SectionMedalsManager.instance.MakeIneligible();
}
}
private void OnDestroy() {
@ -262,5 +269,10 @@ namespace HeavenStudio.Games
evt.Disable();
}
}
private void OnDrawGizmos() {
Gizmos.color = Color.magenta;
Gizmos.DrawWireCube(Vector3.zero, new Vector3(17.77695f, 10, 0));
}
}
}

View File

@ -6,6 +6,8 @@ using DG.Tweening;
using HeavenStudio.Util;
using Starpelly;
using HeavenStudio.Common;
namespace HeavenStudio.Games
{
@ -62,6 +64,7 @@ namespace HeavenStudio.Games
if (noAutoplay && autoplayOnly) autoplayOnly = false;
if (noAutoplay && triggersAutoplay){ triggersAutoplay = false; }
if (!enabled) return;
double normalizedTime = GetNormalizedTime();
double stateProg = ((normalizedTime - Minigame.PerfectTime()) / (Minigame.LateTime() - Minigame.PerfectTime()) - 0.5f) * 2;
@ -161,9 +164,20 @@ namespace HeavenStudio.Games
GameManager.instance.AvgInputOffset = offset;
OnHit(this, (float) state);
if (countsForAccuracy && !(noAutoplay || autoplayOnly) && isEligible)
GameManager.instance.ScoreInputAccuracy(TimeToAccuracy(time), time > 1.0, 1.0);
CleanUp();
if (countsForAccuracy && !(noAutoplay || autoplayOnly) && isEligible)
{
GameManager.instance.ScoreInputAccuracy(TimeToAccuracy(time), time > 1.0, time);
if (state >= 1f || state <= -1f)
{
GoForAPerfect.instance.Miss();
SectionMedalsManager.instance.MakeIneligible();
}
else
{
GoForAPerfect.instance.Hit();
}
}
} else
{
Blank();
@ -176,7 +190,6 @@ namespace HeavenStudio.Games
if (time >= Minigame.AceStartTime() && time <= Minigame.AceEndTime())
{
// Ace
Debug.Log("Accuracy (Ace): " + 1.0);
return 1.0;
}
@ -190,7 +203,6 @@ namespace HeavenStudio.Games
state = 1.0 - ((time - Minigame.AceEndTime()) / (Minigame.LateTime() - Minigame.AceEndTime()));
state *= 1.0 - Minigame.rankHiThreshold;
state += Minigame.rankHiThreshold;
Debug.Log("Accuracy (Late): " + state);
}
else
{
@ -198,7 +210,6 @@ namespace HeavenStudio.Games
state = ((time - Minigame.PerfectTime()) / (Minigame.AceStartTime() - Minigame.PerfectTime()));
state *= 1.0 - Minigame.rankHiThreshold;
state += Minigame.rankHiThreshold;
Debug.Log("Accuracy (Early): " + state);
}
}
else
@ -208,14 +219,12 @@ namespace HeavenStudio.Games
// late half of timing window
state = 1.0 - ((time - Minigame.LateTime()) / (Minigame.EndTime() - Minigame.LateTime()));
state *= Minigame.rankOkThreshold;
Debug.Log("Accuracy (Late NG): " + state);
}
else
{
//early half of timing window
state = ((time - Minigame.PerfectTime()) / (Minigame.AceStartTime() - Minigame.PerfectTime()));
state *= Minigame.rankOkThreshold;
Debug.Log("Accuracy (Early NG): " + state);
}
}
return state;
@ -230,7 +239,11 @@ namespace HeavenStudio.Games
CleanUp();
if (countsForAccuracy && !(noAutoplay || autoplayOnly))
GameManager.instance.ScoreInputAccuracy(0, true, 1.0);
{
GameManager.instance.ScoreInputAccuracy(0, true, 2.0, 1.0, false);
GoForAPerfect.instance.Miss();
SectionMedalsManager.instance.MakeIneligible();
}
}
public void Blank()

View File

@ -2,6 +2,7 @@ using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using HeavenStudio.Editor;
using HeavenStudio.Editor.Track;
namespace HeavenStudio.Games
@ -15,7 +16,7 @@ namespace HeavenStudio.Games
//the variables below seem to be mostly unused (they are never used in any meaningful way)
public int aceTimes; //always set to 0 no matter what (also, the one time it's used doesn't seem to make sense)
public bool isEligible; //value never used for anything
public bool isEligible = true;
private bool autoPlayEnabledOnStart; //value never used for anything
public bool triggersAutoplay = true;
@ -147,7 +148,7 @@ namespace HeavenStudio.Games
private void AceVisuals()
{
if (Timeline.instance != null)
if (Timeline.instance != null && Editor.Editor.instance != null && !Editor.Editor.instance.fullscreen)
{
Timeline.instance.AutoplayBTN.GetComponent<Animator>().Play("Ace", 0, 0);
}
@ -165,4 +166,4 @@ namespace HeavenStudio.Games
MakeInEligible();
}
}
}
}

View File

@ -33,6 +33,20 @@ namespace HeavenStudio
public static int ScreenSizeIndex = 0;
public static float MasterVolume = 0.8f;
public static int currentDspSize = 256;
public static int currentSampleRate = 44100;
public static readonly int[] DSP_BUFFER_SIZES =
{
128, 256, 340, 480, 512, 1024
};
public static readonly int[] SAMPLE_RATES =
{
22050, 44100, 48000, 88200, 96000,
};
public static RenderTexture GameRenderTexture;
public static RenderTexture OverlayRenderTexture;
public enum Scenes : int
{
@ -57,6 +71,16 @@ namespace HeavenStudio
CustomScreenHeight = PersistentDataManager.gameSettings.resolutionHeight;
ChangeMasterVolume(PersistentDataManager.gameSettings.masterVolume);
if (PersistentDataManager.gameSettings.dspSize == 0)
PersistentDataManager.gameSettings.dspSize = 256;
if (PersistentDataManager.gameSettings.sampleRate == 0)
PersistentDataManager.gameSettings.sampleRate = 44100;
currentDspSize = PersistentDataManager.gameSettings.dspSize;
currentSampleRate = PersistentDataManager.gameSettings.sampleRate;
ChangeAudioSettings(currentDspSize, currentSampleRate);
if (PersistentDataManager.gameSettings.isFullscreen)
{
Screen.SetResolution(Display.main.systemWidth, Display.main.systemHeight, FullScreenMode.FullScreenWindow);
@ -167,15 +191,39 @@ namespace HeavenStudio
}
}
public static void ResetGameRenderTexture()
{
GameRenderTexture.width = Screen.width;
GameRenderTexture.height = Screen.height;
OverlayRenderTexture.width = Screen.width;
OverlayRenderTexture.height = Screen.height;
}
public static void ChangeMasterVolume(float value)
{
MasterVolume = value;
AudioListener.volume = MasterVolume;
}
public static void ChangeAudioSettings(int dspSize, int sampleRate)
{
// don't reset audio if no changes are done
AudioConfiguration config = AudioSettings.GetConfiguration();
if (dspSize == config.dspBufferSize && sampleRate == config.sampleRate) return;
currentDspSize = dspSize;
currentSampleRate = sampleRate;
config.dspBufferSize = currentDspSize;
config.sampleRate = currentSampleRate;
AudioSettings.Reset(config);
PersistentDataManager.gameSettings.dspSize = currentDspSize;
PersistentDataManager.gameSettings.sampleRate = currentSampleRate;
}
void OnApplicationQuit()
{
PersistentDataManager.SaveSettings();
Debug.Log("Disconnecting JoyShocks...");
PlayerInput.DisconnectJoyshocks();
}

View File

@ -1,70 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using HeavenStudio.Util;
namespace HeavenStudio
{
public class GoForAPerfect : MonoBehaviour
{
public static GoForAPerfect instance { get; set; }
private Animator pAnim;
private bool active = false;
public bool perfect;
private void Awake()
{
instance = this;
}
private void Start()
{
pAnim = transform.GetChild(0).GetChild(0).GetComponent<Animator>();
perfect = true;
}
public void Hit()
{
if (!active) return;
pAnim.Play("PerfectIcon_Hit", 0, 0);
}
public void Miss()
{
if (!active) return;
perfect = false;
GameProfiler.instance.perfect = false;
transform.GetChild(0).GetChild(1).gameObject.SetActive(false);
this.GetComponent<Animator>().Play("GoForAPerfect_Miss");
Jukebox.PlayOneShot("perfectMiss");
}
public void Enable()
{
SetActive();
transform.GetChild(0).gameObject.SetActive(true);
}
public void Disable()
{
SetInactive();
transform.GetChild(0).gameObject.SetActive(false);
}
public void SetActive()
{
active = true;
}
public void SetInactive()
{
active = false;
}
}
}

View File

@ -13,6 +13,9 @@ namespace HeavenStudio
{
public class Initializer : MonoBehaviour
{
[SerializeField] RenderTexture gameRenderTexture;
[SerializeField] RenderTexture overlayRenderTexture;
public TextAsset level;
public AudioClip music;
public GameObject canvas;
@ -50,7 +53,9 @@ namespace HeavenStudio
GameObject MainCamera = Cameras.transform.GetChild(0).gameObject;
GameObject CursorCamera = Cameras.transform.GetChild(1).gameObject;
GameObject OverlayCamera = Cameras.transform.GetChild(2).gameObject;
GameObject GameLetterbox = Cameras.transform.GetChild(3).gameObject;
GameObject StaticCamera = Cameras.transform.GetChild(3).gameObject;
GameObject GameLetterbox = Cameras.transform.GetChild(4).gameObject;
GameObject Cursor = Instantiate(Resources.Load<GameObject>("Prefabs/Cursor"));
Cursor.name = "Cursor";
@ -68,6 +73,7 @@ namespace HeavenStudio
gameManager.GameCamera = MainCamera.GetComponent<Camera>();
gameManager.CursorCam = CursorCamera.GetComponent<Camera>();
gameManager.OverlayCamera = OverlayCamera.GetComponent<Camera>();
gameManager.StaticCamera = StaticCamera.GetComponent<Camera>();
gameManager.GameLetterbox = GameLetterbox;
GameObject Profiler = Instantiate(Resources.Load<GameObject>("Prefabs/GameProfiler"));
@ -87,6 +93,10 @@ namespace HeavenStudio
source.outputAudioMixerGroup = Settings.GetMusicMixer();
// Conductor.AddComponent<AudioDspTimeKeeper>();
GlobalGameManager.GameRenderTexture = gameRenderTexture;
GlobalGameManager.OverlayRenderTexture = overlayRenderTexture;
GlobalGameManager.ResetGameRenderTexture();
if (editor)
{
this.GetComponent<HeavenStudio.Editor.Editor>().Init();

View File

@ -94,9 +94,8 @@ namespace HeavenStudio.Editor
public void Init()
{
GameCamera.instance.camera.targetTexture = ScreenRenderTexture;
GameManager.instance.StaticCamera.targetTexture = ScreenRenderTexture;
GameManager.instance.CursorCam.targetTexture = ScreenRenderTexture;
GameManager.instance.OverlayCamera.targetTexture = ScreenRenderTexture;
GameLetterbox = GameManager.instance.GameLetterbox;
Screen.texture = ScreenRenderTexture;
@ -501,9 +500,8 @@ namespace HeavenStudio.Editor
MainCanvas.enabled = false;
EditorCamera.enabled = false;
GameCamera.instance.camera.targetTexture = null;
GameManager.instance.StaticCamera.targetTexture = null;
GameManager.instance.CursorCam.enabled = false;
GameManager.instance.OverlayCamera.targetTexture = null;
fullscreen = true;
}
@ -514,9 +512,8 @@ namespace HeavenStudio.Editor
MainCanvas.enabled = true;
EditorCamera.enabled = true;
GameCamera.instance.camera.targetTexture = ScreenRenderTexture;
GameManager.instance.StaticCamera.targetTexture = ScreenRenderTexture;
GameManager.instance.CursorCam.enabled = true && isCursorEnabled;
GameManager.instance.OverlayCamera.targetTexture = ScreenRenderTexture;
fullscreen = false;
GameCamera.instance.camera.rect = new Rect(0, 0, 1, 1);

View File

@ -1,3 +1,4 @@
using System.Linq;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
@ -16,6 +17,8 @@ namespace HeavenStudio.Editor
public Slider volSlider;
public TMP_InputField volLabel;
public TMP_Dropdown dspSizeDropdown;
public TMP_Dropdown sampleRateDropdown;
private void Start() {
List<TMP_Dropdown.OptionData> dropDownData = new List<TMP_Dropdown.OptionData>();
@ -53,16 +56,30 @@ namespace HeavenStudio.Editor
volSlider.value = GlobalGameManager.MasterVolume;
volLabel.text = System.Math.Round(volSlider.value * 100, 2).ToString();
dspSizeDropdown.onValueChanged.AddListener(delegate
{
GlobalGameManager.currentDspSize = GlobalGameManager.DSP_BUFFER_SIZES[dspSizeDropdown.value];
GlobalGameManager.ChangeAudioSettings(GlobalGameManager.currentDspSize, GlobalGameManager.currentSampleRate);
});
sampleRateDropdown.onValueChanged.AddListener(delegate
{
GlobalGameManager.currentSampleRate = GlobalGameManager.SAMPLE_RATES[sampleRateDropdown.value];
GlobalGameManager.ChangeAudioSettings(GlobalGameManager.currentDspSize, GlobalGameManager.currentSampleRate);
});
}
public void WindowFullScreen()
{
GlobalGameManager.WindowFullScreen();
GlobalGameManager.ResetGameRenderTexture();
}
public void WindowConfirmSize()
{
GlobalGameManager.ChangeScreenSize();
GlobalGameManager.ResetGameRenderTexture();
}
public void OnVolSliderChanged()
@ -88,6 +105,13 @@ namespace HeavenStudio.Editor
volSlider.value = GlobalGameManager.MasterVolume;
volLabel.text = System.Math.Round(volSlider.value * 100, 2).ToString();
dspSizeDropdown.ClearOptions();
sampleRateDropdown.ClearOptions();
dspSizeDropdown.AddOptions(GlobalGameManager.DSP_BUFFER_SIZES.Select(x => x.ToString()).ToList());
sampleRateDropdown.AddOptions(GlobalGameManager.SAMPLE_RATES.Select(x => x.ToString()).ToList());
dspSizeDropdown.value = GlobalGameManager.DSP_BUFFER_SIZES.ToList().IndexOf(GlobalGameManager.currentDspSize);
sampleRateDropdown.value = GlobalGameManager.SAMPLE_RATES.ToList().IndexOf(GlobalGameManager.currentSampleRate);
}
public override void OnCloseTab()

View File

@ -0,0 +1,273 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using HeavenStudio.Common;
namespace HeavenStudio.Editor
{
public class GameSettings : TabsContent
{
public static bool InPreview;
[SerializeField] Toggle editorOverlaysToggle;
[SerializeField] Toggle perfectChallengeToggle;
[SerializeField] Toggle sectionMedalsToggle;
[SerializeField] Toggle timingDispMinModeToggle;
[Header("Layout Settings - Header")]
[SerializeField] TMP_Text ElementNameText;
[Header("Layout Settings - General")]
[SerializeField] Toggle ElementToggle;
[SerializeField] TMP_InputField XPosInput;
[SerializeField] TMP_InputField YPosInput;
[SerializeField] Slider XPosSlider;
[SerializeField] Slider YPosSlider;
[SerializeField] TMP_InputField RotationInput;
[SerializeField] Slider RotationSlider;
[SerializeField] TMP_InputField ScaleInput;
[SerializeField] Slider ScaleSlider;
[Header("Layout Settings - Timing Display")]
[SerializeField] GameObject TimingDispTypeContainer;
[SerializeField] TMP_Dropdown TimingDispTypeDropdown;
List<OverlaysManager.OverlayOption> lytElements = new List<OverlaysManager.OverlayOption>();
static int currentElementIdx = 0;
const string fFormat = "0.000";
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
void CreateDefaultLayout()
{
PersistentDataManager.gameSettings.timingDisplayComponents = new List<OverlaysManager.TimingDisplayComponent>()
{
OverlaysManager.TimingDisplayComponent.CreateDefaultDual()
};
PersistentDataManager.gameSettings.skillStarComponents = new List<OverlaysManager.SkillStarComponent>()
{
OverlaysManager.SkillStarComponent.CreateDefault()
};
PersistentDataManager.gameSettings.sectionComponents = new List<OverlaysManager.SectionComponent>()
{
OverlaysManager.SectionComponent.CreateDefault()
};
PersistentDataManager.SaveSettings();
}
public void OnEditorOverlaysToggleChanged()
{
PersistentDataManager.gameSettings.overlaysInEditor = editorOverlaysToggle.isOn;
}
public void OnPerfectChallengeToggleChanged()
{
PersistentDataManager.gameSettings.perfectChallengeType = perfectChallengeToggle.isOn ? PersistentDataManager.PerfectChallengeType.On : PersistentDataManager.PerfectChallengeType.Off;
}
public void OnSectionMedalsToggleChanged()
{
PersistentDataManager.gameSettings.isMedalOn = sectionMedalsToggle.isOn;
}
public void OnTimingDispMinModeToggleChanged()
{
PersistentDataManager.gameSettings.timingDisplayMinMode = timingDispMinModeToggle.isOn;
}
public override void OnOpenTab()
{
TimingDispTypeDropdown.ClearOptions();
TimingDispTypeDropdown.AddOptions(Enum.GetNames(typeof(OverlaysManager.TimingDisplayComponent.TimingDisplayType)).ToList());
editorOverlaysToggle.isOn = PersistentDataManager.gameSettings.overlaysInEditor;
perfectChallengeToggle.isOn = PersistentDataManager.gameSettings.perfectChallengeType != PersistentDataManager.PerfectChallengeType.Off;
sectionMedalsToggle.isOn = PersistentDataManager.gameSettings.isMedalOn;
timingDispMinModeToggle.isOn = PersistentDataManager.gameSettings.timingDisplayMinMode;
if (PersistentDataManager.gameSettings.timingDisplayComponents.Count == 0 &&
PersistentDataManager.gameSettings.skillStarComponents.Count == 0 &&
PersistentDataManager.gameSettings.sectionComponents.Count == 0)
{
CreateDefaultLayout();
}
lytElements = new List<OverlaysManager.OverlayOption>();
foreach (var c in PersistentDataManager.gameSettings.timingDisplayComponents) { lytElements.Add(c); c.EnablePreview();}
foreach (var c in PersistentDataManager.gameSettings.skillStarComponents) { lytElements.Add(c); c.EnablePreview();}
foreach (var c in PersistentDataManager.gameSettings.sectionComponents) { lytElements.Add(c); c.EnablePreview();}
UpdateLayoutSettings();
InPreview = true;
}
public override void OnCloseTab()
{
foreach (var e in lytElements)
{
e.DisablePreview();
}
lytElements.Clear();
InPreview = false;
}
void UpdateLayoutSettings()
{
var element = lytElements[currentElementIdx];
element.EnablePreview();
ElementToggle.isOn = element.enable;
XPosInput.text = element.position.x.ToString(fFormat);
YPosInput.text = element.position.y.ToString(fFormat);
XPosSlider.value = element.position.x;
YPosSlider.value = element.position.y;
RotationInput.text = element.rotation.ToString(fFormat);
RotationSlider.value = element.rotation;
ScaleInput.text = element.scale.ToString(fFormat);
ScaleSlider.value = element.scale;
if (element is OverlaysManager.TimingDisplayComponent)
{
TimingDispTypeContainer.SetActive(true);
TimingDispTypeDropdown.value = (int)(element as OverlaysManager.TimingDisplayComponent).tdType;
ElementNameText.text = "Timing Display";
}
else
{
TimingDispTypeContainer.SetActive(false);
}
if (element is OverlaysManager.SkillStarComponent)
{
ElementNameText.text = "Skill Star";
}
if (element is OverlaysManager.SectionComponent)
{
ElementNameText.text = "Section Progress";
}
}
public void OnNextElementButtonClicked()
{
currentElementIdx = (currentElementIdx + 1) % lytElements.Count;
UpdateLayoutSettings();
}
public void OnPrevElementButtonClicked()
{
currentElementIdx = (currentElementIdx - 1 + lytElements.Count) % lytElements.Count;
UpdateLayoutSettings();
}
public void OnElementToggled()
{
var element = lytElements[currentElementIdx];
element.enable = ElementToggle.isOn;
element.PositionElement();
}
public void OnXPosInputChanged()
{
var element = lytElements[currentElementIdx];
XPosSlider.value = float.Parse(XPosInput.text);
element.position.x = XPosSlider.value;
element.PositionElement();
}
public void OnXPosSliderChanged()
{
var element = lytElements[currentElementIdx];
XPosInput.text = XPosSlider.value.ToString(fFormat);
element.position.x = XPosSlider.value;
element.PositionElement();
}
public void OnYPosInputChanged()
{
var element = lytElements[currentElementIdx];
YPosSlider.value = float.Parse(YPosInput.text);
element.position.y = YPosSlider.value;
element.PositionElement();
}
public void OnYPosSliderChanged()
{
var element = lytElements[currentElementIdx];
YPosInput.text = YPosSlider.value.ToString(fFormat);
element.position.y = YPosSlider.value;
element.PositionElement();
}
public void OnRotationInputChanged()
{
var element = lytElements[currentElementIdx];
RotationSlider.value = float.Parse(RotationInput.text);
element.rotation = RotationSlider.value;
element.PositionElement();
}
public void OnRotationSliderChanged()
{
var element = lytElements[currentElementIdx];
RotationInput.text = RotationSlider.value.ToString(fFormat);
element.rotation = RotationSlider.value;
element.PositionElement();
}
public void OnScaleInputChanged()
{
var element = lytElements[currentElementIdx];
ScaleSlider.value = float.Parse(ScaleInput.text);
element.scale = ScaleSlider.value;
element.PositionElement();
}
public void OnScaleSliderChanged()
{
var element = lytElements[currentElementIdx];
ScaleInput.text = ScaleSlider.value.ToString(fFormat);
element.scale = ScaleSlider.value;
element.PositionElement();
}
public void OnTimingDispTypeDropdownChanged()
{
var element = lytElements[currentElementIdx] as OverlaysManager.TimingDisplayComponent;
if (element == null) return;
element.tdType = (OverlaysManager.TimingDisplayComponent.TimingDisplayType)TimingDispTypeDropdown.value;
bool elHide = element.enable;
switch (element.tdType)
{
case OverlaysManager.TimingDisplayComponent.TimingDisplayType.Dual:
element.position = new Vector2(-0.84f, 0);
element.rotation = 0f;
break;
default:
element.position = new Vector2(0, -0.8f);
element.rotation = 90f;
break;
}
element.scale = 1f;
element.enable = elHide;
element.PositionElement();
UpdateLayoutSettings();
}
}
}

View File

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

View File

@ -1,6 +1,7 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using HeavenStudio;
using HeavenStudio.Editor;
@ -11,6 +12,7 @@ public class SectionDialog : Dialog
{
SectionTimelineObj sectionObj;
[SerializeField] TMP_InputField sectionName;
[SerializeField] Toggle challengeEnable;
public void SwitchSectionDialog()
{
@ -29,6 +31,7 @@ public class SectionDialog : Dialog
{
this.sectionObj = sectionObj;
sectionName.text = sectionObj.chartSection.sectionName;
challengeEnable.isOn = sectionObj.chartSection.startPerfect;
}
public void DeleteSection()
@ -48,4 +51,10 @@ public class SectionDialog : Dialog
sectionObj.chartSection.sectionName = name;
sectionObj.UpdateLabel();
}
public void SetSectionChallenge()
{
if (sectionObj == null) return;
sectionObj.chartSection.startPerfect = challengeEnable.isOn;
}
}

View File

@ -132,10 +132,6 @@ namespace HeavenStudio.Editor.Track
[SerializeField] private RectTransform TimelineEventObjRef;
[SerializeField] private RectTransform LayersRect;
[SerializeField] private GameObject TimelineSectionDisplay;
[SerializeField] private TMP_Text TimelineSectionText;
[SerializeField] private Slider TimelineSectionProgress;
[Header("Timeline Inputs")]
public TMP_InputField FirstBeatOffset;
public TMP_InputField StartingTempoSpecialAll;
@ -307,7 +303,6 @@ namespace HeavenStudio.Editor.Track
timelineState.SetState(CurrentTimelineState.State.Selection);
AutoBtnUpdate();
GameManager.instance.onSectionChange += OnSectionChange;
}
public void FitToSong()
@ -387,7 +382,6 @@ namespace HeavenStudio.Editor.Track
SongBeat.text = $"Beat {string.Format("{0:0.000}", Conductor.instance.songPositionInBeats)}";
SongPos.text = FormatTime(Conductor.instance.songPosition);
}
TimelineSectionProgress.value = GameManager.instance.sectionProgress;
SliderControl();
@ -993,21 +987,6 @@ namespace HeavenStudio.Editor.Track
UpdateStartingVolText();
}
public void OnSectionChange(DynamicBeatmap.ChartSection section)
{
if (section == null)
{
TimelineSectionDisplay.SetActive(false);
}
else
{
if (!TimelineSectionDisplay.activeSelf)
TimelineSectionDisplay.SetActive(true);
TimelineSectionText.text = section.sectionName;
TimelineSectionProgress.value = GameManager.instance.sectionProgress;
}
}
#endregion
#region Commands

View File

@ -268,7 +268,19 @@ namespace HeavenStudio
GameManager.instance.Stop(0);
}
),
new GameAction("skill star", "Skill Star", 1f, true),
new GameAction("skill star", "Skill Star", 1f, true)
{
//temp for testing
function = delegate {
var e = eventCaller.currentEntity;
HeavenStudio.Common.SkillStarManager.instance.DoStarIn(e.beat, e.length);
// BeatAction.New(HeavenStudio.Common.SkillStarManager.instance.gameObject, new List<BeatAction.Action>(){
// new BeatAction.Action(e.beat + e.length, delegate {
// HeavenStudio.Common.SkillStarManager.instance.DoStarJust();
// })
// });
}
},
new GameAction("toggle inputs", "Toggle Inputs", 0.5f, true,
new List<Param>()
{
@ -391,20 +403,44 @@ namespace HeavenStudio
),
new GameAction("move camera", "Move Camera", 1f, true, new List<Param>()
{
new Param("valA", new EntityTypes.Float(-50, 50, 0), "Right / Left"),
new Param("valB", new EntityTypes.Float(-50, 50, 0), "Up / Down"),
new Param("valC", new EntityTypes.Float(-0, 250, 10), "In / Out"),
new Param("ease", EasingFunction.Ease.Linear, "Ease Type")
new Param("valA", new EntityTypes.Float(-50, 50, 0), "Right / Left", "Next position on the X axis"),
new Param("valB", new EntityTypes.Float(-50, 50, 0), "Up / Down", "Next position on the Y axis"),
new Param("valC", new EntityTypes.Float(-0, 250, 10), "In / Out", "Next position on the Z axis"),
new Param("ease", EasingFunction.Ease.Linear, "Ease Type"),
new Param("axis", GameCamera.CameraAxis.All, "Axis", "The axis to move the camera on" )
}
),
new GameAction("rotate camera", "Rotate Camera", 1f, true, new List<Param>()
{
new Param("valA", new EntityTypes.Integer(-360, 360, 0), "Pitch"),
new Param("valB", new EntityTypes.Integer(-360, 360, 0), "Yaw"),
new Param("valC", new EntityTypes.Integer(-360, 360, 0), "Roll"),
new Param("ease", EasingFunction.Ease.Linear, "Ease Type")
new Param("valA", new EntityTypes.Integer(-360, 360, 0), "Pitch", "Next rotation on the X axis"),
new Param("valB", new EntityTypes.Integer(-360, 360, 0), "Yaw", "Next rotation on the Y axis"),
new Param("valC", new EntityTypes.Integer(-360, 360, 0), "Roll", "Next rotation on the Z axis"),
new Param("ease", EasingFunction.Ease.Linear, "Ease Type"),
new Param("axis", GameCamera.CameraAxis.All, "Axis", "The axis to move the camera on" )
}
),
new GameAction("pan view", "Pan Viewport", 1f, true, new List<Param>()
{
new Param("valA", new EntityTypes.Float(-50, 50, 0), "Right / Left", "Next position on the X axis"),
new Param("valB", new EntityTypes.Float(-50, 50, 0), "Up / Down", "Next position on the Y axis"),
new Param("ease", EasingFunction.Ease.Linear, "Ease Type"),
new Param("axis", StaticCamera.ViewAxis.All, "Axis", "The axis to pan the viewport in" )
}
),
new GameAction("rotate view", "Rotate Viewport", 1f, true, new List<Param>()
{
new Param("valA", new EntityTypes.Float(-360, 360, 0), "Rotation", "Next viewport rotation"),
new Param("ease", EasingFunction.Ease.Linear, "Ease Type"),
}
),
new GameAction("scale view", "Scale Viewport", 1f, true, new List<Param>()
{
new Param("valA", new EntityTypes.Float(0, 50, 1), "Width", "Next viewport width"),
new Param("valB", new EntityTypes.Float(0, 50, 1), "Height", "Next viewport height"),
new Param("ease", EasingFunction.Ease.Linear, "Ease Type"),
new Param("axis", StaticCamera.ViewAxis.All, "Axis", "The axis to scale the viewport in" )
}
),
new GameAction("screen shake", "Screen Shake", 1f, true,
new List<Param>()

View File

@ -10,28 +10,52 @@ namespace HeavenStudio.Common
{
public static class PersistentDataManager
{
public enum PerfectChallengeType
{
Off, // no perfect challenge
Arcade, // "arcade rule"
Legacy, // "legacy rule"
On // "megamix rule"
}
[NonSerialized] public static GameSettings gameSettings;
public static void CreateDefaultSettings()
{
gameSettings = new GameSettings();
gameSettings.isFullscreen = false;
gameSettings.resolutionIndex = 0;
gameSettings.resolutionWidth = GlobalGameManager.DEFAULT_SCREEN_SIZES[gameSettings.resolutionIndex].width;
gameSettings.resolutionHeight = GlobalGameManager.DEFAULT_SCREEN_SIZES[gameSettings.resolutionIndex].height;
gameSettings.masterVolume = 0.8f;
gameSettings.editorCursorEnable = true;
gameSettings = new GameSettings(
false,
1,
GlobalGameManager.DEFAULT_SCREEN_SIZES[1].width,
GlobalGameManager.DEFAULT_SCREEN_SIZES[1].height,
0.8f,
256,
44100,
true,
true,
PerfectChallengeType.On,
true,
false
);
// disable if platform is mac
if (Application.platform == RuntimePlatform.OSXPlayer || Application.platform == RuntimePlatform.OSXEditor)
gameSettings.discordRPCEnable = false;
else
gameSettings.discordRPCEnable = true;
gameSettings.timingDisplayComponents = new List<OverlaysManager.TimingDisplayComponent>()
{
OverlaysManager.TimingDisplayComponent.CreateDefaultDual()
};
gameSettings.skillStarComponents = new List<OverlaysManager.SkillStarComponent>()
{
OverlaysManager.SkillStarComponent.CreateDefault()
};
gameSettings.sectionComponents = new List<OverlaysManager.SectionComponent>()
{
OverlaysManager.SectionComponent.CreateDefault()
};
SaveSettings();
}
@ -68,6 +92,58 @@ namespace HeavenStudio.Common
[Serializable]
public struct GameSettings
{
// default settings constructor
public GameSettings(
bool isFullscreen = false,
int resolutionIndex = 0,
int resolutionWidth = 1280,
int resolutionHeight = 720,
float masterVolume = 0.8f,
int dspSize = 256,
int sampleRate = 44100,
bool editorCursorEnable = true,
bool discordRPCEnable = true,
PerfectChallengeType perfectChallengeType = PerfectChallengeType.On,
bool isMedalOn = true,
bool timingDisplayMinMode = false,
bool overlaysInEditor = true
)
{
this.isFullscreen = isFullscreen;
this.resolutionIndex = resolutionIndex;
this.resolutionWidth = resolutionWidth;
this.resolutionHeight = resolutionHeight;
this.masterVolume = masterVolume;
this.dspSize = dspSize;
this.sampleRate = sampleRate;
this.editorCursorEnable = editorCursorEnable;
if (Application.platform == RuntimePlatform.OSXPlayer || Application.platform == RuntimePlatform.OSXEditor)
this.discordRPCEnable = false;
else
this.discordRPCEnable = true;
this.perfectChallengeType = perfectChallengeType;
this.isMedalOn = isMedalOn;
this.timingDisplayMinMode = timingDisplayMinMode;
this.overlaysInEditor = overlaysInEditor;
this.timingDisplayComponents = new List<OverlaysManager.TimingDisplayComponent>()
{
OverlaysManager.TimingDisplayComponent.CreateDefaultDual()
};
this.skillStarComponents = new List<OverlaysManager.SkillStarComponent>()
{
OverlaysManager.SkillStarComponent.CreateDefault()
};
this.sectionComponents = new List<OverlaysManager.SectionComponent>()
{
OverlaysManager.SectionComponent.CreateDefault()
};
}
// Display / Audio Settings
public bool isFullscreen;
@ -76,11 +152,21 @@ namespace HeavenStudio.Common
public int resolutionHeight;
public float masterVolume;
public int dspSize;
public int sampleRate;
// Editor Settings
public bool editorCursorEnable;
public bool discordRPCEnable;
// Gameplay Settings
public PerfectChallengeType perfectChallengeType;
public bool isMedalOn;
public bool timingDisplayMinMode;
public bool overlaysInEditor;
public List<OverlaysManager.TimingDisplayComponent> timingDisplayComponents;
public List<OverlaysManager.SkillStarComponent> skillStarComponents;
public List<OverlaysManager.SectionComponent> sectionComponents;
}
}
}

View File

@ -0,0 +1,201 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using HeavenStudio.Util;
namespace HeavenStudio
{
public class StaticCamera : MonoBehaviour
{
[SerializeField] RectTransform canvas;
[SerializeField] GameObject overlayView;
public static StaticCamera instance { get; private set; }
public new Camera camera;
public enum ViewAxis
{
All,
X,
Y,
}
const float AspectRatioWidth = 1;
const float AspectRatioHeight = 1;
private List<DynamicBeatmap.DynamicEntity> panEvents = new List<DynamicBeatmap.DynamicEntity>();
private List<DynamicBeatmap.DynamicEntity> scaleEvents = new List<DynamicBeatmap.DynamicEntity>();
private List<DynamicBeatmap.DynamicEntity> rotationEvents = new List<DynamicBeatmap.DynamicEntity>();
static Vector3 defaultPan = new Vector3(0, 0, 0);
static Vector3 defaultScale = new Vector3(1, 1, 1);
static float defaultRotation = 0;
private static Vector3 pan;
private static Vector3 scale;
private static float rotation;
private static Vector3 panLast;
private static Vector3 scaleLast;
private static float rotationLast;
private void Awake()
{
instance = this;
camera = this.GetComponent<Camera>();
}
// Start is called before the first frame update
void Start()
{
GameManager.instance.onBeatChanged += OnBeatChanged;
Reset();
panLast = defaultPan;
scaleLast = defaultScale;
rotationLast = defaultRotation;
}
public void OnBeatChanged(float beat)
{
Reset();
panEvents = EventCaller.GetAllInGameManagerList("vfx", new string[] { "pan view" });
scaleEvents = EventCaller.GetAllInGameManagerList("vfx", new string[] { "scale view" });
rotationEvents = EventCaller.GetAllInGameManagerList("vfx", new string[] { "rotate view" });
panLast = defaultPan;
scaleLast = defaultScale;
rotationLast = defaultRotation;
UpdatePan();
UpdateRotation();
UpdateScale();
}
// Update is called once per frame
void Update()
{
UpdatePan();
UpdateRotation();
UpdateScale();
canvas.localPosition = pan;
canvas.eulerAngles = new Vector3(0, 0, rotation);
canvas.localScale = scale;
}
private void UpdatePan()
{
foreach (var e in panEvents)
{
float prog = Conductor.instance.GetPositionFromBeat(e.beat, e.length);
if (prog >= 0f)
{
EasingFunction.Function func = EasingFunction.GetEasingFunction((EasingFunction.Ease) e["ease"]);
switch (e["axis"])
{
case (int) ViewAxis.X:
pan.x = func(panLast.x, e["valA"], Mathf.Min(prog, 1f));
break;
case (int) ViewAxis.Y:
pan.y = func(panLast.y, e["valB"], Mathf.Min(prog, 1f));
break;
default:
float dx = func(panLast.x, e["valA"], Mathf.Min(prog, 1f));
float dy = func(panLast.y, e["valB"], Mathf.Min(prog, 1f));
pan = new Vector3(dx, dy, 0);
break;
}
}
if (prog > 1f)
{
switch (e["axis"])
{
case (int) ViewAxis.X:
panLast.x = e["valA"];
break;
case (int) ViewAxis.Y:
panLast.y = e["valB"];
break;
default:
panLast = new Vector3(e["valA"], e["valB"], 0);
break;
}
}
}
}
private void UpdateRotation()
{
foreach (var e in rotationEvents)
{
float prog = Conductor.instance.GetPositionFromBeat(e.beat, e.length);
if (prog >= 0f)
{
EasingFunction.Function func = EasingFunction.GetEasingFunction((EasingFunction.Ease) e["ease"]);
rotation = func(rotationLast, -e["valA"], Mathf.Min(prog, 1f));
}
if (prog > 1f)
{
rotationLast = -e["valA"];
}
}
}
private void UpdateScale()
{
foreach (var e in scaleEvents)
{
float prog = Conductor.instance.GetPositionFromBeat(e.beat, e.length);
if (prog >= 0f)
{
EasingFunction.Function func = EasingFunction.GetEasingFunction((EasingFunction.Ease) e["ease"]);
switch (e["axis"])
{
case (int) ViewAxis.X:
scale.x = func(scaleLast.x, e["valA"], Mathf.Min(prog, 1f)) * AspectRatioWidth;
break;
case (int) ViewAxis.Y:
scale.y = func(scaleLast.y, e["valB"], Mathf.Min(prog, 1f)) * AspectRatioHeight;
break;
default:
float dx = func(scaleLast.x, e["valA"], Mathf.Min(prog, 1f)) * AspectRatioWidth;
float dy = func(scaleLast.y, e["valB"], Mathf.Min(prog, 1f)) * AspectRatioHeight;
scale = new Vector3(dx, dy, 1);
break;
}
}
if (prog > 1f)
{
switch (e["axis"])
{
case (int) ViewAxis.X:
scaleLast.x = e["valA"] * AspectRatioWidth;
break;
case (int) ViewAxis.Y:
scaleLast.y = e["valB"] * AspectRatioHeight;
break;
default:
scaleLast = new Vector3(e["valA"] * AspectRatioWidth, e["valB"] * AspectRatioHeight, 1);
break;
}
}
}
}
public static void Reset()
{
pan = defaultPan;
scale = defaultScale;
rotation = defaultRotation;
}
public void ToggleOverlayView(bool toggle)
{
overlayView.SetActive(toggle);
}
}
}

View File

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

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 04104d67d26331a4faaa333588f757e3
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View 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);
}
}
}
}
}

View File

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

View 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;
}
}
}

View 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();
}
}
}

View File

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

View 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);
}
}
}
}

View File

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

View 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();
}
}
}
}

View File

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

View 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();
}
}
}

View File

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