基础内容

必要插件安装
缓动曲线和动画基础
ElementFolder,Track与其次级模块,PathNode重构
This commit is contained in:
SoulliesOfficial
2025-01-26 21:10:16 -05:00
parent 40f63dd2bd
commit 8d0abec75f
9320 changed files with 2950357 additions and 0 deletions

BIN
Assets/Scripts/.DS_Store vendored Normal file

Binary file not shown.

View File

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

View File

@@ -0,0 +1,24 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Unity.Mathematics;
using UnityEngine;
namespace Ichni.RhythmGame
{
public abstract class AnimationBase : BaseElement
{
public BaseElement targetObject;
public FlexibleReturnType animationReturnType;
//public ICanHaveAnimation target;
public void NewInitialize(string elementName, BaseElement targetObject)
{
base.NewInitialize(elementName);
this.targetObject = targetObject;
SetParent(this.targetObject);
}
public abstract void UpdateAnimation(float songTime);
}
}

View File

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

View File

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

View File

@@ -0,0 +1,114 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Dreamteck.Splines;
using Lean.Pool;
using Unity.Mathematics;
using Unity.VisualScripting;
using UnityEngine;
namespace Ichni.RhythmGame
{
public class Displacement : AnimationBase
{
public TransformSubmodule targetTransformSubmodule;
public FlexibleFloat positionX, positionY, positionZ;
public static Displacement GenerateElement(string elementName, BaseElement targetObject,
FlexibleFloat positionX, FlexibleFloat positionY, FlexibleFloat positionZ)
{
Displacement displacement = LeanPool.Spawn(new GameObject()).AddComponent<Displacement>();//TODO: 替换 new GameObject();
displacement.NewInitialize(elementName, targetObject);
displacement.positionX = positionX;
displacement.positionY = positionY;
displacement.positionZ = positionZ;
displacement.animationReturnType = FlexibleReturnType.Before;
if (targetObject.transformSubmodule != null)
{
displacement.targetTransformSubmodule = targetObject.transformSubmodule;
}
else
{
throw new System.Exception("Target object does not have a TransformSubmodule");
}
displacement.SetTimeDuration();
return displacement;
}
public override void SetTimeDuration()
{
positionX.Sort();
positionY.Sort();
positionZ.Sort();
List<float> startTimes = new List<float>();
List<float> endTimes = new List<float>();
if (positionX.animations.Count > 0)
{
startTimes.Add(positionX.animations[0].startTime);
endTimes.Add(positionX.animations[^1].endTime);
}
if (positionY.animations.Count > 0)
{
startTimes.Add(positionY.animations[0].startTime);
endTimes.Add(positionY.animations[^1].endTime);
}
if (positionZ.animations.Count > 0)
{
startTimes.Add(positionZ.animations[0].startTime);
endTimes.Add(positionZ.animations[^1].endTime);
}
float startTime = startTimes.Min();
float endTime = endTimes.Max();
timeDurationSubmodule = new TimeDurationSubmodule(startTime, endTime);
}
protected void Update()
{
if (timeDurationSubmodule.CheckTimeInDuration(EditorManager.instance.songModule.songTime))
{
UpdateAnimation(EditorManager.instance.songModule.songTime);
}
}
public override void UpdateAnimation(float songTime)
{
positionX.UpdateFlexibleFloat(songTime);
positionY.UpdateFlexibleFloat(songTime);
positionZ.UpdateFlexibleFloat(songTime);
if (positionX.returnType is FlexibleReturnType.MiddleExecuting ||
positionY.returnType is FlexibleReturnType.MiddleExecuting ||
positionZ.returnType is FlexibleReturnType.MiddleExecuting)
{
animationReturnType = FlexibleReturnType.MiddleExecuting;
Vector3 currentPosition = new Vector3(positionX.value, positionY.value, positionZ.value);
targetTransformSubmodule.positionOffset.Add(currentPosition);
targetTransformSubmodule.positionDirtyMark = true;
}
//本体使用,用于判断动画是否结束
// else if (positionX.returnType is FlexibleReturnType.After or FlexibleReturnType.None &&
// positionY.returnType is FlexibleReturnType.After or FlexibleReturnType.None &&
// positionZ.returnType is FlexibleReturnType.After or FlexibleReturnType.None)
// {
// animationReturnType = FlexibleReturnType.After;
// float3 currentPosition = new float3(positionX.value, positionY.value, positionZ.value);
// targetTransformSubmodule.positionOffset.Add(currentPosition);
// targetTransformSubmodule.positionDirtyMark = true;
// Destroy(gameObject);
// }
}
}
}

View File

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

8
Assets/Scripts/Base.meta Normal file
View File

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

View File

