基於unity的飛行模擬設計

  使用unity開發遊戲真是很是方便。研究飛行模擬也有一段時日,嘗試過物理和數學模擬。從效果上來看,物理模擬較爲真實一點。可是操做很差。數學模擬的話,雖然犧牲了飛行效果,操控是很是方便的。html

  所謂的數學模擬,就是位移模擬,經過定義起飛速度,加速度等,模擬飛機的飛行過程,包括轉向,飛行墜落等。算法

  來看一下飛機的飛行狀態:在地面上,飛機達到起飛速度時,能夠拉起飛機,不然,一直在地面上;在空中:當飛機低於起飛速度,降低。大於起飛速度則能保持在空中。飛機不可能倒着飛行的,因此,飛機的速度狀態就有:起飛速度,正常速度,最大速度。經過輸入改變當前速度,而後經過判斷速度所對應的狀態處理。dom

  飛機的轉向:飛機的轉向,有y軸的轉向,控制飛機的左右飛行。x的轉向,控制飛機的升降。左右飛行時,飛機自身須要以z軸旋轉,來模擬飛機轉彎的效果。ide

  飛機的失速:當飛機在空中的速度低於起飛速度時,飛機下落。當着地時,下落的速度爲0.spa

  整理一下思路:能夠經過射線檢測的方式獲取距離地面的高度,判斷飛機是在空中仍是地面。經過輸入值來調整currentSpeed,經過與飛機的offSpeed,normalSpeed,maxSpeed比較,來判斷飛機所處的狀態。經過四元數方法,調整飛機的角度,保持飛機的平衡和轉向時的偏轉效果。設計

  主要使用到的方法:code

 

  移動:transform.Translate(vector, Space.World);orm

  旋轉:transform.Rotate(vector, Space.World);協程

  轉角:transform.rotation = Quaternion.RotateTowards(transform.rotation,rotation, speed);htm

     高度:Physics.Raycast(ray, out hit,1<<0);height = hit.distance;

 

  

  飛機的抽象控制方法:

    public abstract void MoveLR(float speed);//左右移動
    public abstract void RoteUD(float speed);//上下旋轉
    public abstract void MoveFB(float speed);//速度控制
    public abstract void RoteLR(float speed);//左右旋轉
  public abstract Balance(Quaternion r, float speed);//轉角
 
 
  public abstract void Operational();//飛行狀態
 

  接下來就是飛行方法的實現:

  

public override void MoveFB(float speed)//速度控制
    {
        IsRun = true;//主動控制打開
        CurrentSpeed += speed*aircaft.Acc*Time.deltaTime;//加/減速
        CurrentSpeed = Mathf.Clamp(CurrentSpeed, 0, aircaft.MaxSpeed);//控制速度在最大值範圍內
    }
    public override void MoveLR(float speed)//水平移動飛機,飛機的側飛
    {
        //左右移動
        if ((IsSing) || IsOnGround) return;//若是在地面或者飛機處於特技狀態
        //IsLRB = false;
        Vector3 vector = body.right;
        vector.y = 0;

        Move(speed * vector * aircaft.MoveLRSpeed * Time.deltaTime * CurrentSpeed/aircaft.MoveFBSpeed);//側飛
Balance(Quaternion.Euler(body.eulerAngles.x, body.eulerAngles.y, -aircaft.AxisLR * speed), aircaft.RoteLRSpeed * Time.deltaTime);/旋起色身,實現側飛的效果 //print("MoveLR" + speed); } public override void Operational()//飛機的狀態控制 { Altigraph();//測量高度 if (CurrentSpeed < aircaft.OffSpeed)//小於起飛速度 { //落下 if (!IsOnGround)//在空中 { Move(-Vector3.up * Time.deltaTime * 10 * (1 - CurrentSpeed / (aircaft.OffSpeed)));//失重下落 downSpeed = Mathf.Lerp(downSpeed, 0.1f, Time.deltaTime); //print("downSpeed" + downSpeed); RoteUD(downSpeed);//機身前傾實現下落效果
}
if (!rigidbody) rigidbody = GetComponent<Rigidbody>(); rigidbody.useGravity = IsOnGround;//若是飛機在地面,啓用重力,不然不使用重力 } else { downSpeed = 0; } Balance();//保持飛機的平衡 if (!IsRun) {//保持飛機以正常速度飛行 if (CurrentSpeed > aircaft.MoveFBSpeed) CurrentSpeed = Mathf.Lerp(CurrentSpeed, aircaft.MoveFBSpeed,Time.deltaTime); else if (CurrentSpeed > aircaft.OffSpeed && !IsOnGround) CurrentSpeed =Random.Range(aircaft.OffSpeed,aircaft.MoveFBSpeed); else if (IsOnGround && CurrentSpeed < aircaft.OffSpeed) { CurrentSpeed = Mathf.Lerp(CurrentSpeed,0,Time.deltaTime); } } Move(body.forward * CurrentSpeed * Time.deltaTime);//調用飛行方法 } public override void RoteLR(float speed)//飛機的轉向 { //左右旋轉 if ((IsSing) || IsOnGround) return; IsLRB = false; Rote(speed * Vector3.up * aircaft.RoteLRSpeed * Time.deltaTime * CurrentSpeed / aircaft.MoveFBSpeed); Balance(Quaternion.Euler(body.eulerAngles.x, body.eulerAngles.y,-aircaft.AxisLR * speed), aircaft.RoteLRSpeed * Time.deltaTime); //print("RoteLR" + speed); } public override void RoteUD(float speed)//飛機的轉向 { //上下旋轉 //速度和角度 if ((IsSing) || IsOnGround && CurrentSpeed < aircaft.MoveFBSpeed / 3.6f) return; if (CurrentSpeed < aircaft.MoveFBSpeed / 3.6f && speed<0) return; IsFBB = false; Balance(Quaternion.Euler(aircaft.AxisFB * speed, body.eulerAngles.y, body.eulerAngles.z), aircaft.RoteFBSpeed * Time.deltaTime * CurrentSpeed / aircaft.MoveFBSpeed); //print("RoteUD" + speed); } public override void Balance()//飛機的平衡方法,當無輸入事件時,飛機自動平衡 { if (IsSing) return; if (IsLRB)//z軸平衡(左右) { Balance(Quaternion.Euler(body.eulerAngles.x, body.eulerAngles.y, 0), aircaft.RoteLRSpeed * Time.deltaTime/1.2f ); } if (IsFBB)//x軸平衡(上下) { Balance(Quaternion.Euler(0, body.eulerAngles.y, body.eulerAngles.z), aircaft.RoteFBSpeed * Time.deltaTime /1.3f); } IsLRB = true;//自動平衡打開 IsFBB = true;//自動平衡打開 }

  都是很是簡單的代碼,關鍵是如何使用,以及邏輯的處理。

  爲了增長飛機飛行的效果,我還設計了飛機的特技飛行:90度轉角的左右飛行,以及180度翻轉飛行。固然也是使用了基礎的公式完成的。左右90度特技飛行:飛機側身90度,並大角度的轉角轉向飛機的左或右方。獲取相對於飛機在世界座標的左/右方向V,而後經過判斷當前方向與V的角度,來斷定是否完成特技飛行。180度轉角飛行:飛機繞自身x軸180度旋轉,而後恢復平衡。

  由於特技飛行的完成須要一段時間,而這段時間是玩家不須要控制,系統完成後進入正常狀態的,因此用到須要協程。

  

