“大智,昨天我学习了物理系统,知道了碰撞体,那物理系统里面还有别的东西么?”
“你呀,太天真了,Collider只是物理系统的一部分。你看昨天学的Collider,他们都只是一个静态的碰撞体。我们昨天学习过物理系统很重要的是模拟物体的受力情况,物体根据物理规则运动呀。”
“那通过代码修改物体的位置不就能运动起来了么?”
“通过代码直接修改物体的位置并没有通过受力的方式改变物体,所以这种方式并没有应用到物理系统,要想让物体通过受力运动,我们首先要了解一下Rigidbody组件。物体只有添加了Rigidbody组件,才会对受力作出反应。小新,你现在先去看看Unity的文档,看看Rigidbody组件到底是什么吧!”
“收到,我马上就去看!”
小新马上打开Unity文档,找到了Rigidbody的介绍文档https://docs.unity3d.com/Manual/RigidbodiesOverview.html。
Rigidbody组件可以让一个物体受到物理影响。比如添加Rigidbody组建后,物体会立马对重力作出反应。如果物体上还添加了Collider,物体在受到碰撞时也会移动。
“大智,我知道了!Rigidbody可以让物体对受力做出反应,但是我有点和Collider搞不清楚了。”
“这两个是初学者不太容易搞清楚的概念。Collider只是给物体加上了碰撞,让物体拥有了实体,而不是一个可以穿过去‘全息投影’。而Rigidbody是让物体拥有了动态受力的功能。所以我们一般称只有Collider没有Rigidbody的物体叫静态Collider。一般刚体物体上也会有Collider,因为需要和别的物体发生碰撞。如果没有Collider,即使刚体也不会和其他物体碰撞。”
“那是不是不应该给场景里所有的物体添加Rigidbody组件?”
“非常对,场景里有一些物体是基本不会动的,比如说一栋大楼,一块大石头等等,他们只需要阻挡别的物体别穿过他们就行了,所以它们只需要添加Collider就行。因为添加了Rigidbody组件后会带来很多的物理计算,所以只需要给会移动的物体添加Rigidbody就行。”
“我明白了!我说呢怎么给所有物体添加了Rigidbody后卡的跑不起来呢,我去把我的吃鸡场景重新设置一下,嘿嘿!”
“你呀你呀,真是个急性子!顺便好好看一下刚体组件的各个参数!”
“知道啦!”
小新认真看了Rigidbody组件的文档,并做了笔记:
小新看完之后,还是有一些地方不太清楚,又来骚扰大智。
“大智,刚体的Is Kinematic我明白了是什么,这个有什么用呢?加上刚体不就是为了受力么,为什么还要提供一个这个属性,关掉受力呢?”
“有些情况,我们不想让物体受力,但是仍然能够和其他物体发生碰撞,影响其他物体。比如这个物体上有动画,在播放动画的时候,我们想让动画来控制物体的位置。”
“明白了,但是感觉还是需要用到的时候再去理解。那这个Collision Detection该什么时候设置呢?是不是类似子弹这种情况?”
“说的很对,Collision Detection就是针对快速移动的小物体,比如说子弹,但是通常游戏射击并不用实体的子弹,因为子弹多的时候性能太差了,我可以给你推荐一个文章来看看:https://zhuanlan.zhihu.com/p/22105641”
“谢谢智哥,我也发现了用刚体的话性能太差了,我去看看”
“大智,我发现有些情况两个物体发生了碰撞,但是并没有调用脚本中的代码,这是怎么回事?”
“哈哈,这时候我就要祭出大招了,给你两个表格,对照着看看是否会发生碰撞哈。”
OnCollision消息
OnTrigger消息
模拟GameObject对象在现实世界中的物理特性
对Rigidbody对象属性的赋值代码通常放在脚本的OnFixedUpdate()方法中
B、Rigidbody类实例方法
1、AddExplosionForce
public void AddExplosionForce(float explosionForce, Vetor3 explosionPosition, float explosionRadius);
public void AddExplosionForce(float explosionForce, Vetor3 explosionPosition, float explosionRadius, float upwardModifier);
public void AddExplosionForce(float explosionForce, Vetor3 explosionPosition, float explosionRadius, float upwardModifier, ForceMode mode);
#explosioForce为爆炸点施加的力的大小
#explosionPosition为爆炸点坐标,相对世界坐标系
#explosionRadius为爆炸作用力有效半径
#upwardsModifier为爆炸力作用点在y轴方向上的偏移
#mode为爆炸力的作用模式,默认为ForceMode.Force
此方法用于对刚体添加一个模拟爆炸效果的作用力。
设爆炸力大小为F,爆炸点坐标为E,有效半径为R,y轴的偏移量为y_m,刚体A的坐标为P,A受到的爆炸作用力为A.AddExplosionForce(F,E,R,y_m);
#爆炸力作用在刚体上的力的大小和方向是分开计算的;
爆炸力作用到A的大小为:
FA=(R-|EB|)*F/R,
#当爆炸点E固定时,刚体在某个范围移动时受到的爆炸力的大小可能不变
#当作用力半径R=0,所有接受爆炸点E作用的刚体收到的作用力大小都为F
2、AddForceAtPosition
public void AddForceAtPosition(Vector3 force, Vector3 position);
public void AddForceAtPosition(Vector3 force, Vector3 position, ForceMode mode);
#force为扭矩向量
#position为作用点坐标值
#mode为力的作用方式
此方法用于为参数position点增加一个力force,其参考坐标系为世界坐标系,作用方式为mode,默认值为ForceMode.Force
该方法与AddForce不同之处在于,AddForce方法对刚体施加力时不会产生扭矩使物体发生旋转,而AddForceAtPosition方法是在某个坐标点对刚体施加力,这样很可能会产生扭矩使刚体产生旋转
#当力的作用点在刚体重心时,刚体不发生旋转
#当力的作用点不在刚体重心时,刚体发生旋转,但当作用力的方向晋国刚体的重心坐标时不发生旋转
3、AddTorque
public void AddTorque(Vector3 torque) ;
public void AddTorque(Vector3 torque, ForceMode mode) ;
public void AddTorque(float x, float y, float z);
public void AddTorque(float x, float y, float z, ForceMode mode);
参数torque为扭矩向量,参数mode为力的作用方式
此方法用于给刚体添加一个扭矩torque,扭矩的作用力方式由mode决定,默认为ForceMode.Force
不同形状的刚体及不同的转轴,其转动惯量1的计算方式是不同的。
刚体有最大角速度的限制,默认值为7.0f,可以通过属性maxAngularVelocity更改刚体的最大角速度
4、ClosestPointOnBounds
public Vector3 ClosestPointOnBounds(Vector3 position);
参数position为爆炸点坐标
此方法用于求爆炸点到刚体Colloder表面的作用点,通常用在AddExplosionForce中计算爆炸力的大小
返回值为刚体Collider表面上的某一点,而不是Mesh上的点
5、GetPointVelocity
public Vector3 GetPointVelocity(Vector3 worldPoint);
worldPoint为世界坐标系中的点坐标
此方法用于获取世界坐标系中worldPoint点在刚体局部坐标系中对应位置的速度
往往是刚体重心的移动速度和点绕刚体重心的角速度
6、GetRelativePointVelocity
public Vector3 GetRelativePointVelocity(Vector3 relativePoint);
参数relativePoint为刚体自身坐标系中的点坐标
此方法用于获取刚体自身坐标系中relativePoint点的速度,速度的计算会受刚体角速度的影响
此方法和GetPointVelocity作用类似,只是GetPointVelocity使用世界坐标系中的坐标来确定位置,而此方法是使用自身坐标系中的坐标来确定位置。
可以将上述两种方法的点理解为刚体子类物体的重心坐标值。
7、MovePosition
public void MovePosition(Vector3 postion);
刚体的位置移动,通常用在刚体失去动力学模拟的情况下,即isKinematic为true时
8、Sleep
public void Sleep();
此方法可使刚体进入休眠状态,且至少休眠一帧
9、SweepTest
public bool SweepTest(Vector3 direction, out RaycastHit hitInfo);
public bool SweepTest(Vector3 direction, out RaycastHit hitInfo, float distance);
参数direction为探测方向,参数distance为有效探测距离,默认为无穷大
此方法用于检测在刚体的direction方向是否有碰撞器对象,且对象的有效探测距离不大于distance
#distance为A物体右表面到B物体左表面的距离,而非它们重心坐标之间的距离
#B物体只要含有Collider组件即可,无需含Rigidbody组件
#direction的方向为A在世界坐标系中的方向
#SweepTest的返回结果只是第一个被探测到的物体
10、SweepTestAll
public RaycastHit[ ] SweepTestAll(Vector3 direction);
public RaycastHit[ ] SweepTestAll(Vector3 direction, float distance);
此方法用于探测刚体的direction方向的distance距离内是否含有碰撞器,并返回所有探测到的物体的RaycastHit
与SweepTest的不同之处在于返回每个探测到的物体的RaycastHit
11、WakeUp
public void WakeUp( )
此方法用于将刚体从休眠状态唤醒,除了调用WakeUp方法外,在发生以下4种情况时,刚体会被自动唤醒
①其他刚体与休眠种的刚体发生了碰撞
②使用关节连接的其他刚体发生了移动
③刚体的属性发生了改变
④给休眠中的刚体施加了一个外力