unity2D 船只型物体驱动的实现

Wesley13
• 阅读 735
  • 船只向前行驶的驱动力

假设在水中没有摩擦阻力,船只有惯性,船只可以转弯,按下前进键时船只会在力的作用下使得自身的物理运动方向变化到自身的前方方向,从而向前行进。

unity2D 船只型物体驱动的实现

上图中

V:船当前物理速度

V1,V2:V在两个方向上的分速度

 Vn:船要达到的目标速度

假设

船的最大前进推进力为pushForce,船的最大速率只能是maxSpeed。

具体思想为:将V分解为V1和V2,利用V1,V2和Vn的关系,得出当前的船需要添加的恒力,下面总结出三种方案。

第一种:

制动力和推进力分离

1.先得出在船正方向的最大力Fn,这里可以抽象的认为这个力是一直添加在船的正方向的,就像火箭的推进引擎。

2.制动力,这是另外计算的,根据当前的船的速度,以及Fn,得出,然后和已经添加的Fn相加得出当前需要添加到恒力组件上的力(由于概念问题,这里Fn做了一次不必要的计算)

第二种:

驱动力即是制动力,也就是一次性计算

用最大正方向速度向量减去当前速度向量,得到和船的偏移向量刚好相反但是模相等的抵消向量。为了抵消偏移向量,让pushForce乘以   该抵消向量的单位向量,从而得到需要添加到恒力组件上的力

改良:为了防止抖动,求出需要的力向量后,让当前组件上的力渐变到求出的力。

第三种:

将当前速度方向分解为V1和V2,然后计算两个垂直方向上需要添加的力,最后又这两个力相加得到需要的力向量

可以看出,其实上面三种方案最后的结果都是相同的,只是为了功能上的需求,以及防止物理演算失真的原因,从而得到的三种不同的计算方案

方案2的unity代码如下

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player01Test : MonoBehaviour
{
    public float rotateSpeed;//旋转速度
    ConstantForce2D constantForce2D;//恒力组件
    public float maxSpeed;//最大速度
    Rigidbody2D rigid2D;

    //推力参数
    public float pushForce;//推力大小
    //[HideInInspector]
    //public float currentForce;//当前推力

    // Start is called before the first frame update
    void Start()
    {
        constantForce2D = GetComponent<ConstantForce2D>();
        rigid2D = GetComponent<Rigidbody2D>();
    }

    private void FixedUpdate()
    {
        //旋转
        //为利用扭矩力使得船往某个方向旋转
        if (Input.GetKey(KeyCode.LeftArrow))//按下左方向键,船往顺时针旋转
        {
            //transform.Rotate(new Vector3(0, 0, rotateSpeed * Time.fixedDeltaTime));
            if (rigid2D.angularVelocity >= 200)//如果旋转速度已经达到最大,那么撤销扭矩力
                constantForce2D.torque = 0;
            else                               //否则添加扭矩力
                constantForce2D.torque = 600;
            rigid2D.angularDrag = 0;
        }
        else if (Input.GetKey(KeyCode.RightArrow))//按下右方向键,船往逆时针旋转
        {
            //transform.Rotate(new Vector3(0, 0, -rotateSpeed * Time.fixedDeltaTime));
            if (rigid2D.angularVelocity <= -200)
                constantForce2D.torque = 0;
            else
                constantForce2D.torque = -600;
            rigid2D.angularDrag = 0;
        }
        else
        {
            constantForce2D.torque = 0;
            rigid2D.angularDrag = 50;
        }

        //推进
        if (Input.GetKey(KeyCode.UpArrow))
        {
            Vector3 Vn= transform.up * maxSpeed;//船需要达到的目标速度
            Vector3 V = rigid2D.velocity;//当前的速度(方向不一定是当前的船正方向)
            Vector3 forceDir = (Vn - V).normalized;//获得力方向的单元向量

            //添加力到船的恒力组件上
            constantForce2D.force = forceDir * pushForce;
            /*//这里为改进代码方式0,用来代替上面这段代码,即允许微小误差存在,主要是防止物理演算的失真抖动
            if (forceDir.magnitude>Time.fixedDeltaTime*pushForce)
                constantForce2D.force = forceDir * pushForce;
            else
                constantForce2D.force = Vector3.zero;
                */
        }
        else//如果没有添加任何力,那么让恒力组件为0
        {
            constantForce2D.force = Vector3.zero;
        }
    }
}

组件

unity2D 船只型物体驱动的实现

  • 船只转弯的驱动

为了方便理解这里进行类似例子引申:

以2d为例,一个小球在一个横杆上,小球唯一的移动方式是设置constant force组件的方向力,小球有惯性,没有摩擦力,如何让小球移动到杆上的任意一个点,并且在该点的速度刚好为0?

很显然,在物理上这个问题很难解决,因此解决的方案是如何让小球无限的接近目标点,并且速度为0。

