This document is about: PUN 1
SWITCH TO

PUN Classic (v1), PUN 2, Bolt는 휴업 모드입니다. Unity2022에 대해서는 PUN 2에서 서포트하지만, 신기능의 추가는 없습니다. 현재 이용중인 고객님의 PUN 및 Bolt 프로젝트는 중단되지 않고, 퍼포먼스나 성능이 떨어지는 일도 없습니다. 앞으로의 새로운 프로젝트에는 Photon Fusion 또는 Quantum을 사용해 주십시오.

파트 5 - 플레이어 만들기

이 섹션에서는 이 튜토리얼에서 사용할 플레이어 [프리팹]을 처음부터 생성하는 단계별 생성 절차를 통해 안내해 드릴 것 입니다.

PUN 연결 없이 작동할 수 있는 플레이어 [프리팹]을 생성하여 시도하는 것이 좋은 방식으로 테스트, 디버그가 쉬우며 네트워키 기능 없이 모든 기능이 올바르게 작동하는지 확인 할 수 있습니다. 그리고 천천히 네트워크 기능을 추가하면서 구축해 나갈 수 있습니다: 전형적으로 다른 플레이어들의 컴퓨터가 아닌 플레이어가 소유한 인스턴스에서만 사용자 입력이 활성화 되게 됩니다. 아래에서 이것에 대해 상세히 다룰 것 입니다.

Prefab 기초

PUN 에서 가장 먼저 알아야 하고 중요한 것은 네트워크 상에서 Prefab 이 인스턴스화 되는 것으로 프리팹은 Resources 폴더내에 있어야만 합니다. Resources 안에 Prefabs 을 갖고 있는 것에는 두 번째로 중요한 사이드 이펙트는 프리팹의 이름을 감시해야 한다는 것 입니다.

에셋의 Resources 패스에 동일한 이름을 가진 2개의 Prefab이 있어서는 안됩니다. 왜냐하면 유니티는 찾아지는 첫 번째 것을 고르기 때문에 항상 프로젝트 에셋내에서 Resources 폴터 패스에 동일한 이름을 가진 Prefabs 이 존재 하지 않는 것을 항상 확인 하시기 바랍니다.

이제 유니티가 무료 에셋으로 제공 하고 있는 Kyle Robot 을 사용할 것입니다. Kyle 은 Fbx 파일로 제공되며 3ds Max, Maya , cinema4d 와 같은 3D 소프트웨어에서 생성되는 것 입니다. 이러한 소프트웨에의 메시 또는 애니메이션을 다루기에는 이 튜토리얼의 범위를 벗어납니다. 하지만 이런 것은 나만의 캐릭터와 애니메이션을 창조하는 기본이긴 합니다. 이 Robot Kyle.fbx 파일은 /Assets/Photon Unity Networking/Demos/Shared Assets/ 에 위치하고 있습니다.

kyle robot fbx asset
Kyle Robot Fbx 에셋

플레이어로 Kyle Robot.fbx 를 사용하기 위한 방법 입니다:

  1. Project Browser 에서 컨텐츠를 관리 해주는 "Resources" 와 동일한 이름의 폴더를 생성합니다. 'DemoAnimator_tutorial/Resources/' 이런 식으로 말이죠.
  2. 새로운 비어있는 신을 생성하고 /PunBasics_tutorial/Scenes/Kyle Test 로 저장 합니다.
  3. 신 Hierarchy 에서 Robot Kyle 을 드래그앤 드롭 합니다.
  4. Hierarchy 에서 방금 생성한 게임 오브젝트를 My Robot Kyle로 이름을 변경 합니다.
  5. My Robot Kyle/PunBasics_tutorial/Resources/ 로 드래그앤 드롭 합니다.

이제 Kyle Robot 에셋을 기반으로 한 [프리팹]을 생성 했고 Kyle Test 신의 Hierarchy 에는 이 프리팹 인스턴스가 있습니다. 이제 이것으로 작업을 진행할 수 있습니다.

