대마법사의 개발 블로그랄까?

Unity 멀티 Photon PUN 본문

Unity 한테 정복당하기

Unity 멀티 Photon PUN

대마법사(진) 2025. 3. 23. 22:18

유니티에서 멀티게임을 구현하는 방법중 하나인 Photon PUN2 를 이용해 구현을 해보겠다.

 

세팅하기

우선은 에셋스토어에서 PUN 2- Free를 임포트 해준다.

https://assetstore.unity.com/packages/tools/network/pun-2-free-119922

 

PUN 2 - FREE | 네트워크 | Unity Asset Store

Get the PUN 2 - FREE package from Photon Engine and speed up your game development process. Find this & other 네트워크 options on the Unity Asset Store.

assetstore.unity.com

 

 

1. 다운을 받는동안 Photon 회원가입을 한다.

2. 회원가입을 한후 APP을 등록을 해준다.

 

등록된 APP

 

대쉬보드에 보면 새로 생성한 APP의 정보가 있을거다 

요기의 APP ID를 복사하여 유니티에 PUN2가 설치가 완료되면 붙여넣기를 한다.

(이 정도는 금방금방 하시겠지)

 

자세한 메뉴얼은 https://doc.photonengine.com/pun/current/getting-started/initial-setup

 

Pun 2 - Setup And Connect | Photon Engine

Photon Unity Networking (PUN) is really easy to setup. Import PUN into a new project and the PUN Wizard will pop up. Alternatively it's in the menu: "

doc.photonengine.com


자 그러면 일단 준비는 대충 끝났다.

빠르게 테스트 해보자

 

NetworkManager

public class NetworkManager : MonoBehaviourPunCallbacks
{
    // Start is called once before the first execution of Update after the MonoBehaviour is created
    void Start()
    {
        PhotonNetwork.ConnectUsingSettings();
    }

    // Update is called once per frame
    void Update()
    {
        
    }
    public override void OnConnectedToMaster()
    {
        Debug.Log("Connected to Master");
        PhotonNetwork.JoinLobby();
    }
}

1. PhotonNetwork.ConnectUsingSettings() 세팅하고

2. PhotonNetwork.JoinLobby() 로비 들어가고

 

 

RoomManger

방을 파고 들어갈 수 있게끔 해야겠쥬?

using Photon.Pun;
using Photon.Realtime;
using System.Collections.Generic;
using TMPro;
using UnityEngine;

public class RoomManager : MonoBehaviourPunCallbacks
{
    [SerializeField] private TextMeshProUGUI roomItem;

    /// <summary>
    /// 랜덤 방 들어가기
    /// </summary>
    public void JoinRoom()
    {
        PhotonNetwork.JoinRandomRoom();
    }

    /// <summary>
    /// 방만들기
    /// </summary>
    public void CreateRoom()
    {
        PhotonNetwork.CreateRoom("Test", new RoomOptions { MaxPlayers = 3 });
        Debug.Log("Count of Rooms" + PhotonNetwork.CountOfRooms);       
    }
    
    /// <summary>
    /// 새로운 방이 파지거나 업데이트 될 때 호출
    /// </summary>
    /// <param name="roomList"></param>
    public override void OnRoomListUpdate(List<RoomInfo> roomList)
    {
        base.OnRoomListUpdate(roomList);
        foreach (var room in roomList)
        {
            GameObject roomText = Instantiate(roomItem.gameObject, roomItem.transform.parent);
            roomText.GetComponent<TextMeshProUGUI>().text = room.Name;
        }
        roomItem.gameObject.SetActive(false);

        Debug.Log("Room List Updated " + roomList.Count);
    }
    
    /// <summary>
    /// 방에 정상 접속하였을때
    /// </summary>
    public override void OnJoinedRoom()
    {
        Debug.Log("Joined Room");
        PhotonNetwork.Instantiate("Player", Vector3.zero, Quaternion.identity);
    }
}

 

중요

플레이어블 캐릭터는 Object.Instantiate가 아닌 PhotonNetwork.Instantiate()로 생성해야한다.

PhotonNetwork.Instantiate() 로 생성할 Prefab은 Resources 폴더에 있어야한다!!!

Resources 폴더내 Prefab

 

player의 Inspector를 살펴보자

Player Inspector

Photon View

동기화할 오브젝트에 Photon View 컴포넌트를 추가해준다.

  • Ownership : 플레이어니까 Fixed로 설정하고
  • Synchronization 
    (GPT 님이 말씀하시길)
     1) Off
    • 동기화 안 함 (로컬에서만 변화 적용)
     2) Reliable Delta Compressed (권장)
    • 변경된 데이터만 전송
    • 데이터가 손실되지 않도록 신뢰성 보장
     3) Unreliable
    • 변경된 데이터만 전송
    • 데이터 손실 가능하지만 빠르게 동기화
     4) Unreliable On Change
    • 값이 변할 때만 전송 (최적화됨)
    • 빠르게 동기화하고 싶지만 최소한의 네트워크 사용을 원할 때 유용
  • Observable Serach 
    • 기본 Auto로 설정되어있을텐데 그대로 하면된다.

요거는 공식 Docs 내용이다. https://doc.photonengine.com/pun/current/gameplay/synchronization-and-state

더보기
  • Off:
    Synchronization does not occur, nothing is sent or received
  • Reliable Delta Compressed:
    Data is guaranteed to be received with an internal optimization mechanism which sends null if data doesn't change.
    For this to work, make sure you populate the stream with distinct SendNext calls.
  • Unreliable:
    Data is received in order but some updates may be lost.
    This means no delay in cases of loss.
  • Unreliable OnChange:
    Data is received in order but some updates may be lost.
    If updates repeat the last information, the PhotonView will pause sending updates until the next change.

Photon Transform View

플레이어는 Transform을 동기화 해야하니까 Transform View 까지 추가해준다.

설정 당연히 Postion, Rotate 둘다 체크를 해주고

 

여기서 중요 !!!

Postion을 직접 변경한값을 동기화가 안됨

무슨말이냐

 

직접 변경

 void Update()
    {
        if (!photonView.IsMine) return;

        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");

        Vector3 move = new Vector3(h, 0, v) * speed * Time.deltaTime;
        transform.position += move;
    }

Rigidbody 이동

    Public void Update()
    {
        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");

        Vector3 movement = new Vector3(h, 0, v) * 10;
        rb.linearVelocity = movement;
    }
}

Rigidbody로 이동해야 자동으로 동기화가 되고

직접변경해서 동기화를 하려면 수동으로 RPC를 통해 동기화 하여야한다. (안해봐서 모름)

 


테스트는 한 PC에 2개의 인스턴스로 진행하였다.

하나는 Build하고 나머지 하나는 에디터에서 실행함

(더 좋은 방법있으면 알려주세요...)

테스트 화면

 

정상적으로 포지션값이 동기화되는것을 확인할수 있다

그럼 끝