크래프톤 정글 TIL : 1022

lazyArtisan·2024년 10월 22일
0

정글 TIL

목록 보기
114/147

📝 배운 것들


🏷️ 싱글톤 패턴

public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
    private static T Instance;

    public static T instance
    {
        get
        {
            if (Instance == null)
            {
                Instance = FindObjectOfType<T>();
            }
            return Instance;
        }
    }
}

팀장님이 짜신 싱글톤 패턴

정의

싱글톤 패턴은 특정 클래스의 인스턴스를 1개만 생성되는 것을 보장하는 디자인 패턴이다. 즉, 생성자를 통해서 여러 번 호출이 되더라도 인스턴스를 새로 생성하지 않고 최초 호출 시에 만들어두었던 인스턴스를 재활용하는 패턴이다.

사용 이유

싱글톤 패턴을 사용함으로써 얻을 수 있는 이점 중 하나는 메모리 낭비를 방지할 수 있다는 것이다.

사용자가 1초에 10번 똑같은 요청을 보내면 요청을 처리하기 위한 똑같은 객체를 1초에 10번 생성하고 소멸되는 메모리 낭비 문제가 발생하게 된다.
하지만 싱글톤 패턴을 사용하면 최초 한번 new로 객체를 생성하고 해당 객체를 이후에도 사용하도록 공유(static)하면 메모리 낭비 문제를 방지할 수 있다.

문제점

1. 의존성이 높아진다.
싱글톤 패턴을 사용하는 경우 클래스의 객체를 미리 생성한 뒤에 필요한 경우 정적 메서드를 이용하기 대문에 클래스 사이에 의존성이 높아지게 된다는 문제점이 있다. (= 높은 결합)

싱글톤의 인스턴스가 변경 되면 해당 인스턴스를 참조하는 모든 클래스들을 수정해야 하는 문제가 발생한다.

2. private 생성자 때문에 상속이 어렵다.
싱글톤 패턴은 기본 생성자를 private로 만들었기 때문에 상속을 통한 자식 클래스를 만들 수 없다는 문제점이 있다. 즉, 자바의 객체지향 언어의 장점 중 하나인 다형성을 적용하지 못한다는 문제로 이어진다.

3. 테스트하기가 힘들다.
싱글톤 패턴의 인스턴스는 자원을 공유하고 있다는 특징이 있다. 이는 서로 독립적이어야 하는 단위 테스트를 하는데 문제가 된다.

독립적인 테스트가 진행이 되려면 전역에서 상태를 공유하고 있는 인스턴스의 상태를 매번 초기화해야 한다. 초기화해주지 않으면 전역에서 상태를 공유 중이기 때문에 테스트가 정상적으로 수행되지 못할 가능성이 존재한다.

이러한 문제점들 때문에 싱글톤 패턴은 안티패턴이라고 불리기도 한다.

설명 출처 : https://velog.io/@jhbae0420/%EC%8B%B1%EA%B8%80%ED%86%A4-%ED%8C%A8%ED%84%B4%EC%9D%98-%EC%82%AC%EC%9A%A9-%EC%9D%B4%EC%9C%A0%EC%99%80-%EB%AC%B8%EC%A0%9C%EC%A0%90



👾 나만무


게임 대기 화면 전에 시작됨 : 새로 만든 맵 가져올 때 남아있던 캐릭터 때문에 메인 카메라가 이미 있었음

UI가 안 보임 : 게임뷰 중앙 상단 렌더링 디스플레이 배율 조절 이상해서 그랬음

빌드 뽑는 법 : 빌드 세팅

이쁜 애셋에 달려있는 코드들이 어려워서 초보자 분쇄기다

맵 NavMesh 수정

Bake하면 온갖 곳에 다 NavMesh가 칠해짐

navmeshsurface에 달려있는 parameter들 전부 설명해줘

Unity에서 NavMeshSurface 컴포넌트는 특정 표면에 대해 NavMesh(네비게이션 메시)를 베이크할 수 있게 해주는 중요한 컴포넌트입니다. 각종 파라미터들이 NavMesh를 어떻게 생성할지 결정하는데, 이 파라미터들을 모두 설명하겠습니다:

1. Agent Type ID

  • 설명: NavMesh가 특정 에이전트 타입에 맞게 생성되도록 설정하는 값입니다. 에이전트는 Unity에서 NavMesh를 사용하여 이동하는 캐릭터 또는 객체를 의미합니다. 예를 들어, 사람 크기의 에이전트와 차량 크기의 에이전트는 서로 다른 NavMesh를 사용할 수 있습니다. 여기서 에이전트 타입 ID는 해당 에이전트 타입에 맞는 NavMesh를 설정합니다.
  • 기본값: Unity의 기본 에이전트는 "Humanoid"입니다.

