Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

heewoung-lee/ImpossibleBossesPublicRepo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

101 Commits

Repository files navigation

1titleImage

📘 목차



🔍 프로젝트 소개

ImpossibleBosses Steam panel

📺 플레이 영상

ImpossibleBosses 플레이 영상


🔧 사용 기술

구분 기술명
엔진 유니티 6
네트워킹 Netcode for GameObjects, Unity Relay, Unity Lobby
음성 / 채팅 Unity Vivox
인증 / 보안 Steamworks.NET, Steam Web API Auth, Firebase HTTPS Functions
백엔드 / DB Firebase, Node.js
게임 데이터 Google Sheets CSV, Json
AI 행동 제어 Behavior Designer
비동기 / 유틸리티 UniTask
테스트 / 개발 도구 Unity Play Mode Scenarios, Hot Reload
프레임워크 Zenject / Extenject
인스펙터 커스터마이징 Odin Inspector


🗝 핵심 로직

💉 의존성 주입 (Zenject)

프로젝트 전반의 객체 의존성을 명확하게 관리하고, 씬과 오브젝트 생성 흐름에서 발생하는 결합도를 줄이기 위해 Zenject 기반 DI 구조를 사용했습니다.

주요 구현 방식:

  1. Installer를 통한 의존성 구성

    • ProjectContext, SceneContext, GameObjectContext 계층에 따라 필요한 의존성을 분리해 바인딩했습니다.
    • 매니저, 서비스, 팩토리, 씬 전용 객체의 생성 책임을 Installer에 모아 의존성 흐름을 추적하기 쉽게 구성했습니다.
  2. 명시적인 의존성 주입

    • 일반 C# 클래스는 생성자 주입을 사용하고, MonoBehaviour 계열 객체는 [Inject] 기반 초기화 메서드를 통해 의존성을 받도록 구성했습니다.
    • 이를 통해 객체 내부에서 직접 다른 시스템을 탐색하지 않고, 필요한 의존성을 외부에서 명확히 전달받도록 했습니다.
  3. Netcode for GameObjects와 Zenject 생성 흐름 연동

    • Netcode for GameObjects의 NetworkObject 생성 흐름과 Zenject의 객체 생성 흐름이 충돌하지 않도록 커스텀 팩토리 구조를 구현했습니다.
    • NgoZenjectFactory, NgoZenjectHandler, INetworkPrefabInstanceHandler를 활용해 네트워크 오브젝트 생성 시에도 의존성 주입이 적용되도록 구성했습니다.
    • 생성 가능한 프리팹은 IFactoryManager를 통해 관리하고, 리소스 로딩은 IResourcesServices 흐름과 연결되도록 구성했습니다.

효과:

  • 의존성 흐름 명확화: 어떤 객체가 어떤 서비스와 매니저에 의존하는지 Installer를 통해 확인할 수 있습니다.
  • 결합도 감소: 객체가 직접 다른 시스템을 탐색하지 않고 필요한 의존성을 주입받기 때문에 코드 변경 범위를 줄일 수 있습니다.
  • 네트워크 객체 생성 안정화: Netcode for GameObjects의 생성 규칙을 유지하면서도 Zenject 기반 초기화 흐름을 함께 적용할 수 있습니다.
  • 확장 및 교체 용이성: 인터페이스 기반 주입 구조를 통해 구현체 변경이나 기능 확장이 쉬워졌습니다.


🔐 로그인 (Steam + Firebase 연동)

Steam 계정 인증 티켓으로 계정을 확인하고, Firebase Cloud Functions와 Cloud Firestore를 통해 플레이어 프로필을 관리합니다.

  1. 로그인 화면 로드: 게임 시작 시 Steam 로그인 또는 닉네임 설정을 할 수 있는 화면이 표시됩니다.
  2. 최초 로그인 및 닉네임 설정 시:
    • 클라이언트는 Steamworks.NET을 통해 Steam Web API 인증 티켓을 발급받고, 티켓을 Firebase HTTPS Functions로 전달합니다.
    • Firebase Cloud Functions는 전달받은 티켓을 Steam Web API로 검증하며, 클라이언트가 보낸 계정 정보 대신 검증된 SteamID64만 신뢰합니다.
    • 닉네임 설정 시 Cloud Firestore의 profiles 컬렉션에서 중복 닉네임을 확인한 뒤, 사용 가능한 경우 SteamID64와 닉네임을 저장합니다.

