유니티 (Unity 3D) 헬기 비행 시뮬레이터 만들기 Part 8
Sky Box 와 그림자 설정하기
앞장에서는 지형 위에서 헬기가 비행할 때 메인 카메라가 계속 헬기를 주시하면서 화면 중앙에 헬기가 오도록 해봤습니다.
화면을 벗어나서 헬기가 비행한다면 이용자가 헬기 조종이 불가능하기 때문입니다
여기서는 지형에 추가하여 구름과 하늘을 표현할 수 있는 Sky Box 를 설정해보겠습니다
스카이박스와 그림자는 스크립트가 아닌 유니티 에디터에서 설정해주는 것 만으로 작업이 완성됩니다
Sky Box 설정 순서
1. Project 뷰의 Assets 폴더 위에서 마우스 우측을 누르고 Import Package... > Skyboxes 을 선택하여 유니티가 지원하는 스카이박스를 임포트한다
2. Edit > Render Settings... 를 선택하고 Inspector 뷰에서 Skybox Material 항목에서 원하는 재질을 골라서 선택해주면 된다.
그림자 설정
Hierarchy 뷰에서 Drectional Light 를 선택하고 Inspector 뷰에서 Shadow Type 을 Soft Shadow 등으로 설정해주면 된다
헬기의 그림자와 멀리 하늘이 보이도록 설정한 화면
스카이박스와 그림자와는 상관 없지만 비행특성을 고려하여 수정한 코드
using UnityEngine; using System.Collections; public class Helicopter : MonoBehaviour { public float rotSpeed; public float moveSpeed; public float TOW; // Take Off Weight public float verticalSpeed; public float lateralSpeed; public float _PITCH; //키보드에서 입력용으로 사용 float PITCH; //기체에 적용할 때 사용 float ROLL; public float YAW; public float POWER; float powerDelta; float pitchDelta; float yawDelta; float speedDelta; float turnDelta; float flyAttDelta; float maxSpeedForPitch; float AGL; //Above Ground Level int STATUS; const int STATUS_GROUND = 0; const int STATUS_TAKEOFF = 1; const int STATUS_HOVER = 2; const int STATUS_FLYING = 3; const int STATUS_BRAKE = 4; void Start () { _PITCH = 0.0f; // -45 ~ 45 deg PITCH = 0.0f; ROLL = 0.0f; // -45 ~ 45 deg YAW = 0.0f; // 0 ~ 359 deg POWER = 20f; // 0 ~ 100 rotSpeed = 0.0f; moveSpeed = 0.0f; lateralSpeed = 0.0f; verticalSpeed = 0f; maxSpeedForPitch = 0.0f; TOW = 50f; AGL = 0.0f; powerDelta = 0.0f; pitchDelta = 0.0f; yawDelta = 0.0f; speedDelta = 0.0f; turnDelta = 0.0f; flyAttDelta = 0.0f; STATUS = STATUS_GROUND; } void Update () { inputPower (); inputPitch (); inputRoll (); inputYaw (); printControls (); // for debug checkAGL (); switch (STATUS) { case STATUS_GROUND: status_ground(); break; case STATUS_TAKEOFF: status_takeoff(); break; case STATUS_HOVER: status_hover(); break; case STATUS_FLYING: status_flying(); break; case STATUS_BRAKE: status_break(); break; default: break; } //헬기의 X(Pitch), Y(Yaw), Z(Roll) 죽을 따라 회전 transform.rotation = Quaternion.Euler (new Vector3 (PITCH, YAW, ROLL)); // 상승/하강 transform.Translate (Vector3.up * Time.deltaTime * verticalSpeed); // 전진/후진 transform.Translate (Vector3.forward * Time.deltaTime * moveSpeed); // 좌우이동 transform.Translate (Vector3.right * Time.deltaTime * lateralSpeed); } void OnTriggerEnter(Collider other) { if (other.gameObject.tag == "terrain") { STATUS = STATUS_GROUND; //} //print ("Trigger Enter"); } } void OnTriggerExit(Collider other) { //print ("Trigger Exit"); if(other.gameObject.tag=="terrain") { STATUS = STATUS_TAKEOFF; } } /* void OnCollisionEnter(Collision other) { print ("충돌"); if(other.gameObject.tag=="terrain") { } }*/ void inputPower() { // Collective 조작시 if (Input.GetKey (KeyCode.W)) { //동력증가 powerDelta += 0.01f; } else if (Input.GetKey (KeyCode.S)) { //동력감소 if(POWER>0.0f) { //if(powerDelta -0.01f > 0f) powerDelta += -0.01f; } } else { powerDelta = 0.0f; } if (POWER + powerDelta < 100f && POWER + powerDelta > 0.0f) { POWER += powerDelta; } // 동력이 이륙중량을 초과하면 상승시작, if (POWER > TOW) { verticalSpeed = (POWER / TOW - 1f); } else if (POWER < TOW) { if(STATUS==STATUS_GROUND) verticalSpeed = 0f; else if(transform.position.y>=0f) verticalSpeed = (POWER / TOW - 1f); } } void inputPitch() {// Cyclic 전/후 조작시 //UP, DOWN 키를 동시에 누르면 브레이크를 작동한다 if (Input.GetKey ("up") && Input.GetKey ("down")) { if(STATUS!=STATUS_GROUND) return; _PITCH = 0.0f; STATUS = STATUS_BRAKE; return; } else { // UP 키는 전방속도, DOWN키는 후방속도 조절 if (Input.GetKey ("up")) { pitchDelta += 0.01f; } else if (Input.GetKey ("down")) { pitchDelta += -0.01f; }else{ if(pitchDelta>0.0f){ pitchDelta += -0.05f; } if(pitchDelta<0.05f) { pitchDelta = 0.0f; } } } if (_PITCH + pitchDelta > -60f && _PITCH + pitchDelta < 60f) { _PITCH += pitchDelta; } applyPitch(); } void applyPitch() { var cosVal = Mathf.Cos (_PITCH*Mathf.PI/180f); if(STATUS==STATUS_FLYING) { verticalSpeed *= cosVal*4f; }else { verticalSpeed *= cosVal*3f; } var sinVal = Mathf.Sin (_PITCH*Mathf.PI/180f); maxSpeedForPitch = sinVal*12f; if (sinVal > 0) { if (moveSpeed < maxSpeedForPitch) // 무한정 가속되지 않도록 최대속도 설정 speedDelta += 0.01f; } else if (sinVal < 0.0f) { if (speedDelta > 0.0f) speedDelta += -0.01f; } moveSpeed = sinVal*speedDelta; if(STATUS > STATUS_TAKEOFF){ setAttitudeForPitch(); } } void setAttitudeForPitch() { if(flyAttDelta < 0.2f) { flyAttDelta += 0.001f; } PITCH = _PITCH*flyAttDelta; //print ("_PITCH="+_PITCH+", PITCH="+PITCH); } void inputRoll() { //LEFT, RIGHT 키는 동체의 좌우경사각(ROLL) 조절 if (STATUS == STATUS_GROUND) { return; } if (Input.GetKey ("left")) { turnDelta += 0.007f; if(ROLL< 45f) ROLL += turnDelta; } else if (Input.GetKey ("right")) { turnDelta += 0.007f; if(ROLL> -45f) ROLL += -turnDelta; } else { if(turnDelta>0.1f) turnDelta += -0.01f; else if(turnDelta<0.1f) turnDelta = 0.01f; } applyRoll (); } void applyRoll() { var rad = ROLL * Mathf.PI / 180f; var sinVal = Mathf.Sin (rad); lateralSpeed = -sinVal; //동체가 기울어진 방향으로 선회가 되려면 그 방향으로 기수를 조금씩 회전하도록 한다 //경사량에 따라서 선회량도 증가한다 var rate = Mathf.Abs (ROLL) / 45f; if (ROLL > 0) { YAW += -(rate*1.1f); } else if (ROLL < 0) { YAW += rate*1.1f; } } void inputYaw() { // Rudder // A키는 좌회전, D키는 우회전 if (Input.GetKey (KeyCode.A)) { yawDelta += -0.007f; } else if (Input.GetKey (KeyCode.D)) { yawDelta += 0.007f; } else { if(Mathf.Abs(yawDelta)>0) { if(yawDelta>0f) yawDelta += -0.03f; else if(yawDelta<0f) yawDelta += 0.03f; if(Mathf.Abs(yawDelta)<=0.03f) yawDelta = 0.0f; } } YAW += yawDelta; YAW %= 360f; } void printControls() { print ("PITCH="+Mathf.Round(_PITCH)+" SPEED="+Mathf.Round(moveSpeed)+", ROLL="+Mathf.Round(ROLL)+ ", YAW="+Mathf.Round(YAW)+", POWER="+Mathf.Round(POWER)+", STATUS="+Mathf.Round(STATUS)+ ", AGL="+Mathf.Round(AGL)+", ALT="+Mathf.Round(transform.position.y)); } void status_ground() { ROLL = 0.0f; PITCH = 0.0f; } void status_takeoff() { flyAttDelta = 0.0f; if(AGL>1.0f) { STATUS = STATUS_FLYING; } } void status_hover() { } void status_flying() { /* if (moveSpeed < 0.5f) { STATUS = STATUS_HOVER; }*/ } void status_break(){ moveSpeed *= 0.96f; verticalSpeed = 0.0f; if (Mathf.Abs (moveSpeed) < 0.05f) { moveSpeed = 0f; STATUS = STATUS_GROUND; } } void checkAGL() { float y = Terrain.activeTerrain.SampleHeight (transform.position); AGL = (transform.position.y - y); if (AGL <= 0.2237293f) { //0.2237293f : 지면에서 헬기의 중심부까지의 높이 initHeli (); STATUS = STATUS_GROUND; } } void initHeli(){ //PITCH = 0.0f; //ROLL = 0.0f; //YAW = 0.0f; //POWER = 0.0f; //moveSpeed = 0.0f; //verticalSpeed = 0.0f; //lateralSpeed = 0.0f; Vector3 pos = transform.position; pos.y = 0.2237293f + Terrain.activeTerrain.SampleHeight (transform.position);; transform.position = pos; } }