Photon Realtime : 핵심 엔진이자 기반
Pun : Photon Unity Networking
그 Realtime을 유니티에 맞춰서 최적화하고 쓰기쉽게 만든 패키지
Photon View : 게임 오브젝트에 추가해서 나의 움직임(로컬)을 다른쪽(리모트)에게 전달하기 위한 것, 통신
________________________________________________________
private void OnGUI()
{
GUILayout.Label(PhotonNetwork.NetworkClientState.ToString());
}

왼쪽위에 접속 상태를 표시하는게 뜬다
GUILayout.Label : 왼쪽위에 글씨 띄우기
PhotonNetwork.NetworkClientState : 접속 상태에 대한 내용 . ToString()
꼭 저렇게 왼쪽 위가 아니라, 그냥 UI에 안에 내용만 저걸로 넣으면 UI로써 표시할수도 있다
________________________________________________________________
Photon View

게임 오브젝트에 부착(프리팹에도 붙일수 있음)


Synchronization : 동기화 방식
Reliable Delta Compressed : TCP/IP : 가다가 잃어버리는 등, 잘 안가면, 다시 보낸다 -> 데이터가 중요한 내용들에 대해선 이렇게 처리함
Unreliable : UDP : 가다가 잃어버리든 말든 그냥 순서대로 다 보낸다 -> 렉걸리면 겟앰프드 등의 게임에서 상대가 순간이동을 하는것
Unreliable On Change : 필요에 따라서 양쪽을 다 하는데, 무료버전에선 안바뀐다

Manual로 변경(사실 auto Find All로 해도 된다)

Transform 끌어다 붙이기

Position과 Rotation은 리모트쪽에 전달되고, Scale은 전달되지 않는다
void CreateTank()
{
float pos = Random.Range(-80f, 80f);
PhotonNetwork.Instantiate(
"Tank",
new Vector3(pos, 3f, pos),
Quaternion.identity,
0 ,
null);
}
PhotonNetwork.Instantiate
-> string : Resources 에서 저 이름인 애를 가져옴
Vector : 생성될 좌표
Quaternion : 회전값
그리고 그룹, 전달할 데이터 순

