유니티
유니티에서 1인칭 운전자 시점 자동차 구현해보기
영춘권의달인
2022. 12. 21. 20:24
VR 주차연습 시뮬레이터를 만들기 위해
구글, 유튜브에서 여러 정보들을 찾아보면서 1인칭 운전자 시점의 자동차를 만들었다.
유니티에서 휠 콜라이더라는 컴포넌트를 제공해줘서 생각보다 간단하게 움직이기는 하는 자동차를 만들 수 있었다.
기어를 Parking, Reverse, Neutral, Drive 로 나누고 각각의 기어에 맞게 엑셀과 브레이크를 밟을때의 동작을 간단하게 구현하였다.
속력에 따라 자동차에 가해지는 압력, 애커만 조향과 같이 자동차의 자연스러운 움직임을 위한 것들도 적용을 하긴 했는데 추가적으로 자세히 알아본 뒤에 수정하면 좋을 것 같다.
일단 키보드로 동작하도록 해서 입력이 1 아니면 0 이런식이어서 차체가 좀 흔들리거나 하는 부분이 있는데 데 실제 스티어링 휠을 적용하면서 수치를 맞춰나가야 할 것 같다.
아래는 대충 동작만 하도록 만들어본 자동차 컨트롤러 클래스이다. 코드가 좀 더럽다ㅎㅎ;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum GearType
{
Parking,
Reverse,
Neutral,
Drive,
}
public class CarController2 : MonoBehaviour // 자동차 컨트롤러 클래스(키보드로 조작, 임시)
{
public float powerN; // 바퀴를 회전시킬 힘 (기본)
public float powerD; // 바퀴를 회전시킬 힘 (주행)
public float powerB; // 브레이크 힘
public float rot; // 바퀴의 회전량
public float downForceValue; // 차에 가해지는 압력
public float radius = 6; // 애커만 조향 관련 변수, 뭘 의미하는지는 모르겠음
public GearType gearType = GearType.Parking; // 기어
public Camera cam = null; // 카메라의 위치, VR 적용시킬때는 카메라 대신 XR Origin으로 수정
public Transform DriverPos; // 운전자의 위치
public WheelCollider[] wheels = new WheelCollider[4]; // 바퀴의 휠 콜라이더
public GameObject[] wheelMesh = new GameObject[4]; // 바퀴의 메쉬 게임오브젝트
private Rigidbody rigidBody;
void Start()
{
for(int i = 0; i < wheelMesh.Length; i++)
{
wheels[i].transform.position = wheelMesh[i].transform.position;
}
rigidBody = GetComponent<Rigidbody>();
//무게중심을 y축 아래방향으로
rigidBody.centerOfMass = new Vector3(0, -1, 0);
cam.transform.position = DriverPos.position;
}
void Update()
{
InputMove();
SynchronizeCarMove();
AddDownForce();
}
private void LateUpdate()
{
cam.transform.position = DriverPos.position;
// 차체가 흔들려도 z축 중심의 회전은 하지 않도록 함
cam.transform.rotation = Quaternion.Euler(DriverPos.transform.rotation.eulerAngles.x,
DriverPos.rotation.eulerAngles.y, cam.transform.rotation.eulerAngles.z);
}
private void InputMove()
{
// 기어 조작 (P -> R -> N -> D)
if (Input.GetKeyDown(KeyCode.K))
{
if (gearType != GearType.Drive)
{
gearType += 1;
}
}
// 기어 조작 (D -> N -> R -> P)
else if (Input.GetKeyDown(KeyCode.I))
{
if(gearType!= GearType.Parking)
{
gearType -= 1;
}
}
// 4바퀴 이동 조작
switch (gearType)
{
case GearType.Parking:
for (int i = 0; i < wheelMesh.Length; i++)
{
wheels[i].motorTorque = 0;
wheels[i].brakeTorque = powerB;
}
break;
case GearType.Reverse:
for (int i = 0; i < wheelMesh.Length; i++)
{
if (Input.GetKey(KeyCode.Space))
{
wheels[i].motorTorque = 0;
wheels[i].brakeTorque = powerB;
}
else
{
wheels[i].motorTorque = -powerN;
wheels[i].brakeTorque = 0;
}
}
break;
case GearType.Neutral:
for (int i = 0; i < wheelMesh.Length; i++)
{
wheels[i].motorTorque = 0;
wheels[i].brakeTorque = powerB;
}
break;
case GearType.Drive:
for (int i = 0; i < wheelMesh.Length; i++)
{
if (Input.GetKey(KeyCode.Space))
{
wheels[i].motorTorque = 0;
wheels[i].brakeTorque = powerB;
}
else
{
wheels[i].brakeTorque = 0;
if (Input.GetKey(KeyCode.UpArrow) || Input.GetKey(KeyCode.W))
{
wheels[i].motorTorque = powerD;
}
else
{
wheels[i].motorTorque = powerN;
}
}
}
break;
}
// 회전은 앞에 두 바퀴만
// 앞 두 바퀴의 회전량 같은 경우
//for(int i = 0; i < 2; i++)
//{
// wheels[i].steerAngle = Input.GetAxis("Horizontal") * rot;
//}
// 애커만 조향을 적용하여 앞의 두 바퀴의 회전이 다른 경우
if (Input.GetAxis("Horizontal") > 0)
{ // rear tracks size is set to 1.5f wheel base has been set to 2.55f
wheels[0].steerAngle = Mathf.Rad2Deg * Mathf.Atan(2.55f / (radius + (1.5f / 2))) * 1;
wheels[1].steerAngle = Mathf.Rad2Deg * Mathf.Atan(2.55f / (radius - (1.5f / 2))) * 1;
}
else if (Input.GetAxis("Horizontal") < 0)
{
wheels[0].steerAngle = Mathf.Rad2Deg * Mathf.Atan(2.55f / (radius - (1.5f / 2))) * (-1);
wheels[1].steerAngle = Mathf.Rad2Deg * Mathf.Atan(2.55f / (radius + (1.5f / 2))) * (-1);
}
else
{
if (wheels[0].steerAngle > 0)
{
wheels[0].steerAngle = Mathf.Max(wheels[0].steerAngle - 0.5f, 0);
wheels[1].steerAngle = Mathf.Max(wheels[1].steerAngle - 0.5f, 0);
}
else if (wheels[0].steerAngle < 0)
{
wheels[0].steerAngle = Mathf.Min(wheels[0].steerAngle + 0.5f, 0);
wheels[1].steerAngle = Mathf.Min(wheels[1].steerAngle + 0.5f, 0);
}
else
{
wheels[0].steerAngle = 0;
wheels[1].steerAngle = 0;
}
}
}
// 휠 콜라이더와 메시 동기화
private void SynchronizeCarMove()
{
Vector3 wheelPos;
Quaternion wheelRot;
for(int i=0; i < wheelMesh.Length; i++)
{
wheels[i].GetWorldPose(out wheelPos, out wheelRot);
wheelMesh[i].transform.position = wheelPos;
wheelMesh[i].transform.rotation = wheelRot;
}
}
// 속도에 따라 차에 가해지는 압력 적용
void AddDownForce()
{
rigidBody.AddForce(-transform.up * downForceValue * rigidBody.velocity.magnitude);
}
}