김김김의 게임개발
  • 유니티 게임개발 입문 #1
    2023년 09월 04일 21시 02분 44초에 업로드 된 글입니다.
    작성자: noun06

    PPU

    • PPU(Pixel Per Unit)은 스프라이트의 크기와 월드 공간에서의 픽셀 단위 크기 간의 관계를 나타냄. 스프라이트 크기 조정과 일관된 스케일링을 위한 중요한 요소임. 2D에서 PPU가 높을수록 스프라이트가 작아지며 낮을수록 스프라이트가 커짐. 

     

    로컬 좌표계 & 월드 좌표계

    • 로컬 좌표계는 개별 객체 또는 게임 오브젝트 자체 좌표 시스템. (상대적인 위치와 변화)
    • 월드 좌표계는 게임 월드 전체를 나타내는 전역 좌표 시스템. (절대적인 위치와 변화)

     

    Time.deltaTime

    • 이전 프레임부터 현재 프레임까지의 경과 시간을 나타냄. 프레임 간 간격은 하드웨어의 성능과 상관없이 일정한 속도로 게임이 진행되도록 보장함.
    public float moveSpeed = 5.0f;
    
    void Update()
    {
        // 이동 로직: 매 프레임마다 이동
        transform.Translate(Vector3.forward * moveSpeed * Time.deltaTime);
    }

     

    직렬화 속성

    • SerializedField는 private로 선언된 변수를 코드 내에서는 보호하면서 인스펙터에 노출시키는데 활용함. 

     

    New Input System

    • Input Action은 특정 입력에 대한 정의된 작업을 나타냄. Input Action Asset을 통해 여러개의 입력 행동을 그룹화하여 저장함. Player Input이라는 컴포넌트를 통해 자동으로 입력 해동을 처리하고 해당 오브젝트에 메시지를 보냄.
    • 크로스플랫폼 호환, 리바인딩, 멀티플레이어 지원등의 장점을 가지고 있음.

     

    입력 이벤트와 이벤트 호출

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class TopDownCharacterController : MonoBehaviour
    {
        // Move 이벤트와 Look 이벤트를 처리하기 위한 델리게이트
        public event Action<Vector2> OnMoveEvent;
        public event Action<Vector2> OnLookEvent;
    
        // Move 이벤트를 호출하는 메서드
        public void CallMoveEvent(Vector2 direction)
        {
            // 이벤트가 null이 아니면 이벤트를 호출하고 방향 값을 전달
            OnMoveEvent?.Invoke(direction);
        }
    
        // Look 이벤트를 호출하는 메서드
        public void CallLookEvent(Vector2 direction)
        {
            // 이벤트가 null이 아니면 이벤트를 호출하고 방향 값을 전달
            OnLookEvent?.Invoke(direction);
        }
    }

     

    사용자 입력 처리

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.InputSystem;
    
    public class PlayerInputController : TopDownCharacterController
    {
        private Camera _camera;
    
        private void Awake()
        {
            // 메인 카메라 참조
            _camera = Camera.main;
        }
    
        public void OnMove(InputValue value)
        {
            // InputValue에서 벡터 값을 가져와 정규화
            Vector2 moveInput = value.Get<Vector2>().normalized;
            
            // 상위 클래스인 TopDownCharacterController의 CallMoveEvent 메서드 호출
            CallMoveEvent(moveInput);
        }
    
        public void OnLook(InputValue value)
        {
            // InputValue에서 벡터 값을 가져옴
            Vector2 newAim = value.Get<Vector2>();
            
            // 화면 좌표를 월드 좌표로 변환
            Vector2 worldPos = _camera.ScreenToWorldPoint(newAim);
            newAim = (worldPos - (Vector2)transform.position).normalized;
    
            // 벡터의 크기가 0.9 이상일 때만 상위 클래스인 TopDownCharacterController의 CallLookEvent 메서드 호출
            if (newAim.magnitude >= .9f)
            {
                CallLookEvent(newAim);
            }
        }
    
        public void OnFire(InputValue value)
        {
            // 총 발사와 관련된 입력 처리(진)
            Debug.Log("OnFire" + value.ToString());
        }
    }

     

    캐릭터 이동 처리

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class TopDownMovement : MonoBehaviour
    {
        private TopDownCharacterController _controller;
        private Vector2 _movementDirection = Vector2.zero;
        private Rigidbody2D _rigidbody;
    
        private void Awake()
        {
            // TopDownCharacterController 컴포넌트 및 Rigidbody2D 컴포넌트 참조
            _controller = GetComponent<TopDownCharacterController>();
            _rigidbody = GetComponent<Rigidbody2D>();
        }
    
        private void Start()
        {
            // TopDownCharacterController의 OnMoveEvent에 Move 메서드를 등록
            _controller.OnMoveEvent += Move;
        }
    
        private void FixedUpdate()
        {
            // 이동 벡터를 사용하여 RigidBody2D의 속도를 설정
            ApplyMovment(_movementDirection);
        }
    
        private void Move(Vector2 direction)
        {
            // 이동 벡터를 업데이트
            _movementDirection = direction;
        }
    
        private void ApplyMovment(Vector2 direction)
        {
            // 이동 벡터*5를 RigidBody2D의 속도로 설정
            direction = direction * 5;
            _rigidbody.velocity = direction;
        }
    }

     

    조준 시스템

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class TopDownAimRotation : MonoBehaviour
    {
        [SerializeField] private SpriteRenderer armRenderer;
        [SerializeField] private Transform armPivot;
        [SerializeField] private SpriteRenderer characterRenderer;
    
        private TopDownCharacterController _controller;
    
        private void Awake()
        {
            // TopDownCharacterController 컴포넌트를 참조
            _controller = GetComponent<TopDownCharacterController>();
        }
    
        void Start()
        {
            // TopDownCharacterController의 OnLookEvent 이벤트에 OnAim 메서드를 등록
            _controller.OnLookEvent += OnAim;
        }
    
        // OnAim 메서드는 Look 이벤트에서 호출
        public void OnAim(Vector2 newAimDirection)
        {
            // RotateArm 메서드를 호출하여 팔을 회전
            RotateArm(newAimDirection);
        }
    
        // 팔의 회전을 처리하는 메서드
        private void RotateArm(Vector2 direction)
        {
            // 방향 벡터를 이용하여 회전 각도를 계산
            float rotZ = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
    
            // 팔의 SpriteRenderer를 상하로 반전
            armRenderer.flipY = Mathf.Abs(rotZ) > 90f;
    
            // 캐릭터의 SpriteRenderer를 팔의 뒤집힌 상태에 맞춰 좌우로 반전
            characterRenderer.flipX = armRenderer.flipY;
    
            // 팔 피벗(Transform)을 사용하여 팔을 회전
            armPivot.rotation = Quaternion.Euler(0, 0, rotZ);
        }
    }

     

    이벤트 공격 로직 추가

    // TopDownCharacterController.cs
    
    public event Action OnAttackEvent;
    
    private float _timeSinceLastAttack = float.MaxValue; // 마지막 공격 이후의 시간을 추적하기 위한 변수
    protected bool IsAttacking { get; set; }
    
    protected virtual void Update()
    {
        HandleAttackDelay();
    }
    
    private void HandleAttackDelay()
    {
        if (_timeSinceLastAttack <= 0.2f)
        {
            _timeSinceLastAttack += Time.deltaTime; // _timeSinceLastAttack 값을 증가시켜 딜레이 시간을 계산
        }
    
        if (IsAttacking && _timeSinceLastAttack > 0.2f)
        {
            _timeSinceLastAttack = 0; // _timeSinceLastAttack 값을 0으로 재설정하여 다음 공격을 위한 딜레이를 시작
            CallAttackEvent();
        }
    }

     

    슈팅 시스템

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class TopDownShooting : MonoBehaviour
    {
        private TopDownCharacterController _contoller;
    
        [SerializeField] private Transform projectileSpawnPosition; // 발사체 생성 위치
        private Vector2 _aimDirection = Vector2.right; // 초기 발사 방향 (오른쪽)
    
        public GameObject testPrefab; 
    
        private void Awake()
        {
            _contoller = GetComponent<TopDownCharacterController>();
        }
    
        void Start()
        {
            // TopDownCharacterController의 이벤트에 메서드를 연결
            _contoller.OnAttackEvent += OnShoot; // OnAttackEvent 이벤트에 OnShoot 메서드를 연결하여 공격 이벤트 발생 시 총 발사
            _contoller.OnLookEvent += OnAim; // OnLookEvent 이벤트에 OnAim 메서드를 연결하여 마우스 이동에 따른 조준 업데이트
        }
    
        private void OnAim(Vector2 newAimDirection)
        {
            _aimDirection = newAimDirection; // 마우스 이동에 따라 조준 방향을 업데이트
        }
    
        private void OnShoot()
        {
            CreateProjectile(); // 공격 이벤트가 발생하면 발사체 생성 메서드 호출
        }
    
        private void CreateProjectile()
        {
            // 발사체 프리팹을 지정한 위치에 생성 (회전은 없음)
            Instantiate(testPrefab, projectileSpawnPosition.position, Quaternion.identity);
        }
    }

     

     

    댓글