Humility

아무리 노력해도 최고가 되지 못할 수 있다⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀그럼에도 노력하는자가 가장 겸손한 것 아닌가

공부하는 블로그

유니티/기능구현

[Unity] 유니티 C#) 2D 게임 사다리(Ladder)와 피킹(Peeking)

새벽_글쓴이 2024. 12. 25. 00:40
반응형

소닉 더 헤이호그

 

소닉이나 여타 많은 게임들은 Peeking 기능을 제공한다
특히 다양한 기믹과 숨겨진 장치들이 즐비한 매트로베니아 장르에도
이러한 기능은 잘 어울릴 것 같다 

0. 준비

플레이어로 사용할 캡슐콜라이더를 만들고 타일맵을 대충 깔아보자

 

1. 지형 제작

이렇게 3가지 나누어 만들었다

 

타일맵 (바깥지형)

특징: 타일맵 콜라이더 컴포넌트가 붙어있다

 

거짓맵 ( 단순히 지형을 보여주기 위함 )

특징: 아무런 컴포넌트도 붙어있지 않음

 

플랫폼맵 ( 밟고 올라갈 수 있는 지형 )

특징: 아래에선 통과가 되지만 위에선 통과가 안됨

 

설정방법

 

1. 타일맵 콜라이더 Used By Effector 활성화


2. 리지드바디 2D 컴포넌트 부착 후 바디타입 Static으로 변경


3. Platform Effector 2D 컴포넌트 부착

 

※ 사다리가 붙을 지형 맨 위에 이러한 지형을 만들어 놓으면

같은 효과를 볼 수 있을 것이다.

 

 

 

 

 

 

 

 

 

 

 

 

2. 카메라

카메라의 이동, 제한범위, 피킹을 만든 스크립트이다

using UnityEngine;

namespace Peeking
{
    public class CameraController : MonoBehaviour
    {
        // 플레이어
        [SerializeField]
        private GameObject player;

        // 카메라가 움직이는 값
        private float cameraX;
        private float cameraY;
        private float limitCameraX;
        private float limitCameraY;

        // 피킹 관련
        private bool isPeeking;
        private float peekingSpeed = 5f;
        private float peekingDir = 0f;
        private float maxPeekingOffset = 2f;
        private float peeking = 0f;

        // 카메라 범위 제한
        [SerializeField] private float leftLimit;
        [SerializeField] private float rightLimit;
        [SerializeField] private float topLimit;
        [SerializeField] private float bottomLimit;

        private void LateUpdate()
        {
            // 카메라 이동
            cameraX = player.transform.position.x;
            cameraY = player.transform.position.y;

            // 카메라 범위 제한
            limitCameraX = Mathf.Clamp(cameraX, leftLimit, rightLimit);
            limitCameraY = Mathf.Clamp(cameraY, bottomLimit, topLimit);

            // 피킹
            Peek();
        }

        // 피킹 함수
        private void Peek()
        {
            if (isPeeking)
            {
                float targetPeekingOffset = peekingDir * maxPeekingOffset;

                peeking = Mathf.MoveTowards(peeking, targetPeekingOffset, peekingSpeed * Time.deltaTime);

                float movePeeking = limitCameraY + peeking;
                if (movePeeking > topLimit)
                {
                    movePeeking = topLimit;
                }
                else if (movePeeking < bottomLimit)
                {
                    movePeeking = bottomLimit;
                }
                transform.position = new Vector3(limitCameraX, movePeeking, -10);
            }
            else
            {
                transform.position = new Vector3(limitCameraX, limitCameraY, -10);
                peeking = 0;
            }
        }

        // 외부에서 값을 변경해줄 수 있게 해주는 함수
        public void SetPeeking(bool _isPeeking, float _peekingDir)
        {
            isPeeking = _isPeeking;
            peekingDir = _peekingDir;
        }
    }
}

 

3. 플레이어

피킹은 플레이어가 움직이지 않을때만 가능하게 만들고 싶었다

그렇기 때문에 Enum을 사용하여 조금 더 명확하게 만들었다

using UnityEngine;

namespace Peeking
{
    public enum PlayerState
    {
        None,
        Idle,
        Move,
    }

    public class PlayerController : MonoBehaviour
    {
        private PlayerState state;

        // 카메라 피킹
        float peekDir;
        float peekingTime;

        // 이동
        float moveSpeed = 7f;
        float moveDir = 0f;
        Rigidbody2D rigid;
        int jumpCount = 1;

        void Start()
        {
            state = PlayerState.Idle;
            rigid = GetComponent<Rigidbody2D>();
        }

        void Update()
        {
            OnKeyboard();
            PlayerStateSwitch();
        }
        
        private void PlayerStateSwitch()
        {
            switch (state)
            {
                case PlayerState.Idle:
                    Peeking();
                    break;

                case PlayerState.Move:
                    PlayerMove();
                    break;

                default:
                    break;
            }
        }

        // 키보드 입력
        private void OnKeyboard()
        {
            // 이동
            if(Input.GetKey(KeyCode.LeftArrow)) 
            {
                state = PlayerState.Move;
                moveDir = -1f;
            }
            else if(Input.GetKey(KeyCode.RightArrow)) 
            {
                state = PlayerState.Move;
                moveDir = 1f;
            }
            else
            {
                moveDir = 0f;
                state = PlayerState.Idle;
            }

            // 점프
            if (Input.GetKey(KeyCode.Space) && jumpCount < 1)
            {
                rigid.velocity = Vector2.up * 9;
                jumpCount++;
            }

            if (rigid.velocity.y == 0f)
                jumpCount = 0;
        }

        // 피킹
        void Peeking()
        {
            float targetPeekDir = Input.GetKey(KeyCode.UpArrow) ? 1f : (Input.GetKey(KeyCode.DownArrow) ? -1f : 0f);

            if (targetPeekDir != 0f)
            {
                peekingTime += Time.deltaTime;
                if (peekingTime > 0.5f)
                {
                    peekDir = targetPeekDir;
                }
            }
            else
            {
                peekingTime = 0f;
                peekDir = 0f;
            }

            Camera.main.GetComponent<CameraController>().SetPeeking(true,peekDir);
        }

        // 이동
        void PlayerMove()
        {
            transform.position += Vector3.right * moveDir * moveSpeed * Time.deltaTime;
        }
    }
}

 

4. 완성

반응형