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

[Unity Course 2] 06. 적 캐릭터 제작 4

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

 

혈흔 효과

해당 에셋의 파티클이 어떤 효과인지 살펴보기

 

지금까지는 프리팹을 연결하여 사용했지만 프리팹을 런 모드에서 스크립트를 통해 로드하는 방법을 사용

 

프로젝트 뷰에서 "Resources" 라는 이름의 폴더를 생성, 이 폴더는 유니티에 예약된 폴더로 대소문자를 지켜야함

 

이 폴더는 런타임에 불러와 사용할 수 있는 특징이 있지만 빌드할 때 실행 파일에 모두 포함됨, 사용되지 않는 리소스는 없어야됨

 

using System.Collections;
using UnityEngine;
using UnityEngine.AI; // 내비게이션 기능을 사용하기 위해 추가해야 하는 네임 스페이스
public class MonsterCtrl : MonoBehaviour
{
	...생략

    private readonly int hashTrace  = Animator.StringToHash("IsTrace");
    private readonly int hashAttack = Animator.StringToHash("IsAttack");
    private readonly int hashHit    = Animator.StringToHash("Hit");

    // 혈흔 효과 프리팹
    private GameObject bloodEffect;

    private void Start()
    {
        // 몬스터의 Transform 할당
        monsterTr = GetComponent<Transform>();
        // 추적 대상인 Player의 Trasnform 할당
        playerTr = GameObject.FindWithTag("PLAYER").GetComponent<Transform>();

        // NavMEshAgent 컴포넌트 할당
        agent = GetComponent<NavMeshAgent>();

        // Animator 컴포넌트 할당
        anim = GetComponent<Animator>();

        // BloodSprayEffect 프리팹 로드
        bloodEffect = Resources.Load<GameObject>("BloodSprayEffect");

        // 몬스터의 상태를 체크하는 코루틴 함수 호출
        StartCoroutine(CheckMonsterState());
        // 상태에 따라 몬스터의 행동을 수행하는 코루틴 함수 호출
        StartCoroutine(MonstorAction());
    }
    
    // 일정한 간격을 몬스터의 행동 상태를 체크
    IEnumerator CheckMonsterState()
    {
        ...생략
    }

    IEnumerator MonstorAction()
    {
        ...생략
    }

    private void OnCollisionEnter(Collision coll)
    {
        if(coll.collider.CompareTag("BULLET"))
        {
            // 충돌한 총알을 삭제
            Destroy(coll.gameObject);
            // 피격 애니메이션 실행
            anim.SetTrigger(hashHit);

            //총알의 충돌 지점
            Vector3 pos = coll.GetContact(0).point;
            // 총알의 충돌 지점의 법선 벡터
            Quaternion rot = Quaternion.LookRotation(-coll.GetContact(0).normal);
            // 혈흔 효과를 생성하는 함수 호출
            ShowBloodEffect(pos, rot);
        }
    }

    void ShowBloodEffect(Vector3 pos, Quaternion rot)
    {
        // 혈흔 효과 생성
        GameObject blood = Instantiate<GameObject>(bloodEffect, pos, rot, monsterTr);
        Destroy(blood, 1.0f);
    }

    private void OnDrawGizmos()
    {
        ...생략
    }
}

 

여러개의 에셋을 로드할때는 Resources.LoadAll<T>()

Resources.Load<T>()

 

폴더에서 에셋의 타입에 따라 서브 폴더로 분류 하면 아래와 같이 작성

폴더 로드할 에셋 문법
Resources/Prefabs/ Monster Resources.Load<GameObject>("Prefabs/Monster");
Resources/Sounds/ gunFire Resources.Load<AudioClip>("Sounds/gunFire");

 

총알을 맞은 지점에 혈흔 효과를 발생해야됨

 

맞은 지점의 좌푯값

Vector3 pos = coll.GetContact(0).point;

 

Z축으로 피가 튀는 파티클로 회전 각도를 총알 법선 벡터의 반대 방향으로 설정

Quaternion.LookRoation 함수로 쿼터니언 타입으로 변환

Quaternion rot = Quaternion.LookRotation(-coll.GetContact(0).normal);

 

혈흔의 생성위치를 Monster의 하위에 생성되게 하면 몬스터가 움직임에 따라 같이 이동하게 된다.

Instantiate<T>(생성할 객체, 위치, 각도, 부모 게임 오브젝트)

 

적 캐릭터의 공격 능력