CharacterController

  1. 이제 Hierarchy 의 My Kyle Robot 인스턴스에게 CharacterController Component 를 추가 하겠습니다. [프리팹] 자체에 직접 수행 할 수 도 있지만 좀 더 빠른 방식으로 변형할 필요가 있습니다.

    이 컴포넌트는 유니티가 제공하는 아주 편리한 표준 에셋으로 애니메이터를 사용하고 있는 캐릭터를 더 빠르게 만들 수 있도록 해주니 이러한 유니티의 기능을 이용 하도록 하겠습니다.
  2. My Kyle Robot 를 더블클릭하여 Scene View 에서 크게 보이도록 합니다. 발 끝 중심에 Capsule Collider 가 있는 것을 주목합니다; Capsule Collider가 캐릭터에 맞게 위치해 있어야 합니다.

  3. CharacterController Component에서 Center.y 프로퍼티를 1로 변경 합니다( Height 프로퍼티의 반).

    kyle robot capsule collider
    Kyle Robot Capsule Collider
  4. Apply 를 눌러 변경사항을 적용 하세요. 매우 중요한 단계로 My Kyle Robot 프리팹의 인스턴스를 편집 했으나 하나의 인스턴스가 아닌 모든 인스턴스에 변경사항이 적용 되어야 하므로 Apply 를 누르는 것 입니다.

    apply prefab changes
    Apply Prefab Changes

Animator 설정


Animator Controller 지정하기

Kyle Robot Fbx 에셋은 Animator Graph 로 제어 되어야 합니다. 이 튜토리얼에서는 이 그래프에 대한 생성 방법을 다루지는 않습니다. 그리고 이에 대한 컨트롤러는 프로젝트 에셋내의 Photon Unity Networking/Demos/PunBasics/Animator/ 에서 Kyle Robot 이라고 하는 컨트롤러를 제공하고 있습니다.

animatorcontroller
AnimatorController

[프리팹]에 Kyle Robot 컨트롤러를 할당 해주기 위해서는 Animator 컴포넌트의 Controller 프로퍼티를 Kyle Robot 으로 설정 합니다.

assigning animatorcontroller
Assigning AnimatorController

My Kyle Robot의 인스턴스에서 진행 했다면 Apply 버튼을 눌러 이러한 변경사항이 [프리팹] 에 적용 될 수 있도록 해야 한다는 것을 잊지 마세요.

Controller 파라미터로 작업 하기

Animator Controller 에서 이해 해야 하는 중요한 특징은 Animation Parameters로 스크립트를 통해 애니메이션을 제어하는 것 입니다. 우리의 경우에 있어서 Speed, Direction, Jump, Hi와 같은 파라미터들이 있습니다. Animator Component 의 훌륭한 기능들 중의 하나는 애니메이션에 기반하여 캐릭터를 실제로 움직 일 수 있다는 것으로 이 기능은 Root Motion 이라고 부르며Animator Component 에 디폴트로 true 인 Apply Root Motion 프로퍼티가 있습니다. 이제 사실상 걷고 있는 캐릭터가 있기 때문에 Speed Animation Parameter를 양수 값으로 설정하여 앞으로 걸어가게 할 필요가 있습니다. 시작 해 봅시다!

Animator Manager 스크립트

사용자 입력값에 따라 캐릭터를 제어 할 수 있는 새로운 스크립트를 생성 하도록 하겠습니다.

  1. DemoAnimator/tutorial/Scripts/PlayerAnimatorManager 이름의 새로운 C# 스크립트를 생성 합니다.
  2. 이 스크립트를 My Robot Kyle [프리팹]에 붙입니다.
  3. 아래와 같이 Com.MyCompany.MyGame 네임스페이스로 클래스를 둘러 쌉니다.
  4. Start()Update() 를 region MONOBEHAVIOUR MESSAGES 안에 놓습니다.

C#

using UnityEngine;
using System.Collections;

namespace Com.MyCompany.MyGame
{
    public class PlayerAnimatorManager : MonoBehaviour 
    {
        #region MONOBEHAVIOUR MESSAGES

        // Use this for initialization
        void Start () {
            
        }
        
        // Update is called once per frame
        void Update () {
            
        }
        
        #endregion
    }
}

Animator Manager: 속도 제어

