Unity API 解析(9)—— Rigidbody 类

模拟 GameObject 对象在现实世界中的物理特性(重力,阻力,质量,速度)

对Rigidbody 对象属性的赋值代码通常放在脚本的OnFixedUpdate 方法中

 

collisonDetectionMode 属性 —— 碰撞检测模式

刚体的碰撞检测模式有3种

Discrete —— 静态离散检测模式

Continuous —— 静态连续监测模式 ,一般用在高速运动刚体的目标碰撞体上

ContinousDynamic —— 最强的连续动态检测模式

drag 属性 —— 刚体阻力

drag 值越大刚体速度减慢的越快,当drag >0 时,刚体在增加到一定速度后会匀速运动

刚体在自由落体运动中的最大速度值只与gravity 和drag值有关,与mass无关

inertiaTensor 属性 —— 惯性张量

在距离重心同等的条件下,刚体会向张量值大的一边倾斜

rigidbody.inertiaTensor = new Vector3(5.0f,10.0f,1.0f)

using UnityEngine;
using System.Collections;

public class inertiaTensor_ts : MonoBehaviour
{
    void OnGUI()
    {
        if (GUI.Button(new Rect(10.0f, 10.0f, 160.0f, 45.0f), "X轴惯性张量大于Y轴"))
        {
            transform.position = new Vector3(0, 4, 0);
            //transform绕Z轴旋转45度
            transform.rotation = Quaternion.Euler(0.0f, 0.0f, 45.0f);
            //设置rigidbody的惯性张量
            //X轴分量值大于Y轴,则刚体会向X轴方向倾斜
            rigidbody.inertiaTensor = new Vector3(15.0f, 10.0f, 1.0f);
        }
        if (GUI.Button(new Rect(10.0f, 60.0f, 160.0f, 45.0f), "Y轴惯性张量大于X轴"))
        {
            transform.position = new Vector3(0, 4, 0);
            transform.rotation = Quaternion.Euler(0.0f, 0.0f, 45.0f);
            //设置rigidbody的惯性张量
            //X轴分量值小于Y轴,则刚体会向Y轴方向倾斜
            rigidbody.inertiaTensor = new Vector3(5.0f, 10.0f, 1.0f);
        }
        if (GUI.Button(new Rect(10.0f, 110.0f, 160.0f, 45.0f), "X轴和Y轴惯性张量相同"))
        {
            transform.position = new Vector3(0, 4, 0);
            transform.rotation = Quaternion.Euler(0.0f, 0.0f, 45.0f);
            //设置rigidbody的惯性张量
            //X轴和Y轴惯性张量相同,则刚体会保持静止
            rigidbody.inertiaTensor = new Vector3(10.0f, 10.0f, 1.0f);
        }
    }
}

mass 属性 —— 刚体质量

一般刚体质量取值在0.1 附近模拟最佳,最大不要超过10,否则容易出现模拟不稳定的情况

mass 的主要作用是物体发生碰撞时计算碰撞后的物体的速度

velocity 属性 —— 刚体速度

在脚本中无论是给刚体赋予一个vector3类型的速度向量,还是获取当前刚体的速度,v的方向都是相对世界坐标系而言

velocity 的单位是米每秒 —— 米是Unity中默认的长度单位

AddExplosionForce 方法 —— 模拟爆炸力

爆炸力的大小,爆炸点的坐标,爆炸力的有效半径

AddForceAtPosition —— 增加刚体点作用力

为参数position点增加一个力force

AddForce 方法对刚体施加力时不会产生扭矩使物体发生旋转,但此方法会

using UnityEngine;
using System.Collections;

public class AddExplosionForce_ts : MonoBehaviour
{
    public Rigidbody A;
    public Transform Z;//Scene视图中显示爆炸点坐标
    Vector3 E = Vector3.zero;//爆炸点坐标
    float F, R, y_m;
    bool is_change = false;

    void Start()
    {
        //初始位置使得爆炸点和A的x,y轴坐标值相等
        //可以更改F及R的大小查看运行时结果
        E = A.position - new Vector3(0.0f, 0.0f, 3.0f);
        F = 40.0f;
        R = 10.0f;
        y_m = 0.0f;
        A.transform.localScale = Vector3.one * 2.0f;
        Z.position = E;
    }