닉네임 생성 과정
<닉네임 생성 과정>


  1. 로그인 시:
    • Steam 인증 티켓을 Firebase Cloud Functions의 getSteamProfile 엔드포인트로 전달합니다.
    • 서버는 Steam Web API 검증 결과로 얻은 SteamID64를 기준으로 Cloud Firestore에서 프로필을 조회합니다.
  2. 인증 결과에 따른 처리:
    • 성공: SteamID64와 닉네임이 게임 내에 임시 저장됩니다. 닉네임이 아직 없다면 닉네임 설정 화면으로 안내하고, 모든 정보가 확인되면 로비 화면으로 이동합니다.
    • 실패: Steam 미실행, Steam 인증 실패, 서버 오류, 네트워크 오류 등의 상황을 알림창으로 알립니다.

로그인 흐름
<Steam 로그인 및 닉네임 설정 처리 흐름도>



🏠 로비 (Unity Lobby + Vivox)

Unity Lobby를 기반으로 방 생성, 검색, 참가, 준비 상태 동기화를 처리하고, Vivox를 통해 로비 채팅을 제공합니다.


<로비 화면>


🧩 주요 구현 내용

  • 방 생성 및 참가

    • LobbyManager를 통해 공개/비공개 방 생성, 방 목록 조회, 비밀번호 방 참가 흐름을 관리했습니다.
    • 방 이름, 최대 인원, 비밀번호, 호스트 정보, Relay Join Code 등 게임 참가에 필요한 데이터를 Lobby 데이터로 저장했습니다.
  • 로비 상태 동기화

    • Lobby 이벤트 구독을 통해 플레이어 입장, 퇴장, 캐릭터 선택, 준비 상태 변경을 실시간으로 반영했습니다.
    • 호스트는 Lobby Heartbeat를 주기적으로 전송해 방이 자동으로 만료되지 않도록 유지했습니다.
  • 채팅 연동

    • VivoxManager를 통해 로비 채널 입장, 텍스트 메시지 송신, 채팅 수신 흐름을 처리했습니다.
    • 플레이어는 게임 시작 전 로비에서 파티원과 실시간으로 소통할 수 있습니다.
  • Relay 접속 정보 연동

    • 호스트가 Relay Allocation을 생성하면 발급된 Join Code를 Lobby 데이터에 저장했습니다.
    • 클라이언트는 Lobby에서 Join Code를 읽어 Relay 서버에 접속합니다.

방 목록
<게임 방 목록>
현재 참여 가능한 방 정보를 조회합니다. 비밀번호 입력
<비밀번호 입력>
비공개 방 참가 시 비밀번호를 검증합니다. 방 만들기
<방 만들기>
방 이름, 최대 인원, 비밀번호를 설정합니다.

캐릭터 선택
<캐릭터 선택 및 준비 완료>
방 참가자의 캐릭터 선택과 준비 상태를 Lobby 데이터로 동기화합니다.


효과:

  • 방 생성부터 게임 접속까지의 멀티플레이 진입 흐름을 하나의 로비 시스템으로 연결했습니다.
  • Lobby 이벤트와 Heartbeat를 활용해 로비 상태를 안정적으로 유지했습니다.
  • Vivox 채팅과 캐릭터 선택/준비 상태를 로비 화면에 통합해 게임 시작 전 협업 흐름을 개선했습니다.


🔗 릴레이 서버 (Unity Relay + Netcode for GameObjects)

로비에서 공유된 Relay Join Code를 기반으로 플레이어를 같은 네트워크 세션에 연결하고, Netcode for GameObjects를 통해 호스트 권한 기반 동기화를 수행합니다.