우선 첫 번째로 코딩할 필요가 있는 것은 Animator Component 를 얻어 제어 할 수 있도록 하는 것 입니다.

  1. PlayerAnimatorManager 스크립트를 편집하는지 확인 해 주세요.

  2. Animator 타입의 private 변수 animator 를 생성 합니다.

  3. Start() 메소드에서 Animator 컴포넌트를 이 변수에 저장 합니다.

    C#

        private Animator animator;
        // Use this for initialization
        void Start () {
            animator = GetComponent<Animator>();
            if (!animator)
            {
                Debug.LogError("PlayerAnimatorManager is Missing Animator Component",this);
            }
        }
    

    우리가 Animator Component 가 필요하기 때문에 만약 이 컴포넌트를 얻지 못했다면 개발자가 바로 알 수 있도록 에러를 로그한다는 것을 주목 하세요. 다른 사람에 의해서 사용될 수도 있기 때문에 항상 이런 방식으로 코딩 합니다 :) 지루하긴 하지만 장기적인 관점에서 그만큼 가치가 있습니다.

  4. 이제 [사용자 입력]을 기다리고 Speed Animation Parameter를 제어 합니다. 그리고 PlayerAnimatorManager 스크립트를 저장 합니다.

C#

    // Update is called once per frame
    void Update () {

        if (!animator)
        {
            return;
        }

        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");
        
        if( v < 0 )
        {
            v = 0;
        }
        
        animator.SetFloat( "Speed", h*h+v*v );

    }

스크립트가 어떤 기능을 하는 지 학습해 보겠습니다: 게임에서는 뒤로 가는 것을 허용하지 않기 때문에 v 가 0 보다 작은 값인지 확인 하세요. 사용자가 아래화살표 키 또는 's' 키를 입력하면 허용하지 않고 값을 0 으로 설정합니다.

두 입력값을 제곱하고 있다는 것을 알아 챘을 것 입니다. 왜죠? 항상 양의 절대 값이고 easing을 추가하기 때문 입니다.

절묘한 트릭입니다. Mathf.Abs() 도 역시 사용할 수 있으며 잘 동작 합니다.

Speed를 제어하기 위한 두 개의 입력을 추가하여 우측 입력의 왼쪽을 누를 때 속도를 얻게 될 것 입니다.

물론 이러한 모든 것은 우리 캐릭터 디자인 특성이고 게임 로직에 의존 하고 있으며 캐릭터가 제자리에서 회전 하기를 원할 수 도 있고 또는 뒤로 갈 수 있는 능력을 부여 하길 원할 수 도 있습니다. 즉, [Animation 파라미터]들을 제어하는 것은 게임마다 다르게 됩니다.

테스트, 테스트, 1 2 3...

지금까지 우리가 진행해 왔던 것을 검증 해 보도록 하겠습니다. Kyle Test 신이 열려져 있는지 확인 해 주세요. 현재 이 신에서 하나의 카메라와 Kyle Robot 인스턴스가 있으며 로봇이 서 있을 땅이 없습니다. 지금 실행 시키면 Kyle 로봇은 아래로 떨어질 것 입니다. 또한 우리는 신에 빛과 같은 것이 하나도 없으며 캐릭터와 스크립트가 잘 동작 하는지 테스트 하고 검증만 하도록 할 것 입니다.

  1. 신에 큐브를 추가 합니다. 큐브는 디폴트로 Box Collider 를 가지고 있으므로 바닥으로 사용하기 좋습니다.
  2. 큐브의 높이가 1이기 때문에 Position 을 0,-0.5,0 로 설정 합니다. 큐브의 윗면을 바닥으로 사용할 것 입니다.
  3. 큐브의 Scale 을 30,1,30 로 설정 합니다. 이제 우리는 실험 장소를 만들었습니다.
  4. 카메라를 선택 하여 보기 좋게 위치를 움직여 줍니다. 편리한 트릭은 신 뷰를 보기 좋게 표시한 후 카메라를 선택하고 메뉴에서 "GameObject/Align With View" 를 선택하면 카메라가 신 뷰에 맞춰 지게 됩니다.
  5. 마지막 단계에서 My Robot Kyle 을 y 를 0.1 증가 시켜 줍니다. 이렇게 하지 않으면 충돌 감지가 되지 않아 캐릭터가 바닥 아래로 떨어지게 됩니다. 따라서 접촉을 만들어 시뮬레이션 하기 위하여 두 collider 간에 물리적인 공간이 두어야 합니다.
  6. 신을 플레이하고 위 화살표 또는 'a' 키를 누르면 캐릭터가 걷게 됩니다. 모든 키를 확인 해 볼 수 있습니다.

