Unity 강의/Unity Course(2) - 절대강좌! 유니티

[Unity Course 2] 15. 포톤 클라우드를 활용한 네트워크 게임 5

첨부엉. 2024. 7. 4. 21:52
반응형
위키북스 출판사 이재현 저자님 의 '절대강좌! 유니티' 책을 참고하여 필기한 내용입니다.

배틀 필드 세부기능 구현

접속 정보 및 룸 나가기 기능 구현

해당 룸의 룸 이름과 접속자 수를 표시하고 룸 나가기 버튼을 구현해보기

 

BattleField 씬으로 전환해서 UI를 다음과 같이 구성하기

 

GameManager.cs 스크립트 수정하기

더보기
using UnityEngine;
using Photon.Pun;
using Photon.Realtime;
using TMPro;
using UnityEngine.UI;
using UnityEngine.SceneManagement;

public class GameManager : MonoBehaviourPunCallbacks
{
    public TMP_Text roomName;
    public TMP_Text connectInfo;
    public Button exitBtn;

    private void Awake()
    {
        CreatePlayer();
        // 접속 정보 추출 및 표시
        SetRoomInfo();
        // Exit 버튼 이벤트 연결
        exitBtn.onClick.AddListener(() => OnExitClick());
    }

    void CreatePlayer()
    {
        // 출현 위치 정보를 배열에 저장
        Transform[] points = GameObject.Find("SpawnPointGroup").GetComponentsInChildren<Transform>();
        int idx = Random.Range(1, points.Length);

        // 네트워크상에 캐릭터 생성
        PhotonNetwork.Instantiate("Player",
                                    points[idx].position,
                                    points[idx].rotation,
                                    0);
    }

    /// <summary>
    /// 룸 접속 정보를 출력
    /// </summary>
    void SetRoomInfo()
    {
        Room room = PhotonNetwork.CurrentRoom;
        roomName.text = room.Name;
        connectInfo.text = $"({room.PlayerCount} / {room.MaxPlayers})";
    }

    /// <summary>
    /// Exit 버튼의 OnClick에 연결할 함수
    /// </summary>
    private void OnExitClick()
    {
        PhotonNetwork.LeaveRoom();
    }

    /// <summary>
    /// 포톤 룸에서 퇴장했을 때 호출되는 콜백 함수
    /// </summary>
    public override void OnLeftRoom()
    {
        SceneManager.LoadScene("Lobby");
    }

    /// <summary>
    /// 룸에서 새로운 네트워크 유저가 접속했을 때 호출되는 콜백 함수
    /// </summary>
    /// <param name="newPlayer"></param>
    public override void OnPlayerEnteredRoom(Player newPlayer)
    {
        SetRoomInfo();
    }

    public override void OnPlayerLeftRoom(Player otherPlayer)
    {
        SetRoomInfo();
    }
}

 

 

PhotonNetwork.CurrentRoom은 현제 접속한 룸 정보를 나타냄

반환 값은 Room 클래스 타입으로 룸이름, 현재 접속자수, 최대 접속자수 등을 확인 가능

/// <summary>
    /// 룸 접속 정보를 출력
    /// </summary>
void SetRoomInfo()
{
    Room room = PhotonNetwork.CurrentRoom;
    roomName.text = room.Name;
    connectInfo.text = $"({room.PlayerCount} / {room.MaxPlayers})";
}

 

버튼과 Text를 각각 연결하기

접속 로그 모니터링

다음과 같이 Pannel 하나를 더 추가하고 디자인하기

 

Text 를 하나 추가하고 설정은 다음과 같이 한다.

그리고 Pannel-Msg에 Mask 컴포넌트를 추가한다.

 

GameManager.cs를 다음과 같이 수정한다.

더보기
using UnityEngine;
using Photon.Pun;
using Photon.Realtime;
using TMPro;
using UnityEngine.UI;
using UnityEngine.SceneManagement;

public class GameManager : MonoBehaviourPunCallbacks
{
    public TMP_Text roomName;
    public TMP_Text connectInfo;
    public TMP_Text msgList;
    public Button exitBtn;


    private void Awake()
    {
        CreatePlayer();
        // 접속 정보 추출 및 표시
        SetRoomInfo();
        // Exit 버튼 이벤트 연결
        exitBtn.onClick.AddListener(() => OnExitClick());
    }

