static void TextClear(FSNScriptSequence.Parser.ICommandGenerateProtocol protocol) { var newseg = new Segments.Text(); newseg.textType = Segments.Text.TextType.Clear; var newSegInfo = new FSNScriptSequence.Parser.GeneratedSegmentInfo(); newSegInfo.newSeg = newseg; newSegInfo.usePrevPeriod = true; newSegInfo.selfPeriod = false; protocol.PushSegment(newSegInfo); }
private void AddLastOptionText(FSNSnapshot.Layer layer, Segments.Text textSeg, IInGameSetting setting) { // TODO : 상하좌우 여백, 정렬 등도 따져야함 // NOTE : 선택한 방향은 setting 쪽에 설정되어 오는 것으로... SnapshotElems.Text optionText = null; foreach (var elem in layer.Elements) // * 이전 레이어를 전부 뒤져서 진행 방향이 같은 선택지 텍스트를 찾아온다 { var textElem = elem as SnapshotElems.Text; if (textElem.type == SnapshotElems.Text.Type.OptionTexts && textElem.optionDir == setting.CurrentFlowDirection) { optionText = textElem; break; } } if (optionText != null) // * 찾은 경우에 한해서... { // 텍스트를 새로 만드는 것이 아니라 기존 것을 변경한다. // PushTextsToDirection 에서는 이 시점의 LastOption텍스트는 건들지 않는다. var textSize = CalculateTextSize(optionText.text, setting); var posToCenter = optionText.Position; TextPositionToCenter(ref posToCenter, textSize, optionText.optionDir, setting); // 중앙 위치 맞추기 optionText.Position = posToCenter; optionText.type = SnapshotElems.Text.Type.LastOption; // LastOption 타입으로 변경 CopyCurrentToFinal(optionText); // 임시 FinalState PushTextsToDirection(layer, setting.CurrentFlowDirection, Vector2.zero); // 기존 텍스트 일괄적으로 해당 방향으로 밀기 (내부 조건체크에 따라 LastOption은 이 타이밍에는 제외된다) } else { Debug.LogError("cannot find option text to direction : " + setting.CurrentFlowDirection.ToString()); } }
/// <summary> /// 문자열으로 스크립트 파싱 /// </summary> /// <param name="scriptData"></param> /// <returns></returns> public static FSNScriptSequence FromString(string scriptData, FSNSession session) { // 디버깅 세션 세팅 FSNDebug.currentRuntimeStage = FSNDebug.RuntimeStage.Compile; var sequence = new FSNScriptSequence(); sequence.OriginalScriptPath = "(string)"; sequence.ScriptHashKey = GenerateHashKeyFromScript(scriptData); // 해시키 생성해두기 (세이브 파일과 스크립트 파일 버전 체크용) // ===== FIRST PASS : 헤더 파일 먼저 해석 ================================== ProcessHeaders(scriptData, sequence, session); // ===== SECOND PASS : 나머지 스크립트 요소들 해석 ========================= var strstream = new System.IO.StringReader(scriptData); // 스크립트 해석 상태값들 CommandGenerateProtocol protocol = new CommandGenerateProtocol(); // flags Segments.Period periodSeg = null; // Period 세그먼트. 먼저 만들어놓고 있다가 적당한 때에 삽입한다. (스크립트와 실제 세그먼트 순서가 다르기 때문) bool textMultilineMode = false; // 텍스트 여러줄 처리중인지 (//) string multilineText = ""; // 멀티라인 모드에서, 텍스트 처리중일 때 // // ** 스크립트 로드 후 첫번째 스냅샷에서 다시 이전으로 돌아가는 것은 불가능하므로, 맨 처음에 oneway 컨트롤 세그먼트를 추가해준다 var onewayAtFirstSeg = new Segments.Control(); onewayAtFirstSeg.controlType = Segments.Control.ControlType.Oneway; var onewaySegInfo = new FSNScriptSequence.Parser.GeneratedSegmentInfo() { newSeg = onewayAtFirstSeg }; protocol.PushSegment(onewaySegInfo); // string line = null; int linenumber = 0; // 줄 번호 while ((line = strstream.ReadLine()) != null) // 줄 단위로 읽는다. { line = sequence.Header.Macros.Replace(line); // 정적 매크로 치환 linenumber++; FSNDebug.currentProcessingScriptLine = linenumber; // 디버깅 정보 설정 if (!textMultilineMode && line.Length == 0) // * 빈 줄은 스루. 단 여러줄 텍스트 모드일 경우 빈 줄에서는 여러줄 모드를 끝내게 한다. { continue; } if (line.EndsWith(c_token_LineConcat)) // * 여러줄 텍스트로 지정된 경우, 자동으로 여러줄 모드로. 해당 라인 붙이기 { textMultilineMode = true; if (multilineText.Length > 0) // 이미 쌓여있는 텍스트가 있다면 공백 추가 { multilineText += "\n"; } multilineText += line.Substring(0, line.Length - c_token_LineConcat.Length); } else { var pretoken = line.Length > 0? line.Substring(0, 1) : ""; switch (pretoken) // 앞쪽 토큰으로 명령 구분 { case c_token_Comment: // * 주석 // 스루. 뭐 왜 뭐 주석인데 뭐 break; case c_token_PreProcessor: // * 전처리 구문 // 프리프로세서는 첫번째 패스에서 처리함. break; case c_token_Command: // * 명령 { var commandAndParam = line.Substring(1).Split(c_whiteSpaceArray, 2); // 명령어 파라미터 구분 var command = commandAndParam[0]; var paramStr = commandAndParam.Length > 1? commandAndParam[1] : ""; CommandSegmentGenerateFunc genfunc = null; s_aliasToSegFunction.TryGetValue(command, out genfunc); if (genfunc == null) // 등록된 명령어인지 체크 { Debug.LogError("Unknown command : " + command); } else { protocol.parameters = ParseParameters(paramStr); genfunc(protocol); } } break; case c_token_HardLabel: // * hard label { var labelSeg = new Segments.Label(); labelSeg.labelName = line.Substring(1); labelSeg.labelType = Segments.Label.LabelType.Hard; var segInfo = new GeneratedSegmentInfo(); segInfo.newSeg = labelSeg; segInfo.usePrevPeriod = true; // Label 전에 period로 다 출력해야함 segInfo.selfPeriod = false; protocol.PushSegment(segInfo); } break; case c_token_SoftLabel: // * soft label { var labelSeg = new Segments.Label(); labelSeg.labelName = line.Substring(1); labelSeg.labelType = Segments.Label.LabelType.Soft; var segInfo = new GeneratedSegmentInfo(); segInfo.newSeg = labelSeg; segInfo.usePrevPeriod = true; // Label 전에 period로 다 출력해야함 segInfo.selfPeriod = false; protocol.PushSegment(segInfo); } break; case c_token_Period: // * period if (line.Length == 1) // . 한글자일 때 - 일반 period { if (periodSeg != null) // * 이미 period 명령어가 대기중일 때, 기존 명령어를 먼저 처리한다 { sequence.m_segments.Add(periodSeg); } periodSeg = new Segments.Period(); } else if (line.Length == 2 && line[1].ToString() == c_token_Period) // .. 으로 두 글자일 때 - 연결 period, 만약 period가 이전에 등장했다면 연결으로 변경 { Segments.Period lastPeriodseg; if (periodSeg != null) // 처리 안된 period가 있을 경우, 이것을 chaining으로 변경해준다 { lastPeriodseg = periodSeg; } else if ((lastPeriodseg = sequence.m_segments[sequence.m_segments.Count - 1] as Segments.Period) .type == Segment.Type.Period) // 아닐 경우, 가장 마지막으로 추가된 세그먼트가 period를 chaining으로 변경한다 { // } else { // 그도 아닐 경우 새로 period 생성 periodSeg = new Segments.Period(); lastPeriodseg = periodSeg; } lastPeriodseg.isChaining = true; // 선택한 period에 chaining속성 부여 } else { Debug.LogError("invalid command... is it a period command?"); } break; case c_token_ForceText: default: // * 아무 토큰도 없음 : 텍스트 { if (line.Length > 0 && line[0].ToString() == c_token_ForceText) // 만약 강제 텍스트 토큰 (~) 이 붙어있었다면, 해당 토큰 제거 { line = line.Substring(1); } multilineText += multilineText.Length > 0? "\n" + line : line; var textSeg = new Segments.Text(); textSeg.text = multilineText; textSeg.textType = Segments.Text.TextType.Normal; multilineText = ""; // 멀티라인 텍스트 보관되어있던 것을 초기화 textMultilineMode = false; var segInfo = new GeneratedSegmentInfo(); segInfo.newSeg = textSeg; segInfo.usePrevPeriod = true; // 출력 명령어임 segInfo.selfPeriod = true; // 스스로 period를 포함함 protocol.PushSegment(segInfo); } break; } GeneratedSegmentInfo newSegInfo = null; while ((newSegInfo = protocol.PullSegment()) != null) // 새로 생성된 시퀀스 모두 처리 { if (newSegInfo.usePrevPeriod && periodSeg != null) // * 선행 period를 먼저 처리해야하는 상황 { periodSeg.scriptLineNumber = linenumber; // 줄번호 기록? sequence.m_segments.Add(periodSeg); periodSeg = null; } newSegInfo.newSeg.scriptLineNumber = linenumber; // 줄번호 기록 sequence.m_segments.Add(newSegInfo.newSeg); // 시퀀스 추가 if (newSegInfo.newSeg.type == Segment.Type.Label) // 라벨일 경우 등록 { sequence.RegisterLabelSegment(); } if (newSegInfo.selfPeriod) // * 방금 추가된 세그먼트가 period를 포함하는 개념이라면, period 대기시켜놓기 { periodSeg = new Segments.Period(); } } } } if (periodSeg != null) // 끝날 때까지 처리되지 않은 period가 있다면 여기서 추가해준다 { periodSeg.scriptLineNumber = linenumber; // 줄번호 기록? sequence.m_segments.Add(periodSeg); periodSeg = null; } // 디버깅 세션 세팅 FSNDebug.currentRuntimeStage = FSNDebug.RuntimeStage.Runtime; return(sequence); }
/// <summary> /// 일반 선택지 표시 /// </summary> /// <param name="protocol"></param> static void Option_end(FSNScriptSequence.Parser.ICommandGenerateProtocol protocol) { var optionData = protocol.GetStateVar(c_key_optionData) as string[][]; if (optionData == null) { Debug.LogError("You can't make options without starting an option sequence."); } else { var newOptionTextSeg = new Segments.Text(); newOptionTextSeg.text = protocol.GetStateVar(c_key_optionTitle) as string; newOptionTextSeg.textType = Segments.Text.TextType.Options; // 선택지 선택 후 해당 선택지를 잠깐 보여주기 위해서, // 가상 Label을 추가한 뒤 LastOption 텍스트 출력, 이후 원래 Label로 점프하는 추가 시퀀스를 만든다. newOptionTextSeg.optionTexts = new string[4]; var optionTransitionLabels = new string[4]; // 트랜지션용 임시 라벨 목록 for (int i = 0; i < 4; i++) { var option = optionData[i]; if (option != null) { optionTransitionLabels[i] = option[0] + "__transition"; newOptionTextSeg.optionTexts[i] = option[1]; } } // 선택지 텍스트 세그먼트 푸시 var newOptionTextSegInfo = new FSNScriptSequence.Parser.GeneratedSegmentInfo() { newSeg = newOptionTextSeg, selfPeriod = false, usePrevPeriod = true, }; protocol.PushSegment(newOptionTextSegInfo); // 임시 레이블로 점프하는 선택지 점프 세그먼트 var userChoiceSeg = new Segments.Control(); userChoiceSeg.controlType = Segments.Control.ControlType.SwipeOption; for (int i = 0; i < 4; i++) { userChoiceSeg.SetSwipeOptionData((FSNInGameSetting.FlowDirection)i, optionTransitionLabels[i]); } var userOptionControlSegInfo = new FSNScriptSequence.Parser.GeneratedSegmentInfo() { newSeg = userChoiceSeg, selfPeriod = false, usePrevPeriod = false, }; protocol.PushSegment(userOptionControlSegInfo); // period 세그먼트 (선택지 표시를 위해서) var periodSeg = new Segments.Period(); periodSeg.isChaining = false; var periodSegInfo = new FSNScriptSequence.Parser.GeneratedSegmentInfo() { newSeg = periodSeg }; protocol.PushSegment(periodSegInfo); // 처리 블록 세그먼트 var blockSeg = new Segments.Control(); blockSeg.controlType = Segments.Control.ControlType.Block; var blockControlSegInfo = new FSNScriptSequence.Parser.GeneratedSegmentInfo() { newSeg = blockSeg, selfPeriod = false, usePrevPeriod = false, }; protocol.PushSegment(blockControlSegInfo); // 각 임시 라벨에 해당하는 시퀀스 만들기 // for (int i = 0; i < 4; i++) { if (optionTransitionLabels[i] == null) // 라벨이 지정된 경우만 진행 { continue; } // 라벨 (soft 라벨 모드를 사용한다) var labelSeg = new Segments.Label(); labelSeg.labelName = optionTransitionLabels[i]; labelSeg.labelType = Segments.Label.LabelType.Soft; var labelSegInfo = new FSNScriptSequence.Parser.GeneratedSegmentInfo() { newSeg = labelSeg, }; protocol.PushSegment(labelSegInfo); // LastOption 텍스트 var lastOptionSeg = new Segments.Text(); lastOptionSeg.textType = Segments.Text.TextType.LastOption; var lastOptionSegInfo = new FSNScriptSequence.Parser.GeneratedSegmentInfo() { newSeg = lastOptionSeg }; protocol.PushSegment(lastOptionSegInfo); // 원래 label로 점프 var gotoSeg = new Segments.Control(); gotoSeg.controlType = Segments.Control.ControlType.Goto; gotoSeg.SetGotoData(optionData[i][0]); var gotoSegInfo = new FSNScriptSequence.Parser.GeneratedSegmentInfo() { newSeg = gotoSeg }; protocol.PushSegment(gotoSegInfo); // Period (chaining을 사용한다) var chainPeriodSeg = new Segments.Period(); chainPeriodSeg.isChaining = true; var chainPeriodSegInfo = new FSNScriptSequence.Parser.GeneratedSegmentInfo() { newSeg = chainPeriodSeg }; protocol.PushSegment(chainPeriodSegInfo); // 블럭 추가 protocol.PushSegment(blockControlSegInfo); } } }
private void ShowOptionTexts(FSNSnapshot.Layer layer, Segments.Text textSeg, IInGameSetting setting) { // TODO : 상하좌우 여백, 정렬 등도 따져야함 Vector2 screenDim = FSNEngine.Instance.ScreenDimension; // (계산용) 화면 크기 Vector3 fadePosOffset; switch (setting.CurrentFlowDirection) // 흐름 방향에 따라 이동 오프셋 세팅 { case FSNInGameSetting.FlowDirection.Up: fadePosOffset = new Vector3(0, screenDim.y / 2); break; case FSNInGameSetting.FlowDirection.Down: fadePosOffset = new Vector3(0, -screenDim.y / 2); break; case FSNInGameSetting.FlowDirection.Right: fadePosOffset = new Vector3(screenDim.x / 2, 0); break; case FSNInGameSetting.FlowDirection.Left: fadePosOffset = new Vector3(-screenDim.x / 2, 0); break; default: throw new System.Exception("HUH???"); } // 새 텍스트 엘레먼트 세팅 : 선택지 질문 텍스트 var newTextSize = CalculateTextSize(textSeg.text, setting); var questionTextElem = new SnapshotElems.Text(setting); questionTextElem.text = textSeg.text; questionTextElem.fontSize = setting.FontSize; questionTextElem.Color = Color.white; //TODO questionTextElem.Alpha = 1; questionTextElem.TransitionTime = setting.TextTransitionTime; //TODO questionTextElem.type = SnapshotElems.Text.Type.OptionTexts; questionTextElem.MakeItUnique(); questionTextElem.InitialState.Alpha = 0; questionTextElem.FinalState.Alpha = 0; CopyCurrentToFinal(questionTextElem); // 임시 FinalState // 새 텍스트 엘레먼트 - 위치 세팅 (시작 위치만. 끝 위치는 프로세스 끝에 결정된다) var qtextPos = new Vector3(-newTextSize.x / 2f, newTextSize.y / 2f); ApplyCenterTextMargin(ref qtextPos, setting); // 여백 지정 questionTextElem.Position = qtextPos; questionTextElem.InitialState.Position = questionTextElem.Position - fadePosOffset; layer.AddElement(questionTextElem); // 텍스트 엘리멘트 추가 // int dirIndex; string dirText; // 선택지 : 위쪽 (등장 위치는 아래쪽) dirIndex = (int)FSNInGameSetting.FlowDirection.Up; dirText = textSeg.optionTexts[dirIndex]; if (textSeg.optionTexts.Length - 1 >= dirIndex && !string.IsNullOrEmpty(dirText)) { var upTextSize = CalculateTextSize(dirText, setting); var upTextElem = new SnapshotElems.Text(setting); upTextElem.text = dirText; upTextElem.fontSize = setting.FontSize; upTextElem.Color = Color.white; //TODO upTextElem.Alpha = 1; upTextElem.TransitionTime = setting.TextTransitionTime; //TODO upTextElem.type = SnapshotElems.Text.Type.OptionTexts; upTextElem.optionDir = (FSNInGameSetting.FlowDirection)dirIndex; upTextElem.MakeItUnique(); upTextElem.InitialState.Alpha = 0; upTextElem.FinalState.Alpha = 0; CopyCurrentToFinal(upTextElem); // 임시 FinalState // 새 텍스트 엘레먼트 - 위치 세팅 (시작 위치만. 끝 위치는 프로세스 끝에 결정된다) var tpos = new Vector3(upTextSize.x / 2f, -screenDim.y / 2f + upTextSize.y); ApplySideTextMargin(ref tpos, setting, FSNInGameSetting.FlowDirection.Up); upTextElem.Position = tpos; upTextElem.InitialState.Position = upTextElem.Position - fadePosOffset; layer.AddElement(upTextElem); // 텍스트 엘리멘트 추가 } // 선택지 : 아래쪽 (등장 위치는 위쪽) dirIndex = (int)FSNInGameSetting.FlowDirection.Down; dirText = textSeg.optionTexts[dirIndex]; if (textSeg.optionTexts.Length - 1 >= dirIndex && !string.IsNullOrEmpty(dirText)) { var downTextSize = CalculateTextSize(dirText, setting); var downTextElem = new SnapshotElems.Text(setting); downTextElem.text = dirText; downTextElem.fontSize = setting.FontSize; downTextElem.Color = Color.white; //TODO downTextElem.Alpha = 1; downTextElem.TransitionTime = setting.TextTransitionTime; //TODO downTextElem.type = SnapshotElems.Text.Type.OptionTexts; downTextElem.optionDir = (FSNInGameSetting.FlowDirection)dirIndex; downTextElem.MakeItUnique(); downTextElem.InitialState.Alpha = 0; downTextElem.FinalState.Alpha = 0; CopyCurrentToFinal(downTextElem); // 임시 FinalState // 새 텍스트 엘레먼트 - 위치 세팅 (시작 위치만. 끝 위치는 프로세스 끝에 결정된다) var tpos = new Vector3(downTextSize.x / 2f, screenDim.y / 2f); ApplySideTextMargin(ref tpos, setting, FSNInGameSetting.FlowDirection.Down); downTextElem.Position = tpos; downTextElem.InitialState.Position = downTextElem.Position - fadePosOffset; layer.AddElement(downTextElem); // 텍스트 엘리멘트 추가 } // 선택지 : 왼쪽 (등장 위치는 오른쪽) dirIndex = (int)FSNInGameSetting.FlowDirection.Left; dirText = textSeg.optionTexts[dirIndex]; if (textSeg.optionTexts.Length - 1 >= dirIndex && !string.IsNullOrEmpty(dirText)) { var leftTextSize = CalculateTextSize(dirText, setting); var leftTextElem = new SnapshotElems.Text(setting); leftTextElem.text = dirText; leftTextElem.fontSize = setting.FontSize; leftTextElem.Color = Color.white; //TODO leftTextElem.Alpha = 1; leftTextElem.TransitionTime = setting.TextTransitionTime; //TODO leftTextElem.type = SnapshotElems.Text.Type.OptionTexts; leftTextElem.optionDir = (FSNInGameSetting.FlowDirection)dirIndex; leftTextElem.MakeItUnique(); leftTextElem.InitialState.Alpha = 0; leftTextElem.FinalState.Alpha = 0; CopyCurrentToFinal(leftTextElem); // 임시 FinalState // 새 텍스트 엘레먼트 - 위치 세팅 (시작 위치만. 끝 위치는 프로세스 끝에 결정된다) var tpos = new Vector3(screenDim.x / 2f - leftTextSize.x, -leftTextSize.y * 3f); ApplySideTextMargin(ref tpos, setting, FSNInGameSetting.FlowDirection.Left); leftTextElem.Position = tpos; leftTextElem.InitialState.Position = leftTextElem.Position - fadePosOffset; layer.AddElement(leftTextElem); // 텍스트 엘리멘트 추가 } // 선택지 : 오른쪽 (등장 위치는 왼쪽) dirIndex = (int)FSNInGameSetting.FlowDirection.Right; dirText = textSeg.optionTexts[dirIndex]; if (textSeg.optionTexts.Length - 1 >= dirIndex && !string.IsNullOrEmpty(dirText)) { var rightTextSize = CalculateTextSize(dirText, setting); var rightTextElem = new SnapshotElems.Text(setting); rightTextElem.text = dirText; rightTextElem.fontSize = setting.FontSize; rightTextElem.Color = Color.white; //TODO rightTextElem.Alpha = 1; rightTextElem.TransitionTime = setting.TextTransitionTime; //TODO rightTextElem.type = SnapshotElems.Text.Type.OptionTexts; rightTextElem.optionDir = (FSNInGameSetting.FlowDirection)dirIndex; rightTextElem.MakeItUnique(); rightTextElem.InitialState.Alpha = 0; rightTextElem.FinalState.Alpha = 0; CopyCurrentToFinal(rightTextElem); // 임시 FinalState // 새 텍스트 엘레먼트 - 위치 세팅 (시작 위치만. 끝 위치는 프로세스 끝에 결정된다) var tpos = new Vector3(-screenDim.x / 2f, rightTextSize.y * 3f); ApplySideTextMargin(ref tpos, setting, FSNInGameSetting.FlowDirection.Right); rightTextElem.Position = tpos; rightTextElem.InitialState.Position = rightTextElem.Position - fadePosOffset; layer.AddElement(rightTextElem); // 텍스트 엘리멘트 추가 } }
private void AddNormalText(FSNSnapshot.Layer layer, Segments.Text textSeg, IInGameSetting setting) { // TODO : 상하좌우 여백, 정렬 등도 따져야함 var newTextSize = CalculateTextSize(textSeg.text, setting); // 텍스트 영역 크기 미리 구하기 // 새 텍스트 엘레먼트 세팅 var newTextElem = new SnapshotElems.Text(setting); newTextElem.text = textSeg.text; newTextElem.fontSize = setting.FontSize; newTextElem.Color = Color.white; //TODO newTextElem.Alpha = 1; newTextElem.TransitionTime = setting.TextTransitionTime; //TODO newTextElem.MakeItUnique(); newTextElem.InitialState.Alpha = 0; newTextElem.FinalState.Alpha = 0; // 새 텍스트 엘레먼트 - 위치 세팅 (시작 위치만. 끝 위치는 프로세스 끝에 결정된다) Vector2 screenDim = FSNEngine.Instance.ScreenDimension; // (계산용) 화면 크기 Vector3 fadeinpos; switch (setting.CurrentFlowDirection) // 흐름 방향에 따라 시작 위치를 지정해준다 { case FSNInGameSetting.FlowDirection.Up: fadeinpos = new Vector3(-screenDim.x / 2, -screenDim.y / 2); break; case FSNInGameSetting.FlowDirection.Down: fadeinpos = new Vector3(-screenDim.x / 2, screenDim.y / 2 + newTextSize.y); break; case FSNInGameSetting.FlowDirection.Right: fadeinpos = new Vector3(-screenDim.x / 2 - newTextSize.x, 0); break; case FSNInGameSetting.FlowDirection.Left: fadeinpos = new Vector3(screenDim.x / 2, 0); break; default: throw new System.Exception("HUH???"); } ApplySideTextMargin(ref fadeinpos, setting, setting.CurrentFlowDirection); // 여백 적용 newTextElem.Position = fadeinpos; // 나중에 일괄적으로 이동시킬 것이기 때문에 시작 위치랑 화면 밖 위치를 같게 설정한다 newTextElem.InitialState.Position = fadeinpos; if (!setting.StackTexts || setting.ScreenCenterText) // 텍스트를 쌓지 않는 경우, 기존 텍스트를 전부 Clear한다. // 가운데텍스트 모드일 경우에도 (텍스트 쌓기 여부와는 상관 없이) 기존 텍스트를 지운다. { ClearTextsToDirection(layer, setting.CurrentFlowDirection); } if (setting.ScreenCenterText) // * 가운데 텍스트일 경우, { layer.AddElement(newTextElem); // 텍스트 엘리멘트 추가 var posToCenter = newTextElem.Position; TextPositionToCenter(ref posToCenter, newTextSize, setting.CurrentFlowDirection, setting); // 텍스트 중앙으로 움직이기 newTextElem.Position = posToCenter; CopyCurrentToFinal(newTextElem); // 임시 FinalState } else { // * 일반 텍스트 layer.AddElement(newTextElem); // 텍스트 엘리멘트 추가 PushTextsToDirection(layer, setting.CurrentFlowDirection, newTextSize, setting.ParagraphSpacing); // 텍스트 일괄적으로 해당 방향으로 밀기 } }