Bouncy Road improvements (#944)

* Add custom notes to Bouncy Road + fix swing animations

* Add simple tap controls to Bouncy Road

* Add blank pole sounds + volume panning to Bouncy Road
This commit is contained in:
EpicGamer2469 2024-05-19 16:14:59 -05:00 committed by GitHub
parent 95e658f31e
commit 049959bed3
4 changed files with 156 additions and 27 deletions

View file

@ -0,0 +1,22 @@
fileFormatVersion: 2
guid: 9b584037609d9084aa8a838607884dcc
AudioImporter:
externalObjects: {}
serializedVersion: 6
defaultSettings:
loadType: 0
sampleRateSetting: 0
sampleRateOverride: 44100
compressionFormat: 1
quality: 1
conversionMode: 0
platformSettingOverrides: {}
forceToMono: 0
normalize: 1
preloadAudioData: 1
loadInBackground: 0
ambisonic: 0
3D: 1
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,22 @@
fileFormatVersion: 2
guid: c0997440911c8a543b05babc2e2ed806
AudioImporter:
externalObjects: {}
serializedVersion: 6
defaultSettings:
loadType: 0
sampleRateSetting: 0
sampleRateOverride: 44100
compressionFormat: 1
quality: 1
conversionMode: 0
platformSettingOverrides: {}
forceToMono: 0
normalize: 1
preloadAudioData: 1
loadInBackground: 0
ambisonic: 0
3D: 1
userData:
assetBundleName:
assetBundleVariant:

View file

@ -15,6 +15,12 @@ namespace HeavenStudio.Games.Scripts_BouncyRoad
public Color color; public Color color;
[System.NonSerialized] public bool goal; [System.NonSerialized] public bool goal;
[System.NonSerialized] public bool useCustomNotes;
[System.NonSerialized] public int[] bounceNotes;
[System.NonSerialized] public int bounceNote;
[System.NonSerialized] public int rightNote;
[System.NonSerialized] public int leftNote;
[System.NonSerialized] public int goalNote;
private bool isMiss; private bool isMiss;
@ -33,9 +39,9 @@ namespace HeavenStudio.Games.Scripts_BouncyRoad
if (currentCurve is not null) if (currentCurve is not null)
{ {
float curveProg = cond.GetPositionFromBeat(currentBeat, lengthBeat); float curveProg = cond.GetPositionFromBeat(currentBeat, lengthBeat, ignoreSwing: false);
if (isMiss) { if (isMiss) {
curveProg = cond.GetPositionFromBeat(currentBeat, lengthBeat/2); curveProg = cond.GetPositionFromBeat(currentBeat, lengthBeat/2, ignoreSwing: false);
} else { } else {
// curveProg /= (float)(1 + BouncyRoad.ngLateTime); // curveProg /= (float)(1 + BouncyRoad.ngLateTime);
if (curveProg >= 1) curveProg = 1; if (curveProg >= 1) curveProg = 1;
@ -44,9 +50,19 @@ namespace HeavenStudio.Games.Scripts_BouncyRoad
} }
} }
private void Bounce() private void Bounce() {
{ float[] pitches = null;
game.PlayBounceSound(startBeat, lengthBeat); if (bounceNotes != null)
{
pitches = new float[12];
for (int i = 0; i < 12; i++)
{
pitches[i] = GetPitch(bounceNotes[i]);
}
}
game.PlayBounceSound(startBeat, lengthBeat, pitches, GetPitch(bounceNote));
var actions = new List<BeatAction.Action>(); var actions = new List<BeatAction.Action>();
@ -70,7 +86,7 @@ namespace HeavenStudio.Games.Scripts_BouncyRoad
public void RightSuccess(PlayerActionEvent caller, float state) public void RightSuccess(PlayerActionEvent caller, float state)
{ {
SoundByte.PlayOneShotGame("bouncyRoad/ballRight"); SoundByte.PlayOneShotGame("bouncyRoad/ballRight", pitch: GetPitch(rightNote));
game.ThingsAnim[12].Play("podium", 0, 0); game.ThingsAnim[12].Play("podium", 0, 0);
currentCurve = curve[1+12]; currentCurve = curve[1+12];
currentBeat = startBeat + 12 * lengthBeat; currentBeat = startBeat + 12 * lengthBeat;
@ -80,7 +96,7 @@ namespace HeavenStudio.Games.Scripts_BouncyRoad
public void RightMiss(PlayerActionEvent caller) public void RightMiss(PlayerActionEvent caller)
{ {
SoundByte.PlayOneShotGame("bouncyRoad/ballBounce"); SoundByte.PlayOneShotGame("bouncyRoad/ballBounce", pitch: GetPitch(bounceNote));
currentCurve = curve[^2]; currentCurve = curve[^2];
currentBeat = Conductor.instance.songPositionInBeats; currentBeat = Conductor.instance.songPositionInBeats;
isMiss = true; isMiss = true;
@ -95,12 +111,12 @@ namespace HeavenStudio.Games.Scripts_BouncyRoad
public void LeftSuccess(PlayerActionEvent caller, float state) public void LeftSuccess(PlayerActionEvent caller, float state)
{ {
SoundByte.PlayOneShotGame("bouncyRoad/ballLeft"); SoundByte.PlayOneShotGame("bouncyRoad/ballLeft", pitch: GetPitch(leftNote));
game.ThingsAnim[13].Play("podium", 0, 0); game.ThingsAnim[13].Play("podium", 0, 0);
currentCurve = curve[1+13]; currentCurve = curve[1+13];
currentBeat = startBeat + 13 * lengthBeat; currentBeat = startBeat + 13 * lengthBeat;
if (goal) SoundByte.PlayOneShotGame("bouncyRoad/goal", startBeat + 14 * lengthBeat); if (goal) SoundByte.PlayOneShotGame("bouncyRoad/goal", startBeat + 14 * lengthBeat, pitch: GetPitch(goalNote));
BeatAction.New(game, new List<BeatAction.Action>() BeatAction.New(game, new List<BeatAction.Action>()
{ {
new BeatAction.Action(startBeat + 14 * lengthBeat, delegate new BeatAction.Action(startBeat + 14 * lengthBeat, delegate
@ -118,7 +134,7 @@ namespace HeavenStudio.Games.Scripts_BouncyRoad
public void LeftMiss(PlayerActionEvent caller) public void LeftMiss(PlayerActionEvent caller)
{ {
SoundByte.PlayOneShotGame("bouncyRoad/ballBounce"); SoundByte.PlayOneShotGame("bouncyRoad/ballBounce", pitch: GetPitch(bounceNote));
currentCurve = curve[^1]; currentCurve = curve[^1];
currentBeat = Conductor.instance.songPositionInBeats; currentBeat = Conductor.instance.songPositionInBeats;
isMiss = true; isMiss = true;
@ -132,5 +148,10 @@ namespace HeavenStudio.Games.Scripts_BouncyRoad
} }
public void Empty(PlayerActionEvent caller) { } public void Empty(PlayerActionEvent caller) { }
private float GetPitch(int semitones)
{
return useCustomNotes ? SoundByte.GetPitchFromSemiTones(semitones, true) : 1;
}
} }
} }

View file

@ -24,6 +24,29 @@ namespace HeavenStudio.Games.Loaders
{ {
new Param("goal", true, "Play Goal Sound"), new Param("goal", true, "Play Goal Sound"),
new Param("color", Color.white, "Color", "Choose the color of the ball."), new Param("color", Color.white, "Color", "Choose the color of the ball."),
new Param("useCustomNotes", false, "Custom Notes", "Toggle if the ball should use custom notes.", new List<Param.CollapseParam>()
{
new Param.CollapseParam((x, _) => (bool)x, new string[] { "bounceNote", "rightNote", "leftNote", "goalNote", "separateBounceNotes"})
}),
new Param("separateBounceNotes", false, "Separate Bounce Notes", "Toggle if the ball should use separate notes for the each of the first 12 poles.", new List<Param.CollapseParam>()
{
new Param.CollapseParam((x, _) => (bool)x, new string[] { "bounceNote2", "bounceNote3", "bounceNote4", "bounceNote5", "bounceNote6", "bounceNote7", "bounceNote8", "bounceNote9", "bounceNote10", "bounceNote11", "bounceNote12" })
}),
new Param("bounceNote", new EntityTypes.Note(0, 7, 4, "bouncyRoad/ballBounce"), "Bounce Note", "Choose the note the ball plays when bouncing on the first 12 poles or the first pole if using custom notes."),
new Param("bounceNote2", new EntityTypes.Note(0, 7, 4, "bouncyRoad/ballBounce"), "Bounce Note 2", "Choose the note the ball plays when bouncing on the second pole."),
new Param("bounceNote3", new EntityTypes.Note(0, 7, 4, "bouncyRoad/ballBounce"), "Bounce Note 3", "Choose the note the ball plays when bouncing on the third pole."),
new Param("bounceNote4", new EntityTypes.Note(0, 7, 4, "bouncyRoad/ballBounce"), "Bounce Note 4", "Choose the note the ball plays when bouncing on the fourth pole."),
new Param("bounceNote5", new EntityTypes.Note(0, 7, 4, "bouncyRoad/ballBounce"), "Bounce Note 5", "Choose the note the ball plays when bouncing on the fifth pole."),
new Param("bounceNote6", new EntityTypes.Note(0, 7, 4, "bouncyRoad/ballBounce"), "Bounce Note 6", "Choose the note the ball plays when bouncing on the sixth pole."),
new Param("bounceNote7", new EntityTypes.Note(0, 7, 4, "bouncyRoad/ballBounce"), "Bounce Note 7", "Choose the note the ball plays when bouncing on the seventh pole."),
new Param("bounceNote8", new EntityTypes.Note(0, 7, 4, "bouncyRoad/ballBounce"), "Bounce Note 8", "Choose the note the ball plays when bouncing on the eighth pole."),
new Param("bounceNote9", new EntityTypes.Note(0, 7, 4, "bouncyRoad/ballBounce"), "Bounce Note 9", "Choose the note the ball plays when bouncing on the ninth pole."),
new Param("bounceNote10", new EntityTypes.Note(0, 7, 4, "bouncyRoad/ballBounce"), "Bounce Note 10", "Choose the note the ball plays when bouncing on the tenth pole."),
new Param("bounceNote11", new EntityTypes.Note(0, 7, 4, "bouncyRoad/ballBounce"), "Bounce Note 11", "Choose the note the ball plays when bouncing on the eleventh pole."),
new Param("bounceNote12", new EntityTypes.Note(0, 7, 4, "bouncyRoad/ballBounce"), "Bounce Note 12", "Choose the note the ball plays when bouncing on the twelfth pole."),
new Param("rightNote", new EntityTypes.Note(0, 3, 4, "bouncyRoad/ballRight"), "A Note", "Choose the note the ball plays when it bounces on the A pole."),
new Param("leftNote", new EntityTypes.Note(0, 10, 4, "bouncyRoad/ballLeft"), "+ Note", "Choose the note the ball plays when it bounces on the + pole."),
new Param("goalNote", new EntityTypes.Note(0, 3, 5, "bouncyRoad/goal"), "Goal Note", "Choose the note the ball plays when it bounces on the last pole."),
} }
}, },
new GameAction("background appearance", "Background Appearance") new GameAction("background appearance", "Background Appearance")
@ -87,12 +110,20 @@ namespace HeavenStudio.Games
private ColorEase[] colorEases = new ColorEase[2]; private ColorEase[] colorEases = new ColorEase[2];
const double BALL_SEEK_TIME = 1.0; const double BALL_SEEK_TIME = 1.0;
private struct ScheduledBall
public struct ScheduledBall
{ {
public double beat; public double beat;
public double length; public double length;
public bool goal; public bool goal;
public Color color; public Color color;
public bool useCustomNotes;
public bool seperateBounceNotes;
public int[] bounceNotes;
public int bounceNote;
public int rightNote;
public int leftNote;
public int goalNote;
} }
List<ScheduledBall> scheduledBalls = new(); List<ScheduledBall> scheduledBalls = new();
int ballIndex; int ballIndex;
@ -111,16 +142,18 @@ namespace HeavenStudio.Games
} }
protected static bool IA_TouchLeft(out double dt) protected static bool IA_TouchLeft(out double dt)
{ {
return PlayerInput.GetTouchDown(InputController.ActionsTouch.Left, out dt); return PlayerInput.GetTouchDown(InputController.ActionsTouch.Tap, out dt) && instance.IsExpectingInputNow(InputAction_Left);
} }
protected static bool IA_PadRight(out double dt) protected static bool IA_PadRight(out double dt)
{ {
return PlayerInput.GetPadDown(InputController.ActionsPad.East, out dt); return PlayerInput.GetPadDown(InputController.ActionsPad.East, out dt);
} }
protected static bool IA_TouchRight(out double dt) protected static bool IA_TouchRight(out double dt) {
{ return PlayerInput.GetTouchDown(InputController.ActionsTouch.Tap, out dt)
return PlayerInput.GetTouchDown(InputController.ActionsTouch.Right, out dt); && (instance.IsExpectingInputNow(InputAction_Right)
|| (!instance.IsExpectingInputNow(InputAction_Left) && !instance.IsExpectingInputNow(InputAction_Right)));
} }
public static PlayerInput.InputAction InputAction_Left = public static PlayerInput.InputAction InputAction_Left =
@ -204,12 +237,30 @@ namespace HeavenStudio.Games
foreach (var e in events) foreach (var e in events)
{ {
if (e.length == 0) continue; if (e.length == 0) continue;
int[] bounceNotes = null;
if (e["separateBounceNotes"]) {
bounceNotes = new int[12];
bounceNotes[0] = e["bounceNote"];
for (int i = 1; i < 12; i++)
{
bounceNotes[i] = e[$"bounceNote{i+1}"];
}
}
var ball = new ScheduledBall var ball = new ScheduledBall
{ {
beat = e.beat, beat = e.beat,
length = e.length, length = e.length,
goal = e["goal"], goal = e["goal"],
color = e["color"], color = e["color"],
useCustomNotes = e["useCustomNotes"],
seperateBounceNotes = e["separateBounceNotes"],
bounceNotes = bounceNotes,
bounceNote = e["bounceNote"],
rightNote = e["rightNote"],
leftNote = e["leftNote"],
goalNote = e["goalNote"],
}; };
scheduledBalls.Add(ball); scheduledBalls.Add(ball);
} }
@ -228,10 +279,12 @@ namespace HeavenStudio.Games
if (PlayerInput.GetIsAction(InputAction_Right) && !IsExpectingInputNow(InputAction_Right)) if (PlayerInput.GetIsAction(InputAction_Right) && !IsExpectingInputNow(InputAction_Right))
{ {
ThingsAnim[12].Play("podium", 0, 0); ThingsAnim[12].Play("podium", 0, 0);
SoundByte.PlayOneShotGame("bouncyRoad/rightBlank", volume: .5f);
} }
if (PlayerInput.GetIsAction(InputAction_Left) && !IsExpectingInputNow(InputAction_Left)) if (PlayerInput.GetIsAction(InputAction_Left) && !IsExpectingInputNow(InputAction_Left))
{ {
ThingsAnim[13].Play("podium", 0, 0); ThingsAnim[13].Play("podium", 0, 0);
SoundByte.PlayOneShotGame("bouncyRoad/leftBlank", volume: .5f);
} }
UpdateBalls(); UpdateBalls();
@ -246,7 +299,7 @@ namespace HeavenStudio.Games
var ball = scheduledBalls[ballIndex]; var ball = scheduledBalls[ballIndex];
if (ball.beat - ball.length < beat + BALL_SEEK_TIME) if (ball.beat - ball.length < beat + BALL_SEEK_TIME)
{ {
SpawnBall(ball.beat, ball.length, ball.goal, ball.color); SpawnBall(ball);
ballIndex++; ballIndex++;
} }
else else
@ -256,20 +309,26 @@ namespace HeavenStudio.Games
} }
} }
public void SpawnBall(double beat, double length, bool goal, Color color) public void SpawnBall(ScheduledBall scheduledBall)
{ {
var newBall = Instantiate(baseBall, transform).GetComponent<Ball>(); var newBall = Instantiate(baseBall, transform).GetComponent<Ball>();
newBall.startBeat = beat; newBall.startBeat = scheduledBall.beat;
newBall.lengthBeat = length; newBall.lengthBeat = scheduledBall.length;
newBall.goal = goal; newBall.goal = scheduledBall.goal;
newBall.color = color; newBall.color = scheduledBall.color;
newBall.useCustomNotes = scheduledBall.useCustomNotes;
newBall.bounceNotes = scheduledBall.bounceNotes;
newBall.bounceNote = scheduledBall.bounceNote;
newBall.rightNote = scheduledBall.rightNote;
newBall.leftNote = scheduledBall.leftNote;
newBall.goalNote = scheduledBall.goalNote;
newBall.curve = GetHeightCurve((float)length); newBall.curve = GetHeightCurve((float)scheduledBall.length);
BeatAction.New(instance, new List<BeatAction.Action>() BeatAction.New(instance, new List<BeatAction.Action>()
{ {
new BeatAction.Action(beat - length, delegate new BeatAction.Action(scheduledBall.beat - scheduledBall.length, delegate
{ {
newBall.Init(); newBall.Init();
newBall.gameObject.SetActive(true); newBall.gameObject.SetActive(true);
@ -278,13 +337,18 @@ namespace HeavenStudio.Games
} }
List<double> bounceBeats = new(); List<double> bounceBeats = new();
public void PlayBounceSound(double beat, double length) public void PlayBounceSound(double beat, double length, float[] pitches, float singlePitch)
{ {
var sounds = new List<MultiSound.Sound>(); var sounds = new List<MultiSound.Sound>();
for (int i = 0; i < 12 ; i++) float volume = .65f;
{ for (int i = 0; i < 12 ; i++) {
if (i >= 6) volume += .059f;
var bounceBeat = beat + i * length; var bounceBeat = beat + i * length;
if (!bounceBeats.Contains(bounceBeat)) sounds.Add(new MultiSound.Sound("bouncyRoad/ballBounce", bounceBeat)); if (!bounceBeats.Contains(bounceBeat)) {
float pitch = pitches == null ? singlePitch : pitches[i];
sounds.Add(new MultiSound.Sound("bouncyRoad/ballBounce", bounceBeat, pitch, volume));
}
bounceBeats.Add(bounceBeat); bounceBeats.Add(bounceBeat);
} }
MultiSound.Play(sounds.ToArray()); MultiSound.Play(sounds.ToArray());