This commit is contained in:
2025-06-01 15:36:20 +08:00
parent 9d92c7c944
commit df7abdb320
18 changed files with 21599 additions and 19177 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,19 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 1726182683, guid: a4865f1ab4504ed8a368670db22f409c, type: 3}
m_Name: AOTGenerationConfig
m_EditorClassIdentifier:
automateBeforeBuilds: 0
deleteDllAfterBuilds: 1
AutomateForAllAOTPlatforms: 1
automateForPlatforms: 0900000014000000
supportSerializedTypes: []

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f2eba5830ab42344094ca15aadbbee0a
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,15 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 188390376, guid: a4865f1ab4504ed8a368670db22f409c, type: 3}
m_Name: ImportSettingsConfig
m_EditorClassIdentifier:
automateBeforeBuild: 1

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8396767207711744b8b53709007debcc
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,209 @@
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

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

View File

@@ -44,7 +44,7 @@ namespace Ichni.RhythmGame
eulerAngleY.UpdateFlexibleFloat(songTime);
eulerAngleZ.UpdateFlexibleFloat(songTime);
if ((eulerAngleX.returnType is FlexibleReturnType.MiddleExecuting || eulerAngleX.isSwitchingReturnType) ||
if (eulerAngleX.returnType is FlexibleReturnType.MiddleExecuting || eulerAngleX.isSwitchingReturnType ||
(eulerAngleY.returnType is FlexibleReturnType.MiddleExecuting || eulerAngleY.isSwitchingReturnType) ||
(eulerAngleZ.returnType is FlexibleReturnType.MiddleExecuting || eulerAngleZ.isSwitchingReturnType))
{

View File

@@ -15,7 +15,7 @@ namespace Ichni.RhythmGame
this.value = value;
this.time = time;
}
public void ApplyTimeOffset(float offset)
{
time += offset;
@@ -61,16 +61,20 @@ namespace Ichni.RhythmGame
/// <returns>返回距离当前时间最近的前一个AnimatedBool</returns>
AnimatedBool GetAnimatedBool(float nowTime)
{
if (animations.Count == 0)
{
return new AnimatedBool(0, false);
}
if (nowTime < animations[0].time)
{
return animations[0];
}
if (nowTime > animations[animations.Count - 1].time)
{
return animations[animations.Count - 1];
}
for (int i = 0; i < animations.Count; i++)
{
if (nowTime < animations[i].time)
@@ -81,7 +85,7 @@ namespace Ichni.RhythmGame
return new AnimatedBool(0, false);
}
/// <summary>
/// 转换为Beatmap存档类型
/// </summary>
@@ -89,7 +93,7 @@ namespace Ichni.RhythmGame
public FlexibleBool_BM ConvertToBM()
{
FlexibleBool_BM flexibleBool_BM = new FlexibleBool_BM();
foreach (AnimatedBool animatedBool in animations)
{
flexibleBool_BM.animatedBoolList.Add(new AnimatedBool(animatedBool.time, animatedBool.value));

View File

@@ -14,8 +14,8 @@ namespace Ichni.RhythmGame
public partial class EffectSubmodule : SubmoduleBase
{
public Dictionary<string, List<EffectBase>> effectCollection;
public EffectSubmodule(GameElement attachedGameElement, EffectSubmodulePreset preset = EffectSubmodulePreset.Default)
public EffectSubmodule(GameElement attachedGameElement, EffectSubmodulePreset preset = EffectSubmodulePreset.Default)
: base(attachedGameElement)
{
effectCollection = new Dictionary<string, List<EffectBase>>();
@@ -87,7 +87,7 @@ namespace Ichni.RhythmGame
{
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
Inspector inspectorMain = EditorManager.instance.uiManager.inspector;
foreach (var effect in effectCollection)
{
var container = inspector.GenerateContainer(effect.Key);
@@ -102,10 +102,10 @@ namespace Ichni.RhythmGame
}
else
{
LogWindow.Log("Effect Type not found.", Color.red);
LogWindow.Log("Effect Type not found.", Color.yellow);
}
});
foreach (var effectBase in effect.Value)
{
effectBase.SetUpInspector();
@@ -150,17 +150,17 @@ namespace Ichni.RhythmGame
public class EffectSubmodule_BM : Submodule_BM
{
public Dictionary<string, List<EffectBase_BM>> effectCollection;
public EffectSubmodule_BM()
{
}
public EffectSubmodule_BM(GameElement attachedElement) : base(attachedElement)
{
effectCollection = new Dictionary<string, List<EffectBase_BM>>();
IHaveEffectSubmodule element = attachedElement as IHaveEffectSubmodule;
foreach (var effect in element.effectSubmodule.effectCollection)
{
List<EffectBase_BM> effectList = new List<EffectBase_BM>();
@@ -177,7 +177,7 @@ namespace Ichni.RhythmGame
attachedElement = GameElement_BM.GetElement(attachedElementGuid);
(attachedElement as IHaveEffectSubmodule).effectSubmodule = new EffectSubmodule(attachedElement, effectCollection);
}
public override void DuplicateBM(GameElement attached)
{
(attached as IHaveEffectSubmodule).effectSubmodule = new EffectSubmodule(attached, effectCollection);
@@ -194,14 +194,14 @@ namespace Ichni.RhythmGame
After = 1,
Error = 100
}
public BaseElement_BM matchedBM { get; set; }
/// <summary>
/// 效果的持续时间如果为0则表示瞬间效果
/// </summary>
public float effectTime;
/// <summary>
/// 是否是瞬间效果
/// </summary>
@@ -211,7 +211,7 @@ namespace Ichni.RhythmGame
/// 效果的进度百分比
/// </summary>
public float effectProgressPercent;
/// <summary>
/// 效果当前的状态
/// </summary>
@@ -230,7 +230,7 @@ namespace Ichni.RhythmGame
this.nowEffectState = EffectState.Before;
this.effectProgressPercent = 0;
}
public void SaveBM()
{
throw new System.NotImplementedException();
@@ -240,7 +240,7 @@ namespace Ichni.RhythmGame
{
EffectState state = CheckEffectState(triggerTime);
float songTime = EditorManager.instance.songInformation.songTime;
if (state == EffectState.Before && nowEffectState != EffectState.Before)
{
nowEffectState = EffectState.Before;
@@ -253,7 +253,7 @@ namespace Ichni.RhythmGame
{
PreExecute();
}
nowEffectState = EffectState.Middle;
effectProgressPercent = (songTime - triggerTime) / effectTime;
Execute();
@@ -288,21 +288,21 @@ namespace Ichni.RhythmGame
return EffectState.Error;
}
/// <summary>
/// 当从Before状态进入Middle状态时仅在效果的开始时触发一次方法
/// </summary>
public virtual void PreExecute()
{
}
/// <summary>
/// 在效果的持续时间内,触发这个方法
/// </summary>
public virtual void Execute()
{
}
/// <summary>
@@ -311,7 +311,7 @@ namespace Ichni.RhythmGame
/// </summary>
public virtual void Adjust()
{
}
/// <summary>
@@ -319,7 +319,7 @@ namespace Ichni.RhythmGame
/// </summary>
public virtual void Recover()
{
}
/// <summary>
@@ -330,7 +330,7 @@ namespace Ichni.RhythmGame
public void Refresh()
{
}
public abstract void SetUpInspector();
@@ -341,17 +341,17 @@ namespace Ichni.RhythmGame
public abstract class EffectBase_BM
{
public float effectTime;
public EffectBase_BM()
{
}
public EffectBase_BM(float effectTime)
{
this.effectTime = effectTime;
}
/// <summary>
/// 转换为游戏类
/// </summary>

View File

@@ -75,10 +75,27 @@ namespace Ichni.RhythmGame
var swirlButton = inspector.GenerateButton(this, generateAnimation, "Swirl",
() => Swirl.GenerateElement("New Swirl", Guid.NewGuid(), new List<string>(), true, this,
new FlexibleFloat(), new FlexibleFloat(), new FlexibleFloat()));
var extensionButton = inspector.GenerateButton(this, generateAnimation, "Extension",
() => GameCameraExtension.GenerateElement("New Extension", Guid.NewGuid(),
() => 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
));
}
}
}
@@ -117,9 +134,9 @@ namespace Ichni.RhythmGame
{
return;
}
bool willRefresh = false;
if (transformSubmodule.eulerAnglesOffsetLock)
{
transform.localEulerAngles = transformSubmodule.originalEulerAngles;
@@ -151,15 +168,15 @@ namespace Ichni.RhythmGame
transformSubmodule.positionDirtyMark = false;
willRefresh = true;
}
if(willRefresh)
if (willRefresh)
{
this.Refresh();
}
transformSubmodule.eulerAnglesOffset.Clear();
transformSubmodule.positionOffset.Clear();
}).AddTo(gameObject);
}
}

View File

@@ -12,22 +12,24 @@ namespace Ichni.RhythmGame
{
public partial class Hold : NoteBase
{
public static List<Hold> holdingHoldList = new();
public float holdEndTime;
public float holdingTime;
public bool isHolding;
public bool isFinalJudged;
public static Hold GenerateElement(string elementName, Guid id, List<string> tags, bool isFirstGenerated,
GameElement parentElement, float exactJudgeTime, float holdEndTime)
{
Hold hold = Instantiate(EditorManager.instance.basePrefabs.holdNote, parentElement.transform)
.GetComponent<Hold>();
if (EditorManager.instance.useNotePrefab)
{
isFirstGenerated = false;
}
hold.Initialize(elementName, id, tags, isFirstGenerated, parentElement);
hold.exactJudgeTime = exactJudgeTime;
hold.holdEndTime = holdEndTime;
@@ -54,10 +56,10 @@ namespace Ichni.RhythmGame
hold.track = null;
hold.isOnTrack = false;
}
if (EditorManager.instance.useNotePrefab)
{
EditorManager.instance.projectManager.notePrefabManager.LoadNotePrefab(hold,GetNoteTypeName(hold) + "_Prefab");
EditorManager.instance.projectManager.notePrefabManager.LoadNotePrefab(hold, GetNoteTypeName(hold) + "_Prefab");
}
return hold;
@@ -88,7 +90,7 @@ namespace Ichni.RhythmGame
}
}
}
public partial class Hold
{
public override void SetDefaultSubmodules()
@@ -105,9 +107,9 @@ namespace Ichni.RhythmGame
public override void SetUpInspector()
{
base.SetUpInspector();
IHaveInspection inspector = EditorManager.instance.uiManager.inspector;
var holdSpecial = inspector.GenerateContainer("Hold");
var holdSpecialSubcontainer = holdSpecial.GenerateSubcontainer(3);
var holdEndTimeInputField = inspector.GenerateInputField(this, holdSpecialSubcontainer, "holdEndTime", nameof(holdEndTime));
@@ -118,7 +120,7 @@ namespace Ichni.RhythmGame
effect.effectTime = holdEndTime - exactJudgeTime;
});
});
inspector.MarkedElements["ExactJudgeTime"].AddListenerFunction(() =>
{
noteVisual?.effectSubmodule.effectCollection["Holding"].ForEach(effect =>
@@ -133,12 +135,12 @@ namespace Ichni.RhythmGame
{
protected override void Update()
{
if(holdEndTime < exactJudgeTime)
if (holdEndTime < exactJudgeTime)
{
LogWindow.Log("Hold end time is earlier than exact judge time.", Color.red);
return;
}
/*if (isOnTrack)
{
UpdateNoteInTrack();
@@ -148,22 +150,23 @@ namespace Ichni.RhythmGame
if (isFirstJudged && songTime < exactJudgeTime)
{
isFirstJudged = false;
isHolding = false;
isFinalJudged = false;
holdingTime = 0;
}
if (isHolding)
{
holdingTime = songTime - exactJudgeTime;
}
if (isHolding && songTime > holdEndTime)
{
isHolding = false;
isFinalJudged = true;
noteAudioSubmodule.PlayNoteJudgeAudios(EditorManager.instance.currentJudgeType);
noteAudioSubmodule?.PlayNoteJudgeAudios(EditorManager.instance.currentJudgeType);
}
if (!isFirstJudged && songTime >= exactJudgeTime)
@@ -214,8 +217,16 @@ namespace Ichni.RhythmGame
noteScreenPosition = EditorManager.instance.cameraManager.gameCamera.gameCamera.WorldToScreenPoint(noteVisual.transform.position);
}
}
if (isHolding && !holdingHoldList.Contains(this))
{
holdingHoldList.Add(this);
}
else if (!isHolding && holdingHoldList.Contains(this))
{
holdingHoldList.Remove(this);
}
}
private void LateUpdate()
{
if (isOnTrack)
@@ -230,7 +241,7 @@ namespace Ichni.RhythmGame
public class Hold_BM : NoteBase_BM
{
public float holdEndTime;
public Hold_BM()
{

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -92,6 +92,11 @@ Song Location歌曲路径导入后歌曲存储在编辑器内与原路
### **元素选择**
1. **鼠标左键**:点击选择场景中的游戏元素(需不在 UI 上)。
### Logger
Logger窗口在播放控制器的上方会显示和程序有关的信息
> 绿色=执行没问题,白色=提示
> 红色=有重大问题截图发给开发者先保存项目并切记不要删除AutoSave
> 黄色=小警告,通常不影响正常写谱
## 谱面结构
@@ -185,21 +190,28 @@ trackPercentPoint使用0到1的值代表track进程配合trail使用可以实
trail配有位置材质颜色等仍在开发
目前Trail只能在trackPercentPoint下生成但是有一点奇技淫巧是可以把他复制出来然后直接粘贴在folder下这样就可以通过事件控制他了
目前Trail只能在trackPercentPoint下生成但是有一点~~奇技淫巧~~是可以把他复制出来然后直接粘贴在folder下这样就可以通过事件控制他了
### Note
#### 基础
note可以视为环境物体拥有击打时间击打效果等
exactJudgeTime 就是 Note 的判定时间。随后点击 New Note Visual ,展开 Note 可以看到 New Note Visual ,选中它,在右边的 Choose 那里下拉菜单选择 Note 样式所属的主题包。点击 generate Note Visual 就创建好了。此时您播放谱面,应该会有一个 Tap 落下并在相应秒被判定。
通过调整 position 即可实现无轨的效果Note 可以适当大些,这样在小屏幕上也不会显得太小。
创建 Stay 的过程与创建 Note 大同小异。选中 note 轨道,点击 Stay 以创建 Stay随后创建 Note Visual、
>**Stay 可以使用 Tap 的材质吗?**
>
>- 您可能注意到了,编辑器并不限制您做这件事。从视觉上讲,这相当于创建了一个判定更宽的 Tap因此逻辑上是被允许的。但是**Tap 不可以使用 Stay 的材质**这与读谱的基本逻辑相悖。Hold 的材质与上述两者有较大不同,不能和它们混用。
击打时间:建议使用采音器=>见采音器指南
##### 导出 Note 配置
在导出 Note 配置之后,您就不必通过复制的方式创建 Note像之前那样单击轨道下的相应按钮即可新创建的 Note 会自动使用导出的 Note 配置。在noteinfo旁有save Note PrefabSave之后可以直接在顶上的EditorManger选择Use Note Prefab这样之后放置note就可以自动套用对应的Note Visual
#### 击打效果和判定模式
由于不同主题包拥有不同击打效果和判定模式在add页需要输入字符来创建
---
## 谱面存档
@@ -217,6 +229,8 @@ ichni每隔一段时间就会自动保存谱面如果要回档需要手动
这个属于与本体对接的功能,目的是生成本体用的加密版谱面
---
## 一般写谱流程
### 0寻找音源确定bpm选择主题包
@@ -229,7 +243,7 @@ ichni每隔一段时间就会自动保存谱面如果要回档需要手动
#### 创建摄像机文件夹
您需要创建一个摄像机文件夹来放置摄像机及其附着的轨道(如果有),现有的两个方法便是使用cross track point或者直接对摄像机所在的文件夹增加动画动画法可以直接参照其他元素的方法来进行创建而cross track point可以获取到文件夹内对应的track然后使用类似track precent point的设置来调节当然Cross track point里设有快捷设置所有的Track也就是说可以完全不用管cross track point的东西直接在文件夹下把track和对应的起止时间都写好然后直接使用paste track list粘贴所有的时间。
现有的两个移动game camera方法便是使用cross track point或者直接对摄像机所在的文件夹增加动画动画法可以直接参照其他元素的方法来进行创建而cross track point可以获取到文件夹内对应的track然后使用类似track precent point的设置来调节当然Cross track point里设有快捷设置所有的Track也就是说可以完全不用管cross track point的东西直接在文件夹下把track和对应的起止时间都写好然后直接使用paste track list粘贴所有的时间。
和beta版不同的是cross track point是会自动设置旋转的默认的旋转方向对应的是当前track的切线方向形象的说法就是你用track画一个圆然后设置之后不用手动操作你的摄像机就可以自动沿着圆旋转如果把y值设为了90它就可以始终看向圆的中心点
@@ -237,5 +251,9 @@ ichni每隔一段时间就会自动保存谱面如果要回档需要手动
#### 1, movable track
由于movable track的功能介绍早在前面有提及这里不过多介绍
需要注意的是尽量不要让track面的旋转垂直于屏幕也就是“不要竖着看一张纸”
#### 2Static track
Static track适合生成装饰兼任下落note的方法
功能介绍早在前面有提及,这里不过多介绍
### 3创建Note