유니티 심화/복습

[복습] HeroShooter - Main 만들기 + AngleAxis, Mathf.Atan2, Mathf.Rad2Deg

다모아 2023. 8. 24. 01:30

잘 모르겠어서 다시한번 알아본 3가지들

 

1. Mathf.Atan2(float y, floay x); , Mathf.Rad2Deg

Quaternion을 사용해야할 때, 즉 회전이 필요할 때 사용한다.

유니티에서 제공하고 있는 아크탄젠트 함수는 반환값이 라디안값이라서 Mathf.Atan2를 해준 후에 Mathf.Rad2Deg를 곱해주면 우리가 알고있는 몇'도'로 라디안값에서 변하게 된다.

 

2. Quaternion.AngleAxis(float angle, Vector3 axis);

Mathf.Atan2와 MathfRad2Deg로 구한 '도'를 이용해서 z축으로 축을 회전시켜준다.


지금까지 HeroShooter를 하면서 Main을 만들지 않고 했었는데 만들어온 함수들을 보니.. PlayerController에만 놔둘 수는 없을 것 같았고, 지금은 괜찮지만 나중에 가서 10스테이지 100스테이지를 간다는 가정하에 코드가 얼마나 개판이 될지 상상이 안가서 Main에다가 따로 만들어주기로 결심을 하였다.


하이라키와 프로젝트는 이렇게 구성되어있다.

 

TutorialSceneMain

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class TutorialSceneMain : MonoBehaviour
{
    //플레이어
    [SerializeField]
    private PlayerController playerController;
    //조이스틱
    [SerializeField]
    private CJoystick joystick;
    //포탈
    [SerializeField]
    private GameObject portalGo;
    //왼쪽 문
    [SerializeField]
    private DoorController leftDoorController;
    //오른쪽 문
    [SerializeField]
    private DoorController rightDoorController;
    //페이드아웃
    [SerializeField]
    private Image dim;
    //NPC 텍스트
    [SerializeField]
    private Text text;

    // Start is called before the first frame update
    void Start()
    {
        //포탈에 닿았을 때
        this.playerController.reachPortal = () => {
            //포탈 삭제하고
            Destroy(this.portalGo.gameObject);
            //문 열기
            this.leftDoorController.OpenDoor();
            this.rightDoorController.OpenDoor();
        };

        //문에 닿았을 때
        this.playerController.reachDoor = () => {
            //씬 전환하기
            this.FadeOutAndSceneManager();
        };
    }

    // Update is called once per frame
    void Update()
    {
        //수평, 수직 좌표 구하고 moveDir에 넣기
        float h = this.joystick.Horizontal;
        float v = this.joystick.Vertical;
        Vector3 moveDir = new Vector3(h, 0, v);
        //moveDir이 Vector3.zero일 경우
        if(moveDir == Vector3.zero)
        {
            //정지 애니메이션 실행
            this.playerController.PlayAnimation(EnumState.ePlayerState.Idle);
        }
        else // (0,0,0)이 아닐 경우
        {
            this.playerController.Move(moveDir);
        }
    }

    private void FadeOutAndSceneManager()
    {
        //페이드아웃 이미지 액티브 활성화
        this.dim.gameObject.SetActive(true);
        //코루틴 활성화
        this.StartCoroutine(this.CoFadeOutAndSceneManager());
    }

    private IEnumerator CoFadeOutAndSceneManager()
    {
        //dim 컬러 가져오기
        Color color = this.dim.color;
        while (true)
        {
            //color.a --> 0.01f씩 더해주기
            color.a += 0.01f;
            //color.a가 1보다 높으면 break
            if(color.a >= 1)
            {
                //while문 나오기
                break;
            }
            //정지
            yield return null;
        }
        //while문 나온 후 씬 전환
        SceneManager.LoadScene("Stage1Scene");
    }
}

 

PlayerController

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class PlayerController : MonoBehaviour
{
    //캐릭터 움직임 속도
    [SerializeField]
    private float moveSpeed = 5f;
    //애니메이션
    private Animator anim;
    //포탈태그했을 때
    public Action reachPortal;
    //문에 닿았을 때
    public Action reachDoor;
    void Start()
    {
        this.anim = this.GetComponent<Animator>();
    }

    // Update is called once per frame
    void Update()
    {
        //Ray로 한번 발견이나 해보자
        Ray ray = new Ray(this.transform.position, this.transform.forward);
        RaycastHit hit;

        if(Physics.Raycast(ray, out hit, 1000f))
        {
            DrawArrow.ForDebug(this.transform.position, hit.collider.transform.position, 2f, Color.red, ArrowType.Solid);
            if (hit.collider.tag == "Monster")
            {
                Debug.Log("몬스터");
            }
        }
    }

    public void Move(Vector3 moveDir)
    {
        //달리는 애니메이션 실행
        this.PlayAnimation(EnumState.ePlayerState.RunF);
        //Mathf.Atan2와 Mathf.Rad2Deg를 이용해서 회전시키기
        float angle = Mathf.Atan2(moveDir.x, moveDir.z) * Mathf.Rad2Deg;
        //AngleAxis를 이용하기
        Quaternion q = Quaternion.AngleAxis(angle, Vector3.up);
        //캐릭터에 회전 적용
        this.transform.rotation = q;
        //캐릭터 움직이기
        this.transform.Translate(Vector3.forward * this.moveSpeed * Time.deltaTime);
    }
    public void PlayAnimation(EnumState.ePlayerState state)
    {
        this.anim.SetInteger("State", (int)state);
    }

    private void OnCollisionEnter(Collision collision)
    {
        if (collision.collider.tag == "Door")
        {
            //대리자 사용해서 씬 전환하기
            this.reachDoor();
        }
        else if (collision.collider.tag == "Portal")
        {
            //대리자 사용해서 문 열고, 포탈 제거하기
            this.reachPortal();
        }
    }
}

 

EnumState

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EnumState
{
    public enum ePlayerState
    {
        Idle, RunF
    }
}

 

동영상은 똑같으므로 찍지않았다.

TutorialMain으로 옮겨오면서 다시 한번 공부하게되어 도움이 된 것 같다.