2. Collect Objects

  • 설명: 어떤 객체들이 NavMesh 생성 시 포함될지를 설정합니다.
  • 옵션들:
    • All: 모든 객체를 대상으로 합니다.
    • Volume: NavMeshSurface의 경계 내에 있는 객체만 포함됩니다.
    • Children: NavMeshSurface 오브젝트의 자식 객체만 포함됩니다.

3. Use Geometry

  • 설명: NavMesh를 만들 때 어떤 지오메트리를 사용할지를 설정합니다.
  • 옵션들:
    • Rendered Meshes: 현재 씬에서 렌더링되는 메쉬를 기반으로 NavMesh를 생성합니다.
    • Physics Colliders: 물리 콜라이더 정보를 기반으로 NavMesh를 생성합니다.

4. Default Area

  • 설명: NavMesh에 포함된 기본적으로 지정될 영역 유형을 선택합니다. 각 영역(Area)은 이동 비용을 설정할 수 있으며, 비용에 따라 AI가 경로를 탐색하는 방식이 달라집니다. 예를 들어, "Walkable" 영역과 "Not Walkable" 영역 등이 있습니다.
  • 기본값: Walkable

5. Layer Mask

  • 설명: NavMesh를 생성할 때 포함할 레이어를 설정합니다. 특정 레이어만 NavMesh 베이크 시 포함되도록 하여, 다른 객체들이 NavMesh에 영향을 미치지 않도록 할 수 있습니다.

6. Override Tile Size

  • 설명: NavMesh의 타일 크기를 수동으로 설정할 수 있는 옵션입니다. 기본적으로 Unity는 자동으로 타일 크기를 설정하지만, 이 옵션을 사용하면 타일 크기를 직접 조정할 수 있습니다.
  • 기본값: False (비활성화)

7. Tile Size

  • 설명: 타일의 크기를 지정합니다. Override Tile Size가 활성화된 경우에만 사용할 수 있으며, 타일 크기를 더 크게 하면 메모리 사용량이 증가하지만 베이크 속도가 빨라질 수 있습니다.

8. Override Voxel Size

  • 설명: NavMesh가 베이크될 때 사용하는 보셀(Voxel) 크기를 수동으로 설정할 수 있는 옵션입니다. 보셀은 NavMesh를 생성할 때 공간을 나누는 작은 셀이며, 이 값이 작아지면 NavMesh의 정확도가 증가하지만, 베이크 시간이 길어질 수 있습니다.
  • 기본값: False (비활성화)

9. Voxel Size

  • 설명: NavMesh의 보셀 크기를 지정합니다. Override Voxel Size가 활성화된 경우에만 사용할 수 있으며, 작은 값은 더 정밀한 NavMesh를 생성하지만 더 많은 계산 자원을 사용하게 됩니다.

10. Min Region Area

  • 설명: NavMesh에 포함될 최소 영역의 크기를 설정합니다. 이 값보다 작은 크기의 NavMesh 영역은 제거됩니다. 이를 통해 불필요하게 작은 NavMesh 섹션들을 제거하고 성능을 향상시킬 수 있습니다.

11. Build Height Mesh

  • 설명: NavMesh가 표면 높이에 대한 정보를 포함할지를 결정하는 옵션입니다. 이 옵션을 활성화하면 에이전트가 경사면을 오르거나 내릴 때 더 정확하게 이동할 수 있지만, NavMesh 데이터 크기가 커질 수 있습니다.
  • 기본값: False (비활성화)

12. Center

  • 설명: NavMeshSurface의 베이크 중심을 설정합니다. 이 값은 Collect Objects가 "Volume"으로 설정된 경우 NavMesh가 어느 위치를 중심으로 생성될지를 결정합니다.
  • 기본값: (0, 0, 0)

13. Size

  • 설명: NavMeshSurface가 베이크할 영역의 크기를 설정합니다. 이 값은 Collect Objects가 "Volume"으로 설정된 경우에만 사용됩니다. 기본적으로 이 값은 NavMeshSurface의 경계를 나타냅니다.

14. Ignore NavMesh Agent

  • 설명: NavMesh 에이전트를 NavMesh 베이크에서 무시할지를 설정합니다. 이 옵션을 활성화하면 NavMesh 에이전트가 있는 부분을 제외하고 NavMesh를 생성할 수 있습니다.
  • 기본값: False

15. Ignore NavMesh Obstacle

  • 설명: NavMesh 장애물을 베이크 시 무시할지를 설정합니다. 장애물은 에이전트가 이동하지 못하는 영역을 생성하는 요소로, 무시하면 더 많은 영역에 대해 NavMesh가 생성됩니다.
  • 기본값: False

16. Use Advanced Settings

  • 설명: 고급 설정을 사용할지를 선택하는 옵션입니다. 이 옵션을 활성화하면 베이크 과정에서 더 세부적인 조정이 가능해집니다.
  • 기본값: False (비활성화)