좋습니다. 하지만 앞으로 해야 할 일이 많습니다. 카메라가 따라가야 하고, 그리고 아직 회전 하지도 못했습니다...

지금 카메라에 대해서 계속 작업 하길 원하면 전용 섹션으로 가서 이 페이지의 남은 부분에서는 Animator 제어와 회전의 구현을 끝낼 것 입니다.

Animator Manager 스크립트: 방향 제어

회전을 제어하는 것은 좀 더 복잡한 문제 입니다; 왼쪽 또는 오른쪽 키를 누를 때 캐릭터가 회전을 갑자기 하는 것을 완만하고 부드럽게 회전 하는 것을 원합니다. 다행스럽게도 Animation Parameter 는 감쇠를 위해서 사용 될 수 있습니다.

  1. PlayerAnimatorManager 를 편집하고 있는지 확인 하세요.

  2. public float 변수 DirectionDampTime 를 "PRIVATE PROPERTIES" region 위에 "PUBLIC PROPERTIES" region 에 생성 하세요.

    C#

    //#region PUBLIC PROPERTIES
    public float DirectionDampTime = .25f;
    //#endregion
    
  3. Update() 함수에 추가 합니다:

    C#

    animator.SetFloat( "Direction", h, DirectionDampTime, Time.deltaTime );
    

    우리는 직관적으로 animator.SetFloat() 에 다른 특징이 있다는 것을 알아 챘습니다. Speed 를 제어하는 데 사용한 것은 직관적 이었으나 여기에서는 두 개의 파라미터를 더 받고 있습니다. 하나는 damping time 이고 하나는 deltaTime 입니다. Damping time 은 일리가 있습니다: 이 것은 원하는 값까지 도달하는데 걸리는 시간입니다. 하지만 deltaTime은요? Update()가 프레임율에 의존적이기 떄문에 프레임율 독립적으로 코드를 작성할 수 있도록 해 주는 것 입니다. 우리는 deltaTime 을 카운터로 사용할 필요가 있습니다. 이 주제에 대해서 가능한 많이 읽어 보고 웹 검색에서도 많이 찾을 수 있을 것 입니다. 개념에 대해서 이해 한 후에 수 많은 유니티 기능을 작성할 수 있을 것이고 애니메이션과 일관성 있는 제어를 시간에 구애 받지 않고 할 수 있을 것 입니다.

  4. 스크립트를 저장 합니다.

  5. 신을 플레이하고 화살표키를 사용하여 캐릭터가 걷고 회전을 잘 하는지 살펴 봅니다.

  6. DirectionDampTime 효과를 테스트 해 보세요: 1을 넣고 보고 확인 후 5 를 넣어서 확인 해 보세요. 회전 반경이 DirectionDampTime 에 대해서 증가되는 것을 볼 수 있을 것 입니다.

Animator Manager 스크립트: 점프하기

두 개의 팩터 때문에 점프에 대해서 좀 더 많은 작업을 해 주어야 합니다. 하나는 달리고 있지 않으면 점프를 하면 안되며 두 번째는 반복하여 점프를 해서는 안됩니다.

  1. PlayerAnimatorManager 스크립트를 편집하고 있는지 확인 해 주세요.

  2. Update() 메소드에서 사용자 입력을 얻는 부분 앞에 삽입 합니다.

    C#

        // deal with Jumping
        AnimatorStateInfo stateInfo = animator.GetCurrentAnimatorStateInfo(0);            
        // only allow jumping if we are running.
        if (stateInfo.IsName("Base Layer.Run"))
        {
            // When using trigger parameter
            if (Input.GetButtonDown("Fire2")) animator.SetTrigger("Jump"); 
        }
    
  3. 스크립트를 저장 합니다.

  4. 테스트 합니다. 달리기를 하고 "alt" 키를 누르면 Kyle 은 점프를 해야 합니다.

