개발/Unity

유니티 3D 간보기 - 골드메탈 3D강좌:플레이어 컨트롤

huiyu 2022. 2. 5. 19:00

아래 3D강좌 기반 공부 시작

-캐릭터 기본 조작

https://youtu.be/WkMM7Uu2AoA

 - 캐릭터 컨트롤 하기. 객체에 일단 RigidBody+Capsule Collider 넣고 시작.

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

public class Player : MonoBehaviour
{
    public float speed;
    float hAxis;
    float vAxis;
    bool wDown;

    Vector3 moveVec;
    Animator animator;

    void Awake()
    {
        animator = GetComponentInChildren<Animator>();
    }

    void Update()
    {
        hAxis = Input.GetAxisRaw("Horizontal");
        vAxis = Input.GetAxisRaw("Vertical");
        wDown = Input.GetButton("Walk");

        moveVec = new Vector3(hAxis, 0, vAxis).normalized;

        transform.position += moveVec * speed * (wDown ? 0.3f : 1f) * Time.deltaTime;

        animator.SetBool("isRun", moveVec != Vector3.zero);
        animator.SetBool("isWalk", wDown);

        transform.LookAt(transform.position + moveVec);
    }
}

- Input System : Edit->Project Settings->Input Manager 에서 받아오는 값.
 여기서 Size를 추가하여 'Walk'라는 새로운 인풋 추가, Shift입력 받아 올 경우 걷게 구현.

아래 코드로 캐릭터 이동 구현.

hAxis = Input.GetAxisRaw("Horizontal");
vAxis = Input.GetAxisRaw("Vertical");

moveVec = new Vector3(hAxis, 0, vAxis).normalized;
transform.position += moveVec * speed * Time.deltaTime;

아래 코드로 입력에 따라 캐릭터 방향 회전 설정

transform.LookAt(transform.position + moveVec);

wDown 입력시 속도를 늦춰 걷는 속도 설정

wDown = Input.GetButton("Walk");

transform.position += moveVec * speed * (wDown ? 0.3f : 1f) * Time.deltaTime;

isRun이나 isWalk는 Animator Controller에서 3D 모델링 기준 애니메이션 설정인데,
이전 강의의 2D 게임 Sprite Animation과 사용 방법이 동일하다.

 * 객체간 충돌 체크를 좀더 정확하게 하려면?
 - 캐릭터 객체 선택, 'Rigidbody'의 'Collision Detection'을 'Continuous'로 설정.


 - 충돌 대상이 되는 객체(Floor, Wall)은 Inspector의 'Static'으로 설정할 것.
   Rigidbody를 추가하고 Use Gravity 해제, IsKinematic  설정할 것.

  - 벽에는 Physic Material 추가하기


- 카메라가 캐릭터 따라오게 하기

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

public class Follow : MonoBehaviour
{
    public Transform target;
    public Vector3 offset;

    void Update()
    {
        transform.position = target.position + offset;
    }
}

위 스크립트를 카메라에 붙여준다.
 target에 캐릭터를 입력.
 offset은 캐릭터를 기준으로 떨어진 위치. 입력값(0, 21, -11)

기본 조작 완성

- 캐릭터 점프와 회피
https://youtu.be/eZ8Dm809j4c

- 코드 정리로 시작, 함수로 묶기.

    void Update()
    {
        GetInput();
        Move();
        Turn();
    }

    void GetInput()
    {
        hAxis = Input.GetAxisRaw("Horizontal");
        vAxis = Input.GetAxisRaw("Vertical");
        wDown = Input.GetButton("Walk");

    }

    void Move()
    {
        moveVec = new Vector3(hAxis, 0, vAxis).normalized;
        transform.position += moveVec * speed * (wDown ? 0.3f : 1f) * Time.deltaTime;

        animator.SetBool("isRun", moveVec != Vector3.zero);
        animator.SetBool("isWalk", wDown);
    }

    void Turn()
    {
        transform.LookAt(transform.position + moveVec);
    }

- 점프 만들기

//변수추가
bool isJump;
Rigidbody rigid;

//Input- 점프 입력 추가
jDown = Input.GetButton("Jump");

//Jump 함수 추가
void Jump()
{
    //점프가 눌릴때 && 점프중이 아닐때
    if (jDown && !isJump)
    {
        //위로 AddForce
        rigid.AddForce(Vector3.up * 20, ForceMode.Impulse);
        //설정한 Animation paramter:isJump & doJump trigger
        animator.SetBool("isJump", true);
        animator.SetTrigger("doJump");
        isJump = true;
    }
}

