[SKKU DT] 37일차 -유니티(Unity) 포톤 클라우드(Photon Cloud)로 멀티플레이 슈팅 게임 만들기 -포톤 설정/플레이어 이동/카메라 회전

2023. 12. 20. 18:11SKKU DT

728x90
반응형

포톤 클라우드를 활용한 네트워크 게임 제작

https://www.photonengine.com/ko-kr#

 

글로벌 크로스 플랫폼 실시간 게임 개발 | Photon Engine

EssentialPhoton Details Discover a summary of our product range, notable features, the power of the Photon Cloud, and our cost-effective pricing plans. HAVE A LOOK

www.photonengine.com

https://doc.photonengine.com/ko-kr/pun/current/getting-started/pun-intro

 

소개 | Photon Engine

Photon Unity Networking (PUN)은 멀티플레이어 게임용 유니티 패키지입니다. 유연한 매치메이킹을 통해 플레이어들은 객체들이 네트워크를 통해 동기화될 수 있는 룸으로 이끌어 줍니다. RPC, 사용자 정

doc.photonengine.com

Photon Engine Documents

 

 

가격 정책은 다음과 같다.

 

 

포톤 서버와 포톤 클라우드

 

 

포톤 동작 방식

처음에 유저는 Name Server에 접속해서 App ID와 게임 버전으로 구분이 된다.

 

 


 

 

포톤 가입을 하면 [관리 화면 이동]에서 새 어플리케이션을 만드는 화면을 볼 수 있다.

 

 

Photon 종류는 Pun으로 설정하여 작성하기 완료한다.

어플리케이션 ID로 유니티와 연동되기 때문에 매우 중요하며 공개되어서는 안된다.

 

 


 

 

**3D 프로젝트를 URP를 바꾸고 싶다면,

[Window] - [Package Manager] - [Universal RP]를 설치한다.

그다음 Project 에서 오른쪽 마우스 클릭, [Create] - [Rendering] - [URP Asset (with Universal Renderer)] 생성한다.

만든 URP 에셋을 적용하려면 [Edit] - [Project Settings] - [Graphics]에서 Scriptable Render Pipeline Settings를 변경한다.

 

 


 

 

Unity Hub에서 새로운 3D URP 프로젝트를 생성한다.

 

 

AssetStore에서 아래의 무료 에셋을 가져온다.

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

 

 

설치하면 팝업으로 App ID 또는 Email을 적으라는 창이 뜨는데 거기에 비공개로 유지해야하는 id키를 넣는다. 만약 창을 닫아버렸다면 [Assets] - [PhotonUnityNetwork] - [Resources] - [PhotonServerSettings]에서 키를 넣으면 된다.

 

다른 방법으로는 [Window] - [Photon Unity Networking] - [PUN Wizard] - [Setup Project]에서 같은 팝업창을 열 수 있다.

 

 


 

 

PhotonManager 스크립트 생성

네임스페이스 추가

using Photon.Pun;
using Photon.Realtime;

 

 

MonoBehaviour 수정. Photon 같은 경우 플레이어를 움직이기 보다는 서버에 접속하는 역할이라 다른 클래스를 상속받는다.

public class PhotonManager : MonoBehaviourPunCallbacks //상속 클래스 변경

 

 

이후 코드 작성

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();
    }
}

 

 

빈 오브젝트를 만들고 스크립트를 넣어서 실행시키면 30이 콘솔창에 찍히게 된다.

 

 

혹시 서버가 불안정하다면 아래 경로의 Photon Server Settings에서 Dev Region을 kr에서 빈 칸으로 비워두면 된다.

 

 


 

 

추가 코드 작성 -콜백 함수 추가

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가 반환 될 것이다.
    }
}

마스터 서버 접속과 로비 서버 접속이 잘 된 것을 볼 수 있다.

만약 접속이 실패했을 때는 유니티가 한참 기다리다가 에러 문구가 뜨는 것을 볼 수 있다.

 

 


 

 

랜덤 룸 입장, 룸이 없다면 오류 코드 출력 콜백 함수 추가

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}");
    }
}

방이 없으면 다음과 같은 메시지가 출력된다.

 

 


 

 

룸을 만들고 룸의 이름을 가져오는 코드를 추가하였다.

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}");
    }
}

 

 


 

 

OnJoinedRoom 콜백 함수 추가

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}");
        }
    }
}

 

 


 

 

유니티 패키지 import -Environment, Player

 

 


 

 

Player에 들어갈 Movement 스크립트 생성

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Movement : MonoBehaviour
{
    CharacterController controller;
    private new Transform transform;
    Animator animator;//애니메이션 컨트롤러 가져오기
    private new Camera camera;

    //가상으로 만든 plane
    Plane plane;
    Ray ray;
    Vector3 hitPoint;
    public float moveSpeed = 10f;

    void Start()
    {
        //캐릭터에 포함된 컴포넌트 가져오기
        controller = GetComponent<CharacterController>();
        transform = GetComponent<Transform>();
        animator = GetComponent<Animator>();
        camera = Camera.main; //카메라는 Tag에 MainCamera로 설정되어 있는 것을 가져온다.
        plane = new Plane(transform.up, transform.position); //노멀 방향은 위쪽으로, 플레이어의 위치에 가상 평면을 만든다.
    }
    void Update()
    {
        Move();
    }

    //함수 밖에 정의하기 위해서 "=>" 사용
    float h => Input.GetAxis("Horizontal");
    float v => Input.GetAxis("Vertical");

    void Move()
    {
        //카메라 설정
        Vector3 cameraForward = camera.transform.forward;
        Vector3 cameraRight = camera.transform.right;
        cameraForward.y = 0.0f; //y축을 쓰지 않기 위해서
        cameraRight.y = 0.0f;
        //두 값 벡터 합
        Vector3 moveDir = (cameraForward * v) + (cameraRight * h);
        moveDir.Set(moveDir.x, 0.0f, moveDir.z);
        controller.SimpleMove(moveDir * moveSpeed); //SimpleMove: 스피드만큼 캐릭터를 이동한다.
    }
}