🛠️ 주요 구현 내용

  • Relay 연결 생성 및 참가

    • 호스트는 Unity Relay에 Allocation을 생성하고 Join Code를 발급받습니다.
    • 발급된 Join Code는 Lobby 데이터에 저장되어, 다른 플레이어가 방 참가 시 같은 네트워크 세션으로 접속할 수 있도록 했습니다.
    • 클라이언트는 Lobby에서 Join Code를 읽고 Relay에 참가합니다.
  • Lobby와 Relay 연동

    • Lobby는 방 검색, 참가, 플레이어 상태 관리 역할을 담당하고, Relay는 실제 네트워크 연결 경로를 제공합니다.
    • 방 생성부터 Relay 접속까지의 흐름을 LobbyManagerRelayManager가 분리해서 처리하도록 구성했습니다.
  • 호스트 권한 기반 동기화

    • 게임 내 상태 결정은 호스트가 담당하고, Netcode for GameObjects를 통해 클라이언트에게 동기화합니다.
    • 캐릭터 위치, 오브젝트 생성/소멸, 스킬 발동과 이펙트 재생은 호스트 기준으로 처리해 모든 클라이언트에 일관되게 반영했습니다.
  • 호스트 이전 대응

    • 기존 호스트가 방을 떠나는 경우, Lobby의 호스트 변경 정보를 기준으로 새로운 호스트가 Relay 연결 정보를 재설정하도록 구성했습니다.
    • 이를 통해 방장이 나간 상황에서도 로비와 게임 세션 흐름이 최대한 유지되도록 처리했습니다.

호스트 이전
<호스트 이전 처리>
호스트가 방을 떠나면 Lobby 정보를 기준으로 새로운 호스트가 연결 흐름을 이어받습니다.


이동 동기화
<이동 동기화>
호스트 기준 캐릭터 위치와 방향을 클라이언트에 동기화합니다.

오브젝트 동기화
<오브젝트 동기화>
게임 내 중요 객체의 생성, 소멸, 상태 변화를 모든 클라이언트에 반영합니다.

이펙트 동기화
<이펙트 동기화>
스킬 발동과 시각 효과를 호스트 기준으로 동기화합니다.




🚀 문제 해결 및 기술 개선 사례

프로젝트를 진행하면서 다양한 기술적 문제에 직면했으며, 이를 해결하고 시스템을 개선하기 위해 다음과 같은 노력을 기울였습니다.


1. 인증 구조 개선: 자체 계정 관리 → Steam 인증 티켓 + Firebase 서버 검증

  • 🤔 문제점:

    • 초기 로그인 구조는 사용자가 직접 ID와 비밀번호를 입력하고, 외부 데이터 저장소를 통해 계정 정보를 확인하는 방식이었습니다.
    • 이 방식은 계정 생성, 비밀번호 관리, 중복 계정 처리, 인증 실패 처리 등 계정 관리 책임이 프로젝트 내부에 남는 문제가 있었습니다.
    • 또한 클라이언트에서 전달되는 계정 정보를 그대로 신뢰하면 보안상 위험이 있기 때문에, 서버 측에서 신뢰 가능한 방식으로 플레이어를 식별할 필요가 있었습니다.
  • 💡 해결 과정:

    • 자체 계정/비밀번호 기반 흐름을 제거하고, Steamworks.NET의 GetAuthTicketForWebApi를 통해 Steam 인증 티켓을 발급받는 구조로 변경했습니다.
    • 클라이언트는 Steam 인증 티켓만 Firebase HTTPS Functions로 전달하고, Firebase 서버는 해당 티켓을 Steam Web API에 검증 요청합니다.
    • 서버는 클라이언트가 보낸 SteamID를 신뢰하지 않고, Steam 검증 결과로 반환된 SteamID64만 플레이어 식별자로 사용하도록 구성했습니다.
    • 검증된 SteamID64를 기준으로 Cloud Firestore의 profiles 컬렉션에서 플레이어 프로필을 조회하거나, 최초 접속 시 닉네임을 저장하도록 변경했습니다.
    • 닉네임 중복 검사 역시 클라이언트가 아닌 Firebase 서버에서 처리하도록 하여 프로필 관리 책임을 서버로 분리했습니다.
  • ✨ 개선 결과:

    • 비밀번호를 직접 저장하거나 검증하지 않아도 되어 계정 관리 부담과 보안 리스크를 줄였습니다.
    • Steam 플랫폼 인증 결과를 기반으로 플레이어를 식별하므로, 클라이언트 계정 정보 조작 가능성을 줄였습니다.
    • Firebase Cloud Functions와 Firestore를 통해 로그인 검증과 프로필 저장 흐름을 서버 중심으로 구성했습니다.
    • Steam 계정 기반 로그인으로 전환하면서 사용자 입장에서는 별도 회원가입 없이 게임에 진입할 수 있게 되었습니다.

