[SKKU DT] 42일차 -유니티 개인 프로젝트(3) (경기 스타트업 캠퍼스 디지털 트윈) -캐릭터 이동 애니메이션, 로그인 패스워드 설정, 포톤(photon) 멀티플레이 구현

2023. 12. 28. 17:16SKKU DT

728x90
반응형

캐릭터 걷기 애니메이션 넣기

Mixamo에서 걷는 모션을 얻어와서 캐릭터에 입히는 작업을 할 것이다.

 

Mixamo

 

www.mixamo.com

걷는 애니메이션을 위와 같은 설정으로 DOWNLOAD 받는다. [In Place]를 체크하고 다운받아야 실제로 캐릭터가 움직이지 않고 제자리에서 움직이는 시늉만 할 수 있다. 우리는 스크립트로 캐릭터를 움직이기 때문에 애니메이션에서 실제로는 움직이지 않아도 된다.

 

 

프로젝트로 받은 fbx를 가져오고, Inspector 세팅에서 [Rig] - [Animation Type]을 [Humanoid]로 바꾼다.

 

 

Animator Controller 생성

 

 

Animator에서 "Idle" State 생성, Walking 애니메이션은 끌어다 놓고 서로 Transition을 연결한다.

왼쪽 위 Parameters에서는 isWalking이라는 이름으로 Bool 파라미터를 만들었다.

 

 

fbx안에 들어있는 Walking 애니메이션을 복사해서 fbx의 바깥쪽에 붙여넣기 했고 해당 애니메이션의 Loop Time을 체크한다.

 

 

Idle과 Walking의 Transition 화살표를 누른 후 Inspector의 [Conditions]에서 조건을 추가한다. 바로 위에서 만들었던 isWalking 파라미터를 Idle에서 Walking으로 갈 때는 true로, 반대는 false로 설정한다. 플레이어가 특정 키를 눌렀을 때 true로 바뀌면서 애니메이션이 실행되도록 스크립트로 설정할 예정이다.

 

 

Player에게 "PlayerMove" Animator Controller를 컴포넌트로 추가한다.

컴포넌트 중, [Animator]에서 [Avatar]는 fbx에 들어있는 사람 모양의 아바타를 선택하면 된다. 나는 무료 에셋 중 프리팹을 이용했기 때문에 해당 프리팹의 fbx 파일도 같이 들어와서 해당 fbx의 아바타를 연결하였다.

 

 

Idle에서 Walking 사이의 Transition의 Has Exit Time 체크 해제

 

 

Idle 모션을 위한 Standing 애니메이션 다운로드

 

 

Idle에서 Walking으로 넘어갈 때는 Walking 애니메이션을 최대한 적게 해보았고, Walking에서 Idle로 돌아갈 때도 Walking 애니메이션이 러닝타임을 차지하는 비율을 줄여서 이동 키보드를 눌렀을 때만 걸어가는 모션을 취하도록 설정하였다.

바로 위에서 볼 수 있듯이, Walking을 끝낼 때 캐릭터가 바로 멈추는 모습을 볼 수 있다.

 

 

PlayerMove 함수에서, Move 함수에 WASD 이동에 해당 키를 눌렀을 때 애니메이션이 구현되도록 하였다.

void Move()
{
    //카메라 설정
    Vector3 cameraForward = camera.transform.forward;
    Vector3 cameraRight = camera.transform.right;
    cameraForward.y = 0f;
    cameraRight.y = 0f;
    Vector3 moveDir = (cameraForward * v) + (cameraRight * h);

    moveDir.Set(moveDir.x, 0.0f, moveDir.z);
    characterController.SimpleMove(moveDir * movespeed);

    

    if (Input.GetKey("w"))
    {
        transform.forward = cameraForward;
        anim.SetBool("isWalking", true);
    }
    else if (Input.GetKey("a"))
    {
        transform.right = cameraForward * 90;
        anim.SetBool("isWalking", true);
    }
    else if (Input.GetKey("s"))
    {
        transform.forward = -cameraForward;
        anim.SetBool("isWalking", true);
    }
    else if (Input.GetKey("d"))
    {
        transform.right = cameraForward * -90;
        anim.SetBool("isWalking", true);
    }
    else
    {
        anim.SetBool("isWalking", false);
    }

}

