Unity-BattleStar丨8. 物理引擎Rigidbody组件、Collider组件、Raycast

2024-08-14 16:36:40 浏览数 (2)

游戏中物理引擎用于模拟真实世界物理环境效果,要实现游戏对象的物理行为,Rigidbody(刚体)组件是必不可少的,当挂载该组件之后,物体立刻受到重力等物理效果影响。如果对象身上还挂载着Collider(碰撞)组件,那么该对象还受到碰撞物理效果影响,例如游戏中的被车撞飞

一、Rigidbody组件

Rigidbody组件Unity Manual介绍:Rigidbody

力的效果展示是由Rigidbody组件实现的,只有拥有该组件,物体才会进行力的计算。由Unity Manual我们可知道Rigidbody可实现的功能有哪些

1、组件名称释义

1). Mass:质量,kg,并不是重量mg=N

2). Drag:空气阻力

3). Angular Drag:角旋转阻力

4). Use Gravity:用于确认物体是否受重力影响,如果不勾选该项,则物体不受地心引力影响,不再下坠。但该物体还受其他物理效果影响

5). Is Kinematic:物体不受任何物理效果影响,即使我们通过脚本给它赋予很大的力,也不会移动,只能通过Transform来改变其位置。这通常用于玩家的移动,即不使用力来移动物体,也希望物体进行物理计算的情况,这种运动方式称为“动力学(Kinematic)运动”。

如果该属性设置为true表示该物体运动状态不受外力,碰撞和关节的影响,而只受到动画以及附加在物体上的脚本影响,但是该物体仍然能改变其他物体运动状态,例如游戏中倒下的敌人始终不动 ,就是利用这个不受外力影响的属性,但它也能反馈给其他与他碰撞到的物体一个反作用力,前提是与他碰撞的物体身上要有Rigidbody组件,否则无法产生力的效果(当刚体开启 IsKinematic时,刚体不再参与物理引擎的力计算,如果和他碰撞的物体还没有力,自然就不能计算出碰撞结果)

a、b都有Collider、Rigidbody组件,a开启Is K inematic,b不开启。a撞b,b动;b撞a,b受反作用力动,a不动

6). Constraints:是否约束该物体在X、Y、Z方向的移动或旋转

2、给游戏对象整体施加某个方向的力 AddForce()

Unity Scripting API:Rigidbody.AddForce ForceMode

我们可通过C#脚本方式给物体施加力

代码语言:javascript复制
using UnityEngine;

public class CubeAddForce : MonoBehaviour {

    private Rigidbody myRigidbody;

	void Start () {
        myRigidbody = GetComponent<Rigidbody>();
        myRigidbody.AddForce(new Vector3(0, 10, 0), ForceMode.Impulse);
	}
}

当然,代码中的力的方向我们也可写成

代码语言:javascript复制
AddForce(Vector3.up*10,ForceMode.Impulse)

注意:Vector3(0,10,0)要加new,Vector3.up不用

附单位向量代码

代码语言:javascript复制
Vector3.up      (0,1,0)
Vector3.down    (0,-1,0)
Vector3.left    (-1,0,0)
Vector3.right   (1,0,0)
Vector3.forward (0,0,1)
Vector3.back    (0,0,-1)

ForceMode有四个属性:

a、Force:给Rigidbody添加一个可持续的力,受Mass影响,写在Update()等接口中

b、Acceleration:给Rigidbody添加一个可持续的加速度,忽略Mass影响,可在Start()接口

c、Impulse:立即给Rigidbody添加一个冲力,受Mass影响,可在Start()接口

d、VelocityChange:立即给Rigidbody添加速度,忽略Mass影响,写在Update()等接口中

不考虑其他力,仅考虑重力情况下,在Update中添加的力,并不是每帧调用Force会使物体受到的力处于叠加状态,而是遵循牛顿定律,看该力与物体重力关系,相等时则处于平衡状态,其他情况也遵循牛顿定律F-mg=ma

但不受重力影响的力,不断的调用,没有重力的平衡,只能会使物体受到的力叠加地越来越大,直至飞了出去

3、在指定位置施加力 AddForceAtPosition()

代码语言:javascript复制
AddForceAtPosition(Vector3 Force,Vector3  Position,ForceMode)

在指定位置施加力实现特定效果。

代码语言:javascript复制
myRigidbody.AddForceAtPosition(Vector3.up, new Vector3(1,0,1),ForceMode.Force);

4、实战:模仿手雷爆炸效果 AddExplosionForce()

代码语言:javascript复制
AddExplosionForce(float explosionForce,Vector3 explosionPosition,float explosionRadius,float upwardsModifier,ForceMode mode)