@@ -0,0 +1,93 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Sirenix.OdinInspector;
using UnityEngine;
namespace Ichni.RhythmGame
{
[System.Serializable]
public abstract partial class BaseElement : SerializedMonoBehaviour
{
//物体名
public string elementName;
//序列号
public int serialNumber;
//标识 GUID
public Guid elementGuid;
//存档
//public BaseElement_BM matchedBM;
//父游戏物体
public BaseElement parentElement;
//子物体列表
public List<BaseElement> childElementList = new List<BaseElement>();
//次级模块
public TimeDurationSubmodule timeDurationSubmodule;
public TransformSubmodule transformSubmodule;
/// <summary>
/// 首次初始化
/// </summary>
/// <param name="name">物体名</param>
public virtual void NewInitialize(string name)
{
this.elementName = name;
this.elementGuid = Guid.NewGuid();
//GameManager.beatMapContainer.beatMapElementList.Add(this);
//serialNumber = totalSerialNumber++;
}
/// <summary>
/// 在所有物体生成完毕后,执行的初始化方法
/// </summary>
public virtual void AfterInitialize()
{
}
/// <summary>
/// 刷新物体的状态
/// </summary>
public virtual void Refresh()
{
}
/// <summary>
/// 设置父物体
/// </summary>
/// <param name="parentElement">父物体</param>
public void SetParent(BaseElement parentElement)
{
if (parentElement != null)
{
parentElement.childElementList.Add(this);
this.parentElement = parentElement;
transform.SetParent(parentElement.transform);
}
}
}
public abstract partial class BaseElement
{
public virtual void SetTimeDuration()
{
}
[Button("Apply Time Duration From Child")]
public void ApplyTimeDuration()
{
childElementList.ForEach(x => x.ApplyTimeDuration());
timeDurationSubmodule?.SetDurationFromChildren(childElementList.Select(x=>x.timeDurationSubmodule).ToList());
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,397 @@
using UnityEngine;
using System;
using System.Collections.Generic;
namespace Ichni
{
public enum AnimationCurveType //预设动画曲线类型
{
Linear = 0,
InQuad = 1,
OutQuad = 2,
InOutQuad = 3,
InCubic = 4,
OutCubic = 5,
InOutCubic = 6,
InQuart = 7,
OutQuart = 8,
InOutQuart = 9,
InQuint = 10,
OutQuint = 11,
InOutQuint = 12,
InSine = 13,
OutSine = 14,
InOutSine = 15,
InExpo = 16,
OutExpo = 17,
InOutExpo = 18,
InCirc = 19,
OutCirc = 20,
InOutCirc = 21,
InBounce = 22,
OutBounce = 23,
InOutBounce = 24,
InElastic = 25,
OutElastic = 26,
InOutElastic = 27,
InBack = 28,
OutBack = 29,
InOutBack = 30
}
public static class AnimationCurveEvaluator
{
public static float Evaluate(AnimationCurveType animationCurveType, float t)
{
t = Mathf.Clamp(t, 0, 1);
switch (animationCurveType)
{
case AnimationCurveType.Linear:
return Linear(0, 1, t);
case AnimationCurveType.InQuad:
return InQuad(0, 1, t);
case AnimationCurveType.OutQuad:
return OutQuad(0, 1, t);
case AnimationCurveType.InOutQuad:
return InOutQuad(0, 1, t);
case AnimationCurveType.InCubic:
return InCubic(0, 1, t);
case AnimationCurveType.OutCubic:
return OutCubic(0, 1, t);
case AnimationCurveType.InOutCubic:
return InOutCubic(0, 1, t);
case AnimationCurveType.InQuart:
return InQuart(0, 1, t);
case AnimationCurveType.OutQuart:
return OutQuart(0, 1, t);
case AnimationCurveType.InOutQuart:
return InOutQuart(0, 1, t);
case AnimationCurveType.InQuint:
return InQuint(0, 1, t);
case AnimationCurveType.OutQuint:
return OutQuint(0, 1, t);
case AnimationCurveType.InOutQuint:
return InOutQuint(0, 1, t);
case AnimationCurveType.InSine:
return InSine(0, 1, t);
case AnimationCurveType.OutSine:
return OutSine(0, 1, t);
case AnimationCurveType.InOutSine:
return InOutSine(0, 1, t);
case AnimationCurveType.InExpo:
return InExpo(0, 1, t);
case AnimationCurveType.OutExpo:
return OutExpo(0, 1, t);
case AnimationCurveType.InOutExpo:
return InOutExpo(0, 1, t);
case AnimationCurveType.InCirc:
return InCirc(0, 1, t);
case AnimationCurveType.OutCirc:
return OutCirc(0, 1, t);
case AnimationCurveType.InOutCirc:
return InOutCirc(0, 1, t);
case AnimationCurveType.InBounce:
return InBounce(0, 1, t);
case AnimationCurveType.OutBounce:
return OutBounce(0, 1, t);
case AnimationCurveType.InOutBounce:
return InOutBounce(0, 1, t);
case AnimationCurveType.InElastic:
return InElastic(0, 1, t);
case AnimationCurveType.OutElastic:
return OutElastic(0, 1, t);
case AnimationCurveType.InOutElastic:
return InOutElastic(0, 1, t);
case AnimationCurveType.InBack:
return InBack(0, 1, t);
case AnimationCurveType.OutBack:
return OutBack(0, 1, t);
case AnimationCurveType.InOutBack:
return InOutBack(0, 1, t);
}
throw new NotImplementedException($"Animation curve type {animationCurveType} is not implemented.");
}
#region 线
private static float Linear(float from, float to, float t)
{
float c = to - from;
t /= 1f;
return c * t / 1f + from;
}
private static float InQuad(float from, float to, float t)
{
float c = to - from;
t /= 1f;
return c * t * t + from;
}
private static float OutQuad(float from, float to, float t)
{
float c = to - from;
t /= 1f;
return -c * t * (t - 2f) + from;
}
private static float InOutQuad(float from, float to, float t)
{
float c = to - from;
t /= 0.5f;
if (t < 1) return c / 2f * t * t + from;
t--;
return -c / 2f * (t * (t - 2) - 1) + from;
}
private static float InCubic(float from, float to, float t)
{
float c = to - from;
t /= 1f;
return c * t * t * t + from;
}
private static float OutCubic(float from, float to, float t)
{
float c = to - from;
t /= 1f;
t--;
return c * (t * t * t + 1) + from;
}
private static float InOutCubic(float from, float to, float t)
{
float c = to - from;
t /= 0.5f;
if (t < 1) return c / 2f * t * t * t + from;
t -= 2;
return c / 2f * (t * t * t + 2) + from;
}
private static float InQuart(float from, float to, float t)
{
float c = to - from;
t /= 1f;
return c * t * t * t * t + from;
}
private static float OutQuart(float from, float to, float t)
{
float c = to - from;
t /= 1f;
t--;
return -c * (t * t * t * t - 1) + from;
}
private static float InOutQuart(float from, float to, float t)
{
float c = to - from;
t /= 0.5f;
if (t < 1) return c / 2f * t * t * t * t + from;
t -= 2;
return -c / 2f * (t * t * t * t - 2) + from;
}
private static float InQuint(float from, float to, float t)
{
float c = to - from;
t /= 1f;
return c * t * t * t * t * t + from;
}
private static float OutQuint(float from, float to, float t)
{
float c = to - from;
t /= 1f;
t--;
return c * (t * t * t * t * t + 1) + from;
}
private static float InOutQuint(float from, float to, float t)
{
float c = to - from;
t /= 0.5f;
if (t < 1) return c / 2f * t * t * t * t * t + from;
t -= 2;
return c / 2f * (t * t * t * t * t + 2) + from;
}
private static float InSine(float from, float to, float t)
{
float c = to - from;
return -c * Mathf.Cos(t / 1f * (Mathf.PI / 2f)) + c + from;
}
private static float OutSine(float from, float to, float t)
{
float c = to - from;
return c * Mathf.Sin(t / 1f * (Mathf.PI / 2f)) + from;
}
private static float InOutSine(float from, float to, float t)
{
float c = to - from;
return -c / 2f * (Mathf.Cos(Mathf.PI * t / 1f) - 1) + from;
}
private static float InExpo(float from, float to, float t)
{
float c = to - from;
return c * Mathf.Pow(2, 10 * (t / 1f - 1)) + from;
}
private static float OutExpo(float from, float to, float t)
{
float c = to - from;
return c * (-Mathf.Pow(2, -10 * t / 1f) + 1) + from;
}
private static float InOutExpo(float from, float to, float t)
{
float c = to - from;
t /= 0.5f;
if (t < 1f) return c / 2f * Mathf.Pow(2, 10 * (t - 1)) + from;
t--;
return c / 2f * (-Mathf.Pow(2, -10 * t) + 2) + from;
}
private static float InCirc(float from, float to, float t)
{
float c = to - from;
t /= 1f;
return -c * (Mathf.Sqrt(1 - t * t) - 1) + from;
}
private static float OutCirc(float from, float to, float t)
{
float c = to - from;
t /= 1f;
t--;
return c * Mathf.Sqrt(1 - t * t) + from;
}
private static float InOutCirc(float from, float to, float t)
{
float c = to - from;
t /= 0.5f;
if (t < 1) return -c / 2f * (Mathf.Sqrt(1 - t * t) - 1) + from;
t -= 2;
return c / 2f * (Mathf.Sqrt(1 - t * t) + 1) + from;
}
private static float InBounce(float from, float to, float t)
{
float c = to - from;
return c - OutBounce(0f, c, 1f - t) + from; //does this work?
}
private static float OutBounce(float from, float to, float t)
{
float c = to - from;
if ((t /= 1f) < (1 / 2.75f))
{
return c * (7.5625f * t * t) + from;
}
else if (t < (2 / 2.75f))
{
return c * (7.5625f * (t -= (1.5f / 2.75f)) * t + .75f) + from;
}
else if (t < (2.5 / 2.75))
{
return c * (7.5625f * (t -= (2.25f / 2.75f)) * t + .9375f) + from;
}
else
{
return c * (7.5625f * (t -= (2.625f / 2.75f)) * t + .984375f) + from;
}
}
private static float InOutBounce(float from, float to, float t)
{
float c = to - from;
if (t < 0.5f) return InBounce(0, c, t * 2f) * 0.5f + from;
return OutBounce(0, c, t * 2 - 1) * 0.5f + c * 0.5f + from;
}
private static float InElastic(float from, float to, float t)
{
float c = to - from;
if (t == 0) return from;
if ((t /= 1f) == 1) return from + c;
float p = 0.3f;
float s = p / 4f;
return -(c * Mathf.Pow(2, 10 * (t -= 1)) * Mathf.Sin((t - s) * (2 * Mathf.PI) / p)) + from;
}
private static float OutElastic(float from, float to, float t)
{
float c = to - from;
if (t == 0) return from;
if ((t /= 1f) == 1) return from + c;
float p = 0.3f;
float s = p / 4f;
return (c * Mathf.Pow(2, -10 * t) * Mathf.Sin((t - s) * (2 * Mathf.PI) / p) + c + from);
}
private static float InOutElastic(float from, float to, float t)
{
float c = to - from;
if (t == 0) return from;
if ((t /= 0.5f) == 2) return from + c;
float p = 0.3f * 1.5f;
float s = p / 4f;
if (t < 1)
return -0.5f * (c * Mathf.Pow(2, 10 * (t -= 1f)) * Mathf.Sin((t - 2) * (2 * Mathf.PI) / p)) + from;
return c * Mathf.Pow(2, -10 * (t -= 1)) * Mathf.Sin((t - s) * (2f * Mathf.PI) / p) * 0.5f + c + from;
}
private static float InBack(float from, float to, float t)
{
float c = to - from;
float s = 1.70158f;
t /= 0.5f;
return c * t * t * ((s + 1) * t - s) + from;
}
private static float OutBack(float from, float to, float t)
{
float c = to - from;
float s = 1.70158f;
t = t / 1f - 1f;
return c * (t * t * ((s + 1) * t + s) + 1) + from;
}
private static float InOutBack(float from, float to, float t)
{
float c = to - from;
float s = 1.70158f;
t /= 0.5f;
if (t < 1) return c / 2f * (t * t * (((s *= (1.525f)) + 1) * t - s)) + from;
t -= 2;
return c / 2f * (t * t * (((s *= (1.525f)) + 1) * t + s) + 2) + from;
}
#endregion
}
// [System.Serializable]
// public class PresetAnimationCurve
// {
// public AnimationCurveType animationCurveType; //动画曲线类型
//
// public PresetAnimationCurve(AnimationCurveType animationCurveType)
// {
// this.animationCurveType = animationCurveType;
// }
//
// /// <summary>
// /// 根据Type选择曲线并计算t点(0,1)时曲线的值若t越界则直接返回0或1
// /// </summary>
// public float Evaluate(float t)
// {
// return AnimationCurveEvaluator.Evaluate(this.animationCurveType, t);
// }
// }
}

View File

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

View File

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

View File

@@ -0,0 +1,234 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using UnityEngine;
using UnityEngine.Serialization;
namespace Ichni
{
[System.Serializable]
public class AnimatedFloat : IComparable<AnimatedFloat>
{
public float startValue, endValue; //起止值
public float differenceValue; //差值
public float startTime, endTime; //起止时间
public float totalTime; //总时间
public AnimationCurveType animationCurveType; //动画曲线类型
public AnimatedFloat(float startTime, float endTime, float startValue, float endValue, AnimationCurveType animationCurveType)
{
this.startValue = startValue;
this.endValue = endValue;
this.startTime = startTime;
this.endTime = endTime;
totalTime = endTime - startTime;
differenceValue = endValue - startValue;
this.animationCurveType = animationCurveType;
}
/// <summary>
/// 按照起始时间排序
/// </summary>
public int CompareTo(AnimatedFloat obj)
{
return startTime.CompareTo(obj.startTime);
}
}
[System.Serializable]
public class FlexibleFloat
{
public float value;
public int currentAnimationIndex;
public List<AnimatedFloat> animations;
public FlexibleReturnType returnType;
public FlexibleFloat()
{
animations = new List<AnimatedFloat>();
}
public FlexibleFloat(List<AnimatedFloat> anim)
{
animations = anim;
}
public void Add(AnimatedFloat animatedFloat)
{
animations.Add(animatedFloat);
}
public void Sort()
{
animations.Sort();
}
/// <summary>
/// 在动画脚本的Update中更新value
/// </summary>
/// <param name="歌曲时间"></param>
public void UpdateFlexibleFloat(float nowTime)
{
AnimatedFloat nowAnimatedFloat = GetAnimatedFloat(nowTime); //获取当前时间点对应的AnimatedFloat
if (nowAnimatedFloat != null) //如果能获取到,表明当前时间点存在动画
{
//获取songTime时间点时基于动画曲线的AnimatedFloat比例点->01
float nowPercent = AnimationCurveEvaluator.Evaluate(nowAnimatedFloat.animationCurveType, (nowTime - nowAnimatedFloat.startTime) / nowAnimatedFloat.totalTime);
value = nowAnimatedFloat.startValue + nowPercent * nowAnimatedFloat.differenceValue; //计算value
returnType = FlexibleReturnType.MiddleExecuting;
return;
}
if (animations.Count > 0) //如果当前时间点没有动画
{
float finalStartTime = animations[0].startTime;
float finalEndTime = animations[animations.Count - 1].endTime;
if (nowTime < finalStartTime) //如果当前时间小于第一个动画的开始时间
{
nowAnimatedFloat = animations[0];
//nowPercent = 0;
if (nowAnimatedFloat != null)
{
value = nowAnimatedFloat.startValue;
}
returnType = FlexibleReturnType.Before;
return;
}
if (nowTime > finalEndTime) //如果当前时间大于最后一个动画的结束时间
{
nowAnimatedFloat = animations[animations.Count - 1];
//nowPercent = 1;
if (nowAnimatedFloat != null)
{
value = nowAnimatedFloat.endValue;
}
returnType = FlexibleReturnType.After;
return;
}
if (currentAnimationIndex >= 0)//如果当前时间点在动画之间
{
value = animations[currentAnimationIndex].endValue;
}
returnType = FlexibleReturnType.MiddleInterval;
return;
}
//如果没有动画
value = 0;
returnType = FlexibleReturnType.None;
return;
}
/// <summary>
/// 获取songTime对应的AnimatedFloat的时间段
/// </summary>
/// <param name="歌曲时间"></param>
/// <returns></returns>
AnimatedFloat GetAnimatedFloat(float nowTime)
{
for (int i = 0; i < animations.Count; i++)
{
if (nowTime >= animations[i].startTime && nowTime <= animations[i].endTime)
{
currentAnimationIndex = i;
return animations[i];
}
}
return null;
}
// [BurstCompile]
// public void UpdateFlexibleFloat(float nowTime)
// {
// var animationData = new NativeArray<AnimatedFloat>(animations.ToArray(), Allocator.TempJob);
// var result = new NativeArray<float>(1, Allocator.TempJob);
// var returnTypeResult = new NativeArray<FlexibleReturnType>(1, Allocator.TempJob);
//
// var job = new FlexibleFloatJob
// {
// nowTime = nowTime,
// animationData = animationData,
// result = result,
// returnTypeResult = returnTypeResult
// };
//
// var handle = job.Schedule();
// handle.Complete();
//
// value = result[0];
// returnType = returnTypeResult[0];
//
// animationData.Dispose();
// result.Dispose();
// returnTypeResult.Dispose();
// }
}
/*
[BurstCompile]
struct FlexibleFloatJob : IJob
{
public float nowTime;
[ReadOnly] public NativeArray<AnimatedFloat> animationData;
public NativeArray<float> result;
public NativeArray<FlexibleReturnType> returnTypeResult;
public void Execute()
{
FlexibleReturnType currentReturnType = FlexibleReturnType.None;
float outputValue = 0;
if(animationData.Length == 0)
{
result[0] = outputValue;
returnTypeResult[0] = currentReturnType;
return;
}
if (nowTime < animationData[0].startTime)
{
outputValue = animationData[0].startValue;
currentReturnType = FlexibleReturnType.Before;
}
else if (nowTime > animationData[animationData.Length - 1].endTime)
{
outputValue = animationData[animationData.Length - 1].endValue;
currentReturnType = FlexibleReturnType.After;
}
for (int i = 0; i < animationData.Length; i++)
{
var anim = animationData[i];
if (nowTime >= anim.startTime && nowTime <= anim.endTime)
{
float nowPercent = AnimationCurveEvaluator.Evaluate(anim.animationCurveType,(nowTime - anim.startTime) / anim.totalTime);
outputValue = anim.startValue + nowPercent * anim.differenceValue;
currentReturnType = FlexibleReturnType.MiddleExecuting;
break;
}
}
if (currentReturnType == FlexibleReturnType.None)
{
currentReturnType = FlexibleReturnType.MiddleInterval;
}
result[0] = outputValue;
returnTypeResult[0] = currentReturnType;
}
}*/
}

View File

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

View File

@@ -0,0 +1,15 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Ichni
{
public enum FlexibleReturnType
{
None = -1, //没动画
Before = 0, //动画开始之前
MiddleExecuting = 1, //动画中间,正在运动
MiddleInterval = 2, //动画中间,在停顿空隙
After = 3 //动画结束后
}
}

View File

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

View File

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

View File

@@ -0,0 +1,41 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Ichni.RhythmGame
{
public class ColorSubmodule : SubmoduleBase
{
public Color originalBaseColor;
public bool emissionEnabled;
public Color originalEmissionColor;
public float originalEmissionIntensity;
public List<Color> baseColorOffset = new List<Color>();
public List<Color> emissionColorOffset = new List<Color>();
public List<float> emissionIntensityOffset = new List<float>();
public Color currentBaseColor;
public Color currentEmissionColor;
public float currentEmissionIntensity;
public bool baseColorDirtyMark;
public bool emissionColorDirtyMark;
public ColorSubmodule(Color originalBaseColor)
{
this.originalBaseColor = originalBaseColor;
this.emissionEnabled = false;
this.originalEmissionColor = Color.black;
this.originalEmissionIntensity = 0;
}
public ColorSubmodule(Color originalBaseColor, bool emissionEnabled, Color originalEmissionColor, float originalEmissionIntensity)
{
this.originalBaseColor = originalBaseColor;
this.emissionEnabled = emissionEnabled;
this.originalEmissionColor = originalEmissionColor;
this.originalEmissionIntensity = originalEmissionIntensity;
}
}
}

View File

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

View File

@@ -0,0 +1,14 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Ichni.RhythmGame
{
public abstract class SubmoduleBase
{
public virtual void InitialRefresh()
{
}
}
}

View File

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

View File

@@ -0,0 +1,48 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Unity.Mathematics;
using UnityEngine;
namespace Ichni.RhythmGame
{
public class TimeDurationSubmodule: SubmoduleBase
{
public float startTime, endTime; //起止时间
public TimeDurationSubmodule()
{
startTime = 0;
endTime = 0;
}
public TimeDurationSubmodule(float startTime, float endTime)
{
this.startTime = startTime;
this.endTime = endTime;
}
public bool CheckTimeInDuration(float time, float offset = 0.2f)
{
return time >= startTime - offset && time <= endTime + offset;
}
public void SetDurationFromChildren(List<TimeDurationSubmodule> children)
{
List<float2> durations = new List<float2>();
if (children.Count == 0)
{
return;
}
foreach (var child in children)
{
durations.Add(new float2(child.startTime, child.endTime));
}
startTime = durations.Min(duration => duration.x);
endTime = durations.Max(duration => duration.y);
}
}
}

View File

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

View File

@@ -0,0 +1,68 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UniRx;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Events;
namespace Ichni.RhythmGame
{
public class TransformSubmodule : SubmoduleBase
{
public Vector3 originalPosition;
public Vector3 originalEulerAngles;
public Vector3 originalScale;
public List<Vector3> positionOffset;
public List<Vector3> eulerAnglesOffset;
public List<Vector3> scaleOffset;
public Vector3 currentPosition;
public Vector3 currentEulerAngles;
public Vector3 currentScale;
public bool positionDirtyMark;
public bool eulerAnglesDirtyMark;
public bool scaleDirtyMark;
public UnityAction OnPositionChanged;
public UnityAction OnEulerAnglesChanged;
public UnityAction OnScaleChanged;
public TransformSubmodule(Vector3 originalPosition, Vector3 originalEulerAngles, Vector3 originalScale)
{
this.originalPosition = originalPosition;
this.originalEulerAngles = originalEulerAngles;
this.originalScale = originalScale;
positionOffset = new List<Vector3>();
eulerAnglesOffset = new List<Vector3>();
scaleOffset = new List<Vector3>();
currentPosition = originalPosition;
currentEulerAngles = originalEulerAngles;
currentScale = originalScale;
positionDirtyMark = false;
eulerAnglesDirtyMark = false;
scaleDirtyMark = false;
}
public void SetObserver(BaseElement target)
{
Observable.EveryUpdate().Subscribe(_ =>
{
if (positionDirtyMark)
{
Vector3 offset = Vector3.zero;
foreach (var positionOffset in positionOffset)
{
offset += positionOffset;
}
currentPosition = originalPosition + offset;
positionDirtyMark = false;
}
positionOffset.Clear();
}).AddTo(target);
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,14 @@
using System.Collections;
using System.Collections.Generic;
using Sirenix.OdinInspector;
using UnityEngine;
[CreateAssetMenu(fileName = "BasePrefabsCollection", menuName = "Ichni/BasePrefabsCollection", order = 0)]
public class BasePrefabsCollection : SerializedScriptableObject
{
[Title("基础预制体")]
public GameObject emptyObject;
public GameObject elementFolder;
public GameObject track;
public GameObject pathNode;
}

View File

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

View File

@@ -0,0 +1,44 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Ichni.RhythmGame;
using Sirenix.OdinInspector;
using UnityEngine;
namespace Ichni
{
public class EditorManager : SerializedMonoBehaviour
{
public static EditorManager instance;
public SongModule songModule;
public BasePrefabsCollection basePrefabs;
private void Awake()
{
instance = this;
}
private void Start()
{
var f0 = ElementFolder.GenerateElement("Folder", null);
var t0 = Track.GenerateElement("Track", f0, Vector3.left * 5f);
t0.trackPathSubmodule = new TrackPathSubmodule();
t0.trackTimeSubmodule = new TrackTimeSubmoduleMovable();
t0.trackRendererSubmodule = new TrackRendererSubmoduleAutoOrient();
(t0.trackPathSubmodule).NewInitialize(t0, false, Track.TrackSpaceType.Linear, Track.TrackSamplingType.TimeDistributed);
(t0.trackTimeSubmodule as TrackTimeSubmoduleMovable).NewInitialize(t0, 0, 1, 1, AnimationCurveType.Linear);
(t0.trackRendererSubmodule as TrackRendererSubmoduleAutoOrient).NewInitialize(t0);
var p0 = PathNode.GeneratePathNode("PathNode-0", t0, 0, Vector3.zero, Vector3.forward, 1, Color.white);
var p1 = PathNode.GeneratePathNode("PathNode-1", t0, 1, Vector3.one * 10f, Vector3.left, 0, Color.white);
t0.AfterInitialize();
}
}
public class SongModule
{
public float songTime;
public float songBeat;
}
}

View File

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

View File

@@ -0,0 +1,232 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Networking;
namespace Ichni
{
public class ThemeBundleManager : MonoBehaviour
{
public static ThemeBundleManager instance;
public List<ThemeBundleAbstract> themeBundleAbstractList;
public List<string> selectedThemeBundleList;
public List<ThemeBundle> loadedThemeBundleList;
public int waitingBundleAmount;
private void Awake()
{
instance = this;
}
private void Start()
{
loadedThemeBundleList = new List<ThemeBundle>();
AssetBundle.UnloadAllAssetBundles(true);
LoadAllThemeBundlesAbstract();
//DontDestroyOnLoad(gameObject);
LoadThemeBundle("fundamental");
}
public T GetObject<T>(string themeBundleName, string objectName) where T : class
{
return loadedThemeBundleList.Find(bundle => bundle.themeBundleName == themeBundleName)?.GetObject<T>(objectName);
}
public void LoadThemeBundles(List<string> list)
{
foreach (var bundle in list)
{
LoadThemeBundle(bundle);
}
}
public void LoadAllThemeBundlesAbstract()
{
string uri = Application.streamingAssetsPath + "/ThemeBundles";
if (ES3.DirectoryExists(uri))
{
List<string> allFileList = ES3.GetFiles(uri).ToList();
List<string> absList = new List<string>();
foreach (var abs in allFileList)
{
if (abs.EndsWith(".abs"))
{
absList.Add(abs);
}
}
foreach (var abs in absList)
{
ES3Settings settings = new ES3Settings(uri + "/" + abs, ES3.EncryptionType.None);
themeBundleAbstractList.Add(ES3.Load<ThemeBundleAbstract>("ThemeBundleAbstract", settings));
}
}
}
public void LoadThemeBundle(string themeBundleName)
{
waitingBundleAmount++;
StartCoroutine(LoadThemeBundleCoroutine(themeBundleName));
}
IEnumerator LoadThemeBundleCoroutine(string themeBundleName)
{
string uri = "";
if (Application.platform == RuntimePlatform.WindowsEditor ||
Application.platform == RuntimePlatform.WindowsPlayer)
{
uri = Application.streamingAssetsPath + "/ThemeBundles/Windows64/" + themeBundleName;
}else if (Application.platform == RuntimePlatform.OSXEditor ||
Application.platform == RuntimePlatform.OSXPlayer)
{
uri = Application.streamingAssetsPath + "/ThemeBundles/OSX/" + themeBundleName;
}
UnityWebRequest request = UnityWebRequestAssetBundle.GetAssetBundle(uri, 0);
yield return request.SendWebRequest();
AssetBundle bundle = AssetBundle.LoadFromFile(uri);
Object[] ob = bundle.LoadAllAssets<Object>();
loadedThemeBundleList.Add(new ThemeBundle(themeBundleName));
for (int i = 0; i < ob.Length; i++)
{
if (ob[i].GetType() == typeof(GameObject))
{
loadedThemeBundleList[^1].assetList_GameObject.Add(ob[i] as GameObject);
}
else if (ob[i].GetType() == typeof(Material))
{
loadedThemeBundleList[^1].assetList_Material.Add(ob[i] as Material);
}
else if (ob[i].GetType() == typeof(Texture2D))
{
loadedThemeBundleList[^1].assetList_Texture.Add(ob[i] as Texture2D);
}
else
{
loadedThemeBundleList[^1].assetList_Other.Add(ob[i]);
}
}
yield return new WaitForEndOfFrame();
waitingBundleAmount--;
}
}
[System.Serializable]
public class ThemeBundleAbstract
{
public string themeBundleName;
public List<string> tags;
public string iconPath;
public ThemeBundleAbstract()
{
}
public ThemeBundleAbstract(string themeBundleName, List<string> tags, string iconPath)
{
this.themeBundleName = themeBundleName;
this.tags = tags;
this.iconPath = iconPath;
}
public Texture2D GetIcon()
{
string uri = Application.streamingAssetsPath + "/" + iconPath;
if (ES3.FileExists(uri))
{
Texture2D sp = ES3.LoadImage(uri);
return sp;
}
return null;
}
}
[System.Serializable]
public class ThemeBundle
{
public string themeBundleName;
public List<GameObject> assetList_GameObject;
public List<Material> assetList_Material;
public List<Texture2D> assetList_Texture;
public List<Object> assetList_Other;
public ThemeBundle(string name)
{
themeBundleName = name;
assetList_GameObject = new List<GameObject>();
assetList_Material = new List<Material>();
assetList_Texture = new List<Texture2D>();
assetList_Other = new List<Object>();
}
public T GetObject<T>(string name)
{
if (name == "None")
{
return default(T);
}
T[] assetList;
if (typeof(T) == typeof(GameObject))
{
assetList = assetList_GameObject.ToArray() as T[];
for (int i = 0; i < assetList_GameObject.Count; i++)
{
if (name == assetList_GameObject[i].name)
{
return assetList[i];
}
}
}
else if (typeof(T) == typeof(Material))
{
assetList = assetList_Material.ToArray() as T[];
for (int i = 0; i < assetList_Material.Count; i++)
{
if (name == assetList_Material[i].name)
{
return assetList[i];
}
}
}
else if (typeof(T) == typeof(Texture2D))
{
assetList = assetList_Texture.ToArray() as T[];
for (int i = 0; i < assetList_Texture.Count; i++)
{
if (name == assetList_Texture[i].name)
{
return assetList[i];
}
}
}
else
{
assetList = assetList_Other.ToArray() as T[];
for (int i = 0; i < assetList_Other.Count; i++)
{
if (name == assetList_Other[i].name)
{
return assetList[i];
}
}
}
return default(T);
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,18 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BeatmapContainer : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}

View File

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

View File

@@ -0,0 +1,18 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CommandScriptContainer : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}

View File

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

View File

@@ -0,0 +1,18 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ProjectInformation : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}

View File

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

View File

@@ -0,0 +1,18 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SongInformation : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}

View File

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

View File

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

View File

@@ -0,0 +1,23 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Lean.Pool;
using UnityEngine;
namespace Ichni.RhythmGame
{
public class ElementFolder : BaseElement
{
public static ElementFolder GenerateElement(string name, BaseElement parentElement)
{
ElementFolder elementFolder = LeanPool.Spawn(EditorManager.instance.basePrefabs.elementFolder).GetComponent<ElementFolder>();
elementFolder.NewInitialize(name);
elementFolder.SetParent(parentElement);
//elementFolder.GenerateTab(parentElement);
return elementFolder;
}
}
}

View File

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

View File

@@ -0,0 +1,71 @@
using System.Collections;
using System.Collections.Generic;
using Dreamteck.Splines;
using Ichni;
using Lean.Pool;
using UnityEngine;
namespace Ichni.RhythmGame
{
public partial class PathNode : BaseElement
{
public ColorSubmodule colorSubmodule;
public Track track;
public int index;
public SplinePoint node;
public static PathNode GeneratePathNode(string elementName, Track track, int index, Vector3 nodePosition,
Vector3 nodeNormal, float nodeSize, Color nodeColor)
{
PathNode pathNode = LeanPool.Spawn(EditorManager.instance.basePrefabs.pathNode, track.transform).GetComponent<PathNode>();
pathNode.NewInitialize(elementName, track, index, nodePosition, nodeNormal, nodeSize, nodeColor);
track.trackPathSubmodule.pathNodeList.Add(pathNode);
pathNode.SetParent(track);
return pathNode;
}
public void NewInitialize(string elementName, Track track, int index, Vector3 nodePosition,
Vector3 nodeNormal, float nodeSize, Color nodeColor)
{
base.NewInitialize(elementName);
this.track = track;
this.index = index;
if (track.trackPathSubmodule.pathNodeList.Count > index)
{
this.index = track.trackPathSubmodule.pathNodeList.Count;
}
this.transformSubmodule = new TransformSubmodule(nodePosition, Quaternion.LookRotation(nodeNormal, Vector3.up).eulerAngles, Vector3.one * nodeSize);
this.colorSubmodule = new ColorSubmodule(nodeColor);
Refresh();
}
public override void AfterInitialize()
{
Refresh();
if (track.trackPathSubmodule.pathNodeList.Count > 3)
{
track.trackPathSubmodule.ClosePath(track.trackPathSubmodule.isClosed);
}
}
}
public partial class PathNode
{
public override void Refresh()
{
Vector3 position = transformSubmodule.currentPosition;
Vector3 normal = Quaternion.Euler(transformSubmodule.currentEulerAngles) * Vector3.up;
float size = transformSubmodule.currentScale.x;
Color color = colorSubmodule.currentBaseColor;
node = new SplinePoint(position, Vector3.up, normal, size, color);
track.trackPathSubmodule.SetPathNode(this);
}
}
}

View File

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

View File

@@ -0,0 +1,23 @@
using System.Collections;
using System.Collections.Generic;
using Lean.Pool;
using UnityEngine;
namespace Ichni.RhythmGame
{
public class SubstantialObject : BaseElement
{
public string themeBundleName, objectName;
public static SubstantialObject GenerateElement(string elementName, string themeBundleName,
string objectName, Vector3 position, Vector3 eulerAngles, Vector3 scale, BaseElement parent,
bool isFirstGenerated = true)
{
GameObject themeBundleObject = ThemeBundleManager.instance.GetObject<GameObject>(themeBundleName, objectName);
SubstantialObject substantialObject = LeanPool.Spawn(themeBundleObject, parent.transform).GetComponent<SubstantialObject>();
substantialObject.NewInitialize(elementName);
return substantialObject;
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,79 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Lean.Pool;
using UnityEngine;
namespace Ichni.RhythmGame
{
public partial class Track : BaseElement
{
public TrackPathSubmodule trackPathSubmodule;
public TrackTimeSubmodule trackTimeSubmodule;
public TrackRendererSubmodule trackRendererSubmodule;
public static Track GenerateElement(string elementName, BaseElement parent, Vector3 position)
{
if (parent == null)
{
throw new System.Exception("Parent is null");
}
Track track = LeanPool.Spawn(EditorManager.instance.basePrefabs.track, parent.transform).GetComponent<Track>();
track.NewInitialize(elementName, position);
track.SetParent(parent);
return track;
}
private void NewInitialize(string elementName, Vector3 position)
{
base.NewInitialize(elementName);
timeDurationSubmodule = new TimeDurationSubmodule();
transformSubmodule = new TransformSubmodule(position, Vector3.zero, Vector3.one);
trackPathSubmodule = null;
trackTimeSubmodule = null;
trackRendererSubmodule = null;
Refresh();
}
public override void AfterInitialize()
{
trackPathSubmodule.path.RebuildImmediate();
//Refresh();
}
private void Update()
{
if (timeDurationSubmodule.CheckTimeInDuration(EditorManager.instance.songModule.songTime))
{
(trackTimeSubmodule as TrackTimeSubmoduleMovable)?.UpdateTrackPart();
}
}
}
public partial class Track
{
public override void Refresh()
{
transform.localPosition = transformSubmodule.currentPosition;
}
}
public partial class Track
{
public enum TrackSpaceType
{
CatmullRom = 0,
BSpline = 1,
Linear = 3
}
public enum TrackSamplingType
{
TimeDistributed = 0,
DistanceDistributed = 1
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,74 @@
using System.Collections;
using System.Collections.Generic;
using Dreamteck.Splines;
using Unity.VisualScripting;
using UnityEngine;
namespace Ichni.RhythmGame
{
public partial class TrackPathSubmodule : TrackSubmodule
{
public SplineComputer path;
public List<PathNode> pathNodeList;
public Track.TrackSpaceType trackSpaceType;
public Track.TrackSamplingType trackSamplingType;
public bool isClosed;
public void NewInitialize(Track track, bool isClosed, Track.TrackSpaceType trackSpaceType,
Track.TrackSamplingType trackSamplingType)
{
this.track = track;
this.path = track.AddComponent<SplineComputer>();
track.trackPathSubmodule = this;
this.pathNodeList = new List<PathNode>();
this.trackSpaceType = trackSpaceType;
this.trackSamplingType = trackSamplingType;
this.isClosed = isClosed;
SetUpSplineComputer(trackSpaceType, trackSamplingType);
}
}
public partial class TrackPathSubmodule
{
private void SetUpSplineComputer(Track.TrackSpaceType trackSpaceType, Track.TrackSamplingType trackSamplingType)
{
path.type = (Spline.Type)(int)trackSpaceType;
path.sampleMode = (SplineComputer.SampleMode)(int)trackSamplingType;
path.space = SplineComputer.Space.Local;
}
public void ClosePath(bool close)
{
if (close)
{
path.Close();
}
else
{
path.Break();
}
isClosed = close;
}
public void SetTrackSpaceType(int spaceType)
{
trackSpaceType = (Track.TrackSpaceType)spaceType;
path.type = (Spline.Type)spaceType;
}
public void AddPathNode(PathNode point)
{
path.SetPoint(pathNodeList.Count, point.node, SplineComputer.Space.Local);
pathNodeList.Add(point);
}
public void SetPathNode(PathNode point)
{
path.SetPoint(point.index, point.node, SplineComputer.Space.Local);
}
}
}

View File

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

View File

@@ -0,0 +1,42 @@
using System.Collections;
using System.Collections.Generic;
using Dreamteck.Splines;
using Unity.VisualScripting;
using UnityEngine;
namespace Ichni.RhythmGame
{
public class TrackRendererSubmodule : TrackSubmodule
{
public Track track;
public MeshGenerator meshGenerator;
public MeshRenderer meshRenderer;
}
public class TrackRendererSubmoduleAutoOrient : TrackRendererSubmodule
{
public SplineRenderer splineRenderer;
public void NewInitialize(Track track)
{
this.track = track;
this.track.trackRendererSubmodule = this;
this.splineRenderer = track.AddComponent<SplineRenderer>();
this.meshGenerator = splineRenderer;
this.splineRenderer.spline = track.trackPathSubmodule.path;
this.splineRenderer.clipFrom = 0;
this.splineRenderer.clipTo = 1;
Debug.Log(splineRenderer.clipFrom + " " + splineRenderer.clipTo);
this.splineRenderer.color = Color.white;
}
public override void InitialRefresh()
{
if (track.trackTimeSubmodule is TrackTimeSubmoduleMovable)
{
splineRenderer.clipFrom = 0;
splineRenderer.clipTo = 0;
}
}
}
}

View File

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

View File

@@ -0,0 +1,12 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Ichni.RhythmGame
{
public abstract class TrackSubmodule : SubmoduleBase
{
public Track track;
public bool isUpdating;
}
}

View File

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

View File

@@ -0,0 +1,76 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Ichni.RhythmGame
{
public class TrackTimeSubmodule : TrackSubmodule
{
public float headPercent, tailPercent;
}
public class TrackTimeSubmoduleMovable : TrackTimeSubmodule
{
public float trackStartTime;
public float trackEndTime;
public float trackTotalTime;
public float visibleTrackTimeLength;
public AnimationCurveType animationCurveType;
public void NewInitialize(Track track, float trackStartTime, float trackEndTime,
float visibleTrackTimeLength, AnimationCurveType animationCurveType)
{
this.track = track;
this.track.trackTimeSubmodule = this;
this.trackStartTime = trackStartTime;
this.trackEndTime = trackEndTime;
this.trackTotalTime = trackEndTime - trackStartTime;
this.visibleTrackTimeLength = visibleTrackTimeLength;
this.animationCurveType = animationCurveType;
track.timeDurationSubmodule.startTime = trackStartTime;
track.timeDurationSubmodule.endTime = trackEndTime + visibleTrackTimeLength;
}
public void UpdateTrackPart()
{
float songTime = EditorManager.instance.songModule.songTime;
headPercent = GetTrackPercent(songTime);
tailPercent = GetTrackPercent(songTime - visibleTrackTimeLength);
Debug.Log("Head: " + headPercent + " Tail: " + tailPercent);
if (track.trackRendererSubmodule != null)
{
track.trackRendererSubmodule.meshGenerator.clipFrom = tailPercent;
track.trackRendererSubmodule.meshGenerator.clipTo = headPercent;
}
}
private float GetTrackPercent(float songTimeInTime)
{
float per = AnimationCurveEvaluator.Evaluate(animationCurveType, (songTimeInTime - trackStartTime) / trackTotalTime);
return Mathf.Clamp01(per);
}
}
public class TrackTimeSubmoduleStatic : TrackTimeSubmodule
{
public float trackTotalTime;
public AnimationCurveType animationCurveType;
public void NewInitialize(Track track, float trackTotalTime, AnimationCurveType animationCurveType)
{
this.track = track;
this.track.trackTimeSubmodule = this;
this.trackTotalTime = trackTotalTime;
this.animationCurveType = animationCurveType;
this.headPercent = 0;
this.tailPercent = 1;
track.timeDurationSubmodule.startTime = 0;
track.timeDurationSubmodule.endTime = 0;
//timeDurationSubmodule 根据下辖Note的时间来设置
}
}
}

View File

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