问题的关键:

如何在力的 作用下到达目标点

如何在力的作用下变化到目标速度

(这里就可想象为到达目标点时,速度为0)

方案一:(太复杂,以后考虑)

设置减速半径,当运动到里目标点距离为减速半径的长度时进行减速操作。减速力为,减速力为偏移距离剧偏移半径的百分比乘以最大推力,方向为速度的反方向。偏移半径为,最大速度在最大力的反作用下减速到0,期间运动的距离刚好为减速半径(改进,在变力的情况下速度减到0,运动的距离为偏移半径)

方案二:(太简单太low)

进入减速半径后直接使用rigidbody上的迟滞力,将迟滞力放到最大

方案三:(明确简单,优先选择)

1.如果大于减速距离,那么用力驱使速度朝向目标方向移动,目标速度为最大速度

2.如果小于减速距离,那么用力驱使速度朝向目标方向,目标速度为迟滞速度(迟滞速度比最大速度小得多,便于操作调节运动的位置)

3.如果小于迟滞距离:如果当前速度大于迟滞速度,那么设置目标速度为0,此时如果速度小于迟滞速度,那么设置线性阻尼为最大(改进:如果速度方向与目标方向相反,把目标速度改为朝向目标点的迟滞速度);如果速度小于迟滞速度,那么设置线性阻尼为最大(改进:如果速度方向与目标方向相反,把目标速度改为朝向目标点的迟滞速度)。

从而使得角色最大程度的靠近目标点,并且速度为0.

//=============================================================================================================

//更新版,以unity2D飞行游戏为例

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//飞船的最大推进力,扭矩力,推进转弯速率比来自于引擎
public class Ship : MonoBehaviour
{

    public float maxSpeed;
    public float pushForce;

    public float maxRotateSpeed;
    public float torque;

    public float rotateSpeedPercent;//转弯速率比,即当飞船不推进时最大转弯速率是maxRotateSpeed,如果正在开启引擎推进,那么速率是百分率乘以最大速率
    public float currentRotatePercent;

    public Transform weaponAnchor;//武器锚点,生成的武器成为锚点的子物体
    //public Weapon weapon;
    public Transform engineAnchor;
    //public Engine engine;

    Rigidbody2D rigid2D;
    ConstantForce2D constantForce2D;

    public Transform model;

    // Start is called before the first frame update
    void Start()
    {
        rigid2D = GetComponent<Rigidbody2D>();
        constantForce2D = GetComponent<ConstantForce2D>();
    }

    private void Update()
    {
        //Vector3 rotate = new Vector3(0, transform.rotation.eulerAngles.z, 0);
        int n = (int)(transform.rotation.eulerAngles.z / 90);
        if (n == 0 || n == 2) model.transform.rotation = transform.rotation * Quaternion.Euler(0, transform.rotation.eulerAngles.z % 90*(n==0?-1:1), 0);
        else model.transform.rotation = transform.rotation * Quaternion.Euler(0, (90-transform.rotation.eulerAngles.z % 90) * (n == 1 ? -1 : 1), 0);
        Debug.Log(model.rotation.eulerAngles.z);
    }

    // Update is called once per frame
    void FixedUpdate()
    {
        //引擎推进
        if (Input.GetKey(KeyCode.UpArrow))
        {
            PushRigid();
            //engine.EngineActive();
            currentRotatePercent = 0.5f;// rotateSpeedPercent;//如果正在推进引擎,那么当前转弯速率比为默认转弯速率比
        }
        else
        {
            constantForce2D.force = Vector3.zero;
            //engine.EngineUnActive();
            currentRotatePercent = 1;//如果没有推进引擎,那么当前转弯速率比为1,即用最大转弯速率为目标速率旋转
        }

        //转弯
        if (Input.GetKey(KeyCode.LeftArrow)) { rigid2D.angularDrag = 0; RotateRigid(1);  }
        else if (Input.GetKey(KeyCode.RightArrow)) { rigid2D.angularDrag = 0; RotateRigid(-1);  }
        else { RotateRigid(0); rigid2D.angularDrag = 20; }

        //开火
        //if (Input.GetKey(KeyCode.LeftControl) && weapon != null) weapon.Fire();
    }

    //施加推力
    void PushRigid()
    {
        Vector3 velocityReal = transform.up * maxSpeed;
        Vector3 velocityRigid = rigid2D.velocity;
        Vector3 forceDir = (velocityReal - velocityRigid).normalized;
        if ((velocityReal - velocityRigid).magnitude > Time.fixedDeltaTime * pushForce) {
            constantForce2D.force = forceDir * pushForce;
        }
        else
            constantForce2D.force = Vector3.zero;
    }

    //旋转,用最大速率与百分比相乘获得目标旋转速率
    void RotateRigid(int dir)
    {
        RotateRigid(dir, currentRotatePercent * maxRotateSpeed);
    }

