Deff_Dev

[내배캠 Unity/C#] 8주차 본문

부트캠프/내배캠 Unity 4기

[내배캠 Unity/C#] 8주차

Deff_a 2024. 6. 18. 23:58

이번 8주차 3D 개인 프로젝트는 FSM 강의를 듣고 방치형 게임을 만드는 것이다.

 

 

[Unity/C#] FSM

FSM유한 상태 기계 (Finite State Machine, FSM)유한한 갯수의 상태들로 구성된 기계 및 패턴을 만한다.상태와 상태 간의 전환을 기반으로 동작하는 동작 기반 시스템구성 요소상태 (State) : 시스템이 취

deff-dev.tistory.com

 

FSM을 처음 접했을 때는 사실 이해가 완벽하게 잘 되지 않아서 강의를 많이 돌려봤다.

 

강의를 다 본 뒤. 이해한 FSM을 해당 프로젝트에서 비효율적이라도 한번 써보는 것을 목표로 프로젝트를 진행했다.

 

이번 프로젝트로 작업한 방치형 게임이다.

 

강의 보고 이해하는데 시간을 많이 써서 실제로 작업한 시간은 이틀정도였다.

 

시간이 없었던 만큼 퀄리티적인 부분에서 아쉬움이 남지만 일단 처음 목표였던 FSM을 활용해서 플레이어와 적 상태 구현을 해서 나름 얻어가는 내용이 있는 프로젝트였다.

 

 

GitHub - seungdo1234/IdleGame_3D: 3D 방치형 게임

3D 방치형 게임 . Contribute to seungdo1234/IdleGame_3D development by creating an account on GitHub.

github.com

 

DOTween

이번 프로젝트를 진행하면서 DOTween을 처음 사용해봤는데 완전 신세계(?)였다.

 

전투가 끝나고 스테이지가 올라갈 때 화면 Fade 효과를 구현할 때, 평소에는 코루틴Lerf를 이용해서 구현했었다.

이번에는 DOTweenDOFade() 함수를 이용해서 해당 효과를 구현했다.

image.DOFade(alpha, duration);

 

 이 한줄로 Fade 효과를 손쉽게 만들 수 있었다.

 

UI에 애니메이션을 넣어서 구현하는 것을 지양하고 DOTween을 이용해서 UI 효과를 만들어야겠다.

 

FSM

 

FSM을 구현하기 위해서 클래스를 설계하는 부분이 가장 오래걸렸고 어려웠다.

 

플레이어와 적의 상태를 탐색 -> 이동 -> 공격 -> 탐색 ... 으로 설계했다.

 

이 과정에서 가장 어려웠던 부분은 이동 상태 구현이 가장 어려웠다,

 

처음에 구현을 타겟의 방향 + 회전 방향을 구해놓고 해당 위치로 이동하는 식으로 구현을 했다.

적이 플레이어를 지나쳐 간다.

 

이렇게 구현하니깐 플레이어를 지나쳐서 가는 현상이 발생했다.

 

이 문제를 이동, 회전 방향을 계속 최신화 시켜주는 방식으로 구현해서 위 문제를 해결했다.

더보기
    public void Update()
    {
        if (isRotationComplete && isMovementComplete) // 도착, 회전 완료했다면
        {
            SwitchState();
            return;
        }

        if (!isMovementComplete)
        {
            Move();
        }

        Rotate();
    }

    private void SwitchState() // 상태 변경
    {
        if (EnemyManager.Instance.SpawnEnemyList.Count > 0)
        {
            stateMachine.ChangeState(stateMachine.AttackState);
        }
        else
        {
            stateMachine.ChangeState(stateMachine.ChasingState);
        }
    }

    private void Move()
    {
        if (Vector3.Distance(player.transform.position, stateMachine.Target.position) < player.PlayerStat.AttackRange) // 도착
        {
            isMovementComplete = true;
            return;
        }
        targetDir = GetTargetDirection();
        player.Controller.Move(targetDir * (player.PlayerStat.MoveSpeed * Time.deltaTime));
    }

    private void Rotate()
    {
        if (Quaternion.Angle(player.Model.localRotation, targetRotation) < 0.1f) // 회전 완료
        {
            isRotationComplete = true;
            player.Model.localRotation = targetRotation;
        }
        else
        {
            isRotationComplete = false;
            player.Model.localRotation = Quaternion.RotateTowards(player.Model.localRotation, targetRotation, Time.deltaTime * rotationSpeed);
        }
    }