이러한 파라미터들을 통해 NavMeshSurface 컴포넌트는 다양한 상황과 필요에 맞게 NavMesh를 생성할 수 있습니다. 파라미터를 조정하면서 정확도, 성능, 에이전트의 경로 탐색 효율성을 최적화할 수 있습니다.

current object hierarchy는 무슨 뜻인지 알아? Object Collection - Collect Objects에 들어있는 선택지인데.

"Current Object Hierarchy"는 Unity의 NavMeshSurface 컴포넌트에서 Object Collection - Collect Objects 옵션 중 하나로, NavMesh를 베이크할 때 어떤 객체들을 포함할지를 설정하는 기능입니다. 이 선택지는 현재 오브젝트의 계층 구조에 있는 객체들만 NavMesh 베이크에 포함시키겠다는 뜻입니다.

즉, Current Object Hierarchy를 선택하면 NavMeshSurface가 붙어 있는 객체와 그 객체의 자식들만 NavMesh를 만들 때 고려됩니다. 이 옵션을 사용하면 다른 씬의 객체나 동일한 계층 구조 밖에 있는 객체들은 NavMesh 계산에 포함되지 않고, 특정 객체나 그 하위 객체들만을 대상으로 베이크할 수 있습니다.

이 옵션은 Collect Objects가 전체 씬에서 모든 객체를 대상으로 NavMesh를 생성하는 것과는 다르게, 더 제한적이고 구조적인 범위에서 NavMesh를 만들고 싶을 때 유용합니다.

floor 검색해서 바닥들에만 navmesh component 달고 navmesh center를 2에서 -1로 수정하고 size 10에서 2로 바꿔서 bake 했더니 나쁘지 않게 수정됨. 원래는 천장에도 죄다 파란색 (navmesh) 칠해져있었음. 오른쪽 천장은 floor 타일을 천장으로 사용해서 그랬던거. 그냥 저곳만 component 삭제하면 될듯.

바닥만 칠하면 안되고 층고가 높은 계단 쪽은 size를 늘려서 칠했더니 아름답게 다 칠해짐

navmesh 안 칠해지게 하는게 obstacle임 modifier임?

NavMesh에서 특정 영역에 NavMesh가 생성되지 않도록 하는 것은 NavMesh Obstacle과 NavMesh Modifier 둘 다 가능하지만, 그 용도와 차이가 있습니다:

1. NavMesh Obstacle

  • NavMesh Obstacle은 에이전트가 이동하지 못하는 영역을 설정하는 컴포넌트입니다. 장애물의 형태(박스 또는 캡슐)로 설정되며, 이를 배치하면 해당 영역에 NavMesh가 생성되지 않거나, 에이전트가 그 영역을 피하게 됩니다.
  • Carve 옵션을 활성화하면 Obstacle이 NavMesh에 구멍을 뚫어서 그 부분을 에이전트가 이동하지 못하게 합니다.
  • 주 용도: 동적인 장애물(움직이거나 변하는 오브젝트)이 있을 때, NavMesh에 반영할 수 있습니다.

2. NavMesh Modifier

  • NavMesh Modifier는 특정 오브젝트나 영역이 NavMesh에서 어떤 역할을 할지를 정의하는 컴포넌트입니다. 이 컴포넌트를 사용하면 해당 영역이 "Walkable" 또는 "Not Walkable"로 설정되어, NavMesh에 영향을 미칩니다.
  • Ignore From Build 옵션을 활성화하면 해당 객체가 아예 NavMesh에 포함되지 않도록 할 수 있습니다.
  • 주 용도: 특정 오브젝트나 영역을 제외하거나 다른 특성으로 설정할 때 사용됩니다. 예를 들어, 해당 영역을 걷기 불가능하게 설정할 수 있습니다.

결론:

  • NavMesh에 구멍을 뚫거나 특정 영역에 NavMesh가 생기지 않게 하는 용도로는 NavMesh Obstacle과 NavMesh Modifier 둘 다 사용할 수 있습니다.
  • 동적인 장애물에 더 적합한 것은 NavMesh Obstacle이고, 영구적이거나 특수한 영역 지정은 NavMesh Modifier를 사용하는 것이 일반적입니다.

따라서, NavMesh가 안 칠해지게 하려면 NavMesh Obstacle을 사용해서 Carve 옵션을 적용하거나, NavMesh Modifier의 Ignore From Build 옵션을 사용하는 방법이 있습니다.

예외가 있는 부분이 있길래 그냥 size 왕창 늘리고 천장에는 modifier 적용하면 되지 않을까?

바로 영역전개 복마어주자

modifier 다른 곳에도 추가하다가 이상하게 돼서 ctrl + z 하다가 온갖 곳에 modifier가 붙어버려서

using UnityEngine;
using UnityEngine.AI;
using Unity.AI.Navigation;  // NavMeshModifier를 포함하는 네임스페이스
using UnityEditor;