좋습니다. 우선 첫 번째로 이해해야 하는 것은 animator 가 달리고 있는지에 대한 것을 어떻게 판단하는지에 대한 것입니다. stateInfo.IsName("Base Layer.Run") 을 이용해 판단 할 것 입니다. Animator 의 현재 활성 상태가 Run 인지에 대하여 문의 합니다. Base Layer 안에 Run 상태가 있으므로 Base Layer 를 추가 해야 합니다.

Run 상태에 있다면 Fire2 Input을 감지하며 필요시에 Jump 를 트리거 발생시킵니다.

지금까지 했던 PlayerAnimatorManager 의 전체 소스 입니다:

C#

using UnityEngine;
using System.Collections;

namespace Com.MyCompany.MyGame
{
    public class PlayerAnimatorManager : MonoBehaviour 
    {

        #region PRIVATE PROPERTIES

        public float DirectionDampTime = .25f;

        #endregion

        #region PRIVATE PROPERTIES
        
        private Animator animator;

        #endregion

        #region MONOBEHAVIOUR MESSAGES
        
        // Use this for initialization
        void Start () {
            animator = GetComponent<Animator>();
            if (!animator)
            {
                Debug.LogError("PlayerAnimatorManager is Missing Animator Component",this);
            }

        }
        
        // Update is called once per frame
        void Update () {

            if (!animator)
            {
                return;
            }

            // deal with Jumping
            AnimatorStateInfo stateInfo = animator.GetCurrentAnimatorStateInfo(0);            
            // only allow jumping if we are running.
            if (stateInfo.IsName("Base Layer.Run"))
            {
                // When using trigger parameter
                if (Input.GetButtonDown("Fire2")) animator.SetTrigger("Jump"); 
            }

            float h = Input.GetAxis("Horizontal");
            float v = Input.GetAxis("Vertical");
            
            if( v < 0 )
            {
                v = 0;
            }
            
            animator.SetFloat( "Speed", h*h+v*v );

            animator.SetFloat( "Direction", h, DirectionDampTime, Time.deltaTime );
        }
        
        #endregion
    }
}

신에서 보였던 것에 비하면 그리 많은 코드는 아닙니다. 카메라가 따라 다니도록 하여 세계가 우리 주위에 있는 것처럼 하기 위하여 이제 카메라에 대한 처리를 하도록 하겠습니다.

카메라 설정

이 장에서는 플레이어 프리팹의 전체적인 생성 과정에 집중을 하고 있기 때문에 CameraWork 스크립트를 사용하는 것을 살펴 보도록 하겠습니다. CameraWork를 처음부터 작성하고 싶으면 다음 파트로 가서 학습 한 후 다시 와 주시기 바랍니다.

  1. My Kyle Robot 프리팹에 CameraWork 컴포넌트를 추가 합니다.
  2. Follow on Start 프로퍼티를 On 해줍니다. 처음부터 카메라가 따라다니도록 만들어 주게 되는 것 입니다. 네트워크 구현을 시작 할 때는 off 로 꺼두도록 할 것 입니다.
  3. Center Offset 프로퍼티를 0,4,0 로 설정하여 카메라를 좀 더 높이 두게 되어 좀 더 나은 뷰를 제공 해 줍니다.
  4. Kyle Test 신을 실행 하고 캐릭터를 움직여 카메라가 잘 따라가고 있는지 확인 해 보세요.

Beams 설정

로봇 캐릭터는 아직 무기가 없습니다. 눈에서 발사되는 레이저 빔을 생성 하도록 하겠습니다.

Adding the Beams models

단순하게 진행하기 위해서 큐브 하나를 이용하여 매우 가늘고 길게 만들 것 입니다. 이 작업을 빠르게 할 수 있는 트립이 있습니다:머리의 자식으로 큐브를 추가 하지말고 자신이 생성하고 이동하고 확대하여 머리에 붙입니다. 이렇게 하면 눈에 맞추어 광선의 적절한 회전 값을 추측하지 않아도 됩니다.