<Steam 인증 티켓 기반 로그인 구조>

sequenceDiagram
 participant Client as Unity Client
 participant Steam as Steamworks.NET
 participant Firebase as Firebase HTTPS Functions
 participant SteamAPI as Steam Web API
 participant DB as Cloud Firestore
 Client->>Steam: Steam Auth Ticket 요청
 Steam-->>Client: Auth Ticket 반환
 Client->>Firebase: Auth Ticket 전송
 Firebase->>SteamAPI: Ticket 검증 요청
 SteamAPI-->>Firebase: 검증된 SteamID64 반환
 Firebase->>DB: SteamID64 기준 프로필 조회/저장
 DB-->>Firebase: 프로필 데이터 반환
 Firebase-->>Client: 로그인 결과 반환
Loading

2. 유지보수 및 작업속도 향상을 위한 프레임워크 변경: 싱글톤 패턴 ->컴포넌트 패턴,일반 DI → 젠젝트(Zenject) DI 프레임워크로 전환

  • 🤔 문제점:

    • 초기에 빠른 개발을 위해 싱글톤으로 기능을 확장했지만, 시간이 지나며 전역 의존성이 누적되고 Managers가 비대해졌습니다. 그 결과 한 곳을 수정하면 연쇄적으로 다른 기능이 깨지는 문제가 반복됐습니다. 이를 줄이기 위해 싱글톤을 분해하고 일반 DI 모듈를 통한 의존성 주입으로 통제했지만, 이 방법도 주입을 하는 컨트롤러가 여러 위치로 분산되면서 주입 경로 추적이 어려워져 큰 생산성 증대로 이뤄지진 않았습니다.
  • 💡 해결 과정:

    • Zenject를 도입하여 주입 지점을 Installer로 일원화하고(의존성성 모듈에 대한 가시성 향상), ProjectContext → SceneContext → GameObjectContext의 상향 단일 흐름으로 컴포지션 루트를 표준화했습니다.
    • 인터페이스 중심 바인딩과 환경별(프로덕션/테스트) Installer 분리로, 동일 코드에 Real/Mock을 손쉽게 교체할 수 있게 했습니다.
  • ✨ 개선 결과:

    • 의존성 가시화: "무엇이 어디서 주입되는지"가 한눈에 보이며, 변경 영향 범위가 명확해졌습니다.
    • 유지보수성 향상: 결합도가 낮아져 수정·확장이 수월해졌고, 도미노 이슈가 크게 감소했습니다.
    • 작업속도 개선: 테스트에서는 Installer만 바꿔 끼우면 되므로 세팅 시간이 단축되고, 기능 개발과 디버깅 사이클이 빨라졌습니다.
    • 초기 학습곡선을 넘기기 어려웠지만 이후 생산성과 안정성이 눈에 띄게 향상되었습니다.

Zenject 의존성 주입 구조
<Zenject 기반 의존성 등록 및 주입 구조>


