반응형
  • 티스토리 홈
  • 프로필사진
    우밍
  • 방명록
  • 공지사항
  • 태그
  • 블로그 관리
  • 글 작성
우밍
  • 프로필사진
    우밍
    • 분류 전체보기 (2)
      • 전산 (0)
        • 컴퓨터구조 (0)
        • 운영체제 (0)
        • 네트워크 (0)
        • 데이터베이스 (0)
      • 게임엔진 (2)
        • 유니티 (0)
        • 개발 일지 (2)
  • 방문자 수
    • 전체:
    • 오늘:
    • 어제:
  • 최근 댓글
      등록된 댓글이 없습니다.
    • 최근 공지
        등록된 공지가 없습니다.
      # Home
      # 공지사항
      #
      # 태그
      # 검색결과
      # 방명록
      • [게임 개발 일지 #1] 일단 만들어보자 - 간단한 플레이어 움직이기
        2025년 05월 14일
        • 우밍
        • 작성자
        • 2025.05.14.:54
        반응형

        유니티 기본 세팅
        3D 형태

        유니티 버전은 2022.3.55f1를 사용했다. (그냥 컴퓨터에 깔려있어서... 사용했다.)
        일단 기획은 없지만 <견습 기사 모험기>라는 게임을 보고 간단한 프로토타입을 만들어보려고 한다.

        먼저 3D 형태부터 만들어보도록 하자.

        3D 플레이어에 쓸 모델
        레벨에 쓸 그리드 텍스쳐도 같이~

        유니티 에셋스토어를 찾아보니 괜찮은 모델이 있어서 가져왔다.

        핑꾸핑꾸하당

        항상 에셋스토어에서 임포트해오면 핑꾸핑꾸해진다.

        메테리얼에 쉐이더가 제대로 연결 안돼서 (렌더 파이프라인 불일치)

        핑꾸로 보이는 거라고 하는데 이럴 때 마다 하는 방법이 있다.

        상단 메뉴에서
        1. Window > Rendering > Render Piperline Converter을 열고

        2. Built-in to URP 선택 후

        3. Material Update 체크 후 Initialize And Convert을 눌러주면 된다.

        그러면 URP용으로 자동 변환 해준다.

        (근데 커스텀 쉐이더는 지원을 안해주는 것 같다.)

        1. Window 클릭!
        2. Rendering 클릭
        3. Render Pipeline Converter 클릭!
        4. Material Update에 체크를 하고 Initialize And Convert 클릭!
        핑꾸 삭제~
        초기 세팅

        자 이제 Player를 움직여보자.

        Figma로 클래스 설계

        Figma로 대충 클래스 설계를 했다.

        플레이어 상태를 일단 (대기, 이동, 공격) 3종류가 설계했고

        각 상태별로 입력 처리, 애니메이션 재생, 이동, 공격 로직을 분리 시켰다.

        자 이제 작성을 해보자

        PlayerInput.cs

        using System.Collections;
        using System.Collections.Generic;
        using UnityEngine;
        
        /// <summary>
        /// 플레이어 입력 처리 클래스
        /// </summary>
        public class PlayerInput : MonoBehaviour
        {
            public Vector3 MoveInput { get; private set; }
        
            private const string HORIZONTAL = "Horizontal";
            private const string VERTICAL = "Vertical";
        
            private void Update()
            {
                HandleMoveInput();
            }
        
            private void HandleMoveInput()
            {
                float horizontal = Input.GetAxisRaw(HORIZONTAL);
                float vertical = Input.GetAxisRaw(VERTICAL);
                MoveInput = new Vector3(horizontal, 0, vertical).normalized;
            }
        }

        PlayerInput 클래스는
        플레이어의 키보드 입력을 감지하고 현재 프레임의 이동 방향 벡터를 계산해주는 역할을 한다.
        Unity안에 있는 Input System을 사용해도 되는데 간단한 프로토타입이니 사용을 안했다.

        PlayerData.cs

        using System.Collections;
        using System.Collections.Generic;
        using UnityEngine;
        
        public class PlayerData : MonoBehaviour
        {
            public float MoveSpeed = 5f;
            public float RotationSpeed = 720f;
        }

        PlayerData 클래스는 플레이어의 데이터를 담는 컨테이너다.
        지금은 MonoBehaviour를 상속했지만 추후에 ScriptableObject로 바꿀 예정이다.
        현재는 이동속도와 회전속도밖에 없다.

        PlayerMovement.cs

        using System.Collections;
        using System.Collections.Generic;
        using UnityEngine;
        
        public class PlayerMovement : MonoBehaviour
        {
            private PlayerData _playerData;
        
            private void Awake()
            {
                _playerData = GetComponent<PlayerData>();
            }
        
            public void Move(Vector3 moveInput)
            {
                Vector3 moveDirection = moveInput;
        
                transform.position += moveDirection * _playerData.MoveSpeed * Time.deltaTime;
            }
        
            public void Rotate(Vector3 MoveInput)
            {
                if (MoveInput == Vector3.zero)
                {
                    return;
                }
        
                Quaternion targetRotation = Quaternion.LookRotation(MoveInput);
                Quaternion newRotation = Quaternion.RotateTowards(transform.rotation, targetRotation, _playerData.RotationSpeed * Time.deltaTime);
                transform.rotation = newRotation;
            }
        }

        PlayerMovement 클래스는 현재는 이동 및 회전 동작을 처리한다.
        외부 입력(MoveInut)을 받아 플레이어를 움직이고, 바라보는 방향으로 회전시킨다.

        PlayerAnimaton.cs

        using System.Collections;
        using System.Collections.Generic;
        using UnityEngine;
        
        public class PlayerAnimation : MonoBehaviour
        {
            private Animator _animator;
        
            private static readonly int MOVE_PARAM = Animator.StringToHash("Move");
        
            private void Awake()
            {
                _animator = GetComponent<Animator>();
            }
        
            public void SetMoveAnimation(bool isMoving)
            {
                _animator.SetBool(MOVE_PARAM, isMoving);
            }
        }

        PlayerAnimation 클래스는 플레이어 애니메이션을 관리한다.
        아직 Movement 동작만 적용했다.
        (추후 많은 애니메이션을 추가하겠지?)

        PlayerStateMachine.cs

        using UnityEngine;
        
        /// <summary>
        /// 플레이어 상태 머신 클래스
        /// </summary>
        public class PlayerStateMachine : MonoBehaviour
        {
            public PlayerState CurrentState { get; private set; }
        
            public PlayerIdleState PlayerIdleState { get; private set; }
            public PlayerMoveState PlayerMoveState { get; private set; }
        
        
            public void Start()
            {
                TransitionState(PlayerIdleState);
            }
        
            public void FixedUpdate()
            {
                if (CurrentState != null)
                {
                    CurrentState.FixedUpdate();
                }
            }
        
            public void Update()
            {
                if (CurrentState != null)
                {
                    CurrentState.Update();
                }
            }
        
            public void TransitionState(PlayerState newState)
            {
                if (CurrentState == newState)
                {
                    return;
                }
        
                if (CurrentState != null)
                {
                    CurrentState.Exit();
                }
        
                CurrentState = newState;
        
                if (CurrentState != null)
                {
                    CurrentState.Enter();
                }
            }
        }

        PlayerStateMachine 클래스는 플레이어의 상태를 관리하는 상태 머신 역할을 한다.
        현재 상태를 추적하고 새로운 상태로 전환할 때 Enter(), Exit() 메서드를 자동으로 호출해준다.

        • 예전에는 CurrentState?.Exit()이런 방식으로 널 체크를 했었는데 처음 보는 사람이 이 키워드를 보면 잘 몰라해서 ?.를 쓰지 않는다.
        • 웬만하면 처음 보는 키워드 같이 생기면 잘 안 쓰려고 한다.

        PlayerState.cs

        using System.Collections;
        using System.Collections.Generic;
        using UnityEngine;
        
        public abstract class PlayerState
        {
            protected PlayerStateMachine _playerStateMachine;
            protected PlayerInput _playerInput;
            protected PlayerMovement _playerMovement;
            protected PlayerAnimation _playerAnimation;
        
            public PlayerState(PlayerStateMachine playerStateMachine)
            {
                _playerStateMachine = playerStateMachine;
                _playerInput = playerStateMachine.GetComponent<PlayerInput>();
                _playerMovement = playerStateMachine.GetComponent<PlayerMovement>();
                _playerAnimation = playerStateMachine.GetComponent<PlayerAnimation>();
            }
        
            public abstract void Enter();
            public abstract void FixedUpdate();
            public abstract void Update();
            public abstract void Exit();
        
            public abstract void Transition();
        }
        
        public class PlayerIdleState : PlayerState
        {
            public PlayerIdleState(PlayerStateMachine playerStateMachine) : base(playerStateMachine) { }
        
            public override void Enter()
            {
        
            }
        
            public override void FixedUpdate()
            {
        
            }
        
            public override void Update()
            {
                Transition();
            }
        
            public override void Exit()
            {
        
            }
        
            public override void Transition()
            {
                if (_playerInput.MoveInput != Vector3.zero)
                {
                    _playerStateMachine.TransitionState(_playerStateMachine.PlayerMoveState);
                }
            }
        }
        
        public class PlayerMoveState : PlayerState
        {
        
            public PlayerMoveState(PlayerStateMachine playerStateMachine) : base(playerStateMachine) { }
        
            public override void Enter()
            {
                _playerAnimation.SetMoveAnimation(true);
        
            }
            public override void FixedUpdate()
            {
        
            }
            public override void Update()
            {
                _playerMovement.Move(_playerInput.MoveInput);
                _playerMovement.Rotate(_playerInput.MoveInput);
        
                Transition();
            }
        
            public override void Exit()
            {
                _playerAnimation.SetMoveAnimation(false);
            }
        
            public override void Transition()
            {
                if (_playerInput.MoveInput == Vector3.zero)
                {
                    _playerStateMachine.TransitionState(_playerStateMachine.PlayerIdleState);
                }
        
            }
        }

        PlayerState 클래스는 플레이어 상태 관리 시스템의 핵심이다.

        추상 클래스 PlayerState를 기반으로 각 상태 (Idle, Move)를 상속하고,
        상태에 따라 입력, 이동, 회전, 애니메이션을 처리한다.

        • 원래는 추상 메서드를 작성할 때 public virtual Update() {}를 자주 사용했지만 처음 보는 사람이 이 코드를 봤을 때 왜 갑자기 업데이트를 하고 Exit()는 왜 작동을 안하는 지 질문을 많이 받았다.
        • virtual를 사용하면
          • 꼭 구현하지 않아도 되는 선택지를 줄 수 있지만
          • 처음보는 사람들에게는 구조 파악하기가 힘들어했다.
        • abstract를 사용하면
          • 반드시 구현해야 하고
          • 직접 호출해줘야 작동하는 구조임을 의도적으로 말할 수 있어서 virtual 대신 abstract를 사용한다.
          • 몇 줄 더 쓰는 건 귀찮지만 가독성을 위해서 귀찮아도 해야 한다.

         

         


        애니메이터 컨트롤러도 하나 만들어주었다.
        현재는 Idle상태와 Move상태밖에 없기 때문에 애니메이션 2개와 1개(Move) param이 있다.

         


        자 이제 한번 플레이 해보자.

         

         

         

        플레이 하기전에 Unity엔진안에 Recorder라는 패키지를 다운을 받았다.
        (영상을 찍고 저장할 수 있는 툴이다.)

        다운을 받았으면
        Window > General > Recorder > Recorder Window 들어가면 된다
        Add Recorder를 눌러서 원하는 것을 생성한다. (나는 Movie로 생성했다.)
        빨간색 재생 버튼을 누르면 녹화가 시작된다.
        영상확인은 Output File 쪽에서 확인하면 된다.

         

        잘 작동한다.

         

         

         

        오늘은 간단하게 플레이어 움직이는 것을 구현해보았다.

         

        근데 오늘 진짜 깨달은 게 있다.

        블로그 글쓰는 게 생각보다 오래 걸린다... ㅋㅋㅋㅋㅋㅋㅋㅋ

        짧게 한 줄 썼다고 생각했는데

        캡처하고, 설명 달고, 코드 정리하고 나면

        시간이 훅 지나가 있닼ㅋㅋㅋㅋㅋㅋㅋ

        그래도 재미있어서 한다

        고생했다.

         

        다음엔 무슨 기능을 추가해볼까나

        반응형
        저작자표시 비영리 변경금지 (새창열림)

        '게임엔진 > 개발 일지' 카테고리의 다른 글

        [게임 개발 일지 #0] 프로젝트 WindSong 시작하기 - 픽셀과 복셀 사이에서  (2) 2025.05.14
        다음글
        다음 글이 없습니다.
        이전글
        이전 글이 없습니다.
        댓글
      조회된 결과가 없습니다.
      스킨 업데이트 안내
      현재 이용하고 계신 스킨의 버전보다 더 높은 최신 버전이 감지 되었습니다. 최신버전 스킨 파일을 다운로드 받을 수 있는 페이지로 이동하시겠습니까?
      ("아니오" 를 선택할 시 30일 동안 최신 버전이 감지되어도 모달 창이 표시되지 않습니다.)
      목차
      표시할 목차가 없습니다.
        • 안녕하세요
        • 감사해요
        • 잘있어요

        티스토리툴바