Beatmap Sections & Latency Reduction (#170)

* prep UI for chart section

* all special layers now on one area

todo: have buttons toggle between special layers  (selection mode shows all?), use the tabs system for this

* swapping between special timelines - prelim

* special entities can be placed

* spec. timeline base functions complete

music volume changes should work now

* attempt at input lag reduction

needs testing

* fix dsp issues

* smaller DSP buffer?

* Revert "smaller DSP buffer?"

This reverts commit 9d36db5ff90cf4e2d7bb8db9b4b7376cb493e02b.

* make conductor clock use real time (double)

change order of execution of input-related scripts to further attempt a reduction in input latency

* start values can be changed

make the old special entity bar visible when the corresponding type is selected

* creation of Chart Sections (TODO: GO REFERENCE)

* added GO references

* section edit dialog

* disable wrapping on chart section obj

* backspace can now delete entities

* entities don't shift when duplicated

* fix PlayerActionEvent order of operations

- fixed remix loading trying to clear special timeline while it's writing to itself

* make oop check match parity

* more operation order fix

* fix Karate Man BG initialization

* show section progress in editor

todo: section progress in-game

* more fix for entity duping
This commit is contained in:
minenice55 2022-09-18 16:48:14 -04:00 committed by GitHub
parent 8df4a11666
commit d74fe11e68
51 changed files with 5944 additions and 1129 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View file

@ -0,0 +1,108 @@
fileFormatVersion: 2
guid: e85d5d286a8191b499c570d33dd0899c
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

View file

@ -0,0 +1,108 @@
fileFormatVersion: 2
guid: dd516f1a3380ca642939d4fa86603249
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load diff

View file

@ -31,6 +31,8 @@ namespace HeavenStudio
// Current time of the song // Current time of the song
private float time; private float time;
double lastAbsTime;
// an AudioSource attached to this GameObject that will play the music. // an AudioSource attached to this GameObject that will play the music.
public AudioSource musicSource; public AudioSource musicSource;
@ -140,6 +142,7 @@ namespace HeavenStudio
musicSource.PlayScheduled(AudioSettings.dspTime); musicSource.PlayScheduled(AudioSettings.dspTime);
} }
} }
lastAbsTime = Time.realtimeSinceStartupAsDouble;
// GameManager.instance.SetCurrentEventToClosest(songPositionInBeats); // GameManager.instance.SetCurrentEventToClosest(songPositionInBeats);
} }
@ -172,7 +175,9 @@ namespace HeavenStudio
if (isPlaying) if (isPlaying)
{ {
var dt = Time.unscaledDeltaTime * musicSource.pitch; double absTime = Time.realtimeSinceStartupAsDouble;
float dt = (float) (absTime - lastAbsTime) * musicSource.pitch;
lastAbsTime = absTime;
time += dt; time += dt;
@ -344,7 +349,7 @@ namespace HeavenStudio
secPerBeat = 60f / songBpm; secPerBeat = 60f / songBpm;
} }
public void SetVolume(int percent) public void SetVolume(float percent)
{ {
musicSource.volume = percent / 100f; musicSource.volume = percent / 100f;
} }

View file

@ -31,19 +31,28 @@ namespace HeavenStudio
Coroutine currentGameSwitchIE; Coroutine currentGameSwitchIE;
[Header("Properties")] [Header("Properties")]
public int currentEvent, currentTempoEvent, currentPreEvent, currentPreSwitch; public int currentEvent, currentTempoEvent, currentVolumeEvent, currentSectionEvent,
currentPreEvent, currentPreSwitch;
public float endBeat;
public float startOffset; public float startOffset;
public bool playOnStart; public bool playOnStart;
public float startBeat; public float startBeat;
[NonSerialized] public GameObject currentGameO; [NonSerialized] public GameObject currentGameO;
public bool autoplay; public bool autoplay;
public bool canInput = true; public bool canInput = true;
public DynamicBeatmap.ChartSection currentSection, nextSection;
public float sectionProgress { get {
if (currentSection == null) return 0;
if (nextSection == null) return (Conductor.instance.songPositionInBeats - currentSection.beat) / (endBeat - currentSection.beat);
return (Conductor.instance.songPositionInBeats - currentSection.beat) / (nextSection.beat - currentSection.beat);
}}
public event Action<float> onBeatChanged; public event Action<float> onBeatChanged;
public event Action<DynamicBeatmap.ChartSection> onSectionChange;
public int BeatmapEntities() public int BeatmapEntities()
{ {
return Beatmap.entities.Count + Beatmap.tempoChanges.Count; return Beatmap.entities.Count + Beatmap.tempoChanges.Count + Beatmap.volumeChanges.Count + Beatmap.beatmapSections.Count;
} }
public static GameManager instance { get; private set; } public static GameManager instance { get; private set; }
@ -219,21 +228,46 @@ namespace HeavenStudio
if (!Conductor.instance.isPlaying) if (!Conductor.instance.isPlaying)
return; return;
List<float> entities = Beatmap.entities.Select(c => c.beat).ToList();
List<float> tempoChanges = Beatmap.tempoChanges.Select(c => c.beat).ToList();
List<float> entities = Beatmap.entities.Select(c => c.beat).ToList();
List<float> tempoChanges = Beatmap.tempoChanges.Select(c => c.beat).ToList();
if (currentTempoEvent < Beatmap.tempoChanges.Count && currentTempoEvent >= 0) if (currentTempoEvent < Beatmap.tempoChanges.Count && currentTempoEvent >= 0)
{ {
// Debug.Log("Checking Tempo Change at " + tempoChanges[currentTempoEvent] + ", current beat " + Conductor.instance.songPositionInBeats);
if (Conductor.instance.songPositionInBeats >= tempoChanges[currentTempoEvent]) if (Conductor.instance.songPositionInBeats >= tempoChanges[currentTempoEvent])
{ {
// Debug.Log("Tempo Change at " + Conductor.instance.songPositionInBeats + " of bpm " + DynamicBeatmap.tempoChanges[currentTempoEvent].tempo);
Conductor.instance.SetBpm(Beatmap.tempoChanges[currentTempoEvent].tempo); Conductor.instance.SetBpm(Beatmap.tempoChanges[currentTempoEvent].tempo);
Conductor.instance.timeSinceLastTempoChange = Time.time; Conductor.instance.timeSinceLastTempoChange = Time.time;
currentTempoEvent++; currentTempoEvent++;
} }
} }
List<float> volumeChanges = Beatmap.volumeChanges.Select(c => c.beat).ToList();
if (currentVolumeEvent < Beatmap.volumeChanges.Count && currentVolumeEvent >= 0)
{
if (Conductor.instance.songPositionInBeats >= volumeChanges[currentVolumeEvent])
{
Conductor.instance.SetVolume(Beatmap.volumeChanges[currentVolumeEvent].volume);
currentVolumeEvent++;
}
}
List<float> chartSections = Beatmap.beatmapSections.Select(c => c.beat).ToList();
if (currentSectionEvent < Beatmap.beatmapSections.Count && currentSectionEvent >= 0)
{
if (Conductor.instance.songPositionInBeats >= chartSections[currentSectionEvent])
{
Debug.Log("Section " + Beatmap.beatmapSections[currentSectionEvent].sectionName + " started");
currentSection = Beatmap.beatmapSections[currentSectionEvent];
currentSectionEvent++;
if (currentSectionEvent < Beatmap.beatmapSections.Count)
nextSection = Beatmap.beatmapSections[currentSectionEvent];
else
nextSection = null;
onSectionChange?.Invoke(currentSection);
}
}
float seekTime = 8f; float seekTime = 8f;
//seek ahead to preload games that have assetbundles //seek ahead to preload games that have assetbundles
SeekAheadAndPreload(Conductor.instance.songPositionInBeats, seekTime); SeekAheadAndPreload(Conductor.instance.songPositionInBeats, seekTime);
@ -273,6 +307,8 @@ namespace HeavenStudio
// currentEvent += gameManagerEntities.Count; // currentEvent += gameManagerEntities.Count;
} }
} }
} }
public void ToggleInputs(bool inputs) public void ToggleInputs(bool inputs)
@ -305,6 +341,10 @@ namespace HeavenStudio
} }
KillAllSounds(); KillAllSounds();
Minigame miniGame = currentGameO.GetComponent<Minigame>();
if (miniGame != null)
miniGame.OnPlay(beat);
} }
public void Pause() public void Pause()
@ -389,10 +429,17 @@ namespace HeavenStudio
{ {
SetGame(newGame); SetGame(newGame);
} }
List<DynamicBeatmap.DynamicEntity> allEnds = EventCaller.GetAllInGameManagerList("gameManager", new string[] { "end" });
if (allEnds.Count > 0)
endBeat = allEnds.Select(c => c.beat).Min();
else
endBeat = Conductor.instance.SongLengthInBeats();
} }
else else
{ {
SetGame("noGame"); SetGame("noGame");
endBeat = Conductor.instance.SongLengthInBeats();
} }
if (Beatmap.tempoChanges.Count > 0) if (Beatmap.tempoChanges.Count > 0)
@ -413,6 +460,39 @@ namespace HeavenStudio
// Debug.Log("currentTempoEvent is now " + currentTempoEvent); // Debug.Log("currentTempoEvent is now " + currentTempoEvent);
} }
if (Beatmap.volumeChanges.Count > 0)
{
currentVolumeEvent = 0;
List<float> volumeChanges = Beatmap.volumeChanges.Select(c => c.beat).ToList();
for (int t = 0; t < volumeChanges.Count; t++)
{
if (volumeChanges[t] > beat)
{
break;
}
currentVolumeEvent = t;
}
}
currentSection = null;
nextSection = null;
if (Beatmap.beatmapSections.Count > 0)
{
currentSectionEvent = 0;
List<float> beatmapSections = Beatmap.beatmapSections.Select(c => c.beat).ToList();
for (int t = 0; t < beatmapSections.Count; t++)
{
if (beatmapSections[t] > beat)
{
break;
}
currentSectionEvent = t;
}
}
onSectionChange?.Invoke(currentSection);
SeekAheadAndPreload(beat); SeekAheadAndPreload(beat);
} }

