유니티에서 자동차가 다른 오브젝트에 충돌할 때 충돌음과 스파크가 출력되는 예
앞선 내용 참고 :
자동차가 다른 물체에 충돌할 때 소음과 스파크가 생기는 현상을 표현하려면 사운드 파일과 스파크 효과가 준비되어야 한다
사운드 파일은 인터넷에서 무료로 배포하는 것을 사용하면 되고 스파크 효과는 유니티가 제공하는 패키지를 임포트하고 사용하면 된다.
[사용된 사운드 파일, 충돌음, 스키드음, 엔진음]
[스파크 패키지를 유니티 프로젝트 뷰로 임포트하는 절차]
- Assets > Import Package > Particles > Misc > Sparks 를 선택하여 프로젝트 뷰로 임포트한다
- 위에서 임포트한 Sparks는 이미 Prefab 으로 만들어져 있기 때문에 스크립트에서 GameObject 변수를 선언하고 드래그하여 할당할 수 있다
- 스파크의 형태를 편집하려면 Sparks 프리팹을 프로젝트 뷰에서 드래그하여 Scene 뷰에 올려놓고 Inspector 뷰에서 다양한 속성을 변경해 가면서 편집을 한 후에 프로젝트 뷰에 있는 Sparks 프리팹을 드래그하여 Inspector 뷰의 스크립트 콤포넌트에 있는 변수에 할당한다
- 스파크를 선택하고 Inspector 뷰에서 Autodestruct 항목에 체크하면 스파크가 발생한 후에 자동으로 인스턴스가 소멸되므로 스크립트에 직접 삭제할 필요가 없다
- 스크립트에서 Instantiate() 를 이용하여 스파크 원본 오브젝트를 이용하여 인스턴스를 생성하면 화면에 스파크가 출력된다
[충돌음을 프리팹으로 등록하고 사용하는 절차]
- 빈 오브젝트(EmptyObject)를 생성한다
- 빈 오브젝트에 Audio Source 콤포넌트를 추가한다
- Audio Source 콤포넌트의 Audio Clip 항목에 사운드 파일을 드래그하여 할당한다
- Audio Source 콤포넌트의 Play On Awake 항목을 선택한다
- 프로젝트 뷰에 Prefab을 생성한다
- 프리팹에 위에서 만든 사운드 오브젝트를 드래그하여 할당한다
- Hierarchy 뷰에서 사운드 오브젝트를 삭제한다
- 스크립트에 GameObject 변수를 생성하고 Inspector 뷰에서 해당 변수에 프리팹을 드래그하여 할당한다
- 스크립트에서 Instantiate()를 사용하여 원본 프리팹 오브젝트의 인스턴스를 생성하면 화면에 사운드가 출력된다
[자동차가 충돌시 충돌음과 스파크를 일으키는 코드]
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 = 30;
private float prevSteerAngle;
private bool bHandBraked = false;
private float lowStiffness = 0.2f;
private float highStiffness = 1f;
public Texture2D speedometerDial;
public Texture2D speedometerPointer;
public GameObject sparkPrefab;
public GameObject collisionSndPrefab;
// 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;
}
//speedometerDial(320X170), speedometerPointer(320X40)
void OnGUI() {
GUI.DrawTexture (new Rect(Screen.width/2-160,Screen.height-170,320,170),speedometerDial);
float speedFactor = Mathf.Abs (currentSpeed / maxSpeed);
float rotationAngle = Mathf.Lerp (0, 180, speedFactor);
GUIUtility.RotateAroundPivot(rotationAngle, new Vector2(Screen.width/2,Screen.height-20));
GUI.DrawTexture (new Rect(Screen.width/2-160,Screen.height-40,320,40),speedometerPointer);
}
void OnCollisionEnter(Collision other) {
if (other.gameObject.name.IndexOf ("Terrain") != -1 ||
other.gameObject.name.IndexOf ("skidPrefab") != -1)
return;
if(other.transform != transform && other.contacts.Length!=0){
for(int i=0;i<other.contacts.Length;i++){
Instantiate(sparkPrefab, other.contacts[i].point,Quaternion.identity);
GameObject collisionSndInst = (GameObject)Instantiate(collisionSndPrefab, other.contacts[i].point,Quaternion.identity);
collisionSndInst.AddComponent<GameObjectDestroy>();
}
}
}
}