Prediction

Network Transform의 Interpolation Data Source를 Predicted로 하고 Interpolation Space를 World로 한다.

포톤 퓨전 Prediction 이해를 위한 영상

- Interpolation Target을 분리해 놓은 이유 : Render는 유니티의 Update에서 불리고 Interpolation으로 계속 그려줘야한다. 그러나 이동 입력은 FixedUpdateNetwork에서 불리기 때문에 둘의 단위가 달라서 구분해 준 것이다.

TickTimer를 설정하기 위해 NetworkedAttribute의 기본 생성자인 [Networked] 를 속성으로 부여했다.

FixedUpdateNetwork에서 TIckTimer.Expired(NetworkRunner Runner)가 다 되면 NetworkRunner.Despawn(Network Object)으로 NetworkObject를 파괴하고 안 되면 앞 쪽으로 이동하게 한다.

Init() 함수에서 TickTimer.CreateFromSeconds(NetworkRunner runner, float delayInSeconds)로 Senconds에 대해 타겟의 Tick을 생성하고 반환한다.

public class Ball : NetworkBehaviour
{
    [Networked] TickTimer Life { get; set; }

    public override void FixedUpdateNetwork()
    {
        if (Life.Expired(Runner))
        {
            Runner.Despawn(Object);
        }
        else
        {
            transform.position += 5 * transform.forward * Runner.DeltaTime;
        }
    }

    public void Init()
    {
        Life = TickTimer.CreateFromSeconds(Runner, 5.0f);
        print(Life.TargetTick);
    }
}

PhotonInstantiate 스크립트

NetworkInputData.MOUSEBUTTON1로부터 mouseButton0을 Set해준다.

public void OnInput(NetworkRunner runner, NetworkInput input)
{
    var data = new NetworkInputData();

    if (Input.GetKey(KeyCode.W))
        data.direction += Vector3.forward;
    if (Input.GetKey(KeyCode.S))
        data.direction += Vector3.back;
    if (Input.GetKey(KeyCode.A))
        data.direction += Vector3.left;
    if (Input.GetKey(KeyCode.D))
        data.direction += Vector3.right;

    if (_mouseButton0)
    {
        data.buttons |= NetworkInputData.MOUSEBUTTON1;
    }

    _mouseButton0 = false;
    
    input.Set(data);
}

마우스 좌클릭으로 Ball을 생성하는데 시간차를 두기 위해 Networked속성을 적용한 TickTimer를 생성하여 적용하였다.

public class Player : NetworkBehaviour
{
    [SerializeField] Ball _prefabBall;
    [Networked] TickTimer delay { get; set; }
    
    NetworkCharacterControllerPrototype _cc;
    Vector3 _forward;

    void Awake()
    {
        _cc = GetComponent<NetworkCharacterControllerPrototype>();
        _forward = transform.forward;
    }

    public override void FixedUpdateNetwork()
    {
        if (GetInput(out NetworkInputData data))
        {
            data.direction.Normalize();
            _cc.Move(5 * data.direction * Runner.DeltaTime);
            
            // OnInput에서 Set된 NetworkInputData.direction 값이 0보다 클 때
            if (data.direction.sqrMagnitude > 0)
            {
                _forward = data.direction;
            }

            // NetworkRunner의 TickTimer가 도달했거나 세팅되지 않았을 때 -> 생성 빈도의 제한을 거는 것 
            if (delay.ExpiredOrNotRunning(Runner))
            {
                // NetworkInputData.MOUSEBUTTON1이 0x01이니 byte인 data.button이 1이고 &연산(둘 다 1일 때)으로 1이 될 때
                if ((data.buttons & NetworkInputData.MOUSEBUTTON1) != 0)
                {
                    delay = TickTimer.CreateFromSeconds(Runner, 0.5f);
                    // NetworkRunner의 바라보는 forward만큼 앞으로, 회전은 입력의 방향으로(data.direction의 방향) 
                    // Object.InputAuthority는 누구의 입력에서 나왔는지에 대한 PlayerRef이다(PlayerID를 포함한 정보).
                    // Spawn하기 전에 파괴될 틱을 설정해주어야 하기 때문에(Life.CreateFromSeconds(NetworkRunner, int delayInSeconds))
                    // 마지막의 람다식 delegate void OnBeforeSpawned(NetworkRunner runner, NetworkObject obj)에서 Ball의 Init을 호출한다.
                    Runner.Spawn(_prefabBall, transform.position + _forward, Quaternion.LookRotation(_forward),
                        Object.InputAuthority,
                        (runner, o) =>
                        {
                            print("Lambda init");
                            o.GetComponent<Ball>().Init();
                        });
                }
            }
        }
    }
}

 

시작하기

SDK와 프로젝트 세팅을 한다.

https://doc.photonengine.com/ko-kr/fusion/current/fusion-100/fusion-101

 

Fusion 101 - 시작하기 | Photon Engine

 

doc.photonengine.com

 

씬 설정하기

https://doc.photonengine.com/ko-kr/fusion/current/fusion-100/fusion-102

 

Fusion 102 - 씬 설정하기 | Photon Engine

 

doc.photonengine.com

 

포톤 퓨전은 INetworkRunnerCallbacks를 사용하여 동기화 작업을 하는 것 같다.

프로젝트를 따라가다가 async라는 개념이 나오는데 코루틴과 비슷한 개념이라고 한다.

