假设在水中没有摩擦阻力,船只有惯性,船只可以转弯,按下前进键时船只会在力的作用下使得自身的物理运动方向变化到自身的前方方向,从而向前行进。
上图中
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; } } }
组件