또 다른 중요한 트릭은 두 빔에서 하나의 Collider 만을 사용하는 것 입니다. 물리엔진이 잘 동작 하게 하기 위함 입니다. 가느다른 collider 는 신뢰할 수 없기 때문에 좋은 생각은 아닙니다. 따라서 목표물을 맟추었는지 신뢰할 수 있게 큰 박스 collider 를 만들 것 입니다.

  1. Kyle test 신을 오픈 합니다.
  2. 신에 큐브를 추가하고Beam Left로 이름을 부여 합니다.
  3. 긴 광선 처럼 보기오록 변경 하고 왼쪽 눈과 맟추어 위치가 적절한지 위치를 조정합니다.
  4. HIerarchy 에서 My Kyle Robot 인스턴스를 선택 하세요.
  5. Head child 로 위치 시킵니다.
kyle robot head hierarchy
Kyle Robot Head Hierarchy
1. 자식(child)로 빈 게임오브젝트를 생성 하여 `Head` 게임 오브젝트의 자식으로 이름은 `Beams` 로 부여 합니다.
  1. Beam LeftBeams 안으로 드래그앤 드롭 합니다.
  2. Beams Left 복사하여 Beams Right 로 이름을 부여 합니다.
  3. 오른쪽 눈에 맞춰 질 수 있도록 위치를 이동 시켜 주시기 바랍니다.
  4. Beams Right 에서 Box Collider 컴포넌트를 제거 합니다.
  5. Beams Left 의 Box Collider 를 중앙으로 조정하고 두 빔을 감싸 질 수 있도록 크기를 조정 합니다.
  6. 새로운 Material 을 생성하고 Red Beam으로 이름을 정합니다.'DemoAnimator_tutorial/Scenes/' 에 저장 합니다.
  7. 두 개의 빔에 Red Beam 을 할당 합니다.
  8. 프리팹에 변경사항을 적용 합니다.

이제 다음과 같이 보여야 할 것 입니다.

kyle robot beams
Kyle Robot Head Beams
kyle robot updated head hierarchy
Kyle Robot Updated Head Hierarchy

Controlling the Beams with User Input

좋습니다. 이제 우리는 빔을 갖게 되었습니다. 입력을 받아 발사 할 수 있도록 해보겠습니다.

PlayerManager 이름을 가진 새로운 C# 스크립트를 생성 합니다. 빔이 작동하기 위한 초기 버전의 전체 소스 코드 입니다.

C#

using UnityEngine;
using UnityEngine.EventSystems;

using System.Collections;

namespace Com.MyCompany.MyGame
{
    /// <summary>
    /// Player manager. 
    /// Handles fire Input and Beams.
    /// </summary>
    public class PlayerManager : MonoBehaviour {

        #region Public Variables

        [Tooltip("The Beams GameObject to control")]
        public GameObject Beams;

        #endregion

        #region Private Variables

        //True, when the user is firing
        bool IsFiring;

        #endregion

        #region MonoBehaviour CallBacks

        /// <summary>
        /// MonoBehaviour method called on GameObject by Unity during early initialization phase.
        /// </summary>
        void Awake()
        {
            if (Beams==null)
            {
                Debug.LogError("<Color=Red><a>Missing</a></Color> Beams Reference.",this);
            }else{
                Beams.SetActive(false);
            }

        }

        /// <summary>
        /// MonoBehaviour method called on GameObject by Unity on every frame.
        /// </summary>
        void Update()
        {
            ProcessInputs ();

            // trigger Beams active state 
            if (Beams!=null && IsFiring != Beams.activeInHierarchy) {
                Beams.SetActive(IsFiring);
            }
        }

        #endregion

        #region Custom

        /// <summary>
        /// Processes the inputs. Maintain a flag representing when the user is pressing Fire.
        /// </summary>
        void ProcessInputs()
        {
            
            if (Input.GetButtonDown ("Fire1") ) {
                if (!IsFiring)
                {
                    IsFiring = true;
                }
            }
            
            if (Input.GetButtonUp ("Fire1") ) {
                if (IsFiring)
                {
                    IsFiring = false;
                }
            }
        }
        #endregion

    }
}