public class RemoveNavMeshModifiersEditor : MonoBehaviour
{
    [MenuItem("Tools/Remove NavMesh Modifiers")]
    public static void RemoveModifiersInSelectedObject()
    {
        // 현재 선택된 오브젝트
        GameObject selectedObject = Selection.activeGameObject;

        if (selectedObject == null)
        {
            Debug.LogWarning("오브젝트를 선택하세요.");
            return;
        }

        // 선택된 오브젝트와 모든 자식들에 대해 NavMeshModifier 삭제
        NavMeshModifier[] modifiers = selectedObject.GetComponentsInChildren<NavMeshModifier>();

        foreach (var modifier in modifiers)
        {
            Undo.DestroyObjectImmediate(modifier); // Undo 기능을 사용하여 에디터에서 바로 삭제
        }

        Debug.Log("NavMesh Modifiers가 삭제되었습니다.");
    }
}

에디터 스크립트 추가해서 modifier 다 삭제해버림

클라팀이 해야될 거

  1. NPC : 이동 자연스럽게, 생성 밀집도 해결
  2. 관전 모드 만들기
  3. 합친 Scene Test
  4. 카메라 내부 벽 뚫리는 현상 해결
  5. 조준선 ui 제작
  6. NavMesh 수정 마무리

복마어주자 썼더니 이상한 곳에도 NavMesh가 색칠돼버림.

필요한 부분들만 size 늘려서 해결하려고 했는데
obstacle 넣어도 NavMesh Obstacles랑 안 닿으면 막무가내로 bake 해버림

navmesh bake할 때 분명 obstacle 컴포넌트 추가한 오브젝트 위에도 navmesh가 칠해지는 현상이 있는데 어떻게 고쳐?

NavMesh Obstacle 컴포넌트를 추가한 오브젝트 위에도 NavMesh가 생성되는 경우, Carve 옵션이 활성화되지 않았을 가능성이 큽니다. Carve 옵션을 사용해야 NavMesh Obstacle이 NavMesh에 직접적인 구멍을 뚫어, 그 영역에 NavMesh가 생성되지 않게 할 수 있습니다.

문제 해결 방법:

  1. NavMesh Obstacle의 Carve 옵션 활성화:

    • Obstacle이 있는 오브젝트를 선택한 후, NavMesh Obstacle 컴포넌트에서 Carve 옵션을 체크해야 합니다.
    • Carve 옵션을 활성화하면, NavMesh가 해당 Obstacle을 피해서 생성됩니다.
  2. Obstacle 크기 확인:

    • Obstacle의 크기가 충분히 커야 NavMesh가 제대로 구멍을 뚫고 피할 수 있습니다. 너무 작은 Obstacle은 NavMesh가 영향을 받지 않고, 해당 영역에 NavMesh가 그대로 생성될 수 있습니다.
  3. Dynamic Obstacle 고려:

    • 만약 Dynamic Obstacle을 사용하고 있다면, NavMesh가 실시간으로 업데이트되기 때문에 Obstacle이 제대로 작동하려면 Carve가 반드시 필요합니다.

요약:

  • NavMesh Obstacle에서 Carve 옵션을 활성화하세요.
  • Obstacle 크기를 확인하고 필요하면 조정하세요.

이 설정으로 Obstacle이 있는 영역에 NavMesh가 생성되지 않도록 할 수 있을 것입니다.

이거 설명해줘

해당 스크린샷은 NavMeshObstacle 컴포넌트의 Carve 옵션과 그 세부 설정에 대한 내용입니다. 각 항목을 설명해드리겠습니다.

1. Carve (체크박스)

  • 설명: 이 옵션을 활성화하면 NavMeshObstacleNavMesh 위에 구멍을 뚫습니다. 구멍을 뚫는다는 의미는 Obstacle이 있는 영역에 NavMesh가 생성되지 않도록 하여, 그 위로 NavMesh 에이전트가 이동하지 못하게 만듭니다.
  • 활성화 상태: 활성화되어 있으면, 장애물이 NavMesh에 실시간으로 반영됩니다.

2. Move Threshold (이동 임계값)

  • 설명: NavMeshObstacle이 얼마나 이동해야 NavMesh가 다시 재계산될지 결정하는 값입니다. 예를 들어, 장애물이 이 값 이상 움직이면, NavMesh 구멍이 다시 계산됩니다.
  • 기본값: 0.1
  • 의미: Obstacle이 이 수치만큼 이동할 때 NavMesh에 구멍이 다시 뚫리거나 수정됩니다. 너무 작은 값을 설정하면 Obstacle이 자주 움직일 경우 자주 재계산이 이루어져 성능에 영향을 줄 수 있습니다.