캐릭터가 공격 데미지를 입어 생명력이 줄어드는 로직 구현

 

- 물리적인 타격을 주는 데미지

- 공격 주기의 수치상 데미지

 

물리적인 충돌을 위해 양쪽 손에 Collider, Rigidbody 컴포넌트 추가

IsTrigger, Is Kinematic 체크

 

isTrigger 속성을 체크해서 몬스터 몸체에 있는 Capsule Collider 와의 물리 적인 간섭을 없앰

Is Kinematic 속성을 체크해 물리 시뮬레이션을 연산하지 않게 설정

 

운동역학 : 물리 엔진의 시뮬레이션 연산으로 이동 및 회전하는 것이 아니라 스크립트 또는 애니메이션에 의해 Transform 컴포넌트의 속성값을 변화시켜 이동 및 회전하는 것을 말함

 

PUNCH 라는 이름의 태그를 생성하고 설정함

 

플레이어에도 Collider와 Rigidbody 컴포넌트를 추가하고 아래와 같이 설정

***보통 FPS, TPS 게임을 개발할 때 주인공 캐릭터는 Chacter Controller 컴포넌트를 추가, 이 컴포넌트는 Capslue Collider와 Rigidbody 컴포넌트의 역할, 벽에 부딪혔을 때 떨림현상이나 외부의 힘으로 밀리는 현상이 발생하지 않음

 

Punch가 제대로 닿는지 확인 하기 위해 플레이어와 몬스터를 같이 Shift + f 로 가상 카메라가 따라가게 하고 

좌표축 기즈모의 Y축을 클릭하여 Top View 로 설정하여 확인해본다.

 

OnTriggerEnter 콜백 함수

PlayerCtrl 스크립트에 추가

using System.Collections;
using UnityEngine;

public class PlayerCtrl : MonoBehaviour
{
    [SerializeField]
    private Transform tr;
    // 이동 속력 변수
    public float moveSpeed = 10.0f;
    // 회전 속도 변수
    public float turnSpeed = 80.0f;
    // Animation 컴포넌트를 저장할 변수
    private Animation anim;

    // 초기 생명 값
    private readonly float initHP = 100.0f;
    // 현재 생명 값
    public float currHp;

    IEnumerator Start()
    {
        // HP 초기화
        currHp = initHP;

        // 컴포넌트를 추출해 변수에 대입
        tr = GetComponent<Transform>();
        anim = GetComponent<Animation>();

        // 애니메이션 실행
        anim.Play("Idle");

        turnSpeed = 0.0f;
        yield return new WaitForSeconds(0.3f);
        turnSpeed = 80.0f;
    }

    void Update()
    {
        ...생략
    }

    void PlayerAnim(float h, float v)
    {
        ...생략
    }

    // 충돌한 Collider 의 IsTrigger 옵션이 체크됐을 때 발생
    private void OnTriggerEnter(Collider coll)
    {
        // 충돌한 Collider가 몬스터의 PUNCH이면 Player HP 차감
        if(currHp >= 0.0f && coll.CompareTag("PUNCH"))
        {
            currHp -= 10.0f;
            Debug.Log($"Player hp = {currHp / initHP}");

            // player 생명이 0 이하면 사망 처리
            if (currHp <= 0.0f)
            {
                PlayerDie();
            }
        }
    }
    // Player 사망 처리
    void PlayerDie()
    {
        Debug.Log("Player Die !");
    }
}​

 

생명 변수를 initHP 와 currHp 두개의 변수로 나눈 이유는 이후에 백분율을 구하기 위함

 

collider 컴포넌트의 Is Trigger 가 체크 되어 있다면 OnTrigger~ 계열의 콜백 함수가 호출됨

 

이때 전달되는 파라미터 타입이 Collider 로 펌포넌트가 넘어옴

 

C# 문자열 보간
컴파일러는 특수기호 $로 시작하는 문자열은 문자열을 보간하는 것으로 인식

// 일반적인 문자열 조합
Debug.Log("Player HP : " + currHP + "/" + initHP + "=" + currHp/initHP);

// LogFormat을 사용한 표현
Debug.LogFormat("Player HP : {0}/{1}={2}", currHP, initHP, currHP/initHP);

// 문자열 보간을 사용한 표현
Debug.Log($"Player HP : {currHp}/{initHP}={currHP/initHP}");​


+는 조금 번잡스러움 $ 기호를 사용해 문자열을 보간한 방식은 표시하고 싶은 변수를 중괄호를 사용해 바로 사용가능

반응형