View file

@ -4,7 +4,7 @@ MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2
defaultReferences: [] defaultReferences: []
executionOrder: -43 executionOrder: -44
icon: {instanceID: 0} icon: {instanceID: 0}
userData: userData:
assetBundleName: assetBundleName:

View file

@ -135,6 +135,7 @@ namespace HeavenStudio.Games.Scripts_FanClub
float yWeight = -(yMul*yMul) + 1f; float yWeight = -(yMul*yMul) + 1f;
motionRoot.transform.localPosition = new Vector3(0, 3f * yWeight); motionRoot.transform.localPosition = new Vector3(0, 3f * yWeight);
shadow.transform.localScale = new Vector3((1f-yWeight*0.8f) * 1.4f, (1f-yWeight*0.8f) * 1.4f, 1f); shadow.transform.localScale = new Vector3((1f-yWeight*0.8f) * 1.4f, (1f-yWeight*0.8f) * 1.4f, 1f);
animator.Play("FanJump", -1, 0);
} }
else else
{ {

View file

@ -446,8 +446,24 @@ namespace HeavenStudio.Games
cameraPosition = CameraPosition[0].position; cameraPosition = CameraPosition[0].position;
} }
public override void OnPlay(float beat)
{
var cond = Conductor.instance;
if (!cond.isPlaying)
{
SetBgEffectsToLast(beat);
// remove all children of the ItemHolder
foreach (Transform child in ItemHolder)
{
Destroy(child.gameObject);
}
}
}
private void Start() private void Start()
{ {
var cond = Conductor.instance;
GameCamera.additionalPosition = cameraPosition - GameCamera.defaultPosition; GameCamera.additionalPosition = cameraPosition - GameCamera.defaultPosition;
bgEffectAnimator = BGEffect.GetComponent<Animator>(); bgEffectAnimator = BGEffect.GetComponent<Animator>();
bgEffectSpriteRenderer = BGEffect.GetComponent<SpriteRenderer>(); bgEffectSpriteRenderer = BGEffect.GetComponent<SpriteRenderer>();
@ -456,7 +472,8 @@ namespace HeavenStudio.Games
bgBloodRenderer = BGBlood.GetComponent<SpriteRenderer>(); bgBloodRenderer = BGBlood.GetComponent<SpriteRenderer>();
bgRadialRenderer = BGRadial.GetComponent<SpriteRenderer>(); bgRadialRenderer = BGRadial.GetComponent<SpriteRenderer>();
SetBgAndShadowCol(WantBgChangeStart, WantBgChangeLength, bgType, (int) currentShadowType, BackgroundColors[bgType], customShadowColour, (int)currentBgEffect); SetBgEffectsToLast(cond.songPositionInBeats);
SetBgAndShadowCol(WantBgChangeStart, WantBgChangeLength, bgType, (int) currentShadowType, bgColour, customShadowColour, (int)currentBgEffect);
SetBgTexture(textureType, textureFilterType, filterColour, filterColour); SetBgTexture(textureType, textureFilterType, filterColour, filterColour);
UpdateMaterialColour(BodyColor, HighlightColor, ItemColor); UpdateMaterialColour(BodyColor, HighlightColor, ItemColor);
ToggleBop(WantBop); ToggleBop(WantBop);
@ -806,10 +823,10 @@ namespace HeavenStudio.Games
var e = bgfx[i]; var e = bgfx[i];
if (e.beat > beat) if (e.beat > beat)
break; break;
SetBgAndShadowCol(e.beat, e.length, e["type"], e["type2"], e["colorA"], e["colorB"], e["type3"]); SetBgEffectsUnloaded(e.beat, e.length, e["type"], e["type2"], e["colorA"], e["colorB"], e["type3"], e["type4"], e["type5"], e["colorC"], e["colorD"]);
SetBgTexture(e["type4"], e["type5"], e["colorC"], e["colorD"]);
} }
var camfx = GameManager.instance.Beatmap.entities.FindAll(en => en.datamodel == "karateman/special camera"); var camfx = GameManager.instance.Beatmap.entities.FindAll(en => en.datamodel == "karateman/special camera");
DoSpecialCamera(0, 0, true);
for (int i = 0; i < camfx.Count; i++) for (int i = 0; i < camfx.Count; i++)
{ {
var e = camfx[i]; var e = camfx[i];
@ -817,17 +834,16 @@ namespace HeavenStudio.Games
break; break;
DoSpecialCamera(e.beat, e.length, e["toggle"]); DoSpecialCamera(e.beat, e.length, e["toggle"]);
} }
// has issues when creating a new hitx entity so this is deactivated for now var objfx = GameManager.instance.Beatmap.entities.FindAll(en => en.datamodel == "karateman/set object colors");
// var hitx = GameManager.instance.Beatmap.entities.FindAll(en => en.datamodel == "karateman/hitX"); for (int i = 0; i < objfx.Count; i++)
// for (int i = 0; i < hitx.Count; i++) {
// { var e = objfx[i];
// var e = hitx[i]; if (e.beat > beat)
// if (e.beat > beat) break;
// break; UpdateMaterialColour(e["colorA"], e["colorB"], e["colorC"]);
// Debug.Log("hitx"); }
// DoWord(e.beat, e["type"], false); SetBgAndShadowCol(WantBgChangeStart, WantBgChangeLength, bgType, (int) currentShadowType, bgColour, customShadowColour, (int)currentBgEffect);
// } SetBgTexture(textureType, textureFilterType, filterColour, filterColour);
} }
public static void SetBgEffectsUnloaded(float beat, float length, int newBgType, int newShadowType, Color bgCol, Color shadowCol, int bgFx, int texture, int textureFilter, Color filterCol, Color filterColNext) public static void SetBgEffectsUnloaded(float beat, float length, int newBgType, int newShadowType, Color bgCol, Color shadowCol, int bgFx, int texture, int textureFilter, Color filterCol, Color filterColNext)

View file

