using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class InputManager
{
public Action KeyAction = null;
public void OnUpdate()
{
if (Input.anyKey == false)
return;
if (KeyAction != null)
KeyAction.Invoke();
}
}
사용자의 키 입력을 기다렸다가, 키 입력을 받으면 모든 클래스에 알리는 역할을 한다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Managers : MonoBehaviour
{
static Managers s_Instance;
static Managers Instance { get { Init(); return s_Instance; } }
InputManager _input = new InputManager();
public static InputManager Input { get { return Instance._input; } }
// Start is called before the first frame update
void Start()
{
Init();
}
// Update is called once per frame
void Update()
{
_input.OnUpdate();
}
static void Init()
{
if (s_Instance == null)
{
GameObject go = GameObject.Find("@Managers");
if (go == null)
{
go = new GameObject { name = "@Managers" };
go.AddComponent<Managers>();
}
DontDestroyOnLoad(go);
s_Instance = go.GetComponent<Managers>();
}
}
}
기존에 작성된 코드에서 InputManager의 인스턴스를 추가하고 (InputManager는 Monobehaviour를 상속받지 않았기 때문에 new를 통한 인스턴스화가 가능하다.) 프로퍼티를 이용해 Managers.Input으로 접근 할 수 있도록 하였다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
[SerializeField]
float _speed = 10.0f;
void Start()
{
Managers.Input.KeyAction -= OnKeyboard;
Managers.Input.KeyAction += OnKeyboard;
}
void Update()
{
}
void OnKeyboard()
{
if (Input.GetKey(KeyCode.W))
{
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(Vector3.forward), 0.2f);
transform.position += Vector3.forward * Time.deltaTime * _speed;
}
if (Input.GetKey(KeyCode.S))
{
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(Vector3.back), 0.2f);
transform.position += Vector3.back * Time.deltaTime * _speed;
}
if (Input.GetKey(KeyCode.A))
{
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(Vector3.left), 0.2f);
transform.position += Vector3.left * Time.deltaTime * _speed;
}
if (Input.GetKey(KeyCode.D))
{
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(Vector3.right), 0.2f);
transform.position += Vector3.right * Time.deltaTime * _speed;
}
}
}
Onkeyboard() 함수를 통해 이동과 방향 전환을 구현하였고, start() 함수 내에 InputManager의 Invoke를 받으면 Onkeyboard() 함수를 실행시키도록 하였다.
이 때 굳이 처음에 -= Onkeyboard() 를 하는 이유는 만약 다른곳에서 이미 OnKeyboard를 호출하고 있을 수도 있기 때문에 여러번 호출을 방지하기 위해 호출을 먼저 취소하고 다시 호출한다. (어차피 KeyAction의 기본값은 null이기 때문에 가능)
Time.deltaTime의 역할은 플레이어의 이동을 프레임당 움직이지 않도록 해준다.
플레이어의 이동이 프레임마다 이루어진다고 한다면, 컴퓨터의 사양마다 1초마다 나오는 프레임의 수가 다르기 때문에 어떤 컴퓨터를 사용하냐에 따라서 플레이어의 속도가 달라지는 경우가 생긴다.
이를 방지하기 위해 직전 프레임이 완료되기까지 걸린 시간을 계산하여 그 값을 곱해준다.
이러면 프레임이 낮게 나오는 상황일수록 Time.deltaTime의 값이 커지기 때문에 결국에 플레이어의 속도는 프레임에 상관없이 같은 값이 나온다.
앞서 Managers 클래스를 작성 시에 싱글톤 패턴을 사용했다고 했는데, 싱글톤 패턴이란 Static을 이용해서 매니저 클래스들을 한개의 객체만 생성해서 사용하는 것을 말한다.
만약 매니저 클래스를 Static을 사용하지 않았다면 위의 경우에는 PlayerController에서 InputManager에 접근하는 데에 훨씬 많은 시간이 걸릴 것이다.
void Start()
{
Manager mn = GameObject.Find("@Managers").GetComponent<Managers>();
mn.Input()...
}
이런식으로 매니저에 접근할 때마다 Find함수를 사용해서 접근을 해야하고, 이는 당연히 성능 저하로 이어진다.
따라서 처음에 모든 매니저 클래스들을 Static을 통해 생성한 뒤에 접근하는 것이 성능적으로 효율적이다.