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 ...
随机推荐
- 虚函数与bind 实现设计模式的练习
相同模式使用虚函数与bind function进行实现对比 #include "stdafx.h" #include <iostream> #include <f ...
- db2用户权限
前言: DB2数据库权限分为实例级权限(SYSADM.SYSCTRL.SYSMAINT.SYSMON)和DB2数据库级权限(DBAMD.LOAD).DB2中用户所拥有的权限主要考虑三个方面:实 ...
- 基于注解的接口限流+统一session认证
代码心得: 一个基本的做法:对于用户身份认证做到拦截器里,针对HandlerMethod进行统一拦截认证,根据方法上的注解标识,判别是否需要身份验证,并将查找出来的User实体存入ThreadLoca ...
- Python开课复习-10/17
pickle是一个用来序列化的模块序列化是什么?指的是将内存中的数据结构转化为一种中间格式 并存储到硬盘上 反序列化?将硬盘上存储的中间格式数据在还原为内存中的数据结构 为什么要序列化?就是为了将数据 ...
- 2019.01.23 ural1519 Formula 1(轮廓线dp)
传送门 轮廓线dpdpdp模板题. 题意简述:给一个放有障碍的网格图,问有多少种方法能使所有非障碍格子都在同一条哈密顿回路上面. 考虑用括号序列的写法来状压这个轮廓线. 用000表示没有插头,111表 ...
- SQL MAP 注入测试
SQL MAP是一款测试系统是否有SQL漏洞的工具 下载地址: http://sqlmap.org/ sqlmap 是一款使用python编写的工具,所以需要安装python,需要安装python 为 ...
- Pappus一阶矩公式
- HTTP请求模型和头信息参考
发送HTTP请求:一个请求由四个部分组成:请求行.请求头标.空行和请求数据 请求行 请求行由三个标记组成:请求方法.请求URI和HTTP版本,它们用空格分隔.例如:GET /index.html HT ...
- LINQ 语法
语言集成查询 (LINQ) 是 Visual Studio 2008 和 .NET Framework 3.5 版中一项突破性的创新,它在对象领域和数据领域之间架起了一座桥梁. 传统上,针对数据的查询 ...
- IP和网段及子网掩码基础知识
IP地址由网络号和主机号两部分组成,网络号的最高位必须是"0",IP地址和子网掩码求"与"算出网络地址,只有网络地址相同才可直接通信,否则需要借助路由. 主机标 ...