이 단계 스크립트에서 중요한 점은 광선을 활성화거나 비활성화 하는 것 입니다. 활성화 되었을 때 Beam은 다른 모델과 충돌을 효과적으로 트리거 해 줄 것 입니다. 그리고 이러한 트리거를 각 캐릭터의 체력에 영향을 주도록 할 것 입니다. My Kyle Robot 프리팹의 Hierarchy 내부의 게임오브젝트를 추출하여 참조할 public 프로퍼티 Beams 를 노출 시켜 놓았습니다. Beams를 연결시키기 위해 필요한 작업에 대해서 살펴 보도록 하겠습니다. 프리팹이 Asset 브라우저 안에 있어 애매모호 하기 때문에 프리팹은 첫 번째 child 만 노출 합니다. 그리고 우리 Beams 는 프리팹 Hierarchy 내부 안에 깊숙히 감추어져 있기 때문에 신의 인스턴스로 부터 가져와야 합니다. 그리고 프리팹 자체로 되돌아와 Apply 합니다.

  1. Kyle test 신을 오픈 합니다.
  2. 신 Hierarchy 에서 My Kyle Robot을 선택 합니다.
  3. My Kyle RobotPlayerManager 컴포넌트를 추가 합니다.
  4. 인스펙터에서 PlayerManager Beams 프로퍼티에 My Kyle Robot/Root/Ribs/Neck/Head/Beams 를 드래그앤 드롭 합니다.
  5. 프리팹으로 다시 돌아와서 변경사항을 적용 시켜 줍니다.

플레이 하여 Fire1Input(디폴트로 왼쪽 마우스클릭 또는 왼쪽 컨트롤 키 입니다.)를 누르면 광선이 나타날 것 이고 떼면 즉시 사라지게 됩니다.

헬스 설정

이제 광선이 플레이어를 맞추었을 때 체력이 감소하는 아주 간단한 헬스 시스템을 구현하도록 하겠습니다. 총알이 아니고 에너지가 일정한 흐름이므로 체력 소진에 대해 두가지의 방식이 있습니다. 광선에 맞았을 때와 광선을 맞는 기간 입니다.

  1. Player Manager 스크립트를 오픈 합니다.

  2. Public Variables region 에 public health 프로퍼티를 추가 합니다.

    C#

    [Tooltip("The current Health of our player")]
    public float Health = 1f;
    
  3. MonoBehaviour CallBacks 지역에 다음의 두 개의 메소드를 추가 합니다. 그리고 Player Manager 스크립트를 저장 합니다.

C#

    /// <summary>
    /// MonoBehaviour method called when the Collider 'other' enters the trigger.
    /// Affect Health of the Player if the collider is a beam
    /// Note: when jumping and firing at the same, you'll find that the player's own beam intersects with itself
    /// One could move the collider further away to prevent this or check if the beam belongs to the player.
    /// </summary>
    void OnTriggerEnter(Collider other) {

        if (! photonView.isMine) {
            return;
        }


        // rule out any trigger not tagged "Beam". We are only interested in Beamers
        if (! other.CompareTag ("Beam"))
        {
            return;
        }

        Health -= 0.1f;
    }

    /// <summary>
    /// MonoBehaviour method called once per frame for every Collider 'other' that is touching the trigger.
    /// We're going to affect health while the beams are touching the player
    /// </summary>
    /// <param name="other">Other.</param>
    void OnTriggerStay(Collider other) {

        // we dont' do anything if we are not the local player.
        if (! photonView.isMine) {
            return;
        }

        // rule out any trigger not tagged "Beam". We are only interested in Beamers
        if (! other.CompareTag ("Beam"))
        {
            return;
        }

        // we slowly affect health when beam is constantly hitting us, so player has to move to prevent death.
        Health -= 0.1f*Time.deltaTime; 
    }

우선, 두 개의 메소드는 거의 동일 하고 차이점이 있다면 TriggerStay 동안 감소 속도를 프레임율에 의존하지 않고 DeltaTime을 사용하여 체력을 감소시킨다는 것 입니다. 이 것은 아주 중요한 개념으로 일반적으로 애니메이션에 적용됩니다. 여기에서도 모든 단말에 대해서 예측할 수 있는 체력감소를 원하기 때문에 필요 합니다. 빠른 컴퓨터에서 체력이 빠르게 감소된다는 것은 공평하지 않겠지요 :) Deltatime 을 통하여 일관성을 보장 해 줍니다.