async, await, Task를 이용하여 어떤 작업을 순차적으로 실행할 때, 동시에 실행할 때 코루틴 보다 간단하게 만들 수 있다.

https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/

 

Asynchronous programming in C#

An overview of the C# language support for asynchronous programming using async, await, Task, and Task

learn.microsoft.com

async void StartGame(GameMode mode)
{
    // GameMode
    // 1. 싱글 2. 쉐어드 - 포톤 클라우드 이용(사용자는 클라이언트) 3. 서버(게임 서버를 직접 지원하고 원격 플레이어만 허용)
    // 4. 호스트(로컬 플레이어를 허용하는 게임서버) 5. 클라이언트(호스트나 게임 모드에 클라이언트로 시작)
    // 6. 자동(첫 번째 접속시 호스트 모드, 나중 접속시 클라이언트 모드)
    
    // NetworkRunner : 플레이어와 관련된 변수들을 가지고있는 클래스
    _runner = gameObject.AddComponent<NetworkRunner>();
    // PlayerRef와 INetworkInput을 받는 여부
    _runner.ProvideInput = true;

    // NetworkRunner.StartGame(StartGameArgs args) : 매치메이킹 세팅을 해주는 메서드
    // 비동기로 NetworkRunner.StartGame(StartGameArgs args) 실행
    await _runner.StartGame(new StartGameArgs
    {
        GameMode = mode,
        SessionName = "TestRoom", // 세션 이름(클라이언트와 서버 세션 이름)
        Scene = SceneManager.GetActiveScene().buildIndex, // Scene은 구조체인 SceneRef?타입
        // NetworkSceneManagerDefault : 비동기 씬 관련 메서드가 포함된 클래스
        SceneManager = gameObject.AddComponent<NetworkSceneManagerDefault>(), // SceneManager INetworkSceneManager 타입이다.
    });
}

여기서 예시는 포톤 펀처럼 호스트와 클라이언트가 포톤 클라우드에 요청하고 응답받는 식으로 진행된다. (게임 모드에 따라 달라짐)

void OnGUI()
{
    if (!_runner)
    {
        if (GUI.Button(new Rect(0, 0, 200, 40), "Host"))
        {
            StartGame(GameMode.Host);
        }

        if (GUI.Button(new Rect(0, 40, 20, 40), "Join"))
        {
            StartGame(GameMode.Client);
        }
    }
}

Network Object

포톤 펀의 PhotonView처럼 고유한 ID를 가지고 있고 객체의 생성과 파괴 등을 추적한다.

https://doc.photonengine.com/ko-kr/fusion/current/manual/network-object/network-object

 

네트워크 객체 | Photon Engine

 

doc.photonengine.com

포톤 퓨전에는 Network Character Contoller Prototype처럼 이름 그대로 Prototype을 지원하는데 네트워크에서 Character Controller를 다루는 이동 관련된 부분을 미리 만들어 놓은 스크립트이다.

- NetworkTransform을 상속받는데 Transform의 위치를 동기화 시켜주는 메서드가 들어있다.

- InterPolation Target에 움직일 게임 오브젝트를 넣어주기만 하면 된다.

플레이어 세션 연결과 연결 해제시 콜백

- 생성과 파괴를 구현(딕셔너리에 저장하여 추적)

public void OnPlayerJoined(NetworkRunner runner, PlayerRef player)
{
    // 연결중이면
    if (runner.IsServer)
    {
        // 특정한 위치 저장
        Vector3 spawnPosition =
            new Vector3((player.RawEncoded % runner.Config.Simulation.DefaultPlayers) * 3, 1, 0);
        // NetworkRunner.Spawn이 네트워크 Instantitate와 비슷한 역할인데 추가로 PlayerRef를 받는다.
        NetworkObject networkPlayerObject = runner.Spawn(_playerPrefab, spawnPosition, Quaternion.identity, player);
        // 딕셔너리에 추가
        _spawnedCharacters.Add(player, networkPlayerObject);
    }
}

public void OnPlayerLeft(NetworkRunner runner, PlayerRef player)
{
    if (_spawnedCharacters.TryGetValue(player, out NetworkObject networkObject))
    {
        // 파괴 후 반환
        runner.Despawn(networkObject);
        _spawnedCharacters.Remove(player);
    }
}

클라이언트가 직접 네트워크 객체의 상태를 바꾸는 것이 아닌 호스트가 클라이언트로부터 입력을 받아 다음 상태를 예측하여 갱신하는 식으로 작동한다.

유저의 입력을 받기위해 입력을 담을 공간을 만들어주어야 한다.

INetworkInput을 상속받는 NetworkInputData에 public 변수를 만들고 인스턴싱해 키 입력을 받아놓고(여기선 Vector3) 인자로 받은 NetworkInput에 Set해주는 방식으로 플레이어의 데이터 입력을 저장한다.

public struct NetworkInputData : INetworkInput
{
    public Vector3 direction;
}
public void OnInput(NetworkRunner runner, NetworkInput input)
{
    var data = new NetworkInputData();

    if (Input.GetKey(KeyCode.W))
        data.direction += Vector3.forward;
    if (Input.GetKey(KeyCode.S))
        data.direction += Vector3.back;
    if (Input.GetKey(KeyCode.A))
        data.direction += Vector3.left;
    if (Input.GetKey(KeyCode.D))
        data.direction += Vector3.right;
    
    input.Set(data);
}