입력한 방향키 대로 아바타가 움직이고 해당 방향으로 걷는 애니메이션까지 잘 구현되었다. 아바타를 가만히 놔두면 약간 움직이면서 숨쉬는 듯 한 Idle 애니메이션도 잘 나오는 것을 볼 수 있다!

 

 


 

 

 

로그인 씬에서 Password InputField 설정하기

비밀번호를 가리고 싶다면 Password InputField에서 [Content Type] - [Password]

 

 


 

 

포톤 멀티플레이어 구현하기

포톤 홈페이지에서 [새 어플리케이션 만들기]

멀티플레이, [Photon 종류] - [Pun]으로 [작성하기] 클릭한다.

 

 

photon 무료 에셋을 내 프로젝트에 import

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

 

 

설치하면 나오는 팝업의 입력란에, 포톤에서 새 어플리케이션을 만들고 받은 어플리케이션 ID를 입력한다.

[Setup Project]를 눌러 연결에 성공하면 Done! 메시지가 나온다.

 

 

PhotonManager 스크립트 생성, 빈 오브젝트에 넣고 실행 후 콘솔에 "30"이 출력되는지 확인

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//Photon 네임스페이스 추가
using Photon.Pun;
using Photon.Realtime;

public class PhotonManager : MonoBehaviourPunCallbacks //상속 클래스 변경
{
    //변수 선언
    private readonly string version = "1.0";//게임 버전 체크. 유저가 건드리지 못하게 private, readonly
    private string userId = "Victor"; //아무거나 userId 생성

    //네트워크 접속은 Start()보다 먼저 실행되어야한다. Awake() 함수 사용
    private void Awake()
    {
        //씬 동기화. 맨 처음 접속한 사람이 방장이 된다.
        PhotonNetwork.AutomaticallySyncScene = true;
        //버전 할당. 위에 string으로 만들었던 version을 쓴다.
        PhotonNetwork.GameVersion = version;
        //App ID 할당. 위에 userId로 만들었던 userId를 쓴다.
        PhotonNetwork.NickName = userId;
        //포톤 서버와의 통신 횟수를 로그로 찍기. 기본값 : 30
        Debug.Log(PhotonNetwork.SendRate); //제대로 통신이 되었다면 30이 출력된다.
        //포톤 서버에 접속
        PhotonNetwork.ConnectUsingSettings();
    }
}

 

 

PhotonManager 스크립트에 콜백 함수 추가

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//Photon 네임스페이스 추가
using Photon.Pun;
using Photon.Realtime;

public class PhotonManager : MonoBehaviourPunCallbacks //상속 클래스 변경
{
    //변수 선언
    private readonly string version = "1.0";//게임 버전 체크. 유저가 건드리지 못하게 private, readonly
    private string userId = "Victor"; //아무거나 userId 생성

    //네트워크 접속은 Start()보다 먼저 실행되어야한다. Awake() 함수 사용
    private void Awake()
    {
        //씬 동기화. 맨 처음 접속한 사람이 방장이 된다.
        PhotonNetwork.AutomaticallySyncScene = true;
        //버전 할당. 위에 string으로 만들었던 version을 쓴다.
        PhotonNetwork.GameVersion = version;
        //App ID 할당. 위에 userId로 만들었던 userId를 쓴다.
        PhotonNetwork.NickName = userId;
        //포톤 서버와의 통신 횟수를 로그로 찍기. 기본값 : 30
        Debug.Log(PhotonNetwork.SendRate); //제대로 통신이 되었다면 30이 출력된다.
        //포톤 서버에 접속
        PhotonNetwork.ConnectUsingSettings();
    }

    //CallBack 함수
    public override void OnConnectedToMaster() //정상적으로 마스터 서버에 접속이 되면 호출된다.
    {
        //마스터 서버에 접속이 되었는지 디버깅 한다.
        Debug.Log("Connected to Master");
        Debug.Log($"In Lobby = {PhotonNetwork.InLobby}"); //로비에 들어와 있으면 True, 아니면 False 반환. Master 서버에는 접속했지만 로비에는 아니므로 False 반환된다.
        //로비 접속
        PhotonNetwork.JoinLobby();
    }