//바닥 충돌 검사
private void OnCollisionEnter(Collision collision)
{
    //바닥 tag Floor 추가하기
    if (collision.gameObject.tag == "Floor")
    {
        //바닥에 닿은경우 점프 끝.
        animator.SetBool("isJump", false);
        isJump = false;
    }
}

아래와 같이 애니메이션 추가, 설정

   - DoJump trigger 발생시 Jump 애니메이션
   - Jump종료(바닥에 닿을 경우), isJump가 false가 되면서 착지 애니메이션
   - 착지 애니메이션은 'HasExitTime'체크 설정 해두어야 그대로 애니메이션 종료

 - Dodge(회피) 시스템 추가

    bool isDodge;
    Vector3 dodgeVec;
    
    void Update()
    {
        GetInput();
        Move();
        Turn();
        Jump();
        Dodge();
    }

    void Move()
    {
        moveVec = new Vector3(hAxis, 0, vAxis).normalized;

        if(isDodge)
        {
            moveVec = dodgeVec;
        }
        transform.position += moveVec * speed * (wDown ? 0.3f : 1f) * Time.deltaTime;

        animator.SetBool("isRun", moveVec != Vector3.zero);
        animator.SetBool("isWalk", wDown);
    }

    void Jump()
    {
        if (jDown && moveVec == Vector3.zero && !isJump && !isDodge)
        {
            rigid.AddForce(Vector3.up * 12, ForceMode.Impulse);
            animator.SetBool("isJump", true);
            animator.SetTrigger("doJump");
            isJump = true;
        }
    }

    void Dodge()
    {
        if (jDown && moveVec != Vector3.zero && !isJump && !isDodge)
        {
            dodgeVec = moveVec;
            speed *= 2;
            animator.SetTrigger("doDodge");
            isDodge = true;
            Invoke("DodgeOut", 0.6f);
        }
    }

    void DodgeOut()
    {
        isDodge = false;
        speed *= 0.5f;
    }

 - 닷지는 이동 중 점프 키가 눌렸을 때 동작
 - 점프는 제자리에 가만히 있을 때만 동작
 - 닷지와 점프는 동시에 발생하지 않도록 처리
 - 닷지 시 속도 증가,. 닷지 애니메이션 발동. 닷지 끝나는건 Invoke로 타임설정
 - 닷지 시 이동 벡터 저장해서 닷지중 이동 키가 눌려도 자연스럽게 설정

완성.

캐릭터 전체 스크립트

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

public class Player : MonoBehaviour
{
    public float speed;
    float hAxis;
    float vAxis;
    bool wDown;
    bool jDown;

    bool isJump;
    bool isDodge;

    Vector3 moveVec;
    Vector3 dodgeVec;

    Rigidbody rigid;
    Animator animator;

    void Awake()
    {
        rigid = GetComponent<Rigidbody>();
        animator = GetComponentInChildren<Animator>();
    }

    void Update()
    {
        GetInput();
        Move();
        Turn();
        Jump();
        Dodge();
    }

    void GetInput()
    {
        hAxis = Input.GetAxisRaw("Horizontal");
        vAxis = Input.GetAxisRaw("Vertical");
        wDown = Input.GetButton("Walk");
        jDown = Input.GetButton("Jump");
    }

    void Move()
    {
        moveVec = new Vector3(hAxis, 0, vAxis).normalized;

        if(isDodge)
        {
            moveVec = dodgeVec;
        }
        transform.position += moveVec * speed * (wDown ? 0.3f : 1f) * Time.deltaTime;

        animator.SetBool("isRun", moveVec != Vector3.zero);
        animator.SetBool("isWalk", wDown);
    }

    void Turn()
    {
        transform.LookAt(transform.position + moveVec);
    }

    void Jump()
    {
        if (jDown && moveVec == Vector3.zero && !isJump && !isDodge)
        {
            rigid.AddForce(Vector3.up * 12, ForceMode.Impulse);
            animator.SetBool("isJump", true);
            animator.SetTrigger("doJump");
            isJump = true;
        }
    }

    void Dodge()
    {
        if (jDown && moveVec != Vector3.zero && !isJump && !isDodge)
        {
            dodgeVec = moveVec;
            speed *= 2;
            animator.SetTrigger("doDodge");
            isDodge = true;
            Invoke("DodgeOut", 0.6f);
        }
    }

    void DodgeOut()
    {
        isDodge = false;
        speed *= 0.5f;
    }

    private void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.tag == "Floor")
        {
            animator.SetBool("isJump", false);
            isJump = false;
        }
    }
}
728x90
반응형