Hold Tilt

This commit is contained in:
SoulliesOfficial
2025-06-01 07:41:55 -04:00
parent df7abdb320
commit 61e6ac3a32
13 changed files with 16677 additions and 16674 deletions

View File

@@ -1,209 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using System;
using DG.Tweening.Core.Easing;
using UnityEngine;
using Ichni.Editor;
using Ichni.RhythmGame.Beatmap;
namespace Ichni.RhythmGame
{
public class CameraAngleOffset : AnimationBase
{
public List<Hold> holds => Hold.holdingHoldList;
public TransformSubmodule camtransformSubmodule;
public FlexibleBool enabling;
public float MaxxOffset;
public float MaxyOffset;
public float MaxzOffset;
public float TimeToMax;
public AnimationCurveType animationCurveType;
public float xCurrentValue;
public float yCurrentValue;
public float zCurrentValue;
bool xOffseting;
bool yOffseting;
bool zOffseting;
float xOffsetStartTime = 0f;
float yOffsetStartTime = 0f;
float zOffsetStartTime = 0f;
protected override void UpdateAnimation(float songTime)
{
if (enabling == null)
return;
enabling.UpdateFlexibleBool(songTime);
if (enabling.value && Hold.holdingHoldList.Count != 0)
{
animationReturnType = FlexibleReturnType.MiddleExecuting;
float xValue = 0;
float yValue = 0;
float zValue = 0;
if (Hold.holdingHoldList != null)
{
foreach (Hold i in Hold.holdingHoldList)
{
xValue += i.noteScreenPosition.x - (Screen.width / 2);
yValue += i.noteScreenPosition.y - (Screen.width / 2);
}
}
zValue = xValue;
JudgeState(ref xCurrentValue, xValue, ref xOffseting, ref xOffsetStartTime, songTime);
JudgeState(ref yCurrentValue, yValue, ref yOffseting, ref yOffsetStartTime, songTime);
JudgeState(ref zCurrentValue, zValue, ref zOffseting, ref zOffsetStartTime, songTime);
// 平滑过渡到目标值
if (xOffseting)
{
xCurrentValue = UpdateOffset(xOffsetStartTime, ref xOffseting, songTime, MaxxOffset);
}
if (yOffseting)
{
yCurrentValue = UpdateOffset(yOffsetStartTime, ref yOffseting, songTime, MaxyOffset);
}
if (zOffseting)
{
zCurrentValue = UpdateOffset(zOffsetStartTime, ref zOffseting, songTime, MaxzOffset);
}
if (camtransformSubmodule != null && camtransformSubmodule.eulerAnglesOffset != null)
{
Vector3 currentEulerAngles = new(xCurrentValue, yCurrentValue, zCurrentValue);
camtransformSubmodule.eulerAnglesOffset.Add(currentEulerAngles);
camtransformSubmodule.eulerAnglesDirtyMark = true;
}
}
else
{
animationReturnType = FlexibleReturnType.MiddleInterval;
}
}
public override void SetDefaultSubmodules()
{
timeDurationSubmodule = new TimeDurationSubmodule(this);
}
private void JudgeState(ref float CurrentValue, float Value, ref bool Offseting, ref float OffsetStartTime, float songtime)
{
if (Mathf.Abs(Value - CurrentValue) >= 1)
{
Offseting = true;
OffsetStartTime = songtime;
}
else
{
CurrentValue = Value;
}
}
private float UpdateOffset(float OffsetStartTime, ref bool Offseting, float songTime, float MaxOffset)
{
if (OffsetStartTime + TimeToMax < songTime)
{
Offseting = false;
return MaxOffset;
}
float denominator = songTime + TimeToMax - OffsetStartTime;
if (Mathf.Approximately(denominator, 0f))
return 0f;
float Value = AnimationCurveEvaluator.Evaluate(animationCurveType, (songTime - OffsetStartTime) / denominator);
return Value * MaxOffset;
}
public static CameraAngleOffset GenerateElement(
string elementName, Guid id, List<string> tags, bool isFirstGenerated, GameElement animatedObject,
FlexibleBool enabling, float maxX, float maxY, float maxZ, float timeToMax, AnimationCurveType curveType)
{
CameraAngleOffset offset = Instantiate(EditorManager.instance.basePrefabs.emptyObject)
.AddComponent<CameraAngleOffset>();
offset.Initialize(elementName, id, tags, isFirstGenerated, animatedObject);
offset.animatedObject = animatedObject;
var submoduleHolder = animatedObject as IHaveTransformSubmodule;
if (submoduleHolder != null)
offset.camtransformSubmodule = EditorManager.instance.cameraManager.gameCamera.transformSubmodule;
offset.enabling = enabling;
offset.MaxxOffset = maxX;
offset.MaxyOffset = maxY;
offset.MaxzOffset = maxZ;
offset.TimeToMax = timeToMax;
offset.animationCurveType = curveType;
offset.animationReturnType = FlexibleReturnType.Before;
return offset;
}
public override void SaveBM()
{
matchedBM = new CameraAngleOffset_BM(
elementName, elementGuid, tags, parentElement.matchedBM as GameElement_BM,
enabling.ConvertToBM(), MaxxOffset, MaxyOffset, MaxzOffset, TimeToMax, animationCurveType
);
}
public override void SetUpInspector()
{
base.SetUpInspector();
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
var container = inspector.GenerateContainer("CameraAngleOffset");
var subcontainer = container.GenerateSubcontainer(5);
inspector.GenerateButton(this, subcontainer, "Enabling", () =>
{
inspector.GenerateCompositeParameterWindow(this, "Enabling", nameof(enabling)).SetAsFlexibleBool();
});
inspector.GenerateInputField(this, subcontainer, "Max X Offset", nameof(MaxxOffset));
inspector.GenerateInputField(this, subcontainer, "Max Y Offset", nameof(MaxyOffset));
inspector.GenerateInputField(this, subcontainer, "Max Z Offset", nameof(MaxzOffset));
inspector.GenerateInputField(this, subcontainer, "Time To Max", nameof(TimeToMax));
inspector.GenerateDropdown(this, subcontainer, "Offset Ease", typeof(AnimationCurveType), nameof(animationCurveType));
}
}
// BM类
namespace Beatmap
{
public class CameraAngleOffset_BM : AnimationBase_BM
{
public FlexibleBool_BM enabling;
public float maxX, maxY, maxZ, timeToMax;
public AnimationCurveType animationCurveType;
public CameraAngleOffset_BM() { }
public CameraAngleOffset_BM(string elementName, Guid elementGuid, List<string> tags, GameElement_BM attachedElement,
FlexibleBool_BM enabling, float maxX, float maxY, float maxZ, float timeToMax, AnimationCurveType curveType)
: base(elementName, elementGuid, tags, attachedElement)
{
this.enabling = enabling;
this.maxX = maxX;
this.maxY = maxY;
this.maxZ = maxZ;
this.timeToMax = timeToMax;
this.animationCurveType = curveType;
}
public override void ExecuteBM()
{
matchedElement = CameraAngleOffset.GenerateElement(
elementName, elementGuid, tags, false, GetElement(attachedElementGuid),
enabling.ConvertToGameType(), maxX, maxY, maxZ, timeToMax, animationCurveType
);
}
public override GameElement DuplicateBM(GameElement parent)
{
return CameraAngleOffset.GenerateElement(
elementName, Guid.NewGuid(), tags, false, parent,
enabling.ConvertToGameType(), maxX, maxY, maxZ, timeToMax, animationCurveType
);
}
}
}
}