    public override void OnJoinedLobby() //로비에 접속이 제대로 되었다면 해당 콜백함수 호출
    {
        Debug.Log($"In Lobby = {PhotonNetwork.InLobby}"); //로비에 접속이 되었다면 True가 반환 될 것이다.
    }
}

 

 

PhotonManager 스크립트 수정, 방이 없다면 방을 생성한다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//Photon 네임스페이스 추가
using Photon.Pun;
using Photon.Realtime;

public class PhotonManager : MonoBehaviourPunCallbacks //상속 클래스 변경
{
    //변수 선언
    private readonly string version = "1.0";//게임 버전 체크. 유저가 건드리지 못하게 private, readonly
    private string userId = "Victor"; //아무거나 userId 생성

    //네트워크 접속은 Start()보다 먼저 실행되어야한다. Awake() 함수 사용
    private void Awake()
    {
        //씬 동기화. 맨 처음 접속한 사람이 방장이 된다.
        PhotonNetwork.AutomaticallySyncScene = true;
        //버전 할당. 위에 string으로 만들었던 version을 쓴다.
        PhotonNetwork.GameVersion = version;
        //App ID 할당. 위에 userId로 만들었던 userId를 쓴다.
        PhotonNetwork.NickName = userId;
        //포톤 서버와의 통신 횟수를 로그로 찍기. 기본값 : 30
        Debug.Log(PhotonNetwork.SendRate); //제대로 통신이 되었다면 30이 출력된다.
        //포톤 서버에 접속
        PhotonNetwork.ConnectUsingSettings();
    }

    //CallBack 함수
    public override void OnConnectedToMaster() //정상적으로 마스터 서버에 접속이 되면 호출된다.
    {
        //마스터 서버에 접속이 되었는지 디버깅 한다.
        Debug.Log("Connected to Master");
        Debug.Log($"In Lobby = {PhotonNetwork.InLobby}"); //로비에 들어와 있으면 True, 아니면 False 반환. Master 서버에는 접속했지만 로비에는 아니므로 False 반환된다.
        //로비 접속
        PhotonNetwork.JoinLobby();
    }

    public override void OnJoinedLobby() //로비에 접속이 제대로 되었다면 해당 콜백함수 호출
    {
        Debug.Log($"In Lobby = {PhotonNetwork.InLobby}"); //로비에 접속이 되었다면 True가 반환 될 것이다.
        //방 접속 방법은 두 가지. 1.랜덤 매치메이킹, 2.선택된 방 접속
        PhotonNetwork.JoinRandomRoom();
    }

    //방 생성이 되지 않았으면 오류 콜백 함수 실행
    public override void OnJoinRandomFailed(short returnCode, string message)
    {
        Debug.Log($"JoinRandom Failed {returnCode}: {message}");

        //룸 속성 설정
        RoomOptions roomOptions = new RoomOptions();
        //룸의 접속할 수 있는 최대 접속자 수 최대 제한을 해놔야 CCU를 제한할 수 있다.
        roomOptions.MaxPlayers = 20;
        //룸 오픈 여부
        roomOptions.IsOpen = true;
        //로비에서 룸의 목록에 노출시킬지 여부. 공개방 생성
        roomOptions.IsVisible = true;
        //룸 생성
        PhotonNetwork.CreateRoom("Room1", roomOptions); //룸 이름과 룸 설정. 우리는 roomOptions에 설정을 이미 해놓았다.
    }

    //제대로 룸이 있다면 다음의 콜백 함수를 호출한다.
    public override void OnCreatedRoom()
    {
        Debug.Log("Created Room");
        Debug.Log($"Room Name: {PhotonNetwork.CurrentRoom.Name}");
    }
}

방 이름까지 잘 설정된 것을 볼 수 있다.

 

 

방에 잘 들어왔을 때 PlayerCount로 플레이어 수 받아오기

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//Photon 네임스페이스 추가
using Photon.Pun;
using Photon.Realtime;

public class PhotonManager : MonoBehaviourPunCallbacks //상속 클래스 변경
{
    //변수 선언
    private readonly string version = "1.0";//게임 버전 체크. 유저가 건드리지 못하게 private, readonly
    private string userId = "Victor"; //아무거나 userId 생성

