321 lines
12 KiB
C#
321 lines
12 KiB
C#
using System;
|
||
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using Cinemachine;
|
||
using Convention;
|
||
using Convention.WindowsUI.Variant;
|
||
using Unity.Profiling;
|
||
using UnityEngine;
|
||
using UnityEngine.InputSystem;
|
||
using UnityEngine.SceneManagement;
|
||
|
||
namespace Demo.Game
|
||
{
|
||
public partial class GameController : MonoBehaviour
|
||
{
|
||
[Resources, SerializeField] public BasicAudioSystem MainAudio;
|
||
[Resources, SerializeField] private CinemachineVirtualCamera MainCamera;
|
||
[Resources, SerializeField] public GlobalConfig MainConfig;
|
||
|
||
[Content] private RootObject MainObject;
|
||
|
||
[Content] public bool IsHideTrackRender = false;
|
||
|
||
public string RootSourcePath { get; private set; }
|
||
public Action<float, float> SetupSongDuration { get; private set; } = (_, _) => { };
|
||
public Action<float> SetSongCurrentTime { get; private set; } = _ => { };
|
||
public bool IsMain { get; set; } = false;
|
||
public bool IsAutoPlay { get; private set; } = false;
|
||
public string ScriptEditor { get; private set; } = "{0}";
|
||
public ProjectDefaultFileStyle CurrentProjectDefaultFileStyle = default;
|
||
|
||
public float SongOffset;
|
||
public float CurrentTime => MainAudio.CurrentTime - SongOffset;
|
||
|
||
public Transform MainCameraTransform => MainCamera.transform;
|
||
|
||
[Header("Environment")]
|
||
[Resources] public Transform GlobalLight;
|
||
|
||
public IEnumerator GameInit()
|
||
{
|
||
GameContent content = GameContent.instance;
|
||
yield return new WaitUntil(() => content != null);
|
||
|
||
try
|
||
{
|
||
RootSourcePath = content.RootSourceDir;
|
||
MainConfig = new(content.RootSourceDir, content.IsCreateNewProject, true);
|
||
Debug.Log($"{content.RootSourceDir} loading", this);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.LogException(ex, this);
|
||
Debug.LogError($"{content.RootSourceDir} is not a valid project", this);
|
||
StartCoroutine(GameExit());
|
||
yield break;
|
||
}
|
||
|
||
// Load Song
|
||
if (Editor.EditorController.instance.MainGameController == this)
|
||
{
|
||
string clipPath = (string)MainConfig.FindItem("song", "");
|
||
AudioType audioType = (AudioType)MainConfig.FindItem("audioType", BasicAudioSystem.GetAudioType(clipPath));
|
||
if (string.IsNullOrEmpty(clipPath))
|
||
{
|
||
foreach (var file in MainConfig.ConfigFile.BackToParentDir().DirToolFileIter())
|
||
{
|
||
if (file.IsFile() && !file.IsFileEmpty())
|
||
{
|
||
if (BasicAudioSystem.GetAudioType(file.GetExtension()) != AudioType.UNKNOWN)
|
||
{
|
||
clipPath = file.GetFullPath();
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (string.IsNullOrEmpty(clipPath) == false)
|
||
{
|
||
IEnumerator Run()
|
||
{
|
||
#if ENABLE_CLASS_Interaction
|
||
var clipFile = new Interaction(clipPath);
|
||
#else
|
||
var clipFile = new ToolFile(clipPath);
|
||
#endif
|
||
if (clipFile.Exists() == false)
|
||
clipFile = new(MainConfig.GetFile(clipPath).GetFullPath());
|
||
if (clipFile.Exists() == false)
|
||
{
|
||
Debug.LogError($"Cannt load {clipPath}", this);
|
||
yield break;
|
||
}
|
||
yield return MainAudio.LoadAudio(clipFile, audioType);
|
||
content.SongLoadOverCallback(MainAudio);
|
||
yield return GameAudioSystemInit();
|
||
}
|
||
|
||
StartCoroutine(Run());
|
||
}
|
||
}
|
||
else
|
||
{
|
||
MainAudio = Editor.EditorController.instance.MainGameController.MainAudio;
|
||
yield return GameAudioSystemInit();
|
||
}
|
||
|
||
yield return null;
|
||
|
||
// Setup Game Rules
|
||
if (Editor.EditorController.instance.MainGameController == this)
|
||
{
|
||
ScriptableObject.FastScriptableObjectTypen = content.ScriptableObjectTypen;
|
||
ScriptableObject.IsAutoPlay = content.IsAutoPlay;
|
||
ScriptableObject.OneBarTime = (float)MainConfig.FindItem(nameof(Editor.EditorController.BPM), Editor.EditorController.instance.BPM);
|
||
SetupSongDuration = GameContent.instance.SetupSongDuration;
|
||
SetSongCurrentTime = GameContent.instance.SetSongCurrentTime;
|
||
SongOffset = GameContent.instance.SongOffset;
|
||
}
|
||
{
|
||
IsHideTrackRender = (bool)MainConfig.FindItem(nameof(IsHideTrackRender), false);
|
||
IsAutoPlay = GameContent.instance.IsAutoPlay;
|
||
ScriptEditor = (string)MainConfig.FindItem(nameof(ScriptEditor), ScriptEditor);
|
||
CurrentProjectDefaultFileStyle = content.CurrentProjectDefaultFileStyle;
|
||
}
|
||
|
||
yield return null;
|
||
|
||
MainConfig.SaveProperties();
|
||
|
||
// Load Root Object
|
||
{
|
||
while (MainConfig.Contains("root") == false)
|
||
{
|
||
string defaultRootPath = "root" + CurrentProjectDefaultFileStyle switch
|
||
{
|
||
ProjectDefaultFileStyle.PY => ".py",
|
||
_ => ".h"
|
||
};
|
||
if (content.IsCreateNewProject)
|
||
{
|
||
MainConfig["root"] = defaultRootPath;
|
||
if (MainConfig.CreateFile(defaultRootPath))
|
||
{
|
||
MainConfig.SaveProperties();
|
||
break;
|
||
}
|
||
}
|
||
Debug.LogError($"{nameof(defaultRootPath)} is cannt create or config's root property is not exist", this);
|
||
StartCoroutine(GameExit());
|
||
yield break;
|
||
}
|
||
var rootFileName = (string)MainConfig.FindItem("root");
|
||
var rootObject = new ToolFile(Path.Combine(content.RootSourceDir, rootFileName));
|
||
rootObject.MustExistsPath();
|
||
var rootGameObject = new GameObject(rootObject.GetName(true)).AddComponent<RootObject>();
|
||
rootGameObject.transform.SetParent(transform);
|
||
rootGameObject.ScriptName = rootObject.GetName(true);
|
||
rootGameObject.audioSystem = MainAudio;
|
||
rootGameObject.EnableScript(content.RootSourceDir, rootObject.GetFullPath(), this);
|
||
try
|
||
{
|
||
yield return rootGameObject.LoadScript(rootObject.LoadAsText());
|
||
}
|
||
finally
|
||
{
|
||
MainObject = rootGameObject;
|
||
}
|
||
}
|
||
}
|
||
|
||
public IEnumerator GameInitBySubWorld(List<RootObject.InputCatchEntry> entrys)
|
||
{
|
||
yield return GameInit();
|
||
MainObject.InputCatch = entrys;
|
||
}
|
||
|
||
[Content, SerializeField] private bool IsEnableUpdate = false;
|
||
|
||
private IEnumerator GameAudioSystemInit()
|
||
{
|
||
yield return null;
|
||
IsEnableUpdate = true;
|
||
yield return new WaitUntil(() => MainObject != null);
|
||
MainObject.ScriptUpdate(0, Time.deltaTime, ScriptableObject.TickType.Reset);
|
||
}
|
||
|
||
|
||
public void Stop()
|
||
{
|
||
MainAudio.Stop();
|
||
if (IsMain)
|
||
{
|
||
SetSongCurrentTime(-SongOffset);
|
||
}
|
||
MainObject.ScriptUpdate(-SongOffset, Time.deltaTime, ScriptableObject.TickType.Reset);
|
||
}
|
||
|
||
public void Pause()
|
||
{
|
||
MainAudio.Pause();
|
||
if (IsMain)
|
||
{
|
||
SetSongCurrentTime(CurrentTime);
|
||
}
|
||
MainObject.ScriptUpdate(CurrentTime, Time.deltaTime, ScriptableObject.TickType.Pause);
|
||
}
|
||
|
||
public void Play()
|
||
{
|
||
MainAudio.Play();
|
||
if (IsMain)
|
||
{
|
||
GameContent.instance.SetupSongDuration(0, MainAudio.CurrentClip.length);
|
||
}
|
||
SetSongCurrentTime(CurrentTime);
|
||
MainObject.ScriptUpdate(CurrentTime, Time.deltaTime, ScriptableObject.TickType.Start);
|
||
}
|
||
|
||
public void ForceScriptUpdate(ScriptableObject.TickType type = ScriptableObject.TickType.Update)
|
||
{
|
||
MainObject.ScriptUpdate(CurrentTime, Time.deltaTime, type);
|
||
}
|
||
|
||
private bool IsScrollTimeline = false;
|
||
|
||
#if UNITY_EDITOR
|
||
public ProfilerMarker s_PreparePerfMarker = new(nameof(GameController) + "Runtime");
|
||
#endif
|
||
|
||
private void Update()
|
||
{
|
||
float deltaTime = Time.deltaTime;
|
||
var currentClip = MainAudio.CurrentClip;
|
||
|
||
if (MainObject == null || IsEnableUpdate == false)
|
||
return;
|
||
// 因为涉及到其他UI和时间切片的全局更新,所以需要提前
|
||
// TODO : 修正这个逻辑,这个逻辑是反常的
|
||
if (MainAudio.IsPlaying())
|
||
{
|
||
SetSongCurrentTime(CurrentTime);
|
||
MainObject.ScriptUpdate(CurrentTime, deltaTime, ScriptableObject.TickType.Update);
|
||
}
|
||
if (IsMain == false)
|
||
return;
|
||
|
||
#if UNITY_EDITOR
|
||
s_PreparePerfMarker.Begin(this);
|
||
#endif
|
||
#if UNITY_EDITOR
|
||
if (Keyboard.current[Key.LeftShift].isPressed)
|
||
#else
|
||
if (Keyboard.current[Key.LeftCtrl].isPressed)
|
||
#endif
|
||
{
|
||
if (currentClip != null)
|
||
{
|
||
if (Keyboard.current[Key.P].wasPressedThisFrame)
|
||
{
|
||
if (MainAudio.IsPlaying())
|
||
{
|
||
Pause();
|
||
}
|
||
else
|
||
{
|
||
Play();
|
||
}
|
||
}
|
||
else if (Keyboard.current[Key.S].wasPressedThisFrame)
|
||
{
|
||
Stop();
|
||
}
|
||
var scrollTime = Mouse.current.scroll.ReadValue().y / 120.0f;
|
||
if (Mathf.Approximately(scrollTime, 0) == false)
|
||
{
|
||
IsScrollTimeline = true;
|
||
if (MainAudio.IsPlaying())
|
||
{
|
||
Pause();
|
||
}
|
||
MainAudio.CurrentTime = Mathf.Clamp(MainAudio.CurrentTime + scrollTime, 0, currentClip.length);
|
||
if (IsMain)
|
||
{
|
||
SetSongCurrentTime(CurrentTime);
|
||
}
|
||
MainObject.ScriptUpdate(CurrentTime, deltaTime, ScriptableObject.TickType.Update);
|
||
}
|
||
else if (IsScrollTimeline == true)
|
||
{
|
||
IsScrollTimeline = false;
|
||
MainObject.ScriptUpdate(CurrentTime, deltaTime, ScriptableObject.TickType.Reset);
|
||
MainObject.ScriptUpdate(CurrentTime, deltaTime, ScriptableObject.TickType.Update);
|
||
}
|
||
}
|
||
}
|
||
#if UNITY_EDITOR
|
||
s_PreparePerfMarker.End();
|
||
#endif
|
||
}
|
||
|
||
public IEnumerator GameExit()
|
||
{
|
||
try
|
||
{
|
||
yield return MainObject.UnloadScript();
|
||
}
|
||
finally
|
||
{
|
||
Destroy(MainObject.gameObject);
|
||
if (Editor.EditorController.instance.MainGameController == this)
|
||
{
|
||
Editor.EditorController.instance.MainGameController = null;
|
||
}
|
||
SceneManager.UnloadSceneAsync(gameObject.scene, UnloadSceneOptions.UnloadAllEmbeddedSceneObjects);
|
||
}
|
||
}
|
||
}
|
||
}
|