@ -6,7 +6,7 @@ namespace HeavenStudio.Games
{ {
public class Minigame : MonoBehaviour public class Minigame : MonoBehaviour
{ {
public static float earlyTime = 0.1f, perfectTime = 0.08f, aceEarlyTime = 0.02f, aceLateTime = 0.02f, lateTime = 0.08f, endTime = 0.1f; public static float earlyTime = 0.1f, perfectTime = 0.08f, aceEarlyTime = 0.025f, aceLateTime = 0.025f, lateTime = 0.08f, endTime = 0.1f;
public List<Minigame.Eligible> EligibleHits = new List<Minigame.Eligible>(); public List<Minigame.Eligible> EligibleHits = new List<Minigame.Eligible>();
[System.Serializable] [System.Serializable]
@ -132,7 +132,6 @@ namespace HeavenStudio.Games
{ {
PlayerActionEvent input = GetClosestScheduledInput(); PlayerActionEvent input = GetClosestScheduledInput();
if (input == null) return false; if (input == null) return false;
return input.IsExpectingInputNow(); return input.IsExpectingInputNow();
} }
@ -190,6 +189,11 @@ namespace HeavenStudio.Games
} }
public virtual void OnPlay(float beat)
{
}
public int MultipleEventsAtOnce() public int MultipleEventsAtOnce()
{ {
int sameTime = 0; int sameTime = 0;

View file

@ -52,6 +52,7 @@ namespace HeavenStudio.Games
this.canHit = canHit; this.canHit = canHit;
} }
public void Update() public void Update()
{ {
if(!Conductor.instance.NotStopped()){CleanUp();} // If the song is stopped entirely in the editor, destroy itself as we don't want duplicates if(!Conductor.instance.NotStopped()){CleanUp();} // If the song is stopped entirely in the editor, destroy itself as we don't want duplicates

View file

@ -4,7 +4,7 @@ MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2
defaultReferences: [] defaultReferences: []
executionOrder: 0 executionOrder: 5
icon: {instanceID: 0} icon: {instanceID: 0}
userData: userData:
assetBundleName: assetBundleName:

View file

@ -59,6 +59,7 @@ namespace HeavenStudio
DontDestroyOnLoad(this.gameObject); DontDestroyOnLoad(this.gameObject);
instance = this; instance = this;
Starpelly.OS.ChangeWindowTitle("Heaven Studio DEMO"); Starpelly.OS.ChangeWindowTitle("Heaven Studio DEMO");
QualitySettings.maxQueuedFrames = 1;
PlayerInput.InitInputControllers(); PlayerInput.InitInputControllers();
} }

View file

@ -64,7 +64,7 @@ namespace HeavenStudio.Editor.Commands
{ {
Pos p = new Pos(); Pos p = new Pos();
p.eventObj = eventObjs[i]; p.eventObj = eventObjs[i];
p.lastPos_ = eventObjs[i].lastPos_; p.lastPos_ = eventObjs[i].moveStartPos;
p.previousPos = eventObjs[i].transform.localPosition; p.previousPos = eventObjs[i].transform.localPosition;
this.pos.Add(p); this.pos.Add(p);
} }

View file

@ -134,7 +134,7 @@ namespace HeavenStudio.Editor
Fullscreen(); Fullscreen();
} }
if (Input.GetKeyDown(KeyCode.Delete)) if (Input.GetKeyDown(KeyCode.Delete) || Input.GetKeyDown(KeyCode.Backspace))
{ {
List<TimelineEventObj> ev = new List<TimelineEventObj>(); List<TimelineEventObj> ev = new List<TimelineEventObj>();
for (int i = 0; i < Selections.instance.eventsSelected.Count; i++) ev.Add(Selections.instance.eventsSelected[i]); for (int i = 0; i < Selections.instance.eventsSelected.Count; i++) ev.Add(Selections.instance.eventsSelected[i]);
@ -409,9 +409,9 @@ namespace HeavenStudio.Editor
{ {
GameManager.instance.LoadRemix(json, type); GameManager.instance.LoadRemix(json, type);
Timeline.instance.LoadRemix(); Timeline.instance.LoadRemix();
Timeline.instance.TempoInfo.UpdateStartingBPMText(); // Timeline.instance.SpecialInfo.UpdateStartingBPMText();
Timeline.instance.VolumeInfo.UpdateStartingVolumeText(); // Timeline.instance.VolumeInfo.UpdateStartingVolumeText();
Timeline.instance.TempoInfo.UpdateOffsetText(); // Timeline.instance.SpecialInfo.UpdateOffsetText();
Timeline.FitToSong(); Timeline.FitToSong();
currentRemixPath = string.Empty; currentRemixPath = string.Empty;

View file

@ -19,8 +19,10 @@ namespace HeavenStudio.Editor
[Header("Components")] [Header("Components")]
[SerializeField] private Image layer; [SerializeField] private Image layer;
[SerializeField] private Image specialLayers;
[SerializeField] private Image tempoLayer; [SerializeField] private Image tempoLayer;
[SerializeField] private Image musicLayer; [SerializeField] private Image musicLayer;
[SerializeField] private Image sectionLayer;
private void Awake() private void Awake()
{ {
@ -29,10 +31,14 @@ namespace HeavenStudio.Editor
private void Start() private void Start()
{ {
specialLayers.GetComponent<Image>().color = theme.properties.SpecialLayersCol.Hex2RGB();
tempoLayer.GetComponent<Image>().color = theme.properties.TempoLayerCol.Hex2RGB(); tempoLayer.GetComponent<Image>().color = theme.properties.TempoLayerCol.Hex2RGB();
musicLayer.GetComponent<Image>().color = theme.properties.MusicLayerCol.Hex2RGB(); musicLayer.GetComponent<Image>().color = theme.properties.MusicLayerCol.Hex2RGB();
sectionLayer.GetComponent<Image>().color = theme.properties.SectionLayerCol.Hex2RGB();
Tooltip.AddTooltip(specialLayers.gameObject, $"All Special Tracks");
Tooltip.AddTooltip(tempoLayer.gameObject, $"Tempo Track"); Tooltip.AddTooltip(tempoLayer.gameObject, $"Tempo Track");
Tooltip.AddTooltip(musicLayer.gameObject, $"Music Volume Track"); Tooltip.AddTooltip(musicLayer.gameObject, $"Music Volume Track");
Tooltip.AddTooltip(sectionLayer.gameObject, $"Remix Sections Track");
layer.gameObject.SetActive(false); layer.gameObject.SetActive(false);

View file

@ -183,10 +183,11 @@ namespace HeavenStudio.Editor
public void Drag() public void Drag()
{ {
if (Conductor.instance.NotStopped() || !Timeline.instance.timelineState.selected) return; if (Conductor.instance.NotStopped() || Editor.instance.inAuthorativeMenu) return;
if (Timeline.instance.CheckIfMouseInTimeline() && dragTimes < 1) if (Timeline.instance.CheckIfMouseInTimeline() && dragTimes < 1)
{ {
Timeline.instance.timelineState.SetState(Timeline.CurrentTimelineState.State.Selection);
dragTimes++; dragTimes++;
TimelineEventObj eventObj; TimelineEventObj eventObj;

View file

@ -12,8 +12,10 @@ namespace HeavenStudio.Editor
[Serializable] [Serializable]
public class Properties public class Properties
{ {
public string SpecialLayersCol;
public string TempoLayerCol; public string TempoLayerCol;
public string MusicLayerCol; public string MusicLayerCol;
public string SectionLayerCol;
public string Layer1Col; public string Layer1Col;
public string Layer2Col; public string Layer2Col;

View file

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

View file

@ -0,0 +1,51 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using HeavenStudio;
using HeavenStudio.Editor;
using HeavenStudio.Editor.Track;
using TMPro;
public class SectionDialog : Dialog
{
SectionTimelineObj sectionObj;
[SerializeField] TMP_InputField sectionName;
public void SwitchSectionDialog()
{
if(dialog.activeSelf) {
sectionObj = null;
dialog.SetActive(false);
Editor.instance.inAuthorativeMenu = false;
} else {
Editor.instance.inAuthorativeMenu = true;
ResetAllDialogs();
dialog.SetActive(true);
}
}
public void SetSectionObj(SectionTimelineObj sectionObj)
{
this.sectionObj = sectionObj;
sectionName.text = sectionObj.chartSection.sectionName;
}
public void DeleteSection()
{
if(dialog.activeSelf) {
dialog.SetActive(false);
Editor.instance.inAuthorativeMenu = false;
}
if (sectionObj == null) return;
GameManager.instance.Beatmap.beatmapSections.Remove(sectionObj.chartSection);
sectionObj.DeleteObj();
}
public void ChangeSectionName(string name)
{
if (sectionObj == null) return;
sectionObj.chartSection.sectionName = name;
sectionObj.UpdateLabel();
}
}

View file

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: ea7831ec549a9984c8c3e5afd98bac2f guid: 7e5d39fb5bc171f44ba013cf8e37fdd2
MonoImporter: MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2

View file

@ -0,0 +1,229 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System;
using TMPro;
using Starpelly;
namespace HeavenStudio.Editor.Track
{
public class SpecialTimeline : MonoBehaviour
{
[Header("References")]
[SerializeField] private RectTransform RefTempoChange;
[SerializeField] private RectTransform RefVolumeChange;
[SerializeField] private RectTransform RefSectionChange;
[Header("Components")]
private RectTransform rectTransform;
public List<SpecialTimelineObj> specialTimelineObjs = new List<SpecialTimelineObj>();
[System.Flags]
public enum HoveringTypes
{
TempoChange = 1,
VolumeChange = 2,
SectionChange = 4,
}
public static HoveringTypes hoveringTypes = 0;
private bool firstUpdate;
public static SpecialTimeline instance;
private void Start()
{
instance = this;
rectTransform = this.GetComponent<RectTransform>();
Setup();
}
public void Setup()
{
ClearSpecialTimeline();
foreach (var tempoChange in GameManager.instance.Beatmap.tempoChanges)
AddTempoChange(false, tempoChange);
foreach (var volumeChange in GameManager.instance.Beatmap.volumeChanges)
AddVolumeChange(false, volumeChange);
foreach (var sectionChange in GameManager.instance.Beatmap.beatmapSections)
AddChartSection(false, sectionChange);
Timeline.instance.timelineState.SetState(Timeline.CurrentTimelineState.State.Selection);
FixObjectsVisibility();
}
private void Update()
{
if (!firstUpdate)
{
hoveringTypes = 0;
firstUpdate = true;
}
if (Timeline.instance.userIsEditingInputField || Editor.instance.inAuthorativeMenu)
return;
if (!Conductor.instance.NotStopped())
{
if (RectTransformUtility.RectangleContainsScreenPoint(rectTransform, Input.mousePosition, Editor.instance.EditorCamera))
{
if (Input.GetMouseButtonDown(0))
{
switch (Timeline.instance.timelineState.currentState)
{
case Timeline.CurrentTimelineState.State.TempoChange:
if (!hoveringTypes.HasFlag(HoveringTypes.TempoChange))
AddTempoChange(true);
break;
case Timeline.CurrentTimelineState.State.MusicVolume:
if (!hoveringTypes.HasFlag(HoveringTypes.VolumeChange))
AddVolumeChange(true);
break;
case Timeline.CurrentTimelineState.State.ChartSection:
if (!hoveringTypes.HasFlag(HoveringTypes.SectionChange))
AddChartSection(true);
break;
}
}
}
}
hoveringTypes = 0;
}
public void FixObjectsVisibility()
{
foreach (SpecialTimelineObj obj in specialTimelineObjs)
{
obj.SetVisibility(Timeline.instance.timelineState.currentState);
}
}
public void ClearSpecialTimeline()
{
foreach (SpecialTimelineObj obj in specialTimelineObjs)
{
Destroy(obj.gameObject);
}
specialTimelineObjs.Clear();
}
public void AddTempoChange(bool create, DynamicBeatmap.TempoChange tempoChange_ = null)
{
GameObject tempoChange = Instantiate(RefTempoChange.gameObject, this.transform);
tempoChange.transform.GetChild(0).GetComponent<Image>().color = EditorTheme.theme.properties.TempoLayerCol.Hex2RGB();
tempoChange.transform.GetChild(1).GetComponent<Image>().color = EditorTheme.theme.properties.TempoLayerCol.Hex2RGB();
tempoChange.transform.GetChild(2).GetComponent<TMP_Text>().color = EditorTheme.theme.properties.TempoLayerCol.Hex2RGB();
tempoChange.SetActive(true);
TempoTimelineObj tempoTimelineObj = tempoChange.GetComponent<TempoTimelineObj>();
if (create == true)
{
tempoChange.transform.position = new Vector3(Editor.instance.EditorCamera.ScreenToWorldPoint(Input.mousePosition).x + 0.08f, tempoChange.transform.position.y);
tempoChange.transform.localPosition = new Vector3(Starpelly.Mathp.Round2Nearest(tempoChange.transform.localPosition.x, Timeline.SnapInterval()), tempoChange.transform.localPosition.y);
DynamicBeatmap.TempoChange tempoC = new DynamicBeatmap.TempoChange();
tempoC.beat = tempoChange.transform.localPosition.x;
tempoC.tempo = GameManager.instance.Beatmap.bpm;
tempoTimelineObj.tempoChange = tempoC;
GameManager.instance.Beatmap.tempoChanges.Add(tempoC);
}
else
{
tempoChange.transform.localPosition = new Vector3(tempoChange_.beat, tempoChange.transform.localPosition.y);
tempoTimelineObj.tempoChange = tempoChange_;
}
tempoTimelineObj.SetVisibility(Timeline.instance.timelineState.currentState);
specialTimelineObjs.Add(tempoTimelineObj);
Timeline.instance.FitToSong();
}
public void AddVolumeChange(bool create, DynamicBeatmap.VolumeChange volumeChange_ = null)
{
GameObject volumeChange = Instantiate(RefVolumeChange.gameObject, this.transform);
volumeChange.transform.GetChild(0).GetComponent<Image>().color = EditorTheme.theme.properties.MusicLayerCol.Hex2RGB();
volumeChange.transform.GetChild(1).GetComponent<Image>().color = EditorTheme.theme.properties.MusicLayerCol.Hex2RGB();
volumeChange.transform.GetChild(2).GetComponent<TMP_Text>().color = EditorTheme.theme.properties.MusicLayerCol.Hex2RGB();
volumeChange.SetActive(true);
VolumeTimelineObj volumeTimelineObj = volumeChange.GetComponent<VolumeTimelineObj>();
if (create == true)
{
volumeChange.transform.position = new Vector3(Editor.instance.EditorCamera.ScreenToWorldPoint(Input.mousePosition).x + 0.08f, volumeChange.transform.position.y);
volumeChange.transform.localPosition = new Vector3(Starpelly.Mathp.Round2Nearest(volumeChange.transform.localPosition.x, Timeline.SnapInterval()), volumeChange.transform.localPosition.y);
DynamicBeatmap.VolumeChange volumeC = new DynamicBeatmap.VolumeChange();
volumeC.beat = volumeChange.transform.localPosition.x;
volumeC.volume = GameManager.instance.Beatmap.musicVolume;
volumeTimelineObj.volumeChange = volumeC;
GameManager.instance.Beatmap.volumeChanges.Add(volumeC);
}
else
{
volumeChange.transform.localPosition = new Vector3(volumeChange_.beat, volumeChange.transform.localPosition.y);
volumeTimelineObj.volumeChange = volumeChange_;
}
volumeTimelineObj.SetVisibility(Timeline.instance.timelineState.currentState);
specialTimelineObjs.Add(volumeTimelineObj);
}
public void AddChartSection(bool create, DynamicBeatmap.ChartSection chartSection_ = null)
{
GameObject chartSection = Instantiate(RefSectionChange.gameObject, this.transform);
chartSection.transform.GetChild(0).GetComponent<Image>().color = EditorTheme.theme.properties.SectionLayerCol.Hex2RGB();
chartSection.transform.GetChild(1).GetComponent<Image>().color = EditorTheme.theme.properties.SectionLayerCol.Hex2RGB();
chartSection.transform.GetChild(2).GetComponent<Image>().color = EditorTheme.theme.properties.SectionLayerCol.Hex2RGB();
chartSection.transform.GetChild(3).GetComponent<TMP_Text>().color = EditorTheme.theme.properties.SectionLayerCol.Hex2RGB();
chartSection.SetActive(true);
SectionTimelineObj sectionTimelineObj = chartSection.GetComponent<SectionTimelineObj>();
if (create == true)
{
chartSection.transform.position = new Vector3(Editor.instance.EditorCamera.ScreenToWorldPoint(Input.mousePosition).x + 0.08f, chartSection.transform.position.y);
chartSection.transform.localPosition = new Vector3(Starpelly.Mathp.Round2Nearest(chartSection.transform.localPosition.x, Timeline.SnapInterval()), chartSection.transform.localPosition.y);
DynamicBeatmap.ChartSection sectionC = new DynamicBeatmap.ChartSection();
sectionC.beat = chartSection.transform.localPosition.x;
sectionC.sectionName = "New Section";
sectionC.startPerfect = false;
sectionC.isCheckpoint = false;
sectionTimelineObj.chartSection = sectionC;
GameManager.instance.Beatmap.beatmapSections.Add(sectionC);
}
else
{
chartSection.transform.localPosition = new Vector3(chartSection_.beat, chartSection.transform.localPosition.y);
sectionTimelineObj.chartSection = chartSection_;
}
sectionTimelineObj.SetVisibility(Timeline.instance.timelineState.currentState);
specialTimelineObjs.Add(sectionTimelineObj);
//auto-open the dialog
sectionTimelineObj.OnRightClick();
}
}
}

View file

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 10dd19278a802c24fbeb39d1ccb23219 guid: 364cfb513d7ef744cb0d4828804188e0
MonoImporter: MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2

View file

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

View file

@ -0,0 +1,31 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using HeavenStudio.Editor.Track;
namespace HeavenStudio.Editor
{
public class SpecialTimelineAll : TabsContent
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
public override void OnOpenTab()
{
SpecialTimeline.instance.FixObjectsVisibility();
}
public override void OnCloseTab()
{
}
}
}

View file

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

View file

@ -0,0 +1,31 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using HeavenStudio.Editor.Track;
namespace HeavenStudio.Editor
{
public class SpecialTimelineSection : TabsContent
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
public override void OnOpenTab()
{
SpecialTimeline.instance.FixObjectsVisibility();
}
public override void OnCloseTab()
{
}
}
}

View file

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

View file

@ -0,0 +1,31 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using HeavenStudio.Editor.Track;
namespace HeavenStudio.Editor
{
public class SpecialTimelineTempo : TabsContent
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
public override void OnOpenTab()
{
SpecialTimeline.instance.FixObjectsVisibility();
}
public override void OnCloseTab()
{
}
}
}

View file

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

View file

@ -0,0 +1,31 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using HeavenStudio.Editor.Track;
namespace HeavenStudio.Editor
{
public class SpecialTimelineVolume : TabsContent
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
public override void OnOpenTab()
{
SpecialTimeline.instance.FixObjectsVisibility();
}
public override void OnCloseTab()
{
}
}
}

View file

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

View file

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

View file

@ -0,0 +1,92 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using DG.Tweening;
namespace HeavenStudio.Editor.Track
{
public class SectionTimelineObj : SpecialTimelineObj
{
[Header("Components")]
[SerializeField] private TMP_Text sectionLabel;
[SerializeField] private GameObject chartLine;
[SerializeField] private SectionDialog sectionDialog;
public DynamicBeatmap.ChartSection chartSection;
new private void Update()
{
base.Update();
if (hovering)
{
SpecialTimeline.hoveringTypes |= SpecialTimeline.HoveringTypes.SectionChange;
}
UpdateLabel();
}
public void UpdateLabel()
{
sectionLabel.text = chartSection.sectionName;
}
public override void Init()
{
UpdateLabel();
}
public override void OnLeftClick()
{
if (Timeline.instance.timelineState.currentState == Timeline.CurrentTimelineState.State.ChartSection)
StartMove();
}
public override void OnRightClick()
{
if (Timeline.instance.timelineState.currentState == Timeline.CurrentTimelineState.State.ChartSection)
{
sectionDialog.SetSectionObj(this);
sectionDialog.SwitchSectionDialog();
}
}
public override bool OnMove(float beat)
{
foreach (var sectionChange in GameManager.instance.Beatmap.beatmapSections)
{
if (this.chartSection == sectionChange)
continue;
if (beat > sectionChange.beat - Timeline.instance.snapInterval && beat < sectionChange.beat + Timeline.instance.snapInterval)
return false;
}
this.chartSection.beat = beat;
return true;
}
public override void SetVisibility(Timeline.CurrentTimelineState.State state)
{
if (state == Timeline.CurrentTimelineState.State.ChartSection || state == Timeline.CurrentTimelineState.State.Selection)
{
gameObject.SetActive(true);
if (state == Timeline.CurrentTimelineState.State.ChartSection)
{
chartLine.SetActive(true);
sectionLabel.gameObject.SetActive(true);
}
else
{
chartLine.SetActive(false);
sectionLabel.gameObject.SetActive(false);
}
}
else
{
gameObject.SetActive(false);
}
}
}
}

View file

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

View file

@ -0,0 +1,103 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using DG.Tweening;
namespace HeavenStudio.Editor.Track
{
public class SpecialTimelineObj : MonoBehaviour
{
[Header("Components")]
[SerializeField] private RectTransform rectTransform;
[SerializeField] private RectTransform raycastRect;
private float startPosX;
private bool moving = false;
public bool hovering;
private float lastPosX;
private void Start()
{
rectTransform = GetComponent<RectTransform>();
}
protected void Update()
{
if (!Conductor.instance.NotStopped())
{
if (RectTransformUtility.RectangleContainsScreenPoint(raycastRect, Input.mousePosition, Editor.instance.EditorCamera))
{
if (Input.GetMouseButtonDown(0))
{
OnLeftClick();
}
else if (Input.GetMouseButtonDown(1))
{
OnRightClick();
}
hovering = true;
}
else
{
hovering = false;
}
if (moving)
{
Vector3 mousePos = Editor.instance.EditorCamera.ScreenToWorldPoint(Input.mousePosition);
transform.position = new Vector3(mousePos.x - startPosX, transform.position.y, 0);
transform.localPosition = new Vector3(Mathf.Clamp(Starpelly.Mathp.Round2Nearest(transform.localPosition.x, Timeline.SnapInterval()), 0, Mathf.Infinity), transform.localPosition.y);
if (Input.GetMouseButtonUp(0))
{
if (!OnMove(transform.localPosition.x))
transform.localPosition = new Vector3(lastPosX, transform.localPosition.y);
moving = false;
lastPosX = transform.localPosition.x;
}
}
}
else
{
if (moving)
{
if (!OnMove(transform.localPosition.x))
transform.localPosition = new Vector3(lastPosX, transform.localPosition.y);
moving = false;
lastPosX = transform.localPosition.x;
}
hovering = false;
}
}
public void StartMove()
{
Vector3 mousePos = Editor.instance.EditorCamera.ScreenToWorldPoint(Input.mousePosition);
startPosX = mousePos.x - transform.position.x;
moving = true;
lastPosX = transform.localPosition.x;
}
public void DeleteObj()
{
transform.parent.GetComponent<SpecialTimeline>().specialTimelineObjs.Remove(this);
Destroy(this.gameObject);
}
//events
public virtual void Init() {}
public virtual void OnLeftClick() {}
public virtual void OnRightClick() {}
public virtual bool OnMove(float beat)
{
return true;
}
public virtual void SetVisibility(Timeline.CurrentTimelineState.State state) {}
}
}

View file

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

View file

@ -0,0 +1,98 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using DG.Tweening;
namespace HeavenStudio.Editor.Track
{
public class TempoTimelineObj : SpecialTimelineObj
{
[Header("Components")]
[SerializeField] private TMP_Text tempoTXT;
[SerializeField] private GameObject tempoLine;
public DynamicBeatmap.TempoChange tempoChange;
new private void Update()
{
base.Update();
if (hovering)
{
SpecialTimeline.hoveringTypes |= SpecialTimeline.HoveringTypes.TempoChange;
if (Timeline.instance.timelineState.currentState == Timeline.CurrentTimelineState.State.TempoChange)
{
float newTempo = Input.mouseScrollDelta.y;
if (Input.GetKey(KeyCode.LeftShift))
newTempo *= 5f;
if (Input.GetKey(KeyCode.LeftControl))
newTempo /= 100f;
tempoChange.tempo += newTempo;
//make sure tempo is positive
if (tempoChange.tempo < 1)
tempoChange.tempo = 1;
}
}
UpdateTempo();
}
private void UpdateTempo()
{
tempoTXT.text = $"{tempoChange.tempo} BPM";
Timeline.instance.FitToSong();
}
public override void Init()
{
UpdateTempo();
}
public override void OnLeftClick()
{
if (Timeline.instance.timelineState.currentState == Timeline.CurrentTimelineState.State.TempoChange)
StartMove();
}
public override void OnRightClick()
{
if (Timeline.instance.timelineState.currentState == Timeline.CurrentTimelineState.State.TempoChange)
{
GameManager.instance.Beatmap.tempoChanges.Remove(tempoChange);
DeleteObj();
}
}
public override bool OnMove(float beat)
{
foreach (var tempoChange in GameManager.instance.Beatmap.tempoChanges)
{
if (this.tempoChange == tempoChange)
continue;
if (beat > tempoChange.beat - Timeline.instance.snapInterval && beat < tempoChange.beat + Timeline.instance.snapInterval)
return false;
}
this.tempoChange.beat = beat;
return true;
}
public override void SetVisibility(Timeline.CurrentTimelineState.State state)
{
if (state == Timeline.CurrentTimelineState.State.TempoChange || state == Timeline.CurrentTimelineState.State.Selection)
{
gameObject.SetActive(true);
if (state == Timeline.CurrentTimelineState.State.TempoChange)
tempoLine.SetActive(true);
else
tempoLine.SetActive(false);
}
else
gameObject.SetActive(false);
}
}
}

View file

@ -0,0 +1,96 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using DG.Tweening;
namespace HeavenStudio.Editor.Track
{
public class VolumeTimelineObj : SpecialTimelineObj
{
[Header("Components")]
[SerializeField] private TMP_Text volumeTXT;
[SerializeField] private GameObject volumeLine;
public DynamicBeatmap.VolumeChange volumeChange;
new private void Update()
{
base.Update();
if (hovering)
{
SpecialTimeline.hoveringTypes |= SpecialTimeline.HoveringTypes.VolumeChange;
if (Timeline.instance.timelineState.currentState == Timeline.CurrentTimelineState.State.MusicVolume)
{
float newVolume = Input.mouseScrollDelta.y;
if (Input.GetKey(KeyCode.LeftShift))
newVolume *= 5f;
if (Input.GetKey(KeyCode.LeftControl))
newVolume /= 100f;
volumeChange.volume += newVolume;
//make sure volume is positive
volumeChange.volume = Mathf.Clamp(volumeChange.volume, 0, 100);
}
}
UpdateVolume();
}
private void UpdateVolume()
{
volumeTXT.text = $"{volumeChange.volume}%";
}
public override void Init()
{
UpdateVolume();
}
public override void OnLeftClick()
{
if (Timeline.instance.timelineState.currentState == Timeline.CurrentTimelineState.State.MusicVolume)
StartMove();
}
public override void OnRightClick()
{
if (Timeline.instance.timelineState.currentState == Timeline.CurrentTimelineState.State.MusicVolume)
{
GameManager.instance.Beatmap.volumeChanges.Remove(volumeChange);
DeleteObj();
}
}
public override bool OnMove(float beat)
{
foreach (var volumeChange in GameManager.instance.Beatmap.volumeChanges)
{
if (this.volumeChange == volumeChange)
continue;
if (beat > volumeChange.beat - Timeline.instance.snapInterval && beat < volumeChange.beat + Timeline.instance.snapInterval)
return false;
}
this.volumeChange.beat = beat;
return true;
}
public override void SetVisibility(Timeline.CurrentTimelineState.State state)
{
if (state == Timeline.CurrentTimelineState.State.MusicVolume || state == Timeline.CurrentTimelineState.State.Selection)
{
gameObject.SetActive(true);
if (state == Timeline.CurrentTimelineState.State.MusicVolume)
volumeLine.SetActive(true);
else
volumeLine.SetActive(false);
}
else
gameObject.SetActive(false);
}
}
}

View file

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 86bb8f2f290876a4387f1ea6fedf332b guid: 84c1633846a16fb42baa226572335fae
MonoImporter: MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2

View file

@ -1,180 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System;
using TMPro;
using Starpelly;
namespace HeavenStudio.Editor.Track
{
public class TempoTimeline : MonoBehaviour
{
[Header("Components")]
private RectTransform rectTransform;
[SerializeField] private RectTransform RefTempoChange;
public TMP_InputField StartingBPM;
private RectTransform StartingBPMRect;
public TMP_InputField FirstBeatOffset;
public List<TempoTimelineObj> tempoTimelineObjs = new List<TempoTimelineObj>();
private bool firstUpdate;
private void Start()
{
rectTransform = this.GetComponent<RectTransform>();
StartingBPMRect = StartingBPM.GetComponent<RectTransform>();
for (int i = 0; i < GameManager.instance.Beatmap.tempoChanges.Count; i++)
{
DynamicBeatmap.TempoChange tempoChange = GameManager.instance.Beatmap.tempoChanges[i];
AddTempoChange(false, tempoChange);
}
}
private void Update()
{
if (!firstUpdate)
{
UpdateStartingBPMText();
UpdateOffsetText();
firstUpdate = true;
}
if (Timeline.instance.userIsEditingInputField)
return;
if (Timeline.instance.timelineState.tempoChange && !Conductor.instance.NotStopped())
{
if (RectTransformUtility.RectangleContainsScreenPoint(rectTransform, Input.mousePosition, Editor.instance.EditorCamera))
{
if (Input.GetMouseButtonDown(0))
{
if (tempoTimelineObjs.FindAll(c => c.hovering == true).Count == 0)
{
AddTempoChange(true);
}
}
}
if (RectTransformUtility.RectangleContainsScreenPoint(StartingBPMRect, Input.mousePosition, Editor.instance.EditorCamera))
{
float increase = Input.mouseScrollDelta.y;
if (Input.GetKey(KeyCode.LeftControl))
increase /= 100f;
if (Input.GetKey(KeyCode.LeftShift))
increase *= 5f;
if (increase != 0f)
{
GameManager.instance.Beatmap.bpm += increase;
UpdateStartingBPMText();
UpdateStartingBPMFromText(); // In case the scrolled-to value is invalid.
}
}
}
}
public void UpdateStartingBPMText()
{
StartingBPM.text = GameManager.instance.Beatmap.bpm.ToString("G");
}
public void UpdateOffsetText()
{
FirstBeatOffset.text = (GameManager.instance.Beatmap.firstBeatOffset * 1000f).ToString("G");
}
public void UpdateStartingBPMFromText()
{
// Failsafe against empty string.
if (String.IsNullOrEmpty(StartingBPM.text))
StartingBPM.text = "120";
var newBPM = Convert.ToSingle(StartingBPM.text);
// Failsafe against negative BPM.
if (newBPM < 1f)
{
StartingBPM.text = "1";
newBPM = 1;
}
// Limit decimal places to 4.
newBPM = (float)System.Math.Round(newBPM, 4);
GameManager.instance.Beatmap.bpm = newBPM;
// In case the newBPM ended up differing from the inputted string.
UpdateStartingBPMText();
Timeline.instance.FitToSong();
}
public void UpdateOffsetFromText()
{
// Failsafe against empty string.
if (String.IsNullOrEmpty(FirstBeatOffset.text))
FirstBeatOffset.text = "0";
// Convert ms to s.
var newOffset = Convert.ToSingle(FirstBeatOffset.text) / 1000f;
// Limit decimal places to 4.
newOffset = (float)System.Math.Round(newOffset, 4);
GameManager.instance.Beatmap.firstBeatOffset = newOffset;
UpdateOffsetText();
}
public void ClearTempoTimeline()
{
foreach (TempoTimelineObj tempoTimelineObj in tempoTimelineObjs)
{
Destroy(tempoTimelineObj.gameObject);
}
tempoTimelineObjs.Clear();
}
public void AddTempoChange(bool create, DynamicBeatmap.TempoChange tempoChange_ = null)
{
GameObject tempoChange = Instantiate(RefTempoChange.gameObject, this.transform);
tempoChange.transform.GetChild(0).GetComponent<Image>().color = EditorTheme.theme.properties.TempoLayerCol.Hex2RGB();
tempoChange.transform.GetChild(1).GetComponent<Image>().color = EditorTheme.theme.properties.TempoLayerCol.Hex2RGB();
tempoChange.transform.GetChild(2).GetComponent<TMP_Text>().color = EditorTheme.theme.properties.TempoLayerCol.Hex2RGB();
tempoChange.SetActive(true);
TempoTimelineObj tempoTimelineObj = tempoChange.GetComponent<TempoTimelineObj>();
if (create == true)
{
tempoChange.transform.position = new Vector3(Editor.instance.EditorCamera.ScreenToWorldPoint(Input.mousePosition).x + 0.08f, tempoChange.transform.position.y);
tempoChange.transform.localPosition = new Vector3(Starpelly.Mathp.Round2Nearest(tempoChange.transform.localPosition.x, Timeline.SnapInterval()), tempoChange.transform.localPosition.y);
DynamicBeatmap.TempoChange tempoC = new DynamicBeatmap.TempoChange();
tempoC.beat = tempoChange.transform.localPosition.x;
tempoC.tempo = GameManager.instance.Beatmap.bpm;
tempoTimelineObj.tempoChange = tempoC;
GameManager.instance.Beatmap.tempoChanges.Add(tempoC);
}
else
{
tempoChange.transform.localPosition = new Vector3(tempoChange_.beat, tempoChange.transform.localPosition.y);
tempoTimelineObj.tempoChange = tempoChange_;
}
tempoTimelineObjs.Add(tempoTimelineObj);
Timeline.instance.FitToSong();
}
}
}

View file

@ -1,106 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using DG.Tweening;
namespace HeavenStudio.Editor.Track
{
public class TempoTimelineObj : MonoBehaviour
{
[Header("Components")]
[SerializeField] private RectTransform rectTransform;
[SerializeField] private TMP_Text tempoTXT;
[SerializeField] private RectTransform raycastRect;
public DynamicBeatmap.TempoChange tempoChange;
private float startPosX;
private bool moving = false;
public bool hovering;
private float lastPosX;
private void Start()
{
rectTransform = GetComponent<RectTransform>();
tempoTXT = transform.GetChild(2).GetComponent<TMP_Text>();
UpdateTempo();
}
private void Update()
{
if (Timeline.instance.timelineState.tempoChange && !Conductor.instance.NotStopped())
{
if (RectTransformUtility.RectangleContainsScreenPoint(raycastRect, Input.mousePosition, Editor.instance.EditorCamera))
{
float newTempo = Input.mouseScrollDelta.y;
if (Input.GetKey(KeyCode.LeftShift))
newTempo *= 5f;
if (Input.GetKey(KeyCode.LeftControl))
newTempo /= 100f;
tempoChange.tempo += newTempo;
//make sure tempo is positive
if (tempoChange.tempo < 1)
tempoChange.tempo = 1;
if (Input.GetMouseButtonDown(0))
{
Vector3 mousePos = Editor.instance.EditorCamera.ScreenToWorldPoint(Input.mousePosition);
startPosX = mousePos.x - transform.position.x;
moving = true;
lastPosX = transform.localPosition.x;
}
else if (Input.GetMouseButtonDown(1))
{
GameManager.instance.Beatmap.tempoChanges.Remove(tempoChange);
transform.parent.GetComponent<TempoTimeline>().tempoTimelineObjs.Remove(this);
Destroy(this.gameObject);
}
hovering = true;
}
else
{
hovering = false;
}
if (moving)
{
Vector3 mousePos = Editor.instance.EditorCamera.ScreenToWorldPoint(Input.mousePosition);
transform.position = new Vector3(mousePos.x - startPosX, transform.position.y, 0);
transform.localPosition = new Vector3(Mathf.Clamp(Starpelly.Mathp.Round2Nearest(transform.localPosition.x, Timeline.SnapInterval()), 0, Mathf.Infinity), transform.localPosition.y);
}
if (Input.GetMouseButtonUp(0))
{
if (transform.parent.GetComponent<TempoTimeline>().tempoTimelineObjs.Find(c => c.gameObject.transform.localPosition.x == this.transform.localPosition.x && c != this) != null)
{
transform.localPosition = new Vector3(lastPosX, transform.localPosition.y);
}
else
{
tempoChange.beat = transform.localPosition.x;
}
moving = false;
lastPosX = transform.localPosition.x;
}
UpdateTempo();
}
}
private void UpdateTempo()
{
tempoTXT.text = $"{tempoChange.tempo} BPM";
Timeline.instance.FitToSong();
}
}
}

View file

@ -1,4 +1,4 @@
using System.Collections; using System;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
@ -34,45 +34,109 @@ namespace HeavenStudio.Editor.Track
public class CurrentTimelineState public class CurrentTimelineState
{ {
public bool selected; public enum State
public bool tempoChange; {
public bool musicVolume; Selection,
TempoChange,
MusicVolume,
ChartSection
}
public State currentState = State.Selection;
public bool selected { get { return currentState == State.Selection; } }
public bool tempoChange { get { return currentState == State.TempoChange; } }
public bool musicVolume { get { return currentState == State.MusicVolume; } }
public bool chartSection { get { return currentState == State.ChartSection; } }
public void SetState(bool selected, bool tempoChange, bool musicVolume) public void SetState(bool selected, bool tempoChange, bool musicVolume)
{ {
if (Conductor.instance.NotStopped()) return; if (Conductor.instance.NotStopped()) return;
this.selected = selected;
this.tempoChange = tempoChange;
this.musicVolume = musicVolume;
if (selected) if (selected)
{
currentState = State.Selection;
instance.SelectionsBTN.transform.GetChild(0).GetComponent<Image>().color = Color.white; instance.SelectionsBTN.transform.GetChild(0).GetComponent<Image>().color = Color.white;
instance.SelectionsBTN.GetComponent<TabButton>().Invoke("OnClick", 0);
}
else else
instance.SelectionsBTN.transform.GetChild(0).GetComponent<Image>().color = Color.gray; instance.SelectionsBTN.transform.GetChild(0).GetComponent<Image>().color = Color.gray;
if (tempoChange) if (tempoChange)
{
currentState = State.TempoChange;
instance.TempoChangeBTN.transform.GetChild(0).GetComponent<Image>().color = Color.white; instance.TempoChangeBTN.transform.GetChild(0).GetComponent<Image>().color = Color.white;
instance.TempoChangeBTN.GetComponent<TabButton>().Invoke("OnClick", 0);
}
else else
instance.TempoChangeBTN.transform.GetChild(0).GetComponent<Image>().color = Color.gray; instance.TempoChangeBTN.transform.GetChild(0).GetComponent<Image>().color = Color.gray;
if (musicVolume) if (musicVolume)
{
currentState = State.MusicVolume;
instance.MusicVolumeBTN.transform.GetChild(0).GetComponent<Image>().color = Color.white; instance.MusicVolumeBTN.transform.GetChild(0).GetComponent<Image>().color = Color.white;
instance.MusicVolumeBTN.GetComponent<TabButton>().Invoke("OnClick", 0);
}
else else
instance.MusicVolumeBTN.transform.GetChild(0).GetComponent<Image>().color = Color.gray; instance.MusicVolumeBTN.transform.GetChild(0).GetComponent<Image>().color = Color.gray;
} }
public void SetState(State state)
{
if (Conductor.instance.NotStopped()) return;
currentState = state;
if (selected)
{
instance.SelectionsBTN.transform.GetChild(0).GetComponent<Image>().color = Color.white;
instance.SelectionsBTN.GetComponent<TabButton>().Invoke("OnClick", 0);
}
else
instance.SelectionsBTN.transform.GetChild(0).GetComponent<Image>().color = Color.gray;
if (tempoChange)
{
instance.TempoChangeBTN.transform.GetChild(0).GetComponent<Image>().color = Color.white;
instance.TempoChangeBTN.GetComponent<TabButton>().Invoke("OnClick", 0);
}
else
instance.TempoChangeBTN.transform.GetChild(0).GetComponent<Image>().color = Color.gray;
if (musicVolume)
{
instance.MusicVolumeBTN.transform.GetChild(0).GetComponent<Image>().color = Color.white;
instance.MusicVolumeBTN.GetComponent<TabButton>().Invoke("OnClick", 0);
}
else
instance.MusicVolumeBTN.transform.GetChild(0).GetComponent<Image>().color = Color.gray;
if (chartSection)
{
instance.ChartSectionBTN.transform.GetChild(0).GetComponent<Image>().color = Color.white;
instance.ChartSectionBTN.GetComponent<TabButton>().Invoke("OnClick", 0);
}
else
instance.ChartSectionBTN.transform.GetChild(0).GetComponent<Image>().color = Color.gray;
}
} }
[Header("Timeline Components")] [Header("Timeline Components")]
[SerializeField] private RectTransform TimelineSlider; [SerializeField] private RectTransform TimelineSlider;
[SerializeField] private RectTransform TimelineGridSelect; [SerializeField] private RectTransform TimelineGridSelect;
[SerializeField] private RectTransform TimelineEventGrid;
[SerializeField] private TMP_Text TimelinePlaybackBeat; [SerializeField] private TMP_Text TimelinePlaybackBeat;
public RectTransform TimelineContent; public RectTransform TimelineContent;
[SerializeField] private RectTransform TimelineSongPosLineRef; [SerializeField] private RectTransform TimelineSongPosLineRef;
[SerializeField] private RectTransform TimelineEventObjRef; [SerializeField] private RectTransform TimelineEventObjRef;
[SerializeField] private RectTransform LayersRect; [SerializeField] private RectTransform LayersRect;
public TempoTimeline TempoInfo; [SerializeField] private GameObject TimelineSectionDisplay;
public VolumeTimeline VolumeInfo; [SerializeField] private TMP_Text TimelineSectionText;
[SerializeField] private Slider TimelineSectionProgress;
[Header("Timeline Inputs")]
public TMP_InputField FirstBeatOffset;
public TMP_InputField StartingTempoSpecialAll;
public TMP_InputField StartingTempoSpecialTempo;
public TMP_InputField StartingVolumeSpecialVolume;
public SpecialTimeline SpecialInfo;
private RectTransform TimelineSongPosLine; private RectTransform TimelineSongPosLine;
[Header("Timeline Playbar")] [Header("Timeline Playbar")]
@ -84,6 +148,7 @@ namespace HeavenStudio.Editor.Track
public Button SelectionsBTN; public Button SelectionsBTN;
public Button TempoChangeBTN; public Button TempoChangeBTN;
public Button MusicVolumeBTN; public Button MusicVolumeBTN;
public Button ChartSectionBTN;
public Slider PlaybackSpeed; public Slider PlaybackSpeed;
public Vector3[] LayerCorners = new Vector3[4]; public Vector3[] LayerCorners = new Vector3[4];
@ -117,16 +182,10 @@ namespace HeavenStudio.Editor.Track
AddEventObject(e.datamodel, false, new Vector3(e.beat, -e.track * LayerHeight()), e, false, RandomID()); AddEventObject(e.datamodel, false, new Vector3(e.beat, -e.track * LayerHeight()), e, false, RandomID());
} }
//tempo changes SpecialInfo.Setup();
TempoInfo.ClearTempoTimeline(); UpdateOffsetText();
for (int i = 0; i < GameManager.instance.Beatmap.tempoChanges.Count; i++) UpdateStartingBPMText();
{ UpdateStartingVolText();
var t = GameManager.instance.Beatmap.tempoChanges[i];
TempoInfo.AddTempoChange(false, t);
}
//volume changes
} }
public void Init() public void Init()
@ -170,15 +229,19 @@ namespace HeavenStudio.Editor.Track
SelectionsBTN.onClick.AddListener(delegate SelectionsBTN.onClick.AddListener(delegate
{ {
timelineState.SetState(true, false, false); timelineState.SetState(CurrentTimelineState.State.Selection);
}); });
TempoChangeBTN.onClick.AddListener(delegate TempoChangeBTN.onClick.AddListener(delegate
{ {
timelineState.SetState(false, true, false); timelineState.SetState(CurrentTimelineState.State.TempoChange);
}); });
MusicVolumeBTN.onClick.AddListener(delegate MusicVolumeBTN.onClick.AddListener(delegate
{ {
timelineState.SetState(false, false, true); timelineState.SetState(CurrentTimelineState.State.MusicVolume);
});
ChartSectionBTN.onClick.AddListener(delegate
{
timelineState.SetState(CurrentTimelineState.State.ChartSection);
}); });
Tooltip.AddTooltip(SongBeat.gameObject, "Current Beat"); Tooltip.AddTooltip(SongBeat.gameObject, "Current Beat");
@ -195,15 +258,21 @@ namespace HeavenStudio.Editor.Track
Tooltip.AddTooltip(SelectionsBTN.gameObject, "Tool: Selection <color=#adadad>[1]</color>"); Tooltip.AddTooltip(SelectionsBTN.gameObject, "Tool: Selection <color=#adadad>[1]</color>");
Tooltip.AddTooltip(TempoChangeBTN.gameObject, "Tool: Tempo Change <color=#adadad>[2]</color>"); Tooltip.AddTooltip(TempoChangeBTN.gameObject, "Tool: Tempo Change <color=#adadad>[2]</color>");
Tooltip.AddTooltip(MusicVolumeBTN.gameObject, "Tool: Music Volume <color=#adadad>[3]</color>"); Tooltip.AddTooltip(MusicVolumeBTN.gameObject, "Tool: Music Volume <color=#adadad>[3]</color>");
Tooltip.AddTooltip(ChartSectionBTN.gameObject, "Tool: Beatmap Sections <color=#adadad>[4]</color>");
Tooltip.AddTooltip(StartingTempoSpecialAll.gameObject, "Starting Tempo (BPM)");
Tooltip.AddTooltip(StartingTempoSpecialTempo.gameObject, "Starting Tempo (BPM)");
Tooltip.AddTooltip(StartingVolumeSpecialVolume.gameObject, "Starting Volume (%)");
Tooltip.AddTooltip(PlaybackSpeed.gameObject, "The preview's playback speed. Right click to reset to 1.0"); Tooltip.AddTooltip(PlaybackSpeed.gameObject, "The preview's playback speed. Right click to reset to 1.0");
SetTimeButtonColors(true, false, false); SetTimeButtonColors(true, false, false);
MetronomeBTN.transform.GetChild(0).GetComponent<Image>().color = Color.gray; MetronomeBTN.transform.GetChild(0).GetComponent<Image>().color = Color.gray;
timelineState.SetState(true, false, false); timelineState.SetState(CurrentTimelineState.State.Selection);
AutoBtnUpdate(); AutoBtnUpdate();
GameManager.instance.onSectionChange += OnSectionChange;
} }
public void FitToSong() public void FitToSong()
@ -269,6 +338,7 @@ namespace HeavenStudio.Editor.Track
SongBeat.text = $"Beat {string.Format("{0:0.000}", Conductor.instance.songPositionInBeats)}"; SongBeat.text = $"Beat {string.Format("{0:0.000}", Conductor.instance.songPositionInBeats)}";
SongPos.text = FormatTime(Conductor.instance.songPosition); SongPos.text = FormatTime(Conductor.instance.songPosition);
} }
TimelineSectionProgress.value = GameManager.instance.sectionProgress;
SliderControl(); SliderControl();
@ -300,15 +370,19 @@ namespace HeavenStudio.Editor.Track
if (Input.GetKeyDown(KeyCode.Alpha1)) if (Input.GetKeyDown(KeyCode.Alpha1))
{ {
timelineState.SetState(true, false, false); timelineState.SetState(CurrentTimelineState.State.Selection);
} }
else if (Input.GetKeyDown(KeyCode.Alpha2)) else if (Input.GetKeyDown(KeyCode.Alpha2))
{ {
timelineState.SetState(false, true, false); timelineState.SetState(CurrentTimelineState.State.TempoChange);
} }
else if (Input.GetKeyDown(KeyCode.Alpha3)) else if (Input.GetKeyDown(KeyCode.Alpha3))
{ {
timelineState.SetState(false, false, true); timelineState.SetState(CurrentTimelineState.State.MusicVolume);
}
else if (Input.GetKeyDown(KeyCode.Alpha4))
{
timelineState.SetState(CurrentTimelineState.State.ChartSection);
} }
@ -350,7 +424,7 @@ namespace HeavenStudio.Editor.Track
TimelineContent.transform.localPosition = new Vector3(Mathf.Clamp(TimelineContent.transform.localPosition.x, Mathf.NegativeInfinity, 0), TimelineContent.transform.localPosition.y); TimelineContent.transform.localPosition = new Vector3(Mathf.Clamp(TimelineContent.transform.localPosition.x, Mathf.NegativeInfinity, 0), TimelineContent.transform.localPosition.y);
CurrentTempo.text = $" = {Conductor.instance.songBpm}"; CurrentTempo.text = $" = {Conductor.instance.songBpm}";
LayersRect.GetWorldCorners(LayerCorners); LayersRect.GetWorldCorners(LayerCorners);
} }
@ -497,7 +571,7 @@ namespace HeavenStudio.Editor.Track
public bool CheckIfMouseInTimeline() public bool CheckIfMouseInTimeline()
{ {
return (this.gameObject.activeSelf && RectTransformUtility.RectangleContainsScreenPoint(TimelineContent.transform.parent.gameObject.GetComponent<RectTransform>(), Input.mousePosition, Editor.instance.EditorCamera)); return (this.gameObject.activeSelf && RectTransformUtility.RectangleContainsScreenPoint(TimelineEventGrid, Input.mousePosition, Editor.instance.EditorCamera));
} }
#endregion #endregion
@ -683,6 +757,95 @@ namespace HeavenStudio.Editor.Track
} }
} }
public void UpdateOffsetText()
{
FirstBeatOffset.text = (GameManager.instance.Beatmap.firstBeatOffset * 1000f).ToString("G");
}
public void UpdateOffsetFromText()
{
// Failsafe against empty string.
if (String.IsNullOrEmpty(FirstBeatOffset.text))
FirstBeatOffset.text = "0";
// Convert ms to s.
var newOffset = Convert.ToSingle(FirstBeatOffset.text) / 1000f;
// Limit decimal places to 4.
newOffset = (float)System.Math.Round(newOffset, 4);
GameManager.instance.Beatmap.firstBeatOffset = newOffset;
UpdateOffsetText();
}
public void UpdateStartingBPMText()
{
StartingTempoSpecialAll.text = GameManager.instance.Beatmap.bpm.ToString("G");
StartingTempoSpecialTempo.text = StartingTempoSpecialAll.text;
}
public void UpdateStartingBPMFromText(bool all)
{
string text = all ? StartingTempoSpecialAll.text : StartingTempoSpecialTempo.text;
// Failsafe against empty string.
if (String.IsNullOrEmpty(text))
text = "120";
var newBPM = Convert.ToDouble(text);
// Failsafe against negative BPM.
if (newBPM < 1f)
{
text = "1";
newBPM = 1;
}
// Limit decimal places to 4.
newBPM = System.Math.Round(newBPM, 4);
GameManager.instance.Beatmap.bpm = (float) newBPM;
// In case the newBPM ended up differing from the inputted string.
UpdateStartingBPMText();
Timeline.instance.FitToSong();
}
public void UpdateStartingVolText()
{
StartingVolumeSpecialVolume.text = (GameManager.instance.Beatmap.musicVolume).ToString("G");
}
public void UpdateStartingVolFromText()
{
// Failsafe against empty string.
if (String.IsNullOrEmpty(StartingVolumeSpecialVolume.text))
StartingVolumeSpecialVolume.text = "100";
var newVol = Convert.ToInt32(StartingVolumeSpecialVolume.text);
newVol = Mathf.Clamp(newVol, 0, 100);
GameManager.instance.Beatmap.musicVolume = newVol;
UpdateStartingVolText();
}
public void OnSectionChange(DynamicBeatmap.ChartSection section)
{
if (section == null)
{
TimelineSectionDisplay.SetActive(false);
}
else
{
if (!TimelineSectionDisplay.activeSelf)
TimelineSectionDisplay.SetActive(true);
TimelineSectionText.text = section.sectionName;
TimelineSectionProgress.value = GameManager.instance.sectionProgress;
}
}
#endregion #endregion
#region Commands #region Commands

