본문 바로가기

카테고리 없음

Unity 3D Helicopter Turning

유니티 (Unity 3D) 헬기 비행 시뮬레이터 만들기 Part 05


헬기 비행 중 좌우 경사각에 따라서 헬기가 선회하는 기능의 구현




비행 중에 헬기가 좌우측으로 경사지게 되면 경사진 방향으로 서서히 선회가 이루어집니다


앞장에서는 키보드를 이용하여 헬기의 기본 비행이 가능하도록 작성했습니다

이번에는 좀더 사실감 있는 선회동작을 구현해보겠습니다.

비행기나 헬기는 비행방향을 전환할 때 먼저 종축을 기준으로 좌우로 경사(ROLL)가 적용되어야 부드럽게 선회가 됩니다. 경사각이 클수록 급히 선회가 가능하므로 선회량은 경사각에 비례한다고 생각하면 됩니다.

그러나 과도한 경사각은 양력을 잃게 되는 원인이 되고 기체에 무리한 스트레스를 주게 되므로 일정 경사각을 초과하면 위험하게 됩니다.

기초 상식은 이정도로 해두고 경사각에따라서 경사진 방향으로 선회량이 비례하도록 작성해보겠습니다

앞장의 Helicopter.cs 파일의 내용을 많이 수정하거나 추가했습니다



다른 파일과 화면은 앞장에서 작성한 내용과 동일하고 Helicopter.cs 내용만 변경했습니다

Helicopter.cs

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;
	public float ROLL;
	public float YAW;
	public float POWER;
	
	float powerDelta;
	float pitchDelta;
	float yawDelta;
	float speedDelta;
	
	int STATUS;
	const int STATUS_GROUND = 0;
	const int STATUS_HOVER = 1;
	const int STATUS_FLYING = 2;
	const int STATUS_BRAKE = 3;
	
	void Start () {
		
		PITCH = 0.0f;   // -45 ~ 45 deg
		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;
		
		TOW = 50f;
		
		powerDelta = 0.0f;
		pitchDelta = 0.0f;
		yawDelta = 0.0f;
		speedDelta = 0;
		
		STATUS = STATUS_GROUND;
	}
	
	void Update () {
		
		inputPower ();
		inputPitch ();
		inputRoll ();
		inputYaw ();
		
		printControls (); // for debug
		
		switch (STATUS) {
		case STATUS_GROUND:
			status_ground();
			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 == "ground")
		{
			STATUS = STATUS_GROUND;
			verticalSpeed = 0f;
			//print ("On Ground");
		}
	}
	
	void OnTriggerExit(Collider other)
	{
		if(other.gameObject.tag=="ground")
		{
			STATUS = STATUS_HOVER;
			//print ("HOVER");
		}
	}
	
	void inputPower() { // Collective 조작시
		if (Input.GetKey (KeyCode.W)) {         //동력증가
			powerDelta += 0.01f;
		} else if (Input.GetKey (KeyCode.S)) {  //동력감소
			if(POWER>0.0f) {
				powerDelta += -0.01f;
			}
		} else {
			powerDelta = 0.0f;
		}
		if (POWER + powerDelta < 100f) {
			POWER += powerDelta;
		} else {
			POWER = 100f;
		}
		// 동력이 이륙중량을 초과하면 상승시작, 
		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")) {
			PITCH = 0.0f;
			STATUS = STATUS_BRAKE;
		} else {
			// UP 키는 전방속도, DOWN키는 후방속도 조절
			if (Input.GetKey ("up")) {
				pitchDelta += 0.0025f;
				//PITCH += 0.1f;
			} else if (Input.GetKey ("down")) {
				pitchDelta += -0.0025f;
				//PITCH += -0.1f;
			}else{
				pitchDelta = 0.0f;
			}
		}
		if (PITCH + pitchDelta > -45f && PITCH + pitchDelta < 45f) {
			PITCH += pitchDelta;
		}
		applyPitch();
	}
	
	
	void applyPitch() { 
		
		var cosVal = Mathf.Cos (PITCH*Mathf.PI/180f);
		verticalSpeed *= cosVal;
		//print ("VS=" + verticalSpeed);
		
		var sinVal = Mathf.Sin (PITCH*Mathf.PI/180f);
		//print ("PITCH=" + PITCH);
		if (sinVal > 0) {
			if (speedDelta < 10.0f)
				speedDelta += 0.01f;
		} else if (sinVal < 0f) {
			if (speedDelta > 0.0f)
				speedDelta += -0.01f;
		}
		moveSpeed = sinVal*speedDelta;
	}
	
	void inputRoll() {
		//LEFT, RIGHT 키는 동체의 좌우경사각(ROLL) 조절
		if (Input.GetKey ("left")) {
			if(ROLL< 45f) ROLL += 0.1f;
		} else if (Input.GetKey ("right")) {
			if(ROLL> -45f) ROLL += -0.1f;
		} else {
			//if(ROLL>0.0f) ROLL += -0.1f;
			//else if(ROLL<0.0f) ROLL += 0.1f;
		}
		applyRoll ();
	}
	
	void applyRoll() {
		var rad = ROLL * Mathf.PI / 180f;
		//print ("ROLL=" + angle);
		var sinVal = Mathf.Sin (rad);
		lateralSpeed = -sinVal;
		//동체가 기울어진 방향으로 선회가 되려면 그 방향으로 기수를 조금씩 회전하도록 한다
		//경사량에 따라서 선회량도 증가한다
		var rate = Mathf.Abs (ROLL) / 45f;
		if (ROLL > 0) {
			YAW += -(rate);
		} else if (ROLL < 0) {
			YAW += rate;
		}
	}
	
	void inputYaw() { // Rudder
		// A키는 좌회전, D키는 우회전
		if (Input.GetKey (KeyCode.A)) {
			yawDelta += -0.001f;
		} else if (Input.GetKey (KeyCode.D)) {
			yawDelta += 0.001f;
		} else {
			if(Mathf.Abs(yawDelta)>0) {
				if(yawDelta>0f) yawDelta += -0.005f;
				else if(yawDelta<0f) yawDelta += 0.005f;
				if(Mathf.Abs(yawDelta)<=0.005f) yawDelta = 0.0f;
			}
		}
		YAW += yawDelta;
		//print ("YAW=" + YAW);
	}
	
	
	void printControls() 
	{
		print ("PITCH="+PITCH+", ROLL="+ROLL+", YAW="+YAW+", POWER="+POWER);
	}
	
	void status_ground() {
		ROLL = 0.0f;
		//PITCH = 0.0f;
	}
	
	void status_hover() {
		if(moveSpeed>0.5f) {
			STATUS = STATUS_FLYING;
			//print ("FLYING");
		}
	}
	
	void status_flying() {
		if (moveSpeed < 0.5f) {
			STATUS = STATUS_HOVER;
			//print ("HOVER");
		}
	}
	
	void status_break(){
		if (moveSpeed > 0)
			moveSpeed += -0.01f;
		else if (moveSpeed < 0)
			moveSpeed += 0.01f;
		if (Mathf.Abs (moveSpeed) < 0.05f)
			moveSpeed = 0f;
	}
}