unity随笔(2)——物理系统

  1. 物理引擎基本概念

1.1刚体Rigidbody

挂载了Rigidbody组件的物体会受物体引擎的控制

若要让角色完全受脚本直接控制,但同时又不让角色被触发器检测到,可以使用Kinematic(动力学)

1.2休眠

当物体运动已经慢于一定的阈值,并保持一定时间,物体就会进入休眠状态,物理引擎也不会反复计算物体运动,多数情况,刚体的休眠和唤醒都是自动进行的。

个别情况无法自动唤醒:

一个稳定放在地面且带有刚体组件的物体,在地面被移除后,物体任然悬挂在空中,遇到这种情况,可以在脚本中使用WakeUp()方法将其唤醒

1.3碰撞体

一个复杂的模型可以通过建立多个子物体构成,达到于实际物体更接近,使用添加子物体碰撞体或组合碰撞体,可以更方便调节旋转和偏移。要注意的是,只在父物体上添加刚体,子物体上不要添加刚体组件。

基本碰撞体不支持非等比例缩放。

1.4物理材质

通过物理材质(Physics Materials)设置物体表面的摩擦系数和弹性,使得碰撞体交互时更加真实。

1.5触发器

碰撞体组件勾选Is Trigger,就可以变成触发器。

触发事件需要两个物体至少有一个带有刚体(可以是动力学刚体)

1.6层

层的概念对物理系统有很多意义。

如:玩家发射的子弹只对敌人有影响,而不会碰撞到友方,此时就可以指定两层之间是否发生碰撞,形成一个Layer Collision Maxtrix(层碰撞矩阵)。

1.7物理关节

关节总是限制一类运动的自由度,允许另外一类运动的自由度

2.物理系统脚本编程

2.1获取刚体

Rigidbody rigid;
void Start()
{
    rigid=GetComponent<Rigidbody>();//rigid后续经常使用
}

2.2施加作用力