MonoBehaviour를 상속받는 퓨전의 컴포넌트로 입력을 틱 단위로 받기 위해 FixedUpdateNetwork에 GetInput<T>(out T input)으로 입력이 들어왔는지 확인한다.(입력이 들어오면 true를 안 들어오면 false를 반환)

* 이전에 입력을 담을 변수를 정의한 NetworkInputdata가 out으로 지역변수로 사용되어 data에 접근하여 방향을 Normailize하고 그 값을 NetworkCharacterControllerPrototype의 Move에 담아 이동시킨다.

- Runner는 NetworkBehaviour을 상속하는 SimulationBehaviour의 변수이다. (NetworkRunner 클래스)

- Runner.DeltaTime은 FixedDeltaTime처럼 고정된 DeltaTime이다.

public class Player : NetworkBehaviour
{
    NetworkCharacterControllerPrototype _cc;

    void Awake()
    {
        _cc = GetComponent<NetworkCharacterControllerPrototype>();
    }

    public override void FixedUpdateNetwork()
    {
        if (GetInput(out NetworkInputData data))
        {
            data.direction.Normalize();
            _cc.Move(5 * data.direction * Runner.DeltaTime);
        }
        
    }
}

'공부 모음 > 포톤 퓨전 시작해보기' 카테고리의 다른 글

포톤 퓨전 원격 프로시져 호출(RPC)  (0) 2022.12.09
포톤 퓨전 속성 변경  (0) 2022.12.09
포톤 퓨전 물리  (2) 2022.12.08
포톤 퓨전 Prediction  (0) 2022.12.08

배운 내용

플레이펩의 클라우드 스크립트

서버 측에서 실행하는 것들 (공지 등)을 위한 스크립팅이다.

코드 변조를 일부 막을 수 있고 콜백 지연 등의 문제가 해결된다.

유니티 내의 클라우드 스크립트 실행 요청

클라우드 스크립팅을 통하여 서버에서의 로직 실행으로 플레이어에게 돈을 부여하거나 할 수 있다.

 

협업 툴

Agile / Scrum 서비스 (방법론)

  • Jira : PM용 툴
    • 이슈 등록
      • 담당자 : 개발자
      • 보고자 : 상급자
      • 관찰자 : 참조
  • Confluence : 노션 같은 것(협업 문서 작성)

코드 관리

  • GitHub
  • BitBucket
  • GitLab

CI(Continous Integration)

  • Jenkins : 빌드 자동
  • 유지보수
  • 배포용

 

아틀라시안

  • Jira + Confluence

https://www.atlassian.com/ko

 

Atlassian | 소프트웨어 개발 및 협업 도구

전 세계 수백 만 사용자가 Atlassian 제품을 이용해 소프트웨어 개발과 프로젝트 관리, 협업, 코드 품질을 개선하고 있습니다.

www.atlassian.com

 

Git

Git Merge 충돌 시 생각해야할 것 3가지 Merge Option

  • 두 파일의 내용 중 선택적으로 가져오는 경우(겹치는 부분에서 선택적으로 가져온다.)
  • 두 파일의 내용을 전부 가져오는 경우
    • 유니티로 치면 meta파일 - Setting값, Component 값을 가져와야하는데 한 프리펩에 서로 다른 컴포넌트를 같이 넣은 경우 등 이 때는 메타파일을 설정하여 둘 다 가져오게 해야한다. (아니면 한 쪽이 없어짐)
    • xcode xc workspace
      • id 비번 MD5 Hash 값이 적힌다. → 적는 것을 무시하게 해야됨
  • 두 파일의 내용 중 한 쪽만 가져오는 것 (Stach, Discard)

Conflict 경우

  • 코드가 다를 경우
  • 진행 차이가 날 경우
    • Above
    • behind
      • MergeTool 사용 → 라인으로 따옴

Stash 경우

  • local과 server가 다른경우

Revert

  • Commit을 되돌림(잘못된 행동,커밋)

Ammend

  • Commit 수정(메시지/로그)

 

튜토리얼

https://learngitbranching.js.org/?locale=ko 

 

Learn Git Branching

An interactive Git visualization tool to educate and challenge!

learngitbranching.js.org

 

개인 프로젝트

- 숨바꼭질을 주제로 하는 게임

- 서버로 포톤 PUN2 사용

- 플레이펩을 통한 로그인, 로비에서의 룸 들어가기까지 완료

- 게임 씬은 진행 중

 

 