움직일 수 있지만 Camera의 x, y, z 방향에 맞게 움직이고 있다.

 

 


 

 

캐릭터에 들어있는 Animator Controller - Blend Tree에 있는 파라미터를 이용할 것이다. Blend Tree에 이미 여러 애니메이션이 들어있기 때문에 값을 바꾸면 해당 애니메이션이 표현된다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Movement : MonoBehaviour
{
    CharacterController controller;
    private new Transform transform;
    Animator animator;//애니메이션 컨트롤러 가져오기
    private new Camera camera;

    //가상으로 만든 plane
    Plane plane;
    Ray ray;
    Vector3 hitPoint;
    public float moveSpeed = 10f;

    void Start()
    {
        //캐릭터에 포함된 컴포넌트 가져오기
        controller = GetComponent<CharacterController>();
        transform = GetComponent<Transform>();
        animator = GetComponent<Animator>();
        camera = Camera.main; //카메라는 Tag에 MainCamera로 설정되어 있는 것을 가져온다.
        plane = new Plane(transform.up, transform.position); //노멀 방향은 위쪽으로, 플레이어의 위치에 가상 평면을 만든다.
    }
    void Update()
    {
        Move();
    }

    //함수 밖에 정의하기 위해서 "=>" 사용
    float h => Input.GetAxis("Horizontal");
    float v => Input.GetAxis("Vertical");

    void Move()
    {
        //카메라 설정
        Vector3 cameraForward = camera.transform.forward;
        Vector3 cameraRight = camera.transform.right;
        cameraForward.y = 0.0f; //y축을 쓰지 않기 위해서
        cameraRight.y = 0.0f;
        //두 값 벡터 합
        Vector3 moveDir = (cameraForward * v) + (cameraRight * h);
        moveDir.Set(moveDir.x, 0.0f, moveDir.z);
        controller.SimpleMove(moveDir * moveSpeed); //SimpleMove: 스피드만큼 캐릭터를 이동한다.

        float forward = Vector3.Dot(moveDir, transform.forward);
        float strafe = Vector3.Dot(moveDir, transform.right);

        //Animator Controller에 들어있는 float형태의 두 parameter Forward와 Strafe 가져오기
        animator.SetFloat("Forward", forward); //float 타입 파라미터라 SetFloat를 쓴다.
        animator.SetFloat("Strafe", strafe);
    }
}

 

이제 캐릭터를 움직이면 애니메이션도 적용된다.

 

 


 

 

void Turn() 함수 생성

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Movement : MonoBehaviour
{
    CharacterController controller;
    private new Transform transform;
    Animator animator;//애니메이션 컨트롤러 가져오기
    private new Camera camera;

    //가상으로 만든 plane
    Plane plane;
    Ray ray;
    Vector3 hitPoint;
    public float moveSpeed = 10f;

    void Start()
    {
        //캐릭터에 포함된 컴포넌트 가져오기
        controller = GetComponent<CharacterController>();
        transform = GetComponent<Transform>();
        animator = GetComponent<Animator>();
        camera = Camera.main; //카메라는 Tag에 MainCamera로 설정되어 있는 것을 가져온다.
        plane = new Plane(transform.up, transform.position); //노멀 방향은 위쪽으로, 플레이어의 위치에 가상 평면을 만든다.
    }
    void Update()
    {
        Move();
        Turn();
    }

    //함수 밖에 정의하기 위해서 "=>" 사용
    float h => Input.GetAxis("Horizontal");
    float v => Input.GetAxis("Vertical");

    void Move()
    {
        //카메라 설정
        Vector3 cameraForward = camera.transform.forward;
        Vector3 cameraRight = camera.transform.right;
        cameraForward.y = 0.0f; //y축을 쓰지 않기 위해서
        cameraRight.y = 0.0f;
        //두 값 벡터 합
        Vector3 moveDir = (cameraForward * v) + (cameraRight * h);
        moveDir.Set(moveDir.x, 0.0f, moveDir.z);
        controller.SimpleMove(moveDir * moveSpeed); //SimpleMove: 스피드만큼 캐릭터를 이동한다.

        float forward = Vector3.Dot(moveDir, transform.forward);
        float strafe = Vector3.Dot(moveDir, transform.right);

        //Animator Controller에 들어있는 float형태의 두 parameter Forward와 Strafe 가져오기
        animator.SetFloat("Forward", forward); //float 타입 파라미터라 SetFloat를 쓴다.
        animator.SetFloat("Strafe", strafe);
    }

    void Turn()
    {
        ray = camera.ScreenPointToRay(Input.mousePosition);
        //Plane에 쏠건데 Plane에 맞는 부분
        float enter = 0f;
        plane.Raycast(ray, out enter);
        hitPoint = ray.GetPoint(enter);

        Vector3 lookDir = hitPoint - transform.position;//바라보는 방향 목적지 - 플레이어의 위치
        lookDir.y = 0.0f;//y축은 필요 없음
        transform.localRotation = Quaternion.LookRotation(lookDir);
    }
}

아직 카메라를 플레이어 뒤로 위치하지 않았기 때문에 마우스를 따라 움직이는 플레이어의 회전이 어색하다.

가상 Plane을 만든 이유는 3인칭 카메라를 적용하기 위해서.

728x90
반응형