3. 확장성과 재사용성을 위한 스킬 시스템 구조 개선: 타겟+뷰+로직 조합 → 파이프라인 기반 모듈 조합(Trigger/Targeting/Sequence/Decorator/Effect)

  • 🤔 문제점:

    • 초기에는 타겟 + 프리팹(연출/뷰) + 효과(로직) 를 조합해 스킬을 만들었습니다.
      뷰와 로직이 분리되어 있어 단순한 스킬은 빠르게 만들 수 있었고, 비슷한 형태라면 재사용도 가능했습니다.
    • 하지만 스킬이 조금만 복잡해져도 요구조건을 추가하기가 급격히 어려워졌습니다.
      실제 "데미지 스킬" 하나에도 아래 요소들이 붙습니다.
      • 즉시 발동 / 시전 시간 / 채널링
      • 단일 타겟 / 범위 / 지점 지정 / 논타겟
      • 조건부 효과 변화(버프/디버프/피격 등)
    • 요구조건이 늘어날수록 스킬 전용 코드가 비대해지고,
      비슷한 스킬이라도 "조금 다른 요구조건" 때문에 재사용이 깨지면서 스킬 수만큼 코드가 증가했습니다.
    • 결과적으로 유지보수 관점에서 확장 가능한 형태의 조합이 필요했고, 기존 구조로는 한계가 명확했습니다.
  • 💡 해결 과정:

    • 스킬을 하나의 큰 로직으로 만들지 않고, 기능 단위로 단계별 파이프라인으로 분해했습니다.
      • Trigger: 스킬이 어떻게 시작되는가
      • Targeting: 누구/어디에 적용되는가
      • Sequence: 언제/어떤 순서로/몇 번 실행되는가
      • Decorator: 어떻게 보여줄 것인가(애니메이션/VFX 등 연출)
      • Effect: 무엇이 일어나는가(데미지/힐/버프/투사체 등 게임플레이 결과)
    • 각 단계는 "자기 책임만" 가지도록 제한하고, 스킬은 모듈을 조합해서 만들도록 변경했습니다.
    • 특히 연출(Decorator)과 결과(Effect)를 분리해,
      게임플레이 로직이 연출 때문에 오염되지 않도록 구조를 정리했습니다.
  • ✨ 개선 결과:

    • 확장성 향상: 요구조건이 늘어도 "스킬 전용 코드"가 커지지 않고, 필요한 모듈 추가/교체로 대응 가능
    • 재사용성 향상: 비슷한 스킬은 공통 모듈을 공유하고, 다른 부분만 바꿔서 제작 가능
    • 유지보수성 향상: 책임 경계가 명확해져 변경 영향 범위가 줄고, 디버깅이 쉬워짐
    • 작업속도 향상: 스킬을 새로 "작성"하기보다 모듈을 "조합"하는 형태가 되어 제작 속도가 개선됨

📚 개발 일지: 모듈형 스킬 시스템

유니티로 확장 가능한 스킬 시스템을 밑바닥부터 구현한 기록입니다.

👇 개발일지 전체 보기 (Click)

| Part | 주제 | 링크 | | 1 | 개요 | 보러가기 | | 2 | 스킬 데이터 보관함 만들기 | 보러가기 | | 3 | 스킬 실행 | 보러가기 | | 4 | RuntimeSkill 조립과 등록 | 보러가기 | | 5 | SerializeReference로 레시피(SO) 만들기 | 보러가기 | | 6 | Trigger (어떻게 시작할까?) | 보러가기 | | 7 | Targeting (무엇을 모을까?) | 보러가기 | | 8 | Sequence (언제/어떤 순서로 실행할까?) | 보러가기 | | 9 | Decorator: 연출 (무엇을 보여줄까?) | 보러가기 | | 10 | Effect (어떤 효과를 입힐까?) | 보러가기 |


4. 애니메이션 속도 동기화: 예측과 보정을 통한 일관된 움직임 구현

  • 🤔 문제점:
    • 온라인 환경에서 모든 참여자에게 동일한 애니메이션을 시각적으로 일관되게 제공하는 것은 기술적 난제였습니다. 범용적으로 사용되는 애니메이션 동기화 방식은 애니메이션의 재생 속도 변화를 정확히 반영하지 못할뿐더러, 네트워크 지연이 발생한 클라이언트 측에서 애니메이션이 중간 지점부터 시작되는 현상이 생겨 플레이어간 동일한 시각적효과를 얻지 못하게 되었습니다. 결과적으로 호스트 환경에서는 정상적인 연출이 이루어지는 반면, 클라이언트에서는 애니메이션의 주요 구간을 놓치거나 갑작스러운 장면 전환을 경험하는 문제가 발생했습니다.