IEnumerator SLR(float speed) {
        //90度轉角飛行
        speed = (speed > 0 ? 1 : -1);
        Vector3 aim = body.right * (speed);
        aim.y = 0;
        while(Vector3.Dot(aim.normalized,body.forward.normalized)<0.99f){
            Rote(speed * Vector3.up * aircaft.RoteLRSpeed * Time.deltaTime);
            
            Balance(Quaternion.Euler(body.eulerAngles.x, body.eulerAngles.y, -85 * (speed )), aircaft.RoteLRSpeed * Time.deltaTime*3.8f);
            Balance(Quaternion.Euler(0, body.eulerAngles.y, body.eulerAngles.z), aircaft.RoteFBSpeed * Time.deltaTime *1.8f);
            yield return new WaitForFixedUpdate();
        }
        while ((body.eulerAngles.z > 15) && (body.eulerAngles.z < 180) || (body.eulerAngles.z < 345) && (body.eulerAngles.z > 270))
        {
            Balance(Quaternion.Euler(0, body.eulerAngles.y, body.eulerAngles.z), aircaft.RoteFBSpeed * Time.deltaTime);
            Balance(Quaternion.Euler(body.eulerAngles.x, body.eulerAngles.y, 0), aircaft.RoteLRSpeed * Time.deltaTime * 3);
            yield return new WaitForFixedUpdate();
        }
        IsSing = false;
    }
    public override void StuntUD(float axis)
    {
        if ((IsSing) || IsOnGround && CurrentSpeed < aircaft.MoveFBSpeed / 3.6f) return;

        if (!IsSing)
        {
            IsSing = true;
            StartCoroutine(SUD(axis));

        }
    }
    IEnumerator SUD(float speed)
    {
        //180度翻轉
        speed = (speed > 0 ? 1 : -1);
        Vector3 aim = -body.forward ;
        aim.y = 0;
        while (Vector3.Dot(aim.normalized, body.forward.normalized) < 0.8f)//飛機翻轉
        {
            Vector3 v = body.right;
            v.y= 0;
            Rote(body.right * Time.deltaTime * -90 * speed);
            Move(-Vector3.up * speed * Time.deltaTime * 10 * (CurrentSpeed / (aircaft.OffSpeed)));
            //body.Rotate(Vector3.right * Time.deltaTime * -90,Space.Self);
            //Balance(Quaternion.Euler(body.eulerAngles.x, body.eulerAngles.y, 0), aircaft.RoteLRSpeed * Time.deltaTime*5);
            yield return new WaitForFixedUpdate();
        }
        while ((body.eulerAngles.z > 15) && (body.eulerAngles.z < 180) || (body.eulerAngles.z < 345) && (body.eulerAngles.z >270))//翻轉後飛機完成平衡
        {
            Balance(Quaternion.Euler(0, body.eulerAngles.y, body.eulerAngles.z), aircaft.RoteFBSpeed * Time.deltaTime );
            Balance(Quaternion.Euler(body.eulerAngles.x, body.eulerAngles.y, 0), aircaft.RoteLRSpeed * Time.deltaTime*3);
            yield return new WaitForFixedUpdate();
        }
        IsSing = false;
    }

 

  

 

  以上是飛機的飛行模擬思路以及基本算法實現。玩家操做的代碼就是輸入檢測之類的,而後再調用一下飛行接口就好了,我就不提供了。

  接下來的問題就是AI飛行了,由電腦邏輯控制的AI飛行。如上圖,是由計算機控制的飛行。這個AI就一個核心方法,就是飛到目標點。要實現這個功能也很簡單,就是計算一下目標點的距離,角度等。而後根據距離以及當前狀態控制加減速度,經過角度控制轉向。主要使用到Vector.Dot(v1,v2);,而後就是各類狀況下的判斷和飛行方法的調用。例如當目標在飛機的後方時,能夠調用180度翻轉,來鎖定目標。

  好,咱們簡單的分析一下如何去寫這個AI,獲取目標Point,計算距離差,高度差,角度差。經過高度差,控制飛機的升降,距離差控制速度,角度差控制轉向。

  嗯,下面就是實現的代碼。

  