3. Time To Stationary (정지 시간)

  • 설명: NavMeshObstacle이 정지한 후 얼마나 시간이 경과해야 Obstacle을 정지된 상태로 간주하고, 그때서야 NavMesh 구멍을 뚫기 시작할지 설정합니다.
  • 기본값: 0.5
  • 의미: Obstacle이 0.5초 동안 정지 상태로 있으면 그때부터 Carve가 적용되어 NavMesh에 구멍이 생기게 됩니다. 이 값을 적절히 설정해주면, 자주 움직이는 Obstacle에 의해 성능 저하를 방지할 수 있습니다.

4. Carve Only Stationary (정지 상태에서만 구멍 생성)

  • 설명: 이 옵션을 활성화하면 Obstacle이 이동 중일 때는 NavMesh에 구멍을 뚫지 않고, Obstacle이 정지한 경우에만 구멍을 뚫습니다.
  • 활성화 상태: 이 옵션이 활성화되어 있으면, Obstacle이 움직이는 동안에는 NavMesh에 변화가 없고, 정지한 후에만 구멍을 뚫게 됩니다. 이 설정은 이동 중인 오브젝트가 있을 때 불필요한 NavMesh 재계산을 줄이는 데 도움이 됩니다.

요약:

  • Carve: NavMesh에 구멍을 뚫을지 여부를 설정.
  • Move Threshold: Obstacle이 NavMesh 재계산을 유발할 만큼 움직이는 거리.
  • Time To Stationary: Obstacle이 정지 상태로 간주되기까지 걸리는 시간.
  • Carve Only Stationary: Obstacle이 정지한 상태에서만 구멍을 뚫도록 설정.

이 옵션들은 NavMesh의 실시간 갱신과 성능을 최적화하기 위해 사용됩니다. 이동하는 오브젝트가 있을 때 빈번한 재계산을 피하면서, 정지한 경우에만 NavMesh에 영향을 주도록 조정할 수 있습니다.

안 움직이는 물체에 carve 넣으면 연산량 불필요하게 늘어나 안 늘어나?

Carve 옵션을 활성화한 NavMeshObstacle을 정적인, 즉 움직이지 않는 물체에 적용하면 불필요한 연산이 발생하지는 않습니다. 이유는 다음과 같습니다:

  1. 정적 Obstacle:

    • Carve 옵션이 적용된 Obstacle이 움직이지 않으면, Unity는 처음에 NavMesh에서 구멍을 뚫은 후에 추가적인 연산을 하지 않습니다. 이는 정적인 오브젝트에 대해 추가적인 계산이 필요 없음을 의미합니다.
    • 즉, Obstacle이 한 번 배치되고 움직이지 않으면, NavMesh 재계산이 일어나지 않기 때문에 연산량은 크게 늘어나지 않습니다.
  2. 움직이는 Obstacle과의 차이:

    • Carve 옵션은 동적인 물체(움직이는 물체)에 특히 유용하며, 움직일 때마다 NavMesh에 구멍을 뚫고 다시 계산합니다. 이 경우에는 움직일 때마다 NavMesh가 업데이트되므로 연산량이 늘어나지만, 정적 물체의 경우에는 한 번만 처리되면 이후로는 영향을 미치지 않습니다.
  3. Carve Only Stationary 옵션:

    • 이 옵션이 활성화되면 정지한 상태에서만 구멍을 뚫기 때문에, 오브젝트가 움직이지 않는 한 재계산이 일어나지 않습니다. 따라서 이 옵션이 정적인 물체에서는 불필요한 옵션이 됩니다.

update문마다 움직였는지 아닌지 확인하는게 아니라 exception을 일으키는거임?

Unity에서 NavMeshObstacleCarve 옵션은 매 프레임마다 오브젝트가 움직였는지 확인하는 방식으로 작동하는 것이 아닙니다. 대신, 내부적으로 효율적으로 설계되어 있으며, 움직임이 발생할 때만 NavMesh를 업데이트하게 되어 있습니다.

작동 방식

  • NavMeshObstacle 컴포넌트가 활성화되면 Unity는 해당 Obstacle이 NavMesh 상에 영향을 미치도록 설정합니다.
  • Move ThresholdTime to Stationary 같은 설정은 NavMeshObstacle이 일정 기준 이상 움직였을 때만 NavMesh를 다시 계산하게 합니다. 즉, 매 프레임마다 무조건 오브젝트가 움직였는지를 확인하는 것이 아니라, 움직임이 기준치를 초과했을 때만 NavMesh가 업데이트됩니다.
  • 이는 내부적으로 최적화되어 있어서 매 프레임마다 오브젝트의 움직임을 체크하는 것과 달리, 필요할 때만 움직임을 감지하고 처리하여 성능을 저하시키지 않도록 합니다.

예외 발생이 아닌 이벤트 기반 처리

  • NavMeshObstacle의 Carve 동작은 예외(Exception)를 일으키거나 불필요한 연산을 매 프레임 발생시키는 것이 아니라, 이벤트 기반으로 작동한다고 할 수 있습니다. 즉, Obstacle이 이동했거나 변화가 생겼을 때만 구멍을 뚫거나 수정하는 방식으로 처리합니다.
  • 예를 들어, Move Threshold 값을 설정하면 그 값 이상으로 움직임이 발생해야만 NavMesh가 다시 업데이트되므로, 작은 변화나 미세한 움직임에는 불필요한 연산이 발생하지 않도록 방지됩니다.