    void CreatePlayer()
    {
        // 출현 위치 정보를 배열에 저장
        Transform[] points = GameObject.Find("SpawnPointGroup").GetComponentsInChildren<Transform>();
        int idx = Random.Range(1, points.Length);

        // 네트워크상에 캐릭터 생성
        PhotonNetwork.Instantiate("Player",
                                    points[idx].position,
                                    points[idx].rotation,
                                    0);
    }

    /// <summary>
    /// 룸 접속 정보를 출력
    /// </summary>
    void SetRoomInfo()
    {
        Room room = PhotonNetwork.CurrentRoom;
        roomName.text = room.Name;
        connectInfo.text = $"({room.PlayerCount} / {room.MaxPlayers})";
    }

    /// <summary>
    /// Exit 버튼의 OnClick에 연결할 함수
    /// </summary>
    private void OnExitClick()
    {
        PhotonNetwork.LeaveRoom();
    }

    /// <summary>
    /// 포톤 룸에서 퇴장했을 때 호출되는 콜백 함수
    /// </summary>
    public override void OnLeftRoom()
    {
        SceneManager.LoadScene("Lobby");
    }

    /// <summary>
    /// 룸에서 새로운 네트워크 유저가 접속했을 때 호출되는 콜백 함수
    /// </summary>
    /// <param name="newPlayer"></param>
    public override void OnPlayerEnteredRoom(Player newPlayer)
    {
        SetRoomInfo();
        string msg = $"\n<color=#00ff00>{newPlayer.NickName}</color> is joined room";
        msgList.text += msg;
    }

    public override void OnPlayerLeftRoom(Player otherPlayer)
    {
        SetRoomInfo();
        string msg = $"\n<color=#ff0000>{otherPlayer.NickName}</color> is left room";
        msgList.text += msg;
    }
}

 

ActorNumber 활용

여러 명의 유저가 접속해 서로 전투를 벌이다가 사망했을 때 누구에 의해 사망했는지 알아야됨

RPC로 호출된 FireBullet 함수에서 생성한 Bullet 에 룸에 접속한 네트워크 유저의 고유 번호인 ActorNumber 를 저장하여 확인 가능

 

Bullet.cs를 다음과 같이 수정

더보기
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Bullet : MonoBehaviour
{
    public GameObject effect;
    // 총알을 발사한 플레이어의 고유 번호
    public int actorNumber;

    private void Start()
    {
        GetComponent<Rigidbody>().AddRelativeForce(Vector3.forward * 1000.0f);
        // 일정 시간이 지난 후 총알을 삭제
        Destroy(this.gameObject, 3.0f);
    }

    private void OnCollisionEnter(Collision  coll)
    {
        // 충돌 지점 추출
        var contact = coll.GetContact(0);
        // 충돌 지점에 스카프 이펙트 생성
        var obj = Instantiate(effect,
                                contact.point,
                                Quaternion.LookRotation(-contact.normal));
        Destroy(obj, 2.0f);
        Destroy(this.gameObject);
    }
}

 

총알을 발사하는 스크립트에서 총알을 발사하는 네트워크 고유 번호인 ActorNumber를 설정

Fire.cs 스크립트를 다음과 같이 수정

더보기
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun;

public class Fire : MonoBehaviour
{
    public Transform firePos;
    public GameObject bulletPrefab;

    private ParticleSystem muzzleFlash;

    private PhotonView pv;
    // 왼쪽 마우스 버튼 클릭 이벤트 저장
    private bool isMouseClick => Input.GetMouseButtonDown(0);

    private void Start()
    {
        // 포톤뷰 컴포넌트 연결
        pv = GetComponent<PhotonView>();
        // FirePos 하위에 있는 총구 화염 효과 연결
        muzzleFlash = firePos.Find("MuzzleFlash").GetComponent<ParticleSystem>();
    }
    private void Update()
    {
        // 로컬 유저 여부와 마우스 왼쪽 버튼을 클릭했을 때 총알을 발사
        if(pv.IsMine && isMouseClick)
        {
            FireBullet(pv.Owner.ActorNumber);
            // RPC로 원격지에 있는 함수를 호출
            pv.RPC("FireBullet", RpcTarget.Others, pv.Owner.ActorNumber);
        }
    }
    [PunRPC]
    void FireBullet(int actorNo)
    {
        // 총구 화염 효과가 실행 중이 아닌 경우에 총구 화염 효과 실행
        if (!muzzleFlash.isPlaying) muzzleFlash.Play(true);

        GameObject bullet = Instantiate(bulletPrefab,
                                        firePos.position,
                                        firePos.rotation);
        bullet.GetComponent<Bullet>().actorNumber = actorNo;
    }

}

 