한 주 후기

 이번 주는 플레이펩의 클라우드 스크립트부터 각 협업 툴들에 대해 배웠다. 클라우드 스크립트는 클라이언트의 재화 등 변조 위협이 있는 것들을 서버에서 로직을 처리하여 방지할 수 있다. 협업 툴 중에 Jira는 아틀라시안에서 제공하며 이슈를 등록하고 관찰자 보고자 담당자 보고자 관찰자로 나누어 이슈 해결을 진행한다. 그리고 Git은 로컬 저장소에 있는 코드을 원격 저장소와 연동하는 것인데 여러 경우를 다루어야 한다.

 지금까지 개인 프로젝트를 하면서 어려움이 많았다. 포톤 PUN의 UI는 따로 원격과 상태를 맞추어주어야 하는데 룸 리스트UI와 플레이어 리스트UI에서 꽤 시간을 쓴 것 같다. 룸 리스트는 OnRoomListUpdated를 이용하여 해결하였고 플레이어UI 갱신은 OnPlayerEnteredRoom과 OnPlayerLeftRoom을 활용하여 해결하였다. 이런 함수들을 내가 주도적으로 사용해본 적이 없어서 좀 어려웠는데 이번 기회로 익숙해져서 다른 사용이 있을 때 잘 활용할 수 있을 것 같다.

 이번 주에 많은 새로운 내용들을 배우니 재밌었다. 현업에서도 많이 사용하는 Jira나 Confluence는 처음 접해봐서 신기하기도 했다. Git은 GitHub 클라이언트인 Git Desktop만을 사용해 보았는데 Git 튜토리얼 사이트인 LearnGitBranching에서 Git에 대한 사용법을 배워보니 쉽게 접근할 수 있어서 좋았다. 다음에도 이런 유익한 내용들을 배웠으면 좋겠다. 다음 주도 파이팅!

 

 

유데미코리아 바로가기

본 포스팅은 유데미-웅진씽크빅 취업 부트캠프 유니티 1기 과정 후기로 작성되었습니다.

배운 내용

포톤의 IPunPrefabPool을 이용하여 오브젝트 풀을 만들 수 있다.

Resources.Load 대신에 PrefabList에 생성할 프리펩을 넣어놓고 저장하는 방식이다.

IPunPrefabPool의 Instantitate는 기존의 유니티의 GameObject.Instantiate를 래핑하여 대신 사용하는 것이다.

플레이펩의 Economy에 들어가서 플레이펩 서버에 등록되는 게임 돈, 실제 돈과 관련된 것들을 관리할 수 있다.

아이템을 만들고 설정하거나

게임에 사용되는 가상통화를 만들 수 있다.

또한 아이템의 통화적 가치를 결정하거나

특정 플레이어에게 아이템을 줄 수도 있다.

 

 

한 주 후기

 이번 주는 포톤의 IPunPrefabPool과 플레이펩의 통화에 대해서 배웠다. 포톤에서의 IPunPrefabPool은 생성과 파괴가 반복되는 상황이 많을 때 오브젝트 풀처럼 활용이 가능해 과부하를 줄여준다.

 포톤을 사용한 프로젝트를 만들면서 해결하기 어려웠던 문제가 있었는데 플레이어가 룸에 들어오고 레디를 눌렀을 때 체크하는 것이 쉽지 않았다. 처음엔 OnJoinedRoom에서 처리를 하면 되겠거니 했는데 OnJoinedRoom은 로컬인 해당 플레이어가 입장했을 때만 호출되고 다른 플레이어가 방에 들어왔을 때는 OnPlayerEnteredRoom으로 호출해야 한다는 것을 알고 그 방식으로 처리했다. 그런데 아직 로컬에서만 실행된다는 문제가 있어 여러 방법을 검색하다가 커스텀 프로퍼티를 이용하여 다른 플레이어에게 레디 상태를 전달하는 방식으로 해결하게 되었다.

 포톤은 공식 문서에 있는 튜토리얼을 따라가 보았지만 튜토리얼은 튜토리얼일 뿐 응용하는 방법은 그 때마다 찾아서 배워야한다는 것을 알았다. 아직 모르는 부분이 더 있겠지만 그 때마다 작게작게 일을 나누어 해결해 나가야겠다. 다음 주도 파이팅!

 

 

유데미코리아 바로가기

본 포스팅은 유데미-웅진씽크빅 취업 부트캠프 유니티 1기 과정 후기로 작성되었습니다.

 

배운 내용

PlayFab

마이크로 소프트에서 제공하는 온라인 서버로 PlayFab이라는 것이 있다.

https://playfab.com/

 

Full Stack LiveOps, Real-Time Control

PlayFab is a suite of products that complement your existing backend infrastructure. Mix and match to meet your needs, or adopt the entire platform as a powerful base for current and future games.

playfab.com

유니티 3D용 API도 있으며 해당 사이트에서 다운이 가능하다.

포톤과 연동하여 Playfab에서는 데이터를 관리하고 Photon에서는 방 관리를 하는 등 사용하는 방법이 Docs에 안내되어 있다.

홈페이지에서 계정을 생성하고 패키지를 설치한 후 유니티에서 Playfab에 로그인해보면 설정이 완료된 것이다.

아래처럼 데이터를 요청하여 PlayFab에 이메일과 비밀번호 정보를 저장하고 등록된 이메일만 로그인 할 수 있게 만들 수 있다.

위의 코드로 등록하는 UI이다.

로그인 할 때 여기서 등록한 정보로만 로그인이 가능하다.

여기서 서버로 올라온 데이터에 대한 관리가 가능하다.

 

 