void Update()
{
    if(Input.GetButtonDown("Jump")//按下Jump键
    {
        rigid.AddForce(new Vector3(0,100,0);//对刚体施加一个向上的力,大小为100
    }
}

2.3修改速度

Vector3 vel = rigid.velocity;//获取当前物体速度
rigid.velocity = vel + new Vector3(0,0,1);//将当前速度沿z轴增加1m/s

施加力会让物体产生加速度,而修改速度会直接跳过加速度

如:实现二段跳

若施加力来实现二段跳,物体下落有一定速度,当再次施加力,让物体向上运动时,需要抵消下落得速度,就得保证向上的力足够大,否则之后减缓下落。

若修改速度来实现二段跳,在按下jump键时,将物体在y轴的速度设置为0,然后再施加向上的力。

void Update()
{
    if(Input.GetButtonDown("Jump"))
    {
        rigid.velocity = new Vector3(rigid.velocity.x,0,rigid.velocity.z);
        rigid.AddForce(new Vector3(0,100,0);
    }
}

这样处理,无论物体是出于下降还是上升阶段,都会有比较好的多次跳跃效果。

2.4射线的使用

射线(Ray)包含origin(起点)和direction(方向),都是Vector3类型,前者一个是坐标,后者是一个表示方向的向量。

发射射线:

Physics.Raycast()和Physics.RaycastAll()

重载:

bool Raycast(Vector3 origin, Vector3 direction);
bool Raycast(Vector3 origin, Vector3 direction, float maxDistance);//maxDistance最远距离
bool Raycast(Vector3 origin, Vector3 direction, float maxDistance, int layerMask);//layerMask所在图层

另一种形式,先将射线数据对象先单独创建出来,没实际区别

bool Raycast(Ray ray, out RaycastHit hitInfo);
bool Raycast(Vector3 origin, Vector3 direction,out RaycastHit hitInfo);
bool Raycast(Ray ray, out RaycastHit hitInfo,float maxDistance);
bool Raycast(Ray ray, out RaycastHit hitInfo,float maxDistance,int layerMask);

Ray有多种创建方法:

Ray ray=new Ray(Vector3.zero,Vector3.up)//创建从原点向上的射线

Vector2 mousePos=Input.mousePosition;//获取当前鼠标指针在屏幕上的位置
Ray ray2=Camera.main.ScreenPointToRay(mousePos);//起点是相机位置,方向指向鼠标指针所在的点

Physics.Raycast(ray,1000,LayerMask.GetMask("Default"));//发射ray,距离1000,层为“默认”

RaycastHit的参数hitInfo保存着详细的碰撞信息

综合1用法演示如下:

void TestRay()
{
    RaycastHit hitInfo;//保存碰撞信息
    if(Physics.Raycast(transform.position,Vector3.forward,out hitInfo)
    {//发射射线
        Vector3 point = hitInfo.position;//获取碰撞点的坐标
        Collider coll = hitInfo.collider;//获取对方的碰撞体
        Transform trans = hitInfo.transform;//获取碰撞的Tansform组件
        string name = coll.gameObject.name;//获取对方物体名称
        Vector3 normal = hitInfo.normal;//获取碰撞点法线向量
    }
}

其他形状的射线重载

//  球形射线:
bool SphereCast(Ray ray, float radius);
bool SphereCast(Ray ray, float radius, out RaycastHit hitInfo);

//  盒子射线:
bool BoxCast(Vector3 center, Vector3 halfExtents, Vector3 direction);
bool BoxCast(Vector3 center, Vector3 halfExtents, Vector3 direction, out 
RaycastHit hitInfo, Quaternion orientation);

//  胶囊体射线:
bool CapsuleCast(Vector3 point1, Vector3 point2, float radius, Vector3 direction);
bool CapsuleCast(Vector3 point1, Vector3 point2, float radius, Vector3 direction, 
out RaycastHit hitInfo, float maxDistance);

使用Physics.RaycastAll()函数可以获取到射线沿途碰到的所有碰撞信息,该函数的返回值是RaycastHit数组。

有时需要检测一个空间范围,例如炸弹爆炸时,范围10米之内的物体都会受到波及,那么这里需要的就不是一条射线,而是一个半径为10米的球形区域。物理系统也提供了这类函数,它们均以Physics.Overlap开头,列举如下。RaycastHit[] RaycastAll(Ray ray, float maxDistance);
RaycastHit[] RaycastAll(Vector3 origin, Vector3 direction, float maxDistance);
RaycastHit[] RaycastAll(Ray ray, float maxDistance, int layerMask);
RaycastHit[] RaycastAll(Ray ray);

区域覆盖型射线(Overlap)

有时需要检测一个空间范围,例如炸弹爆炸时,范围之内的物体都会受到波及,那么这里需要的就不是一条射线,而是一个球形区域。物理系统也提供了这类函数,它们均以Physics.Overlap开头,列举如下。返回的一个数组。

Collider[] OverlapBox(Vector3 center, Vector3 halfExtents, Quaternion 
orientation, int layerMask);
Collider[] OverlapCapsule(Vector3 point0, Vector3 point1, float radius, int 
layerMask);
Collider[] OverlapSphere(Vector3 position, float radius, int layerMask);

射线调试

使用Debug.DrawLine()函数和Debug.DrawRay()函数,将看不见的射线以可视化的形式表现出来,方便查看参数是否正确。该辅助线仅在编辑器的场景窗口中可见,常规用法如下:

void DrawLine(Vector3 start, Vector3 end, Color color);
void DrawLine(Vector3 start, Vector3 end, Color color, float duration);

void DrawRay(Vector3 start, Vector3 dir, Color color);
void DrawRay(Vector3 start, Vector3 dir, Color color, float duration);

DrawLine()方法用的是起点和终点

DrawLine(起点, 起点+方向向量.normalized * 长度, Color.red);
//其中nomalized是将向量标准化,即方向不变长度变为1

2.5层和层遮罩

若只想让射线检测到指定层,如只检测到地面,玩家和障碍物,代码如下:

int mask=layerMask.GetMask("Ground","Player","Obstacle");
if(Physics.Raycast(transform.position,Vector3.forward,mask)
{
    //执行语句
}

如果让mask表示这三层以外的层,则用一个二进制的取反运算即可,方法如下:

mask = ~mask;//英文波浪线表示二进制取反
LayerMask.NameToLayer();//将层名称转为层的整数
LayerMask.LayerToName();//将层的整数转为层名

2.6FixedUpdate()

Time.detlaTime用于获取两次Update之间的时间间隔

Time.fixedDetlaTime用于获取两次FixedUpdate之间的时间间隔

有时候由于设备运行不流畅导致Time.detlaTime相差太大

但Time.fixedDetlaTime不会,Time.fixedDetlaTime一般会是一个固定值(默认为0.02秒)

FixedUpdate()可以用来解决碰撞未及时检测和检测不到问题

当设备卡顿时,错过运行时刻,FixedUpdate()会在下一个运行时刻补上之前未运行的

有时使用放在Update中的相机跟随方法,在跟随对象运动过快时,相机会出现抖动现象。

针对物理移动(因速度和受力产生的运动)的刚体,只要将跟随相机的移动编写到FixedUpdate()里,抖动问题就会消失。

2.7修改角速度

修改刚体的角速度可让刚体旋转

void Update()
{
    if(Input.GetKeyDown(KeyCode.R)
    {
        rigid.angularVelocity = new Vector3(0,60,0);
    }
}

由于刚体具有角阻尼,在没有接触其他物体的情况下,旋转会慢慢停下

2.8质心

质心就像现实世界的重心一样

通过改变质心,可以做出不倒翁的模型

void Start()
{
    rigid=GetComponent<Rigidbody>();
    rigid.centerOfMass = new Vector3(0,-1,0);//设置centerOfMass就可以指定重心了
}

如果受力方向通过了物体的质心,物体就不会获得角速度,若错过了质心,那么物体就会有旋转的趋势

2.9更多施加力的方法

函数AddForce()施加力时,就是从物体的质心位置施加的

以下函数可以在不同位置施加力

void AddForceAtPosition(Vector3 force,Vector3 position);
void AddForceAtPosition(Vector3 force,Vector3 position,ForceMode mode);
//force施加的力,用向量表示,position施加力的位置,ForceMode力的模式,是一个枚举类型

ForceMode定义如下

public enum ForceMode
{
    Force=0,//持续施加力
    Impulse=1,//瞬间爆发力
    VelocityChange=2,//瞬间改变刚体速度,不考虑物理质量
    Acceleration=5//直接改变加速度,不考虑物体质量
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值