public void pressButton(ref bool deleteMode, int deleteCount)
{
Debug.Log("Button Pressed");
if (isAnimating) return; // 이미 애니메이션 중이면 실행 안 함
isAnimating = true; // 애니메이션 시작
// 아래로 이동 후 다시 원위치로 복귀 (Sequence 사용)
Sequence sequence = DOTween.Sequence()
.Append(transform.DOMoveY(originalPos.y - bounce_amount, press_duration).SetEase(Ease.InOutQuad)) // 아래로 이동
.Append(transform.DOMoveY(originalPos.y, press_duration).SetEase(Ease.InOutQuad)) // 원위치 복귀
.OnComplete(() => isAnimating = false); // 애니메이션 종료 후 isAnimating을 false로 변경
sequence.Play();
if (deleteCount <= 0)
{
terminalText.SetTemporalTerminalText("You can't delete anymore.");
return;
}
deleteMode = !deleteMode;
if (deleteMode)
terminalText.SetTerminalText("Delete mode enabled");
else
terminalText.BackToOriginalText();
}
버튼 눌렀는데 delete count가 0 이하면 delete mode 활성화 안 해주고
더 못 지운다는 출력 문구 잠시 띄움
public void BackToOriginalTextWith(string context)
{
SetTerminalText(context + "\n" + originalText);
}
public void BackToOriginalText()
{
SetTerminalText(originalText);
}
public void SetTerminalText(string text)
{
if (typingCoroutine != null) StopCoroutine(typingCoroutine);
terminalText.text = text;
typingCoroutine = StartCoroutine(TypeText());
}
public void SetTemporalTerminalText(string text)
{
if (typingCoroutine != null) StopCoroutine(typingCoroutine);
terminalText.text = text;
typingCoroutine = StartCoroutine(TypeText(true));
}
IEnumerator TypeText(bool isTemporal = false)
{
terminalText.ForceMeshUpdate();
TMP_TextInfo textInfo = terminalText.textInfo;
int totalCharacters = textInfo.characterCount;
terminalText.maxVisibleCharacters = 0;
for (int i = 0; i < totalCharacters; i++)
{
terminalText.maxVisibleCharacters = i + 1;
yield return new WaitForSeconds(typingSpeed);
}
if (isTemporal)
{
yield return new WaitForSeconds(1.5f);
typingCoroutine = null;
SetTerminalText(originalText);
}
else
{
typingCoroutine = null;
}
}
임시 출력 로직 때문에 코드가 살짝 읽기 어려워졌다
/// <summary>
/// 맨 처음 게임 시작할 때 호출되는 함수
/// </summary>
public void StartGame()
{
currentState = playerTurnState;
terminalText.BackToOriginalText();
board.SetBoard();
}
/// <summary>
/// 다음 스테이지로 넘어가는 함수
/// </summary>
public void NextStage()
{
spotLight.enabled = false;
currentState = waitingState;
terminalText.SetTerminalText("Stage Clear!");
stage++;
// if (stage > 3)
// {
// 게임 엔딩 시퀀스 시작
// return;
// }
Invoke("ResetGame", 3f);
}
/// <summary>
/// 게임 오버 후에 restart 눌렀을 때 호출
/// </summary>
public void RestartGame()
{
ResetGame();
gameoverCanvas.gameObject.SetActive(false);
}
/// <summary>
/// 새 게임을 시작하기 위해 정보를 초기화하는 함수
/// </summary>
public void ResetGame()
{
whiteTurn = true;
currentState = playerTurnState;
spotLight.enabled = true;
board.ResetBoard();
inputHandler.ResetInputHandler(stage);
terminalText.BackToOriginalTextWith("Stage " + stage);
}
/// <summary>
/// 종 치거나 게임 지면 호출되는 함수
/// </summary>
public void GameOver()
{
shooter.PlayShooterFootstep();
}
각 시나리오에 맞게 터미널 출력
public class IntroManager : MonoBehaviour
{
private GameManager gameManager;
private TerminalText terminalText;
private CinemachineCamera current_vcam;
private bool isWatchingMonitor = false;
private float vcam_trans_time;
[SerializeField] private CinemachineBrain brain;
[SerializeField] private CinemachineCamera player_vacm;
[SerializeField] private CinemachineCamera monitor_vcam;
[SerializeField] private string[] introTexts; // bool이 true면 기존 텍스트와 함께 출력
[SerializeField] private bool[] introTextsType; // true면 기존 텍스트와 함께 출력
private int textIdx = 0;
private void Start()
{
GameObject serviceLocator = GameObject.FindGameObjectWithTag("ServiceLocator");
gameManager = serviceLocator.GetComponentInChildren<GameManager>();
terminalText = serviceLocator.GetComponentInChildren<TerminalText>();
vcam_trans_time = brain.DefaultBlend.BlendTime;
StartIntro();
}
void Update()
{
if (isWatchingMonitor && Input.GetKeyDown(KeyCode.Return))
{
NextTerminalTexts();
}
}
private void NextTerminalTexts()
{
if (textIdx == introTexts.Length)
{
isWatchingMonitor = false;
ChangeVcam(player_vacm);
gameManager.StartGame();
Destroy(this);
return;
}
if (introTextsType[textIdx])
terminalText.AddTerminalText(introTexts[textIdx]);
else
terminalText.SetTerminalText(introTexts[textIdx]);
textIdx++;
}
private void StartIntro()
{
// 천이 벗겨짐, 카메라가 모니터 앞으로 감, 모니터에 대사 출력, 엔터 누르면 다음 대사, 마지막 대사 뜬 후에 카메라가 돌아오면 게임 시작돼있음.
Sequence seq = DOTween.Sequence();
seq.AppendCallback(() => ChangeVcam(monitor_vcam));
seq.AppendInterval(vcam_trans_time);
seq.AppendCallback(() =>
{
isWatchingMonitor = true;
NextTerminalTexts();
});
}
private void ChangeVcam(CinemachineCamera vcam)
{
if (current_vcam != null)
current_vcam.Priority = 0;
vcam.Priority = 10;
current_vcam = vcam;
}
}
타임라인으로 하기엔 잠시 멈췄다가 플레이어의 입력에 맞추어 실행해야 하는 부분들이 있어서
전부 스크립트로 제어.
Unity 6여서 약간 달랐던 점
introTextsType[]으로 같은 인덱스를 가진 introTexts[]의 텍스트가
이전 줄에 이어서 출력될지 새로 출력될지 결정
/// <summary>
/// 터미널에 출력되고 있던 텍스트 아래에 텍스트를 추가한다.
/// </summary>
public void AddTerminalText(string text)
{
if (typingCoroutine != null) StopCoroutine(typingCoroutine);
int preLen = terminalText.text.Length;
terminalText.text += "\n" + text;
typingCoroutine = StartCoroutine(AddText(preLen));
}
/// <summary>
/// 텍스트 추가 코루틴
/// </summary>
IEnumerator AddText(int preLen)
{
terminalText.ForceMeshUpdate();
TMP_TextInfo textInfo = terminalText.textInfo;
int totalCharacters = textInfo.characterCount;
terminalText.maxVisibleCharacters = preLen;
for (int i = preLen; i < totalCharacters; i++)
{
terminalText.maxVisibleCharacters = i + 1;
yield return new WaitForSeconds(typingSpeed);
}
typingCoroutine = null;
}
이전 줄에 이어서 출력하는 효과 메서드 및 코루틴.
코드 재사용하려고 하다간 코드가 더 복잡해질 것 같아서 AddText() 코루틴은 따로 작성.
