공부/Game Bootcamp

[멋쟁이사자처럼 부트캠프 TIL] 유니티 게임 개발 3기 : Animator, Rigidbody 등

Ail_ 2024. 11. 27. 16:40

Collider과 Trigger의 Stay

Trigger Stay는 잘 사용하지 않음

: 예를 들어 총알을 맞았을 때, TriggerEnter 에서 데미지를 입히고 TriggerExit에서 데미지를 그만 입히면 됨

 

2단 점프 구현에는 쓸 수 있지 않을까?

그라운드에 있을 경우를 기준으로 Stay로 해도 되지만, 마찬가지로 땅에서 떨어지는 Exit일 땐 점프 막고 Enter일때 점프 허용 처리로 해도 됨

 

Physic Material

탄성 넣기

Static Friction

멈춰있는 대상에 대한 마찰력

ex. 멈춰있던 자동차가 출발할 때

 

Dynamic Friction

움직이는 대상에 대한 마찰력

ex. 모래사장을 달리는 자동차

 

보통 위 두개는 동일하게 넣음

 

Bounciness

탄성

0-1까지 설정 가능(10이랑 100을 넣어도 실행하면 1로 바뀜)

1로 설정한 후

Collider에 설정

 

결과물

땅에는 탄성이 없어서(받쳐주지 않아서) 많이 안튕기는 것처럼 보임

 

Ground에도 탄성을 넣어주면 튕기면서 점점 더 올라감

 

Rigidbody에서 공기저항 연산값들을 바꿀 수 있음

 

축이 돌아가는 것을 방지하기 위해 아래처럼 넣는 경우도 있음

공식 문서에선 Infinity로 넣으라고 하지만 보통 1000만 넣어도 동일한 효과를 보임

 

리소스 정리

프로젝트 볼륨 관리를 위해 에셋 Import하고 나서 본인이 사용할 항목만 Resource 폴더로 옮기고 나머지는 삭제하는게 좋음

하지만 의존성 Dependence를 해칠 우려가 있기 때문에 항상 유의해야 함

 

 

애니메이션 Animation

애니메이션을 실행하는 Component

하지만 2021~에서는 해당 컴포넌트 대신 Animator에서 통합 관리하여 사용

 

애니메이터 윈도우 생성 방법

단축키 : ctrl + 6

이벤트 프레임 : Script 실행 가능 = 함수 실행 가능

마우스 휠 누른 채 드래그 : 좌우 이동 가능

커브 누르면 커브 수정 가능

 

자식 Object

일반적으로 GameObject에서 자식 Object를 가지고 Animation을 생성 -> 자식 Object에서는 부모 Object를 건드릴 수 없기 때문

문 오브젝트가 이렇게 있을 때, Door_14_Coffe에서 애니메이터 켜서 애니메이션 파일 생성 후

나머지 자식 Object들을 조작하여 애니메이션 생성

 

이벤트 프레임

이벤트 프레임 추가하면 Inspector view에 Function 설정 가능

Function을 설정 하려면 스크립트(+함수)가 필요함

따라서

1. 해당 스크립트 작성

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

public class DoorEvent : MonoBehaviour
{
    void AnimationEvent()
    {
        Debug.Log("문이 열리는 중");
    }
}

 

2. 스크립트를 애니메이션 생성했던 root object에 넣기

 

3. 작성한 함수 확인 가능

 

실행하면 로그가 잘 찍힘

 

애니메이션 루프 끄기

 

무언가가 감지되면 문이 열리도록 로직 추가

Box Collider 추가

 

해당 Collider 영역만큼 감지됨

 

스크립트 작성

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

public class DoorEvent : MonoBehaviour
{
    public void OnTriggerEnter(Collider other)
    {
        Debug.Log("감지 시작");
    }

    public void OnTriggerExit(Collider other)
    {
        Debug.Log("감지 종료");
    }
}

 

기존 Prefab과 분리

분리되면 이름이 회색으로 변함

 

Prefab 새로 생성

 

캐릭터가 있던 Scene에 넣기

 

캐릭터를 움직여서 문의 해당 영역에 접근하면 로그가 잘 찍힘

 

매개변수

Collider 타입인 other가 바로 감지한 대상에 속한다

other를 로그에 찍어보자

public void OnTriggerEnter(Collider other)
{
    Debug.Log("감지 시작 :" + other.name);
    // = other.gameObject.transform.name

}

 