동기화 오류: 왼쪽 서버,오른쪽 클라이언트
<일반적인 동기화 방식 사용 시 발생한 문제: 좌측(호스트)과 달리 우측(클라이언트)은 애니메이션이 부자연스럽게 시작됨>

  • 💡 해결 과정:

    • 애니메이션 동기화의 정확도를 높이기 위해, 서버(호스트)는 주요 애니메이션 실행 시 시작 시점, 의도된 전체 길이, 그리고 애니메이션의 속도 변화 계획(예: 특정 구간에서의 점진적 감속, 최종 정지 조건 등)을 포함한 상세한 실행 정보를 모든 클라이언트에게 전달합니다.
    • 각 클라이언트는 이 정보를 수신하면, 모든 시스템 참여자가 공유하는 서버 시간을 이용하여 네트워크로 인한 지연 시간을 계산합니다. 이후, 애니메이션이 각클라이언트마다 재생되면 산출된 지연 시간을 보상하고 호스트와 거의 동시에 애니메이션이 종료될 수 있도록 재생 속도를 동적으로 조절합니다. 이러한 속도 조절은 점진적인 보간 방식을 통해 부드럽게 이루어집니다.
  • ✨ 개선 결과:

    • 본 시스템 적용을 통해, 네트워크 지연이 존재하는 환경에서도 클라이언트 측 애니메이션이 중간 생략 없이 처음부터 부드럽게 재생되며, 호스트와의 시각적 동기화 수준이 크게 향상되었습니다.
    • 특히, 공격 애니메이션과 실제 피해 판정 발생 시점 간의 일관성이 증대되어, 모든 플레이어에게 동일한 게임 플레이 경험을 제공하게 되었습니다.
동기화 성공: 왼쪽 서버,오른쪽 클라이언트
<개선된 시스템 적용 후: 모든 환경에서 애니메이션이 일관되고 부드럽게 동기화되는 모습>

[개발일지] 애니메이션 싱크를 맞추는방법



5. 테스트 효율성 증대: 커스텀 인스펙터를 통한 Play Mode Scenario 제어

  • 🤔 문제점:

    • Unity 6에서 도입된 Play Mode Scenario 기능은 멀티플레이 테스트를 매우 편리하게 만들어주었지만, 테스트 환경(클라이언트 수, 역할 태그 등)을 변경할 때마다 매번 Configure play mode Scenario 메뉴에 들어가 수동으로 수정해야 하는 불편함이 있었습니다.
  • 💡 해결 과정:

    • 목표 설정: 인스펙터 내에서 직접 테스트 클라이언트 수와 각 클라이언트의 태그를 설정하고, 시나리오 모드 사용 여부를 토글할 수 있는 커스텀 에디터를 구현하는 것을 목표로 삼았습니다.
    • 구현 : Scenario 에셋 접근 및 수정: SerializedObject API를 사용하여 Scenario 에셋 파일의 직렬화된 필드(m_MainEditorInstance, m_EditorInstances 등)에 접근했습니다. 이를 통해 인스펙터에서 입력한 태그 값을 에셋에 직접 반영하는 기능을 구현했습니다.
  • ✨ 개선 결과:

    • 멀티플레이 테스트 환경 설정을 위해 더 이상 메뉴를 찾아다닐 필요 없이, 인스펙터 내에서 모든 조작이 가능해졌습니다. 클라이언트 수를 늘리거나 각 클라이언트에 정보를 추가하는 작업이 매우 직관적이고 빨라져, 테스트 준비 시간이 단축되었습니다. 이러한 작업 개선은 디버깅 효율을 높이고, 개발자가 핵심 로직 개발에 더 집중할 수 있는 환경을 만들어주었습니다.

커스텀 인스펙터 결과물
< 인스펙터에서 테스트 환경 제어>

[개발일지] 커스텀 인스펙터 제작


