Mr. Upbeat Rework (#418)

* everything but the text

* new spritesheet (thank u sean)
* better/more accurate background color
* bg color changing with fade (code courtesy of ras)
* mr. upbeat blip color changing
* full blip implementation, with size changing and text display

* text is still weird (gray box around all the letters) but i hope that's an easy fix.

* almost done

just need to fix the text and get rid of the reliance on the ding! block

* finishing touches + a buncha testing

everything works!!! and i don't think i left anything out

* forgot an n lol

* oh my god i hate mr. downbeat

this "force" option is not very intuitive to use. nor is it coded amazingly. but i do not care. if you really wanna use it then mess around and see what works
This commit is contained in:
AstrlJelly
2023-05-15 15:52:41 -04:00
committed by GitHub
parent caf7d9465f
commit c33532ee78
27 changed files with 2466 additions and 338 deletions

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using System;
using Starpelly;
using DG.Tweening;
using HeavenStudio.Util;
@ -15,26 +15,69 @@ namespace HeavenStudio.Games.Loaders
public static Minigame AddGame(EventCaller eventCaller) {
return new Minigame("mrUpbeat", "Mr. Upbeat", "ffffff", false, false, new List<GameAction>()
{
new GameAction("stepping", "Start Stepping")
new GameAction("start stepping", "Start Stepping")
{
preFunction = delegate {var e = eventCaller.currentEntity; MrUpbeat.Stepping(e.beat, e.length); },
preFunction = delegate {var e = eventCaller.currentEntity; MrUpbeat.StartStepping(e.beat, e.length, e["force"]); },
defaultLength = 4f,
resizable = true,
parameters = new List<Param>()
{
new Param("force", false, "Force Mr. Downbeat", "Forces inputs to not be only on the offbeats"),
}
},
new GameAction("blipping", "Beeping")
new GameAction("ding", "Ding!")
{
function = delegate {var e = eventCaller.currentEntity; MrUpbeat.Blipping(e.beat, e.length); },
defaultLength = 4f,
resizable = true,
inactiveFunction = delegate {var e = eventCaller.currentEntity; MrUpbeat.Blipping(e.beat, e.length); },
},
new GameAction("ding!", "Ding!")
{
function = delegate { MrUpbeat.instance.Ding(eventCaller.currentEntity["toggle"]); },
function = delegate {
var e = eventCaller.currentEntity;
MrUpbeat.instance.Ding(eventCaller.currentEntity["toggle"], e["stopBlipping"]); },
defaultLength = 0.5f,
parameters = new List<Param>()
{
new Param("toggle", false, "Applause")
new Param("toggle", false, "Applause", "Plays an applause sound effect."),
new Param("stopBlipping", true, "Stop Blipping?", "When the stepping stops, should the blipping stop too?"),
}
},
new GameAction("changeBG", "Change Background Color")
{
function = delegate {
var e = eventCaller.currentEntity;
MrUpbeat.instance.FadeBackgroundColor(e["start"], e["end"], e.length, e["toggle"]); },
defaultLength = 1f,
resizable = true,
parameters = new List<Param>()
{
new Param("start", new Color(0.878f, 0.878f, 0.878f), "Start Color", "The start color for the fade or the color that will be switched to if -instant- is ticked on."),
new Param("end", new Color(0.878f, 0.878f, 0.878f), "End Color", "The end color for the fade."),
new Param("toggle", false, "Instant", "Should the background instantly change color?")
}
},
new GameAction("upbeatColors", "Upbeat Colors")
{
function = delegate {
var e = eventCaller.currentEntity;
MrUpbeat.instance.UpbeatColors(e["blipColor"], e["setShadow"], e["shadowColor"]);
},
defaultLength = 0.5f,
parameters = new List<Param>()
{
new Param("blipColor", new Color(0, 1f, 0), "Blip Color", "Change blip color"),
new Param("setShadow", false, "Set Shadow Color?", "Should Mr. Upbeat's shadow be custom?"),
new Param("shadowColor", new Color(1f, 1f, 1f, 0), "Shadow Color", "If \"Set Shadow Color\" is checked, this will set the shadow's color."),
}
},
new GameAction("blipEvents", "Blip Events")
{
function = delegate {
var e = eventCaller.currentEntity;
MrUpbeat.instance.BlipEvents(e["letter"], e["shouldGrow"], e["resetBlip"], e["blip"]);
},
defaultLength = 0.5f,
parameters = new List<Param>()
{
new Param("letter", "", "Letter To Appear", "Which letter to appear on the blip"),
new Param("shouldGrow", true, "Grow Antenna?", "Should Mr. Upbeat's antenna grow?"),
new Param("resetBlip", false, "Reset Antenna?", "Should Mr. Upbeat's antenna reset?"),
new Param("blip", true, "Should Blip?", "Should Mr. Upbeat blip every offbeat?"),
}
},
});
@ -48,160 +91,135 @@ namespace HeavenStudio.Games
public class MrUpbeat : Minigame
{
static List<float> queuedBeeps = new List<float>();
static List<queuedUpbeatInputs> queuedInputs = new List<queuedUpbeatInputs>();
public struct queuedUpbeatInputs
{
public float beat;
public bool goRight;
}
static List<float> queuedInputs = new List<float>();
[Header("References")]
public Animator metronomeAnim;
public UpbeatMan man;
[SerializeField] Animator metronomeAnim;
[SerializeField] UpbeatMan man;
[SerializeField] Material blipMaterial;
[SerializeField] SpriteRenderer bg;
[SerializeField] SpriteRenderer[] shadowSr;
[Header("Properties")]
bool startLeft;
private Tween bgColorTween;
public int stepIterate = 0;
public static float downbeatMod = 0.5f;
public static bool shouldBlip;
static bool noDing;
public static MrUpbeat instance;
private void Awake()
{
instance = this;
blipMaterial.SetColor("_ColorBravo", new Color(0, 1f, 0));
}
private void Start()
{
man.Blip();
}
void OnDestroy()
{
if (!Conductor.instance.isPlaying || Conductor.instance.isPaused)
{
if (!Conductor.instance.isPlaying || Conductor.instance.isPaused) {
if (queuedInputs.Count > 0) queuedInputs.Clear();
if (queuedBeeps.Count > 0) queuedBeeps.Clear();
}
// these variables wouldn't get reset, even when you go in and out of unity play mode???
shouldBlip = false;
stepIterate = 0;
}
public void Update()
{
var cond = Conductor.instance;
if (cond.isPlaying && !cond.isPaused)
{
if (queuedInputs.Count > 0)
{
foreach (var input in queuedInputs)
{
ScheduleInput(cond.songPositionInBeats, input.beat - cond.songPositionInBeats, InputType.STANDARD_DOWN, Success, Miss, Nothing);
if (input.goRight)
{
BeatAction.New(instance.gameObject, new List<BeatAction.Action>()
{
new BeatAction.Action(input.beat - 0.5f, delegate { MrUpbeat.instance.metronomeAnim.DoScaledAnimationAsync("MetronomeGoLeft", 0.5f); }),
new BeatAction.Action(input.beat - 0.5f, delegate { Jukebox.PlayOneShotGame("mrUpbeat/metronomeRight"); }),
});
}
else
{
BeatAction.New(instance.gameObject, new List<BeatAction.Action>()
{
new BeatAction.Action(input.beat - 0.5f, delegate { MrUpbeat.instance.metronomeAnim.DoScaledAnimationAsync("MetronomeGoRight", 0.5f); }),
new BeatAction.Action(input.beat - 0.5f, delegate { Jukebox.PlayOneShotGame("mrUpbeat/metronomeLeft"); }),
});
}
}
if (queuedInputs.Count % 2 != 0)
{
startLeft = true;
}
else
{
startLeft = false;
if (cond.isPlaying && !cond.isPaused) {
if (queuedInputs.Count > 0) {
foreach (var input in queuedInputs) {
string dir = stepIterate % 2 == 1 ? "Right" : "Left";
BeatAction.New(instance.gameObject, new List<BeatAction.Action>() {
new BeatAction.Action(input, delegate {
instance.metronomeAnim.DoScaledAnimationAsync("MetronomeGo" + dir, 0.5f);
Jukebox.PlayOneShotGame("mrUpbeat/metronome" + dir);
ScheduleInput(input, 0.5f, InputType.STANDARD_DOWN, Success, Miss, Nothing);
if (MrUpbeat.noDing) queuedInputs.Add(input + 1);
}),
});
stepIterate++;
}
queuedInputs.Clear();
}
if (PlayerInput.Pressed() && !IsExpectingInputNow(InputType.STANDARD_DOWN))
{
if (PlayerInput.Pressed() && !IsExpectingInputNow(InputType.STANDARD_DOWN)) {
man.Step();
}
}
if (queuedBeeps.Count > 0) {
var beepAnims = new List<BeatAction.Action>();
foreach (var item in queuedBeeps)
{
beepAnims.Add(new BeatAction.Action(item, delegate { man.blipAnimator.Play("Blip", 0, 0); }));
}
BeatAction.New(instance.gameObject, beepAnims);
queuedBeeps.Clear();
}
}
public void Ding(bool applause)
public void Ding(bool applause, bool stopBlipping)
{
Jukebox.PlayOneShotGame("mrUpbeat/ding");
if (applause) Jukebox.PlayOneShot("applause");
if (stopBlipping) shouldBlip = false;
}
public static void StartStepping(float beat, float length, bool force)
{
// mr. downbeat stuff. god i hate mr. downbeat
// force != true means that mr. upbeat will always blip/step on the offbeats
beat = force ? beat - 0.5f : MathF.Floor(beat);
downbeatMod = force ? (beat % 1) : 0.5f;
if (GameManager.instance.currentGame != "mrUpbeat") {
Blipping(beat, length);
MrUpbeat.shouldBlip = true;
} else {
BeatAction.New(instance.gameObject, new List<BeatAction.Action>() {
new BeatAction.Action(beat, delegate {
MrUpbeat.shouldBlip = true;
}),
});
}
var dings = EventCaller.GetAllInGameManagerList("mrUpbeat", new string[] { "ding" });
if (dings.Count == 0) {
MrUpbeat.noDing = true;
queuedInputs.Add(beat + (force ? length : MathF.Floor(length)));
return;
}
MrUpbeat.noDing = false;
int whichDing = 0;
for (int i = 0; i < dings.Count; i++) {
if (dings[i].beat > beat) {
whichDing = i;
break;
}
}
for (int i = (int)length; i < dings[whichDing].beat - beat; i++) {
queuedInputs.Add(beat + i - (force ? downbeatMod : 0));
}
}
public static void Blipping(float beat, float length)
{
List<MultiSound.Sound> beeps = new List<MultiSound.Sound>();
for (int i = 0; i < length + 1; i++)
{
beeps.Add(new MultiSound.Sound("mrUpbeat/blip", beat + i));
queuedBeeps.Add(beat + i);
}
MultiSound.Play(beeps.ToArray(), forcePlay: true);
}
public static void Stepping(float beat, float length)
{
if (GameManager.instance.currentGame == "mrUpbeat")
{
float offSet = 0;
if (MrUpbeat.instance.startLeft)
{
offSet = 1;
}
for (int i = 0; i < length + 1; i++)
{
MrUpbeat.instance.ScheduleInput(beat - 1, 1 + i, InputType.STANDARD_DOWN, MrUpbeat.instance.Success, MrUpbeat.instance.Miss, MrUpbeat.instance.Nothing);
if ((i + offSet) % 2 == 0)
{
BeatAction.New(instance.gameObject, new List<BeatAction.Action>()
{
new BeatAction.Action(beat + i - 0.5f, delegate { MrUpbeat.instance.metronomeAnim.DoScaledAnimationAsync("MetronomeGoLeft", 0.5f); }),
new BeatAction.Action(beat + i - 0.5f, delegate { Jukebox.PlayOneShotGame("mrUpbeat/metronomeRight"); }),
});
}
else
{
BeatAction.New(instance.gameObject, new List<BeatAction.Action>()
{
new BeatAction.Action(beat + i - 0.5f, delegate { MrUpbeat.instance.metronomeAnim.DoScaledAnimationAsync("MetronomeGoRight", 0.5f); }),
new BeatAction.Action(beat + i - 0.5f, delegate { Jukebox.PlayOneShotGame("mrUpbeat/metronomeLeft"); }),
});
List<MultiSound.Sound> blips = new List<MultiSound.Sound>();
var switchGames = EventCaller.GetAllInGameManagerList("gameManager", new string[] { "switchGame" });
int whichSwitch = 0;
if (switchGames.Count != 0) {
for (int i = 0; i < switchGames.Count; i++) {
if (switchGames[i].beat > beat) {
whichSwitch = i;
break;
}
}
}
}
if ((length + 1) % 2 != 0)
{
MrUpbeat.instance.startLeft = true;
}
else
{
MrUpbeat.instance.startLeft = false;
}
}
else
{
for (int i = 0; i < length + 1; i++)
{
queuedInputs.Add(new queuedUpbeatInputs
{
beat = beat + i,
goRight = i % 2 == 0
});
}
for (int i = 0; i < switchGames[whichSwitch].beat - beat - 0.5f; i++) {
blips.Add(new MultiSound.Sound("mrUpbeat/blip", beat + 0.5f + i));
}
MultiSound.Play(blips.ToArray(), forcePlay: true);
}
public void Success(PlayerActionEvent caller, float state)
@ -214,13 +232,41 @@ namespace HeavenStudio.Games
man.Fall();
}
bool isPlaying(Animator anim, string stateName)
public void ChangeBackgroundColor(Color color, float beats)
{
if (anim.GetCurrentAnimatorStateInfo(0).IsName(stateName) &&
anim.GetCurrentAnimatorStateInfo(0).normalizedTime < 1.0f)
return true;
else
return false;
var seconds = Conductor.instance.secPerBeat * beats;
if (bgColorTween != null)
bgColorTween.Kill(true);
if (seconds == 0) {
bg.color = color;
} else {
bgColorTween = bg.DOColor(color, seconds);
}
}
public void FadeBackgroundColor(Color start, Color end, float beats, bool instant)
{
ChangeBackgroundColor(start, 0f);
if (!instant) ChangeBackgroundColor(end, beats);
}
public void UpbeatColors(Color blipColor, bool setShadow, Color shadowColor)
{
blipMaterial.SetColor("_ColorBravo", blipColor);
if (setShadow) foreach (var shadow in shadowSr) {
shadow.color = new Color(shadowColor.r, shadowColor.g, shadowColor.b, 1);
}
}
public void BlipEvents(string inputLetter, bool shouldGrow, bool resetBlip, bool blip)
{
if (shouldGrow && man.blipSize < 4) man.blipSize++;
if (resetBlip) man.blipSize = 0;
man.blipString = inputLetter;
shouldBlip = blip;
}
public void Nothing(PlayerActionEvent caller) {}

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using UnityEngine;
using System;
using Starpelly;
using TMPro;
using HeavenStudio.Util;
@ -11,60 +12,61 @@ namespace HeavenStudio.Games.Scripts_MrUpbeat
public class UpbeatMan : MonoBehaviour
{
[Header("References")]
public MrUpbeat game;
public Animator animator;
public Animator blipAnimator;
public GameObject[] shadows;
[SerializeField] Animator anim;
[SerializeField] Animator blipAnim;
[SerializeField] Animator letterAnim;
[SerializeField] GameObject[] shadows;
[SerializeField] TMP_Text blipText;
public float targetBeat = 0.25f;
public int stepTimes = 0;
private bool stepped = false;
private bool onGround = false;
public int blipSize = 0;
public string blipString = "M";
public GameEvent blip = new GameEvent();
public void Idle()
public void Blip()
{
stepTimes = 0;
transform.localScale = new Vector3(1, 1);
animator.Play("Idle", 0, 0);
float c = Conductor.instance.songPositionInBeats;
// checks if the position is on an offbeat; accurate until you get down to 20 fps or so (i.e unplayable)
float pos = ((MathF.Floor(c * 10)/10 % 1) == 0.5f) ? MathF.Floor(c) : MathF.Round(c);
// recursive, should happen on the offbeat (unless downbeatMod is different)
BeatAction.New(gameObject, new List<BeatAction.Action>() {
new BeatAction.Action(pos + MrUpbeat.downbeatMod, delegate {
if (MrUpbeat.shouldBlip) {
Jukebox.PlayOneShotGame("mrUpbeat/blip");
blipAnim.Play("Blip"+(blipSize+1), 0, 0);
blipText.text = (blipSize == 4 && blipString != "") ? blipString : "";
}
}),
new BeatAction.Action(pos + MrUpbeat.downbeatMod + 0.999f, delegate {
Blip();
}),
});
}
public void Step()
{
stepTimes++;
bool x = (stepTimes % 2 == 1);
shadows[0].SetActive(!x);
shadows[1].SetActive(x);
transform.localScale = new Vector3(x ? -1 : 1, 1);
animator.Play("Step", 0, 0);
anim.DoScaledAnimationAsync("Step", 0.5f);
letterAnim.DoScaledAnimationAsync(x ? "StepRight" : "StepLeft", 0.5f);
Jukebox.PlayOneShotGame("mrUpbeat/step");
onGround = false;
CheckShadows();
}
public void Fall()
{
animator.Play("Fall", 0, 0);
blipSize = 0;
blipAnim.Play("Idle", 0, 0);
blipText.text = "";
anim.DoScaledAnimationAsync("Fall", 0.5f);
Jukebox.PlayOneShot("miss");
shadows[0].SetActive(false);
shadows[1].SetActive(false);
onGround = true;
}
private void CheckShadows()
{
if (onGround) return;
if (stepTimes % 2 == 1)
{
shadows[0].SetActive(false);
shadows[1].SetActive(true);
transform.localScale = new Vector3(-1, 1);
} else
{
shadows[0].SetActive(true);
shadows[1].SetActive(false);
transform.localScale = new Vector3(1, 1);
}
}
}
}