public void MoveToPoint(Vector3 point, float stopDistance) {
        
        Vector3 hPoint = point;
        hPoint.y = flight.body.position.y;
        hDistence = Vector3.Distance(hPoint, flight.body.position);//水平距離
        distence = Vector3.Distance(point, flight.body.position);//距離
        dHeight = point.y - flight.body.position.y;//高度差
        vector = (point - flight.body.position).normalized;
        dUDAgle = Vector3.Dot(vector, flight.body.TransformDirection(Vector3.up).normalized);
        //獲取目標點與當前位置的距離,高度差,判斷目標相對於當前位置的前/後,左/右
        dFBAgle = Vector3.Dot(vector, (flight.body.TransformDirection(Vector3.forward)).normalized);//判斷目標點相對於當前位置的先後
        //print(forward);
        dLRAgle = Vector3.Dot(vector, (flight.body.TransformDirection(Vector3.right)).normalized);//判斷目標點相對於當前位置的左右
        
        
        //獲取這個信息後,接下來就是移動到目標點
        //先判斷什麼?正前方到目標點的角度,若是這個角度在必定範圍內,則能夠進行移動
        //
        float axisFB = 0;
        if (dFBAgle > objectAI.Precision)
        {
            //方向偏移在正常範圍內
            //根據距離判斷是否加/減速度
            //

            if (distence > objectAI.RunDistance * stopDistance || (flight.CurrentSpeed < flight.aircaft.OffSpeed && !IsLand))
            {
                axisFB = Random.Range(0f, 1f);
            }
            else if (distence > objectAI.FreeDistance * stopDistance && !IsLand)
            {
                axisFB = Random.Range(-0.5f, 0.5f);
            }
            else if (distence < objectAI.SlowDistance * stopDistance && IsLand)
            {
                axisFB = Random.Range(-1f, 0f);
            }
            
        }
        else { 
        //方向偏移不在正常範圍
            //判斷距離是否達到中止距離
            //判斷高度是否在偏差以內
            //調整左右和先後,來
            if (distence < stopDistance)
            {
                //到達目標點附近,可是角度誤差過大,判斷目標的左右,進行旋轉
                axisFB = Random.Range(0.5f, 1f);
                flight.RoteLR(dLRAgle);
            }
            else {
                
                //if (Mathf.Abs(dLRAgle) < 0.05f) dLRAgle = 0;
                if (dFBAgle < -0.75f)
                {
                    flight.StuntUD(flight.Height>100?-1:1);
                }
                else if (dFBAgle < -0.25f) {
                    flight.StuntLR(dLRAgle);
                }
                
                flight.RoteLR(dLRAgle);
                if (!IsLand) {
                    if (Mathf.Abs(dHeight) > 5) {

                        flight.RoteUD(-dUDAgle);
                    }
                    if(flight.IsOnGround){
                        flight.RoteUD(-1);
                    }
                }
                    
                if(flight.CurrentSpeed<flight.aircaft.MoveFBSpeed)
                    axisFB = Random.Range(0.5f, 1f);
            }
        }
        //flight.RoteLR(dLRAgle);
        if (Mathf.Abs(axisFB) > 0.01f)
            flight.MoveFB(axisFB);
    }

  好了,今天的飛行模擬就介紹到這了。預告一下,下一篇文章阿亮將探討導彈算法的實現(預計目標點位置,而非經過獲取目標的當前位置)

  導彈算法已經完成,博客地址:http://www.cnblogs.com/jqg-aliang/p/4768101.html謝謝觀看!

本文的連接:http://www.cnblogs.com/jqg-aliang/p/4598515.html。轉載請申明出處,謝謝!

相關文章
相關標籤/搜索