mirror of
https://github.com/RHeavenStudio/HeavenStudio.git
synced 2025-06-13 05:17:38 +02:00
Bouncy Road (#854)
* Bouncy Road Stuff yeah -weirdly set up prefab so i can see animations side by side - animations for the 3 podium things we ball * modified: Assets/Resources/Games/bouncyRoad.prefab * Preliminary stage of sorting according to the equation * reached a point where it's functional * miss curve * proper spawn * oops * bounce sound avoid duplication --------- Co-authored-by: GhostiGuy <srskok1@gmail.com> Co-authored-by: GhostiGuy <156829475+GhostiGuy@users.noreply.github.com>
This commit is contained in:
8
Assets/Scripts/Games/BouncyRoad.meta
Normal file
8
Assets/Scripts/Games/BouncyRoad.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b101fa9860bf48443839aa02aace64a9
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
136
Assets/Scripts/Games/BouncyRoad/Ball.cs
Normal file
136
Assets/Scripts/Games/BouncyRoad/Ball.cs
Normal file
@ -0,0 +1,136 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using NaughtyBezierCurves;
|
||||
|
||||
namespace HeavenStudio.Games.Scripts_BouncyRoad
|
||||
{
|
||||
using HeavenStudio.Util;
|
||||
public class Ball : MonoBehaviour
|
||||
{
|
||||
[System.NonSerialized] public double startBeat, lengthBeat;
|
||||
private double currentBeat;
|
||||
|
||||
[System.NonSerialized] public BezierCurve3D[] curve;
|
||||
private BezierCurve3D currentCurve;
|
||||
|
||||
public Color color;
|
||||
[System.NonSerialized] public bool goal;
|
||||
|
||||
private bool isMiss;
|
||||
|
||||
private BouncyRoad game;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
game = BouncyRoad.instance;
|
||||
|
||||
GetComponent<SpriteRenderer>().color = color;
|
||||
Bounce();
|
||||
}
|
||||
void Update()
|
||||
{
|
||||
var cond = Conductor.instance;
|
||||
|
||||
if (currentCurve is not null)
|
||||
{
|
||||
float curveProg = cond.GetPositionFromBeat(currentBeat, lengthBeat);
|
||||
if (isMiss) {
|
||||
curveProg = cond.GetPositionFromBeat(currentBeat, lengthBeat/2);
|
||||
} else {
|
||||
// curveProg /= (float)(1 + BouncyRoad.ngLateTime);
|
||||
if (curveProg >= 1) curveProg = 1;
|
||||
}
|
||||
transform.position = currentCurve.GetPoint(curveProg);
|
||||
}
|
||||
}
|
||||
|
||||
private void Bounce()
|
||||
{
|
||||
game.PlayBounceSound(startBeat, lengthBeat);
|
||||
|
||||
var actions = new List<BeatAction.Action>();
|
||||
|
||||
actions.Add(new BeatAction.Action(startBeat - lengthBeat, delegate {
|
||||
currentCurve = curve[0];
|
||||
currentBeat = startBeat - lengthBeat;
|
||||
}));
|
||||
for (int i = 0; i < 12 ; i++)
|
||||
{
|
||||
int currentItr = i;
|
||||
actions.Add(new BeatAction.Action(startBeat + currentItr * lengthBeat, delegate {
|
||||
game.ThingsAnim[currentItr].Play("podium", 0, 0);
|
||||
currentCurve = curve[1+currentItr];
|
||||
currentBeat = startBeat + currentItr * lengthBeat;
|
||||
}));
|
||||
}
|
||||
BeatAction.New(game, actions);
|
||||
|
||||
game.ScheduleInput(startBeat + 11 * lengthBeat, lengthBeat, BouncyRoad.InputAction_Right, RightSuccess, RightMiss, Empty);
|
||||
}
|
||||
|
||||
public void RightSuccess(PlayerActionEvent caller, float state)
|
||||
{
|
||||
SoundByte.PlayOneShotGame("bouncyRoad/ballRight");
|
||||
game.ThingsAnim[12].Play("podium", 0, 0);
|
||||
currentCurve = curve[1+12];
|
||||
currentBeat = startBeat + 12 * lengthBeat;
|
||||
|
||||
game.ScheduleInput(startBeat + 12 * lengthBeat, lengthBeat, BouncyRoad.InputAction_Left, LeftSuccess, LeftMiss, Empty);
|
||||
}
|
||||
|
||||
public void RightMiss(PlayerActionEvent caller)
|
||||
{
|
||||
SoundByte.PlayOneShotGame("bouncyRoad/ballBounce");
|
||||
currentCurve = curve[^2];
|
||||
currentBeat = Conductor.instance.songPositionInBeats;
|
||||
isMiss = true;
|
||||
BeatAction.New(game, new List<BeatAction.Action>()
|
||||
{
|
||||
new BeatAction.Action(currentBeat + lengthBeat / 2, delegate
|
||||
{
|
||||
Destroy(gameObject);
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
public void LeftSuccess(PlayerActionEvent caller, float state)
|
||||
{
|
||||
SoundByte.PlayOneShotGame("bouncyRoad/ballLeft");
|
||||
game.ThingsAnim[13].Play("podium", 0, 0);
|
||||
currentCurve = curve[1+13];
|
||||
currentBeat = startBeat + 13 * lengthBeat;
|
||||
|
||||
if (goal) SoundByte.PlayOneShotGame("bouncyRoad/goal", startBeat + 14 * lengthBeat);
|
||||
BeatAction.New(game, new List<BeatAction.Action>()
|
||||
{
|
||||
new BeatAction.Action(startBeat + 14 * lengthBeat, delegate
|
||||
{
|
||||
game.ThingsAnim[14].Play("podium", 0, 0);
|
||||
currentCurve = curve[1+14];
|
||||
currentBeat = startBeat + 14 * lengthBeat;
|
||||
}),
|
||||
new BeatAction.Action(startBeat + 15 * lengthBeat, delegate
|
||||
{
|
||||
Destroy(gameObject);
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
public void LeftMiss(PlayerActionEvent caller)
|
||||
{
|
||||
SoundByte.PlayOneShotGame("bouncyRoad/ballBounce");
|
||||
currentCurve = curve[^1];
|
||||
currentBeat = Conductor.instance.songPositionInBeats;
|
||||
isMiss = true;
|
||||
BeatAction.New(game, new List<BeatAction.Action>()
|
||||
{
|
||||
new BeatAction.Action(currentBeat + lengthBeat / 2, delegate
|
||||
{
|
||||
Destroy(gameObject);
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
public void Empty(PlayerActionEvent caller) { }
|
||||
}
|
||||
}
|
11
Assets/Scripts/Games/BouncyRoad/Ball.cs.meta
Normal file
11
Assets/Scripts/Games/BouncyRoad/Ball.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 632739a1ddc985249adeaeccbd66de58
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
314
Assets/Scripts/Games/BouncyRoad/BouncyRoad.cs
Normal file
314
Assets/Scripts/Games/BouncyRoad/BouncyRoad.cs
Normal file
@ -0,0 +1,314 @@
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using NaughtyBezierCurves;
|
||||
|
||||
using HeavenStudio.Util;
|
||||
using HeavenStudio.InputSystem;
|
||||
|
||||
namespace HeavenStudio.Games.Loaders
|
||||
{
|
||||
using static Minigames;
|
||||
|
||||
public static class AgbBouncyRoadLoader
|
||||
{
|
||||
public static Minigame AddGame(EventCaller eventCaller)
|
||||
{
|
||||
return new Minigame("bouncyRoad", "Bouncy Road", "0296FF", false, false, new List<GameAction>()
|
||||
{
|
||||
new GameAction("ball", "Ball")
|
||||
{
|
||||
defaultLength = 1f,
|
||||
resizable = true,
|
||||
parameters = new List<Param>()
|
||||
{
|
||||
new Param("goal", true, "Play Goal Sound"),
|
||||
new Param("color", Color.white, "Color", "Choose the color of the ball."),
|
||||
}
|
||||
},
|
||||
},
|
||||
new List<string>() { "agb", "normal" },
|
||||
"agbbouncy", "en",
|
||||
new List<string>() { },
|
||||
chronologicalSortKey: 24
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace HeavenStudio.Games
|
||||
{
|
||||
using Scripts_BouncyRoad;
|
||||
public class BouncyRoad : Minigame
|
||||
{
|
||||
[SerializeField] GameObject baseBall;
|
||||
[SerializeField] GameObject baseBounceCurve;
|
||||
[SerializeField] Transform CurveHolder;
|
||||
|
||||
[SerializeField] Transform ThingsTrans;
|
||||
[System.NonSerialized] public Animator[] ThingsAnim;
|
||||
[System.NonSerialized] public Dictionary<float, BezierCurve3D[]> CurveCache;
|
||||
[SerializeField] BezierCurve3D PosCurve;
|
||||
|
||||
[SerializeField] float fallY;
|
||||
|
||||
const double BALL_SEEK_TIME = 1.0;
|
||||
private struct ScheduledBall
|
||||
{
|
||||
public double beat;
|
||||
public double length;
|
||||
public bool goal;
|
||||
public Color color;
|
||||
}
|
||||
List<ScheduledBall> scheduledBalls = new();
|
||||
int ballIndex;
|
||||
|
||||
public static BouncyRoad instance;
|
||||
|
||||
const int IALeft = 0;
|
||||
const int IARight = 1;
|
||||
|
||||
protected static bool IA_PadLeft(out double dt)
|
||||
{
|
||||
return PlayerInput.GetPadDown(InputController.ActionsPad.Up, out dt)
|
||||
|| PlayerInput.GetPadDown(InputController.ActionsPad.Down, out dt)
|
||||
|| PlayerInput.GetPadDown(InputController.ActionsPad.Left, out dt)
|
||||
|| PlayerInput.GetPadDown(InputController.ActionsPad.Right, out dt);
|
||||
}
|
||||
protected static bool IA_TouchLeft(out double dt)
|
||||
{
|
||||
return PlayerInput.GetTouchDown(InputController.ActionsTouch.Left, out dt);
|
||||
}
|
||||
|
||||
protected static bool IA_PadRight(out double dt)
|
||||
{
|
||||
return PlayerInput.GetPadDown(InputController.ActionsPad.East, out dt);
|
||||
}
|
||||
protected static bool IA_TouchRight(out double dt)
|
||||
{
|
||||
return PlayerInput.GetTouchDown(InputController.ActionsTouch.Right, out dt);
|
||||
}
|
||||
|
||||
public static PlayerInput.InputAction InputAction_Left =
|
||||
new("AgbBouncyLeft", new int[] { IALeft, IALeft, IALeft },
|
||||
IA_PadLeft, IA_TouchLeft, IA_BatonBasicPress);
|
||||
|
||||
public static PlayerInput.InputAction InputAction_Right =
|
||||
new("AgbBouncyRight", new int[] { IARight, IARight, IAEmptyCat },
|
||||
IA_PadRight, IA_TouchRight, IA_Empty);
|
||||
|
||||
void Awake()
|
||||
{
|
||||
instance = this;
|
||||
|
||||
ThingsAnim = new Animator[ThingsTrans.childCount];
|
||||
int childIndex = 0;
|
||||
foreach (Transform child in ThingsTrans)
|
||||
{
|
||||
// var prog = (float)childIndex/(ThingsTrans.childCount-1);
|
||||
// child.transform.localPosition = PosCurve.GetPoint(prog);
|
||||
ThingsAnim[childIndex++] = child.GetComponent<Animator>();
|
||||
}
|
||||
|
||||
var newCurves = new BezierCurve3D[ThingsTrans.childCount + 3];
|
||||
// for (var i = 0; i < ThingsAnim.Length + 1; ++i)
|
||||
// {
|
||||
// var prog1 = (float)(i-1)/(ThingsTrans.childCount-1);
|
||||
// var prog2 = (float)(i)/(ThingsTrans.childCount-1);
|
||||
// var pos1 = PosCurve.GetPoint(prog1);
|
||||
// var pos2 = PosCurve.GetPoint(prog2);
|
||||
|
||||
// var newCurve = GenerateInitCurve(pos1, pos2);
|
||||
|
||||
// newCurves[i] = newCurve.GetComponent<BezierCurve3D>();
|
||||
// }
|
||||
{
|
||||
Vector3 pos1, pos2;
|
||||
pos1 = PosCurve.GetPoint((float)(-1)/(ThingsAnim.Length-1));
|
||||
pos2 = PosCurve.GetPoint(0);
|
||||
newCurves[0] = GenerateInitCurve(pos1, pos2).GetComponent<BezierCurve3D>();
|
||||
|
||||
for (var i = 0; i < ThingsAnim.Length-1; ++i)
|
||||
{
|
||||
pos1 = ThingsTrans.GetChild(i).transform.localPosition;
|
||||
pos2 = ThingsTrans.GetChild(i+1).transform.localPosition;
|
||||
|
||||
var newCurve = GenerateInitCurve(pos1, pos2);
|
||||
|
||||
newCurves[1+i] = newCurve.GetComponent<BezierCurve3D>();
|
||||
}
|
||||
|
||||
pos1 = PosCurve.GetPoint(1);
|
||||
pos2 = PosCurve.GetPoint((float)(ThingsAnim.Length)/(ThingsAnim.Length-1));
|
||||
newCurves[ThingsAnim.Length] = GenerateInitCurve(pos1, pos2).GetComponent<BezierCurve3D>();
|
||||
}
|
||||
newCurves[^2] = GenerateMissCurve(13).GetComponent<BezierCurve3D>();
|
||||
newCurves[^1] = GenerateMissCurve(14).GetComponent<BezierCurve3D>();
|
||||
|
||||
CurveCache = new Dictionary<float, BezierCurve3D[]>();
|
||||
CurveCache.Add(1, newCurves);
|
||||
}
|
||||
|
||||
public override void OnGameSwitch(double beat)
|
||||
{
|
||||
double gameStartBeat = beat, gameEndBeat = double.MaxValue;
|
||||
var firstEnd = EventCaller.GetAllInGameManagerList("gameManager", new string[] { "switchGame", "end" }).Find(x => x.beat > gameStartBeat);
|
||||
gameEndBeat = firstEnd?.beat ?? gameEndBeat;
|
||||
|
||||
|
||||
scheduledBalls.Clear();
|
||||
ballIndex = 0;
|
||||
var events = EventCaller.GetAllInGameManagerList("bouncyRoad", new string[] { "ball" }).FindAll(x => x.beat >= gameStartBeat && x.beat < gameEndBeat);
|
||||
foreach (var e in events)
|
||||
{
|
||||
if (e.length == 0) continue;
|
||||
var ball = new ScheduledBall
|
||||
{
|
||||
beat = e.beat,
|
||||
length = e.length,
|
||||
goal = e["goal"],
|
||||
color = e["color"],
|
||||
};
|
||||
scheduledBalls.Add(ball);
|
||||
}
|
||||
scheduledBalls.Sort((x, y) => (x.beat - x.length).CompareTo(y.beat - y.length));
|
||||
}
|
||||
public override void OnPlay(double beat)
|
||||
{
|
||||
OnGameSwitch(beat);
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
var cond = Conductor.instance;
|
||||
if (!cond.isPlaying || cond.isPaused) return;
|
||||
|
||||
if (PlayerInput.GetIsAction(InputAction_Right) && !IsExpectingInputNow(InputAction_Right))
|
||||
{
|
||||
ThingsAnim[12].Play("podium", 0, 0);
|
||||
}
|
||||
if (PlayerInput.GetIsAction(InputAction_Left) && !IsExpectingInputNow(InputAction_Left))
|
||||
{
|
||||
ThingsAnim[13].Play("podium", 0, 0);
|
||||
}
|
||||
|
||||
UpdateBalls();
|
||||
}
|
||||
|
||||
void UpdateBalls()
|
||||
{
|
||||
double beat = conductor.songPositionInBeatsAsDouble;
|
||||
while(ballIndex < scheduledBalls.Count)
|
||||
{
|
||||
var ball = scheduledBalls[ballIndex];
|
||||
if (ball.beat - ball.length < beat + BALL_SEEK_TIME)
|
||||
{
|
||||
SpawnBall(ball.beat, ball.length, ball.goal, ball.color);
|
||||
ballIndex++;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SpawnBall(double beat, double length, bool goal, Color color)
|
||||
{
|
||||
var newBall = Instantiate(baseBall, transform).GetComponent<Ball>();
|
||||
|
||||
newBall.startBeat = beat;
|
||||
newBall.lengthBeat = length;
|
||||
newBall.goal = goal;
|
||||
newBall.color = color;
|
||||
|
||||
newBall.curve = GetHeightCurve((float)length);
|
||||
|
||||
BeatAction.New(instance, new List<BeatAction.Action>()
|
||||
{
|
||||
new BeatAction.Action(beat - length, delegate
|
||||
{
|
||||
newBall.Init();
|
||||
newBall.gameObject.SetActive(true);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
List<double> bounceBeats = new();
|
||||
public void PlayBounceSound(double beat, double length)
|
||||
{
|
||||
var sounds = new List<MultiSound.Sound>();
|
||||
for (int i = 0; i < 12 ; i++)
|
||||
{
|
||||
var bounceBeat = beat + i * length;
|
||||
if (!bounceBeats.Contains(bounceBeat)) sounds.Add(new MultiSound.Sound("bouncyRoad/ballBounce", bounceBeat));
|
||||
bounceBeats.Add(bounceBeat);
|
||||
}
|
||||
MultiSound.Play(sounds.ToArray());
|
||||
}
|
||||
|
||||
private Transform GenerateInitCurve(Vector3 pos1, Vector3 pos2)
|
||||
{
|
||||
float dist = Vector3.Distance(pos1, pos2);
|
||||
float angle = Mathf.Atan2(pos1.z - pos2.z, pos1.x - pos2.x) * Mathf.Rad2Deg;
|
||||
|
||||
var newCurve = Instantiate(baseBounceCurve, CurveHolder).transform;
|
||||
|
||||
var point0 = newCurve.GetChild(0);
|
||||
var point1 = newCurve.GetChild(1);
|
||||
|
||||
point0.transform.localPosition = pos1;
|
||||
point0.transform.localEulerAngles = new Vector3(0, -angle, 0);
|
||||
point0.transform.localScale = new Vector3(dist, 1, 1);
|
||||
point1.transform.localPosition = pos2;
|
||||
point1.transform.localEulerAngles = new Vector3(0, -angle, 0);
|
||||
point1.transform.localScale = new Vector3(dist, 1, 1);
|
||||
|
||||
return newCurve;
|
||||
}
|
||||
|
||||
private Transform GenerateMissCurve(int number)
|
||||
{
|
||||
var curve = CurveHolder.GetChild(number);
|
||||
|
||||
var newCurve = Instantiate(curve, CurveHolder).transform;
|
||||
|
||||
var point0 = newCurve.GetChild(0);
|
||||
var point1 = newCurve.GetChild(1);
|
||||
|
||||
Vector3 pos1 = point1.transform.localPosition;
|
||||
point1.transform.localPosition = pos1 + new Vector3(0, fallY, 0);
|
||||
|
||||
return newCurve;
|
||||
}
|
||||
|
||||
private BezierCurve3D[] GetHeightCurve(float length)
|
||||
{
|
||||
BezierCurve3D[] newCurves;
|
||||
CurveCache.TryGetValue(length, out newCurves);
|
||||
|
||||
if (newCurves is null)
|
||||
{
|
||||
var newCurveHolder = Instantiate(CurveHolder, transform);
|
||||
newCurveHolder.name = $"CurveHolder_{length}";
|
||||
var newCurvesTrans = newCurveHolder.transform;
|
||||
|
||||
newCurves = new BezierCurve3D[newCurvesTrans.childCount];
|
||||
int childIndex = 0;
|
||||
foreach (Transform child in newCurvesTrans)
|
||||
{
|
||||
var point0 = child.GetChild(0);
|
||||
var point1 = child.GetChild(1);
|
||||
|
||||
Vector3 scale = point0.transform.localScale;
|
||||
point0.transform.localScale = new Vector3(scale.x, length * scale.y, scale.z);
|
||||
point1.transform.localScale = new Vector3(scale.x, length * scale.y, scale.z);
|
||||
newCurves[childIndex++] = child.GetComponent<BezierCurve3D>();
|
||||
}
|
||||
CurveCache.Add(length, newCurves);
|
||||
}
|
||||
|
||||
return newCurves;
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Scripts/Games/BouncyRoad/BouncyRoad.cs.meta
Normal file
11
Assets/Scripts/Games/BouncyRoad/BouncyRoad.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ad2e36698feab2c44a4b72a4d69c49c2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -63,6 +63,16 @@ namespace HeavenStudio
|
||||
Debug.LogWarning("Game loader AgbBonOdoriLoader failed!");
|
||||
}
|
||||
|
||||
game = AgbBouncyRoadLoader.AddGame(eventCaller);
|
||||
if (game != null)
|
||||
{
|
||||
eventCaller.minigames.Add(game.name, game);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("Game loader AgbBouncyRoadLoader failed!");
|
||||
}
|
||||
|
||||
game = NtrFlickLoader.AddGame(eventCaller);
|
||||
if (game != null)
|
||||
{
|
||||
|
Reference in New Issue
Block a user