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 >= )//如果旋转速度已经达到最大,那么撤销扭矩力
constantForce2D.torque = ;
else //否则添加扭矩力
constantForce2D.torque = ;
rigid2D.angularDrag = ;
}
else if (Input.GetKey(KeyCode.RightArrow))//按下右方向键,船往逆时针旋转
{
//transform.Rotate(new Vector3(0, 0, -rotateSpeed * Time.fixedDeltaTime));
if (rigid2D.angularVelocity <= -)
constantForce2D.torque = ;
else
constantForce2D.torque = -;
rigid2D.angularDrag = ;
}
else
{
constantForce2D.torque = ;
rigid2D.angularDrag = ;
} //推进
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;
}
}
}
组件

- 船只转弯的驱动
为了方便理解这里进行类似例子引申:
以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 / );
if (n == || n == ) model.transform.rotation = transform.rotation * Quaternion.Euler(, transform.rotation.eulerAngles.z % *(n==?-:), );
else model.transform.rotation = transform.rotation * Quaternion.Euler(, (-transform.rotation.eulerAngles.z % ) * (n == ? - : ), );
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,即用最大转弯速率为目标速率旋转
} //转弯
if (Input.GetKey(KeyCode.LeftArrow)) { rigid2D.angularDrag = ; RotateRigid(); }
else if (Input.GetKey(KeyCode.RightArrow)) { rigid2D.angularDrag = ; RotateRigid(-); }
else { RotateRigid(); rigid2D.angularDrag = ; } //开火
//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, -, );
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; }
}
unity2D 船只型物体驱动的实现的更多相关文章
- 小梅哥FPGA数字逻辑设计教程——基于线性序列机的TLC5620型DAC驱动设计
基于线性序列机的TLC5620型DAC驱动设计 目录 TLC5620型DAC芯片概述: 2 TLC5620型DAC芯片引脚说明: 2 TLC5620型DAC芯片详细介绍: 3 TLC ...
- Linux内核驱动学习(三)字符型设备驱动之初体验
Linux字符型设备驱动之初体验 文章目录 Linux字符型设备驱动之初体验 前言 框架 字符型设备 程序实现 cdev kobj owner file_operations dev_t 设备注册过程 ...
- Android中Input型输入设备驱动原理分析(一)
转自:http://blog.csdn.net/eilianlau/article/details/6969361 话说Android中Event输入设备驱动原理分析还不如说Linux输入子系统呢,反 ...
- Android中Input型输入设备驱动原理分析<一>
话说Android中Event输入设备驱动原理分析还不如说Linux输入子系统呢,反正这个是没变的,在android的底层开发中对于Linux的基本驱动程序设计还是没变的,当然Android底层机制也 ...
- 嵌入式Linux学习笔记(三) 字符型设备驱动--LED的驱动开发
在成功构建了一个能够运行在开发板平台的系统后,下一步就要正式开始应用的开发(这里前提是有一定的C语言基础,对ARM体系的软/硬件,这部分有疑问可能要参考其它教程),根据需求仔细分解任务,可以发现包含的 ...
- Linux设备驱动开发基础--阻塞型设备驱动
1. 当一个设备无法立刻满足用户的读写请求时(例如调用read时,设备没有数据提供),驱动程序应当(缺省的)阻塞进程,使它进入等待(睡眠)状态,知道请求可以得到满足. 2. Linux内核等待队列:在 ...
- sdk(输入驱动物体 驱动属性 被驱动物体 被驱动属性 驱动数值 一键搞定驱动),当你的目标体很多展开会卡的时候使用这个
import maya.cmds as mc def sdksomething( sdk_obj="", sdkAttr=" ...
- Part10-字符型设备驱动模型-part10.1-使用字符型设备
‘ ’
- Linux高级字符设备驱动
转载:http://www.linuxidc.com/Linux/2012-05/60469p4.htm 1.什么是Poll方法,功能是什么? 2.Select系统调用(功能) Select ...
随机推荐
- Tomcat优化方案
摘自网络: 调优方案分类: 1,外部环境调优 2,自身调优 --------------------------------------------------- 外部环境调优: 1, JAVA虚拟机 ...
- 674. Longest Continuous Increasing Subsequence
static int wing=[]() { std::ios::sync_with_stdio(false); cin.tie(NULL); ; }(); class Solution { publ ...
- RF 和 GBDT联系和区别
1.RF 原理 用随机的方式建立一个森林,森林里面有很多的决策树,随机森林的每一棵决策树之间是没有关联的.在得到森林之后,当有一个新的输入样本进入的时候,就让森林中的每一棵决策树分别进行一下判断,看看 ...
- 2018.11.09 bzoj1706: relays 奶牛接力跑(倍增+floyd)
传送门 倍增+floyd板子题. 先列出状态fi,j,kf_{i,j,k}fi,j,k表示经过iii条边从jjj到kkk的最短路. 然后发现可以用fi−1,j,kf_{i-1,j,k}fi−1,j, ...
- ES6展开运算符的3个用法
展开运算符的用法1:传参 // 展开运算符的用法1 : 传参 function test(a,b) { return a + b ; } var arr = [1,2]; console.log(te ...
- 【MVC】bootstrap-paginator 分页
[MVC]bootstrap-paginator 分页http://www.cnblogs.com/stoneniqiu/p/4000041.htmlhttp://www.cnblogs.com/Le ...
- acitvemq的jvm监控
在conf/activemq.xml启用jmx监控 即在broker后面添加useJmx="true"配置参数 <managementContext> <mana ...
- BZOJ 4129 Haruna’s Breakfast (分块 + 带修莫队)
4129: Haruna’s Breakfast Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 835 Solved: 409[Submit][St ...
- Path类对路径字符串的操作
在写程序时,不时会用到处理文件路径的问题,例如:取得扩展名.从路径中取出文件名.路径合并.取出或者去年扩展名等.这些功能都可以通过System.IO.Path类提供的方法来实现.这些相关功能用过多次了 ...
- WinSockets编程(六)select模式
select模式的思想 创建FD_SET fd_all,并初始化FD_ZERO(&fd_all); Step1 初始时: Step2 加入一个套接字之后,比如FD_SET(sServer ...