Unity的live2dgalgame多语言可配置剧情框架
这段代码用于读取表格
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using OfficeOpenXml;
using System.IO;
using UnityEngine.Networking;
using UnityEngine.UI;
using Random = UnityEngine.Random;
public class Plots : MonoBehaviour
{
public static string ReadingExcel;//正在读取的表格
[Header("表格文件夹")] public static string URL = Application.streamingAssetsPath;
public static string PlotsEXCEL = "Plots";
private static bool m_loaded;
public class plot
{
public string index, CN,NameCN, NameEN,EN,NameJP, JP,Face;
}
public static List<plot> S_Plots= new List<plot>();
public virtual void OnEnable()
{
initialization();
}
//初始化
public void initialization()
{
if (!m_loaded)
{
LoadExcel();
m_loaded = true;
}
}
void LoadExcel()
{
//获取Excel文件的信息
foreach (var VARIABLE in ReadFile())
{
Debug.Log("剧情挂载成功");
FileInfo fileInfo = new FileInfo(VARIABLE);
//加载背包信息
if (VARIABLE.Contains(PlotsEXCEL))
{
//通过Excel表格的文件信息,打开Excel表格
//使用using(){}语句命令,在结束时自动关闭文件
using (ExcelPackage excelPackage = new ExcelPackage(fileInfo))
{
//读取Excel中的第一张表, 注意EPPlus的索引值是从1开始的
ExcelWorksheet worksheet = excelPackage.Workbook.Worksheets[1];
//取得第一行第一列的数据
// Debug.Log("行数"+worksheet.Dimension.End.Row + 1);
for (int Left = 2; Left < worksheet.Dimension.End.Row + 1; Left++) //根据行数遍历
{
if (worksheet.Cells[Left, 1].Value.ToString().Length>0)
{
plot Plot = new plot();
Plot.index= worksheet.Cells[Left, 1].Value.ToString();
Plot.Face= worksheet.Cells[Left, 2].Value.ToString();
Plot.CN= worksheet.Cells[Left, 3].Value.ToString();
Plot.NameCN= worksheet.Cells[Left, 4].Value.ToString();
Plot.EN= worksheet.Cells[Left, 5].Value.ToString();
Plot.NameEN= worksheet.Cells[Left, 6].Value.ToString();
Plot.JP= worksheet.Cells[Left, 7].Value.ToString();
Plot.NameJP= worksheet.Cells[Left, 8].Value.ToString();
S_Plots.Add(Plot);
}
}
}
}
}
}
/// <summary>
/// 字符串转Enum
/// </summary>
/// <typeparam name="T">枚举</typeparam>
/// <param name="str">字符串</param>
/// <returns>转换的枚举</returns>
public static T ToEnum<T>(string str)
{
try
{
return (T)Enum.Parse(typeof(T), str);
}
catch (ArgumentException e)
{
Debug.LogError("未找到系列"+str);
throw;
}
}
static List<string> ReadFile()
{
List<string> files = GetFiles(URL, "*.xlsx");
List<string> GetFiles(string directory, string pattern)
{
List<string> files = new List<string>();
foreach (var item in Directory.GetFiles(directory, pattern))
{
files.Add(item);
}
foreach (var item in Directory.GetDirectories(directory))
{
files.AddRange(GetFiles(item, pattern));
}
return files;
}
return files;
}
}
这个方法里面设置剧情列表,和分支事件
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Text.RegularExpressions;
using DG.Tweening;
using Live2D.Cubism.Rendering;
using OfficeOpenXml.FormulaParsing.Excel.Functions.Math;
using UnityEngine;
using UnityEngine.UI;
public class PlotReader : MonoBehaviour
{
public Button[] ChoiceButton;
public Text Text_Plot,Text_Name;
public static int[] PlotBar;
public static Action PlotFinEvent;
public Button BTN_NextStep,BTN_Auto,BTN_Skip;
public bool AutoMode,SkipMode;
public Button LanguageCN, LanguageEN, LanguageJP;
public float DomoveOffeset = 100;
public float DomoveTime=0.1f;
public GameObject Live2DFolider;
public int index=0;
private void Start()
{
LanguageCN.onClick.AddListener(()=>
{
Center.Language = "CN";
RefreshLanguage();
});
LanguageEN.onClick.AddListener(()=>
{
Center.Language = "EN";
RefreshLanguage();
});
LanguageJP.onClick.AddListener(()=>
{
Center.Language = "JP";
RefreshLanguage();
});
BTN_Auto.onClick.AddListener(() =>
{
AutoMode = !AutoMode;
SkipMode = false;
});
BTN_Skip.onClick.AddListener(() =>
{
SkipMode = !SkipMode;
AutoMode = false;
});
BTN_NextStep.onClick.AddListener(PlotNext);
//注意!!! 剧情要-1,选项要-2
SetPlotBar(new int[]{0,1,2,3,4,5,6,7},() => MakeChoice(new int[] { 7, 8 ,16}));
PlotNext();
}
private float AutoTimeindex;
private float AutoTimeMax = 3f;
private void Update()
{
if (AutoMode)
{
if (index<=PlotBar.Length)
{
AutoTimeindex -= Time.deltaTime;
}
if (AutoTimeindex < 0 )
{
AutoTimeindex = AutoTimeMax;
PlotNext();
}
}
if (SkipMode)
{
if (index<=PlotBar.Length)
{
AutoTimeindex -= Time.deltaTime*20;
}
if (AutoTimeindex < 0 )
{
AutoTimeindex = AutoTimeMax;
PlotNext();
}
}
}
public void PlotNext()
{
if (index<PlotBar.Length)
{
DoMethodEvent(Plots.S_Plots[PlotBar[index]].Face);
Text_Plot.text = GetLanguagePlot(PlotBar[index]);
Text_Name.text = GetLanguageName(PlotBar[index]);
}else if (index== PlotBar.Length)
{
PlotFinEvent();
}
index++;
}
public void RefreshLanguage()
{
try
{
Text_Plot.text = GetLanguagePlot(PlotBar[index-1]);
Text_Name.text = GetLanguageName(PlotBar[index-1]);
}
catch (IndexOutOfRangeException e)
{
Text_Plot.text = GetLanguagePlot(PlotBar[PlotBar.Length-1]);
Text_Name.text = GetLanguageName(PlotBar[PlotBar.Length-1]);
}
}
/// <summary>
/// 根据语言获得剧情
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public string GetLanguagePlot(int index)
{
// Debug.LogError(index+Plots.S_Plots[index].CN+Plots.S_Plots[index].NameCN);
switch (Center.Language)
{
case "CN":
return Plots.S_Plots[index].CN;
case "EN":
return Plots.S_Plots[index].EN;
case "JP":
return Plots.S_Plots[index].JP;
}
Debug.LogError("语言??");
return null;
}
/// <summary>
/// 根据语言获得剧情
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public string GetLanguageName(int index)
{
switch (Center.Language)
{
case "CN":
return Plots.S_Plots[index].NameCN;
case "EN":
return Plots.S_Plots[index].NameEN;
case "JP":
return Plots.S_Plots[index].NameJP;
}
Debug.LogError("语言??");
return null;
}
/// <summary>
/// 设置对话列表
/// </summary>
/// <param name="t"></param>
public void SetPlotBar(int[] t,Action FinEvent)
{
PlotBar = t;
PlotFinEvent = FinEvent;
}
#region 分支
public void MakeChoice(int [] choices)
{
CloseAllButtons();
for (int i = 0; i<choices.Length; i++)
{
int i1 = i;
ChoiceButton[i1].gameObject.SetActive(true);
ChoiceButton[i1].transform.Find("Text").GetComponent<Text>().text = GetLanguagePlot(choices[i1]+1);
ChoiceButton[i1].onClick.AddListener(() =>
{
index = 0;
Debug.LogError("执行事件BTN"+(choices[i1]+2));
ExecuteMethodByName("BTN"+(choices[i1]+2));
CloseAllButtons();
PlotNext();
});
}
}
public void CloseAllButtons()
{
foreach (var VARIABLE in ChoiceButton)
{
VARIABLE.onClick.RemoveAllListeners();
VARIABLE.gameObject.SetActive(false);
}
}
public void BTN9()
{
SetPlotBar(new int[]{10,11,12,13},JumpToGameLevel);
}
public void BTN10()
{
SetPlotBar(new int[]{14,15,16},JumpToGameLevel);
}
public void BTN18()
{
SetPlotBar(new int[]{0,1,2,3,4,5,6,7},() => MakeChoice(new int[] { 7, 8 ,16}));
}
#endregion
public void JumpToGameLevel()
{
Debug.LogError("将跳转场景");
}
public void ExecuteMethodByName(string methodName)
{
// 使用反射获取类的类型
Type type = this.GetType();
// 使用反射获取方法信息
MethodInfo methodInfo = type.GetMethod(methodName);
if (methodInfo != null)
{
// 调用匹配的方法
methodInfo.Invoke(this, null);
}
else
{
Console.WriteLine("Method not found: " + methodName);
}
}
public string[] CutMethod(string input)
{
return input.Split('+');
}
public void DoMethodEvent(string input)
{
Debug.LogError(input);
foreach (var VARIABLE in CutMethod(input))
{
if (!VARIABLE.Contains("("))
{
ExecuteMethodByName(VARIABLE);
}
else if (VARIABLE.Contains("JOIN_"))
{
Match match = Regex.Match(VARIABLE, @"\(([^)]*)\)");
if (match.Success)
{
// 获取括号内的内容,并使用逗号分割
string[] parts = match.Groups[1].Value.Split(',');
if (parts.Length == 4)
{
string stringValue = parts[0];
int intValue1, intValue2;
if (int.TryParse(parts[1], out intValue1) && int.TryParse(parts[2], out intValue2))
{
string stringValue2 = parts[3];
GameObject go = Instantiate(Resources.Load<GameObject>($"Live2D/{stringValue}"), Live2DFolider.transform);
go.transform.position = new Vector3(-999, -999, 2);
go.name = go.name.Replace("(Clone)", "");
Vector3 worldCoordinate= Vector3.down;
;
switch (stringValue2)
{
case "Left":
worldCoordinate =Camera.main.ScreenToWorldPoint( new Vector3(intValue1-DomoveOffeset, intValue2, 2));
Debug.LogError(worldCoordinate);
worldCoordinate.z = 2;
go.transform.position = worldCoordinate;
worldCoordinate = Camera.main.ScreenToWorldPoint(new Vector3(intValue1, intValue2, 0));
worldCoordinate.z = 0;
go.transform.DOMove(worldCoordinate,DomoveTime);
break;
case "Right":
worldCoordinate =Camera.main.ScreenToWorldPoint( new Vector3(intValue1+DomoveOffeset, intValue2, 2));
Debug.LogError(worldCoordinate);
worldCoordinate.z = 2;
go.transform.position = worldCoordinate;
worldCoordinate = Camera.main.ScreenToWorldPoint(new Vector3(intValue1, intValue2, 0));
worldCoordinate.z = 0;
go.transform.DOMove(worldCoordinate,DomoveTime);
break;
}
}
else
{
Debug.LogError("Failed to parse integers from the input.");
}
}
else
{
Debug.LogError("Input does not contain 4 comma-separated values.");
}
}
else
{
Debug.LogError("Input does not match the expected format.");
}
}
else if (VARIABLE.Contains("SETA_"))
{
Match match = Regex.Match(VARIABLE, @"\(([^)]*)\)");
if (match.Success)
{
// 获取括号内的内容,并使用逗号分割
string[] parts = match.Groups[1].Value.Split(',');
if (parts.Length == 3)
{
string stringValue = parts[0];
int intValue1, intValue2;
if (int.TryParse(parts[1], out intValue1) && int.TryParse(parts[2], out intValue2))
{
GameObject go = Live2DFolider.transform.Find(parts[0]).gameObject;
SetLive2DAlpha(go,intValue1,intValue2);
}
else
{
Debug.LogError("Failed to parse integers from the input.");
}
}
else
{
Debug.LogError("Input does not contain 3 comma-separated values."+VARIABLE+$"length={parts.Length}");
}
}
else
{
Debug.LogError("Input does not match the expected format.");
}
}
else if (VARIABLE.Contains("FACE"))
{
Match match = Regex.Match(VARIABLE, @"\(([^)]*)\)");
if (match.Success)
{
// 获取括号内的内容,并使用逗号分割
string[] parts = match.Groups[1].Value.Split(',');
if (parts.Length == 2)
{
string stringValue = parts[0];
string stringValue2 = parts[1];
GameObject go = Live2DFolider.transform.Find(parts[0]).gameObject;
SetFace(go,stringValue2);
}
else
{
Debug.LogError("Input does not contain 4 comma-separated values.");
}
}
else
{
Debug.LogError("Input does not match the expected format.");
}
}
else if (VARIABLE.Contains("LEAV_"))
{
Match match = Regex.Match(VARIABLE, @"\(([^)]*)\)");
if (match.Success)
{
// 获取括号内的内容,并使用逗号分割
string[] parts = match.Groups[1].Value.Split(',');
if (parts.Length == 2)
{
string stringValue = parts[0];
GameObject go = Live2DFolider.transform.Find(parts[0]).gameObject;
Vector3 worldCoordinate;
StartCoroutine(des(go));
break;
IEnumerator des(GameObject go)
{
SetLive2DAlpha(go,
(int)(go.transform.Find("Drawables").GetComponentsInChildren<CubismRenderer>()[0]
.GetComponent<CubismRenderer>().Color.a)*100, 0);
Debug.LogError($"Des{go.name}");
yield return new WaitForSeconds(0.3f);
Destroy(go);
}
}
else
{
Debug.LogError("Input does not contain 4 comma-separated values.");
}
}
else
{
Debug.LogError("Input does not match the expected format.");
}
}
}
}
public void SetLive2DAlpha(GameObject target, int begin,int end)
{
StartCoroutine(AlphaChangerCoroutine(target, begin / 100.0f, end / 100.0f));
}
private IEnumerator AlphaChangerCoroutine(GameObject target, float begin, float end)
{
foreach (var renderer in target.transform.Find("Drawables").GetComponentsInChildren<CubismRenderer>())
{
Color currentColor = renderer.Color;
currentColor.a = begin;
Debug.LogError(currentColor.a);
renderer.Color = currentColor;
}
float duration = 0.1f;
int numSteps = 10;
float stepTime = duration / numSteps;
for (int step = 0; step <= numSteps; step++)
{
float a = begin;
if (end>begin)
{
a += (end - begin) / numSteps * step;
}
else
{
a -= (begin - end) / numSteps * step;
}
foreach (var renderer in target.transform.Find("Drawables").GetComponentsInChildren<CubismRenderer>())
{
// Debug.LogError(renderer.name);
Color currentColor = renderer.Color;
currentColor.a = a;
Debug.LogError(currentColor.a);
renderer.Color = currentColor;
}
yield return new WaitForSeconds(stepTime);
}
}
public void SetFace(GameObject go, string Animatorname)
{
go.GetComponent<Animator>().Play(Animatorname);
}
}
最终效果