    //네트워크 접속은 Start()보다 먼저 실행되어야한다. Awake() 함수 사용
    private void Awake()
    {
        //씬 동기화. 맨 처음 접속한 사람이 방장이 된다.
        PhotonNetwork.AutomaticallySyncScene = true;
        //버전 할당. 위에 string으로 만들었던 version을 쓴다.
        PhotonNetwork.GameVersion = version;
        //App ID 할당. 위에 userId로 만들었던 userId를 쓴다.
        PhotonNetwork.NickName = userId;
        //포톤 서버와의 통신 횟수를 로그로 찍기. 기본값 : 30
        Debug.Log(PhotonNetwork.SendRate); //제대로 통신이 되었다면 30이 출력된다.
        //포톤 서버에 접속
        PhotonNetwork.ConnectUsingSettings();
    }

    //CallBack 함수
    public override void OnConnectedToMaster() //정상적으로 마스터 서버에 접속이 되면 호출된다.
    {
        //마스터 서버에 접속이 되었는지 디버깅 한다.
        Debug.Log("Connected to Master");
        Debug.Log($"In Lobby = {PhotonNetwork.InLobby}"); //로비에 들어와 있으면 True, 아니면 False 반환. Master 서버에는 접속했지만 로비에는 아니므로 False 반환된다.
        //로비 접속
        PhotonNetwork.JoinLobby();
    }

    public override void OnJoinedLobby() //로비에 접속이 제대로 되었다면 해당 콜백함수 호출
    {
        Debug.Log($"In Lobby = {PhotonNetwork.InLobby}"); //로비에 접속이 되었다면 True가 반환 될 것이다.
        //방 접속 방법은 두 가지. 1.랜덤 매치메이킹, 2.선택된 방 접속
        PhotonNetwork.JoinRandomRoom();
    }

    //방 생성이 되지 않았으면 오류 콜백 함수 실행
    public override void OnJoinRandomFailed(short returnCode, string message)
    {
        Debug.Log($"JoinRandom Failed {returnCode}: {message}");

        //룸 속성 설정
        RoomOptions roomOptions = new RoomOptions();
        //룸의 접속할 수 있는 최대 접속자 수 최대 제한을 해놔야 CCU를 제한할 수 있다.
        roomOptions.MaxPlayers = 20;
        //룸 오픈 여부
        roomOptions.IsOpen = true;
        //로비에서 룸의 목록에 노출시킬지 여부. 공개방 생성
        roomOptions.IsVisible = true;
        //룸 생성
        PhotonNetwork.CreateRoom("Room1", roomOptions); //룸 이름과 룸 설정. 우리는 roomOptions에 설정을 이미 해놓았다.
    }

    //제대로 룸이 있다면 다음의 콜백 함수를 호출한다.
    public override void OnCreatedRoom()
    {
        Debug.Log("Created Room");
        Debug.Log($"Room Name: {PhotonNetwork.CurrentRoom.Name}");
    }

    //룸에 들어왔을 때 콜백 함수
    public override void OnJoinedRoom()
    {
        Debug.Log($"In Room = {PhotonNetwork.InRoom}");
        Debug.Log($"Player Count = {PhotonNetwork.CurrentRoom.PlayerCount}");
    }
}

 

 

접속한 사용자 닉네임 확인

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//Photon 네임스페이스 추가
using Photon.Pun;
using Photon.Realtime;

public class PhotonManager : MonoBehaviourPunCallbacks //상속 클래스 변경
{
    //변수 선언
    private readonly string version = "1.0";//게임 버전 체크. 유저가 건드리지 못하게 private, readonly
    private string userId = "Victor"; //아무거나 userId 생성

    //네트워크 접속은 Start()보다 먼저 실행되어야한다. Awake() 함수 사용
    private void Awake()
    {
        //씬 동기화. 맨 처음 접속한 사람이 방장이 된다.
        PhotonNetwork.AutomaticallySyncScene = true;
        //버전 할당. 위에 string으로 만들었던 version을 쓴다.
        PhotonNetwork.GameVersion = version;
        //App ID 할당. 위에 userId로 만들었던 userId를 쓴다.
        PhotonNetwork.NickName = userId;
        //포톤 서버와의 통신 횟수를 로그로 찍기. 기본값 : 30
        Debug.Log(PhotonNetwork.SendRate); //제대로 통신이 되었다면 30이 출력된다.
        //포톤 서버에 접속
        PhotonNetwork.ConnectUsingSettings();
    }