    void FixedUpdate()
    {
        if (is_change)
        {
            A.AddExplosionForce(F, E, R, y_m);
            is_change = false;
        }
    }

    void OnGUI()
    {
        //当爆炸点和A的重心的两个坐标轴相等时,A将平移不旋转
        if (GUI.Button(new Rect(10.0f, 10.0f, 200.0f, 45.0f), "刚体移动不旋转"))
        {
            is_change = true;
            inits();
        }
        //虽然受力大小不变,但产生扭矩发生旋转
        if (GUI.Button(new Rect(10.0f, 60.0f, 200.0f, 45.0f), "刚体发生移动但受力大小不变"))
        {
            inits();
            A.position += new Vector3(0.5f, -0.5f, 0.0f);
            is_change = true;
        }
        if (GUI.Button(new Rect(10.0f, 110.0f, 200.0f, 45.0f), "按最近表面距离计算力的大小"))
        {
            inits();
            A.position += new Vector3(0.0f, 2.0f, 0.0f);
            is_change = true;
        }
        //y 轴的偏移改变了A的原始方向
        // 可以更改y_m的值查看不同的效果
        if (GUI.Button(new Rect(10.0f, 160.0f, 200.0f, 45.0f), "Y?á·¢éú??ò?"))
        {
            inits();
            is_change = true;
            A.position += new Vector3(0.0f, 2.0f, 0.0f);
            y_m = -2.0f;
        }
    }
    //初始化数据
    void inits()
    {
        A.velocity = Vector3.zero;
        A.angularVelocity = Vector3.zero;
        A.position = E + new Vector3(0.0f, 0.0f, 3.0f);
        A.transform.rotation = Quaternion.identity;
        y_m = 0.0f;
    }
}

AddTorque 方法 —— 刚体添加扭矩

using UnityEngine;
using System.Collections;

public class AddTorque_ts : MonoBehaviour {
    public Rigidbody R;
    Vector3 m_torque = new Vector3(0.0f,10.0f,0.0f);
	void Start () {
        R.transform.localScale = new Vector3(2.0f,2.0f,2.0f);
        R.mass = 1.0f;
        R.angularDrag = 0.0f;
        Debug.Log("刚体默认的最大角速度"+R.maxAngularVelocity);
        // 可以使用如下代码更改刚体的最大角速度
        //R.maxAngularVelocity = 10.0f;
	}
	
	void FixedUpdate () {
        // 每帧给物体添加一个扭矩,使其转速不断加快
        R.AddTorque(m_torque,ForceMode.Force);
        Debug.Log("刚体当前角速度"+R.angularVelocity);
	}
}

ClosestPointOnBounds 方法 —— 爆炸点到刚体最短距离

此方法通常用在 AddExplosionForce 中计算爆炸力的大小

GetPointVelocity —— 刚体点速度

获取世界坐标系中worldPoint点在刚体局部坐标系中的速度 ,速度的计算会受刚体角速度的影响

GetRelativePointVelocity —— 刚体点相对速度

 

MovePosition —— 刚体位置移动

对刚体的位置进行移动,通常用在刚体失去动力学模拟的情况下 —— isKinematic 为 true时

Sleep —— 刚体休眠

SweepTest —— 检测碰撞器

检测在刚体的direction方向是否有碰撞器对象,且对象的有效探测距离不大于distance

using UnityEngine;
using System.Collections;

public class SweepTest_ts : MonoBehaviour
{
    public GameObject A, B;
    RaycastHit hit;
    float len = 10.0f;// 有效探测距离
    void Start()
    {
        A.transform.position = new Vector3(1.0f, 1.0f, 1.0f);
        B.transform.position = new Vector3(4.0f, 1.0f, 1.0f);
    }

    void FixedUpdate()
    {
        // 探测刚体A右侧len距离内是否存在物体
        if (A.rigidbody.SweepTest(A.transform.right, out hit, len))
        {
            Debug.Log("A物体右侧存在物体" + hit.transform.name + " 其距A的距离为" + hit.distance);
        }
        else
        {
            Debug.Log("A物体右侧" + len + "米范围没有检测到带碰撞器的物体");
        }
    }