만약 DeltaTime 에 대해서 의문이 있다면 유니티 커뮤니티에서 찾아 보시고 완전히 이해하신 다음에 여기로 다시 오시기 바랍니다. 아주 기본적인 사항입니다.

두 번째로 중요한 개념은 이미 이해하고 있으셔야 하는 것으로 로컬 플레이어의 체력에만 영향을 주게 되는 것으로 PhotonView 가 Mine 아 아니면 메소드 초반부에서 빠져나왔기 때문입니다.

마지막으로 객체가 우리를 친 것이 광선일 때만 체력에 영향 받기를 원하기 때문에 tag "beam" 을 사용하여 체크할 것 입니다.

쉬운 디버깅을 위해 UI 가 구축 되기전에 Health를 public float 로 만들어 쉽게 값을 체크할 수 있도록 했습니다.

좋습니다. 모든 것이 잘되었나요? 음... 체력 시스템은 체력이 0 이 되어 플레이어의 게임 오버 상태 고려 없이는 완전한 것이 아닙니다. 이제 해 보죠.

게임종료 체력 체크

단순함을 유지 하기 위해서 플레이어 체력이 0 으로 간단하게 룸을 떠나고 GameManager 스크립트에 룸을 떠나는 메소드를 이미 생성 해 놓았다는 것을 기억 하실 수 있을 것 입니다. 동일한 기능을 두번 코딩 할 필요 없이 이 메소드를 재 사용할 수 있으면 정말 좋을 것입니다. 동일한 결과를 내는 중복 코드는 정말 피해야 하는 사항입니다. 여기에서 매우 편리한 프로그래밍 개념인 "싱글톤(Singleton)" 에 대한 소개를 할 적절한 시기 입니다. 이 주제만으로도 강의를 진행 할 수 있겠지만 "Singleton" 에 대한 최소한의 구현만 할 예정 입니다. 싱글톤을 이해 한다는 것은 유니티 컨택스트에서의 변형과 강력한 기능을 구축하는데 매우 중요하며 많은 어려움을 극복할 수 있게 해줄것입니다. 따라서 싱글톤을 학습하는데 이 튜토리얼과는 별도로 하는 것을 주저 하지 마시기 바랍니다.

  1. GameManager 스크립트를 오픈 합니다.

  2. Public Properties Region 에 아래 변수를 추가 합니다.

    C#

        static public GameManager Instance;
    
  3. Add in the Start() method this new piece of code

    C#

    Instance = this;
    
  4. GameManager 스크립트를 저장 합니다.

인스턴스 변수를 [static] 키워드로 했다는 것을 주목 하세요. 이 변수는 GameManager 의 인스턴스 없이 사용 할 수 있도록 합니다. 따라서 코드의 어떤 곳에서도 GameManager.instance.xxx() 를 사용 할 수 있게 됩니다. 정말 실용적입니다! 로직에서 어떻게 게임 오버에 대한 것을 처리 했는지 살펴 보겠습니다.

  1. PlayerManager 스크립트를 오픈 합니다.
  2. Update() 메소드 안에서 ProcessInputs 이후에 다음을 추가하고 PlayerManager 스크립트를 저장 합니다.

    C#

    if (photonView.isMine)
    {
        ProcessInputs();
        if (Health <= 0f)
        {
            GameManager.Instance.LeaveRoom();
        }
    }
    
  3. PlayerManager 스크립트를 저장 합니다.
  • 레이저 빔의 강도가 다르기 때문에 손상 정도가 달라 체력이 음수로 갈 수 있다는 것을 고려했다는 것을 주목 하세요.
  • GameManager 인스턴스의 LeaveRoom() public 메소드에 도달 했을 때 GameManager 컴포넌트가 현재 신의 게임오브젝트에 있다고 사실에 의존하고 있음을 주목 하세요.

좋아요. 이제 우리는 네트워크에 빠져 보도록 하겠습니다!

다음 파트.
이전 파트.

Back to top