    //CallBack 함수
    public override void OnConnectedToMaster() //정상적으로 마스터 서버에 접속이 되면 호출된다.
    {
        //마스터 서버에 접속이 되었는지 디버깅 한다.
        Debug.Log("Connected to Master");
        Debug.Log($"In Lobby = {PhotonNetwork.InLobby}"); //로비에 들어와 있으면 True, 아니면 False 반환. Master 서버에는 접속했지만 로비에는 아니므로 False 반환된다.
        //로비 접속
        PhotonNetwork.JoinLobby();
    }

    public override void OnJoinedLobby() //로비에 접속이 제대로 되었다면 해당 콜백함수 호출
    {
        Debug.Log($"In Lobby = {PhotonNetwork.InLobby}"); //로비에 접속이 되었다면 True가 반환 될 것이다.
        //방 접속 방법은 두 가지. 1.랜덤 매치메이킹, 2.선택된 방 접속
        PhotonNetwork.JoinRandomRoom();
    }

    //방 생성이 되지 않았으면 오류 콜백 함수 실행
    public override void OnJoinRandomFailed(short returnCode, string message)
    {
        Debug.Log($"JoinRandom Failed {returnCode}: {message}");

        //룸 속성 설정
        RoomOptions roomOptions = new RoomOptions();
        //룸의 접속할 수 있는 최대 접속자 수 최대 제한을 해놔야 CCU를 제한할 수 있다.
        roomOptions.MaxPlayers = 20;
        //룸 오픈 여부
        roomOptions.IsOpen = true;
        //로비에서 룸의 목록에 노출시킬지 여부. 공개방 생성
        roomOptions.IsVisible = true;
        //룸 생성
        PhotonNetwork.CreateRoom("Room1", roomOptions); //룸 이름과 룸 설정. 우리는 roomOptions에 설정을 이미 해놓았다.
    }

    //제대로 룸이 있다면 다음의 콜백 함수를 호출한다.
    public override void OnCreatedRoom()
    {
        Debug.Log("Created Room");
        Debug.Log($"Room Name: {PhotonNetwork.CurrentRoom.Name}");
    }

    //룸에 들어왔을 때 콜백 함수
    public override void OnJoinedRoom()
    {
        Debug.Log($"In Room = {PhotonNetwork.InRoom}");
        Debug.Log($"Player Count = {PhotonNetwork.CurrentRoom.PlayerCount}");
        //접속한 사용자 닉네임 확인
        foreach(var player in PhotonNetwork.CurrentRoom.Players)
        {
            //플레이어 닉네임, 유저의 고유값 가져오기
            Debug.Log($"플레이어 닉네임: {player.Value.NickName}, 유저 고유값: {player.Value.ActorNumber}");
        }
    }
}

 

 

플레이어 컴포넌트로 Photon View 추가

포톤이 적용되면서 이미 만들어져있는 Photon View 스크립트를 플레이어의 컴포넌트로 추가한다.

 

Photon Transform View도 이미 만들어져 있는 스크립트이기 때문에 검색해서 추가만 하면 된다.

 

애니메이션도 유저들 끼리 동기화 해야하기 때문에 Photon Animator View도 넣고, 애니메이터에 들어있는 isWalking Bool 파라미터를 Discrete으로 바꾼다. Layer도 바꾼다.(Layer를 왜 바꿨는지는 기억이 나지 않는다...)

 

 


 

 

멀티플레이로 들어오는 Player 랜덤 위치 생성(Instantiate)

이전에 빈 오브젝트로 포인트를 여러 개 만들어서 배열로 받아서 랜덤으로 불러오는 Instantiate를 적용했었다. 일정 지역 안에서 플레이어가 랜덤으로 나올 수 있는 방법이 있을까? x, z 값 범위 사이에 Random.Range를 주는 방법, 특정 콜라이더를 만들고 해당 콜라이더 범위 안에서만 플레이어를 생성하는 방법을 생각 해봤지만 Global 좌표로 봤을 때 rotation이 틀어져 있어서 x, z 값 범위 랜덤은 힘들 것으로 보인다.

 