View File

@@ -30,6 +30,7 @@ namespace Ichni.RhythmGame
{
effectCollection.Add("Generate", new List<EffectBase>());
effectCollection.Add("GeneralJudge", new List<EffectBase>());
effectCollection.Add("StartHold", new List<EffectBase>());
effectCollection.Add("Holding", new List<EffectBase>()); //仅用于Hold
effectCollection.Add("Perfect", new List<EffectBase>());
effectCollection.Add("Good", new List<EffectBase>());
@@ -322,6 +323,15 @@ namespace Ichni.RhythmGame
}
/// <summary>
/// 如果效果被打断主要对于Holding Effect则触发这个方法
/// </summary>
public virtual void Disrupt()
{
}
/// <summary>
/// 转换为存档类
/// </summary>

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5f85e090ad2708745a8fd3b9c254ad3a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,56 @@
using System.Collections;
using System.Collections.Generic;
using Lean.Pool;
using UnityEngine;
using UnityEngine.Events;
namespace Ichni
{
public class PooledObject : MonoBehaviour, IPoolable
{
public UnityAction onSpawn;
public UnityAction onDespawn;
public void OnSpawn()
{
onSpawn?.Invoke();
}
public void OnDespawn()
{
onDespawn?.Invoke();
}
public void SetOnSpawn(UnityAction action, bool isOverride = false, bool isAdditive = false)
{
if (isOverride)
{
onSpawn = action;
}
else if (isAdditive)
{
onSpawn += action;
}
else
{
onSpawn ??= action;
}
}
public void SetOnDespawn(UnityAction action, bool isOverride = false, bool isAdditive = false)
{
if (isOverride)
{
onDespawn = action;
}
else if (isAdditive)
{
onDespawn += action;
}
else
{
onDespawn ??= action;
}
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 1ec2771618afafe489a6cd3de5b20bc1
guid: d6ba0e915cddec743a29f85a93f12594
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -79,23 +79,6 @@ namespace Ichni.RhythmGame
var extensionButton = inspector.GenerateButton(this, generateAnimation, "Extension",
() => GameCameraExtension.GenerateElement("New Extension", Guid.NewGuid(),
new List<string>(), true, this, 1000f));
if (childElementList.Where(i => i is CameraAngleOffset).Count() == 0)
{
var CAOButton = inspector.GenerateButton(this, generateAnimation, "CameraAngleOffset",
() => CameraAngleOffset.GenerateElement(
"New CameraAngleOffset",
Guid.NewGuid(),
new List<string>(),
true,
this,
new FlexibleBool(),
0f,
0f,
0f,
0f,
AnimationCurveType.Linear
));
}
}
}

View File

@@ -1,5 +1,6 @@
using System.Collections;
using System.Collections.Generic;
using DG.Tweening;
using Ichni.Editor;
using Ichni.RhythmGame.Beatmap;
using Lean.Pool;
@@ -26,6 +27,8 @@ namespace Ichni.RhythmGame
public float zPeak;
public AnimationCurve tiltCurveZ;
public List<MMF_Player> avtiveEffectExecutors;
public CameraTiltEffect(bool haveXTilt, float xDuration, float xPeak, AnimationCurve tiltCurveX,
bool haveYTilt, float yDuration, float yPeak, AnimationCurve tiltCurveY,
bool haveZTilt, float zDuration, float zPeak, AnimationCurve tiltCurveZ)
@@ -46,6 +49,8 @@ namespace Ichni.RhythmGame
this.zDuration = zDuration;
this.zPeak = zPeak;
this.tiltCurveZ = tiltCurveZ;
avtiveEffectExecutors = new List<MMF_Player>();
}
public override void Adjust()
@@ -66,6 +71,8 @@ namespace Ichni.RhythmGame
effect.GetFeedbackOfType<MMF_Rotation>().AnimateZ = false;
effect.GetFeedbackOfType<MMF_Rotation>().RemapCurveOne = xPeak;
effect.GetFeedbackOfType<MMF_Rotation>().AnimateRotationX = tiltCurveX;
effect.GetComponent<PooledObject>().SetOnDespawn(() => avtiveEffectExecutors.Remove(effect));
avtiveEffectExecutors.Add(effect);
effect.PlayFeedbacks();
LeanPool.Despawn(effect.gameObject, xDuration);
}
@@ -80,6 +87,8 @@ namespace Ichni.RhythmGame
effect.GetFeedbackOfType<MMF_Rotation>().AnimateZ = false;
effect.GetFeedbackOfType<MMF_Rotation>().RemapCurveOne = yPeak;
effect.GetFeedbackOfType<MMF_Rotation>().AnimateRotationY = tiltCurveY;
effect.GetComponent<PooledObject>().SetOnDespawn(() => avtiveEffectExecutors.Remove(effect));
avtiveEffectExecutors.Add(effect);
effect.PlayFeedbacks();
LeanPool.Despawn(effect.gameObject, yDuration);
}
@@ -94,6 +103,8 @@ namespace Ichni.RhythmGame
effect.GetFeedbackOfType<MMF_Rotation>().AnimateY = false;
effect.GetFeedbackOfType<MMF_Rotation>().RemapCurveOne = zPeak;
effect.GetFeedbackOfType<MMF_Rotation>().AnimateRotationZ = tiltCurveZ;
effect.GetComponent<PooledObject>().SetOnDespawn(() => avtiveEffectExecutors.Remove(effect));
avtiveEffectExecutors.Add(effect);
effect.PlayFeedbacks();
LeanPool.Despawn(effect.gameObject, zDuration);
}
@@ -106,7 +117,22 @@ namespace Ichni.RhythmGame
haveYTilt, yDuration, yPeak, tiltCurveY,
haveZTilt, zDuration, zPeak, tiltCurveZ);
}
public override void Disrupt()
{
foreach (MMF_Player executor in avtiveEffectExecutors)
{
executor.StopFeedbacks();
}
for (var i = 0; i < avtiveEffectExecutors.Count; i++)
{
LeanPool.Despawn(avtiveEffectExecutors[i].gameObject);
}
EditorManager.instance.cameraManager.gameCamera.gameCamera.transform.DOLocalRotate(Vector3.zero, 0.2f);
}
public override void SetUpInspector()
{
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;

View File

@@ -0,0 +1,24 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Ichni.RhythmGame
{
public class CameraTiltHoldingEffect : MonoBehaviour
{
public bool haveXTilt;
public float xDuration;
public float xPeak;
public AnimationCurve tiltCurveX;
public bool haveYTilt;
public float yDuration;
public float yPeak;
public AnimationCurve tiltCurveY;
public bool haveZTilt;
public float zDuration;
public float zPeak;
public AnimationCurve tiltCurveZ;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ffad603e762bde64ab5f9e4688f3fde1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -7,6 +7,7 @@ using Ichni.Editor;
using Ichni.RhythmGame.Beatmap;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.InputSystem;
namespace Ichni.RhythmGame
{
@@ -135,6 +136,15 @@ namespace Ichni.RhythmGame
{
protected override void Update()
{
if (Keyboard.current.hKey.wasPressedThisFrame)
{
foreach (KeyValuePair<string,List<EffectBase>> effect in noteVisual.effectSubmodule.effectCollection)
{
effect.Value.ForEach(x => x.Disrupt());
}
}
if (holdEndTime < exactJudgeTime)
{
LogWindow.Log("Hold end time is earlier than exact judge time.", Color.red);
@@ -193,9 +203,11 @@ namespace Ichni.RhythmGame
if (noteVisual != null)
{
noteVisual.effectSubmodule.effectCollection["Generate"].ForEach(e => e.UpdateEffect(exactJudgeTime));
noteVisual.effectSubmodule.effectCollection["GeneralJudge"].ForEach(e => e.UpdateEffect(holdEndTime));
noteVisual.effectSubmodule.effectCollection["StartHold"].ForEach(e => e.UpdateEffect(exactJudgeTime));
noteVisual.effectSubmodule.effectCollection["Holding"].ForEach(e => e.UpdateEffect(exactJudgeTime));
noteVisual.effectSubmodule.effectCollection["GeneralJudge"].ForEach(e => e.UpdateEffect(holdEndTime));
switch (EditorManager.instance.currentJudgeType)
{
case NoteJudgeType.Perfect:
@@ -211,6 +223,8 @@ namespace Ichni.RhythmGame
noteVisual.effectSubmodule.effectCollection["Miss"].ForEach(e => e.UpdateEffect(holdEndTime));
break;
}
noteVisual.effectSubmodule.effectCollection["AfterJudge"].ForEach(e => e.UpdateEffect(holdEndTime));
if (EditorManager.instance.cameraManager.haveGameCamera)
{