피격 당했을 때 충돌한 총알의 ActorNumber 를 활용하여 어떤 네트워크 유저가 발사한 총알인지 확인하기 위해

Damage.cs를 다음과 같이 수정

더보기
using System.Collections;
using UnityEngine;
using Photon.Pun;
using Photon.Realtime;
using Player = Photon.Realtime.Player;

public class Damage : MonoBehaviourPunCallbacks
{
    // 사망 후 투명 처리를 위한 MeshRenderer 컴포넌트의 배열
    private Renderer[] renderers;

    // 캐릭터의 초기 생명치
    private int initHp = 100;
    // 캐릭터의 현재 생명치
    public int currHp = 100;

    private Animator anim;
    private CharacterController controller;

    // 애니메이터 뷰에 생성한 파라미터의 해시값 추출
    private readonly int hashDie = Animator.StringToHash("Die");
    private readonly int hashRespawn = Animator.StringToHash("Respawn");

    private GameManager gameManager;

    private void Awake()
    {
        // 캐릭터 모델의 모든 Renderer 컴포넌트를 추출한 후 배열에 할당
        renderers = GetComponentsInChildren<Renderer>();
        anim = GetComponent<Animator>();
        controller = GetComponent<CharacterController>();

        // 현재 생명치를 초기 생명치로 초깃값 설정
        currHp = initHp;

        gameManager = GameObject.Find("GameManager").GetComponent<GameManager>();
    }

    private void OnCollisionEnter(Collision coll)
    {
        // 생명 수치가 0보다 크고 충돌체의 태그가 CULLET인 경우에 생명 수치를 차감
        if(currHp > 0 && coll.collider.CompareTag("BULLET"))
        {
            currHp -= 20;
            if(currHp<=0)
            {
                // 자신의 PhotonView 일 때만 메세지를 출력
                if(photonView.IsMine)
                {
                    // 총알의 ActorNumber를 추출
                    var actorNo = coll.collider.GetComponent<Bullet>().actorNumber;
                    // ActorNumber로 현재 룸에 입장한 플레이어를 추출
                    Player lastShootPlayer = PhotonNetwork.CurrentRoom.GetPlayer(actorNo);

                    // 메세지 출력을 위한 문자열 포맷
                    string msg = string.Format("\n<color=#00ff00>{0}</color> is killed by <color=#ff0000>{1}</color>",
                                                photonView.Owner.NickName,
                                                lastShootPlayer.NickName);
                    photonView.RPC("KillMessage", RpcTarget.AllBufferedViaServer, msg);
                }
                StartCoroutine(PlayerDie());
            }
        }
    }
    [PunRPC]
    void KillMessage(string msg)
    {   

        // 메세지 출력
        gameManager.msgList.text += msg;
    }

    IEnumerator PlayerDie()
    {
        // CharacterController 컴포넌트 비활성화
        controller.enabled = false;
        // 리스폰 비활성화
        anim.SetBool(hashRespawn, false);
        // 캐릭터 사망 애니메이션 실행
        anim.SetTrigger(hashDie);

        yield return new WaitForSeconds(3.0f);

        // 리스폰 활성화
        anim.SetBool(hashRespawn, true);

        // 캐릭터 투명 처리
        SetPlayerVisible(false);

        yield return new WaitForSeconds(1.5f);

        // 생성 위치를 재조정
        Transform[] points = GameObject.Find("SpawnPointGroup").GetComponentsInChildren<Transform>();
        int idx = Random.Range(1, points.Length);
        transform.position = points[idx].position;

        // 리스폰 시 생명 초깃값 설정
        currHp = 100;

        // 캐릭터를 다시보이게 처리
        SetPlayerVisible(true);
        // CharacterController 컴포넌트 활성화
        controller.enabled = true;
    }
    // Renderer 컴포넌트를 활성 / 비활성화 하는 함수
    void SetPlayerVisible(bool isVisible)
    {
        for(int i =0; i<renderers.Length; i++)
        {
            renderers[i].enabled = isVisible;
        }
    }
}

빌드한 후 실행하게 되면 KillMessage 함수가 실행되면서 다음과 같이 메세지가 표시됨

 

반응형