View file

@ -14,7 +14,7 @@ namespace HeavenStudio.Editor.Track
private float startPosY; private float startPosY;
private Vector3 lastPos; private Vector3 lastPos;
public Vector2 lastPos_; public Vector2 moveStartPos;
private RectTransform rectTransform; private RectTransform rectTransform;
[Header("Components")] [Header("Components")]
@ -50,7 +50,7 @@ namespace HeavenStudio.Editor.Track
private void Start() private void Start()
{ {
lastPos_ = transform.localPosition; moveStartPos = transform.localPosition;
rectTransform = GetComponent<RectTransform>(); rectTransform = GetComponent<RectTransform>();
@ -163,13 +163,16 @@ namespace HeavenStudio.Editor.Track
this.wasDuplicated = false; this.wasDuplicated = false;
this.moving = false; this.moving = false;
transform.localPosition = moveStartPos;
OnComplete(false);
var te = Timeline.instance.CopyEventObject(this); var te = Timeline.instance.CopyEventObject(this);
Selections.instance.DragSelect(te); Selections.instance.DragSelect(te);
te.wasDuplicated = true; te.wasDuplicated = true;
te.transform.localPosition = transform.localPosition; te.transform.localPosition = transform.localPosition;
te.lastPos_ = transform.localPosition; te.moveStartPos = transform.localPosition;
for (int i = 0; i < Timeline.instance.eventObjs.Count; i++) for (int i = 0; i < Timeline.instance.eventObjs.Count; i++)
{ {
@ -179,12 +182,11 @@ namespace HeavenStudio.Editor.Track
te.moving = true; te.moving = true;
} }
else
{
lastPos_ = transform.localPosition; this.transform.position = new Vector3(mousePos.x - startPosX, mousePos.y - startPosY - 0.40f, 0);
this.transform.localPosition = new Vector3(Mathf.Max(Mathp.Round2Nearest(this.transform.localPosition.x, Timeline.SnapInterval()), 0), Timeline.instance.SnapToLayer(this.transform.localPosition.y));
this.transform.position = new Vector3(mousePos.x - startPosX, mousePos.y - startPosY - 0.40f, 0); }
this.transform.localPosition = new Vector3(Mathf.Max(Mathp.Round2Nearest(this.transform.localPosition.x, Timeline.SnapInterval()), 0), Timeline.instance.SnapToLayer(this.transform.localPosition.y));
if (lastPos != transform.localPosition) if (lastPos != transform.localPosition)
{ {
@ -281,7 +283,7 @@ namespace HeavenStudio.Editor.Track
{ {
if (selected && Timeline.instance.timelineState.selected) if (selected && Timeline.instance.timelineState.selected)
{ {
lastPos_ = transform.localPosition; moveStartPos = transform.localPosition;
for (int i = 0; i < Timeline.instance.eventObjs.Count; i++) for (int i = 0; i < Timeline.instance.eventObjs.Count; i++)
{ {
@ -314,6 +316,7 @@ namespace HeavenStudio.Editor.Track
if (eligibleToMove) if (eligibleToMove)
{ {
OnComplete(true); OnComplete(true);
moveStartPos = transform.localPosition;
} }
moving = false; moving = false;

View file

@ -1,89 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System;
using TMPro;
using Starpelly;
namespace HeavenStudio.Editor.Track
{
public class VolumeTimeline : MonoBehaviour
{
[Header("Components")]
private RectTransform rectTransform;
public TMP_InputField StartingVolume;
private RectTransform StartingVolumeRect;
public List<VolumeTimelineObj> volumeTimelineObjs = new List<VolumeTimelineObj>();
private bool firstUpdate;
void Start()
{
rectTransform = this.GetComponent<RectTransform>();
StartingVolumeRect = StartingVolume.GetComponent<RectTransform>();
}
void Update()
{
if (!firstUpdate)
{
UpdateStartingVolumeText();
firstUpdate = true;
}
if (Timeline.instance.userIsEditingInputField)
return;
if (Timeline.instance.timelineState.musicVolume && !Conductor.instance.NotStopped())
{
if (RectTransformUtility.RectangleContainsScreenPoint(StartingVolumeRect, Input.mousePosition, Editor.instance.EditorCamera))
{
int increase = Mathf.RoundToInt(Input.mouseScrollDelta.y);
if (Input.GetKey(KeyCode.LeftShift))
increase *= 5;
if (increase != 0)
{
GameManager.instance.Beatmap.musicVolume += increase;
UpdateStartingVolumeText();
UpdateStartingVolumeFromText(); // In case the scrolled-to value is invalid.
}
}
}
}
public void UpdateStartingVolumeText()
{
StartingVolume.text = GameManager.instance.Beatmap.musicVolume.ToString();
}
public void UpdateStartingVolumeFromText()
{
// Failsafe against empty string.
if (String.IsNullOrEmpty(StartingVolume.text))
StartingVolume.text = "100";
var newVol = Convert.ToInt32(StartingVolume.text);
// Failsafe against invalid volume.
if (newVol > 100)
{
StartingVolume.text = "100";
newVol = 100;
}
else if (newVol < 0)
{
StartingVolume.text = "0";
newVol = 0;
}
GameManager.instance.Beatmap.musicVolume = newVol;
// In case the newVol ended up differing from the inputted string.
UpdateStartingVolumeText();
}
}
}

View file

@ -1,105 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using DG.Tweening;
namespace HeavenStudio.Editor.Track
{
public class VolumeTimelineObj : MonoBehaviour
{
[Header("Components")]
[SerializeField] private RectTransform rectTransform;
[SerializeField] private TMP_Text volumeTXT;
[SerializeField] private RectTransform raycastRect;
public DynamicBeatmap.VolumeChange volumeChange;
private float startPosX;
private bool moving = false;
public bool hovering;
private float lastPosX;
private void Start()
{
rectTransform = GetComponent<RectTransform>();
volumeTXT = transform.GetChild(2).GetComponent<TMP_Text>();
UpdateVolume();
}
private void Update()
{
if (Timeline.instance.timelineState.musicVolume && !Conductor.instance.NotStopped())
{
if (RectTransformUtility.RectangleContainsScreenPoint(raycastRect, Input.mousePosition, Editor.instance.EditorCamera))
{
float newVolume = Input.mouseScrollDelta.y;
if (Input.GetKey(KeyCode.LeftShift))
newVolume *= 5f;
if (Input.GetKey(KeyCode.LeftControl))
newVolume /= 100f;
volumeChange.volume += newVolume;
//make sure volume is positive
volumeChange.volume = Mathf.Clamp(volumeChange.volume, 0, 100);
if (Input.GetMouseButtonDown(0))
{
Vector3 mousePos = Editor.instance.EditorCamera.ScreenToWorldPoint(Input.mousePosition);
startPosX = mousePos.x - transform.position.x;
moving = true;
lastPosX = transform.localPosition.x;
}
else if (Input.GetMouseButtonDown(1))
{
GameManager.instance.Beatmap.volumeChanges.Remove(volumeChange);
transform.parent.GetComponent<VolumeTimeline>().volumeTimelineObjs.Remove(this);
Destroy(this.gameObject);
}
hovering = true;
}
else
{
hovering = false;
}
if (moving)
{
Vector3 mousePos = Editor.instance.EditorCamera.ScreenToWorldPoint(Input.mousePosition);
transform.position = new Vector3(mousePos.x - startPosX, transform.position.y, 0);
transform.localPosition = new Vector3(Mathf.Clamp(Starpelly.Mathp.Round2Nearest(transform.localPosition.x, Timeline.SnapInterval()), 0, Mathf.Infinity), transform.localPosition.y);
}
if (Input.GetMouseButtonUp(0))
{
if (transform.parent.GetComponent<VolumeTimeline>().volumeTimelineObjs.Find(c => c.gameObject.transform.localPosition.x == this.transform.localPosition.x && c != this) != null)
{
transform.localPosition = new Vector3(lastPosX, transform.localPosition.y);
}
else
{
volumeChange.beat = transform.localPosition.x;
}
moving = false;
lastPosX = transform.localPosition.x;
}
UpdateVolume();
}
}
private void UpdateVolume()
{
volumeTXT.text = $"{volumeChange.volume}%";
Timeline.instance.FitToSong();
}
}
}

View file

@ -5,8 +5,11 @@
"name": "Heaven Studio Default Editor Theme", "name": "Heaven Studio Default Editor Theme",
"properties": { "properties": {
"SpecialLayersCol": "f06d60",
"TempoLayerCol": "6cbcc4", "TempoLayerCol": "6cbcc4",
"MusicLayerCol": "a663cc", "MusicLayerCol": "a663cc",
"SectionLayerCol": "ed9b2f",
//A7F085
"Layer1Col": "ef476f", "Layer1Col": "ef476f",
"Layer2Col": "f5813d", "Layer2Col": "f5813d",

View file

@ -8,8 +8,8 @@ AudioManager:
Rolloff Scale: 1 Rolloff Scale: 1
Doppler Factor: 1 Doppler Factor: 1
Default Speaker Mode: 2 Default Speaker Mode: 2
m_SampleRate: 96000 m_SampleRate: 48000
m_DSPBufferSize: 1024 m_DSPBufferSize: 512
m_VirtualVoiceCount: 512 m_VirtualVoiceCount: 512
m_RealVoiceCount: 32 m_RealVoiceCount: 32
m_EnableOutputSuspension: 1 m_EnableOutputSuspension: 1
@ -17,4 +17,4 @@ AudioManager:
m_AmbisonicDecoderPlugin: m_AmbisonicDecoderPlugin:
m_DisableAudio: 0 m_DisableAudio: 0
m_VirtualizeEffects: 1 m_VirtualizeEffects: 1
m_RequestedDSPBufferSize: 0 m_RequestedDSPBufferSize: 512

View file

@ -320,7 +320,9 @@ PlayerSettings:
Android: 1 Android: 1
iPhone: 1 iPhone: 1
tvOS: 1 tvOS: 1
m_BuildTargetGroupLightmapEncodingQuality: [] m_BuildTargetGroupLightmapEncodingQuality:
- m_BuildTarget: Standalone
m_EncodingQuality: 1
m_BuildTargetGroupLightmapSettings: [] m_BuildTargetGroupLightmapSettings: []
m_BuildTargetNormalMapEncoding: [] m_BuildTargetNormalMapEncoding: []
playModeTestRunnerEnabled: 0 playModeTestRunnerEnabled: 0