mirror of
https://github.com/RHeavenStudio/HeavenStudio.git
synced 2025-06-13 00:57:36 +02:00
Chameleon (#868)
* wip * wip2 * wip2 * Far * upscale * wip * close * fix lerp * bg * wip3 * resizable
This commit is contained in:
8
Assets/Scripts/Games/Chameleon.meta
Normal file
8
Assets/Scripts/Games/Chameleon.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: da337eb792108b247ad5917f68d408f2
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
313
Assets/Scripts/Games/Chameleon/Chameleon.cs
Normal file
313
Assets/Scripts/Games/Chameleon/Chameleon.cs
Normal file
@ -0,0 +1,313 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
using HeavenStudio.Util;
|
||||
using HeavenStudio.InputSystem;
|
||||
|
||||
using Jukebox;
|
||||
|
||||
namespace HeavenStudio.Games.Loaders
|
||||
{
|
||||
using static Minigames;
|
||||
public static class RvlChameleonLoader
|
||||
{
|
||||
public static Minigame AddGame(EventCaller eventCaller)
|
||||
{
|
||||
return new Minigame("chameleon", "Chameleon", "ffffff", false, false, new List<GameAction>()
|
||||
{
|
||||
new GameAction("far", "Far Fly")
|
||||
{
|
||||
preFunction = delegate {
|
||||
var e = eventCaller.currentEntity;
|
||||
Chameleon.PreSpawnFly(e.beat, e.length - 4, (int)Scripts_Chameleon.FlyType.Far);
|
||||
if (e["countIn"]) Chameleon.CountIn(e.beat);
|
||||
},
|
||||
defaultLength = 8f,
|
||||
resizable = true,
|
||||
parameters = new List<Param>()
|
||||
{
|
||||
new Param("countIn", false, "Count-In"),
|
||||
}
|
||||
},
|
||||
new GameAction("close", "Close Fly")
|
||||
{
|
||||
preFunction = delegate {
|
||||
var e = eventCaller.currentEntity;
|
||||
Chameleon.PreSpawnFly(e.beat, e.length - 4, (int)Scripts_Chameleon.FlyType.Close);
|
||||
if (e["countIn"]) Chameleon.CountIn(e.beat);
|
||||
},
|
||||
defaultLength = 8f,
|
||||
resizable = true,
|
||||
parameters = new List<Param>()
|
||||
{
|
||||
new Param("countIn", false, "Count-In"),
|
||||
}
|
||||
},
|
||||
new GameAction("background appearance", "Background Appearance")
|
||||
{
|
||||
function = delegate {
|
||||
var e = eventCaller.currentEntity;
|
||||
Chameleon.instance.BackgroundColorSet(e.beat, e.length, e["colorBG1Start"], e["colorBG1End"], e["colorBG2Start"], e["colorBG2End"], e["ease"]);
|
||||
},
|
||||
defaultLength = 0.5f,
|
||||
resizable = true,
|
||||
parameters = new List<Param>()
|
||||
{
|
||||
new Param("colorBG1Start", new Color(0.204f, 0.385f, 0.064f), "Start BG Color", "Set top-most color of the background gradient at the start of the event."),
|
||||
new Param("colorBG1End", new Color(0.204f, 0.385f, 0.064f), "End BG Color", "Set top-most color of the background gradient at the end of the event."),
|
||||
new Param("colorBG2Start", new Color(0.07f, 0.133f, 0.02f), "Start BG Color", "Set bottom-most color of the background gradient at the start of the event."),
|
||||
new Param("colorBG2End", new Color(0.07f, 0.133f, 0.02f), "End BG Color", "Set bottom-most color of the background gradient at the end of the event."),
|
||||
new Param("ease", Util.EasingFunction.Ease.Instant, "Ease", "Set the easing of the action."),
|
||||
}
|
||||
},
|
||||
new GameAction("modifiers", "Modifiers")
|
||||
{
|
||||
function = delegate {
|
||||
var e = eventCaller.currentEntity;
|
||||
Chameleon.Modifiers(e["enableCrown"]);
|
||||
},
|
||||
defaultLength = 0.5f,
|
||||
parameters = new List<Param>()
|
||||
{
|
||||
new Param("enableCrown", true, "Enable Crown", "Toggle if the crown should appear or not."),
|
||||
},
|
||||
},
|
||||
},
|
||||
new List<string>() { "rvl", "normal" },
|
||||
"rvlchameleon", "en",
|
||||
new List<string>() {},
|
||||
chronologicalSortKey: 107
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace HeavenStudio.Games
|
||||
{
|
||||
using Scripts_Chameleon;
|
||||
public class Chameleon : Minigame
|
||||
{
|
||||
public Transform baseFly;
|
||||
public Animator chameleonAnim;
|
||||
[SerializeField] Transform chameleonEye;
|
||||
[SerializeField] GameObject Crown;
|
||||
[System.NonSerialized] public Fly currentFly;
|
||||
|
||||
[SerializeField] SpriteRenderer gradient;
|
||||
[SerializeField] SpriteRenderer bgHigh;
|
||||
[SerializeField] SpriteRenderer bgLow;
|
||||
|
||||
private ColorEase[] colorEases = new ColorEase[2];
|
||||
|
||||
static bool enableCrown;
|
||||
|
||||
float moveEyeTimeThreshold = 0.01f;
|
||||
float moveEyeTime = 0;
|
||||
|
||||
private struct QueuedFly
|
||||
{
|
||||
public double beat;
|
||||
public double length;
|
||||
public int type;
|
||||
}
|
||||
private static List<QueuedFly> queuedFlys = new();
|
||||
|
||||
public static Chameleon instance;
|
||||
|
||||
const int IAAltDownCat = IAMAXCAT;
|
||||
|
||||
protected static bool IA_TouchLeft(out double dt)
|
||||
{
|
||||
bool want = PlayerInput.GetTouchDown(InputController.ActionsTouch.Left, out dt);
|
||||
bool simul = false;
|
||||
return want || simul;
|
||||
}
|
||||
|
||||
protected static bool IA_PadAltPress(out double dt)
|
||||
{
|
||||
return PlayerInput.GetPadDown(InputController.ActionsPad.South, out dt);
|
||||
}
|
||||
protected static bool IA_BatonAltPress(out double dt)
|
||||
{
|
||||
return PlayerInput.GetSqueezeDown(out dt);
|
||||
}
|
||||
protected static bool IA_TouchRight(out double dt)
|
||||
{
|
||||
bool want = PlayerInput.GetTouchDown(InputController.ActionsTouch.Right, out dt);
|
||||
bool simul = false;
|
||||
return want || simul;
|
||||
}
|
||||
|
||||
public static PlayerInput.InputAction InputAction_Close =
|
||||
new("RvlChameleonClose", new int[] { IAPressCat, IAPressCat, IAPressCat },
|
||||
IA_PadBasicPress, IA_TouchLeft, IA_BatonBasicPress);
|
||||
|
||||
public static PlayerInput.InputAction InputAction_Far =
|
||||
new("RvlChameleonFar", new int[] { IAAltDownCat, IAAltDownCat, IAAltDownCat },
|
||||
IA_PadAltPress, IA_TouchRight, IA_BatonAltPress);
|
||||
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
instance = this;
|
||||
|
||||
Crown.SetActive(enableCrown);
|
||||
colorEases = new ColorEase[] {
|
||||
new(new Color(0.204f, 0.385f, 0.064f)),
|
||||
new(new Color(0.07f, 0.133f, 0.02f)),
|
||||
};
|
||||
}
|
||||
|
||||
public override void OnPlay(double beat)
|
||||
{
|
||||
if (queuedFlys.Count > 0) queuedFlys.Clear();
|
||||
foreach (var evt in scheduledInputs)
|
||||
{
|
||||
evt.Disable();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (queuedFlys.Count > 0) queuedFlys.Clear();
|
||||
foreach (var evt in scheduledInputs)
|
||||
{
|
||||
evt.Disable();
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
var cond = Conductor.instance;
|
||||
if (cond.isPlaying && !cond.isPaused)
|
||||
{
|
||||
if (queuedFlys.Count > 0)
|
||||
{
|
||||
foreach (var queuedFly in queuedFlys)
|
||||
{
|
||||
SpawnFly(queuedFly.beat, queuedFly.length, queuedFly.type);
|
||||
}
|
||||
queuedFlys.Clear();
|
||||
}
|
||||
if (PlayerInput.GetIsAction(InputAction_Close) && !IsExpectingInputNow(InputAction_Close))
|
||||
{
|
||||
chameleonAnim.DoScaledAnimationAsync("tongueClose", 0.5f);
|
||||
SoundByte.PlayOneShotGame("chameleon/blankClose");
|
||||
}
|
||||
if (PlayerInput.GetIsAction(InputAction_Far) && !IsExpectingInputNow(InputAction_Far))
|
||||
{
|
||||
chameleonAnim.DoScaledAnimationAsync("tongueFar", 0.5f);
|
||||
SoundByte.PlayOneShotGame("chameleon/blankFar");
|
||||
}
|
||||
|
||||
moveEyeTime += Time.deltaTime;
|
||||
if (moveEyeTime >= moveEyeTimeThreshold)
|
||||
{
|
||||
moveEyeTime = 0;
|
||||
if (currentFly is not null)
|
||||
{
|
||||
if (cond.songPositionInBeatsAsDouble >= currentFly.startBeat + 1)
|
||||
{
|
||||
Vector3 relative = currentFly.gameObject.transform.InverseTransformPoint(chameleonEye.transform.position);
|
||||
float currentAngle = chameleonEye.eulerAngles.z;
|
||||
currentAngle = currentAngle > 180 ? currentAngle - 360 : currentAngle;
|
||||
float nextAngle = 165 + Mathf.Atan2(relative.y, relative.x) * Mathf.Rad2Deg;
|
||||
nextAngle = nextAngle > 180 ? nextAngle - 360 : nextAngle;
|
||||
if (currentFly.isFall)
|
||||
{
|
||||
nextAngle = currentFly.flyType switch {
|
||||
FlyType.Far => (nextAngle < -70) ? -70 : nextAngle,
|
||||
FlyType.Close => (nextAngle < -100) ? -100 : nextAngle,
|
||||
_ => throw new System.NotImplementedException()
|
||||
};
|
||||
}
|
||||
float angle = Mathf.Lerp(currentAngle, nextAngle, 0.05f);
|
||||
chameleonEye.eulerAngles = new Vector3(0, 0, angle);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float currentAngle = chameleonEye.eulerAngles.z;
|
||||
float angle = Mathf.LerpAngle(currentAngle, 15, 0.1f);
|
||||
chameleonEye.eulerAngles = new Vector3(0, 0, angle);
|
||||
}
|
||||
}
|
||||
|
||||
UpdateBackgroundColor();
|
||||
}
|
||||
}
|
||||
|
||||
public static void PreSpawnFly(double beat, double length, int type)
|
||||
{
|
||||
if (GameManager.instance.currentGame == "chameleon")
|
||||
{
|
||||
instance.SpawnFly(beat, length, type);
|
||||
}
|
||||
else
|
||||
{
|
||||
queuedFlys.Add(new QueuedFly
|
||||
{
|
||||
beat = beat,
|
||||
length = length,
|
||||
type = type,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void SpawnFly(double beat, double length, int type)
|
||||
{
|
||||
if (length <= 0) length = 4;
|
||||
var newFly = Instantiate(baseFly, transform).GetComponent<Fly>();
|
||||
newFly.startBeat = beat;
|
||||
newFly.lengthBeat = length;
|
||||
newFly.flyType = (FlyType)type;
|
||||
|
||||
BeatAction.New(instance, new List<BeatAction.Action>()
|
||||
{
|
||||
new BeatAction.Action(beat, delegate {
|
||||
currentFly = newFly;
|
||||
currentFly.gameObject.SetActive(true);
|
||||
currentFly.Init();
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
public static void CountIn(double beat)
|
||||
{
|
||||
MultiSound.Play(new MultiSound.Sound[]{
|
||||
new MultiSound.Sound("count-ins/three2", beat + 4),
|
||||
new MultiSound.Sound("count-ins/two2", beat + 5),
|
||||
new MultiSound.Sound("count-ins/one2", beat + 6),
|
||||
}, forcePlay: true, game: false);
|
||||
}
|
||||
|
||||
|
||||
public void BackgroundColorSet(double beat, float length, Color BG1Start, Color BG1End, Color BG2Start, Color BG2End, int colorEaseSet)
|
||||
{
|
||||
colorEases = new ColorEase[] {
|
||||
new(beat, length, BG1Start, BG1End, colorEaseSet),
|
||||
new(beat, length, BG2Start, BG2End, colorEaseSet),
|
||||
};
|
||||
|
||||
UpdateBackgroundColor();
|
||||
}
|
||||
private void UpdateBackgroundColor()
|
||||
{
|
||||
gradient.color = colorEases[0].GetColor();
|
||||
bgHigh.color = colorEases[0].GetColor();
|
||||
bgLow.color = colorEases[1].GetColor();
|
||||
}
|
||||
|
||||
public static void Modifiers(bool enableCrown)
|
||||
{
|
||||
Chameleon.enableCrown = enableCrown;
|
||||
|
||||
if (GameManager.instance.currentGame == "chameleon")
|
||||
Chameleon.instance.Crown.SetActive(enableCrown);
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Scripts/Games/Chameleon/Chameleon.cs.meta
Normal file
11
Assets/Scripts/Games/Chameleon/Chameleon.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e69a1f275f5a3384a8e889b234a3e511
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
199
Assets/Scripts/Games/Chameleon/Fly.cs
Normal file
199
Assets/Scripts/Games/Chameleon/Fly.cs
Normal file
@ -0,0 +1,199 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
using HeavenStudio.Util;
|
||||
|
||||
namespace HeavenStudio.Games.Scripts_Chameleon
|
||||
{
|
||||
public enum FlyType
|
||||
{
|
||||
Close,
|
||||
Far,
|
||||
}
|
||||
|
||||
public class Fly : MonoBehaviour
|
||||
{
|
||||
public double startBeat, lengthBeat;
|
||||
private double currentBeat;
|
||||
[System.NonSerialized] public FlyType flyType;
|
||||
[SerializeField] private Animator flyAnim, wingAnim;
|
||||
public bool isFall { get; private set; }
|
||||
|
||||
private Sound loopSound;
|
||||
|
||||
float randomAngle = 0;
|
||||
Vector2 moveCurrentPos, moveNextPos, moveEndPos;
|
||||
bool moveFast;
|
||||
|
||||
private Chameleon game;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
game = Chameleon.instance;
|
||||
|
||||
string typePrefix = flyType switch
|
||||
{
|
||||
FlyType.Far => "Far",
|
||||
FlyType.Close => "Close",
|
||||
_ => throw new System.NotImplementedException()
|
||||
};
|
||||
|
||||
moveCurrentPos = flyType switch
|
||||
{
|
||||
FlyType.Far => new Vector2(-4.5f, 5.4f),
|
||||
FlyType.Close => new Vector2(-6, 5.4f),
|
||||
_ => throw new System.NotImplementedException()
|
||||
};
|
||||
moveEndPos = flyType switch
|
||||
{
|
||||
FlyType.Far => new Vector2(5.15f, 1.6f),
|
||||
FlyType.Close => new Vector2(1.5f, -0.25f),
|
||||
_ => throw new System.NotImplementedException()
|
||||
};
|
||||
randomAngle = UnityEngine.Random.Range(0, 2 * Mathf.PI);
|
||||
moveNextPos = new Vector2(Mathf.Cos(randomAngle), Mathf.Sin(randomAngle)) + moveEndPos;
|
||||
|
||||
currentBeat = startBeat;
|
||||
|
||||
loopSound = SoundByte.PlayOneShotGame("chameleon/fly" + typePrefix + "Loop", -1, 1, 1, true);
|
||||
MultiSound.Play(
|
||||
new MultiSound.Sound[] {
|
||||
new MultiSound.Sound("chameleon/fly" + typePrefix + "1", startBeat + lengthBeat),
|
||||
new MultiSound.Sound("chameleon/fly" + typePrefix + "2", startBeat + lengthBeat + 1),
|
||||
new MultiSound.Sound("chameleon/fly" + typePrefix + "3", startBeat + lengthBeat + 2),
|
||||
}
|
||||
);
|
||||
|
||||
flyAnim.enabled = false;
|
||||
BeatAction.New(game, new List<BeatAction.Action>()
|
||||
{
|
||||
// new BeatAction.Action(startBeat, delegate {
|
||||
// var currentBeat = Conductor.instance.songPositionInBeatsAsDouble;
|
||||
// flyAnim.DoScaledAnimationAsync("move" + typePrefix, 0.5f, (float)((currentBeat - startBeat)/8));
|
||||
// }),
|
||||
new BeatAction.Action(startBeat + lengthBeat, delegate {
|
||||
loopSoundRelease();
|
||||
}),
|
||||
new BeatAction.Action(startBeat + lengthBeat + 3, delegate {
|
||||
if (!flyAnim.enabled)
|
||||
{
|
||||
flyAnim.enabled = true;
|
||||
flyAnim.DoScaledAnimationAsync("moveEnd" + typePrefix, 0.5f);
|
||||
}
|
||||
}),
|
||||
});
|
||||
|
||||
var InputAction = flyType switch
|
||||
{
|
||||
FlyType.Far => Chameleon.InputAction_Far,
|
||||
FlyType.Close => Chameleon.InputAction_Close,
|
||||
_ => throw new System.NotImplementedException()
|
||||
};
|
||||
game.ScheduleInput(startBeat + lengthBeat, 3, InputAction, JustCatch, MissCatch, Empty);
|
||||
|
||||
BeatAction.New(this, new List<BeatAction.Action>()
|
||||
{
|
||||
new BeatAction.Action(startBeat + lengthBeat + 6, delegate {
|
||||
Destroy();
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
var cond = Conductor.instance;
|
||||
if (cond.isPlaying && !cond.isPaused)
|
||||
{
|
||||
if (!flyAnim.enabled)
|
||||
{
|
||||
float startBeatPosition = Conductor.instance.GetPositionFromBeat(startBeat, 2, ignoreSwing: true);
|
||||
|
||||
if (startBeatPosition <= 1)
|
||||
{
|
||||
startBeatPosition = (startBeatPosition < 0.5) ? 0 : startBeatPosition * 2 - 1;
|
||||
transform.position = Vector2.Lerp(moveCurrentPos, moveNextPos, startBeatPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
float currentBeatPosition = Conductor.instance.GetPositionFromBeat(currentBeat, moveFast ? 0.17: 0.5, ignoreSwing: true);;
|
||||
if (currentBeatPosition > 1)
|
||||
{
|
||||
if (startBeatPosition > 1.5) moveFast = true;
|
||||
|
||||
currentBeat = cond.songPositionInBeatsAsDouble;
|
||||
moveCurrentPos = moveNextPos;
|
||||
randomAngle = randomAngle + UnityEngine.Random.Range(0.7f, 1.3f * Mathf.PI);
|
||||
moveNextPos = (moveFast ? 0.5f : 1) * new Vector2(Mathf.Cos(randomAngle), Mathf.Sin(randomAngle)) + moveEndPos;
|
||||
}
|
||||
else
|
||||
{
|
||||
float newPosX = EasingFunction.EaseInOutSine(moveCurrentPos.x, moveNextPos.x, currentBeatPosition);
|
||||
float newPosY = EasingFunction.EaseInOutSine(moveCurrentPos.y, moveNextPos.y, currentBeatPosition);
|
||||
transform.position = new Vector2(newPosX, newPosY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void JustCatch(PlayerActionEvent caller, float state)
|
||||
{
|
||||
string typePrefix = flyType switch
|
||||
{
|
||||
FlyType.Far => "Far",
|
||||
FlyType.Close => "Close",
|
||||
_ => throw new System.NotImplementedException()
|
||||
};
|
||||
flyAnim.enabled = true;
|
||||
game.chameleonAnim.DoScaledAnimationAsync("tongue" + typePrefix, 0.5f);
|
||||
if (state <= -1f || state >= 1f)
|
||||
{
|
||||
isFall = true;
|
||||
flyAnim.DoScaledAnimationAsync("fall" + typePrefix, 0.5f);
|
||||
return;
|
||||
}
|
||||
game.currentFly = null;
|
||||
SoundByte.PlayOneShotGame("chameleon/eatCatch");
|
||||
SoundByte.PlayOneShotGame("chameleon/eatGulp", startBeat + lengthBeat + 3.25);
|
||||
wingAnim.Play("idle", 0, 0);
|
||||
flyAnim.DoScaledAnimationAsync("catch" + typePrefix, 0.5f);
|
||||
BeatAction.New(game, new List<BeatAction.Action>()
|
||||
{
|
||||
new BeatAction.Action(startBeat + lengthBeat + 3.25, delegate {
|
||||
game.chameleonAnim.DoScaledAnimationAsync("gurp", 0.5f);
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
void MissCatch(PlayerActionEvent caller)
|
||||
{
|
||||
string typePrefix = flyType switch
|
||||
{
|
||||
FlyType.Far => "Far",
|
||||
FlyType.Close => "Close",
|
||||
_ => throw new System.NotImplementedException()
|
||||
};
|
||||
flyAnim.enabled = true;
|
||||
flyAnim.DoScaledAnimationAsync("gone" + typePrefix, 0.5f);
|
||||
}
|
||||
|
||||
void Empty(PlayerActionEvent caller) { }
|
||||
|
||||
private void loopSoundRelease()
|
||||
{
|
||||
if (loopSound != null)
|
||||
{
|
||||
loopSound.KillLoop(0);
|
||||
loopSound = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void Destroy()
|
||||
{
|
||||
if (game.currentFly == this) game.currentFly = null;
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Scripts/Games/Chameleon/Fly.cs.meta
Normal file
11
Assets/Scripts/Games/Chameleon/Fly.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3c9a2a52c2f6539469d21046c1320b64
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user