1
This commit is contained in:
125
Assets/Scripts/GeneralExtensions.cs
Normal file
125
Assets/Scripts/GeneralExtensions.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using Random = UnityEngine.Random;
|
||||
|
||||
namespace Ichni
|
||||
{
|
||||
public static class GeneralExtensions
|
||||
{
|
||||
public static void Invoke(this Dictionary<string, UnityAction> dictionary)
|
||||
{
|
||||
foreach (var action in dictionary.Values)
|
||||
{
|
||||
action.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
public static void Invoke<T>(this Dictionary<string, UnityAction<T>> dictionary, T arg)
|
||||
{
|
||||
foreach (var action in dictionary.Values)
|
||||
{
|
||||
action.Invoke(arg);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Invoke<T1, T2>(this Dictionary<string, UnityAction<T1, T2>> dictionary, T1 arg1, T2 arg2)
|
||||
{
|
||||
foreach (var action in dictionary.Values)
|
||||
{
|
||||
action.Invoke(arg1, arg2);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 遍历列表中的每个元素并执行指定的操作,用于foreach不能遍历的情况,例如在foreach中删除元素
|
||||
/// </summary>
|
||||
public static void For<T>(this IList<T> list, UnityAction<T> action)
|
||||
{
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
{
|
||||
action.Invoke(list[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将列表中的元素从此列表转移到另一个列表
|
||||
/// </summary>
|
||||
public static void Transfer<T>(this List<T> fromList, List<T> toList, T item)
|
||||
{
|
||||
if (fromList.Contains(item))
|
||||
{
|
||||
fromList.Remove(item);
|
||||
toList.Add(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"Item {item} not found in this List.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在列表中交换两个元素
|
||||
/// </summary>
|
||||
public static void Swap<T>(this IList<T> list, int i, int j)
|
||||
{
|
||||
(list[i], list[j]) = (list[j], list[i]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 随机打乱列表中的元素
|
||||
/// </summary>
|
||||
public static void Shuffle<T>(this IList<T> list)
|
||||
{
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
{
|
||||
list.Swap(i, Random.Range(i, list.Count));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 随机获取列表中的元素
|
||||
/// </summary>
|
||||
public static List<T> GetRandomElements<T>(this List<T> list, int count)
|
||||
{
|
||||
List<T> randomElements = new List<T>();
|
||||
|
||||
if (count >= list.Count)
|
||||
{
|
||||
randomElements = new List<T>(list);
|
||||
return randomElements;
|
||||
}
|
||||
|
||||
List<int> randomIndices = new List<int>();
|
||||
while (randomIndices.Count < count)
|
||||
{
|
||||
int randomIndex = Random.Range(0, list.Count);
|
||||
if (!randomIndices.Contains(randomIndex))
|
||||
{
|
||||
randomIndices.Add(randomIndex);
|
||||
randomElements.Add(list[randomIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
return randomElements;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除所有满足 predicate 的键值对。
|
||||
/// </summary>
|
||||
public static void RemoveWhere<TKey, TValue>(this IDictionary<TKey, TValue> dict, Func<TKey, TValue, bool> predicate)
|
||||
{
|
||||
dict.Where(pair => predicate(pair.Key, pair.Value)).Select(pair => pair.Key).ToList().ForEach(key => dict.Remove(key));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有子物体
|
||||
/// </summary>
|
||||
public static List<Transform> GetAllChildren(this Transform parent)
|
||||
{
|
||||
return parent.Cast<Transform>().ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/GeneralExtensions.cs.meta
Normal file
11
Assets/Scripts/GeneralExtensions.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ee9b362a3cc19d14eb7a39d2caac7e6d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -38,7 +38,7 @@ namespace Ichni
|
||||
public Canvas judgeHintCanvas;
|
||||
public GameUIController gameUIController;
|
||||
|
||||
public float songTime => audioManager.musicPlayer.songTimeSegment;
|
||||
public float songTime => audioManager.songPlayer.songTimeSegment;
|
||||
|
||||
public bool isDebugging;
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace Ichni
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (!GameManager.instance.audioManager.musicPlayer.isPlaying)
|
||||
if (!GameManager.instance.audioManager.songPlayer.isPlaying)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -146,7 +146,7 @@ namespace Ichni
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
if (!GameManager.instance.audioManager.musicPlayer.isPlaying)
|
||||
if (!GameManager.instance.audioManager.songPlayer.isPlaying)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace Ichni
|
||||
(GameManager.instance.beatmapContainer.matchedBM as BeatmapContainer_BM).remainingElementAmount.Value == 0)
|
||||
.First().Subscribe(_ =>
|
||||
{
|
||||
GameManager.instance.audioManager.musicPlayer.PlayMusic();
|
||||
GameManager.instance.audioManager.songPlayer.PlaySong();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Ichni.Menu;
|
||||
using Ichni.UI;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Ichni
|
||||
{
|
||||
public partial class ChapterSelectionManager : SerializedMonoBehaviour
|
||||
{
|
||||
public static ChapterSelectionManager instance;
|
||||
|
||||
public ChapterSelectionUIPage chapterSelectionUIPage;
|
||||
public List<ChapterSelectionUnit> chapters;
|
||||
|
||||
}
|
||||
|
||||
public partial class ChapterSelectionManager
|
||||
{
|
||||
private void Awake()
|
||||
{
|
||||
instance = this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8aa09572a86d1ab47af92aa372e1f90d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AK.Wwise;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
|
||||
@@ -14,16 +16,37 @@ namespace Ichni.Menu
|
||||
public string chapterSubtitle;
|
||||
public Color themeColor;
|
||||
public Sprite avatar;
|
||||
public Switch chapterSwitch;
|
||||
|
||||
[ListDrawerSettings(ShowFoldout = true)]
|
||||
public List<SongItemData> songs = new List<SongItemData>();
|
||||
|
||||
[Button]
|
||||
public void SetUpDefaultDifficulties()
|
||||
{
|
||||
foreach (SongItemData song in songs)
|
||||
{
|
||||
if(song.difficultyDataList.All(d => d.difficultyName != "Easy"))
|
||||
{
|
||||
song.difficultyDataList.Add(new DifficultyData("Easy","", "",
|
||||
new Color(0f, 0.7f, 0.2f, 1f)));
|
||||
}
|
||||
|
||||
if (song.difficultyDataList.All(d => d.difficultyName != "Hard"))
|
||||
{
|
||||
song.difficultyDataList.Add(new DifficultyData("Hard", "", "",
|
||||
new Color(1f, 0.2f, 0.2f, 1f)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[InlineProperty]
|
||||
[Serializable]
|
||||
public class SongItemData
|
||||
{
|
||||
[HideLabel, FoldoutGroup("$songName", true)]
|
||||
[FoldoutGroup("$songName", true)]
|
||||
public string songName;
|
||||
|
||||
[FoldoutGroup("$songName")]
|
||||
@@ -36,7 +59,7 @@ namespace Ichni.Menu
|
||||
public bool isNewSong;
|
||||
|
||||
[FoldoutGroup("$songName")]
|
||||
public AK.Wwise.Switch songSwitch;
|
||||
public Switch songSwitch;
|
||||
|
||||
[FoldoutGroup("$songName")]
|
||||
public Sprite albumIconCover;
|
||||
@@ -52,16 +75,6 @@ namespace Ichni.Menu
|
||||
|
||||
[FoldoutGroup("$songName")]
|
||||
public List<DifficultyData> difficultyDataList;
|
||||
|
||||
[Button]
|
||||
[FoldoutGroup("$songName")]
|
||||
public void AddDefaultDifficulties()
|
||||
{
|
||||
difficultyDataList.Add(
|
||||
new DifficultyData("Easy","", "", new Color(0f, 0.7f, 0.2f, 1f)));
|
||||
difficultyDataList.Add(
|
||||
new DifficultyData("Hard","", "", new Color(1f, 0.2f, 0.2f, 1f)));
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
@@ -2,25 +2,39 @@ using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using AK.Wwise;
|
||||
using Ichni.UI;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace Ichni.Menu
|
||||
namespace Ichni
|
||||
{
|
||||
public partial class MenuManager : MonoBehaviour
|
||||
{
|
||||
public static MenuManager instance;
|
||||
|
||||
[FormerlySerializedAs("preparePage")] public PrepareUIPage prepareUIPage;
|
||||
}
|
||||
|
||||
public partial class MenuManager
|
||||
{
|
||||
AsyncOperation asyncOperation;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
instance = this;
|
||||
Application.targetFrameRate = 120;
|
||||
/*asyncOperation = SceneManager.LoadSceneAsync("GameScene");
|
||||
asyncOperation.allowSceneActivation = false;*/
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
asyncOperation = SceneManager.LoadSceneAsync("GameScene");
|
||||
asyncOperation.allowSceneActivation = false;
|
||||
}
|
||||
}
|
||||
|
||||
public partial class MenuManager : MonoBehaviour
|
||||
public partial class MenuManager
|
||||
{
|
||||
public Sprite testSprite;
|
||||
public Switch testChapterSwitch;
|
||||
@@ -37,7 +51,7 @@ namespace Ichni.Menu
|
||||
"Trader",
|
||||
Color.red, testSprite,testChapterSwitch, testMusicSwitch);
|
||||
|
||||
SceneManager.LoadScene("GameScene");
|
||||
asyncOperation.allowSceneActivation = true;
|
||||
}
|
||||
|
||||
[Button]
|
||||
|
||||
8
Assets/Scripts/Menu/SongSelection.meta
Normal file
8
Assets/Scripts/Menu/SongSelection.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b7a92293064a12b44b29a26166706d0a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
18
Assets/Scripts/Menu/SongSelection/SongSelectionManager.cs
Normal file
18
Assets/Scripts/Menu/SongSelection/SongSelectionManager.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class SongSelectionManager : MonoBehaviour
|
||||
{
|
||||
// Start is called before the first frame update
|
||||
void Start()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 726b9313110aee54fbba1c1891bdaa1a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Scripts/Settings.meta
Normal file
8
Assets/Scripts/Settings.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0f7a3d96ccce4d840bd4911722a981b5
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
39
Assets/Scripts/Settings/GameSettings.cs
Normal file
39
Assets/Scripts/Settings/GameSettings.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Ichni
|
||||
{
|
||||
public class GameSettings
|
||||
{
|
||||
public int mainVolume = 100;
|
||||
public int backgroundMusicVolume = 100;
|
||||
public int soundEffectVolume = 100;
|
||||
public int uiVolume = 100;
|
||||
|
||||
public int judgeOffset = 0;
|
||||
public int targetFrame = 60;
|
||||
|
||||
public bool lowResolution = false;
|
||||
public bool debugMode = false;
|
||||
|
||||
public GameSettings()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public GameSettings(int mainVolume, int backgroundMusicVolume, int soundEffectVolume, int uiVolume,
|
||||
int judgeOffset, int targetFrame, bool lowResolution, bool debugMode)
|
||||
{
|
||||
this.mainVolume = mainVolume;
|
||||
this.backgroundMusicVolume = backgroundMusicVolume;
|
||||
this.soundEffectVolume = soundEffectVolume;
|
||||
this.uiVolume = uiVolume;
|
||||
this.judgeOffset = judgeOffset;
|
||||
this.targetFrame = targetFrame;
|
||||
this.lowResolution = lowResolution;
|
||||
this.debugMode = debugMode;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Settings/GameSettings.cs.meta
Normal file
11
Assets/Scripts/Settings/GameSettings.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c2ca67619927cac4a91d5b0612d6f7eb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
51
Assets/Scripts/Settings/SettingsManager.cs
Normal file
51
Assets/Scripts/Settings/SettingsManager.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Ichni.UI;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace Ichni
|
||||
{
|
||||
public class SettingsManager : SerializedMonoBehaviour
|
||||
{
|
||||
public static SettingsManager instance;
|
||||
|
||||
[FormerlySerializedAs("settingsPage")] public SettingsUIPage settingsUIPage;
|
||||
public GameSettings gameSettings;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
instance = this;
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
LoadGameSettings();
|
||||
}
|
||||
|
||||
[Button]
|
||||
public void SaveGameSettings()
|
||||
{
|
||||
string savePath = Application.persistentDataPath + "/GameData/GameSettings.json";
|
||||
ES3.Save("GameSettings", gameSettings, savePath);
|
||||
}
|
||||
|
||||
[Button]
|
||||
public void LoadGameSettings()
|
||||
{
|
||||
string savePath = Application.persistentDataPath + "/GameData/GameSettings.json";
|
||||
if (ES3.FileExists(savePath))
|
||||
{
|
||||
gameSettings = ES3.Load<GameSettings>("GameSettings", savePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
gameSettings = new GameSettings(
|
||||
100, 100, 100, 100,
|
||||
0, 60, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Settings/SettingsManager.cs.meta
Normal file
11
Assets/Scripts/Settings/SettingsManager.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 49501a5fe129ecf4ca83fd1c5d03411e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
93
Assets/Scripts/SpaceConverter.cs
Normal file
93
Assets/Scripts/SpaceConverter.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Ichni
|
||||
{
|
||||
public static class SpaceConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// 世界坐标转换为屏幕坐标
|
||||
/// </summary>
|
||||
/// <param name="worldPoint">屏幕坐标</param>
|
||||
/// <returns></returns>
|
||||
public static Vector2 WorldPointToScreenPoint(Vector3 worldPoint)
|
||||
{
|
||||
// Camera.main 世界摄像机
|
||||
Vector2 screenPoint = GameManager.instance.cameraManager.gameCamera.gameCamera.WorldToScreenPoint(worldPoint);
|
||||
return screenPoint;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 屏幕坐标转换为世界坐标
|
||||
/// </summary>
|
||||
/// <param name="screenPoint">屏幕坐标</param>
|
||||
/// <param name="planeZ">距离摄像机 Z 平面的距离</param>
|
||||
/// <returns></returns>
|
||||
public static Vector3 ScreenPointToWorldPoint(Vector2 screenPoint, float planeZ)
|
||||
{
|
||||
// Camera.main 世界摄像机
|
||||
Vector3 position = new Vector3(screenPoint.x, screenPoint.y, planeZ);
|
||||
Vector3 worldPoint = GameManager.instance.cameraManager.gameCamera.gameCamera.ScreenToWorldPoint(position);
|
||||
return worldPoint;
|
||||
}
|
||||
|
||||
|
||||
public static Vector2 WorldPointToUILocalPoint(RectTransform rt, Vector3 worldPosition)
|
||||
{
|
||||
// 将世界坐标转换为屏幕坐标
|
||||
var screenPosition = WorldPointToScreenPoint(worldPosition);
|
||||
// 将屏幕坐标转换为 UGUI 坐标
|
||||
Vector2 localPoint = ScreenPointToUILocalPoint(rt, screenPosition);
|
||||
|
||||
return localPoint;
|
||||
}
|
||||
|
||||
public static Vector3 WorldPointToUIPoint(RectTransform rt, Vector3 worldPosition)
|
||||
{
|
||||
// 将世界坐标转换为屏幕坐标
|
||||
var screenPosition = WorldPointToScreenPoint(worldPosition);
|
||||
// 将屏幕坐标转换为 UGUI 坐标
|
||||
Vector3 uiPoint = ScreenPointToUIPoint(rt, screenPosition);
|
||||
|
||||
return uiPoint;
|
||||
}
|
||||
|
||||
// RectTransformUtility.WorldToScreenPoint
|
||||
// RectTransformUtility.ScreenPointToWorldPointInRectangle
|
||||
// RectTransformUtility.ScreenPointToLocalPointInRectangle
|
||||
// 上面三个坐标转换的方法使用 Camera 的地方
|
||||
// 当 Canvas renderMode 为 RenderMode.ScreenSpaceCamera、RenderMode.WorldSpace 时 传递参数 canvas.worldCamera
|
||||
// 当 Canvas renderMode 为 RenderMode.ScreenSpaceOverlay 时 传递参数 null
|
||||
|
||||
// UI 坐标转换为屏幕坐标
|
||||
public static Vector2 UIPointToScreenPoint(Vector3 worldPoint)
|
||||
{
|
||||
Vector2 screenPoint = RectTransformUtility.WorldToScreenPoint(GameManager.instance.cameraManager.uiCamera, worldPoint);
|
||||
return screenPoint;
|
||||
}
|
||||
|
||||
// 屏幕坐标转换为 UGUI 坐标
|
||||
public static Vector3 ScreenPointToUIPoint(RectTransform rt, Vector2 screenPoint)
|
||||
{
|
||||
//UI屏幕坐标转换为世界坐标
|
||||
// 当 Canvas renderMode 为 RenderMode.ScreenSpaceCamera、RenderMode.WorldSpace 时 uiCamera 不能为空
|
||||
// 当 Canvas renderMode 为 RenderMode.ScreenSpaceOverlay 时 uiCamera 可以为空
|
||||
RectTransformUtility.ScreenPointToWorldPointInRectangle(rt, screenPoint, GameManager.instance.cameraManager.uiCamera, out Vector3 globalMousePos);
|
||||
// 转换后的 globalMousePos 使用下面方法赋值
|
||||
// target 为需要使用的 UI RectTransform
|
||||
// rt 可以是 target.GetComponent<RectTransform>(), 也可以是 target.parent.GetComponent<RectTransform>()
|
||||
// target.transform.position = globalMousePos;
|
||||
return globalMousePos;
|
||||
}
|
||||
|
||||
// 屏幕坐标转换为 UGUI RectTransform 的 anchoredPosition
|
||||
public static Vector2 ScreenPointToUILocalPoint(RectTransform parentRT, Vector2 screenPoint)
|
||||
{
|
||||
RectTransformUtility.ScreenPointToLocalPointInRectangle(parentRT, screenPoint, GameManager.instance.cameraManager.uiCamera, out Vector2 localPos);
|
||||
// 转换后的 localPos 使用下面方法赋值
|
||||
// target 为需要使用的 UI RectTransform
|
||||
// parentRT 是 target.parent.GetComponent<RectTransform>()
|
||||
// 最后赋值 target.anchoredPosition = localPos;
|
||||
return localPos;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/SpaceConverter.cs.meta
Normal file
11
Assets/Scripts/SpaceConverter.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1d0e13f7c5078234d9d2a058d3a929af
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Scripts/Story/Dialog.meta
Normal file
8
Assets/Scripts/Story/Dialog.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2de1e277bcf059c42826353d1c086ccc
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
400
Assets/Scripts/Story/Dialog/DialogManager.cs
Normal file
400
Assets/Scripts/Story/Dialog/DialogManager.cs
Normal file
@@ -0,0 +1,400 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Ichni.Story.UI;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace Ichni.Story
|
||||
{
|
||||
public partial class DialogManager : SerializedMonoBehaviour
|
||||
{
|
||||
public static DialogManager instance;
|
||||
|
||||
public List<TextAsset> dialogTextAssets;
|
||||
|
||||
public bool isPlayingDialog;
|
||||
public bool isPlayingChoice;
|
||||
|
||||
public string currentDialog;
|
||||
public int currentDialogSentenceIndex;
|
||||
public string currentFinalType;
|
||||
|
||||
public Dictionary<string, List<string>> functionDictionary;
|
||||
public Dictionary<string, List<DialogSentence>> dialogDictionary;
|
||||
public Dictionary<string, List<Choice>> choiceDictionary;
|
||||
public Dictionary<string, List<Condition>> conditionDictionary;
|
||||
|
||||
private string currentLoadingDialog;
|
||||
|
||||
[Header("Test")]
|
||||
public List<TextAsset> testTextAssets;
|
||||
|
||||
public DialogUIPage dialogUIPage;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
instance = this;
|
||||
}
|
||||
|
||||
public void SetDialog(string dialogName)
|
||||
{
|
||||
TextAsset dialog = Resources.Load<TextAsset>("Dialogs/" + dialogName);
|
||||
SetDialog(new List<TextAsset> { dialog }, "Entry");
|
||||
}
|
||||
|
||||
public void SetDialog(List<TextAsset> dialogFiles, string dialogParagraphName)
|
||||
{
|
||||
dialogUIPage.FadeIn();
|
||||
|
||||
currentDialog = "NULL";
|
||||
isPlayingDialog = true;
|
||||
LoadDialog(dialogFiles);
|
||||
|
||||
if (!string.IsNullOrEmpty(dialogParagraphName))
|
||||
{
|
||||
currentDialog = dialogParagraphName;
|
||||
}
|
||||
|
||||
PlayNextDialogParagraph(currentDialog);
|
||||
}
|
||||
|
||||
public void PlayNextDialogParagraph(string nextDialog)
|
||||
{
|
||||
currentDialog = nextDialog;
|
||||
currentDialogSentenceIndex = 0;
|
||||
|
||||
if (functionDictionary.TryGetValue(currentDialog, out List<string> functionList))
|
||||
{
|
||||
functionList.ForEach(x => StoryInterpreters.FunctionInterpreter.Eval(x));
|
||||
}
|
||||
|
||||
if (choiceDictionary.ContainsKey(currentDialog))
|
||||
{
|
||||
currentFinalType = "Choice";
|
||||
}
|
||||
else if (conditionDictionary.ContainsKey(currentDialog))
|
||||
{
|
||||
currentFinalType = "Condition";
|
||||
}
|
||||
else
|
||||
{
|
||||
currentFinalType = "None";
|
||||
}
|
||||
|
||||
PlayDialog();
|
||||
}
|
||||
|
||||
[Button("Test Play")]
|
||||
public void PlayDialog()
|
||||
{
|
||||
if(currentDialog == "NULL")
|
||||
{
|
||||
throw new Exception("Current dialog is NULL");
|
||||
}
|
||||
|
||||
/*if (dialogInterface.dialogTextFrame.isPlayingSentence)
|
||||
{
|
||||
dialogInterface.dialogTextFrame.FinishSentence();
|
||||
return;
|
||||
}*/
|
||||
|
||||
if (dialogDictionary[currentDialog].Count > 0 && currentDialogSentenceIndex < dialogDictionary[currentDialog].Count)
|
||||
{
|
||||
DialogSentence currentSentence = dialogDictionary[currentDialog][currentDialogSentenceIndex];
|
||||
|
||||
string interpretedContent = currentSentence.GetInterpretedContent();
|
||||
|
||||
dialogUIPage.textFrame.PlaySentence(currentSentence.characterName, interpretedContent);
|
||||
currentDialogSentenceIndex++;
|
||||
|
||||
if (currentDialogSentenceIndex <= dialogDictionary[currentDialog].Count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(currentDialogSentenceIndex >= dialogDictionary[currentDialog].Count)
|
||||
{
|
||||
if (currentFinalType == "Choice")
|
||||
{
|
||||
isPlayingChoice = true;
|
||||
dialogUIPage.choiceFrame.PlayChoice(choiceDictionary[currentDialog]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentFinalType == "Condition")
|
||||
{
|
||||
foreach (var condition in conditionDictionary[currentDialog])
|
||||
{
|
||||
if (condition.GetConditionResult())
|
||||
{
|
||||
PlayNextDialogParagraph(condition.nextDialogName);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (currentFinalType == "None" && currentDialogSentenceIndex >= dialogDictionary[currentDialog].Count)
|
||||
{
|
||||
dialogUIPage.FadeOut();
|
||||
dialogUIPage.choiceFrame.gameObject.SetActive(false);
|
||||
//currentDialogNPC.priorStoryTexts.Remove(dialogTextAsset);
|
||||
//currentDialogNPC = null;
|
||||
isPlayingDialog = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public partial class DialogManager
|
||||
{
|
||||
public void LoadDialog(List<TextAsset> dialogFiles)
|
||||
{
|
||||
ClearDictionaries();
|
||||
|
||||
dialogTextAssets = dialogFiles;
|
||||
List<string> dialogLines = new List<string>();
|
||||
|
||||
foreach (TextAsset textAsset in dialogTextAssets)
|
||||
{
|
||||
dialogLines.AddRange(ExtractValidFragments(textAsset.text));
|
||||
}
|
||||
|
||||
dialogLines.RemoveAll(line => line.Trim() == "");
|
||||
|
||||
dialogLines.ForEach(Debug.Log);
|
||||
|
||||
foreach (var line in from line in dialogLines
|
||||
where !ParseHeader(line)
|
||||
where !ParseChoiceModule(line)
|
||||
where !ParseConditionModule(line)
|
||||
where !ParseDialogSentence(line)
|
||||
select line)
|
||||
{
|
||||
throw new Exception($"Invalid dialog line: {line}"); // 抛出异常,提示不合法的对话行
|
||||
}
|
||||
|
||||
//dialogDictionary.RemoveWhere((header, sentences) => sentences == null || sentences.Count == 0);
|
||||
choiceDictionary.RemoveWhere((header, choices) => choices == null || choices.Count == 0);
|
||||
conditionDictionary.RemoveWhere((header, conditions) => conditions == null || conditions.Count == 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从原始大文本中提取所有以 '$' 开头的有效片段,忽略以 '#' 开头的注释片段。
|
||||
/// 拆分依据:每当遇到 '$' 或 '#' 字符,即视为一个新片段的起始。
|
||||
/// </summary>
|
||||
/// <param name="inputText">未分割的完整文本(可能包含任意换行或连续内容)。</param>
|
||||
/// <returns>剥离首 '$' 后的有效文本列表。</returns>
|
||||
public static List<string> ExtractValidFragments(string inputText)
|
||||
{
|
||||
if (inputText == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(inputText));
|
||||
}
|
||||
|
||||
// 正则:(?<prefix>[$#]) // 片段起始前缀
|
||||
// (?<content>.*? ) // 非贪婪捕获所有内容
|
||||
// (?=(?:[$#])|\z) // 直到下一个 '$'、'#' 或文末
|
||||
|
||||
const string pattern = @"(?<prefix>[$#])(?<content>.*?)(?=(?:[$#])|\z)";
|
||||
MatchCollection matches = Regex.Matches(inputText, pattern, RegexOptions.Singleline);
|
||||
|
||||
var result = new List<string>(matches.Count);
|
||||
foreach (Match m in matches)
|
||||
{
|
||||
char prefix = m.Groups["prefix"].Value[0];
|
||||
string content = m.Groups["content"].Value;
|
||||
|
||||
if (prefix == '$')
|
||||
{
|
||||
result.Add(content.Trim());
|
||||
}
|
||||
// prefix == '#' 时自动忽略
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public partial class DialogManager
|
||||
{
|
||||
public void ClearDictionaries()
|
||||
{
|
||||
dialogDictionary.Clear();
|
||||
choiceDictionary.Clear();
|
||||
conditionDictionary.Clear();
|
||||
functionDictionary.Clear();
|
||||
}
|
||||
|
||||
public bool ParseHeader(string line)
|
||||
{
|
||||
//格式:[currentLoadingDialog]{Function0();Function1();Function2();}
|
||||
line = line.Trim();
|
||||
|
||||
string dialogTitle = line.Split("{")[0];
|
||||
if (dialogTitle[0] != '[' || dialogTitle[^1] != ']')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
currentLoadingDialog = dialogTitle.Replace("[", "").Replace("]", "");
|
||||
|
||||
dialogDictionary.Add(currentLoadingDialog, new List<DialogSentence>());
|
||||
choiceDictionary.Add(currentLoadingDialog, new List<Choice>());
|
||||
conditionDictionary.Add(currentLoadingDialog, new List<Condition>());
|
||||
|
||||
if (currentDialog == "NULL")
|
||||
{
|
||||
currentDialog = currentLoadingDialog;
|
||||
}
|
||||
|
||||
if (!line.Contains("{")) // 这个Header没有函数需要执行
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
string functions = line.Split("{")[1];
|
||||
if (functions.Contains("}"))
|
||||
{
|
||||
functions = functions.Split("}")[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new System.Exception("Dialog header's function list must be enclosed in {}.");
|
||||
}
|
||||
|
||||
functions = functions.Replace(" ", "").Replace("\n", "").Replace("\r", "").Trim(); //忽略空格,换行
|
||||
|
||||
List<string> functionList = functions.Split(';').ToList(); //分割函数
|
||||
functionList = functionList.Where(x => !string.IsNullOrEmpty(x)).ToList(); //去除空函数
|
||||
functionDictionary.Add(currentLoadingDialog, functionList);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool ParseDialogSentence(string line)
|
||||
{
|
||||
//speakerName(emotion):sentence
|
||||
|
||||
string[] sentenceData;
|
||||
if (line.Contains(":"))
|
||||
{
|
||||
sentenceData = line.Split(":", 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string character = sentenceData[0];
|
||||
|
||||
string speakerName = character;
|
||||
string emotion = "Default";
|
||||
|
||||
|
||||
if (character.Contains("("))
|
||||
{
|
||||
emotion = character.Split("(")[1].Replace(")", "");
|
||||
speakerName = character.Split("(")[0];
|
||||
}
|
||||
else if (character.Contains("("))
|
||||
{
|
||||
emotion = character.Split("(")[1].Replace(")", "");
|
||||
speakerName = character.Split("(")[0];
|
||||
}
|
||||
|
||||
DialogSentence dialogSentence = new DialogSentence
|
||||
{
|
||||
characterName = speakerName,
|
||||
characterEmotion = emotion,
|
||||
content = sentenceData[1]
|
||||
};
|
||||
|
||||
|
||||
dialogDictionary[currentLoadingDialog].Add(dialogSentence);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool ParseChoiceModule(string line)
|
||||
{
|
||||
//$Choice{
|
||||
//choiceText0(Hint0)->[nextDialogName0];
|
||||
//choiceText1(Hint1)->[nextDialogName1];
|
||||
//}
|
||||
|
||||
line = line.Trim();
|
||||
|
||||
if (line.Contains("Choice"))
|
||||
{
|
||||
string[] choiceModuleData = line.Split('{');
|
||||
|
||||
List<Choice> choices = new List<Choice>();
|
||||
|
||||
string[] choiceData = choiceModuleData[1].Split(';');
|
||||
|
||||
for (var index = 0; index < choiceData.Length - 1; index++)
|
||||
{
|
||||
Choice choice = new Choice
|
||||
{
|
||||
choiceText = choiceData[index].Split("->")[0].Split("(")[0].Trim(),
|
||||
hint = choiceData[index].Split("->")[0].Split("(")[1].Replace(")", "").Trim(),
|
||||
nextDialogName = choiceData[index].Split("->[")[1].Replace("]", "").Trim(),
|
||||
};
|
||||
|
||||
choices.Add(choice);
|
||||
}
|
||||
|
||||
choiceDictionary[currentLoadingDialog] = choices;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 解析条件模块
|
||||
/// </summary>
|
||||
/// <param name="line"></param>
|
||||
/// <returns></returns>
|
||||
private bool ParseConditionModule(string line)
|
||||
{
|
||||
//$Condition{
|
||||
//conditionSentence0->[nextDialogName0];
|
||||
//conditionSentence1->[nextDialogName1];
|
||||
//}
|
||||
|
||||
if (line.Contains("Condition"))
|
||||
{
|
||||
string[] conditionModuleData = line.Split('{');
|
||||
|
||||
List<Condition> conditions = new List<Condition>();
|
||||
|
||||
string[] conditionData = conditionModuleData[1].Split(';');
|
||||
|
||||
for (var index = 0; index < conditionData.Length - 1; index++)
|
||||
{
|
||||
conditionData[index] = conditionData[index].Replace(" ", "").Replace("\n", "").Replace("\r", "").Trim();
|
||||
Condition condition = new Condition
|
||||
{
|
||||
conditionSentence = conditionData[index].Split("->[")[0].Trim(),
|
||||
nextDialogName = conditionData[index].Split("->[")[1].Replace("]", "").Trim(),
|
||||
};
|
||||
|
||||
conditions.Add(condition);
|
||||
}
|
||||
|
||||
conditionDictionary[currentLoadingDialog] = conditions;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Story/Dialog/DialogManager.cs.meta
Normal file
11
Assets/Scripts/Story/Dialog/DialogManager.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9e89b9d7eea97734baa166072b050239
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
91
Assets/Scripts/Story/Dialog/DialogModule.cs
Normal file
91
Assets/Scripts/Story/Dialog/DialogModule.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Ichni.Story
|
||||
{
|
||||
public class DialogSentence
|
||||
{
|
||||
public string content;
|
||||
public string audioEventName;
|
||||
|
||||
public string characterName;
|
||||
public string characterEmotion;
|
||||
|
||||
public DialogSentence()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public DialogSentence(string content, string audioEventName, string characterName, string characterEmotion)
|
||||
{
|
||||
this.content = content;
|
||||
this.audioEventName = audioEventName;
|
||||
this.characterName = characterName;
|
||||
this.characterEmotion = characterEmotion;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 匹配{@FUNCTION},解析函数并返回解析后的语句内容。
|
||||
/// </summary>
|
||||
public string GetInterpretedContent()
|
||||
{
|
||||
List<string> parts = new List<string>();
|
||||
Regex regex = new Regex(@"\{\@.*?\}");
|
||||
int lastIndex = 0;
|
||||
|
||||
foreach (Match match in regex.Matches(content))
|
||||
{
|
||||
if (match.Index > lastIndex)
|
||||
{
|
||||
parts.Add(content.Substring(lastIndex, match.Index - lastIndex));
|
||||
}
|
||||
parts.Add(match.Value);
|
||||
lastIndex = match.Index + match.Length;
|
||||
}
|
||||
|
||||
if (lastIndex < content.Length)
|
||||
{
|
||||
parts.Add(content.Substring(lastIndex));
|
||||
}
|
||||
|
||||
for (int i = 0; i < parts.Count; i++)
|
||||
{
|
||||
if (parts[i].StartsWith("{@") && parts[i].EndsWith("}"))
|
||||
{
|
||||
string expression = parts[i].Substring(2, parts[i].Length - 3);
|
||||
object result = StoryInterpreters.FunctionInterpreter.Eval(expression);
|
||||
parts[i] = result.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
return string.Join("", parts);
|
||||
}
|
||||
}
|
||||
|
||||
public class Choice
|
||||
{
|
||||
public string choiceText;
|
||||
public string hint;
|
||||
public string nextDialogName;
|
||||
}
|
||||
|
||||
public class Condition
|
||||
{
|
||||
public string conditionSentence;
|
||||
public string nextDialogName;
|
||||
|
||||
public bool GetConditionResult()
|
||||
{
|
||||
bool result = StoryInterpreters.ConditionInterpreter.Eval<bool>(conditionSentence);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public class DialogCharacter
|
||||
{
|
||||
public string name;
|
||||
public string title;
|
||||
public Dictionary<string, Sprite> emotions;
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Story/Dialog/DialogModule.cs.meta
Normal file
11
Assets/Scripts/Story/Dialog/DialogModule.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d32abf19c3d0f4546b2b86f9fcc4e117
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
48
Assets/Scripts/Story/Dialog/StoryInterpreters.cs
Normal file
48
Assets/Scripts/Story/Dialog/StoryInterpreters.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using DynamicExpresso;
|
||||
using Ichni.Story;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Ichni.Story
|
||||
{
|
||||
public static partial class StoryInterpreters
|
||||
{
|
||||
public static readonly Interpreter FunctionInterpreter;
|
||||
public static readonly Interpreter ConditionInterpreter;
|
||||
|
||||
static StoryInterpreters()
|
||||
{
|
||||
FunctionInterpreter = new Interpreter();
|
||||
ConditionInterpreter = new Interpreter();
|
||||
|
||||
SetFunctionInterpreter();
|
||||
SetConditionInterpreter();
|
||||
}
|
||||
|
||||
static void SetFunctionInterpreter()
|
||||
{
|
||||
FunctionInterpreter.SetFunction("GetGlobalVariable", new Func<string, int>(GetGlobalVariable));
|
||||
}
|
||||
|
||||
static void SetConditionInterpreter()
|
||||
{
|
||||
ConditionInterpreter.SetFunction("GetGlobalVariable", new Func<string, int>(GetGlobalVariable));
|
||||
}
|
||||
}
|
||||
|
||||
public static partial class StoryInterpreters
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取全局变量的值
|
||||
/// </summary>
|
||||
static int GetGlobalVariable(string variableName)
|
||||
{
|
||||
if (StoryManager.instance.globalVariables.TryGetValue(variableName, out int value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Global variable '{variableName}' not found.");
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Story/Dialog/StoryInterpreters.cs.meta
Normal file
11
Assets/Scripts/Story/Dialog/StoryInterpreters.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dd119fb631aebb548a637407d15c5eea
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
25
Assets/Scripts/Story/Dialog/StoryManager.cs
Normal file
25
Assets/Scripts/Story/Dialog/StoryManager.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Ichni.Story.UI;
|
||||
using Ichni.UI;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace Ichni.Story
|
||||
{
|
||||
public class StoryManager : SerializedMonoBehaviour
|
||||
{
|
||||
public static StoryManager instance;
|
||||
|
||||
public StorylineDisplay storylineDisplay;
|
||||
[FormerlySerializedAs("StoryPage")] public StoryUIPage storyUIPage;
|
||||
|
||||
public Dictionary<string, int> globalVariables;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
instance = this;
|
||||
}
|
||||
}
|
||||
}
|
||||
28
Assets/Scripts/Story/StoryData.cs
Normal file
28
Assets/Scripts/Story/StoryData.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Ichni.Story
|
||||
{
|
||||
[CreateAssetMenu(fileName = "StoryData", menuName = "Ichni/Story/StoryData")]
|
||||
public class StoryData : SerializedScriptableObject
|
||||
{
|
||||
public List<StoryBlockData> StoryBlockDatas; // 剧情单元格名称列表
|
||||
public Dictionary<string, int> storyVariables; // 剧情变量字典,键为变量名,值为默认值,如果Save中没有该变量,则生成,并使用默认值
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class StoryBlockData
|
||||
{
|
||||
public string blockName;
|
||||
public string blockID;
|
||||
|
||||
public StoryBlockData(string blockName, string blockID)
|
||||
{
|
||||
this.blockName = blockName;
|
||||
this.blockID = blockID;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Story/StoryData.cs.meta
Normal file
11
Assets/Scripts/Story/StoryData.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7ab917c50249812429ebd44d6574497c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
61
Assets/Scripts/Story/StoryData_Chapter1.asset
Normal file
61
Assets/Scripts/Story/StoryData_Chapter1.asset
Normal file
@@ -0,0 +1,61 @@
|
||||
%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: 11500000, guid: 7ab917c50249812429ebd44d6574497c, type: 3}
|
||||
m_Name: StoryData_Chapter1
|
||||
m_EditorClassIdentifier:
|
||||
serializationData:
|
||||
SerializedFormat: 2
|
||||
SerializedBytes:
|
||||
ReferencedUnityObjects: []
|
||||
SerializedBytesString:
|
||||
Prefab: {fileID: 0}
|
||||
PrefabModificationsReferencedUnityObjects: []
|
||||
PrefabModifications: []
|
||||
SerializationNodes:
|
||||
- Name: storyVariables
|
||||
Entry: 7
|
||||
Data: 0|System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.Int32,
|
||||
mscorlib]], mscorlib
|
||||
- Name: comparer
|
||||
Entry: 7
|
||||
Data: 1|System.Collections.Generic.GenericEqualityComparer`1[[System.String,
|
||||
mscorlib]], mscorlib
|
||||
- Name:
|
||||
Entry: 8
|
||||
Data:
|
||||
- Name:
|
||||
Entry: 12
|
||||
Data: 1
|
||||
- Name:
|
||||
Entry: 7
|
||||
Data:
|
||||
- Name: $k
|
||||
Entry: 1
|
||||
Data: TestVariable
|
||||
- Name: $v
|
||||
Entry: 3
|
||||
Data: 0
|
||||
- Name:
|
||||
Entry: 8
|
||||
Data:
|
||||
- Name:
|
||||
Entry: 13
|
||||
Data:
|
||||
- Name:
|
||||
Entry: 8
|
||||
Data:
|
||||
StoryBlockDatas:
|
||||
- blockName:
|
||||
blockID:
|
||||
blockPosition: {x: 0, y: 0}
|
||||
previousBlockIDs: []
|
||||
nextBlockIDs: []
|
||||
8
Assets/Scripts/Story/StoryData_Chapter1.asset.meta
Normal file
8
Assets/Scripts/Story/StoryData_Chapter1.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6e95609f9d235c042a48dbabae121053
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -6,6 +6,24 @@ namespace Ichni.Story
|
||||
{
|
||||
public class StorySave : MonoBehaviour
|
||||
{
|
||||
public StoryBlockSave blockSave;
|
||||
public StoryVariableSave variableSave;
|
||||
}
|
||||
|
||||
public class StoryBlockSave
|
||||
{
|
||||
public enum StoryBlockState
|
||||
{
|
||||
Locked,
|
||||
Current,
|
||||
Completed
|
||||
}
|
||||
|
||||
public Dictionary<string, StoryBlockState> storyBlockStates = new Dictionary<string, StoryBlockState>();
|
||||
}
|
||||
|
||||
public class StoryVariableSave
|
||||
{
|
||||
public Dictionary<string, int> variables = new Dictionary<string, int>();
|
||||
}
|
||||
}
|
||||
8
Assets/Scripts/UI/Base.meta
Normal file
8
Assets/Scripts/UI/Base.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a28c0aedac2c2664c876534db3e815f5
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
44
Assets/Scripts/UI/Base/UIPageBase.cs
Normal file
44
Assets/Scripts/UI/Base/UIPageBase.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using DG.Tweening;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace Ichni.UI
|
||||
{
|
||||
public class UIPageBase : SerializedMonoBehaviour
|
||||
{
|
||||
public CanvasGroup mainCanvasGroup;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
CanvasGroup group = GetComponent<CanvasGroup>();
|
||||
if (group != null)
|
||||
{
|
||||
mainCanvasGroup ??= group;
|
||||
}
|
||||
}
|
||||
|
||||
public void FadeIn(float duration = 0.5f)
|
||||
{
|
||||
mainCanvasGroup.gameObject.SetActive(true);
|
||||
mainCanvasGroup.DOFade(1f, duration).OnComplete(() =>
|
||||
{
|
||||
mainCanvasGroup.interactable = true;
|
||||
mainCanvasGroup.blocksRaycasts = true;
|
||||
}).Play();
|
||||
}
|
||||
|
||||
public void FadeOut(float duration = 0.5f)
|
||||
{
|
||||
mainCanvasGroup.interactable = false;
|
||||
mainCanvasGroup.blocksRaycasts = false;
|
||||
mainCanvasGroup.DOFade(0f, duration).OnComplete(() =>
|
||||
{
|
||||
mainCanvasGroup.gameObject.SetActive(false);
|
||||
}).Play();
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/UI/Base/UIPageBase.cs.meta
Normal file
11
Assets/Scripts/UI/Base/UIPageBase.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b5131dcefd0f7134b8d7c8956e949e52
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Scripts/UI/ChapterSelection.meta
Normal file
8
Assets/Scripts/UI/ChapterSelection.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 215cc7c4af3bc0545b877d49d482468f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
37
Assets/Scripts/UI/ChapterSelection/ChapterSelectionUI.cs
Normal file
37
Assets/Scripts/UI/ChapterSelection/ChapterSelectionUI.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Ichni.Menu;
|
||||
using Ichni.Story;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Ichni.UI
|
||||
{
|
||||
public class ChapterSelectionUI : MonoBehaviour
|
||||
{
|
||||
public RectTransform content;
|
||||
public TMP_Text title;
|
||||
public TMP_Text subtitle;
|
||||
public Image avatar;
|
||||
public Image border;
|
||||
public Button area;
|
||||
|
||||
public void Initialize(ChapterSelectionUnit chapter)
|
||||
{
|
||||
title.text = chapter.chapterName;
|
||||
title.color = chapter.themeColor;
|
||||
subtitle.text = chapter.chapterSubtitle;
|
||||
subtitle.color = chapter.themeColor;
|
||||
|
||||
border.color = chapter.themeColor;
|
||||
avatar.sprite = chapter.avatar;
|
||||
|
||||
area.onClick.AddListener(() =>
|
||||
{
|
||||
ChapterSelectionManager.instance.chapterSelectionUIPage.FadeOut();
|
||||
StoryManager.instance.storyUIPage.FadeIn();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a483faf242f8c4f449b809db70741cf2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
22
Assets/Scripts/UI/ChapterSelection/ChapterSelectionUIPage.cs
Normal file
22
Assets/Scripts/UI/ChapterSelection/ChapterSelectionUIPage.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Ichni.UI
|
||||
{
|
||||
public class ChapterSelectionUIPage : UIPageBase
|
||||
{
|
||||
public RectTransform chapterContainer;
|
||||
|
||||
public GameObject chapterSelectionUIPrefab;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
foreach (var chapter in ChapterSelectionManager.instance.chapters)
|
||||
{
|
||||
ChapterSelectionUI item = Instantiate(chapterSelectionUIPrefab, chapterContainer).GetComponent<ChapterSelectionUI>();
|
||||
item.Initialize(chapter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3adff9bbefe437245af8b1d1c9bf7e56
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Scripts/UI/DialogUI.meta
Normal file
8
Assets/Scripts/UI/DialogUI.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8c234abdb2207c8459280e59152ba1c9
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
48
Assets/Scripts/UI/DialogUI/ChoiceFrame.cs
Normal file
48
Assets/Scripts/UI/DialogUI/ChoiceFrame.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Michsky.MUIP;
|
||||
using Sirenix.Utilities;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Ichni.Story
|
||||
{
|
||||
public class ChoiceFrame : MonoBehaviour
|
||||
{
|
||||
public GameObject choiceButtonPrefab;
|
||||
public RectTransform choiceButtonContainer;
|
||||
public List<Button> choiceButtonList;
|
||||
public TooltipContent hint;
|
||||
|
||||
public void PlayChoice(List<Choice> choices)
|
||||
{
|
||||
gameObject.SetActive(true);
|
||||
choiceButtonContainer.GetAllChildren().ForEach(x => Destroy(x.gameObject));
|
||||
choiceButtonList.Clear();
|
||||
|
||||
/*if (choiceModule.hint != "")
|
||||
{
|
||||
hint.gameObject.SetActive(true);
|
||||
hint.description = choiceModule.hint;
|
||||
}
|
||||
else
|
||||
{
|
||||
hint.gameObject.SetActive(false);
|
||||
}*/
|
||||
|
||||
foreach (var choice in choices)
|
||||
{
|
||||
GameObject choiceButton = Instantiate(choiceButtonPrefab, choiceButtonContainer);
|
||||
choiceButton.GetComponentInChildren<TMP_Text>().text = choice.choiceText;
|
||||
choiceButton.GetComponent<Button>().onClick.AddListener(() =>
|
||||
{
|
||||
DialogManager.instance.PlayNextDialogParagraph(choice.nextDialogName);
|
||||
DialogManager.instance.isPlayingChoice = false;
|
||||
gameObject.SetActive(false);
|
||||
});
|
||||
choiceButtonList.Add(choiceButton.GetComponent<Button>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/UI/DialogUI/ChoiceFrame.cs.meta
Normal file
11
Assets/Scripts/UI/DialogUI/ChoiceFrame.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 62ce59e989ba99d4ca0407641e0ae2f2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
16
Assets/Scripts/UI/DialogUI/DialogUIPage.cs
Normal file
16
Assets/Scripts/UI/DialogUI/DialogUIPage.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Ichni.UI;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace Ichni.Story.UI
|
||||
{
|
||||
public class DialogUIPage : UIPageBase
|
||||
{
|
||||
public TextFrame textFrame;
|
||||
public ChoiceFrame choiceFrame;
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/UI/DialogUI/DialogUIPage.cs.meta
Normal file
11
Assets/Scripts/UI/DialogUI/DialogUIPage.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 17254192719abee4f9222246fd403de5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
38
Assets/Scripts/UI/DialogUI/TextFrame.cs
Normal file
38
Assets/Scripts/UI/DialogUI/TextFrame.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using DG.Tweening;
|
||||
using DG.Tweening.Core;
|
||||
using DG.Tweening.Plugins.Options;
|
||||
using I2.Loc;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Ichni.Story
|
||||
{
|
||||
public class TextFrame : MonoBehaviour
|
||||
{
|
||||
public GameObject textPrefab;
|
||||
public RectTransform textContainer;
|
||||
|
||||
private TweenerCore<string, string, StringOptions> contentTween;
|
||||
public bool isPlayingSentence;
|
||||
|
||||
public void PlaySentence(string speakerName, string content)
|
||||
{
|
||||
GameObject contentText = Instantiate(textPrefab, textContainer);
|
||||
contentText.transform.GetChild(1).GetComponent<TMP_Text>().text = speakerName;
|
||||
contentText.transform.GetChild(2).GetComponent<TMP_Text>().text = content;
|
||||
//contentText.GetComponent<Localize>().OnLocalize();
|
||||
}
|
||||
|
||||
public void FinishSentence()
|
||||
{
|
||||
if (isPlayingSentence)
|
||||
{
|
||||
contentTween.Complete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/UI/DialogUI/TextFrame.cs.meta
Normal file
11
Assets/Scripts/UI/DialogUI/TextFrame.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 14874d8a3e4a31941879415479892501
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Scripts/UI/Prepare.meta
Normal file
8
Assets/Scripts/UI/Prepare.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0b91bdc95925cee4eaec293d270c570c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
12
Assets/Scripts/UI/Prepare/PrepareUIPage.cs
Normal file
12
Assets/Scripts/UI/Prepare/PrepareUIPage.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Ichni.UI
|
||||
{
|
||||
public class PrepareUIPage : UIPageBase
|
||||
{
|
||||
public Button enterGameButton;
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/UI/Prepare/PrepareUIPage.cs.meta
Normal file
11
Assets/Scripts/UI/Prepare/PrepareUIPage.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f3060506b42e3df44962829ee40e95d9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Scripts/UI/Settings.meta
Normal file
8
Assets/Scripts/UI/Settings.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bfe7bb3757d56ed4ba5f39d9432eeb88
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -2,9 +2,9 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Ichni.Story
|
||||
namespace Ichni.UI
|
||||
{
|
||||
public class StoryManager : MonoBehaviour
|
||||
public class SettingsUIPage : UIPageBase
|
||||
{
|
||||
|
||||
}
|
||||
11
Assets/Scripts/UI/Settings/SettingsUIPage.cs.meta
Normal file
11
Assets/Scripts/UI/Settings/SettingsUIPage.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1d9b1595bce378541852c9c6f86f58ee
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Scripts/UI/SongSelection.meta
Normal file
8
Assets/Scripts/UI/SongSelection.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: be2c6dbc0cd6b3e419fc467ea5f07091
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
11
Assets/Scripts/UI/SongSelection/SongSelectionUIPage.cs
Normal file
11
Assets/Scripts/UI/SongSelection/SongSelectionUIPage.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Ichni.UI
|
||||
{
|
||||
public class SongSelectionUIPage : UIPageBase
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/UI/SongSelection/SongSelectionUIPage.cs.meta
Normal file
11
Assets/Scripts/UI/SongSelection/SongSelectionUIPage.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2bc849ebcc9595745a8a2584eb86eeee
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -3,9 +3,9 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Ichni.Menu
|
||||
namespace Ichni.UI
|
||||
{
|
||||
public class StartPage : MonoBehaviour
|
||||
public class StartUIPage : UIPageBase
|
||||
{
|
||||
public AudioContainer audioContainer;
|
||||
|
||||
@@ -17,6 +17,8 @@ namespace Ichni.Menu
|
||||
public void TouchToStart()
|
||||
{
|
||||
audioContainer.PlaySoundFX("TouchToStart");
|
||||
FadeOut();
|
||||
ChapterSelectionManager.instance.chapterSelectionUIPage.FadeIn();
|
||||
}
|
||||
}
|
||||
}
|
||||
8
Assets/Scripts/UI/StoryUI.meta
Normal file
8
Assets/Scripts/UI/StoryUI.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cf843fc3f4fd10e499a6cc1b60bc1861
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
29
Assets/Scripts/UI/StoryUI/BlockConnectorUI.cs
Normal file
29
Assets/Scripts/UI/StoryUI/BlockConnectorUI.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI.Extensions;
|
||||
|
||||
namespace Ichni.Story
|
||||
{
|
||||
public class BlockConnectorUI : MonoBehaviour
|
||||
{
|
||||
public UILineRenderer curve;
|
||||
|
||||
public void SetCurve(Vector2 startPosition, Vector2 endPosition)
|
||||
{
|
||||
Vector2 mid1 = (startPosition + endPosition) / 2;
|
||||
Vector2 mid2 = (startPosition + endPosition) / 2;
|
||||
|
||||
mid1.y = startPosition.y;
|
||||
mid2.y = endPosition.y;
|
||||
|
||||
curve.Points = new Vector2[]
|
||||
{
|
||||
startPosition,
|
||||
mid1,
|
||||
mid2,
|
||||
endPosition
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/UI/StoryUI/BlockConnectorUI.cs.meta
Normal file
11
Assets/Scripts/UI/StoryUI/BlockConnectorUI.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 98bbf463dca50cc43961c0bb2b8d4f22
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
29
Assets/Scripts/UI/StoryUI/SongBlockUI.cs
Normal file
29
Assets/Scripts/UI/StoryUI/SongBlockUI.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Ichni.Story.UI
|
||||
{
|
||||
public class SongBlockUI : StoryBlockUIBase
|
||||
{
|
||||
public string blockName;
|
||||
public string songName;
|
||||
public Button button;
|
||||
public TMP_Text musicText;
|
||||
|
||||
public void Initialize(string blockName, string songName)
|
||||
{
|
||||
this.blockName = blockName;
|
||||
this.songName = songName;
|
||||
|
||||
musicText.text = songName;
|
||||
button.onClick.AddListener(() =>
|
||||
{
|
||||
MenuManager.instance.prepareUIPage.FadeIn();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/UI/StoryUI/SongBlockUI.cs.meta
Normal file
11
Assets/Scripts/UI/StoryUI/SongBlockUI.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5efe0a39fe908354e9ab6d1edfdb8843
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
13
Assets/Scripts/UI/StoryUI/StoryBlockUIBase.cs
Normal file
13
Assets/Scripts/UI/StoryUI/StoryBlockUIBase.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Ichni.Story.UI
|
||||
{
|
||||
public class StoryBlockUIBase : MonoBehaviour
|
||||
{
|
||||
public RectTransform blockRect;
|
||||
public RectTransform inPort;
|
||||
public RectTransform outPort;
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/UI/StoryUI/StoryBlockUIBase.cs.meta
Normal file
11
Assets/Scripts/UI/StoryUI/StoryBlockUIBase.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d489f986a1eea1e4c938658f0fd468ca
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
11
Assets/Scripts/UI/StoryUI/StoryUIPage.cs
Normal file
11
Assets/Scripts/UI/StoryUI/StoryUIPage.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Ichni.UI
|
||||
{
|
||||
public class StoryUIPage : UIPageBase
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/UI/StoryUI/StoryUIPage.cs.meta
Normal file
11
Assets/Scripts/UI/StoryUI/StoryUIPage.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3ec0b23c77de2764d9425add8f28ea00
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
192
Assets/Scripts/UI/StoryUI/StorylineDisplay.cs
Normal file
192
Assets/Scripts/UI/StoryUI/StorylineDisplay.cs
Normal file
@@ -0,0 +1,192 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace Ichni.Story.UI
|
||||
{
|
||||
public class StorylineDisplay : MonoBehaviour
|
||||
{
|
||||
[Header("UI References")]
|
||||
public RectTransform content; // Content of ScrollRect
|
||||
public GameObject textBlockPrefab; // Prefab of UI Node
|
||||
public GameObject musicBlockPrefab;
|
||||
public GameObject tutorialBlockPrefab;
|
||||
public GameObject connectionCurvePrefab; // Prefab of connection curve
|
||||
|
||||
[Header("Layout Settings")]
|
||||
public float marginLeft = 50f; // additive space on the left
|
||||
public float marginRight = 50f; // Extra space on the right
|
||||
public RectTransform connectionContainer;
|
||||
|
||||
public List<StoryBlockUIBase> generatedBlocks;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
generatedBlocks = new List<StoryBlockUIBase>();
|
||||
TutorialBlockUI t0 = GenerateTutorialBlock(new Vector2(100, 0), "ZakoCurse 0");
|
||||
TextBlockUI b1 = GenerateTextBlock(new Vector2(600, 200), "ZakoCurse 0");
|
||||
TextBlockUI b2 = GenerateTextBlock(new Vector2(1100, -200), "ZakoCurse 0");
|
||||
SongBlockUI m0 = GenerateMusicBlock(new Vector2(1600, 0), "Chaos Zone", "Chaos Zone");
|
||||
TextBlockUI b3_1 = GenerateTextBlock(new Vector2(2100, 300), "ZakoCurse 0");
|
||||
TextBlockUI b3_2 = GenerateTextBlock(new Vector2(2600, -300), "ZakoCurse 0");
|
||||
|
||||
// Generate connections
|
||||
GenerateConnector(t0, b1);
|
||||
GenerateConnector(b1, b2);
|
||||
GenerateConnector(b2, m0);
|
||||
GenerateConnector(m0, b3_1);
|
||||
GenerateConnector(m0, b3_2);
|
||||
|
||||
SetUpBackground();
|
||||
connectionContainer.SetParent(content);
|
||||
}
|
||||
|
||||
public TextBlockUI GenerateTextBlock(Vector2 position, string blockName)
|
||||
{
|
||||
return GenerateTextBlock(position, new Vector2(300, 200), blockName);
|
||||
}
|
||||
|
||||
public TextBlockUI GenerateTextBlock(Vector2 position, Vector2 size, string blockName)
|
||||
{
|
||||
TextBlockUI block = Instantiate(textBlockPrefab, content).GetComponent<TextBlockUI>();
|
||||
|
||||
// Set position and size
|
||||
block.blockRect.anchoredPosition = position + new Vector2(marginLeft, 0);
|
||||
block.blockRect.sizeDelta = size;
|
||||
|
||||
block.Initialize(blockName, "Zako Curse 0");
|
||||
generatedBlocks.Add(block);
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
public TutorialBlockUI GenerateTutorialBlock(Vector2 position, string blockName)
|
||||
{
|
||||
return GenerateTutorialBlock(position, new Vector2(300, 200), blockName);
|
||||
}
|
||||
|
||||
public TutorialBlockUI GenerateTutorialBlock(Vector2 position, Vector2 size, string blockName)
|
||||
{
|
||||
TutorialBlockUI block = Instantiate(tutorialBlockPrefab, content).GetComponent<TutorialBlockUI>();
|
||||
|
||||
// Set position and size
|
||||
block.blockRect.anchoredPosition = position + new Vector2(marginLeft, 0);
|
||||
block.blockRect.sizeDelta = size;
|
||||
|
||||
block.Initialize(blockName);
|
||||
generatedBlocks.Add(block);
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
public SongBlockUI GenerateMusicBlock(Vector2 position, string blockName, string musicName)
|
||||
{
|
||||
return GenerateMusicBlock(position, new Vector2(200, 100), blockName, musicName);
|
||||
}
|
||||
|
||||
public SongBlockUI GenerateMusicBlock(Vector2 position, Vector2 size, string blockName, string musicName)
|
||||
{
|
||||
SongBlockUI block = Instantiate(musicBlockPrefab, content).GetComponent<SongBlockUI>();
|
||||
|
||||
// Set position and size
|
||||
block.blockRect.anchoredPosition = position + new Vector2(marginLeft, 0);
|
||||
block.blockRect.sizeDelta = size;
|
||||
|
||||
block.Initialize(blockName, musicName);
|
||||
generatedBlocks.Add(block);
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
public void GenerateConnector(StoryBlockUIBase outBlock, StoryBlockUIBase inBlock)
|
||||
{
|
||||
BlockConnectorUI connector = Instantiate(connectionCurvePrefab, connectionContainer).GetComponent<BlockConnectorUI>();
|
||||
Vector2 startPosition = GetLocalUIPosition(outBlock.outPort, GetComponent<RectTransform>());
|
||||
Vector2 endPosition = GetLocalUIPosition(inBlock.inPort, GetComponent<RectTransform>());
|
||||
connector.SetCurve(startPosition, endPosition);
|
||||
}
|
||||
|
||||
Vector2 GetLocalUIPosition(RectTransform fromPort, RectTransform targetSpace)
|
||||
{
|
||||
Vector2 screenPos = RectTransformUtility.WorldToScreenPoint(null, fromPort.position);
|
||||
RectTransformUtility.ScreenPointToLocalPointInRectangle(targetSpace, screenPos, null, out Vector2 localPos);
|
||||
return localPos;
|
||||
}
|
||||
|
||||
public void SetUpBackground()
|
||||
{
|
||||
float maxRight = float.MinValue;
|
||||
|
||||
foreach (var block in generatedBlocks)
|
||||
{
|
||||
float rightEdge = block.blockRect.anchoredPosition.x + block.blockRect.sizeDelta.x * 0.5f;
|
||||
if (rightEdge > maxRight)
|
||||
{
|
||||
maxRight = rightEdge;
|
||||
}
|
||||
}
|
||||
|
||||
maxRight += marginRight;
|
||||
|
||||
Debug.Log(maxRight);
|
||||
|
||||
if (maxRight < 1920f)
|
||||
{
|
||||
maxRight = 1920f;
|
||||
}
|
||||
|
||||
content.sizeDelta = new Vector2(maxRight, content.sizeDelta.y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Public method to generate nodes
|
||||
/// </summary>
|
||||
public void Generate(List<Vector2> positions, List<Vector2> sizes)
|
||||
{
|
||||
if (content == null || textBlockPrefab == null || positions.Count != sizes.Count)
|
||||
{
|
||||
Debug.LogError("NodeScrollGenerator: Invalid input data.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear old nodes
|
||||
foreach (Transform child in content)
|
||||
{
|
||||
Destroy(child.gameObject);
|
||||
}
|
||||
|
||||
float maxRight = float.MinValue;
|
||||
|
||||
for (int i = 0; i < positions.Count; i++)
|
||||
{
|
||||
// Calculate right edge
|
||||
float rightEdge = positions[i].x + marginLeft + sizes[i].x * 0.5f;
|
||||
if (rightEdge > maxRight)
|
||||
maxRight = rightEdge;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Create nodes
|
||||
for (int i = 0; i < positions.Count; i++)
|
||||
{
|
||||
GameObject node = Instantiate(textBlockPrefab, content);
|
||||
RectTransform rectTransform = node.GetComponent<RectTransform>();
|
||||
|
||||
// Set position and size
|
||||
rectTransform.anchoredPosition = positions[i] + new Vector2(marginLeft, 0);
|
||||
rectTransform.sizeDelta = sizes[i];
|
||||
|
||||
// Optionally, you can set the name or other properties of the node here
|
||||
node.name = $"Node_{i}";
|
||||
node.GetComponent<TextBlockUI>().Initialize("ZakoCurse 0", "Zako Curse 0");
|
||||
}
|
||||
|
||||
// Set content width
|
||||
content.sizeDelta = new Vector2(maxRight + marginRight, content.sizeDelta.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/UI/StoryUI/StorylineDisplay.cs.meta
Normal file
11
Assets/Scripts/UI/StoryUI/StorylineDisplay.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 42d37c15aeeaf1d4abbdc13962bb8b70
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
29
Assets/Scripts/UI/StoryUI/TextBlockUI.cs
Normal file
29
Assets/Scripts/UI/StoryUI/TextBlockUI.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Ichni.Story.UI
|
||||
{
|
||||
public class TextBlockUI : StoryBlockUIBase
|
||||
{
|
||||
public string blockName;
|
||||
public string blockTitle;
|
||||
public Button button;
|
||||
public TMP_Text titleText;
|
||||
|
||||
public void Initialize(string blockName, string blockTitle)
|
||||
{
|
||||
this.blockName = blockName;
|
||||
this.blockTitle = blockTitle;
|
||||
|
||||
titleText.text = blockTitle;
|
||||
button.onClick.AddListener(() =>
|
||||
{
|
||||
DialogManager.instance.SetDialog(blockName);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/UI/StoryUI/TextBlockUI.cs.meta
Normal file
11
Assets/Scripts/UI/StoryUI/TextBlockUI.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b976128eb3ec59e4e866dbb610441706
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
27
Assets/Scripts/UI/StoryUI/TutorialBlockUI.cs
Normal file
27
Assets/Scripts/UI/StoryUI/TutorialBlockUI.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Ichni.Story.UI
|
||||
{
|
||||
public class TutorialBlockUI : StoryBlockUIBase
|
||||
{
|
||||
public string blockName;
|
||||
public Button button;
|
||||
public TMP_Text tutorialText;
|
||||
|
||||
public void Initialize(string blockName)
|
||||
{
|
||||
this.blockName = blockName;
|
||||
|
||||
tutorialText.text = "Tutorial";
|
||||
button.onClick.AddListener(() =>
|
||||
{
|
||||
// DialogManager.instance.SetDialog(blockName);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/UI/StoryUI/TutorialBlockUI.cs.meta
Normal file
11
Assets/Scripts/UI/StoryUI/TutorialBlockUI.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 821e50e1519236a46b03eb1f005acebe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -3,12 +3,13 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using AK.Wwise;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace Ichni
|
||||
{
|
||||
public class AudioManager : MonoBehaviour
|
||||
{
|
||||
public MusicPlayer musicPlayer;
|
||||
[FormerlySerializedAs("musicPlayer")] public SongPlayer songPlayer;
|
||||
public AudioContainer generalSoundEffects;
|
||||
|
||||
private void Start()
|
||||
|
||||
@@ -6,7 +6,7 @@ using Event = AK.Wwise.Event;
|
||||
|
||||
namespace Ichni
|
||||
{
|
||||
public class MusicPlayer : SerializedMonoBehaviour
|
||||
public class SongPlayer : SerializedMonoBehaviour
|
||||
{
|
||||
private uint _playingId;
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace Ichni
|
||||
|
||||
|
||||
[Button]
|
||||
public void PlayMusic()
|
||||
public void PlaySong()
|
||||
{
|
||||
_playingId = PlayMusicEvent.Post(gameObject,
|
||||
(uint)AkCallbackType.AK_EnableGetMusicPlayPosition | (uint)AkCallbackType.AK_MusicSyncAll,
|
||||
@@ -82,7 +82,7 @@ namespace Ichni
|
||||
}
|
||||
|
||||
[Button]
|
||||
public void PauseMusic()
|
||||
public void PauseSong()
|
||||
{
|
||||
pauseTimeSegment = PlaySegment();
|
||||
isPlaying = false;
|
||||
@@ -92,7 +92,7 @@ namespace Ichni
|
||||
}
|
||||
|
||||
[Button]
|
||||
public void ResumeMusic()
|
||||
public void ResumeSong()
|
||||
{
|
||||
Time.timeScale = 1;
|
||||
isPlaying = true;
|
||||
@@ -101,7 +101,7 @@ namespace Ichni
|
||||
}
|
||||
|
||||
[Button]
|
||||
public void StopMusic()
|
||||
public void StopSong()
|
||||
{
|
||||
isPlaying = false;
|
||||
isPausing = false;
|
||||
Reference in New Issue
Block a user