요약

  • NavMeshObstacleCarve 옵션은 매 프레임마다 오브젝트의 움직임을 확인하는 것이 아니라, 오브젝트가 일정 기준 이상 움직였을 때 NavMesh를 다시 계산합니다.
  • 내부적으로 최적화된 방식으로 작동하며, 예외(Exception)를 발생시키는 방식이 아니라 움직임이 감지될 때만 업데이트하는 이벤트 기반 방식으로 처리됩니다.

carve 옵션 켰더니 높이가 낮은 오브젝트를 잡아먹어버림.
ad-hoc이지만 오브젝트 삭제하는 식으로 해결.

관전 모드 구현

현재 : 데미지를 받으면 카메라와 함께 플레이어가 사라진 후 마스터 클라에 "나 죽었어" 얘기를 한다
마스터 클라가 "나 죽었어"라고 말한 애한테 딱히 뭐 해주진 않는다

그냥 클라에서 OnDamaged에 카메라 변경 로직 추가해주면 되는거

각 플레이어 머리 위에 카메라 달아놓고 마우스 움직이면 카메라 움직이게

이 코드 설명 좀

    public virtual void OnDead()
    {
        throw new System.NotImplementedException();
    }

이 코드는 C#에서 virtual method로 정의된 OnDead라는 메서드를 나타냅니다. 메서드가 가상으로 선언되었고, 아직 구현되지 않은 상태임을 의미합니다. 여기서 각각의 요소를 설명하겠습니다:

이 코드의 의도:

  • 가상 메서드로 선언: OnDead()는 아마도 기본 클래스에서 선언되었으며, 이 메서드는 파생 클래스에서 재정의(Override)될 것입니다. 각 파생 클래스는 자신만의 방식으로 OnDead() 메서드를 구현할 수 있습니다.
  • 미구현 상태: 이 메서드는 아직 구현되지 않았으며, 이를 호출하는 경우 NotImplementedException이 발생하여 기능이 구현되지 않았음을 알립니다.

override 해버리면 throw new System.NotImplementedException();는 무시되는거임? 원래 override하면 원래 건 무시돼?

Override는 기본 클래스의 가상 메서드 구현을 완전히 대체합니다.
재정의된 메서드에서는 기본 클래스의 가상 메서드 구현이 무시됩니다.
기본 메서드를 호출하려면 base 키워드를 사용해 명시적으로 호출해야 합니다.

확인하긴 했는데 OnDead는 애초에 지금 안 쓰는 함수라고 함.

PhotonNetwork.Destroy(this.gameObject); 이건 뭔가?

그냥 Destory()인데 포톤 네트워크에 다른 애들한테서도 이거 없애주세요 라는 신호를 보내는 코드

이 코드 설명 좀

        if (GetComponent<PhotonView>().AmOwner)
        {
            PhotonNetwork.Destroy(this.gameObject);
            NEPlayerDeath.PlayerDeath();
        }

이 코드는 Photon Unity Networking (PUN)을 사용하여 네트워크 상에서 플레이어의 소유 여부를 확인하고, 그 플레이어의 오브젝트를 삭제한 후 특정 사망 처리 기능을 호출하는 역할을 합니다. 한 줄씩 설명해 보겠습니다.

전체적인 코드 설명:

  • PhotonView를 통해 네트워크 상에서 특정 오브젝트가 플레이어 소유인지를 확인한 후, 해당 오브젝트를 네트워크에서 삭제하고 사망 처리를 진행합니다.

코드 상세 설명:

if (GetComponent<PhotonView>().AmOwner)
  • GetComponent<PhotonView>(): 현재 게임 오브젝트에 붙어 있는 PhotonView 컴포넌트를 가져옵니다. PhotonView는 PUN에서 네트워크 상에서 오브젝트를 식별하고 관리하는 핵심 컴포넌트입니다.
  • AmOwner: 이 속성은 현재 네트워크 오브젝트가 로컬 플레이어(자신)의 소유인지 여부를 나타냅니다. true이면 해당 오브젝트가 로컬 플레이어의 소유이고, false이면 다른 플레이어의 소유입니다.
    • 즉, 이 조건은 이 오브젝트가 현재 플레이어의 소유인지를 확인하는 역할을 합니다.