탱크가 생성되지만, 카메라가 탱크를 잡지않음
public class TankMove : MonoBehaviourPun
{
public TankInput input;
public Rigidbody rb;
IEnumerator Start()
{
this.input = GetComponent<TankInput>();
this.rb = GetComponent<Rigidbody>();
this.rb.centerOfMass = new Vector3(0,-0.5f,0); // 물리 엔진에서 탱크의 중심을 조정
yield return null;
if (photonView.IsMine) // 포톤 네트워크 상 포톤 뷰가 나의 것이라면(로컬 이라면)
{
CinemachineVirtualCamera vCam = FindObjectOfType<CinemachineVirtualCamera>();
if (vCam != null)
{
vCam.Follow = this.transform; // 가상 카메라가 탱크를 따라가도록 설정
vCam.LookAt = this.transform; // 가상 카메라가 탱크를 바라보도록 설정
}
}
}
MonoBehaviourPun : MonoBehaviour의 기능을 그대로 쓰면서, 통신까지함

바로 카메라가 탱크를 바라보는 모습
PunCallbacks : 이벤트 들이 있어서 통신관련을 관리할수있음
Pun : photonView를 관리할 수 있음, 간단히 그냥 photonView만 컨트롤 할거면 Pun만 달면 됨

빌드와 에디터를 동시에 키면 탱크가 2개인걸 확인가능
하지만 탱크 2개가 동시에 움직이는 문제가 있다
public class TankMove : MonoBehaviourPun , IPunObservable
Vector3 curPos = Vector3.zero; // 다른 네트워크의 이동값
Quaternion curRot = Quaternion.identity; // 다른 네트워크의 회전값
IEnumerator Start()
{
this.input = GetComponent<TankInput>();
this.rb = GetComponent<Rigidbody>();
this.rb.centerOfMass = new Vector3(0,-0.5f,0); // 물리 엔진에서 탱크의 중심을 조정
this.curPos = this.transform.position; // 현재 위치를 저장
this.curRot = this.transform.rotation; // 현재 회전을 저장
//로컬의 이동 회전을 네트워크상의 타인인 리모트에 송신하고,
//반대로 리모트의 이동 회전을 수신받아서 네트워크 상 움직임이 서로 보여야 하기 때문에
public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
//IPunObservable 인터페이스의 구현 내용
//stream : 데이터 통로, 네트워크에서 정보가 전달되는 통로
{
if (stream.IsWriting) //송신중이라면
{
stream.SendNext(this.transform.position);
stream.SendNext(this.transform.rotation);
}
else if (stream.IsReading) //수신중이라면
//사실 그냥 송신중이 아니라면 이란 뜻으로 else만 해도 된다
{
this.curPos = (Vector3)stream.ReceiveNext(); // 송신된 위치값을 받는다
this.curRot = (Quaternion)stream.ReceiveNext(); // 송신된 회전값을 받는다
}
}
void FixedUpdate()
{
if (photonView.IsMine) // 로컬이라면
{
#region 탱크 회전
this.transform.Rotate(0, input.h * 2f, 0, Space.Self);
#endregion
#region 탱크 이동
this.transform.Translate(Vector3.forward * this.input.v * Time.deltaTime * 100);
#endregion
}
else // 리모트라면
{
this.transform.position = Vector3.Lerp(this.transform.position, this.curPos, Time.deltaTime * 3f); // 다른 네트워크의 위치값으로 보간
this.transform.rotation = Quaternion.Lerp(this.transform.rotation, this.curRot, Time.deltaTime * 3f); // 다른 네트워크의 회전값으로 보간
}
}
______________________________________________________

public override void OnJoinedLobby()
{
base.OnJoinedLobby();
Debug.Log("로비 접속");
PhotonNetwork.JoinRandomRoom(); //랜덤 방 접속 시도
userID.text = GetUserID(); //유저 아이디 가져오기
}
string GetUserID()
{
string userID = PlayerPrefs.GetString("User_ID");
if (string.IsNullOrEmpty(userID))
{
userID = $"User_{Random.Range(0, 999)}";
}
return userID;
}
///////////////////////////////////////////////////////////////
public override void OnJoinedRoom()
{
base.OnJoinedRoom();
Debug.Log("방 접속 성공");
StartCoroutine(LoadBattleFieldScene()); // 씬을 이동하는 코루틴
}
IEnumerator LoadBattleFieldScene()
{
PhotonNetwork.IsMessageQueueRunning = false; // 씬을 이동하는 동안 클라우드 서버로부터
// 네트워크 메시지 수신 중단
AsyncOperation ao = SceneManager.LoadSceneAsync(1); // 씬 비동기적으로 로딩
yield return ao;
}
public void OnClickJoinRandomRoom()
{
PhotonNetwork.NickName = this.userID.text;
PlayerPrefs.SetString("User_ID", this.userID.text);
PhotonNetwork.JoinRandomRoom(); //랜덤 방 접속 시도
}
public class GameManager : MonoBehaviour
{
public static GameManager Instance { get; private set; }
public bool isGameOver = false;
private void Awake()
{
if (Instance == null)
{
Instance = this;
}
else
{
Destroy(gameObject);
}
PhotonNetwork.IsMessageQueueRunning = true; //다시 네트워크로부터 메세지 받게 설정
CreateTank();
}
void CreateTank()
{
float pos = Random.Range(-80f, 80f);
PhotonNetwork.Instantiate(
"Tank",
new Vector3(pos, 3f, pos),
Quaternion.identity,
0,
null);
}
}
PhotonNetwork.IsMessageQueueRunning : 아까 씬 로딩할때 메세지 안받게 false로 바꿨으니, 다시 true로 바꿔야함

내거(editor)

다른애(빌드파일)
____________________________________________________________
RPC : Remote Procedure Call
원격 함수 호출
멀리 떨어진곳에서 함수를 호출시키는것
내 화면에서 총을 쏘면 다른 사람의 화면에서도 총이 쏴지는것

All : 전부 다

Others : 나 빼고 다
이 외에도 많은 옵션이 있음
void Update()
{
if (this.input.isFire)
{
if (photonView.IsMine) // 로컬이라면
{
Fire(); //그냥 내 Fire호출
photonView.RPC("Fire",RpcTarget.Others); //내 컴퓨터 말고 다른 컴퓨터에 있는
//내 Remote들에 원격 호출
}
}
}
[PunRPC] //Fire함수에 어트리뷰트 붙여놔야한다
void Fire()
{
else //리모트라면
{
photonView.RPC("Fire",RpcTarget.Others); //내 컴퓨터 말고 다른 컴퓨터에 있는
//내 Remote들에 원격 호출
}
이렇게 쓰면 뭔가 꼬인다
___________________________________________________________________________
탱크 체력
3대 맞으면 사라질건데, destroy하면 안되고 오브젝트 풀링을 쓰기 복잡하니,
그냥 mesh를 껐다가, 5초뒤 다시 켜서 부활시키는 식으로 구현
public class TankDamage : MonoBehaviourPun
{
//탱크가 3대쯤 맞으면 사망
//메쉬를 전부 껐다가 , 5초뒤 다시 켜서 부활
public Image hpBar; // 탱크의 체력바 UI
[SerializeField]
private MeshRenderer[] meshRenderers; // 탱크의 메쉬 렌더러들
private readonly string tankTag = "Player"; // 탱크 태그
private readonly string apacheTag = "Apache"; // 아파치 태그
private GameObject expEffect; // 폭발 이펙트
private readonly int maxHp = 100; // 최대 체력
private int curHp; // 현재 체력
void Start()
{
this.expEffect = Resources.Load<GameObject>("Effects/BigExplosionEffect");
this.curHp = this.maxHp; // 현재 체력을 최대 체력으로 초기화
}
public void OnDamage(string tag)
{
photonView.RPC("OnDamageRPC", RpcTarget.All, tag); // 모든 클라이언트에 데미지 전달
Debug.Log("OnDamage called with tag: " + tag);
}
[PunRPC]
void OnDamageRPC(string tag)
{
if (this.curHp > 0 && tag == this.tankTag)
{
//데미지 전달
HpBarInit(tag); // 체력바 UI 업데이트
if(this.curHp <= 0)
{
//파괴 연출(이펙트)
}
}
}
void HpBarInit(string tag)
{
if(tag == tankTag)
{
this.curHp -= 40;
}
this.hpBar.fillAmount = (float)this.curHp / (float)this.maxHp; // 체력바 UI 업데이트
}
}
_______________________________________________________________
방 만들기 & 스크롤바
스크롤 바

panel 생성후 scroll rect부착

빈 오브젝트 추가

스크롤바 추가 후 top to bottom

scroll Rect쪽에 content에 ScrollContents부착

vertical Scrollbar에 scrollbar 부착

scroll Contents 밑에 Image생성

image 밑에 text2개 생성


RoomList에 Mask부착
'2025유니티수업' 카테고리의 다른 글
| 0715 (포톤) (0) | 2025.07.15 |
|---|---|
| 0714(포톤 채팅) (1) | 2025.07.14 |
| 0710 유니티(LineRenderer, 사운드매니저) , Pun2 (3) | 2025.07.10 |
| 0709 유니티(헬기, ) (0) | 2025.07.09 |
| 0708 유니티(좀비 서바이벌 완성 , scope zoom) (0) | 2025.07.08 |