基于unity的飞行模拟设计

  使用unity开发游戏真是非常方便。研究飞行模拟也有一段时日,尝试过物理和数学模拟。从效果上来看,物理模拟较为真实一点。但是操作不好。数学模拟的话,虽然牺牲了飞行效果,操控是非常方便的。

  所谓的数学模拟,就是位移模拟,通过定义起飞速度,加速度等,模拟飞机的飞行过程,包括转向,飞行坠落等。

  来看一下飞机的飞行状态:在地面上,飞机达到起飞速度时,可以拉起飞机,否则,一直在地面上;在空中:当飞机低于起飞速度,下降。大于起飞速度则能保持在空中。飞机不可能倒着飞行的,所以,飞机的速度状态就有:起飞速度,正常速度,最大速度。通过输入改变当前速度,然后通过判断速度所对应的状态处理。

  飞机的转向:飞机的转向,有y轴的转向,控制飞机的左右飞行。x的转向,控制飞机的升降。左右飞行时,飞机自身需要以z轴旋转,来模拟飞机转弯的效果。

  飞机的失速:当飞机在空中的速度低于起飞速度时,飞机下落。当着地时,下落的速度为0.

  整理一下思路:可以通过射线检测的方式获取距离地面的高度,判断飞机是在空中还是地面。通过输入值来调整currentSpeed,通过与飞机的offSpeed,normalSpeed,maxSpeed比较,来判断飞机所处的状态。通过四元数方法,调整飞机的角度,保持飞机的平衡和转向时的偏转效果。

  主要使用到的方法:

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

  旋转:transform.Rotate(vector, Space.World);

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

     高度: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);
    }

  好了,今天的飞行模拟就介绍到这了。预告一下,下一篇文章阿亮将探讨导弹算法的实现(预计目标点位置,而非通过获取目标的当前位置)

时间: 2024-08-28 18:37:33

基于unity的飞行模拟设计的相关文章

基于unity的直升机模拟设计

上一篇文章介绍了使用unity3D进行战斗机飞行模拟的设计http://www.cnblogs.com/jqg-aliang/p/4598515.html,其实只要稍微修改一下,就变成了直升机的飞行模拟.只需要复写一下几个移动的方法就行了. 需要写的功能如下:直升机的前进,后退,左右移动,左右旋转,升高和降落 还是以数学模拟为例,我们需要几个值来控制游戏对象(直升机)的移动效果.具体参数的我就不说了,直接上代码吧. using UnityEngine; using System.Collecti

啥?虚拟现实技术已经应用到自动化仓库? | 基于unity实现的自动化仓库模拟监控系统

3D自动化仓库  演示视频  https://www.bilibili.com/video/av57621560 基于unity与Spring MVC实现 项目介绍 视频中的项目是我的本科毕业设计.从电气设备的计算机仿真模拟进行入手,实现自动化仓库场景电气设备功能模拟. 主要解决了以下三个关键问题: 采用unity3D实现自动化仓库硬件设备模拟 采用Spring MVC实现自动化仓库服务端. 采用WebSocket技术实现3D自动化仓库监控模拟端与硬件模拟端进行数据交互. 1 登陆验证 本系统分

CSDN开源夏令营 基于Compiz的switcher插件设计与实现之前言

很开心能够被选中参加开源夏令营,能够参与到中国的开源事业中来,感谢彭欢老师给了我这样一个机会. 1.项目名称:基于Compiz的switcher插件设计与实现 2.项目类别:软件 3.项目参与人员:彭欢(指导老师)  风语(我) 3.项目背景:compiz现有的switcher插件存在或多或少使用上的不便,旨在设计实现一款更好用的switcher,故提出本提案. 4.项目开发环境:Linux(Ubuntu Kylin 14.04) 5.项目目标: 基于开源窗口管理器compiz实现 可以参照其原

基于MATLAB的IIR滤波器设计与实现

基于MATLAB的IIR滤波器设计与实现 IIR滤波器的设计主要有经典设计法.直接设计法和最大平滑滤波器设计法三种方法. 1.经典设计法是基于模拟滤波器的变换原理,首先根据滤波器的技术指标设计出相应的模拟滤波器,然后再离散化为满足给定技术指标的数字滤波器.对应的工具函数由完全设计函数——butter.cheby1.cheby2.ellip.besself:阶数估计函数——buttord.cheb1ord.cheb2ord.ellipord:低通模拟原型滤波器函数——buttap.cheb1ap.

基于Verilog HDL整数乘法器设计与仿真验证

基于Verilog HDL整数乘法器设计与仿真验证 1.预备知识 整数分为短整数,中整数,长整数,本文只涉及到短整数.短整数:占用一个字节空间,8位,其中最高位为符号位(最高位为1表示为负数,最高位为0表示为正数),取值范围为-127~127. 负数的表示方法为正值的求反又加1.例如: 8’b0000_0100; //表示值:4,正值求反为:8’b1111_1011:再加1表示为:8’b1111_1100,这样便得到了-4的表示方法为:8’b1111_1100. 同理,负值变成正值的方法为:负值

基于SEDA的异步框架设计与实现

基于SEDA的异步框架设计与实现 二.为什么使用SEDA 目前,面对并发环境,主流互联网服务器编程模型有两种:多线程模型以及事件驱动模型.但是这两个模型都不足以解决这个问题.我们来首先看一下这两种编程模型. 1.多线程并发模型 多线程并发模型是目前最普遍的服务器编程模型,该模型的架构如下图所示:        该模型针对每一个请求,会为其创建并分配一个线程.该线程负责这个请求的处理.该模型的优点:执行粒度是整个完整的处理流程.处理逻辑清晰,容易开发.但与此同时缺点也很明显:如果处理过程中某一步骤

CSDN开源夏令营 基于Compiz的switcher插件设计与实现之前期准备 git的简单使用

由于项目的代码需要上传到git上,就需要学习一下git的使用了.我初步接触了一下git,准备用此帖来记录git的学习,此帖会随着我对git了解的深入动态更新. 一.GIT的介绍 1.概述:git是一个分布式版本控制系统, 2.详情请见:http://baike.baidu.com/subview/1531489/12032478.htm 二.GIT的安装 这里就介绍一下linux平台下的安装,我使用的是Ubuntu Kylin 14.04,只需要在终端输入命令:sudo apt-get inst

基于key/value+Hadoop HDFS 设计的存储系统的shell命令接口

对于hadoop HDFS 中的所有命令进行解析(其中操作流程是自己的想法有不同意见欢迎大家指正) 接口名称 功能 操作流程 get 将文件复制到本地文件系统 .如果指定了多个源文件,本地目的端必须是一个目录. (1)按照上述机制,在Config server上的存储引擎中逐层读取K-V,直到获得文件名(或大文件元数据信息): (2)根据文件名(或大文件元数据信息)到相应的Data server中获取相应的文件(大文件需要拼接): (3)将获取的文件写入本地文件系统. put 从本地文件系统中复

CSDN开源夏令营 基于Compiz的switcher插件设计与实现之compiz特效插件介绍及特效实现

compiz自带的特效插件不够多,也不够强大,为了更好的体验compiz的特效,我们可以安装特效插件,在终端输入命令:sudo apt-get install compiz-plugins就可以下载特效插件安装了. 一.安装compiz特效插件 在此过程中可能会出现一些问题,比如说依赖问题,我们需要把源更新一下.对于更新源,我们需要注意以下几点: 1.设置软件更新 注意:这里要选主服务器 2.更新源 在终端输入命令:sudo apt-get update来更新源,一般情况下,这样是可以的,但是,