한 주 후기

 이번 주는 유니티에서 사용 가능한 서버 툴인 PlayFab에 대해 배웠다. PlayFab은 멀티 서버에서 데이터 관리가 용이한 툴이고 무료 버전이라는 점에서 좋다. 그리고 포톤과 연동이 가능하여 아예 포톤과 어떻게 같이 잘 사용할 수 있는지에 대한 튜토리얼도 Docs에 제공하고 있어 이용하기 쉽다. 이를 이용하여 랭킹 시스템이나 사용자의 로그인 정보 관리 등 여러가지에 이용할 수 있다.

 플레이펩을 이용하면서 좋았던 것이 원래는 포톤 서버를 이용할 때 데이터 관리가 어려웠었다. 포톤 Docs에서도 그냥 유니티 내장 라이브러리인 PlayerPrefs를 이용하여 로컬 레지스트리에 데이터를 올리는 형식으로 (간단한 데이터만 처리) 데이터 관리를 했었는데 PlayFab이라는 것을 이용하여 더 많은 데이터를 유용하게 관리할 수 있다는 점이 좋았다. 처음 사용하는 것은 사실 PlayerPrefs보다는 어렵지만 마이크로소프트에서 제공하는 튜토리얼도 있어서 따라해보기 좋은 것 같다.

 다음 주에는 본격적으로 포톤과 PlayFab을 연결하는 작업들을 한다고 한다. 수업시간이 아닐 때도 튜토리얼을 따라가보는 시간을 좀 가져야겠다고 생각했다. 다음 주도 파이팅!

 

 

유데미코리아 바로가기

본 포스팅은 유데미-웅진씽크빅 취업 부트캠프 유니티 1기 과정 후기로 작성되었습니다.

 

배운 것들

이번 주는 어몽어스의 벤트, 투표 로직에 대해 배웠다.

Grid Layout Group을 통해 자식의 UI 위치를 정렬 할 수 있다.

이후에 자식 오브젝트 중 하나 빼고 다 지우고 자식 오브젝트를 작업한 후 프리펩화 하고 복사하여 사용하면 효율적으로 배치가 가능하다.

LocalPlayer의 ActorNumber는 계속 증가한다.

PhotonNetwork.PlayerList를 불러와서 로컬 플레이어의 ActorNumber와 비교하는데 플레이어 리스트의 ActorNumber와 같으면 몇 번째인지 PlayerID에 i를 대입하여 (스폰 포인트를 초과하지 않도록) 생성한다.

mSpawnPoints[PhotonNetwork.LocalPlayer.ActorNumber % 8]은 중복때문에 ActorNumber가 증가하면서 다른 플레이어와 중복된 위치에 생성될 수 있다.

PhotonNetwork.CurrentRoom.Players가 딕셔너리인 것을 이용하여 foreach문을 돌면서 딕셔너리의 Value인 Player의 ActorNumber마다 해당 actorNumber를 비교하여 같으면 플레이어의 닉네임을 넣어주는 로직이다.

PhotonNetwork.CurrentRoom.IsOpen = false; 를 이용하여 게임이 시작하고 룸에 못 들어오게 할 수 있다.

애니메이션의 Speed를 -1로하면 (기본 값 1) 거꾸로 실행시킬 수 있는데 이를 이용하여 벤트에 들어가는 애니메이션만 만들어도 벤트로 나오는 애니메이션은 Speed를 -1로해 따로 만들지 않아도 된다.

애니메이션의 Can Transition To Self를 체크해제하면 바로 자기 자신으로 넘어오는 것을 막을 수 있다.

 

한 주 후기

 이번 주는 벤트와 투표를 구현해보는 시간을 가졌다. 포톤 중 플레이어에 대해서 관리하는 법을 많이 다뤘던 것 같다.

 수업을 따라가면서 버그가 많이 발생했었는데 자가적으로 버그를 고쳐보는 시간이 있었다. 벤트를 타는 중에 아무리 코드로 Collider에 접근해 끄려고 해도 꺼지지 않았는데 처음엔 포톤 동기화 문제인 줄 알았다가 인스펙터에서 수동으로 끄려고 해도 안 꺼지는 것을 보고 그건 아니겠구나 싶었다. 그래서 여러 시도를 통해 애니메이션 부분에서 문제가 있던 걸 알게 되었다. 애니메이션에서 벤트에 들어갈 때 Collider의 enable을 false하는 부분이 있었는데 유니티의 라이프사이클 상 코드 실행보다 애니메이션의 실행이 더 늦게 되므로 덮어씌워져서 접근할 수 없던 문제였다. 그래서 콜라이더를 끄는 작업은 코드로 분리시켜서 해결하게 되었다.

 버그를 해결하는 작업은 답답하고 어렵지만 해결한다면 성취감이 느껴지는 일인 것 같다. 하나하나 콘솔로 찍어가면서, 코드의 흐름을 따라가고 문제인 부분을 찾는 것이 어렵지만 그 이후는 비교적 쉬운 것 같다. 그리고 이를 정리하고 다음에 적용하는 부분에서 내가 발전했다는 생각도 든다. 다음에도 재밌게 수업을 들었으면 좋겠다. 다음 주도 파이팅!

 

 

유데미코리아 바로가기

본 포스팅은 유데미-웅진씽크빅 취업 부트캠프 유니티 1기 과정 후기로 작성되었습니다.

배운 것들

먼저 어몽어스를 포톤에 연결했다.

이번엔 새로운 것을 배웠는데 OnPhotonSerializeView라는 것이었다. 로컬 변수를 포톤으로 보내 포톤이 다른 유저들에게 전파하여 동기화하는 방식이다.

PhotonNetwork.Instantiate는 네트워크 객체를 생성하는 함수인데 첫 번째 인자로 String이 들어가서 System.IO의 Path.Combine을 이용하여 폴더를 지정할 수 있게끔 했다.

gameObject.SetActive(bool value)는 bool값에 따라 게임 오브젝트를 킬지 끌지를 정하는 함수이다.

