리소스, 프로젝트 다운받기
https://drive.google.com/file/d/1z2QEv_nvPcrMxLofNHoX5n8NHv1tzjKq/view?usp=sharing
NetworkManager.cs 소스입니다
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine.UI;
public class NetworkManager : MonoBehaviourPunCallbacks
{
public InputField NickNameInput;
public GameObject DisconnectPanel;
public GameObject RespawnPanel;
void Awake()
{
Screen.SetResolution(960, 540, false);
PhotonNetwork.SendRate = 60;
PhotonNetwork.SerializationRate = 30;
}
public void Connect() => PhotonNetwork.ConnectUsingSettings();
public override void OnConnectedToMaster()
{
PhotonNetwork.LocalPlayer.NickName = NickNameInput.text;
PhotonNetwork.JoinOrCreateRoom("Room", new RoomOptions { MaxPlayers = 6 }, null);
}
public override void OnJoinedRoom()
{
DisconnectPanel.SetActive(false);
StartCoroutine("DestroyBullet");
Spawn();
}
IEnumerator DestroyBullet()
{
yield return new WaitForSeconds(0.2f);
foreach (GameObject GO in GameObject.FindGameObjectsWithTag("Bullet")) GO.GetComponent<PhotonView>().RPC("DestroyRPC", RpcTarget.All);
}
public void Spawn()
{
PhotonNetwork.Instantiate("Player", new Vector3(Random.Range(-6f, 19f), 4, 0), Quaternion.identity);
RespawnPanel.SetActive(false);
}
void Update() { if (Input.GetKeyDown(KeyCode.Escape) && PhotonNetwork.IsConnected) PhotonNetwork.Disconnect(); }
public override void OnDisconnected(DisconnectCause cause)
{
DisconnectPanel.SetActive(true);
RespawnPanel.SetActive(false);
}
}
PlayerScript.cs 소스입니다
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine.UI;
using Cinemachine;
public class PlayerScript : MonoBehaviourPunCallbacks, IPunObservable
{
public Rigidbody2D RB;
public Animator AN;
public SpriteRenderer SR;
public PhotonView PV;
public Text NickNameText;
public Image HealthImage;
bool isGround;
Vector3 curPos;
void Awake()
{
// 닉네임
NickNameText.text = PV.IsMine ? PhotonNetwork.NickName : PV.Owner.NickName;
NickNameText.color = PV.IsMine ? Color.green : Color.red;
if (PV.IsMine)
{
// 2D 카메라
var CM = GameObject.Find("CMCamera").GetComponent<CinemachineVirtualCamera>();
CM.Follow = transform;
CM.LookAt = transform;
}
}
void Update()
{
if (PV.IsMine)
{
// ← → 이동
float axis = Input.GetAxisRaw("Horizontal");
RB.velocity = new Vector2(4 * axis, RB.velocity.y);
if (axis != 0)
{
AN.SetBool("walk", true);
PV.RPC("FlipXRPC", RpcTarget.AllBuffered, axis); // 재접속시 filpX를 동기화해주기 위해서 AllBuffered
}
else AN.SetBool("walk", false);
// ↑ 점프, 바닥체크
isGround = Physics2D.OverlapCircle((Vector2)transform.position + new Vector2(0, -0.5f), 0.07f, 1 << LayerMask.NameToLayer("Ground"));
AN.SetBool("jump", !isGround);
if (Input.GetKeyDown(KeyCode.UpArrow) && isGround) PV.RPC("JumpRPC", RpcTarget.All);
// 스페이스 총알 발사
if (Input.GetKeyDown(KeyCode.Space))
{
PhotonNetwork.Instantiate("Bullet", transform.position + new Vector3(SR.flipX ? -0.4f : 0.4f, -0.11f, 0), Quaternion.identity)
.GetComponent<PhotonView>().RPC("DirRPC", RpcTarget.All, SR.flipX ? -1 : 1);
AN.SetTrigger("shot");
}
}
// IsMine이 아닌 것들은 부드럽게 위치 동기화
else if ((transform.position - curPos).sqrMagnitude >= 100) transform.position = curPos;
else transform.position = Vector3.Lerp(transform.position, curPos, Time.deltaTime * 10);
}
[PunRPC]
void FlipXRPC(float axis) => SR.flipX = axis == -1;
[PunRPC]
void JumpRPC()
{
RB.velocity = Vector2.zero;
RB.AddForce(Vector2.up * 700);
}
public void Hit()
{
HealthImage.fillAmount -= 0.1f;
if (HealthImage.fillAmount <= 0)
{
GameObject.Find("Canvas").transform.Find("RespawnPanel").gameObject.SetActive(true);
PV.RPC("DestroyRPC", RpcTarget.AllBuffered); // AllBuffered로 해야 제대로 사라져 복제버그가 안 생긴다
}
}
[PunRPC]
void DestroyRPC() => Destroy(gameObject);
public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
{
if (stream.IsWriting)
{
stream.SendNext(transform.position);
stream.SendNext(HealthImage.fillAmount);
}
else
{
curPos = (Vector3)stream.ReceiveNext();
HealthImage.fillAmount = (float)stream.ReceiveNext();
}
}
}
BulletScript.cs 소스입니다
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun;
using Photon.Realtime;
public class BulletScript : MonoBehaviourPunCallbacks
{
public PhotonView PV;
int dir;
void Start() => Destroy(gameObject, 3.5f);
void Update() => transform.Translate(Vector3.right * 7 * Time.deltaTime * dir);
void OnTriggerEnter2D(Collider2D col) // col을 RPC의 매개변수로 넘겨줄 수 없다
{
if (col.tag == "Ground") PV.RPC("DestroyRPC", RpcTarget.AllBuffered);
if (!PV.IsMine && col.tag == "Player" && col.GetComponent<PhotonView>().IsMine) // 느린쪽에 맞춰서 Hit판정
{
col.GetComponent<PlayerScript>().Hit();
PV.RPC("DestroyRPC", RpcTarget.AllBuffered);
}
}
[PunRPC]
void DirRPC(int dir) => this.dir = dir;
[PunRPC]
void DestroyRPC() => Destroy(gameObject);
}