실행해보면 어떤 게 인식되는지 알 수 있음

 

모든 몸의 부위가 다 각각 감지되고 있음

= 프리미티브 오브젝트(기본 오브젝트)로 만들었는데 각각 콜라이더가 이미 있기 때문

 

따라서 콜라이더를 지워야함

콜라이더는 같은 종류끼리만 다중선택하여 삭제 가능

 

다 지우니 깔끔해짐

 

이제 새로 넣어줘야하는데 보통 캐릭터엔 Capsule Collider를 사용함

해당 항목 수정하여 캐릭터에 맞게 배치함

 

이제 캐릭터의 콜라이더만만 감지함

 

 Animator 수정

Animator의 Controller 더블클릭하여 Animator 창 실행

만약 항목이 안뜨면 껐다 키기(버그가 은근 발생함)

 

각 항목에 대한 설명

Entry : 상시 적용 = 계속 실행
Any State : 원할 때 실행 가능

 

기본 애니메이션 Default Animation

주황색 항목

프로그램이 시작하자마자 실행됨

 

Empty State(Animation) 생성

 

해당 State를 Default State로 변경

Parameter

문을 원할 때 열기 위해서 Parameter가 필요함

1. Any State랑 new Door를 이어줌

 

2. Trigger 파라미터 생성

!! 주의 !!

파라미터명은 대소문자, 띄어쓰기 구별함

 

현재 상태

 

필요할 때 <- 언제 필요한지 key 값 필요

화살표를 클릭하면 inspector가 바뀜

Inspector View에서 Conditions의 +버튼 클릭

 

키 값 등록 완료

 

스크립트 작성

public Animator anim; // public으로 선언한 Animator 타입의 변수

public void OnTriggerEnter(Collider other)
{
    anim.SetTrigger("Open"); // Animator에서 Trigger 파라미터 "Open" 실행
}

 

public으로 설정해서 할당 가능하도록 해둔 곳에 Door 넣으면 됨

 

Private으로 설정하는 법

 

특정 캐릭터가 가까이 갔을 때만 문이 열리도록 설정

조건문에 넣어서 구현

    public Animator anim; // public으로 선언한 Animator 타입의 변수

    public void OnTriggerEnter(Collider other)
    {
        if (other.name == "Character (1)")
        {
        anim.SetTrigger("Open"); // Animator에서 Trigger 파라미터 "Open" 실행

        Debug.Log("감지 시작 :" + other.name);
        }
    }

 

캐릭터 태그 할당

 

조건문 태그로 수정

if (other.tag == "Player")
{
anim.SetTrigger("Open"); // Animator에서 Trigger 파라미터 "Open" 실행

Debug.Log("감지 시작 :" + other.name);
}

 

이 경우엔 아래처럼 CompareTag 쓰는 게 더 좋음

if (other.CompareTag("Player"))
{
anim.SetTrigger("Open"); // Animator에서 Trigger 파라미터 "Open" 실행

Debug.Log("감지 시작 :" + other.name);
}

 

결과물

큐브에는 안열리지만 Player Tag가 붙은 캐릭터에는 열리는 문

 

문 닫는 애니메이션

단순한 역재생만 필요한 경우 그냥 복제해서 속도를 음수 주면 됨(반대)

 

Speed 1에서 -1로 변경

 

이름 변경

 

그 다음은 이전과 똑같이 Key 값으로 쓸 파라미터 추가하고 컨디션에 추가

 

스크립트 수정

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

public class DoorEvent : MonoBehaviour
{
    public Animator anim; // public으로 선언한 Animator 타입의 변수

    public void OnTriggerEnter(Collider other)
    {
        if (other.CompareTag("Player"))
        {
        anim.SetTrigger("Open"); // Animator에서 Trigger 파라미터 "Open" 실행
        }
    }

    public void OnTriggerExit(Collider other)
    {
        if (!other.CompareTag("Player"))
        { return; }

        anim.SetTrigger("Close");
    }
}

 

결과물

 

Transform과 Ridgidbody 차이

Transform : 순간이동

Rigidbody : 물리적 이동 -> 이용해서 점프 구현

 

점프 구현