어몽어스 캐릭터 동기화 중에 Null오류가 떴었다. PhotonNetwork.Instantiate의 순서상의 문제였다.

이는 myAvatarSprite가 Start에서 불리는데 OnPhotonSerializeView와 Start의 순서가 어느 것이 먼저 실행될 지 모르기 때문에 Awake에서 avatar와 myAvatarSprite를 가져오는 것으로 해결했다.

카메라의 CullingMask는 카메라가 특정 레이어만 보이게 할 수 있는 기능이다.

코드에서 비트 연산을 이용하여 여러개 마스크를 설정해보았다.

nickText.text = _pv.Owner.NickName에서 텍스트에 초반에 설정한 포톤뷰 주인의 닉네임을 넣어주었다.

포톤 챗도 사용하였다.

IChatClientListener라는 인터페이스를 통해 채팅 기능을 구현할 수 있다.

 

한 주 후기

 이번 주는 포톤 펀의 동기화와 포톤 챗을 배워보는 시간을 가졌다.

 포톤 펀의 동기화는 RPC함수를 통해서도 가능하지만 실시간으로 변화하는 것을 추적해야하는 상황이면 OnPhotonSerializeView를 사용해야한다는 것을 알게 되었다. 포톤 펀을 배우면서 가장 어려웠던 점은 포톤의 동기화 시점과 유니티의 생명주기를 잘 고려해서 코드를 짜야된다는 것이다. 위에서 언급했듯 아바타 스프라이트를 찾는 시점이 포톤 객체가 생성되기 전이어서 계속 null이 뜨는 것을 몰라서 시간을 많이 소비했던 것 같다. 포톤 펀에 익숙해지기 위해서 지금 하고있는 다른 프로젝트에도 멀티로 바꿔보는 연습을 해야겠다는 생각이 들었다.

 다음 주에는 어떤 것을 배울 지 궁금하다. 적으면서 배우고 필요할 때 다시 꺼내서 써보는 시간을 자주 가져야겠다는 생각이 들었다. 다음 주도 파이팅!

 

 

유데미코리아 바로가기

본 포스팅은 유데미-웅진씽크빅 취업 부트캠프 유니티 1기 과정 후기로 작성되었습니다.

배운 것들

포톤을 적용시키기 전에 어몽어스 기본 구조를 구현해보는 시간이 있었다.

먼저 스프라이트를 수정해줬다.

Sprite Mode를 Multiple로 하고 Slice에서 GridByCellSize로 128씩 잘랐다.

그리고 2D에서 눈과 몸통으로 나누어 세팅을 했는데 플레이어마다 색을 다르게 표현해야하므로 그런 것이다.

이후 새로운 InputSystem을 설치하였다.

Input System을 Player Input 사용이 아닌 코드에서 동적으로 사용하는 법을 배웠는데 사용하려면 먼저 using InputSystem을 한 뒤 [SerializeField] 속성으로 InputAction을 선언해주어야 한다.

이렇게 SerializeField로 선언하면 컴포넌트에 바인딩 옵션이 뜨게 된다.

그리고 InputAction을 유니티 기본 이벤트인 OnEnable에 Enable해주고 OnDisable에 Disable해준다.

Mathf.Sign(float f)는 음수면 -1 양수면 1을 반환하는 함수이다.

InputAction에서 ReadValue로 바인딩한 Vector2값을 불러왔는데 2D의 Scale을 조정했을 때 좌우측 스프라이트가 반전되는 것을 이용한 코드이다.

캐릭터의 색을 바꾸는 과정에서 Color를 배열로 선언하고 SerializeField를 하면 스포이드 버튼으로 색을 옮길 수 있다는 것을 배웠다.

* 이런 식으로 참조하면 알파 값이 0이 되어있으니 주의!

 

LightShafting

외부 플러그인인 LightShafting도 사용해보았다.

https://github.com/ckawell/LightShafting

 

GitHub - ckawell/LightShafting: A simple line-of-sight lighting system for 2D Unity Games.

A simple line-of-sight lighting system for 2D Unity Games. - GitHub - ckawell/LightShafting: A simple line-of-sight lighting system for 2D Unity Games.

github.com

Ignore Me는 이 Layer의 객체는 빛 반사가 안 일어나는 것이고

Get Radius는 범위이다.

Wall Mask은 이 객체는 빛 반사가 일어난다.

Light Rays는 카메라에 TargetTexture를 입힌 Material을 가지고 있는 객체를 통해 Ray를 쏘는 것이다.

* 3D환경에서만 동작한다고 한다. 그래서 이 프로젝트는 3D Collider를 붙이고 작업했다.

 

 

한 주 후기

 이번 주에는 포톤 어몽어스를 만들기 위해 어몽어스를 기본적으로 구현해보는 시간을 가졌다.

 어몽어스가 되게 간단한 게임인줄로만 알았는데 막상 구현을 따라해보니 쉽지 않았던 것 같다. 마피아 역할일 때 자신의 범위 내에 다른 사람이 들어와 있는지 체크하는 것과 거기에서 여러 사람이 있을 땐 어떻게 할 것인지 또 유령이 되었을 때는 신경써줘야할 것이 어떤 것인지 등등 해줘야할 게 많은 게임이었다. 또 다른 어려웠던 점은 새로운 플러그인에 익숙해지는 것이었다. LightShafting은 정말 좋은 툴이지만 아직 제대로 이해하고 쓰진 못한 것 같아 아쉬웠다. 다음에 다른 프로젝트를 할 때 더 이용해보면서 완전히 이해하도록 했으면 좋겠다고 생각했다.

 다음 주에는 포톤과 완전한 연동을 한다고 하는데 연동할 부분이 한 두개가 아니라서 쉽지는 않을 것 같다. 하지만 새로운 것을 배우는 것은 항상 재밌기 때문에 열심히 해보도록 할 것이다. 다음 주도 파이팅!

 

 