    //旋转,带有目标旋转速率
    void RotateRigid(int dir,float rotateSpeed)//0停止旋转,1正转,-1反转,第二个参数为转弯速率
    {

        float tarAngularVelocity = dir * rotateSpeed;//最大目标速度current永远大于0
        float dirAngular = tarAngularVelocity - rigid2D.angularVelocity;//获得速度差
        float proportion = dirAngular / rotateSpeed;
        proportion = Mathf.Clamp(proportion, -1, 1);
        constantForce2D.torque = proportion * torque;
    }

    //这里是数据使得飞船部件的属性数据计算生效
    /*
    public void InitDatas()
    {
        if (engine)
        {
            pushForce = engine.pushForce;
            torque = engine.torque;
            rotateSpeedPercent = engine.rotatePercent;
        }
    }*/

    private void OnDrawGizmos()
    {
        Gizmos.color = Color.red;
        
    }
}
点赞
收藏
评论区
推荐文章
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Karen110 Karen110
3年前
人工智能数学基础-线性代数1:向量的定义及向量加减法
一、向量1.1、向量定义向量也称为欧几里得向量、几何向量、矢量,指具有大小(magnitude)和方向的量。它可以形象化地表示为带箭头的线段。箭头所指:代表向量的方向;线段长度:代表向量的大小。与向量对应的量叫做数量(物理学中称标量),数量(或标量)只有大小,没有方向。1.在物理学和工程学中,几何向量更常被称为矢量。2.一般印刷用黑体的小写
从0到1构建基于自身业务的前端工具库
在实际项目开发中无论M端、PC端,或多或少都有一个utils文件目录去管理项目中用到的一些常用的工具方法,比如:时间处理、价格处理、解析url参数、加载脚本等。
wnm wnm
3年前
万能码立志不断前进(安全扫码专业委员会)
万能码立志不断前进(安全扫码专业委员会)发展是第一动力,发展是不断曲折前进的运动,在发展的过程当中,即便发生了严重的偏离的方向,但最终还是会朝着正确的方向前进,正如任何一个产品都会进行更新迭代一样,这是常有的事情,只不过有些发生了严重的偏差,认知有误,从而导致整个东西停滞不前,那就是旧事物无法转化为新事物,最终只能被淘汰,而那些往正确方向前进的东西成为了新事物,“码上付”的推出便是如此。
全国/ 优学指针诚招IT线上讲师
线上的IT培训平台,现在寻求大佬们入驻成为讲师。我们希望你在Python、机器学习、大数据、C、Java.Go语言、Linux、Web、PHP、C、R语言、容器、K8S、大前端、Vue等等方向有所造诣,可以结合自身擅长方向,研发编程语言课程(如C从入门到精通)或实战项目课程(如如何使用微服务开发微商城),通过项目案例,帮助学生更好的学习IT知识实现就
Wesley13 Wesley13
3年前
Unity3d Transform.forward和Vector3.forward的区别!
在Unity中有两个forward,一个是Transform.forward一个是Vector3.forward。对于Vector3来说,它只是缩写。没有其它任何含义。Vector3.forward,(0,0,1)的缩写。//在transform.Translate()中使用时,如果不表明坐标系,则为物体的局部坐标,即物体自身的正前方。
Wesley13 Wesley13
3年前
VI必记指令
命令模式下的指令必记:1.光标移动h或向左方向键光标向左移动一个字符j或向下方向键光标向下移动一个字符k或向上方向键光标向上移动一个字符l或向右方向键光标向右移动一个字符Ctrlf屏幕向前翻一页(常用)Ctrlb屏幕向后翻一页(常用)0(是数字0)动到这一行的第一个字符处(常用)$
Wesley13 Wesley13
3年前
unity2D以最小的角度旋转到目标方向(y方向为角色的主方向)
一.使用向量原理转换到目标方向为了让角色的自身y转向目标方向,并且以最小角度旋转,要点是获得当前方向与目标方向的叉值,从而判断应该旋转的方向floatrotateSpeed;//相对目标位置运动voidtrackPosition02(Vector3tarPosition){Vector3targetDirtarPo
Wesley13 Wesley13
3年前
Unity2D 面向目标方向
在2d空间上,假设角色的自身的y轴方向为正方向,如果要让角色随时面向一个目标点。这里假设(0,0)点为目标点第一种:Vector3vVector3.zerotransform.position;                //首先获得目标方向v.z0;         
什么是IMU传感器?
传感器全称惯性测量单元,中文名惯性测量单元。它主要是利用惯性原理来检测一个物体的旋转和加速度。IMU通常由三向加速度计和各个方向的陀螺仪组成,是测量惯性和运动的主要结构。3(加速度计)3(陀螺仪)结构也是目前常见的6轴IMU传感器。我们从需求的角度来分析