구현 준비하며 생각해 볼 요소

    public float jumpPower = 10f;

	private void Jump()
    {
        if (Input.GetKeyDown(KeyCode.Space)) // 스페이스바를 눌렀을 때
        {
            // 자기 자신의 Rigidbody Component에 접근해서 AddForce 기능 사용
            this.GetComponent<Rigidbody>().AddForce(힘을 가할 방향 * 파워, 가하는 방식);
            =
            this.GetComponent<Rigidbody>().AddForce(Vector3.up * jumpPower, ForceMode.Impulse);
            // = 자기 자신의 Rigidbody에 접근해서 AddForce로 힘을 가함(위쪽으로 jumpPwer만큼 힘을 가함)
                                                                   // ForceMode : 지속적인 힘, 순간적인 힘
            =
            GetComponent<Rigidbody>().AddForce(Vector3.up * jumpPower, ForceMode.Impulse);
        }
    }

 

ForceMode 종류

질량 차이, 지속적인 힘과 순간적인 힘 차이에 따라 4개가 있음

 

코드 적용 결과물

 

2단 점프 방지

공중에 떠 있는 상태에선 점프 불가하도록 막으면 됨

=> Collision 사용

OnCollisionEnter() -> 캐릭터가 바닥과 충돌 중이다

OnCollisionExit() -> 캐릭터가 바닥에서 떠났다

 

점프하는 캐릭터의 점프 상태(땅에 닿아 있는지)를 알기 위한 변수 필요

// Movement.cs
// 플레이어 상태를 알기 위한 변수 추가
	public bool isGround = false;

 

변수 값을 충돌 여부에 따라 수정해줌

// GroundEvent.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GroundEvent : MonoBehaviour
{
    public void OnCollisionEnter(Collision collision)
    {
        if (collision.collider.CompareTag("Player"))
        {
            // 플레이어 태그를 가진 게임 오브젝트를 characterObj에 담아주기
            GameObject characterObj = collision.gameObject;

            // 캐릭터 오브젝트의 Movement 스크립트(컴포넌트)에 접근해서 isGround 변경 필요
            characterObj.GetComponent<Movement>().isGround = true;
        }
    }

    public void OnCollisionExit(Collision collision)
    {
        if (!collision.collider.CompareTag("Player"))
            return;

        GameObject characterObj = collision.gameObject;

        characterObj.GetComponent<Movement>().isGround = false;
    }
}

 

그럼 '땅에 닿았을 때'라는 조건 추가가 가능해짐

// Movement.cs
	private void Jump()
    {
        if (Input.GetKeyDown(KeyCode.Space) && isGround == true) // 스페이스바를 눌렀을 때 && 땅에 닿았을 때
        {
            // 자기 자신의 Rigidbody Component에 접근해서 AddForce 기능 사용
            GetComponent<Rigidbody>().AddForce(Vector3.up * jumpPower, ForceMode.Impulse);
            // = 자기 자신의 Rigidbody에 접근해서 AddForce로 힘을 가함(위쪽으로 jumpPwer만큼 힘을 가함)
                                               // ForceMode : 지속적인 힘, 순간적인 힘
        }
    }

 

이제 바닥면 Plain에 GroundEvent 스크립트 넣고 실행해보면!

땅(Plain)에 닿았을 때만 점프하는 모습 = 2단 점프 불가한 모습 확인 가능

큐브 또한 땅이 아니기 때문에 큐브 위에서도 점프 불가함

 

+

Raycast

Collider보다 정교한, 포탑 등에 쓰이는 레이저 감지 느낌

 

이슈

캐릭터가 rigidbody 적용된 큐브에 닿으면 혼자 돌아감

해결법

1. 외부 자극에 의한 회전을 막아주는 Freeze Rotation에서 y까지 체크하니 해결 -> Capsule Collider 쓰는 경우 필요

 

2. 지금 transform 이동을 하기 때문에 나타나는 이슈로 Angular Drag에 값을 줘서 막을 수 있음

Rigidbody.MovePosition() -> 충돌에 의한 틀어짐 등을 방지함

 

사담

배운 것들이 하나둘씩 실전에 어떤 식으로 쓰이는지 알아가는 게 참 재밌다...

아직 젤 헷갈리는 건 역시 익숙하지 않은 네이밍들?

많이 쳐보는 수밖에 없긴 하다...ㅎㅎ

질문하면 답변도 엄청 빨리 달려서 감사하게 된다.

재밌다!