큐브를 하나 만들어서 해당 범위 안에 프리팹이 생성되게 한다. Mesh Renderer는 불필요하므로 껐고, 콜라이더만 원하는 크기로 수정하였다. 이제 스크립트로 해당 콜라이더의 bound 범위를 가져와서 사이의 x, z 값을 랜덤 적용해본다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//Photon 네임스페이스 추가
using Photon.Pun;
using Photon.Realtime;

public class PhotonManager : MonoBehaviourPunCallbacks //상속 클래스 변경
{
    //변수 선언
    private readonly string version = "1.0";//게임 버전 체크. 유저가 건드리지 못하게 private, readonly
    private string userId = "Victor"; //아무거나 userId 생성

    //플레이어 범위 랜덤 생성을 위한 콜라이더 변수 선언
    public Collider spawnArea;

    //네트워크 접속은 Start()보다 먼저 실행되어야한다. Awake() 함수 사용
    private void Awake()
    {
        //씬 동기화. 맨 처음 접속한 사람이 방장이 된다.
        PhotonNetwork.AutomaticallySyncScene = true;
        //버전 할당. 위에 string으로 만들었던 version을 쓴다.
        PhotonNetwork.GameVersion = version;
        //App ID 할당. 위에 userId로 만들었던 userId를 쓴다.
        PhotonNetwork.NickName = userId;
        //포톤 서버와의 통신 횟수를 로그로 찍기. 기본값 : 30
        Debug.Log(PhotonNetwork.SendRate); //제대로 통신이 되었다면 30이 출력된다.
        //포톤 서버에 접속
        PhotonNetwork.ConnectUsingSettings();
    }

    //CallBack 함수
    public override void OnConnectedToMaster() //정상적으로 마스터 서버에 접속이 되면 호출된다.
    {
        //마스터 서버에 접속이 되었는지 디버깅 한다.
        Debug.Log("Connected to Master");
        Debug.Log($"In Lobby = {PhotonNetwork.InLobby}"); //로비에 들어와 있으면 True, 아니면 False 반환. Master 서버에는 접속했지만 로비에는 아니므로 False 반환된다.
        //로비 접속
        PhotonNetwork.JoinLobby();
    }

    public override void OnJoinedLobby() //로비에 접속이 제대로 되었다면 해당 콜백함수 호출
    {
        Debug.Log($"In Lobby = {PhotonNetwork.InLobby}"); //로비에 접속이 되었다면 True가 반환 될 것이다.
        //방 접속 방법은 두 가지. 1.랜덤 매치메이킹, 2.선택된 방 접속
        PhotonNetwork.JoinRandomRoom();
    }

    //방 생성이 되지 않았으면 오류 콜백 함수 실행
    public override void OnJoinRandomFailed(short returnCode, string message)
    {
        Debug.Log($"JoinRandom Failed {returnCode}: {message}");

        //룸 속성 설정
        RoomOptions roomOptions = new RoomOptions();
        //룸의 접속할 수 있는 최대 접속자 수 최대 제한을 해놔야 CCU를 제한할 수 있다.
        roomOptions.MaxPlayers = 20;
        //룸 오픈 여부
        roomOptions.IsOpen = true;
        //로비에서 룸의 목록에 노출시킬지 여부. 공개방 생성
        roomOptions.IsVisible = true;
        //룸 생성
        PhotonNetwork.CreateRoom("Room1", roomOptions); //룸 이름과 룸 설정. 우리는 roomOptions에 설정을 이미 해놓았다.
    }

    //제대로 룸이 있다면 다음의 콜백 함수를 호출한다.
    public override void OnCreatedRoom()
    {
        Debug.Log("Created Room");
        Debug.Log($"Room Name: {PhotonNetwork.CurrentRoom.Name}");
    }

