气泡组件和配置项
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aaccc268908358449a45c394f36fd2d8
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,45 @@
|
||||
namespace AibisDream.Config
|
||||
{
|
||||
public class Character
|
||||
{
|
||||
public string Key { get; set; }
|
||||
public string Cn { get; set; }
|
||||
public string NameColor { get; set; }
|
||||
public string HeadPicPath { get; set; }
|
||||
public string VoicePath { get; set; }
|
||||
public ActorRole Role { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 避免干扰,复制结构体出来使用
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public CharacterVo GetStructVo()
|
||||
{
|
||||
CharacterVo vo;
|
||||
vo.key = Key;
|
||||
vo.cn = Cn;
|
||||
vo.nameColor = NameColor;
|
||||
vo.headPicPath = HeadPicPath;
|
||||
vo.voicePath = VoicePath;
|
||||
vo.role = Role;
|
||||
return vo;
|
||||
}
|
||||
}
|
||||
|
||||
public struct CharacterVo
|
||||
{
|
||||
public string key;
|
||||
public string cn;
|
||||
public string nameColor;
|
||||
public string headPicPath;
|
||||
public string voicePath;
|
||||
public ActorRole role;
|
||||
}
|
||||
|
||||
public enum ActorRole
|
||||
{
|
||||
Aside,
|
||||
Player,
|
||||
MainActor
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ed401d6b60a1c51448b66d538c946090
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,71 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AibisDream.Utility;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AibisDream.Config
|
||||
{
|
||||
public class ConfigUtil : SingletonBase<ConfigUtil>
|
||||
{
|
||||
private const string CharacterConfigPath = "/Config/character.csv";
|
||||
private const string BaseConfigPath = "/Config/base_config.csv";
|
||||
|
||||
private Dictionary<string, string> _baseConfigDic;
|
||||
private Dictionary<string, Character> _characters;
|
||||
|
||||
/// <summary>
|
||||
/// 用于单例模式
|
||||
/// </summary>
|
||||
private ConfigUtil() {}
|
||||
|
||||
public void InitConfig()
|
||||
{
|
||||
InitBaseConfig();
|
||||
InitCharacterConfig();
|
||||
}
|
||||
|
||||
private void InitBaseConfig()
|
||||
{
|
||||
var baseList = CsvUtil.Read(Application.streamingAssetsPath + BaseConfigPath);
|
||||
if (baseList.Count <= 0)
|
||||
{
|
||||
Debug.Log("基础配置不存在");
|
||||
}
|
||||
|
||||
// 基础配置只有key和value
|
||||
_baseConfigDic = new Dictionary<string, string>();
|
||||
foreach (var arr in baseList)
|
||||
{
|
||||
_baseConfigDic.Add(arr[0], arr[1]);
|
||||
}
|
||||
}
|
||||
|
||||
private void InitCharacterConfig()
|
||||
{
|
||||
var characterList = CsvUtil.ReadAsBean<Character>(Application.streamingAssetsPath + CharacterConfigPath);
|
||||
_characters = characterList.ToDictionary(item => item.Key, item => item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 按key获取角色预设
|
||||
/// </summary>
|
||||
/// <param name="key">key</param>
|
||||
/// <param name="character">角色配置</param>
|
||||
/// <returns>是否能获取</returns>
|
||||
public bool TryGetCharacter(string key, out Character character)
|
||||
{
|
||||
return _characters.TryGetValue(key, out character);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 按key获取基础配置
|
||||
/// </summary>
|
||||
/// <param name="key">key</param>
|
||||
/// <param name="value">配置项</param>
|
||||
/// <returns>是否能获取</returns>
|
||||
public bool TryGetBaseConfig(string key, out string value)
|
||||
{
|
||||
return _baseConfigDic.TryGetValue(key, out value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fb555b831ee0c8f4aa4a5a2e1c8d4f39
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using AibisDream.Utility;
|
||||
using Yarn.Unity;
|
||||
|
||||
namespace AibisDream
|
||||
@@ -6,24 +7,24 @@ namespace AibisDream
|
||||
public class CommandOptionView : DialogueViewBase
|
||||
{
|
||||
// 进入指令选项模式
|
||||
private bool isInCommand;
|
||||
private string commandName;
|
||||
private bool _isInCommand;
|
||||
private string _commandName;
|
||||
|
||||
public override void RunLine(LocalizedLine dialogueLine, Action onDialogueLineFinished)
|
||||
{
|
||||
// 判断选项选项行
|
||||
if (dialogueLine.Text.IsOptionAttrLine())
|
||||
{
|
||||
isInCommand = true;
|
||||
_isInCommand = true;
|
||||
base.RunLine(dialogueLine, onDialogueLineFinished);
|
||||
return;
|
||||
}
|
||||
// 判断是否进入Command状态
|
||||
isInCommand = dialogueLine.Text.TryGetCommandAttr(out var curCommandName);
|
||||
if (isInCommand)
|
||||
_isInCommand = dialogueLine.Text.TryGetCommandAttr(out var curCommandName);
|
||||
if (_isInCommand)
|
||||
{
|
||||
// 保存指令
|
||||
commandName = curCommandName;
|
||||
_commandName = curCommandName;
|
||||
// 进入下一步
|
||||
onDialogueLineFinished.Invoke();
|
||||
}
|
||||
@@ -36,7 +37,7 @@ namespace AibisDream
|
||||
public override void RunOptions(DialogueOption[] dialogueOptions, Action<int> onOptionSelected)
|
||||
{
|
||||
// 非指令直接跳过
|
||||
if (!isInCommand)
|
||||
if (!_isInCommand)
|
||||
{
|
||||
base.RunOptions(dialogueOptions, onOptionSelected);
|
||||
return;
|
||||
@@ -45,7 +46,7 @@ namespace AibisDream
|
||||
DialogController.Instance.IsTalking = false;
|
||||
// 传输选项
|
||||
YarnOption[] options = YarnOption.GeneOptions(dialogueOptions, onOptionSelected);
|
||||
DialogController.Instance.InvokeOptionCommand(commandName, options);
|
||||
DialogController.Instance.InvokeOptionCommand(_commandName, options);
|
||||
}
|
||||
|
||||
public override void InterruptLine(LocalizedLine dialogueLine, Action onDialogueLineFinished)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using AibisDream.Utility;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using Yarn.Unity;
|
||||
@@ -11,12 +12,13 @@ namespace AibisDream
|
||||
public class DialogController : Singleton<DialogController>
|
||||
{
|
||||
private const string BookManager = "BookManager";
|
||||
|
||||
private DialogueRunner dialogueRunner;
|
||||
|
||||
private DialogueRunner dialogueRunner;
|
||||
private VariableStorageBehaviour _variableStorage;
|
||||
|
||||
private Dictionary<string, Action<YarnOption[]>> commandMap = new();
|
||||
private readonly Dictionary<string, Action<YarnOption[]>> commandMap = new();
|
||||
|
||||
# region controller状态标识
|
||||
|
||||
private bool _isTalking;
|
||||
|
||||
@@ -39,13 +41,15 @@ namespace AibisDream
|
||||
}
|
||||
|
||||
// 对话框被阻塞时不能继续对话框交互
|
||||
public bool IsBlock { get; set; }
|
||||
public bool IsBlock { get; private set; }
|
||||
|
||||
public bool quickRunMode;
|
||||
|
||||
# endregion
|
||||
|
||||
public static UnityAction OnDialogueStart;
|
||||
public static UnityAction OnDialogueComplete;
|
||||
|
||||
public bool quickRunMode;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
dialogueRunner = GetComponentInChildren<DialogueRunner>();
|
||||
@@ -63,6 +67,7 @@ namespace AibisDream
|
||||
// 对话结束时切下一个SO
|
||||
dialogueRunner.onDialogueComplete.AddListener(() =>
|
||||
{
|
||||
DialogCanvasManager.Instance.SwitchDialogView(DialogViewType.Bubble);
|
||||
StartCoroutine(GameLoopManager.Instance.NextSceneSo());
|
||||
});
|
||||
}
|
||||
@@ -97,6 +102,7 @@ namespace AibisDream
|
||||
/// <param name="yarnProject">Yarn组</param>
|
||||
public void StartDialog(YarnProject yarnProject)
|
||||
{
|
||||
dialogueRunner.VariableStorage.Clear();
|
||||
dialogueRunner.SetProject(yarnProject);
|
||||
dialogueRunner.StartDialogue("Start");
|
||||
}
|
||||
@@ -135,7 +141,7 @@ namespace AibisDream
|
||||
{
|
||||
Debug.Log($"命令 {commandName} 已经存在,将被替换");
|
||||
}
|
||||
|
||||
|
||||
commandMap[commandName] = commandAction;
|
||||
}
|
||||
|
||||
@@ -210,11 +216,12 @@ namespace AibisDream
|
||||
{
|
||||
HideDialog();
|
||||
//yield return MainUIController.Instance.FadeInSync(duration);
|
||||
Camera.main.orthographicSize=5;
|
||||
Camera.main.transform.position=new Vector3(0,0,-10);
|
||||
Camera.main.orthographicSize = 5;
|
||||
Camera.main.transform.position = new Vector3(0, 0, -10);
|
||||
yield return SceneLoader.Instance.LoadSceneAsync(sceneName);
|
||||
//yield return new WaitForSeconds(1f);
|
||||
//yield return MainUIController.Instance.FadeOutSync(duration);
|
||||
DialogCanvasManager.Instance.SwitchDialogView(sceneName == "ClinicScene"
|
||||
? DialogViewType.Bubble
|
||||
: DialogViewType.OldBox);
|
||||
}
|
||||
|
||||
[YarnCommand("unload_scene")]
|
||||
@@ -226,7 +233,7 @@ namespace AibisDream
|
||||
[YarnCommand("hide_dialog")]
|
||||
public static void HideDialog()
|
||||
{
|
||||
DialogUiViewer.Instance.HideDialog();
|
||||
DialogCanvasManager.GetCurView().HideDialog();
|
||||
}
|
||||
|
||||
[YarnCommand("fade_in")]
|
||||
@@ -246,24 +253,25 @@ namespace AibisDream
|
||||
{
|
||||
return MainUIController.Instance.ShowObj(picName);
|
||||
}
|
||||
|
||||
|
||||
[YarnCommand("hide_obj")]
|
||||
public static IEnumerator HideObj()
|
||||
{
|
||||
return MainUIController.Instance.HideObj();
|
||||
}
|
||||
|
||||
[YarnCommand("move_to_fix")]
|
||||
public static IEnumerator Move_to_fix()
|
||||
public static IEnumerator MoveToFix()
|
||||
{
|
||||
bool isCompleted = false;
|
||||
Sequence s = DOTween.Sequence();
|
||||
s.Append(Camera.main.transform.DOMove(new UnityEngine.Vector3(6.8f, 0, -10), 2f))
|
||||
.OnComplete(() =>
|
||||
{
|
||||
// 动画完成后的反馈操作
|
||||
Debug.Log("Move_to_fix sequence completed.");
|
||||
isCompleted = true;
|
||||
});
|
||||
.OnComplete(() =>
|
||||
{
|
||||
// 动画完成后的反馈操作
|
||||
Debug.Log("Move_to_fix sequence completed.");
|
||||
isCompleted = true;
|
||||
});
|
||||
|
||||
// 等待动画完成
|
||||
yield return new WaitUntil(() => isCompleted);
|
||||
@@ -271,15 +279,22 @@ namespace AibisDream
|
||||
// 动画完成后的其他操作
|
||||
Debug.Log("Continuing after Move_to_fix sequence.");
|
||||
}
|
||||
|
||||
[YarnCommand("move_out_fix")]
|
||||
public static void Move_out_fix()
|
||||
public static void MoveOutFix()
|
||||
{
|
||||
Camera.main.transform.position=new UnityEngine.Vector3(6.8f, 0, -10);
|
||||
Camera.main.transform.position = new UnityEngine.Vector3(6.8f, 0, -10);
|
||||
Sequence s = DOTween.Sequence();
|
||||
//s.AppendInterval(0.5f);
|
||||
s.Append(Camera.main.transform.DOMove(new UnityEngine.Vector3(0, 0, -10), 2f));
|
||||
}
|
||||
|
||||
|
||||
[YarnCommand("switch_dialog_view")]
|
||||
public static void SwitchDialogView(string viewName)
|
||||
{
|
||||
DialogCanvasManager.Instance.SwitchDialogView(Enum.Parse<DialogViewType>(viewName));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,11 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using AibisDream.Utility;
|
||||
using Yarn.Unity;
|
||||
|
||||
namespace AibisDream
|
||||
{
|
||||
public class LineView : DialogueViewBase
|
||||
{
|
||||
private const string AutoNext = "auto_next";
|
||||
|
||||
// 进入指令选项模式
|
||||
private bool _isInCommand;
|
||||
|
||||
@@ -17,11 +15,11 @@ namespace AibisDream
|
||||
|
||||
if (_isInCommand)
|
||||
{
|
||||
DialogUiViewer.Instance.HideDialog();
|
||||
DialogCanvasManager.GetCurView().HideDialog();
|
||||
base.RunLine(dialogueLine, onDialogueLineFinished);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
HandleLineOutput(dialogueLine, onDialogueLineFinished);
|
||||
|
||||
if (DialogController.Instance.quickRunMode)
|
||||
@@ -35,32 +33,21 @@ namespace AibisDream
|
||||
// 对话系统正在交谈状态
|
||||
DialogController.Instance.IsTalking = true;
|
||||
|
||||
// 解析LocalizedLine
|
||||
if (dialogueLine.Metadata != null && dialogueLine.Metadata.Contains(AutoNext))
|
||||
{
|
||||
// 自动跳过
|
||||
DialogUiViewer.Instance.ShowAutoLine(dialogueLine.TextWithoutCharacterName.Text,
|
||||
dialogueLine.CharacterName, onDialogueLineFinished);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 需要玩家点击
|
||||
DialogUiViewer.Instance.ShowLine(dialogueLine.TextWithoutCharacterName.Text,
|
||||
dialogueLine.CharacterName, onDialogueLineFinished);
|
||||
}
|
||||
DialogCanvasManager.GetCurView().ShowLine(dialogueLine.TextWithoutCharacterName.Text,
|
||||
dialogueLine.GetCharacterVo(), onDialogueLineFinished, dialogueLine.IsAutoSkipLine());
|
||||
}
|
||||
|
||||
public override void RunOptions(DialogueOption[] dialogueOptions, Action<int> onOptionSelected)
|
||||
{
|
||||
if (_isInCommand)
|
||||
{
|
||||
DialogUiViewer.Instance.HideDialog();
|
||||
DialogCanvasManager.GetCurView().HideDialog();
|
||||
base.RunOptions(dialogueOptions, onOptionSelected);
|
||||
return;
|
||||
}
|
||||
|
||||
DialogController.Instance.IsTalking = true;
|
||||
DialogUiViewer.Instance.ShowOptions(dialogueOptions, onOptionSelected);
|
||||
DialogCanvasManager.GetCurView().ShowOptions(dialogueOptions, onOptionSelected);
|
||||
}
|
||||
|
||||
public override void InterruptLine(LocalizedLine dialogueLine, Action onDialogueLineFinished)
|
||||
@@ -84,13 +71,11 @@ namespace AibisDream
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!DialogUiViewer.Instance.TrySkipLine())
|
||||
|
||||
if (!DialogCanvasManager.GetCurView().TrySkipLine())
|
||||
{
|
||||
DialogUiViewer.Instance.AfterEndLine();
|
||||
requestInterrupt.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,8 +10,8 @@ public class ScreenEffectManager : MonoBehaviour
|
||||
{
|
||||
public static ScreenEffectManager Instance { get; private set; }
|
||||
|
||||
public Image blackoutPanel; // 黑屏面板
|
||||
public Image redFlashPanel; // 红色闪烁面板
|
||||
public Image blackoutPanel; // 黑屏面板
|
||||
public Image redFlashPanel; // 红色闪烁面板
|
||||
private float shakeDuration = 0.5f; // 震屏持续时间
|
||||
private float shakeIntensity = 0.04f; // 震屏强度
|
||||
|
||||
@@ -54,7 +54,8 @@ public class ScreenEffectManager : MonoBehaviour
|
||||
[YarnCommand("shakeScreen")]
|
||||
public IEnumerator ShakeScreen()
|
||||
{
|
||||
yield return Camera.main.transform.DOPunchPosition(new Vector3(1, 0, 0) * shakeIntensity, shakeDuration, 8, 1f).WaitForCompletion();
|
||||
yield return Camera.main.transform.DOPunchPosition(new Vector3(1, 0, 0) * shakeIntensity, shakeDuration, 8, 1f)
|
||||
.WaitForCompletion();
|
||||
}
|
||||
|
||||
// 黑屏淡入淡出效果
|
||||
@@ -67,7 +68,8 @@ public class ScreenEffectManager : MonoBehaviour
|
||||
//fadeInSequence.AppendInterval(waitTime);
|
||||
yield return fadeInSequence.WaitForCompletion();
|
||||
}
|
||||
[YarnCommand("fadeOut")]
|
||||
|
||||
[YarnCommand("fadeOut")]
|
||||
public IEnumerator FadeOut(float duration)
|
||||
{
|
||||
Sequence fadeOutSequence = DOTween.Sequence();
|
||||
@@ -91,28 +93,24 @@ public class ScreenEffectManager : MonoBehaviour
|
||||
}
|
||||
|
||||
// 动态调整饱和度
|
||||
public Tween TweenSaturation(float targetSaturation, float duration)
|
||||
{
|
||||
if (colorAdjustments != null)
|
||||
public Tween TweenSaturation(float targetSaturation, float duration)
|
||||
{
|
||||
// 使用 DOTween 创建一个饱和度过渡动画
|
||||
Tween tween = DOTween.To(
|
||||
() => colorAdjustments.saturation.value, // 获取当前饱和度值
|
||||
x => colorAdjustments.saturation.value = x, // 设置新的饱和度值
|
||||
targetSaturation, // 目标饱和度值
|
||||
duration // 过渡持续时间
|
||||
);
|
||||
if (colorAdjustments != null)
|
||||
{
|
||||
// 使用 DOTween 创建一个饱和度过渡动画
|
||||
Tween tween = DOTween.To(
|
||||
() => colorAdjustments.saturation.value, // 获取当前饱和度值
|
||||
x => colorAdjustments.saturation.value = x, // 设置新的饱和度值
|
||||
targetSaturation, // 目标饱和度值
|
||||
duration // 过渡持续时间
|
||||
);
|
||||
|
||||
return tween; // 返回 Tween 对象
|
||||
return tween; // 返回 Tween 对象
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("ColorAdjustments settings not found.");
|
||||
return null; // 如果找不到设置,返回 null
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("ColorAdjustments settings not found.");
|
||||
return null; // 如果找不到设置,返回 null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections;
|
||||
using AibisDream.Config;
|
||||
using UnityEngine;
|
||||
using Yarn.Unity;
|
||||
|
||||
@@ -23,6 +24,8 @@ namespace AibisDream
|
||||
|
||||
public void Start()
|
||||
{
|
||||
ConfigUtil.Instance.InitConfig();
|
||||
|
||||
if (runMode == RunMode.Test)
|
||||
{
|
||||
MainUIController.Instance.TVFadeOut(1);
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using DG.Tweening;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using AibisDream.Utility;
|
||||
using DG.Tweening;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AibisDream
|
||||
@@ -46,7 +44,7 @@ namespace AibisDream
|
||||
{
|
||||
get
|
||||
{
|
||||
if (childGear == null)
|
||||
if (!childGear)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -159,11 +157,11 @@ namespace AibisDream
|
||||
public static bool IsShaftConnected(Shaft shaft1, Shaft shaft2)
|
||||
{
|
||||
// 如果轴上无齿轮就不计算
|
||||
if (shaft1 == null || shaft2 == null)
|
||||
if (!shaft1 || !shaft2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (shaft1.childGear == null || shaft2.childGear == null)
|
||||
if (!shaft1.childGear || !shaft2.childGear)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 626c63a530654b14f9c7a3be634dd0b5
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,17 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AibisDream
|
||||
{
|
||||
public class DialogBox : DialogBoxBase
|
||||
{
|
||||
[SerializeField] protected GameObject choiceButton;
|
||||
|
||||
protected override void LoadChoiceButton()
|
||||
{
|
||||
choiceButtonPrefab = choiceButton;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 51f2233614aba944f9b2dab3d5af433b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,204 @@
|
||||
using System;
|
||||
using AibisDream.Config;
|
||||
using AibisDream.Utility;
|
||||
using Febucci.UI;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using Yarn.Unity;
|
||||
|
||||
namespace AibisDream
|
||||
{
|
||||
public abstract class DialogBoxBase : MonoBehaviour, IDialogView
|
||||
{
|
||||
#region box组件名称
|
||||
|
||||
private const string ActorName = "Actor Name";
|
||||
private const string DialogText = "Dialog Text";
|
||||
private const string NextArray = "Next Array";
|
||||
private const string ChoiceBox = "Choice Box";
|
||||
|
||||
#endregion
|
||||
|
||||
#region box组件
|
||||
|
||||
protected TypewriterByCharacter dialogText;
|
||||
protected TextMeshProUGUI actorName;
|
||||
protected GameObject nextArray;
|
||||
protected GameObject choiceBox;
|
||||
|
||||
#endregion
|
||||
|
||||
private bool _skipLineDelay;
|
||||
[Header("跳过阻塞延迟时间/ms")]
|
||||
[SerializeField] private int delayTime = 300;
|
||||
|
||||
protected GameObject choiceButtonPrefab;
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
LoadChoiceButton();
|
||||
InitComponents();
|
||||
AddSomeListener();
|
||||
}
|
||||
|
||||
private void AddSomeListener()
|
||||
{
|
||||
// 对话显示后有一段延时不可跳过
|
||||
dialogText.onTypewriterStart.AddListener(() =>
|
||||
{
|
||||
_skipLineDelay = true;
|
||||
CommonUtil.Delay(delayTime, () => { _skipLineDelay = false; });
|
||||
});
|
||||
}
|
||||
|
||||
#region 初始化
|
||||
|
||||
protected virtual void LoadChoiceButton()
|
||||
{
|
||||
Debug.Log("无选项对话框");
|
||||
}
|
||||
|
||||
private void InitComponents()
|
||||
{
|
||||
actorName = transform.Find(ActorName)?.gameObject.GetComponent<TextMeshProUGUI>();
|
||||
dialogText = transform.Find(DialogText)?.gameObject.GetComponent<TypewriterByCharacter>();
|
||||
nextArray = transform.Find(NextArray)?.gameObject;
|
||||
choiceBox = transform.Find(ChoiceBox)?.gameObject;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 隐藏对话盒
|
||||
/// </summary>
|
||||
public void HideDialog()
|
||||
{
|
||||
gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 显示对话
|
||||
/// </summary>
|
||||
/// <param name="dialogLine">对话内容</param>
|
||||
/// <param name="character">角色内容</param>
|
||||
/// <param name="nextStep">下一步</param>
|
||||
/// <param name="isAutoSkip">是否自动跳过,默认为false</param>
|
||||
public virtual void ShowLine(string dialogLine, CharacterVo character, Action nextStep, bool isAutoSkip = false)
|
||||
{
|
||||
gameObject.SetActive(true);
|
||||
// 没有dialogText说明不能显示对话,直接跳过
|
||||
if (!dialogText)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 把原有对话清掉
|
||||
ClearBox();
|
||||
dialogText.gameObject.SetActive(true);
|
||||
choiceBox?.SetActive(false);
|
||||
|
||||
// 显示内容
|
||||
ShowActorName(character);
|
||||
dialogText.ShowText(dialogLine);
|
||||
|
||||
// TODO 如果有头像的对话框要加入头像处理
|
||||
|
||||
// 自动跳过的回调很难独立Remove,只能全部清空再注册,下策
|
||||
dialogText.onTextShowed.RemoveAllListeners();
|
||||
dialogText.onTextShowed.AddListener(() => nextArray.gameObject.SetActive(true));
|
||||
|
||||
// 执行自动跳过结局
|
||||
if (isAutoSkip)
|
||||
{
|
||||
dialogText.onTextShowed.AddListener(() => CommonUtil.Delay(200, nextStep));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 展示选项
|
||||
/// </summary>
|
||||
/// <param name="dialogueOptions">对话选项</param>
|
||||
/// <param name="onOptionSelected">选项选择</param>
|
||||
public virtual void ShowOptions(DialogueOption[] dialogueOptions, Action<int> onOptionSelected)
|
||||
{
|
||||
gameObject.SetActive(true);
|
||||
// 没有选择框直接不显示选项
|
||||
if (!choiceBox)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 把原有对话清掉
|
||||
ClearBox();
|
||||
choiceBox.SetActive(true);
|
||||
dialogText.gameObject.SetActive(false);
|
||||
|
||||
// 销毁原来的按钮
|
||||
foreach (Transform child in choiceBox.transform)
|
||||
{
|
||||
Destroy(child.gameObject);
|
||||
}
|
||||
|
||||
// 生成新按钮
|
||||
foreach (DialogueOption option in dialogueOptions)
|
||||
{
|
||||
if (!option.IsAvailable) continue;
|
||||
|
||||
var choiceInstance = Instantiate(choiceButtonPrefab, choiceBox.transform);
|
||||
choiceInstance.GetComponentInChildren<TextMeshProUGUI>().text =
|
||||
option.Line.TextWithoutCharacterName.Text;
|
||||
choiceInstance.GetComponent<Button>().onClick
|
||||
.AddListener(() => onOptionSelected.Invoke(option.DialogueOptionID));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试跳过对话
|
||||
/// true说明成功快进了对话显示
|
||||
/// false说明对话已经显示完成了,可以跳过
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual bool TrySkipLine()
|
||||
{
|
||||
if (_skipLineDelay)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (dialogText.isShowingText)
|
||||
{
|
||||
dialogText.SkipTypewriter();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
nextArray.gameObject.SetActive(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearBox()
|
||||
{
|
||||
gameObject.SetActive(true);
|
||||
dialogText?.ShowText("");
|
||||
nextArray?.SetActive(false);
|
||||
if (!actorName) actorName.text = "";
|
||||
}
|
||||
|
||||
private void ShowActorName(CharacterVo character)
|
||||
{
|
||||
if (!actorName) return;
|
||||
|
||||
if (string.IsNullOrEmpty(character.nameColor))
|
||||
{
|
||||
actorName.text = character.cn;
|
||||
}
|
||||
else
|
||||
{
|
||||
var showStr = $"<color={character.nameColor}>{character.cn}</color>";
|
||||
actorName.text = showStr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8a4b8d325dce1b64980f8fd2ff77c571
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,12 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AibisDream
|
||||
{
|
||||
public class DialogBubble : DialogBoxBase
|
||||
{
|
||||
// 气泡功能比较简单似乎不需要加别的
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d43fa24173f81ea4ea102be91896826e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using AibisDream.Config;
|
||||
using UnityEngine;
|
||||
using Yarn.Unity;
|
||||
|
||||
namespace AibisDream
|
||||
{
|
||||
public class DialogBubbleViewer : MonoBehaviour, IDialogView
|
||||
{
|
||||
private const string PlayerBubble = "Player Bubble";
|
||||
private const string ActorBubble = "Actor Bubble";
|
||||
private const string NormalBox = "Normal Box";
|
||||
|
||||
private IDialogView _normalBox;
|
||||
private IDialogView _playerBubble;
|
||||
private IDialogView _actorBubble;
|
||||
// 应该会有自定义位置的泡泡,但目前还没想好怎么做
|
||||
private Dictionary<string, IDialogView> _customBubblePool = new();
|
||||
|
||||
private List<IDialogView> _dialogViews;
|
||||
|
||||
private IDialogView _currentView;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
private void Init()
|
||||
{
|
||||
_normalBox = transform.Find(NormalBox).GetComponent<IDialogView>();
|
||||
_playerBubble = transform.Find(PlayerBubble).GetComponent<IDialogView>();
|
||||
_actorBubble = transform.Find(ActorBubble).GetComponent<IDialogView>();
|
||||
|
||||
_dialogViews = new List<IDialogView> { _normalBox, _playerBubble, _actorBubble };
|
||||
_dialogViews.ForEach(item => item.HideDialog());
|
||||
}
|
||||
|
||||
public void ShowLine(string dialogLine, CharacterVo character, Action nextStep, bool isAutoSkip = false)
|
||||
{
|
||||
gameObject.SetActive(true);
|
||||
// 根据角色信息气泡
|
||||
_currentView = SelectBubbleByCharacter(character);
|
||||
// 处理其他泡泡
|
||||
ProcessOtherBox();
|
||||
// 显示对话
|
||||
_currentView.ShowLine(dialogLine, character, nextStep, isAutoSkip);
|
||||
}
|
||||
|
||||
public void ShowOptions(DialogueOption[] dialogueOptions, Action<int> onOptionSelected)
|
||||
{
|
||||
gameObject.SetActive(true);
|
||||
_currentView = _normalBox;
|
||||
// 目前只有正常Box可以展示选项
|
||||
// 处理其他泡泡
|
||||
ProcessOtherBox();
|
||||
|
||||
// 展示选项
|
||||
_normalBox.ShowOptions(dialogueOptions, onOptionSelected);
|
||||
}
|
||||
|
||||
public void HideDialog()
|
||||
{
|
||||
gameObject.SetActive(false);
|
||||
// 隐藏整个对话组件
|
||||
_dialogViews?.ForEach(item => item.HideDialog());
|
||||
}
|
||||
|
||||
public bool TrySkipLine()
|
||||
{
|
||||
return _currentView == null || _currentView.TrySkipLine();
|
||||
}
|
||||
|
||||
private IDialogView SelectBubbleByCharacter(CharacterVo characterVo)
|
||||
{
|
||||
return characterVo.role switch
|
||||
{
|
||||
ActorRole.Player => _playerBubble,
|
||||
ActorRole.MainActor => _actorBubble,
|
||||
ActorRole.Aside => _normalBox,
|
||||
_ => _normalBox
|
||||
};
|
||||
}
|
||||
|
||||
private void ProcessOtherBox()
|
||||
{
|
||||
if (_currentView == _actorBubble || _currentView == _playerBubble)
|
||||
{
|
||||
_normalBox.HideDialog();
|
||||
}
|
||||
|
||||
if (_currentView == _normalBox)
|
||||
{
|
||||
_actorBubble.HideDialog();
|
||||
_playerBubble.HideDialog();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 98b5d94e03211a94bba6de692a1d405a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,118 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace AibisDream
|
||||
{
|
||||
public class DialogCanvasManager : Singleton<DialogCanvasManager>
|
||||
{
|
||||
private const string BubbleView = "Dialog Bubble View";
|
||||
private const string OldBoxView = "Dialog Box";
|
||||
|
||||
private const string QuickTalk = "Quick Talk";
|
||||
|
||||
private IDialogView _bubbleView;
|
||||
private IDialogView _oldBoxView;
|
||||
|
||||
private IDialogView _curView;
|
||||
|
||||
private GameObject _quickTalkButton;
|
||||
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
InitDialogViews();
|
||||
InitQuickTalkButton();
|
||||
// 默认为旧Box
|
||||
_curView = _bubbleView;
|
||||
}
|
||||
|
||||
#region 对话结束后隐藏
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
DialogController.OnDialogueComplete += HideDialog;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
DialogController.OnDialogueComplete -= HideDialog;
|
||||
}
|
||||
|
||||
private void HideDialog()
|
||||
{
|
||||
_curView.HideDialog();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (Input.GetKeyDown(KeyCode.Q) && Input.GetKey(KeyCode.P) )
|
||||
{
|
||||
_quickTalkButton.SetActive(!_quickTalkButton.activeSelf);
|
||||
}
|
||||
}
|
||||
|
||||
private void InitDialogViews()
|
||||
{
|
||||
_bubbleView = transform.Find(BubbleView).GetComponent<IDialogView>();
|
||||
_oldBoxView = transform.Find(OldBoxView).GetComponent<IDialogView>();
|
||||
|
||||
_bubbleView.HideDialog();
|
||||
_oldBoxView.HideDialog();
|
||||
}
|
||||
|
||||
private void InitQuickTalkButton()
|
||||
{
|
||||
_quickTalkButton = transform.Find(QuickTalk).gameObject;
|
||||
_quickTalkButton.GetComponent<Button>().onClick.AddListener(SwitchQuickTalkMode);
|
||||
}
|
||||
|
||||
private void SwitchQuickTalkMode()
|
||||
{
|
||||
DialogController.Instance.quickRunMode = !DialogController.Instance.quickRunMode;
|
||||
_quickTalkButton.GetComponent<Image>().color =
|
||||
DialogController.Instance.quickRunMode ? Color.gray : Color.white;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 切换对话UI
|
||||
/// </summary>
|
||||
/// <param name="viewType">UI类型</param>
|
||||
/// <returns></returns>
|
||||
public IDialogView SwitchDialogView(DialogViewType viewType)
|
||||
{
|
||||
var newDlg = viewType switch
|
||||
{
|
||||
DialogViewType.Bubble => _bubbleView,
|
||||
DialogViewType.OldBox => _oldBoxView,
|
||||
_ => _oldBoxView
|
||||
};
|
||||
|
||||
if (_curView != newDlg)
|
||||
{
|
||||
_curView.HideDialog();
|
||||
_curView = newDlg;
|
||||
}
|
||||
|
||||
return _curView;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前正在使用的Dialog View
|
||||
/// </summary>
|
||||
/// <returns>当前正在使用的Dialog View</returns>
|
||||
/// <exception cref="Exception">未找到Canvas</exception>
|
||||
public static IDialogView GetCurView()
|
||||
{
|
||||
return Instance._curView;
|
||||
}
|
||||
}
|
||||
|
||||
public enum DialogViewType
|
||||
{
|
||||
Bubble, OldBox
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c14c52217dac70244b02fe48d9462bae
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace AibisDream
|
||||
{
|
||||
public class DialogOldBox : DialogBoxBase
|
||||
{
|
||||
private void OnEnable()
|
||||
{
|
||||
DialogController.OnDialogueComplete += HideDialog;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
DialogController.OnDialogueComplete -= HideDialog;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2f063fd2d0404e04a809d7614c5b7532
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+13
-20
@@ -1,5 +1,6 @@
|
||||
using Febucci.UI;
|
||||
using System;
|
||||
using AibisDream.Utility;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
@@ -11,12 +12,12 @@ namespace AibisDream
|
||||
{
|
||||
#region 组件名称
|
||||
|
||||
private const string DIALOG_BOX = "Dialog Box";
|
||||
private const string ACTOR_NAME = "Actor Name";
|
||||
private const string DIALOG_TEXT = "Dialog Text";
|
||||
private const string NEXT_BUTTON = "Next Button";
|
||||
private const string CHOICE_BUTTON = "Choice Button";
|
||||
private const string QUICK_TALK = "Quick Talk";
|
||||
private const string DialogBox = "Dialog Box";
|
||||
private const string ActorName = "Actor Name";
|
||||
private const string DialogText = "Dialog Text";
|
||||
private const string NextButton = "Next Button";
|
||||
private const string ChoiceBox = "Choice Box";
|
||||
private const string QuickTalk = "Quick Talk";
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -40,7 +41,7 @@ namespace AibisDream
|
||||
// 获取预制体
|
||||
choiceButtonPrefab = Resources.Load<GameObject>("Prefabs/UI/Choice Button");
|
||||
// 获取子物体
|
||||
dialogBox = transform.Find(DIALOG_BOX).gameObject;
|
||||
dialogBox = transform.Find(DialogBox).gameObject;
|
||||
GetDialogComponentInBox(dialogBox);
|
||||
}
|
||||
|
||||
@@ -54,14 +55,6 @@ namespace AibisDream
|
||||
DialogController.OnDialogueComplete -= HideDialog;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (Input.GetKeyDown(KeyCode.Q) && Input.GetKey(KeyCode.P) )
|
||||
{
|
||||
quickTalkButton.SetActive(!quickTalkButton.activeSelf);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 显示文字
|
||||
/// </summary>
|
||||
@@ -176,11 +169,11 @@ namespace AibisDream
|
||||
|
||||
private void GetDialogComponentInBox(GameObject box)
|
||||
{
|
||||
actorName = box.transform.Find(ACTOR_NAME).gameObject.GetComponent<TextMeshProUGUI>();
|
||||
dialogText = box.transform.Find(DIALOG_TEXT).gameObject.GetComponent<TypewriterByCharacter>();
|
||||
nextButton = box.transform.Find(NEXT_BUTTON).GetComponent<Button>();
|
||||
choiceBox = box.transform.Find(CHOICE_BUTTON).gameObject;
|
||||
quickTalkButton = box.transform.Find(QUICK_TALK).gameObject;
|
||||
actorName = box.transform.Find(ActorName).gameObject.GetComponent<TextMeshProUGUI>();
|
||||
dialogText = box.transform.Find(DialogText).gameObject.GetComponent<TypewriterByCharacter>();
|
||||
nextButton = box.transform.Find(NextButton).GetComponent<Button>();
|
||||
choiceBox = box.transform.Find(ChoiceBox).gameObject;
|
||||
quickTalkButton = box.transform.Find(QuickTalk).gameObject;
|
||||
|
||||
quickTalkButton.GetComponent<Button>().onClick.AddListener(SwitchQuickTalkMode);
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using AibisDream.Config;
|
||||
using Yarn.Unity;
|
||||
|
||||
namespace AibisDream
|
||||
{
|
||||
public interface IDialogView
|
||||
{
|
||||
public void ShowLine(string dialogLine, CharacterVo character, Action nextStep, bool isAutoSkip = false);
|
||||
|
||||
public void ShowOptions(DialogueOption[] dialogueOptions, Action<int> onOptionSelected);
|
||||
|
||||
public void HideDialog();
|
||||
|
||||
public bool TrySkipLine();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d9f2abb67889d3247a1314a7788f8554
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -12,4 +12,3 @@ namespace AibisDream
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AibisDream
|
||||
namespace AibisDream.Utility
|
||||
{
|
||||
public class CommonUtil
|
||||
public static class CommonUtil
|
||||
{
|
||||
private const float Epsilon = 1e-5f;
|
||||
|
||||
|
||||
@@ -0,0 +1,366 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
|
||||
namespace AibisDream
|
||||
{
|
||||
public class CsvUtil
|
||||
{
|
||||
// 默认编码
|
||||
private static readonly Encoding DefaultEncode = Encoding.UTF8;
|
||||
|
||||
// 默认分隔符
|
||||
private const char FieldSeparator = ',';
|
||||
|
||||
public static List<string[]> Read(string filePath)
|
||||
{
|
||||
List<string[]> dataList = new List<string[]>();
|
||||
StreamReader reader;
|
||||
try
|
||||
{
|
||||
using (reader = new StreamReader(filePath, DefaultEncode))
|
||||
{
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
string line = reader.ReadLine();
|
||||
if (line == null) break;
|
||||
string[] data = line.Split(FieldSeparator);
|
||||
dataList.Add(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
foreach (Process process in Process.GetProcesses())
|
||||
{
|
||||
if (process.ProcessName.ToUpper().Equals("EXCEL"))
|
||||
process.Kill();
|
||||
}
|
||||
|
||||
GC.Collect();
|
||||
Thread.Sleep(50);
|
||||
Console.WriteLine(ex.StackTrace);
|
||||
using (reader = new StreamReader(filePath, DefaultEncode))
|
||||
{
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
string line = reader.ReadLine();
|
||||
if (line == null) break;
|
||||
string[] data = line.Split(FieldSeparator);
|
||||
dataList.Add(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reader.Close();
|
||||
return dataList;
|
||||
}
|
||||
|
||||
public static void WriteCsv(string filePath, List<string[]> dataList)
|
||||
{
|
||||
StreamWriter writer = new StreamWriter(filePath, false, DefaultEncode);
|
||||
foreach (string[] data in dataList)
|
||||
{
|
||||
writer.WriteLine(string.Join(FieldSeparator, data));
|
||||
}
|
||||
|
||||
writer.Close();
|
||||
}
|
||||
|
||||
public static List<T> ReadAsBean<T>(string filePath) where T : class
|
||||
{
|
||||
// 反射部分
|
||||
var targetType = typeof(T);
|
||||
var props = targetType.GetProperties();
|
||||
|
||||
// 读取CSV
|
||||
using var reader = new StreamReader(filePath, DefaultEncode);
|
||||
|
||||
// 第一行是注释
|
||||
reader.ReadLine();
|
||||
// 第二行作为title
|
||||
var titles = reader.ReadLine()?.Split(FieldSeparator);
|
||||
if (titles == null)
|
||||
{
|
||||
Debug.WriteLine("csv无标题");
|
||||
throw new Exception("csv无标题");
|
||||
}
|
||||
|
||||
var res = new List<T>();
|
||||
// 第二行开始读配置内容
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
string newLine = reader.ReadLine();
|
||||
if (string.IsNullOrEmpty(newLine)) break;
|
||||
|
||||
string[] rowData = newLine.Split(FieldSeparator);
|
||||
if (rowData == null || rowData.Length == 0) continue;
|
||||
|
||||
// 转为Bean
|
||||
var obj = Activator.CreateInstance(targetType);
|
||||
foreach (var prop in props)
|
||||
{
|
||||
string name = prop.Name;
|
||||
if (!titles.Contains(prop.Name))
|
||||
continue;
|
||||
|
||||
int idx = Array.IndexOf(titles, prop.Name);
|
||||
prop.SetValue(obj, GetDefaultValue(prop, rowData[idx]));
|
||||
}
|
||||
|
||||
res.Add(obj as T);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public static bool ReadAsDataTable(ref DataTable myCsvDt, string filepath)
|
||||
{
|
||||
var strPath = filepath; //csv文件的路径
|
||||
|
||||
try
|
||||
{
|
||||
bool blnFlag = true;
|
||||
|
||||
StreamReader reader = new StreamReader(strPath, DefaultEncode);
|
||||
myCsvDt = new DataTable();
|
||||
while (reader.ReadLine() is { } strLine)
|
||||
{
|
||||
var aryLine = strLine.Split(FieldSeparator);
|
||||
//第一行是列的名字,给datatable加上列名,
|
||||
if (blnFlag)
|
||||
{
|
||||
blnFlag = false;
|
||||
var intColCount = aryLine.Length;
|
||||
|
||||
#region 序号作为列头
|
||||
|
||||
//int col = 0;
|
||||
//for (int i = 0; i < intColCount; i++)
|
||||
//{
|
||||
// col = i + 1;
|
||||
// mydc = new DataColumn(col.ToString());
|
||||
// mycsvdt.Columns.Add(mydc);
|
||||
//}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 第一行作为列头
|
||||
|
||||
for (int i = 0; i < intColCount; i++)
|
||||
{
|
||||
myCsvDt.Columns.Add(new DataColumn(aryLine[i]));
|
||||
}
|
||||
|
||||
continue;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
//填充数据并加入到datatable中
|
||||
myCsvDt.Rows.Add(aryLine);
|
||||
}
|
||||
|
||||
reader.Close();
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void ExportAsCsv(DataTable dt, string savaPath, string strName)
|
||||
{
|
||||
string strPath = savaPath + "\\" + strName; //保存到指定目录下
|
||||
|
||||
if (File.Exists(strPath))
|
||||
{
|
||||
File.Delete(strPath);
|
||||
}
|
||||
|
||||
//先打印标头
|
||||
StringBuilder strColu = new StringBuilder();
|
||||
StringBuilder strValue = new StringBuilder();
|
||||
StreamWriter sw = new StreamWriter(new FileStream(strPath, FileMode.CreateNew), DefaultEncode);
|
||||
try
|
||||
{
|
||||
for (var i = 0; i <= dt.Columns.Count - 1; i++)
|
||||
{
|
||||
strColu.Append(dt.Columns[i].ColumnName);
|
||||
strColu.Append(FieldSeparator);
|
||||
}
|
||||
|
||||
strColu.Remove(strColu.Length - 1, 1); //移出掉最后一个,字符
|
||||
sw.WriteLine(strColu);
|
||||
foreach (DataRow dr in dt.Rows)
|
||||
{
|
||||
strValue.Remove(0, strValue.Length); //移出
|
||||
for (var i = 0; i <= dt.Columns.Count - 1; i++)
|
||||
{
|
||||
strValue.Append(dr[i]);
|
||||
strValue.Append(FieldSeparator);
|
||||
}
|
||||
|
||||
strValue.Remove(strValue.Length - 1, 1); //移出掉最后一个,字符
|
||||
sw.WriteLine(strValue);
|
||||
}
|
||||
|
||||
sw.Close();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(ex.Message);
|
||||
sw.Close();
|
||||
}
|
||||
}
|
||||
|
||||
#region 类型转换
|
||||
|
||||
private static object GetDefaultValue(PropertyInfo prop, string value)
|
||||
{
|
||||
return prop.PropertyType.Name switch
|
||||
{
|
||||
"String" => ToNormalString(value),
|
||||
"Int32" => ToInt32(value),
|
||||
"Decimal" => ToDecimal(value),
|
||||
"Single" => ToFloat(value),
|
||||
"Boolean" => ToBoolean(value),
|
||||
"DateTime" => ToDateTime(value),
|
||||
"Double" => ToDouble(value),
|
||||
_ => ToEnum(value, prop)
|
||||
};
|
||||
}
|
||||
|
||||
private static object ToEnum(string value, PropertyInfo prop)
|
||||
{
|
||||
return prop.PropertyType.BaseType == typeof(Enum) ? Enum.Parse(prop.PropertyType, value) : null;
|
||||
}
|
||||
|
||||
///<summary>
|
||||
/// 转换为Int32
|
||||
/// </summary>
|
||||
private static int ToInt32(string obj)
|
||||
{
|
||||
if (string.IsNullOrEmpty(obj)) return 0;
|
||||
try
|
||||
{
|
||||
if (obj.Contains("."))
|
||||
return (int)Convert.ToSingle(obj);
|
||||
if (int.TryParse(obj, out var tmp))
|
||||
return tmp;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 转换为字符串
|
||||
/// </summary>
|
||||
private static string ToNormalString(string obj)
|
||||
{
|
||||
if (obj == null)
|
||||
return string.Empty;
|
||||
try
|
||||
{
|
||||
return Convert.ToString(obj);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
///<summary>
|
||||
/// 转换为日期
|
||||
/// </summary>
|
||||
private static DateTime ToDateTime(string obj)
|
||||
{
|
||||
if (string.IsNullOrEmpty(obj))
|
||||
{
|
||||
return Convert.ToDateTime("1970-01-01 00:00:00");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return Convert.ToDateTime(obj);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return Convert.ToDateTime("1970-01-01 00:00:00");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 转换为布尔型
|
||||
/// </summary>
|
||||
private static bool ToBoolean(string obj)
|
||||
{
|
||||
if (obj == null) return false;
|
||||
|
||||
return obj.ToLower() == "true" || obj == "1";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 转换为十进制数值
|
||||
/// </summary>
|
||||
private static Decimal ToDecimal(string obj)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
return 0M;
|
||||
}
|
||||
|
||||
var resultString = Regex.Replace(obj, "[^0-9.]", "");
|
||||
var result = resultString.Length == 0 ? 0M : decimal.Parse(resultString);
|
||||
if (obj.StartsWith("-"))
|
||||
{
|
||||
result *= -1M;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
///<summary>
|
||||
/// 转换为双精度.
|
||||
/// </summary>
|
||||
private static double ToDouble(string obj)
|
||||
{
|
||||
if (double.TryParse(obj, out var num))
|
||||
return num;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 转换为单精度.
|
||||
/// </summary>
|
||||
private static float ToFloat(string value)
|
||||
{
|
||||
var normalStr = ToNormalString(value);
|
||||
if (string.IsNullOrEmpty(normalStr))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (float.TryParse(normalStr, out var result))
|
||||
return result;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5beb292d61f8e134284d2e928fb77d0a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -28,5 +28,4 @@ namespace AibisDream
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2c23e36c3bf88034e98063d0c27ba935
|
||||
guid: 214dbdcae17401d4e94285d5e416ce66
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace AibisDream.Utility
|
||||
{
|
||||
public abstract class SingletonBase<T> where T : SingletonBase<T>
|
||||
{
|
||||
private static T _instance;
|
||||
|
||||
public static T Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance != null) return _instance;
|
||||
// 先获取所有非public的构造方法
|
||||
var ctors = typeof(T).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
// 从ctors中获取无参的构造方法
|
||||
var ctor = Array.Find(ctors, c => c.GetParameters().Length == 0);
|
||||
if (ctor == null)
|
||||
throw new Exception("Non-public ctor() not found!");
|
||||
// 调用构造方法
|
||||
_instance = ctor.Invoke(null) as T;
|
||||
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0e06ce6997ac4a14fa439ac9c3747fe9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,11 +1,12 @@
|
||||
using System.Collections;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using System.Linq;
|
||||
using AibisDream.Config;
|
||||
using Yarn;
|
||||
using Yarn.Markup;
|
||||
using Yarn.Unity;
|
||||
|
||||
namespace AibisDream
|
||||
namespace AibisDream.Utility
|
||||
{
|
||||
public static class YarnUtil
|
||||
{
|
||||
@@ -14,6 +15,8 @@ namespace AibisDream
|
||||
private const string OptionAttr = "options";
|
||||
|
||||
private const string CommandProp = "name";
|
||||
|
||||
private const string AutoNext = "auto_next";
|
||||
|
||||
public static bool HasCommandAttr(this MarkupParseResult text)
|
||||
{
|
||||
@@ -37,5 +40,93 @@ namespace AibisDream
|
||||
{
|
||||
runner.Dialogue.CommandHandler += onCommand;
|
||||
}
|
||||
|
||||
#region 处理行信息
|
||||
|
||||
/// <summary>
|
||||
/// 当前行是否应该自动跳过
|
||||
/// </summary>
|
||||
/// <param name="line">当前行</param>
|
||||
/// <returns>是否应该自动跳过</returns>
|
||||
public static bool IsAutoSkipLine(this LocalizedLine line)
|
||||
{
|
||||
return line.Metadata != null && line.Metadata.Contains(AutoNext);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从line中获取角色vo定义
|
||||
/// </summary>
|
||||
/// <param name="line">行数据</param>
|
||||
/// <returns>结果</returns>
|
||||
public static CharacterVo GetCharacterVo(this LocalizedLine line)
|
||||
{
|
||||
if (line.Text.TryGetAttributeWithName("character", out var characterAttribute))
|
||||
{
|
||||
// 直接写就使用配置选项
|
||||
if (characterAttribute.Properties.TryGetValue("name", out var nameValue))
|
||||
{
|
||||
return GetCharacterVoByKey(nameValue.StringValue);
|
||||
}
|
||||
|
||||
// 带key就说明可能有自定义属性
|
||||
if (characterAttribute.Properties.TryGetValue("key", out var keyValue))
|
||||
{
|
||||
// 先从配置文件找
|
||||
var charVo = GetCharacterVoByKey(keyValue.StringValue);
|
||||
// 再用自定义填充
|
||||
return FullCharVoWithProps(charVo, characterAttribute.Properties);
|
||||
}
|
||||
|
||||
// 找不到Name和Key
|
||||
return new CharacterVo();
|
||||
}
|
||||
|
||||
// 没有Character就直接返回空
|
||||
return new CharacterVo();
|
||||
}
|
||||
|
||||
private static CharacterVo GetCharacterVoByKey(string charKey)
|
||||
{
|
||||
if (ConfigUtil.Instance.TryGetCharacter(charKey, out var character))
|
||||
{
|
||||
return character.GetStructVo();
|
||||
}
|
||||
|
||||
return new CharacterVo
|
||||
{
|
||||
key = charKey,
|
||||
cn = charKey,
|
||||
role = ActorRole.Aside
|
||||
};
|
||||
}
|
||||
|
||||
private static CharacterVo FullCharVoWithProps(CharacterVo vo, IReadOnlyDictionary<string, MarkupValue> props)
|
||||
{
|
||||
foreach (var pair in props)
|
||||
{
|
||||
switch (pair.Key)
|
||||
{
|
||||
case "cn":
|
||||
vo.cn = pair.Value.StringValue;
|
||||
break;
|
||||
case "nameColor":
|
||||
vo.nameColor = pair.Value.StringValue;
|
||||
break;
|
||||
case "headPicPath":
|
||||
vo.headPicPath = pair.Value.StringValue;
|
||||
break;
|
||||
case "voicePath":
|
||||
vo.voicePath = pair.Value.StringValue;
|
||||
break;
|
||||
case "role":
|
||||
vo.role = Enum.TryParse<ActorRole>(pair.Value.StringValue, out var type) ? type : ActorRole.Aside;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return vo;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user