AddForce()와 velocity의 사용차이에 대해서 포스팅을 작성하던 도중에, 사실 이 두가지 모두 값으로 받는 백터값 내의 좌표는 모두 GetAxis()함수에서 x좌표와 y좌표를 받는다.
이 GetAxis()함수가 어떻게 동작하여 해당 좌표를 주는지 알 수 가 없었다. "Horizontal"과 "Vertical"이라는 문자열을 받고, 각각 수평과 수직 버튼에 대한 결과를 -1~1 사이값을 리턴해준다는 것만을 알고 있었다.
그래서 문득 궁금해졌다. 조건문으로 만든다면 어떻게 만들어야 할까? 일단 찔러보기로 했다.
우선 가장 기본적으로 Get.Key()를 이용해 방향키로 속도를 조절해보려고 했다.
// ...
if(Input.GetKey(KeyCode.UpArrow))
{
zSpeed = 1.0f
}
if(Input.GetKey(KeyCode.DownArrow))
{
zSpeed = -1.0f
}
// ...
rb.velocity = new Vector3(xSpeed * speed * Time.deltaTime, 0, zSpeed * speed * Time.deltaTime);
동작이 매우매우 딱딱 끊어졌다. 갑자기 막 팍 가고, 팍 꺽고.. 심지어 키를 땟음에도 불구하고 계속 움직였다. 이유는 update밖에서 선언한 zSpeed 변수에 한번 담고나서 계속 그 값이 유지되기 때문에 아무리 반대방향으로 키를 눌러도 이게 Getkey()로 읽으면 불러오는 프레임수가 딱들어맞지 않는한 멈출일 없이 계속 움직였다.
그래서 해당 변수를 Update()내에 선언하고 0.0f으로 초기화 해주었더니, 키입력이 없을때는 0.0f가 되어 문제는 해결되었다. 하지만, GetAxis를 사용할때는 굉장히 부드러웠는데 왜그럴까?
GetAxis를 사용해서 동작중에 속도값의 로그를 찍어보았다.
float speedX = Input.GetAxis("Horizontal") ;
float speedZ = Input.GetAxis("Vertical") ;
Debug.Log("X속도는 " + speedX + " 이다");
Debug.Log("Z속도는 " + speedZ + " 이다");
rb.velocity = new Vector3(speedX * speed * Time.deltaTime, 0, speedZ * speed * Time.deltaTime);
결과적으로 찍힌 로그는 프레임 마다 가속도가 아주 작게 붙는식으로 해서 올라가면 최대 1까지 올라가고 내려가면 최대 -1까지 내려가더라.
그래서 이제는 대입연산자를 쓰지않고 0.0f에서 덧셈, 뺄셈을 위한 연산자로 로직을 변경했다. 또한 랜덤하게 작은값이 들어갈 수 있도록 deltaSpeed라는 변수를 만들어 넣어주었다.
// ...
float deltaSpeed = Random.Range(0.0065f, 0.01f);
if(Input.GetKey(KeyCode.UpArrow))
{
zSpeed += deltaSpeed
}
if(Input.GetKey(KeyCode.DownArrow))
{
zSpeed -= deltaSpeed
}
// ...
rb.velocity = new Vector3(xSpeed * speed * Time.deltaTime, 0, zSpeed * speed * Time.deltaTime);
이러니까 계속 가속도가 붙어서 증가하더라.. 빼먹은게 음.. 아 1, -1 최대치!
// ...
float deltaSpeed = Random.Range(0.0065f, 0.01f);
if(Input.GetKey(KeyCode.UpArrow))
{
zSpeed += deltaSpeed
if(zSpeed > 1.0) zSpeed = 1.0f
}
if(Input.GetKey(KeyCode.DownArrow))
{
zSpeed -= deltaSpeed
if(zSpeed < -1.0) zSpeed = -1.0f
}
// ...
rb.velocity = new Vector3(xSpeed * speed * Time.deltaTime, 0, zSpeed * speed * Time.deltaTime);
와 벌써부터 코드가 지저분해 보였다. 이렇게 하니까 최대속도는 -1과 1이 되었는데, 가속은 부드러워 졌는데 감속은 아직 딱딱했다. 감속 또한 여기서 추가하면 아래와 같이 바뀐다.
// ...
float deltaSpeed = Random.Range(0.0065f, 0.01f);
if(Input.GetKey(KeyCode.UpArrow))
{
zSpeed += deltaSpeed
if(zSpeed > 1.0f) zSpeed = 1.0f
}
if(!Input.GetKey(KeyCode.UpArrow) && zSpeed > 0)
{
zSpeed -= deltaSpeed
if(zSpeed < 0.0f) zSpeed = 0.0f
}
if(Input.GetKey(KeyCode.DownArrow))
{
zSpeed -= deltaSpeed
if(zSpeed < -1.0) zSpeed = -1.0f
}
if(!Input.GetKey(KeyCode.DownArrow) && zSpeed < 0)
{
zSpeed += deltaSpeed
if(zSpeed > 0.0f) zSpeed = 0.0f
}
// ...
rb.velocity = new Vector3(xSpeed * speed * Time.deltaTime, 0, zSpeed * speed * Time.deltaTime);
근데.. 아직도 뚝뚝 끊기더라... 이유는 아까 Update()내에 변수를 선언해 줘서 키를 때면 바로 0.0f가 되어 감속조건에 들어가지 않아서 였어. 변수를 다시 Class레벨로 올려주고 동작시키니 완전 비슷하게 동작하게 되었다.
이제 코드에 중첩 if문을 리팩토링 해보자.
// ...
float deltaSpeed = Random.Range(0.0065f, 0.01f);
// UpArrow 키 처리
if (Input.GetKey(KeyCode.UpArrow))
{
zSpeed += deltaSpeed;
zSpeed = Mathf.Clamp(zSpeed, 0.0f, 1.0f);
}
else if (zSpeed > 0)
{
zSpeed -= deltaSpeed;
zSpeed = Mathf.Max(zSpeed, 0.0f);
}
// DownArrow 키 처리
if (Input.GetKey(KeyCode.DownArrow))
{
zSpeed -= deltaSpeed;
zSpeed = Mathf.Clamp(zSpeed, -1.0f, 0.0f);
}
else if (zSpeed < 0)
{
zSpeed += deltaSpeed;
zSpeed = Mathf.Min(zSpeed, 0.0f);
}
// ...
float horizontalSpeed = xSpeed * speed * Time.deltaTime;
float verticalSpeed = zSpeed * speed * Time.deltaTime;
rb.velocity = new Vector3(horizontalSpeed, 0, verticalSpeed);
if가 난발하던 코드를if else if 구문 2개가 되도록 만들었고, 마지막에 Vector3에 들어갈 값을 명시적으로 변경해 주었다.
여기에 GetAxis는 추가적으로 wasd 키도 같이 적용되도록 한다. 이 부분은 어떻게든 InputManager의 Alt Positive/Negative Button에 접근해보려고 했지만, 잘 안되서 따로 [SerializeField]로 KeyCode 커스텀 키를 만들어 주는 방향으로 진행했다.
// ...
public class PlayerController : MonoBehaviour
{
// ...
[SerializeField] KeyCode customUpKey = KeyCode.W;
[SerializeField] KeyCode customDwonKey = KeyCode.S;
[SerializeField] KeyCode customRightKey = KeyCode.D;
[SerializeField] KeyCode customLeftKey = KeyCode.A;
void Update()
{
// ...
float deltaSpeed = Random.Range(0.0065f, 0.01f);
// UpArrow 키 처리
if (Input.GetKey(KeyCode.UpArrow) || Input.GetKey(customUpKey))
{
zSpeed += deltaSpeed;
zSpeed = Mathf.Clamp(zSpeed, 0.0f, 1.0f);
}
else if (zSpeed > 0)
{
zSpeed -= deltaSpeed;
zSpeed = Mathf.Max(zSpeed, 0.0f);
}
// ...
float horizontalSpeed = xSpeed * speed * Time.deltaTime;
float verticalSpeed = zSpeed * speed * Time.deltaTime;
rb.velocity = new Vector3(horizontalSpeed, 0, verticalSpeed);
}
이제 거의 똑같이 동작하더라. 사람 눈으로 봤을때는 완전히 똑같은 느낌이 들었다. 물론 올라가는 가속도나 감속도는 분명 다르겠지만, 뭐.. GetAxis도 랜덤하게 올라가고 내려가던데.. 둘 다 순간이동 하는 느낌이 있는건 어쩔 수 없는 것 같다. 그냥 고정적으로 올리면 더 부드러울것 같지만 목표가 GetAxis의 동작처럼 하는 것이었으니까 GetAxis로 동작시킬 때나 위 코드로 동작시킬 때나 별다른 느낌의 차이는 없었다. 하지만 역시 작성해야할 코드의 로직이 너무 많아졌고, 생각보다 고려해야할 사항이 많아서 위에 정리한 내용 외에도 시도해 본 방법이 여러가지 있었다.
대충 GetAxis는 위와 비슷하게 돌아갈 것이다. 하지만... 역시 GetAxis 쓰자.. 코드 3줄만 적으면 위 내용을 모두 대응할 수 있다.
float horizontalSpeed = Input.GetAxis("Horizontal") * speed * Time.deltaTime;
float verticalSpeed = Input.GetAxis("Vertical") * speed * Time.deltaTime;
rb.velocity = new Vector3(horizontalSpeed , 0, verticalSpeed );
GetAxisRaw는 훨씬 쉽다. 증가 감소가 아닌 진짜 딱딱 끊어지는 -1,0,1만 반환하기 때문에 처음에 시도하던 방법대로하면 별 문제없이 비슷한 로직을 짤 수 있을 것이다.