Files
Main/Assets/Launcher/ExternalLibs/Others/StoryScript.cs

735 lines
28 KiB
C#
Raw Normal View History

2025-01-25 04:38:09 +08:00
using System.Collections;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
public class StoryScript : MonoBehaviour
{
public const string InstallPath = "install";
/// <summary>
/// 位置父节点的路径
/// </summary>
public const string PosPath = "install/[Transform]";
/// <summary>
/// 路点父节点的路径
/// </summary>
public const string PathPointPath = "install/[PathPoint]";
/// <summary>
/// TimelineNames父节点的路径
/// </summary>
public const string TimelinePath = "install/[TimelineNames]";
/// <summary>
/// TimelineNames父节点的路径
/// </summary>
public const string FormPath = "install/[FormNames]";
/// <summary>
/// 触发器盒子父节点的路径
/// </summary>
public const string TriggerBoxPath = "install/[TriggerBox]";
/// <summary>
/// 3D文字
/// </summary>
public const string Text3DPath = "install/[Text3D]";
/// <summary>
/// 触发器父节点的路径
/// </summary>
public const string TriggersPath = "triggers";
/// <summary>
/// 故事名
/// </summary>
//public string storyName;
/// <summary>
/// 故事id
/// </summary>
public int id;
/// <summary>
/// 描述
/// </summary>
public string describe;
/// <summary>
/// 需要预加载的Timeline
/// </summary>
public List<string> preLoadTimelineNames = new List<string>();
/// <summary>
/// 需要预加载的UI界面
/// </summary>
public List<string> preLoadFormNames = new List<string>();
/// <summary>
/// 位置
/// </summary>
public Dictionary<string, Transform> transforms = new Dictionary<string, Transform>();
/// <summary>
/// 路点
/// </summary>
public Dictionary<string, List<Transform>> pathPoint = new Dictionary<string, List<Transform>>();
/// <summary>
/// 触发器盒子
/// </summary>
public Dictionary<string, Transform> triggerBoxDic = new Dictionary<string, Transform>();
/// <summary>
/// 3D文字
/// </summary>
public Dictionary<string, GameObject> Text3DDic = new Dictionary<string, GameObject>();
/// <summary>
/// 触发器
/// </summary>
public List<StoryTrigger> storyTriggers = new List<StoryTrigger>();
private void CheckNode(string path)
{
//判断是否在Inspacter面板
if (transform.gameObject.activeInHierarchy)
{
var parentTf = transform.Find(path);
if (parentTf == null)
{
var strArr = path.Split('/');
var gobj = new GameObject(strArr[1]);
parentTf = gobj.transform;
parentTf.SetParent(transform.Find(strArr[0]));
#if UNITY_EDITOR
if (UnityEditor.PrefabUtility.GetPrefabObject(gameObject) != null)
{
UnityEditor.PrefabUtility.ReplacePrefab(gameObject, UnityEditor.PrefabUtility.GetPrefabParent(gameObject), UnityEditor.ReplacePrefabOptions.ConnectToPrefab);
}
#endif
}
}
}
//获取节点下所有子节点的名字
public void GetNames(List<string> list, Transform parentTf)
{
list.Clear();
//var parentTf = transform.Find(path);
int count = parentTf.childCount;
if (count > 0)
{
for (int i = 0; i < count; i++)
{
var child = parentTf.GetChild(i);
var name = child.gameObject.name;
list.Add(name);
}
}
list.Sort((a, b) =>
{
return a.CompareTo(b);
});
}
//将List中的所有string 转成Lua格式
public string GetLuaFormatByStringList(string title, List<string> stringList)
{
var count = stringList.Count;
StringBuilder sb = new StringBuilder();
sb.AppendLine(string.Format(" {0} = {{", title));
if (count > 0)
{
Dictionary<string, bool> allTimelineNameDic = new Dictionary<string, bool>();
for (int i = 0; i < count; i++)
{
if (!allTimelineNameDic.ContainsKey(stringList[i]))
{
allTimelineNameDic.Add(stringList[i], true);
}
else
{
Debug.LogError("出现了相同的名字!!! " + stringList[i]);
}
}
foreach (var item in allTimelineNameDic)
{
sb.AppendLine(string.Format(" \"{0}\",", item.Key));
}
}
sb.AppendLine(" },");
return sb.ToString();
}
//获取所有需要预加载的UIForm名字
public List<string> GetAllFormNames()
{
CheckNode(FormPath);
var parentTf = transform.Find(FormPath);
if (parentTf != null)
GetNames(preLoadFormNames, transform.Find(FormPath));
return preLoadFormNames;
}
//获取所有需要预加载的Timeline名字
public List<string> GetAllTimelineNames()
{
CheckNode(TimelinePath);
var parentTf = transform.Find(TimelinePath);
if (parentTf != null)
GetNames(preLoadTimelineNames, parentTf);
return preLoadTimelineNames;
}
//获取所有需要预加载的UIForm名字 lua格式
public string GetLuaFormatByAllFormNames()
{
return GetLuaFormatByStringList("preLoadFormNames", GetAllFormNames());
}
//获取所有需要预加载的Timeline名字 lua格式
public string GetLuaFormatByAllTimelineNames()
{
return GetLuaFormatByStringList("preLoadTimelineNames", GetAllTimelineNames());
}
//获取所有位置
public Dictionary<string, Transform> GetAllPos()
{
CheckNode(PosPath);
transforms.Clear();
var posTf = transform.Find(PosPath);
int count = posTf.childCount;
if (count > 0)
{
for (int i = 0; i < count; i++)
{
var child = posTf.GetChild(i);
var name = child.gameObject.name;
Transform tf;
if (!transforms.TryGetValue(name, out tf))
{
transforms.Add(name, child);
}
else
{
Debug.LogError("故事:" + gameObject.name + ",位置中有相同名字:" + name);
}
}
}
return transforms;
}
//获取所有位置 lua格式
public string GetLuaFormatByAllPos()
{
// transform = {
// npc10001 = { { 13,0,17},{ 0,90,0} },
// interactor10016 = { { 13,0,19},{ 1,1,0} },
// interactor10015 = { { 13,0,21},{ 1,1,1} },
// },
var allTranform = GetAllPos();
StringBuilder sb = new StringBuilder();
sb.AppendLine(" transform = {");
foreach (var item in allTranform)
{
var tf = item.Value;
var pos = tf.position;
var dir = tf.eulerAngles;
sb.AppendLine(string.Format(" {0} = {{{{{1},{2},{3}}},{{{4},{5},{6}}}}},",
item.Key, pos.x < 0.001f ? 0 : pos.x, pos.y < 0.001f ? 0 : pos.y, pos.z < 0.001f ? 0 : pos.z,
dir.x < 0.001f ? 0 : dir.x, dir.y < 0.001f ? 0 : dir.y, dir.z < 0.001f ? 0 : dir.z));
}
sb.AppendLine(" },");
return sb.ToString();
}
//获取所有路点
public Dictionary<string, List<Vector3>> GetAllPathPoint()
{
CheckNode(PathPointPath);
pathPoint.Clear();
var allPathPoint = new Dictionary<string, List<Vector3>>();
var pathPointTf = transform.Find(PathPointPath);
int count = pathPointTf.childCount;
if (count > 0)
{
for (int i = 0; i < count; i++)
{
var childTf = pathPointTf.GetChild(i);
var name = childTf.gameObject.name;
List<Vector3> pathPoints;
List<Transform> pathPointTfs;
if (!allPathPoint.TryGetValue(name, out pathPoints))
{
pathPoints = new List<Vector3>();
pathPointTfs = new List<Transform>();
int childCount = childTf.childCount;
for (int j = 0; j < childCount; j++)
{
pathPoints.Add(childTf.GetChild(j).position);
pathPointTfs.Add(childTf.GetChild(j));
}
allPathPoint.Add(name, pathPoints);
pathPoint.Add(name, pathPointTfs);
}
else
{
Debug.LogError("故事:" + gameObject.name + ",路点中有相同名字:" + name);
}
}
}
return allPathPoint;
}
//获取所有路点 lua格式
public string GetLuaFormatByPathPoint()
{
// pathPoint = {
// npc10001 = { { 1,0,1},{ 2,0,2} },
// npc10002 = { { 3,0,0},{ 4,0,4} },
// },
var allPathPoint = GetAllPathPoint();
StringBuilder sb = new StringBuilder();
sb.AppendLine(" pathPoint = {");
StringBuilder sbTemp = new StringBuilder();
foreach (var item in allPathPoint)
{
sbTemp.Remove(0, sbTemp.Length);
var pathPoints = item.Value;
for (int i = 0; i < pathPoints.Count; i++)
{
sbTemp.Append(string.Format(i == pathPoints.Count - 1 ? "{{{0},{1},{2}}}" : "{{{0},{1},{2}}},",
pathPoints[i].x < 0.001f ? 0 : pathPoints[i].x, pathPoints[i].y < 0.001f ? 0 : pathPoints[i].y, pathPoints[i].z < 0.001f ? 0 : pathPoints[i].z));
}
sb.AppendLine(string.Format(" {0} = {{{1}}},", item.Key, sbTemp.ToString()));
}
sb.AppendLine(" },");
return sb.ToString();
}
/// <summary>
/// 获取所有触发器盒子
/// </summary>
/// <returns></returns>
public Dictionary<string, Transform> GetAllTriggerBox()
{
CheckNode(TriggerBoxPath);
var parentTf = transform.Find(TriggerBoxPath);
if (parentTf == null)
return triggerBoxDic;
triggerBoxDic.Clear();
int count = parentTf.childCount;
if (count > 0)
{
for (int i = 0; i < count; i++)
{
var child = parentTf.GetChild(i);
var name = child.gameObject.name;
Transform tf;
if (!triggerBoxDic.TryGetValue(name, out tf))
{
triggerBoxDic.Add(name, child);
}
else
{
Debug.LogError("故事:" + gameObject.name + ",触发器盒子有相同名字:" + name);
}
}
}
return triggerBoxDic;
}
//获取所有触发盒子 lua格式
public string GetLuaFormatByTriggerBox()
{
// triggerBox = {
// cub_001 = {"Box",{0, 0, 0}, {1, 1, 1}},
// sphere_001 = {"Sphere",{0, 0, 0}, 3},
// },
var allTriggerBox = GetAllTriggerBox();
StringBuilder sb = new StringBuilder();
sb.AppendLine(" triggerBox = {");
StringBuilder sbTemp = new StringBuilder();
foreach (var item in allTriggerBox)
{
sbTemp.Remove(0, sbTemp.Length);
var tf = item.Value;
var boxCollider = tf.gameObject.GetComponent<BoxCollider>();
if (boxCollider != null)
{
var centerPos = tf.position;
var scale = tf.localScale * 0.5f;
sbTemp.Append(string.Format("{{\"{0}\",\"Box\", {{{1:N4}, {2:N4}, {3:N4}}}, {{{4:N4}, {5:N4}, {6:N4}}}}},", item.Key,
centerPos.x - scale.x, centerPos.y - scale.y, centerPos.z - scale.z, centerPos.x + scale.x, centerPos.y + scale.y, centerPos.z + scale.z));
}
else
{
var sphereCollider = tf.gameObject.GetComponent<SphereCollider>();
if (sphereCollider != null)
{
var centerPos = tf.position;
var radius = tf.localScale.x * 0.5f;
tf.localScale = new Vector3(radius * 2, radius * 2, radius * 2);
sbTemp.Append(string.Format("{{\"{0}\",\"Sphere\", {{{1:N4}, {2:N4}, {3:N4}}}, {4:N4}}},", item.Key, centerPos.x, centerPos.y, centerPos.z, radius));
}
}
sb.AppendLine(string.Format(" {0} = {1}", item.Key, sbTemp.ToString()));
}
sb.AppendLine(" },");
return sb.ToString();
}
/// <summary>
/// 获取所有3D文字
/// </summary>
/// <returns></returns>
public Dictionary<string, GameObject> GetAllText3D()
{
CheckNode(Text3DPath);
var parentTf = transform.Find(Text3DPath);
if (parentTf == null)
return Text3DDic;
var uiRoot = parentTf.GetComponent<UIRoot>();
//检查UIRoot组件
if (!uiRoot)
{
uiRoot = parentTf.gameObject.AddComponent<UIRoot>();
uiRoot.scalingStyle = UIRoot.Scaling.ConstrainedOnMobiles;
uiRoot.manualWidth = 1136;
uiRoot.manualHeight = 640;
uiRoot.fitWidth = true;
uiRoot.fitHeight = true;
}
var uiPanel = parentTf.GetComponent<UIPanel>();
//检查UIPanel组件
if (!uiPanel)
{
uiPanel = parentTf.gameObject.AddComponent<UIPanel>();
}
Text3DDic.Clear();
int count = parentTf.childCount;
if (count > 0)
{
for (int i = 0; i < count; i++)
{
var child = parentTf.GetChild(i).gameObject;
var name = child.name;
GameObject gobjOut;
if (!Text3DDic.TryGetValue(name, out gobjOut))
{
Text3DDic.Add(name, child);
}
else
{
Debug.LogError("故事:" + gameObject.name + ",3D文字有相同名字:" + name);
}
}
}
return Text3DDic;
}
//获取所有3D文字 lua格式
public string GetLuaFormatByText3D()
{
// triggerBox = {
// Text001 = {"Text001",{0, 0, 0}, {0, 0, 0},{"内容",18, 200, 26}},
// },
// {"名字",{posX, posY , posZ}, {rotaX, rotaY, rotaZ}, {"内容", foneSize, sizeWidth, sizeHeight}},
var allText3D = GetAllText3D();
StringBuilder sb = new StringBuilder();
sb.AppendLine(" Text3D = {");
StringBuilder sbTemp = new StringBuilder();
foreach (var item in allText3D)
{
sbTemp.Remove(0, sbTemp.Length);
var gobj = item.Value;
var tf = gobj.transform;
var pos = tf.position;
var eulerAngles = tf.eulerAngles;
var uiLabel = gobj.GetComponent<UILabel>();
sbTemp.Append(string.Format("{{\"{0}\", {{{1}, {2}, {3}}}, {{{4}, {5}, {6}}}, {{\"{7}\", {8}, {9}, {10}}}}},",
item.Key, pos.x, pos.y, pos.z, eulerAngles.x, eulerAngles.y, eulerAngles.z, uiLabel.text, uiLabel.fontSize, uiLabel.width, uiLabel.height));
sb.AppendLine(string.Format(" {0} = {1}", item.Key, sbTemp.ToString()));
}
sb.AppendLine(" },");
return sb.ToString();
}
//获取所有触发器
public List<StoryTrigger> GetAllTriggers()
{
storyTriggers.Clear();
var triggersTf = transform.Find(TriggersPath);
var count = triggersTf.childCount;
if (count > 0)
{
for (int i = 0; i < count; i++)
{
var script = triggersTf.GetChild(i).GetComponent<StoryTrigger>();
if (script != null)
{
storyTriggers.Add(script);
}
}
}
return storyTriggers;
}
//添加触发器
public StoryTrigger AddTrigger(string name, int index)
{
var triggersTf = transform.Find(TriggersPath);
var go = new GameObject(name);
go.transform.parent = triggersTf;
if (index != -1)
{
go.transform.SetSiblingIndex(index);
}
var storyTrigger = go.AddComponent<StoryTrigger>();
return storyTrigger;
}
//移除触发器
public void RemoveTrigger(int index)
{
var triggersTf = transform.Find(TriggersPath);
var go = triggersTf.GetChild(index).gameObject;
DestroyImmediate(go);
}
//获取所有触发器 lua格式
public string GetLuaFormatByTrigger()
{
var allTrigger = GetAllTriggers();
StringBuilder sb = new StringBuilder();
sb.AppendLine(" triggers = {");
var count = storyTriggers.Count;
for (int i = 0; i < count; i++)
{
var storyTrigger = storyTriggers[i];
var actions = storyTrigger.actions;
//若没有动作或动作都是None不会导出该触发器
if (actions.Count == 0)
continue;
var isNotEmpty = false;
for (int j = 0; j < actions.Count; j++)
{
isNotEmpty = isNotEmpty || actions[j].actionType != ActionType.None;
}
if (!isNotEmpty)
continue;
//触发条件
var triggerCondition = storyTrigger.triggerCondition;
var triggerConditionParames = storyTrigger.triggerConditionParames;
var triggerConditionStr = GetTriggerConditionStr(triggerCondition, triggerConditionParames);
//限制条件
var limitCondition = storyTrigger.limitCondition;
var limitConditionStr = GetLimitConditionStr(limitCondition, 0);
var actionsStr = GetActionStr(actions);
//动作
sb.AppendLine(" {");
sb.AppendLine(" ID = " + storyTrigger.id + ",");
sb.AppendLine(" isActive = true,");
sb.AppendLine(" triggerCondition = " + triggerConditionStr);
sb.AppendLine(" limitCondition = " + limitConditionStr);
sb.AppendLine(" action = " + actionsStr);
sb.AppendLine(" },");
}
sb.AppendLine(" }");
return sb.ToString();
}
//获取触发条件string lua格式
public string GetTriggerConditionStr(TriggerConditionType triggerConditionType, List<string> parames)
{
string result = "{";
switch (triggerConditionType)
{
case TriggerConditionType.Init: result += "\"init\""; break;
case TriggerConditionType.EnterMap: result += parames[0] == "" ? "\"enterMap\"" : "\"enterMap\"," + parames[0]; break;
case TriggerConditionType.TalkOver: result += parames[0] == "" ? "\"talkOver\"" : "\"talkOver\"," + parames[0]; break;
case TriggerConditionType.InteractionOver: result += parames[0] == "" ? "\"interactionOver\"" : "\"interactionOver\"," + parames[0]; break;
case TriggerConditionType.TimeLineOver: result += parames[0] == "" ? "\"timeLineOver\"" : string.Format("\"timeLineOver\",\"{0}\"", parames[0]); break;
case TriggerConditionType.Variable: result += parames[0] == "" ? "\"variable\"" : "\"variable\"," + parames[0] + ", " + parames[1] + ", " + parames[2]; break;
case TriggerConditionType.EnterTriggerArea: result += parames[0] == "None" ? "\"enterTriggerArea\"" : string.Format("\"enterTriggerArea\",\"{0}\"", parames[0]); break;
default: Debug.LogError("未添加该类型Key"); break;
}
result += "},";
return result;
}
//获取限制条件string lua格式
public string GetLimitConditionStr(LimitCondition limitConditionBase, int layer)
{
if (limitConditionBase.GetType() == typeof(LimitCondition))
{
var limitCondition = limitConditionBase as LimitCondition;
var limitConditionType = limitCondition.curLimitConditionType;
var parames = limitCondition.curLimitCoiditionParames;
var limitConditionChilds = limitCondition.limitConditionChilds;
var count = limitConditionChilds.Count;
string result = layer == 0 ? "" : GetPreStr(layer);
switch (limitConditionType)
{
case LimitConditionType.None: result += string.Format("{{\"{0}\"}},", "none"); break;
case LimitConditionType.EnterMap: result += string.Format("{{\"{0}\",{1}}},", "enterMap", parames[0]); break;
case LimitConditionType.TalkOver: result += string.Format("{{\"{0}\",{1}}},", "talkOver", parames[0]); break;
case LimitConditionType.InteractionOver: result += string.Format("{{\"{0}\",{1}}},", "interactionOver", parames[0]); break;
case LimitConditionType.TimeLineOver: result += string.Format("{{\"{0}\",\"{1}\"}},", "timeLineOver", parames[0]); break;
case LimitConditionType.Variable: result += string.Format("{{\"{0}\",\"{1}\",\"{2}\",{3}}},", "variable", parames[0], parames[1], parames[2]); break;
case LimitConditionType.EnterTriggerArea: result += string.Format("{{\"{0}\",\"{1}\"}},", "enterTriggerArea", parames[0]); break;
case LimitConditionType.And:
result += string.Format("{{\"{0}\",\n", "and");
for (int i = 0; i < count; i++)
{
result += GetLimitConditionStr(limitConditionChilds[i], layer + 1) + "\n";
}
result += GetPreStr(layer) + "},";
break;
case LimitConditionType.Or:
result += string.Format("{{\"{0}\",\n", "or");
for (int i = 0; i < count; i++)
{
result += GetLimitConditionStr(limitConditionChilds[i], layer + 1) + "\n";
}
result += GetPreStr(layer) + "},";
break;
default: Debug.LogError("未添加该类型Key"); break;
}
return result;
}
return "{},";
}
//Log,//日志 1
//SetTriggerActive,//设置触发器开关 2
//CreatNpc,//创建NPC 2
//CreatInteractor,//创建交互物 2
//SetVariable,//设置变量 2
//RemoveEntity,//移除物体 1
//SetNPCAI,//设置NPC的AI 2
//PlayTimeline,//播放Timeline 1
//LoadTimeline,//加载Timeline 1
//StartInteract,//开始交互 1
//SetPlayerTransform,//设置玩家位置和旋转 1
//SetPlayerScale,//设置玩家缩放 1
//OpenFromStoryEnd,//打开故事结束界面 1
//ChangeInteractState,//设置交互物是否可交互 2
//ShowTextTips,//显示文字提示 1
//ShowCalendar,//显示日历 2
public string GetActionStr(List<TriggerAction> triggerActions)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("{");
var count = triggerActions.Count;
if (count > 0)
{
for (int i = 0; i < count; i++)
{
var actionType = triggerActions[i].actionType;
var parames = triggerActions[i].actionParames;
string result = "";
switch (actionType)
{
case ActionType.Log:
result += string.Format("{{\"{0}\", \"{1}\"}}", "log", parames[0]);
break;
case ActionType.SetTriggerActive:
result += string.Format("{{\"{0}\", {1}, {2}}}", "setTriggerActive", parames[0], parames[1]);
break;
case ActionType.CreatNpc:
result += string.Format("{{\"{0}\", {1}, \"{2}\"}}", "creatNpc", parames[0], parames[1]);
break;
case ActionType.CreatInteractor:
result += string.Format("{{\"{0}\", {1}, \"{2}\"}}", "creatInteractor", parames[0], parames[1]);
break;
case ActionType.SetVariable:
result += string.Format("{{\"{0}\", \"{1}\", {2}}}", "setVariable", parames[0], parames[1]);
break;
case ActionType.RemoveEntity:
result += string.Format("{{\"{0}\", {1}}}", "removeEntity", parames[0]);
break;
case ActionType.SetNPCAI:
result += string.Format("{{\"{0}\", {1}, {2}}}", "setNPCAI", parames[0], parames[1]);
break;
case ActionType.PlayTimeline:
result += string.Format("{{\"{0}\", \"{1}\"}}", "playTimeline", parames[0]);
break;
case ActionType.LoadTimeline:
result += string.Format("{{\"{0}\", \"{1}\"}}", "loadTimeline", parames[0]);
break;
case ActionType.StartInteract:
result += string.Format("{{\"{0}\", {1}}}", "startInteract", parames[0]);
break;
case ActionType.SetPlayerTransform:
result += string.Format("{{\"{0}\", \"{1}\"}}", "setPlayerTransform", parames[0]);
break;
case ActionType.SetPlayerScale:
result += string.Format("{{\"{0}\", {1}}}", "setPlayerScale", parames[0]);
break;
case ActionType.OpenFromStoryEnd:
result += string.Format("{{\"{0}\", \"{1}\"}}", "openFromStoryEnd", parames[0]);
break;
case ActionType.ChangeInteractState:
result += string.Format("{{\"{0}\", {1}, {2}}}", "changeInteractState", parames[0], parames[1]);
break;
case ActionType.ShowTextTips:
result += string.Format("{{\"{0}\", \"{1}\"}}", "showTextTips", parames[0]);
break;
case ActionType.ShowCalendar:
result += string.Format("{{\"{0}\", {1}, {2}}}", "showCalendar", parames[0], parames[1]);
break;
case ActionType.AddTriggerBox:
result += string.Format("{{\"{0}\", \"{1}\"}}", "addTriggerBox", parames[0]);
break;
case ActionType.RemoveTriggerBox:
result += string.Format("{{\"{0}\", \"{1}\"}}", "removeTriggerBox", parames[0]);
break;
case ActionType.ShowText3D:
result += string.Format("{{\"{0}\", \"{1}\"}}", "showText3D", parames[0]);
break;
case ActionType.HideText3D:
result += string.Format("{{\"{0}\", \"{1}\"}}", "hideText3D", parames[0]);
break;
case ActionType.ShowProgress:
result += string.Format("{{\"{0}\", \"{1}\"}}", "showProgress", parames[0]);
break;
default:
break;
}
sb.AppendLine(string.Format(GetPreStr(1) + "{0},", result));
}
}
sb.Append(GetPreStr(0) + "},");
return sb.ToString();
}
public string GetPreStr(int layer)
{
string temp = "";
for (int i = -3; i < layer; i++)
{
temp += " ";
}
return temp;
}
}