6. Unity Lobby 서비스 버그 식별 및 해결

  • 🤔 문제점:

    • Netcode for GameObjects 기반 멀티플레이 게임 개발 중, Unity Lobby 서비스에서 특정 시나리오(호스트 이전 후 이전 호스트 재접속)에서 플레이어의 로비 참가 및 행동 변화에 따른 이벤트 콜백이 정상적으로 호출되지 않는 오류가 발생했습니다.
    • 이로 인해 다른 플레이어의 로비 내 활동이 실시간으로 반영되지 않고 로비 데이터가 불일치하는 등, 로비 시스템의 핵심 기능에 문제가 발생하였습니다.
  • 💡 해결 과정:

    • 초기 검증 및 원인 분석: 처음에는 자체 코드의 로직 오류를 의심하여 다양한 테스트와 코드 검증을 진행했으나, 문제가 지속되어 Unity Multiplayer 패키지(Lobby) 자체의 문제일 가능성을 인지했습니다.
    • 심층 분석 및 문제 구체화: 네트워크 트래픽과 콜백 호출 경로를 면밀히 추적하며 Lobby 패키지의 내부 로직을 분석한 결과, Unity 6에서 새로 도입된 패키지의 특정 부분(LobbyPatcher.GetLobbyDiff)에서 이전 호스트의 캐시된 정보로 인해 콜백이 내부적으로 막히는 현상을 발견했습니다.
    • 공식 리포트 및 임시 조치, 최종 해결: 분석한 내용을 바탕으로 해당 문제를 Unity Technologies에 공식적으로 버그 리포트했습니다. Unity 팀으로부터 해당 문제가 Lobby SDK의 버그임을 확인받았으며, 즉각적인 패치가 어려운 상황에서 SDK 코드의 일부 로직을 임시로 수정하여 개발을 지속했습니다. 이후 2025년 2월 6일 자 업데이트를 통해 해당 버그가 공식적으로 수정되었음을 확인했습니다.
  • ✨ 개선 결과:

    • Unity의 공식 패치 이후, 로비 이벤트 콜백이 정상적으로 작동하게 되어 호스트 이전 및 플레이어 재접속 시에도 로비 데이터가 안정적으로 동기화됩니다.
    • 이 경험을 통해 외부 라이브러리 문제 발생 시에도 적극적으로 원인을 분석하고, 공식 채널을 통해 리포트하며, 때로는 임시적인 해결책을 모색하여 프로젝트를 진행시키는 문제 해결 능력을 경험했습니다.

멀티플레이 서비스 SDK 오류발생
멀티플레이 서비스
SDK 오류발생

유니티 멀티플레이 서비스팀 답변 유니티 멀티플레이 서비스팀 답변
유니티 멀티플레이 서비스팀 답변
25년 2월 6일 업데이트 완료
2025년 2월 6일 업데이트 완료

[개발일지] Unity Lobby SDK 콜백 오류 추적 및 해결 기록



7. 네트워크 최적화: GC 발생 최소화를 위한 네트워크 오브젝트 풀링

  • 🤔 문제점:

    • 보스 스킬이나 플레이어 스킬 사용 시 수많은 네트워크 파티클 효과가 반복적으로 생성되고 소멸되었습니다.
    • 이로 인해 잦은 가비지 컬렉션(GC)이 발생하여 프레임 드랍을 유발했고, 특히 온라인 환경에서는 네트워크 부하가 증가하는 문제가 있었습니다.
  • 💡 해결 과정:

    • 네트워크 오브젝트 재활용 시스템 직접 구축:
      • 성능 저하 및 끊김 현상을 해결하고자, 자주 사용되는 네트워크 효과(파티클 등)를 미리 만들어두고 재활용하는 '네트워크 오브젝트 풀링' 시스템을 구현했습니다.
    • Netcode for GameObjects 기본 동작의 한계 인식 및 극복:
      • Netcode for GameObjects의 기본 네트워크 오브젝트 생성(Spawn) 및 소멸(Despawn) 방식은 내부적으로 실제 게임 오브젝트를 만들고 파괴합니다. 이는 파티클처럼 자주 반복되는 효과에 적용 시 불필요한 메모리 할당/해제로 이어져 성능에 부정적인 영향을 미쳤습니다.
    • Netcode 기능 확장: 오브젝트 생성/소멸 방식 직접 제어:
      • 이러한 한계를 극복하기 위해, Netcode for GameObjects가 제공하는 네트워크 오브젝트의 생성 및 소멸 방식을 개발자가 직접 제어할 수 있는 확장 기능(INetworkPrefabInstanceHandler)을 적극 활용했습니다.
      • 맞춤형 관리 로직 구현: '재활용 관리자를 자체 제작하여, 네트워크를 통해 특정 효과 생성 요청 시 새 오브젝트를 만드는 대신 미리 준비된 오브젝트 풀에서 가져와 사용하도록 변경했습니다.
      • 효율적인 반납 처리: 사용이 끝나 소멸 요청이 오면, 오브젝트를 실제로 파괴하는 대신 재활용 관리자가 풀에 반납하여 다음 사용을 위해 대기시키는 방식으로 최적화했습니다.
  • ✨ 개선 결과:

    • 파티클과 같은 네트워크 오브젝트의 빈번한 생성/소멸로 인한 GC 발생 빈도가 현저히 감소하여 게임의 전반적인 프레임 안정성이 향상되었습니다.
    • Netcode for GameObjects의 기본 메커니즘을 프로젝트 특성에 맞게 커스터마이징하여 네트워크 부하를 줄이고, 오브젝트 재사용을 통해 반응 속도 또한 개선되었습니다.