float explosionForce:爆炸力

Vector3 explosionPosition:爆炸中心位置

float explosionRadius:爆炸半径

float upwardsModifier:调节爆炸出现的位置,使其看起来像掀起对象。默认从爆炸中心到刚体的质量中心力的方向是线性,如果upwardsModifier是非0值,该方向将通过减去中心点Y轴的值修改。例如:如果该值为2,那么爆炸出现在实际位置中心点2单位以下。使用这个参数,可轻易使爆炸似乎把物体扔到空中,这往往比单纯的外力更具戏剧性效果

ForceMode mode:力模式,同上2描述

思路:爆炸的力位置是从手雷中心开始的,因此力的位置在手雷中心;

爆炸是瞬间发生而不是持续发生的,因此要写在Start接口中();

爆炸力模式应为瞬间力,而不是持续发生,因此要力的模式应该是Impulse

代码语言:javascript复制
using UnityEngine;

public class CubeAddForce : MonoBehaviour {

    private Rigidbody myRigidbody;

    private void Start()
    {
        myRigidbody = GetComponent<Rigidbody>();
        myRigidbody.AddExplosionForce(20, new Vector3(0,0.5f,0), 5, 0, ForceMode.Impulse);
    }
}

cube中心是(0,0.5,0),我们设置的爆炸中心也是(0,0.5,0),我们可以看到,运行时在cube中心的爆炸,使其向上飞了出去。当我们将爆炸中心调整到(0,0.51,0)时,爆炸力在cube质心点之上,cube无法移动。同时我们也发现,周围的物体无法受中心cube影响而移动,这是因为力没传到周围物体上。

二、Collider组件

物体运动轨迹改变有两个方式,碰撞或代码。上例没有发生碰撞,力无法传递过去,但没碰撞我们就无法传递力了吗?也可以!通过物理引擎Collider组件的代码,Physics.OverlapSphere(Vector3 position,float radius)方法返回带有Collider的对象,我们只需要遍历他们并给他们的Rigidbody组件施加爆炸位置的力就好了,这当然是要有Collider组件才能实现

注意:组件类型定义变量有GetComponent<>()方法,object型变量不具备该方法

为什么要检测是否拥有Rigidbody组件?因为只有拥有该组件,物体才会进行力的计算。力才有地方去赋值。有Collider组件,才额外进行碰撞计算

代码语言:javascript复制
using UnityEngine;

public class CubeAddForce : MonoBehaviour {

    private Rigidbody myRigidbody;

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.A))
        {
            myRigidbody = GetComponent<Rigidbody>();
            myRigidbody.AddExplosionForce(20, new Vector3(0, 0.51f, 0), 5, 0, ForceMode.Impulse);

            Collider[] colliders = Physics.OverlapSphere(transform.position, 5);
          //Collider[] colliders = Physics.OverlapSphere(new Vector3(0,0,0), 5);

//以pointBottom为底部半圆圆心,PointTop为顶部半圆圆心,radius为半径,连接起来构成一个胶囊体
Physics.OverlapCapsule(pointBottom, pointTop, radius, LayerMask);


        foreach(Collider obj in colliders)
            {
                if (obj.GetComponent<Rigidbody>() != null)   //只有组件变量才有GetComponent<>()方法,因此object型变量自然不具备该方法了
                    obj.GetComponent<Rigidbody>().AddExplosionForce(20, new Vector3(0, 0.5f, 0), 5, 2, ForceMode.Impulse);
            }
        }
    }
}

三、Raycast

发射射线,检测一定范围内的物体,返回该物体

代码语言:javascript复制
Physics.Raycast(Vector3 origin,Vector3 direction,out RaycastHit hitinfo,float maxDistance)

Vector3 origin:射线起点

Vector3 direction:射线方向

out RaycastHit hitinfo:返回检测距离内检测到的物体

float maxDistance:最大检测距离

此例我们用Physics.Raycast()方法检测10m距离内的物体。我们先定义RaycastHit类型的变量,用于承载该方法检测返回的物体,当我们按下B键,若距离小于5m,则给它施加一个向上的力

代码语言:javascript复制
using UnityEngine;

public class CubeAddForce : MonoBehaviour
{
    RaycastHit hit;

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.B))
        {
            if (Physics.Raycast(transform.position, new Vector3(0, -1, 0), out hit, 10f))
            {
                if (hit.distance < 5)
                    GetComponent<Rigidbody>().AddForce(Vector3.up*10,ForceMode.Impulse);
            }
        }
    }
}

0 人点赞