    void OnGUI()
    {
        if (GUI.Button(new Rect(10.0f, 10.0f, 200.0f, 45.0f), "设置B坐标使A无法探测到"))
        {
            // 重置B的position,使得物体A,B的间距大于len值
            B.transform.position = new Vector3(12.0f, 1.0f, 1.0f);
        }
        if (GUI.Button(new Rect(10.0f, 60.0f, 200.0f, 45.0f), "取消B中的Rigidbody组件"))
        {
            // 销毁B物体中的Rigidbody组件
            // 运行程序可以发现,B物体中是否存在Rigidbody对探测结果没有影响
            if (B.GetComponent<Rigidbody>())
            {
                Destroy(B.GetComponent<Rigidbody>());
            }
        }
        if (GUI.Button(new Rect(10.0f, 110.0f, 200.0f, 45.0f), "取消B中的Collider组件"))
        {
            // 销毁B物体中的Collider组件
            // 运行程序发现,如果B中无Collider组件则A无论如何也探测不到B的存在
            if (B.GetComponent<Collider>())
            {
                Destroy(B.GetComponent<Collider>());
            }
        }
        // 对B物体的状态重置
        if (GUI.Button(new Rect(10.0f, 160.0f, 200.0f, 45.0f), "重置"))
        {
            B.transform.position = new Vector3(4.0f, 1.0f, 1.0f);
            if (!B.GetComponent<Collider>())
            {
                B.AddComponent<BoxCollider>();
            }
            if (!B.GetComponent<Rigidbody>())
            {
                B.AddComponent<Rigidbody>();
                B.rigidbody.useGravity = false;
            }
        }
    }
}

SweepTestAll —— 探测碰撞器

探测刚体的direction方向的distance距离内是否有碰撞器,并返回所有探测到的物体的RaycastHit

using UnityEngine;
using System.Collections;

public class SweepTestAll_ts : MonoBehaviour
{
    public GameObject A, B, C, D;
    RaycastHit[] hits;
    float len = 10.0f;//有效探测距离
    void Start()
    {
        A.transform.position = new Vector3(1.0f, 1.0f, 1.0f);
        B.transform.position = new Vector3(4.0f, 1.0f, 1.0f);
        C.transform.position = new Vector3(7.0f, 1.0f, 1.0f);
        //D 物体超出了A的有效探测距离,不会被探测到
        D.transform.position = new Vector3(12.0f, 1.0f, 1.0f);
        hits = A.rigidbody.SweepTestAll(A.transform.right, len);
        float l = hits.Length;
        Debug.Log("A 探测到的物体个数" + l + " 他们分别是");
        // 遍历
        foreach (RaycastHit hit in hits)
        {
            Debug.Log(hit.transform.name);
        }
    }
}

WakeUp 方法 —— 唤醒刚体

自动被唤醒的情况 —— 其他刚体与休眠中的刚体发生碰撞,使用关节链接的刚体发生移动,刚体属性发生改变,给休眠中的刚体施加了一个力

 

useGravity 用来确定刚体是否接受重力加速度的感应

isKinematic 属性 —— 用来确定是否接受动力学模拟(重力感应,速度,阻力,质量)

 

刚体velocity 的值只与Gravity ,drag及Kinematic有关,与质量mass及物体的sacle无关

 

若在脚本中未使用 Rigidbody.SetDensity 方法设置刚体的密度,则刚体的质量mass值为在Inspector面板中mass的大小,此mass与transform中的scale无关,否则有关

 

两物体碰撞符合动量守恒定律

 

作用力方式 ForceMode 的功能注解

ForceMode.Force —— 使用刚体质量计算,以每帧间隔时间为单位计算动量

ForceMode.Acceleration

ForceMode.Impulse

ForceMode.VelocityChange

 

onTriggerXXX  onCollisionXX

若A中无Rigidbody 组件,B中无论是否含有Rigidbody组件,A都将穿越B物体,并且A和B脚本中的OnTriger 和 OnCollision 都不会被调用

要激活以上两方法,必须使移动的物体中含有Rigidbody组件

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。