PhotonNetwork.Destroy(this.gameObject);
  • PhotonNetwork.Destroy(this.gameObject): 현재 오브젝트(즉, 이 스크립트가 붙어 있는 오브젝트)를 네트워크 상에서 삭제하는 명령입니다.
    • PhotonNetwork.Destroy는 네트워크 상에 있는 모든 클라이언트에서 이 오브젝트를 제거합니다. 이 명령을 호출하면 해당 오브젝트는 모든 플레이어의 게임에서 삭제됩니다.
    • 오브젝트 삭제는 일반적인 Unity의 Destroy()와 달리, PUN을 사용하여 네트워크 상에서 동기화된 삭제가 이루어집니다.
NEPlayerDeath.PlayerDeath();
  • NEPlayerDeath.PlayerDeath(): 이 코드는 NEPlayerDeath라는 클래스의 PlayerDeath() 메서드를 호출합니다.
    • 이 부분은 네트워크 플레이어의 사망 처리를 위한 사용자 정의 메서드일 가능성이 큽니다. 이 메서드 내부에서는 플레이어의 사망 상태를 처리하는 코드가 있을 것으로 보입니다.
    • 예를 들어, 스코어 처리, 부활 처리, 사망 애니메이션 실행 등과 같은 기능이 들어 있을 수 있습니다.

흐름 정리:

  1. 소유 확인: 이 오브젝트가 현재 플레이어의 소유인지 확인합니다. AmOwnertrue일 경우에만 해당 로직이 실행됩니다.
  2. 오브젝트 삭제: 플레이어의 소유인 경우, 네트워크 상에서 이 오브젝트를 삭제합니다. 이는 모든 클라이언트에서 동기화되어 오브젝트가 제거됩니다.
  3. 사망 처리 호출: 사망 관련 처리를 위한 PlayerDeath() 메서드를 호출하여 추가적인 사망 로직을 처리합니다.

관전 모드 구현하려고 하는데 각 플레이어 위에 카메라가 이미 있고 활성화만 바꾸는게 나을까 아니면 시점 바꿀 때마다 이미 있던 카메라는 사라지고 다른 플레이어 위에 카메라가 새로 생성되는게 나을까?

관전 모드를 구현할 때, 이미 존재하는 카메라를 활성화/비활성화하는 방식과 카메라를 매번 새로 생성하는 방식 중 어떤 방법이 더 나은지는 성능과 관리의 용이성을 고려해야 합니다.

1. 카메라 활성화/비활성화 방식:

  • 장점:
    • 성능: 카메라를 미리 생성해 두고 필요할 때 활성화/비활성화하는 방식은 성능에 유리합니다. 카메라를 매번 생성하는 연산을 줄일 수 있고, 오브젝트가 활성화되는 것만으로 즉시 전환이 가능하므로, 더 빠르고 부드러운 전환이 가능합니다.
    • 코드 간결성: 이미 생성된 카메라들을 관리하고 그 상태만 전환하는 것이므로 코드 관리가 상대적으로 간단합니다.
  • 단점:
    • 메모리: 모든 플레이어마다 카메라가 미리 생성되어 있으므로, 메모리를 다소 더 많이 사용할 수 있습니다. 하지만 일반적인 경우에는 이로 인한 성능 문제가 크지 않을 수 있습니다.
  • 적합한 경우: 플레이어 수가 적당히 적고, 자주 카메라 전환이 일어나는 경우 이 방법이 적합합니다.

2. 카메라를 새로 생성하는 방식:

  • 장점:
    • 메모리 최적화: 필요할 때만 카메라를 생성하므로 메모리를 절약할 수 있습니다. 모든 플레이어에게 미리 카메라를 생성해 두지 않아도 됩니다.
    • 유연성: 새롭게 카메라를 생성할 때마다 다양한 설정을 줄 수 있어 더 유연한 구현이 가능합니다. 예를 들어, 플레이어별로 특수한 카메라 설정이 필요할 때 유용합니다.
  • 단점:
    • 성능: 카메라를 자주 새로 생성하고 파괴하는 것은 성능에 영향을 줄 수 있습니다. 특히 플레이어가 많고, 카메라 전환이 자주 일어나는 경우에는 성능 저하가 발생할 가능성이 있습니다.
    • 복잡성 증가: 카메라 생성 및 소멸을 관리하는 추가적인 코드가 필요합니다. 이를 구현하고 관리하는 과정에서 복잡도가 증가할 수 있습니다.
  • 적합한 경우: 플레이어 수가 많거나, 카메라 전환이 자주 일어나지 않고 유연한 설정이 필요한 경우 유리할 수 있습니다.

결론:

카메라 활성화/비활성화 방식이 더 나은 선택일 가능성이 높습니다. 이유는 성능 최적화 측면에서 카메라를 미리 생성해 두고 필요할 때마다 활성화하는 것이 훨씬 부드럽고 빠른 전환을 제공하기 때문입니다. 대부분의 게임에서는 관전 모드에서도 즉각적인 전환이 필요하기 때문에 이 방식이 더 적합할 수 있습니다.

다만, 플레이어 수가 아주 많고 메모리가 걱정되는 상황이거나, 각 플레이어마다 카메라 설정이 크게 달라야 한다면 새로운 카메라 생성 방식을 고려할 수 있습니다.

