Engine Sound effect in Unity
유니티에서 자동차의 엔진소리가 속도에 비례하여 커지는 예
앞선 내용 참조 : http://micropilot.tistory.com/category/Unity3D%20Car/Friction%20%26%20Slip
테스트용 사운드 파일:
사운드 출력설정 절차
Project 뷰에 사운드 파일을 임포트(혹은 드래그)한다
Hierarchy 뷰의 Car 오브젝트를 선택하고 우측 Inspector 뷰에서 [Add Component] 버튼을 누르거나, 메뉴바에서 Component > Audio > Audio Source > 를 선택한다
Audio Source 콤포넌트가 Car 오브젝트에 추가 되었으면, 우측 Inspector 뷰에서 Audio Source 콤포넌트 섹션의 Audio Clip 란에 Project 뷰의 오디오 파일을 드래그하여 할당한다
프로그램 시작부터 사운드를 출력하려면 Play On Awake를 체크하고, 또 반복해서 출력하려면 Loop 에 체크하면 된다
[Car 오브젝트에 Audio Source 콤포넌트 추가후 Audio Source 콤포넌트에 오디오 파일을 드래그하여 할당한다]
한개의 GameObject 에 한개의 사운드 파일이 포함된 경우에는 별도의 변수를 선언할 필요가 없이 멤버변수인 audio 를 통해 오디오 속성을 제어할 수 있다.
다음과 같은 코드만 사용해도 기본적인 사운드를 출력할 수 있고, 자동차의 기어변속에 따라서 엔진 소음이 달라지도록 하려면 약간의 코드를 더 추가하면 된다
Car.cs
using UnityEngine;
using System.Collections;
public class Car : MonoBehaviour {
// 자동차 바퀴 방향조종을 위한 Transform 4개
public Transform tireTransformFL;
public Transform tireTransformFR;
public Transform tireTransformRL;
public Transform tireTransformRR;
public WheelCollider colliderFR;
public WheelCollider colliderFL;
public WheelCollider colliderRR;
public WheelCollider colliderRL;
// 바퀴 회전을 위한 Transform
public Transform wheelTransformFL;
public Transform wheelTransformFR;
public Transform wheelTransformRL;
public Transform wheelTransformRR;
// 속도에 따라서 방향전환율을 다르게 적용하기 위한 준비
public float highestSpeed = 500f;
public float lowSpeedSteerAngle = 0.1f;
public float highSpeedStreerAngle = 25f;
// 감속량
public float decSpeed = 7f;
// 속도제한을 위한 변수들
public float currentSpeed;
public float maxSpeed = 350f; // 전진 최고속도
public float maxRevSpeed = 100f; // 후진 최고속도
// 백라이트 조정
public GameObject brakeLight;
public GameObject reverseLight;
public Material backBrakeIdle;
public Material backBrakeLight;
public Material backReverseIdle;
public Material backReverseLight;
public int maxTorque = 20;
private float prevSteerAngle;
private bool bHandBraked = false;
private float lowStiffness = 0.2f;
private float highStiffness = 1f;
// Use this for initialization
void Start () {
rigidbody.centerOfMass = new Vector3(0,-0.9f,0.5f); // 무게중심이 높으면 차가 쉽게 전복된다
}
// Update is called once per frame
void FixedUpdate () {
HandBrake ();
SideSlip ();
Control ();
}
void Update() {
// 앞바퀴 2개를 이동방향으로 향하기
tireTransformFL.Rotate (Vector3.up, colliderFL.steerAngle-prevSteerAngle, Space.World);
tireTransformFR.Rotate (Vector3.up, colliderFR.steerAngle-prevSteerAngle, Space.World);
prevSteerAngle = colliderFR.steerAngle;
WheelSuspension();
EngineSound ();
}
void Control() {
// 최고속도 제한
// WheelCollider.rpm 전진:+, 후진:-
currentSpeed = 2 * 3.14f * colliderRL.radius * colliderRL.rpm * 60 / 1000;
float direction = Input.GetAxis("Vertical"); //전진:0.1~1, 후진:-0.1~-1
//print ("direction:" + direction);
float torque = maxTorque * direction;
if(!bHandBraked && direction>0 && currentSpeed<maxSpeed) {
//print ("전진");
colliderFR.motorTorque = torque;
colliderFL.motorTorque = torque;
}else if(!bHandBraked && direction<0 && Mathf.Abs(currentSpeed)<maxRevSpeed) {
//print ("후진");
colliderFR.motorTorque = torque;
colliderFL.motorTorque = torque;
}else{
colliderFR.motorTorque = 0;
colliderFL.motorTorque = 0;
}
BackLight ();
// 전후진 키를 누르지 않으면 제동이 걸리도록 한다
if (!Input.GetButton ("Vertical")) {
colliderRR.brakeTorque = decSpeed;
colliderRL.brakeTorque = decSpeed;
reverseLight.renderer.material = backReverseIdle;
brakeLight.renderer.material = backBrakeLight;
} else {
colliderRR.brakeTorque = 0;
colliderRL.brakeTorque = 0;
}
// 속도에 따라 방향전환율을 달리 적용하기 위한 계산
float speedFactor = rigidbody.velocity.magnitude / highestSpeed;
/** Mathf.Lerp(from, to, t) : Linear Interpolation(선형보간)
* from:시작값, to:끝값, t:중간값(0.0 ~ 1.0)
* t가 0이면 from을 리턴, t가 1이면 to 를 리턴함, 0.5라면 from, to 의 중간값이 리턴됨
*/
float steerAngle = Mathf.Lerp (lowSpeedSteerAngle, highSpeedStreerAngle, 1/speedFactor);
//print ("steerAngle:" + steerAngle);
steerAngle *= Input.GetAxis("Horizontal");
//좌우 방향전환
colliderFR.steerAngle = steerAngle;
colliderFL.steerAngle = steerAngle;
// 바퀴회전효과
wheelTransformFL.Rotate (-colliderFL.rpm/60*360 * Time.fixedDeltaTime, 0, 0);
wheelTransformFR.Rotate (-colliderFR.rpm/60*360 * Time.fixedDeltaTime, 0, 0);
wheelTransformRL.Rotate (-colliderRL.rpm/60*360 * Time.fixedDeltaTime, 0, 0);
wheelTransformRR.Rotate (-colliderRR.rpm/60*360 * Time.fixedDeltaTime, 0, 0);
}
// 브레이크 등, 후진 등 점멸제어
void BackLight() {
float direction = Input.GetAxis("Vertical"); //전진:0.1~1, 후진:-0.1~-1, 아무키도 안눌리면:0
if (direction==1) { //전진
reverseLight.renderer.material = backReverseIdle;
brakeLight.renderer.material = backBrakeIdle;
} else if (direction==-1) { //후진
reverseLight.renderer.material = backReverseLight;
brakeLight.renderer.material = backBrakeIdle;
}
if (!Input.GetButton ("Vertical") || bHandBraked) {
reverseLight.renderer.material = backReverseIdle;
brakeLight.renderer.material = backBrakeLight;
}
}
private RaycastHit hit;
private Vector3 wheelPos;
void WheelSuspension() {
if(Physics.Raycast(colliderFR.transform.position, -colliderFR.transform.up,
out hit,colliderFR.radius+colliderFR.suspensionDistance)){
wheelPos = hit.point +(colliderFR.radius * colliderFR.transform.up);
//print ("지면 충돌");
}else{
wheelPos = colliderFR.transform.position - (colliderFR.transform.up * colliderFR.suspensionDistance);
//print ("충돌 아님");
}
wheelPos.y +=3f; // 정상적인 모델이라면 이부분이 없어도 됨
tireTransformFR.position = wheelPos;
if(Physics.Raycast(colliderFL.transform.position, -colliderFL.transform.up,
out hit,colliderFL.radius+colliderFL.suspensionDistance)){
wheelPos = hit.point + (colliderFL.radius * colliderFL.transform.up);
}else{
wheelPos = colliderFL.transform.position - (colliderFL.transform.up * colliderFL.suspensionDistance);
}
wheelPos.y +=3f;
tireTransformFL.position = wheelPos;
if(Physics.Raycast(colliderRL.transform.position, -colliderRL.transform.up,
out hit,colliderRL.radius+colliderRL.suspensionDistance)){
wheelPos = hit.point + (colliderRL.radius * colliderRL.transform.up);
}else{
wheelPos = colliderRL.transform.position - (colliderRL.transform.up * colliderRL.suspensionDistance);
}
wheelPos.y +=3f;
tireTransformRL.position = wheelPos;
if(Physics.Raycast(colliderRR.transform.position, -colliderRR.transform.up,
out hit,colliderRR.radius+colliderRR.suspensionDistance)){
wheelPos = hit.point + (colliderRR.radius * colliderRR.transform.up);
}else{
wheelPos = colliderRR.transform.position - (colliderRR.transform.up * colliderRR.suspensionDistance);
}
wheelPos.y +=3f;
tireTransformRR.position = wheelPos;
}
void HandBrake(){
if(Input.GetButton("Jump")) {
bHandBraked = true;
//print ("핸드브레이크 작동");
//colliderFL.motorTorque = 0;
//colliderFR.motorTorque = 0;
//colliderFL.brakeTorque = 100;
//colliderFR.brakeTorque = 100;
colliderRL.brakeTorque = 100;
colliderRR.brakeTorque = 100;
}else{
colliderFL.brakeTorque = 0;
colliderFR.brakeTorque = 0;
colliderRL.brakeTorque = 0;
colliderRR.brakeTorque = 0;
bHandBraked = false;
//print ("핸드브레이크 해제");
}
}
void SideSlip() {
if (!Input.GetButton ("Vertical")) {
WheelFrictionCurve wfc = new WheelFrictionCurve();
wfc.asymptoteSlip = colliderRL.sidewaysFriction.asymptoteSlip;
wfc.asymptoteValue = colliderRL.sidewaysFriction.asymptoteValue;
wfc.extremumSlip = colliderRL.sidewaysFriction.extremumSlip;
wfc.extremumValue = colliderRL.sidewaysFriction.extremumValue;
wfc.stiffness = 0.01f;
colliderRL.sidewaysFriction = wfc;
colliderRR.sidewaysFriction = wfc;
//colliderRL.forwardFriction = wfc;
//colliderRR.forwardFriction = wfc;
} else {
WheelFrictionCurve wfc = new WheelFrictionCurve();
wfc.asymptoteSlip = colliderRL.sidewaysFriction.asymptoteSlip;
wfc.asymptoteValue = colliderRL.sidewaysFriction.asymptoteValue;
wfc.extremumSlip = colliderRL.sidewaysFriction.extremumSlip;
wfc.extremumValue = colliderRL.sidewaysFriction.extremumValue;
wfc.stiffness = 1f;
colliderRL.sidewaysFriction = wfc;
colliderRR.sidewaysFriction = wfc;
colliderRL.forwardFriction = wfc;
colliderRR.forwardFriction = wfc;
}
}
void EngineSound() {
audio.pitch = currentSpeed / maxSpeed + 1; //속도가 0이면 사운드도 없으므로...
}
}