오브젝트 풀링
<네트워크 오브젝트 풀링>

[개발일지] 커스텀 네트워크 오브젝트 풀링 제작



8. 상태 관리의 유연성 확보: 유한 상태 머신(FSM)에서 전략 패턴으로

  • 🤔 문제점:

    • 초기 플레이어 캐릭터의 상태(이동, 공격, 정지 등)를 유한 상태 머신(FSM) 방식으로 구현했으나, 새로운 상태를 추가하거나 기존 상태의 로직을 변경할 때 코드 수정 범위가 넓어지고 복잡도가 증가했습니다. 특히, 각 상태에 따른 애니메이션 전환 로직이 강하게 결합되어 유지보수가 어려웠습니다.
  • 💡 해결 과정:

    • 이를 해결하고자 전략 패턴(Strategy Pattern)을 도입하여 각 상태를 독립적인 클래스(IState 인터페이스를 구현하는 형태)로 분리했습니다.
    • BaseController 클래스는 현재 상태 객체(CurrentStateType)를 통해 해당 상태의 로직을 실행하고, StateAnimationDict를 통해 상태 변경 시 적절한 애니메이션을 호출하도록 설계했습니다.
    • 이를 통해 각 상태의 행동 로직과 애니메이션 전환 로직을 캡슐화하고, 새로운 상태 추가 시 기존 코드에 미치는 영향을 최소화했습니다.
  • ✨ 개선 결과:

    • 코드의 가독성과 확장성이 크게 향상되었습니다. 새로운 플레이어 스킬이나 행동 상태를 추가할 때, IState를 구현하는 새 클래스를 만들고 BaseController에 등록하는 것만으로 확장이 가능해졌습니다.
    • 각 상태 로직이 분리되어 테스트와 디버깅이 용이해졌습니다.
FSM(유한상태머신)
<FSM(유한상태머신) 다이어그램>
전략패턴
<전략패턴 다이어그램>

9. 보스 AI 확장성 개선: 유한 상태 머신에서 비헤이비어 트리로 전환

  • 🤔 문제점:

    • 초기 보스 몬스터의 AI를 플레이어와 마찬가지로 유한 상태 머신(FSM)으로 구현했으나, 보스의 행동 패턴이 다양해지고 복잡해짐에 따라 상태 추가 및 전환 로직 관리가 어려워졌습니다. FSM은 복잡한 조건 분기나 병렬적인 행동 표현에 한계가 있었습니다.
  • 💡 해결 과정:

    • 보스 AI 구현을 위해 **비헤이비어 트리(Behavior Tree)**를 도입했습니다. 보스의 다양한 행동(이동, 기본 공격, 스킬 사용, 특정 조건에 따른 패턴 변화 등)을 모듈화된 노드 형태로 설계했습니다.
    • 비헤이비어 트리를 통해 조건 확인, 행동 실행, 흐름 제어(시퀀스, 셀렉터 등)를 직관적으로 구성할 수 있게 되었습니다.
    • 각 행동 노드는 BossGolemControllerBossGolemNetworkController와 상호작용하여 애니메이션, 네트워크 동기화 등을 처리합니다.
  • ✨ 개선 결과:

    • 보스 AI의 복잡한 행동 패턴을 보다 체계적이고 시각적으로 관리할 수 있게 되었습니다.
    • 새로운 스킬이나 행동 패턴을 추가하거나 기존 패턴을 수정하는 작업이 훨씬 용이해졌으며, 다양한 조건에 따른 AI 반응을 쉽게 구현할 수 있게 되어 보스전의 깊이가 더해졌습니다.

<비헤이비어 트리>


About

ImpossibleBosses의 소스코드를 확인할 수 있는 리포지토리 입니다.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

Contributors

Languages

AltStyle によって変換されたページ (->オリジナル) /