유데미코리아 바로가기

본 포스팅은 유데미-웅진씽크빅 취업 부트캠프 유니티 1기 과정 후기로 작성되었습니다.

배운 것들

JSON

class → json

obj를 인자로 받아 string을 반환한다.

float이 부동소수점이 찍혀 나오는데 다시 변환하여 사용할 때는 상관이 없다.

class → json → class

j를 인자로 하고 제너릭 형식으로 T를 반환한다.

Save

수정하면 적용되긴 하는데 오브젝트에 붙어있는 public변수라 인스펙터에서 수정해줘야된다.

Load

벡터 3의 순환참조 문제 - normalized에서 normalized를 부를 수 있는 문제 때문에 오류가 발생한다.

- gameObject.gameObject → 순환 참조 문제

* 그래서 이 블로그에선 따로 Vector3의 좌표를 저장할 클래스를 만들어줘서 사용하면 된다고 한다.

https://wergia.tistory.com/m/332

 

[Unity] JSON

개발단에 가입하여 베르의 게임 개발 유튜브를 후원해주세요! 베르의 게임 개발 유튜브 안녕하세요! 여러분들과 함께 게임 개발을 공부하는 베르입니다! 게임 개발에 도움이 되는 강좌들을 올

wergia.tistory.com

Save에서(Wirte) FileMode가 Open이나 OpenOrCreate 상태이고

Json 내부 변수 값의 길이가 변경됐을 때 json 파일 내부에서 이전 값이 남아있는 경우가 생긴다.

이는 FileMode를 Create로 바꾸면 해결 된다.

 

Mirror

빈 오브젝트에 Network Manager와 NetworkManagerHUD를 추가한다.

HUD

Client Authority : 클라이언트 정보 옮기기

이후 Player를 프리펩화 하고

이후 NetworkManager에 Player 프리펩을 넣는다.

Network Identity

  • Server Only : 서버에만 생성됨

Network Transform : 위치 동기화

  • Client Authority : 클라이언트로부터 정보가 와서 움직인다면 true 해야됨(NPC는 false)
  • Send Rate : 프레임

  • Host(Server + Client) : Server겸 Client라 방도 생성하고 큐브도 생성됨
  • Client : 큐브만 생성됨
  • Server Only : 방만 생성함

* 한 컴퓨터에서 여러 개의 서버를 생성하려고 하면 오류가 뜬다.

 

포톤

https://www.photonengine.com/ko-KR/

 

글로벌 크로스 플랫폼 실시간 게임 개발 | Photon Engine

 

www.photonengine.com

Photon View : Network Identity와 비슷한 역할

RPC로 부르고 싶은 함수들을 호출할 때 PunRPC 속성을 붙여준다.

 

 

한 주 후기

 이번 주에는 데이터관리 할 때 쓰는 json과 멀티 플레이를 위한 서버인 Mirror, 포톤에 대해서 배웠다.

 json은 딕셔너리처럼 key와 value값이 있어 데이터를 편하게 관리가 가능하고 JsonUtility로 유니티와 json 파일간의 변환이 가능하다. 파싱 하는 부분만 잘 이해하면 사용하기 어렵지 않은 것 같았다. Mirror는 GUI가 잘 구성되어있어 그냥 버튼 하나로도 호스트 생성이 가능해서 신기했다. 접근성면에서 좋았던 것 같다. 포톤은 해보면서 AppID1001오류와 이해하기 RPC 등 이해하기 어려운 부분들이 좀 많아서 따로 공부해야겠다는 생각이 들었다. 포톤 코리아나 포톤 튜토리얼을 보면서 공부해서 이번에 확실히 이해하고 넘어가야겠다는 생각이 들었다.

 이번 주 내용이 데이터 관리와 서버라서 확실히 이해하는 게 쉽지 않았다. 포톤 튜토리얼이 한글화도 잘 되어있고 단계별로 구성이 잘 되어있어서 틈틈히 공부해봐야겠다는 생각이 들었다. 다음 주에는 포톤을 이용한 어몽어스 같은 마피아 게임을 만들기로 했는데 재밌을 것 같다. 다음 주도 파이팅!

 

 

유데미코리아 바로가기

본 포스팅은 유데미-웅진씽크빅 취업 부트캠프 유니티 1기 과정 후기로 작성되었습니다.

배운 것들

파티클 시스템 복습

https://docs.unity3d.com/kr/2021.1/Manual/class-ParticleSystem.html

 

파티클 시스템 - Unity 매뉴얼

Particle System 컴포넌트는 씬 안에 다수의 작은 2D 이미지를 생성하고 애니메이션하여 액체, 구름 및 불꽃 같은 유체 엔티티를 시뮬레이션합니다. 파티클 시스템과 그 용도에 대한 전체 개요는 파

docs.unity3d.com

Play Restart Stop

