본문 바로가기
Games/DevLogs

[DevLogs] 구체 플레이어 구현 - 2, 이동, 공격, 애니메이션

by NCTP 2024. 6. 7.

이동 코드의 개선

저번에 구현했던 구체 형태 플레이어의 이동방식은 AddForce로 구현한 매우 간단한 형태였다.

따라서 조작에 여러모로 불편한 부분들이 많았는데, 그 중 가장 큰 단점은 바로 조작감이다.

조작이 어려운 게임을 만들고 싶진 않았기 때문에, 더 간단한 조작을 위하여 CharacterController 컴포넌트를 사용하기로 했다.

 

    private void Move()
    {
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");
        //if(horizontal == vertical) RigidbodyReset();

        direction = new Vector3(horizontal, 0.0f, vertical);

        _rb.AddForce(direction * speed);
        if(Input.GetKeyDown(KeyCode.Space) && _isGrounded)
        {
            _rb.AddForce(0.0f, jumpForce, 0.0f);
        }
        if(Physics.Raycast(transform.position, Vector3.down, out _hitInfo, transform.localScale.x/2 + 0.2f))
        {
            _isGrounded = true;
            //Debug.Log(transform.localScale.x);
        }
        else
        {
            _isGrounded = false;
            //Debug.Log("Not Grounded!");
        }

        if (Input.GetKeyDown(KeyCode.LeftShift))
        {
            _rb.AddForce(direction * chargeSpeed / 2);
        }
        
    }

 

원래 코드는 이랬다면,

 

    private void Move2()
    {
        _isGrounded = _controller.isGrounded;
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");
        //if(horizontal == vertical) RigidbodyReset();
        if (horizontal == 0 && vertical == 0)
            _speed = speed;
        if (_isGrounded)
        {
            direction = new Vector3(horizontal, 0.0f, vertical);
            direction = transform.TransformDirection(direction) * _speed;
            _speed += accelValue * Time.deltaTime;
            if(Input.GetKeyDown(KeyCode.Space))
            {
                Debug.Log("Jump");
                direction.y += Mathf.Sqrt(jumpForce * -10.0f * _gravityValue);
            }
            if (Input.GetKeyDown(KeyCode.LeftShift))
            {
                direction += new Vector3(horizontal, 0.0f, vertical) * 5.0f;
            }
        }
        direction.y += _gravityValue * 50.0f * Time.deltaTime;
        Vector3 rotationDirection = new Vector3(vertical, 0.0f, -horizontal);
        Wheel.transform.Rotate(rotationDirection * 500.0f * Time.deltaTime);
        _controller.Move(direction * Time.deltaTime);
    }

 

이렇게 코드를 수정하였다. Awake에서 Character Controller를 _controller로 GetComponent하여 사용했고, 

컨트롤러를 통해 직접 플레이어의 위치를 수정해주었다.

 

이 때, AddForce 시절의 구체 회전이 사라졌으므로,

플레이어 입력에 따라 플레이어의 회전부가 회전하도록 바꾸었다.

이 때 회전부를 Wheel 이라는 게임 오브젝트를 하나 만들어주었고, 움직임에 따라 이 부분만 회전한다.

 

 

공격 - 사격 구현

공격은 간단한 형태로 구현한다.

플레이어 전방의 적을 자동으로 록온하고,

마우스 좌클릭 입력을 주면 플레이어 주위에 떠다니는 드론이 자동으로 락온된 적을 사격한다.

 

이전에는 레이캐스트 방식으로 구현했는데, 이번에는 직접 투사체를 발사하는 방법을 사용한다.

투사체는 드론의 전방 총구에서 생성되며, 타겟 방향으로 직선으로 향한다.

적이 가만히 있지 않을 수 있으므로, 유도 성능을 추가한다.

 

플레이어가 아닌, 플레이어 주위를 맴도는 드론이 사격하므로, 사격 기능을 드론에 구현하기로 한다.

먼저 간단한 Fire 함수를 만들었다.

 

    public void Fire()
    {
        GameObject _projectile = Instantiate(projectile, muzzle.position, muzzle.rotation);
        _projectile.GetComponent<Rigidbody>().velocity =
            transform.forward * _projectile.GetComponent<Projectile>().speed;
    }

 

그 다음, 드론이 카메라 방향을 바라보도록 만들어주고,

미리 지정된 Target이 있으면, Target을 바라보도록 Update문을 작성했다.

 

    void Update()
    {
        if (target != null)
        {
            transform.LookAt(target.transform);
        }
        else
        {
            transform.rotation = Camera.main.transform.rotation;
        }
    }

 

이제는 타겟을 지정해주면 타겟을 향해서,

그렇지 않다면 플레이어가 바라보는 방향으로 사격할 수 있게 되었다.

결과

 

이 때, 주의할 점은 투사체의 방향 및 속력 등을 반드시 발사하는 게임 오브젝트에서 설정해주자.

그렇지 않으면, 투사체가 총구와 다른 방향으로 나아갈 수 있기 때문이다.

 

 

공격 - 돌격 구현

돌격은 다음과 같은 과정을 거친다.

  • 적을 Targeting
  • Target을 향해 돌진 시작
  • 충돌 시 데미지, 돌진 종료

돌진을 E 키에 할당하고, bool값으로 isCharging을 선언했다. isCharging이 true인 동안

플레이어는 적에게 직선으로 돌진한다.

isCharging이 true일 때, 즉 돌진이 진행중일 때에는 플레이어 입력을 받지 않도록 한다.

        if (Input.GetKeyDown(KeyCode.E) && chargeTarget)
        {
            _isCharging = true;
        }
        if (_isCharging)
        {
            if (chargeTarget)
            {
                Vector3 direction = (chargeTarget.transform.position - transform.position).normalized;
                _controller.Move(direction * chargeSpeed * Time.deltaTime);
            }
        }
        else
        {
            Move2();
        }

 

적과 부딪혔다면 데미지를 주고, isCharing 을 false로 만들어 돌진을 종료한다.

    private void OnCollisionEnter(Collision other)
    {
        if(other.gameObject.CompareTag("Enemy"))
        {
            Enemy enemy = other.gameObject.GetComponent<Enemy>();
            Debug.Log("DD");
            enemy.GetDamage(new DamageMessage(gameObject, collisionDamage));
        }

        if (_isCharging) _isCharging = false;
    }

 

결과

 

돌진 기능의 게임적 기획 (구현 완료)

핵심 콘텐츠인 돌진은 다음과 같은 특성을 갖는다.

  • 타겟인 적에게 돌격
  • 쿨타임
  • 타겟이 돌격으로 인해 파괴될 경우 쿨타임 초기화

 

댓글