250208

lililllilillll·2025년 2월 8일

개발 일지

목록 보기
76/350

✅ 오늘 한 일


  • Project BCA


📝 배운 것들


🏷️ Unity : Coroutine

개념 및 원리

  • 설정한 만큼 멈췄다가 다시 실행
  • 멈췄던 위치에서 다시 실행됨
  • 긴 비동기 작업할 때 쓰면 됨
  • 멀티쓰레딩은 아니다

문제 및 해결

  • 디버깅 어렵다. 로그 넣어주자.
  • 다 쓰고 안 꺼주면 성능 문제.
  • 무거운 연산 쓰면 프레임 드랍. 프레임마다 쪼개서 실행하기.

끄는 법

  • StopCoroutine(), StopAllCoroutines()
  • 코루틴 붙은 오브젝트 SetActive(false), Destroy()
  • Monobehaviour 스크립트는 enabled false로 해도 코루틴 안 꺼짐

🏷️ Service Locator 패턴

<type,object> 인 static 딕셔너리 만들고 의존성 필요하면 그 딕셔너리에 등록하고 가져다쓰고 하는 패턴이다.
싱글톤 패턴보다 유연하긴 하지만, 여전히 의존성은 숨겨진다.
따라서 UI 매니저, 오디오 매니저 같이 전역적으로 공유해야 하는 것에는 서비스 로케이터를 써도 되지만,
플레이어, 몬스터 등 객체들이 서로 상호작용해야 할 땐 의존성 주입을 사용하는게 낫다.
(근데 작은 프로젝트면 그런거 신경 안 쓰고 편한거 써도 된다고 함)

public class ServiceLocator : MonoBehaviour
{
    private static Dictionary<Type, object> _services = new Dictionary<Type, object>();

    public static void Register<T>(T service)
    {
        _services[typeof(T)] = service;
    }

    public static T Get<T>()
    {
        return (T)_services[typeof(T)];
    }
}

사용 방법

public class GameManager : MonoBehaviour
{
    private void Awake()
    {
        ServiceLocator.Register(this); // 등록
    }
}

public class Player : MonoBehaviour
{
    private GameManager _gameManager;

    private void Start()
    {
        _gameManager = ServiceLocator.Get<GameManager>(); // 가져오기
    }
}


🎮 Project BCA


로그

using UnityEngine;
using System;
using System.IO;

public class LogManager : MonoBehaviour
{
    private string logFilePath;

    void Awake()
    {
        // 로그 파일 경로 설정
        logFilePath = Path.Combine(Application.persistentDataPath, "GameLog_" + DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss") + ".txt");

        // 기존 로그 파일이 있으면 삭제
        if (File.Exists(logFilePath))
        {
            File.Delete(logFilePath);
        }

        // Unity 로그 이벤트 핸들러 등록
        Application.logMessageReceived += HandleLog;
    }

    void OnDestroy()
    {
        // 핸들러 해제 (메모리 누수 방지)
        Application.logMessageReceived -= HandleLog;
    }

    private void HandleLog(string logString, string stackTrace, LogType type)
    {
        // 로그 메시지 생성
        string logEntry = $"{DateTime.Now:HH:mm:ss} [{type}] {logString}\n";
        if (type == LogType.Exception || type == LogType.Error)
        {
            logEntry += $"StackTrace:\n{stackTrace}\n";
        }

        // 로그 파일에 기록
        File.AppendAllText(logFilePath, logEntry);
    }
}

로그 파일 저장 경로:
Windows: C:\Users\사용자\AppData\LocalLow\회사명\게임명\GameLog_날짜.txt
Android: /storage/emulated/0/Android/data/패키지명/files/GameLog_날짜.txt
Mac: ~/Library/Application Support/회사명/게임명/GameLog_날짜.txt

리팩토링

현재는 GameManager.cs에서 거의 모든 로직이 돌아가고 있어 기능 추가할 때 힘들다.

보드 관련은 Board.cs로 빼고
입력 관련은 InputHandler.cs로 빼고 (정신력 이슈로 패스)
이동 검증은 MoveValidator.cs로 빼기

    void Awake()
    {
        GameObject serviceLocator = GameObject.FindGameObjectWithTag("ServiceLocator");
        board = serviceLocator.GetComponentInChildren<Board>();
        moveCalculator = serviceLocator.GetComponentInChildren<MoveCalculator>();
    }

코드 짜긴 귀찮아서 Service Locator 패턴을 응용하여 참조시켰다.

코드를 3개로 분리하는데 성공했다.
게임 매니저의 코드가 521 줄에서 235 줄로 줄어들었다.

리팩토링 하면서도 과연 정상 작동 할 수 있을지 의문이었는데,
다행히 디버깅을 좀 거치고 나니 멀쩡하게 돌아가주었다.

다시는 이런 미친짓을 하지 않도록 진작에 SRP 지키자.

AI Integration

  • input.Flush() 호출 이유 : StreamWriter가 명령어를 바로 안 보내고 버퍼에 쌓아뒀다가 보낼 수 있기 때문.
  • string response = output.ReadLine();을 아무 방어 코드 없이 넣어버리면 입력 올 때까지 기다려서 게임이 멈춰버린다.


profile
너 정말 **핵심**을 찔렀어

0개의 댓글