    //룸에 들어왔을 때 콜백 함수
    public override void OnJoinedRoom()
    {
        Debug.Log($"In Room = {PhotonNetwork.InRoom}");
        Debug.Log($"Player Count = {PhotonNetwork.CurrentRoom.PlayerCount}");
        //접속한 사용자 닉네임 확인
        foreach (var player in PhotonNetwork.CurrentRoom.Players)
        {
            //플레이어 닉네임, 유저의 고유값 가져오기
            Debug.Log($"플레이어 닉네임: {player.Value.NickName}, 유저 고유값: {player.Value.ActorNumber}");
        }

        if(spawnArea != null) //만약 콜라이더가 비어있지 않다면,
        {
            Vector3 randomPoint = GetRandomPointInCollider(spawnArea);
            PhotonNetwork.Instantiate("Player", randomPoint, Quaternion.identity, 0);
        }
        else
        {
            Debug.Log("Spawn Area not assigned");
        }

        /*
        //플레이어 생성 포인트 그룹 배열을 받아오기. 포인트 그룹의 자식 오브젝트의 Transform 받아오기.
        Transform[] points = GameObject.Find("PointGroup").GetComponentsInChildren<Transform>();
        //1부터 배열의 길이까지의 숫자 중 Random한 값을 추출
        int idx = Random.Range(1, points.Length);
        //플레이어 프리팹을 추출한 idx 위치와 회전 값에 생성. 네트워크를 통해서.
        PhotonNetwork.Instantiate("Player", points[idx].position, points[idx].rotation, 0);
        */
    }

    Vector3 GetRandomPointInCollider(Collider collider)
    {
        Bounds bounds = collider.bounds; //콜라이더의 좌표 가져오기

        float randomX = Random.Range(bounds.min.x, bounds.max.x);
        float randomZ = Random.Range(bounds.min.z, bounds.max.z);

        return new Vector3(randomX, 3.044f, randomZ); //플레이어의 높이는 고정
    }
}

 

위의 코드를 적고 "Player"라는 이름의 프리팹을 가져오기 위해서 Player 프리팹을 Resources 폴더를 만들어서 넣는다.

 

랜덤 생성은 잘된 것으로 보이나, Virtual Camera가 플레이어를 따라오지 않는다. 일단은 플레이어가 벽 속에 들어가는 현상도 있어서 랜덤 생성 콜라이더의 크기를 조금 줄인다. 그리고 벽에 부딪힐 수 있게 벽에도 콜라이더를 넣는다.

 

 

카메라가 따라오지 않는 문제를 해결했다! Hierarcy의 vertual Camera는 새로 생성된 Player의 transform 값을 가져오지 못했던 것이고, Follow와 LootAt에 내 transform을 넣으니 내 캐릭터를 따라온다.

void Start()
{
    characterController = GetComponent<CharacterController>();
    transform = GetComponent<Transform>();
    camera = Camera.main;

    //virtualCamera 가져오기
    virtualCamera = GameObject.FindObjectOfType<CinemachineVirtualCamera>();

    //애니메이터 가져오기
    anim = GetComponent<Animator>();

    playerState = PlayerState.Idle;

    //포톤뷰 가져오기
    pv = GetComponent<PhotonView>();

    if (pv.IsMine)//내 클라이언트 라면, (남의 캐릭터를 움직일 수 없게 함)
    {
        virtualCamera.Follow = transform; //나 자신의 transform
        virtualCamera.LookAt = transform; //나 자신의 transform
    }
}

 

 

Window 모드로 빌드 후 두 개를 켜서 테스트한다.

 

포톤 연결이 성공하고 각각 캐릭터의 카메라도 잘 돌아가서 다행이다!

 


 

추가로 넣고 싶은 내용들

  • 기존 교실 복사해서 교실 여러 개 생성
  • 교실에 추가 내부 에셋 넣기(쓰레기통 등)
  • 현실과 비슷하게 건물 내부 텍스쳐링
  • 건물 가운데에 난간 에셋 추가
  • 캐릭터 화면에 메뉴 UI 넣기
  • 미니맵 넣기
  • 화재용 불 에셋 가져오기
  • 화재 이벤트 생성, 캐릭터 UI에 표시
  • 건물의 실제 위치와 동일한 곳에 소화기 배치
  • 플레이어가 소화기와 상호작용
728x90
반응형