카메라 다 달아놓으면 될듯

모델 있는 프리팹에 오른쪽 공통 프리팹 오브젝트들이 달리고 있다

서버 코드를 고쳐야할 것 같아서 서버팀에게 물어봤더니
"카메라를 굳이 다 달아주지 말고 카메라 하나를 각 플레이어 시점으로 이동시키면 될 것" 이라고 조언해줌.

천잰가?

카메라에 코드 붙일 건데 자기 자신을 지칭하려면 코드에서 뭐라고 해야됨

카메라에 코드를 붙여서 카메라 자신의 게임 오브젝트를 지칭하려면, 코드에서 gameObject 또는 this를 사용하면 됩니다. 두 가지가 상황에 따라 조금씩 다르게 동작합니다.

1. gameObject

  • gameObject현재 스크립트가 부착된 게임 오브젝트를 가리킵니다.
  • 카메라가 부착된 게임 오브젝트 자체를 제어할 때 사용합니다.
// 현재 카메라 게임 오브젝트를 비활성화
gameObject.SetActive(false);

// 현재 카메라 게임 오브젝트의 이름 출력
Debug.Log(gameObject.name);

2. this

  • this현재 스크립트 인스턴스(즉, 스크립트 클래스 자체)를 가리킵니다.
  • 클래스의 필드나 메서드에 접근할 때 사용합니다.
// 스크립트 내부 메서드 호출
this.SomeMethod();

대부분의 경우, 카메라 게임 오브젝트 자체를 조작하려는 경우에는 gameObject를 사용하는 것이 적절합니다.

기타

꼭 SOLID 원칙을 지켜야되는 건 아님. 확장성을 고려한다면 일부러 무시하기도 하는듯.

GameObject.FindObjectOfType<SpectatorMode>() // 이건 효율이 안 좋음

그냥 gameObject를 해도 되지만 this.gameObject를 하면 더 명확하게 GameObject랑 구분 가능

transform.SetParent(RemainingPlayers[0].transform);
자기 자신의 transform 접근할 땐 transform만 써도 됨
부모 지정하는 건 transform에서 관리

1차 코드

Debug.Log("Remaining Players: " + SpectatingPlayer.name); 이게 자기 자신이 찍히고 있었음.

자기 자신도 remove 해야 되는데 그걸 안해서 카메라가 옮겨가지 못했던 것.

그거 해결하니 일단 관전은 됐다.

using System.Collections.Generic;
using UnityEngine;

public class SpectatorMode : MonoBehaviour
{
    public List<GameObject> RemainingPlayers = new List<GameObject>();

    private GameObject SpectatingPlayer; // 내가 관전 중인 플레이어
    private bool isSpectating = false;

    private void UpdateSpectatingPlayer()
    {
        if (RemainingPlayers.Count == 0)
        {
            Debug.Log("No players to spectate");
            return;
        }

        // 플레이어가 죽었을 때 다음 플레이어로 변경
        if (SpectatingPlayer == null)
        {
            SpectatingPlayer = RemainingPlayers[0];
            Debug.Log("Remaining Players: " + SpectatingPlayer.name);
            transform.SetParent(RemainingPlayers[0].transform);
            transform.position = RemainingPlayers[0].transform.position;
            transform.rotation = RemainingPlayers[0].transform.rotation;
        }
        else
        {
            Debug.Log("your remaining player is null");
        }
    }

    public void StartSpectating()
    {
        isSpectating = true;
        Debug.Log("Start Spectating");
        RemainingPlayers.AddRange(GameObject.FindGameObjectsWithTag("Player"));

        Player p = NetworkManager.Instance.myPlayer; // 자기가 조종하고 있는 플레이어
        RemainingPlayers.Remove(p.gameObject);
        UpdateSpectatingPlayer();
    }

    public void RemovePlayer(GameObject player)
    {
        if (isSpectating)
        {
            RemainingPlayers.Remove(player);
            // 죽은 친구가 지금 관전 중인 친구면 카메라를 옮겨야 함
            if (player == SpectatingPlayer)
                UpdateSpectatingPlayer();
        }
    }
}

나는 rotation 따라가는 로직 안 구현했는데 초기화 이후에도 알아서 따라감.
카메라의 부모가 바뀌었으니 부모의 움직임을 알아서 따라가기 때문이라고 함.

개선하기

카메라의 부모 오브젝트를 따로 만들고 그 부모 오브젝트는 각 플레이어의 위치랑 동기화시켜야됨
마우스 클릭하면 관전하는 사람이 바뀌어야 함

https://artiper.tistory.com/121

아니 굳이 새로운 부모를 만드는게 아니라 그냥 카메라 부모만 옮기면 되는데?
왜 그렇게 생각해놓고 이상하게 하고 있었지?

내일 마저 할 것

0개의 댓글