mirror of
https://github.com/RHeavenStudio/HeavenStudio.git
synced 2025-06-12 10:47:39 +02:00
Tunnel tunnels (#577)
* tunnel tunnel tunnel * tunnel messes with the volume * tempo finder can now be reset by waiting now uses the conductor's time source if it exists and is playing * wip anims * Animations Finished * add tunnel sound * tunnel shader fix the left sound * add pre-sliced BG assets --------- Co-authored-by: Seanski2 <seanbenedit@gmail.com>
This commit is contained in:
@ -1,8 +1,8 @@
|
||||
using System;
|
||||
|
||||
public static class AppInfo {
|
||||
public const string Version = "0.0.1016";
|
||||
public static readonly DateTime Date = new DateTime(2023, 10, 20, 02, 31, 04, 288, DateTimeKind.Utc);
|
||||
public const string Version = "0.0.1017";
|
||||
public static readonly DateTime Date = new DateTime(2023, 11, 21, 01, 58, 14, 615, DateTimeKind.Utc);
|
||||
}
|
||||
|
||||
|
||||
|
@ -88,6 +88,10 @@ namespace HeavenStudio
|
||||
private float musicScheduledPitch = 1f;
|
||||
private double musicScheduledTime = 0;
|
||||
|
||||
// volume modifier
|
||||
private float timelineVolume = 1f;
|
||||
private float minigameVolume = 1f;
|
||||
|
||||
public void SetTimelinePitch(float pitch)
|
||||
{
|
||||
if (pitch != 0 && pitch * minigamePitch != SongPitch)
|
||||
@ -168,6 +172,8 @@ namespace HeavenStudio
|
||||
dspMargin = 2 * dspSizeSeconds;
|
||||
addedPitchChanges.Clear();
|
||||
addedPitchChanges.Add(new AddedPitchChange { time = 0, pitch = SongPitch });
|
||||
|
||||
SetMinigameVolume(1f);
|
||||
}
|
||||
|
||||
var chart = GameManager.instance.Beatmap;
|
||||
@ -273,6 +279,46 @@ namespace HeavenStudio
|
||||
StopOnlyAudio();
|
||||
}
|
||||
|
||||
Coroutine fadeOutAudioCoroutine;
|
||||
public void FadeMinigameVolume(double startBeat, double durationBeats = 1f, float targetVolume = 0f)
|
||||
{
|
||||
if (fadeOutAudioCoroutine != null)
|
||||
{
|
||||
StopCoroutine(fadeOutAudioCoroutine);
|
||||
}
|
||||
fadeOutAudioCoroutine = StartCoroutine(FadeMinigameVolumeCoroutine(startBeat, durationBeats, targetVolume));
|
||||
}
|
||||
|
||||
IEnumerator FadeMinigameVolumeCoroutine(double startBeat, double durationBeats, float targetVolume)
|
||||
{
|
||||
float startVolume = minigameVolume;
|
||||
float endVolume = targetVolume;
|
||||
double startTime = startBeat;
|
||||
double endTime = startBeat + durationBeats;
|
||||
|
||||
while (songPositionInBeatsAsDouble < endTime)
|
||||
{
|
||||
if (!NotStopped()) yield break;
|
||||
double t = (songPositionInBeatsAsDouble - startTime) / durationBeats;
|
||||
SetMinigameVolume(Mathf.Lerp(startVolume, endVolume, (float)t));
|
||||
yield return null;
|
||||
}
|
||||
|
||||
SetMinigameVolume(endVolume);
|
||||
}
|
||||
|
||||
public void SetTimelineVolume(float volume)
|
||||
{
|
||||
timelineVolume = volume;
|
||||
musicSource.volume = timelineVolume * minigameVolume;
|
||||
}
|
||||
|
||||
public void SetMinigameVolume(float volume)
|
||||
{
|
||||
minigameVolume = volume;
|
||||
musicSource.volume = timelineVolume * minigameVolume;
|
||||
}
|
||||
|
||||
void SeekMusicToTime(double fStartPos, double offset)
|
||||
{
|
||||
if (musicSource.clip != null && fStartPos < musicSource.clip.length - offset)
|
||||
@ -517,7 +563,7 @@ namespace HeavenStudio
|
||||
|
||||
public void SetVolume(float percent)
|
||||
{
|
||||
musicSource.volume = percent / 100f;
|
||||
SetTimelineVolume(percent / 100f);
|
||||
}
|
||||
|
||||
public float SongLengthInBeats()
|
||||
|
@ -1,6 +1,6 @@
|
||||
using DG.Tweening;
|
||||
using NaughtyBezierCurves;
|
||||
using HeavenStudio.Util;
|
||||
using HeavenStudio.Util;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
@ -14,23 +14,37 @@ namespace HeavenStudio.Games.Loaders
|
||||
{
|
||||
return new Minigame("tunnel", "Tunnel \n<color=#eb5454>[WIP]</color>", "c00000", false, false, new List<GameAction>()
|
||||
{
|
||||
new GameAction("cowbell", "Cowbell")
|
||||
new GameAction("cowbell", "Start Cowbell")
|
||||
{
|
||||
preFunction = delegate { Tunnel.PreStartCowbell(eventCaller.currentEntity.beat, eventCaller.currentEntity.length); },
|
||||
defaultLength = 1f,
|
||||
resizable = false,
|
||||
|
||||
},
|
||||
new GameAction("tunnel", "Tunnel")
|
||||
{
|
||||
function = delegate { if (Tunnel.instance != null) {
|
||||
var e = eventCaller.currentEntity;
|
||||
Tunnel.instance.StartTunnel(e.beat, e.length, e["volume"] / 100f, e["duration"]);
|
||||
} },
|
||||
defaultLength = 4f,
|
||||
resizable = true,
|
||||
|
||||
parameters = new List<Param>()
|
||||
{
|
||||
new Param("duration", new EntityTypes.Float(0, 8, 2), "Fade Duration", "The duration of the volume fade in beats"),
|
||||
new Param("volume", new EntityTypes.Float(0, 200, 10), "Volume", "The volume to fade to"),
|
||||
}
|
||||
},
|
||||
new GameAction("countin", "Count In")
|
||||
{
|
||||
preFunction = delegate { Tunnel.CountIn(eventCaller.currentEntity.beat, eventCaller.currentEntity.length); },
|
||||
defaultLength = 4f,
|
||||
preFunction = delegate { Tunnel.CountIn(eventCaller.currentEntity.beat, eventCaller.currentEntity.length); },
|
||||
defaultLength = 4f,
|
||||
resizable = true,
|
||||
}
|
||||
},
|
||||
new List<string>() {"ntr", "keep"},
|
||||
new List<string>() { "ntr", "keep" },
|
||||
"ntrtunnel", "en",
|
||||
new List<string>() {"en"}
|
||||
new List<string>() { "en" }
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -40,36 +54,46 @@ namespace HeavenStudio.Games
|
||||
{
|
||||
public class Tunnel : Minigame
|
||||
{
|
||||
const double PostTunnelScrnTime = 0.25;
|
||||
public static Tunnel instance { get; set; }
|
||||
|
||||
[Header("Backgrounds")]
|
||||
public SpriteRenderer fg;
|
||||
public SpriteRenderer bg;
|
||||
[SerializeField] Transform bg;
|
||||
[SerializeField] float bgScrollTime;
|
||||
|
||||
Tween bgColorTween;
|
||||
Tween fgColorTween;
|
||||
[SerializeField] Material tunnelLightMaterial;
|
||||
[SerializeField] Color tunnelTint;
|
||||
[SerializeField] Color tunnelScreen;
|
||||
[SerializeField] GameObject tunnelWall;
|
||||
[SerializeField] SpriteRenderer tunnelWallRenderer;
|
||||
[SerializeField] float tunnelChunksPerSec;
|
||||
[SerializeField] float tunnelWallChunkSize;
|
||||
|
||||
Vector3 tunnelStartPos;
|
||||
Sound tunnelSoundRight, tunnelSoundMiddle, tunnelSoundLeft;
|
||||
|
||||
[Header("References")]
|
||||
public GameObject frontHand;
|
||||
|
||||
[SerializeField] GameObject frontHand;
|
||||
|
||||
[Header("Animators")]
|
||||
public Animator cowbellAnimator;
|
||||
public Animator driverAnimator;
|
||||
[SerializeField] Animator cowbellAnimator;
|
||||
[SerializeField] Animator driverAnimator;
|
||||
|
||||
[Header("Curves")]
|
||||
public BezierCurve3D handCurve;
|
||||
[SerializeField] BezierCurve3D handCurve;
|
||||
|
||||
GameEvent cowbell = new GameEvent();
|
||||
|
||||
public GameEvent cowbell = new GameEvent();
|
||||
float bgStartX;
|
||||
float fadeDuration = 2f;
|
||||
double tunnelStartTime = double.MinValue;
|
||||
double tunnelEndTime = double.MinValue;
|
||||
double lastCowbell = double.MaxValue;
|
||||
|
||||
float handStart;
|
||||
float handProgress;
|
||||
bool inTunnel;
|
||||
|
||||
public int driverState;
|
||||
|
||||
public float handStart;
|
||||
public float handProgress;
|
||||
public bool started;
|
||||
public struct QueuedCowbell
|
||||
{
|
||||
public double beat;
|
||||
@ -80,6 +104,7 @@ namespace HeavenStudio.Games
|
||||
private void Awake()
|
||||
{
|
||||
instance = this;
|
||||
tunnelStartPos = new Vector3(tunnelWallChunkSize, 0, 0);
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
@ -89,22 +114,30 @@ namespace HeavenStudio.Games
|
||||
{
|
||||
evt.Disable();
|
||||
}
|
||||
if (Conductor.instance != null && !(Conductor.instance.isPlaying || Conductor.instance.isPaused))
|
||||
{
|
||||
Conductor.instance.FadeMinigameVolume(Conductor.instance.songPositionInBeatsAsDouble, 0, 1);
|
||||
tunnelLightMaterial.SetColor("_Color", Color.white);
|
||||
tunnelLightMaterial.SetColor("_AddColor", Color.black);
|
||||
|
||||
tunnelSoundRight?.Stop();
|
||||
tunnelSoundMiddle?.Stop();
|
||||
tunnelSoundLeft?.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
driverState = 0;
|
||||
handStart = -1f;
|
||||
tunnelWall.SetActive(false);
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
|
||||
var cond = Conductor.instance;
|
||||
//update hand position
|
||||
handProgress = Math.Min(Conductor.instance.songPositionInBeats - handStart, 1);
|
||||
|
||||
|
||||
frontHand.transform.position = handCurve.GetPoint(EasingFunction.EaseOutQuad(0, 1, handProgress));
|
||||
if (!cond.isPlaying || cond.isPaused)
|
||||
{
|
||||
@ -125,16 +158,33 @@ namespace HeavenStudio.Games
|
||||
queuedInputs.Clear();
|
||||
}
|
||||
|
||||
}
|
||||
if (lastCowbell + 1 <= cond.songPositionInBeatsAsDouble)
|
||||
{
|
||||
lastCowbell++;
|
||||
ScheduleInput(lastCowbell, 1, InputAction_BasicPress, CowbellSuccess, CowbellMiss, CowbellEmpty);
|
||||
}
|
||||
|
||||
// bg.localPosition = new Vector3(bgStartX - (2 * bgStartX * (((float)Time.realtimeSinceStartupAsDouble % bgScrollTime) / bgScrollTime)), 0, 0);
|
||||
if (tunnelWall.activeSelf)
|
||||
{
|
||||
tunnelWall.transform.localPosition = tunnelStartPos - new Vector3(tunnelChunksPerSec * tunnelWallChunkSize * (float)(cond.songPositionAsDouble - tunnelStartTime), 0, 0);
|
||||
}
|
||||
if (inTunnel && cond.songPositionAsDouble >= tunnelEndTime + PostTunnelScrnTime)
|
||||
{
|
||||
cond.FadeMinigameVolume(cond.GetBeatFromSongPos(tunnelEndTime + PostTunnelScrnTime), fadeDuration, 1);
|
||||
tunnelLightMaterial.SetColor("_Color", Color.white);
|
||||
tunnelLightMaterial.SetColor("_AddColor", Color.black);
|
||||
inTunnel = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void HitCowbell()
|
||||
{
|
||||
SoundByte.PlayOneShot("count-ins/cowbell");
|
||||
|
||||
handStart = Conductor.instance.songPositionInBeats;
|
||||
|
||||
cowbellAnimator.Play("Shake",-1,0);
|
||||
|
||||
cowbellAnimator.Play("Shake", -1, 0);
|
||||
}
|
||||
|
||||
public static void PreStartCowbell(double beat, float length)
|
||||
@ -151,18 +201,14 @@ namespace HeavenStudio.Games
|
||||
|
||||
public void StartCowbell(double beat, float length)
|
||||
{
|
||||
started = true;
|
||||
for(int i = 0; i < length; i++)
|
||||
{
|
||||
ScheduleInput(beat, i, InputAction_BasicPress, CowbellSuccess, CowbellMiss, CowbellEmpty);
|
||||
}
|
||||
lastCowbell = beat - 1;
|
||||
ScheduleInput(lastCowbell, 1, InputAction_BasicPress, CowbellSuccess, CowbellMiss, CowbellEmpty);
|
||||
}
|
||||
|
||||
public void CowbellSuccess(PlayerActionEvent caller, float state)
|
||||
{
|
||||
HitCowbell();
|
||||
//print(state);
|
||||
if(Math.Abs(state) >= 1f)
|
||||
if (Math.Abs(state) >= 1f)
|
||||
{
|
||||
driverAnimator.Play("Disturbed", -1, 0);
|
||||
|
||||
@ -171,33 +217,22 @@ namespace HeavenStudio.Games
|
||||
{
|
||||
driverAnimator.Play("Idle", -1, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void CowbellMiss(PlayerActionEvent caller)
|
||||
{
|
||||
//HitCowbell();
|
||||
|
||||
driverAnimator.Play("Angry1", -1, 0);
|
||||
}
|
||||
|
||||
public void CowbellEmpty(PlayerActionEvent caller)
|
||||
{
|
||||
//HitCowbell();
|
||||
}
|
||||
public void CowbellEmpty(PlayerActionEvent caller) { }
|
||||
|
||||
|
||||
|
||||
public static void CountIn(double beat, float length)
|
||||
{
|
||||
|
||||
List<MultiSound.Sound> cuelist = new List<MultiSound.Sound>();
|
||||
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
if(i % 2 == 0)
|
||||
if (i % 2 == 0)
|
||||
{
|
||||
//Jukebox.PlayOneShotGame("tunnel/en/one", beat+i);
|
||||
//print("cueing one at " + (beat + i));
|
||||
@ -209,13 +244,52 @@ namespace HeavenStudio.Games
|
||||
//print("cueing two at " + (beat + i));
|
||||
cuelist.Add(new MultiSound.Sound("tunnel/en/two", beat + i));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
MultiSound.Play(cuelist.ToArray(), forcePlay: true);
|
||||
|
||||
}
|
||||
|
||||
public void StartTunnel(double beat, double length, float volume = 0.1f, float fadeDuration = 2f)
|
||||
{
|
||||
Conductor cond = Conductor.instance;
|
||||
if (cond.songPositionAsDouble < tunnelEndTime + PostTunnelScrnTime)
|
||||
{
|
||||
return;
|
||||
}
|
||||
double targetBeat = beat + length;
|
||||
tunnelStartTime = cond.GetSongPosFromBeat(beat);
|
||||
tunnelEndTime = cond.GetSongPosFromBeat(targetBeat);
|
||||
// tunnel chunks can be divided into quarters
|
||||
double durationSec = Math.Ceiling((tunnelEndTime - tunnelStartTime) * 4 * tunnelChunksPerSec) * 0.25 / tunnelChunksPerSec;
|
||||
|
||||
tunnelWallRenderer.size = new Vector2((float)durationSec * tunnelWallChunkSize * tunnelChunksPerSec, 13.7f);
|
||||
tunnelWall.transform.localPosition = tunnelStartPos;
|
||||
tunnelWall.SetActive(true);
|
||||
this.fadeDuration = fadeDuration;
|
||||
cond.FadeMinigameVolume(beat, fadeDuration, volume);
|
||||
|
||||
tunnelSoundRight?.Stop();
|
||||
tunnelSoundMiddle?.Stop();
|
||||
tunnelSoundLeft?.Stop();
|
||||
|
||||
tunnelSoundRight = SoundByte.PlayOneShotGame("tunnel/tunnelRight", beat, looping: true);
|
||||
tunnelSoundMiddle = SoundByte.PlayOneShotGame("tunnel/tunnelMiddle", beat + (6 / 48f), looping: true);
|
||||
tunnelSoundLeft = SoundByte.PlayOneShotGame("tunnel/tunnelLeft", beat + (12 / 48f), looping: true);
|
||||
|
||||
double tunnelEnd = cond.GetBeatFromSongPos(tunnelEndTime + PostTunnelScrnTime);
|
||||
tunnelSoundRight.SetLoopParams(tunnelEnd, 0.1);
|
||||
tunnelSoundMiddle.SetLoopParams(tunnelEnd + (6 / 48f), 0.1);
|
||||
tunnelSoundLeft.SetLoopParams(tunnelEnd + (12 / 48f), 0.25);
|
||||
|
||||
BeatAction.New(instance, new List<BeatAction.Action>()
|
||||
{
|
||||
new BeatAction.Action(cond.GetBeatFromSongPos(tunnelStartTime + 0.25), delegate {
|
||||
tunnelLightMaterial.SetColor("_Color", tunnelTint);
|
||||
tunnelLightMaterial.SetColor("_AddColor", tunnelScreen);
|
||||
}),
|
||||
});
|
||||
inTunnel = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using TMPro;
|
||||
using System.Linq;
|
||||
|
||||
namespace HeavenStudio.Editor
|
||||
namespace HeavenStudio.Editor
|
||||
{
|
||||
public class BPMText : MonoBehaviour
|
||||
{
|
||||
@ -12,9 +13,9 @@ namespace HeavenStudio.Editor
|
||||
[SerializeField] private TMP_Text BPM;
|
||||
[SerializeField] private TMP_Text BPMRounded;
|
||||
|
||||
private List<float> pressTimes = new List<float>();
|
||||
private List<double> pressTimes = new();
|
||||
|
||||
public void ChangeText(float timePressed)
|
||||
public void ChangeText(double timePressed)
|
||||
{
|
||||
pressTimes.Add(timePressed);
|
||||
|
||||
@ -25,12 +26,13 @@ namespace HeavenStudio.Editor
|
||||
if (pressTimes.Count > maxPressTimes)
|
||||
pressTimes.RemoveAt(0);
|
||||
|
||||
var averageTime = pressTimes.GetRange(1, pressTimes.Count - 1).Average();
|
||||
double averageTime = pressTimes.GetRange(1, pressTimes.Count - 1).Average();
|
||||
|
||||
float thisBPM = 60 / averageTime; // BPM = 60/t
|
||||
BPM.text = $"{thisBPM}";
|
||||
BPMRounded.text = $"{Mathf.RoundToInt(thisBPM)}";
|
||||
double thisBPM = 60 / averageTime;
|
||||
BPM.text = $"{thisBPM:0.000}";
|
||||
BPMRounded.text = $"{(int)Math.Round(thisBPM)}";
|
||||
}
|
||||
|
||||
public void ResetText()
|
||||
{
|
||||
pressTimes.Clear();
|
||||
@ -38,5 +40,21 @@ namespace HeavenStudio.Editor
|
||||
BPM.text = "---";
|
||||
BPMRounded.text = "---";
|
||||
}
|
||||
|
||||
public void ClearSamples()
|
||||
{
|
||||
if (pressTimes.Count < 2) return;
|
||||
|
||||
if (pressTimes.Count > maxPressTimes)
|
||||
pressTimes.RemoveAt(0);
|
||||
|
||||
double averageTime = pressTimes.GetRange(1, pressTimes.Count - 1).Average();
|
||||
|
||||
double thisBPM = 60 / averageTime;
|
||||
BPM.text = $"<color=\"yellow\">{thisBPM:0.000}";
|
||||
BPMRounded.text = $"<color=\"yellow\">{(int)Math.Round(thisBPM)}";
|
||||
|
||||
pressTimes.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,41 +2,76 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace HeavenStudio.Editor
|
||||
namespace HeavenStudio.Editor
|
||||
{
|
||||
public class TempoFinder : Dialog
|
||||
{
|
||||
private bool pressed;
|
||||
private float timePressed;
|
||||
[SerializeField] private BPMText bpmText;
|
||||
private void Awake()
|
||||
private bool pressed;
|
||||
private double timePressed;
|
||||
private double lastTimePressed = double.MinValue;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
pressed = false;
|
||||
timePressed = 0f;
|
||||
lastTimePressed = double.MinValue;
|
||||
}
|
||||
|
||||
public void SwitchTempoDialog()
|
||||
{
|
||||
if(dialog.activeSelf) {
|
||||
if (dialog.activeSelf)
|
||||
{
|
||||
dialog.SetActive(false);
|
||||
timePressed = 0;
|
||||
lastTimePressed = double.MinValue;
|
||||
bpmText.ResetText();
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
ResetAllDialogs();
|
||||
dialog.SetActive(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void TapBPM()
|
||||
{
|
||||
pressed = true;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
bool conductorTimeSource = Conductor.instance != null && Conductor.instance.NotStopped();
|
||||
timePressed += Time.deltaTime;
|
||||
if(pressed)
|
||||
if (timePressed > 2)
|
||||
{
|
||||
pressed = false;
|
||||
bpmText.ChangeText(timePressed);
|
||||
timePressed = 0;
|
||||
lastTimePressed = double.MinValue;
|
||||
bpmText.ClearSamples();
|
||||
pressed = false;
|
||||
}
|
||||
|
||||
if (pressed)
|
||||
{
|
||||
if (!conductorTimeSource)
|
||||
{
|
||||
bpmText.ChangeText(timePressed);
|
||||
lastTimePressed = double.MinValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (lastTimePressed == double.MinValue)
|
||||
{
|
||||
bpmText.ChangeText(timePressed);
|
||||
}
|
||||
else
|
||||
{
|
||||
bpmText.ChangeText(Conductor.instance.songPositionAsDouble - lastTimePressed);
|
||||
}
|
||||
lastTimePressed = Conductor.instance.songPositionAsDouble;
|
||||
}
|
||||
timePressed = 0;
|
||||
pressed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user