Simulate layers를 레이어를 설정하고 동일하게 맞추면 하나만 시연되지 않고 같이 시연된다.

Resimulate를 키면 변경사항이 바로 적용된다.

Show Bounds를 하면 영역이 보인다.

여러 파티클이 부모 자식 관계일 때 해당 파티클만 재생하려면 Show Only Selected를 체크해주면 된다.

Open Editor를 누르면 계층 구조의 파티클을 다 볼 수 있다.

 

Collision : 충돌

  • Type
    • Plane : Plane에 따라 충돌함
    • World : 월드에 있는 벽이나 바닥에 충돌함
  • Dampin : 부딪히면 마찰로 느려짐
  • Bounce : 부딪히면 튀겨나옴 1이면 속도가 안줄어듬
  • LifeTime Loss : 부딪히면 수명이 줄음

Effect Texture Maker

https://mebiusbox.github.io/contents/EffectTextureMaker/

 

EffectTextureMaker

 

mebiusbox.github.io

위의 사이트에서 파티클 Sheet을 받아서 Material에 입혀주고

Texture Sheet Animation을 활성화하여 Renderer에 텍스처를 넣고 Grid를 해당 스프라이트처럼 만들어준다.

 

VFX

VFX 작동을 위해 SRP가 되는 URP 프로젝트를 만들어준다.

https://unity.com/visual-effect-graph

 

Unity Visual Effect Graph

Inspired by leading film tools, the Visual Effect Graph, a node-based VFX editor, lets artists author visual effects simulated directly on the GPU in real-time.

unity.com

VFX Graph

  • Fixed Delta Time : 키면 FixedDeltaTime 끄면 Deltatime
  • Exact Fixed Time : Fixed Delta Time이 켜졌을 때만 해당, 정확한 Fixed Time으로 부하가 많이 듬
  • Ignore Time Scale : 체크하면 파티클의 정상적인 스케일로 움직임
  • Culling Flags
    • 눈에 보일 때만 파티클 동작
    • 파티클 범위
    • 항상 동작
  • PreWarm : 미리 동작
  • OutputRender Order : 표시되는 순서

파티클 안나올 시 Rebuild And Save All VFX Graphs를 누르면 복구된다.

  • 아래의 사진에서 위의 정지 버튼은 생성 된 것 포함해서 없어지고 아래의 Stop은 생성이 안 되는 것이다.
  • Rate는 속도이다.

그래프 Open시

색 변경

외의 설정들은 파티클 시스템과 비슷하다.

 

Terrain

https://docs.unity3d.com/Manual/script-Terrain.html

 

Unity - Manual: Terrain

Creating and editing Terrains Terrain The Unity Editor includes a built-in set of TerrainThe landscape in your scene. A Terrain GameObject adds a large flat plane to your scene and you can use the Terrain’s Inspector window to create a detailed landscape

docs.unity3d.com

Terrain의 소스가 되는 Terrain 에셋을 다운받아 사용할 수 있다.

Window - Terrain - Terrain Toolbox의 Create로 Terrain 생성이 가능하다.

또는 Hierarchy창에 우클릭 - 3D Object - Terrain으로도 생성이 가능하다.

Create Neighbor Terrain : 인접하는 Terrain생성 가능

Brush Mode

  • Strength : 강도
  • Szie : 크기
  • Rotation 회전

Brush Spacing : 드래그 시 그려지는 간격
Scatter : 흩어져서 생성

Paint Texture로 지형에 텍스처를 입히고 레이어를 추가해 칠할 수 있다.

Grass

Tree

 

Poly Brush

Sculpt : 돌출 함몰 조절

Smooth : 표면을 부드럽게 만듬

Paint - Material을 입혀서 색칠함

Texture Blend - 쉐이더를 PolyBrush Blend로 하고 Textuure를 입힌다.

다른 Texture의 타일링은 적용이 안 되고 Main Texture의 Tiling만 적용됨

Scatter Prefabs : Prefab을 등록하여 오브젝트에 붙일 수 있다.

 

 

한 주 후기

 이번 주에는 파티클 시스템의 복습과 파티클 시스템의 대규모 버전인 VFX의 간단한 실습 그리고 지형을 꾸미는 방법 중 Terrain, Poly Brush를 사용해보는 시간을 가졌다.

 VFX는 파티클 시스템에 비해 더 멋진 표현들을 쉽게 가능하게 한다는 점이 좋았다. 대신 SRP를 지원하는 프로젝트에서만 가능해서 따로 SRP를 구축하지 않는다면 일반 프로젝트에선 파티클 시스템을 사용해야 한다는 점이 있었다. Terrain을 사용하면서 느낀 점은 직관적인 기능들을 사용하여 쉽게 지형을 괜찮게 보이도록 만들 수 있지만 많이 꾸미면 꾸밀수록 특히 나무나 풀을 표현하는 것에서 메모리를 많이 잡아먹는다는 단점이 있었다.

 다음 주에는 Mirror라는 서버를 열 수 있는 플러그인을 사용해본다고 한다. Mirror라는 것을 처음 사용해보기 때문에 정말 기대가 된다. 다음 주에는 더 집중해서 들어봐야겠다.

 

 

유데미코리아 바로가기

본 포스팅은 유데미-웅진씽크빅 취업 부트캠프 유니티 1기 과정 후기로 작성되었습니다.

+ Recent posts