赞
踩
内容摘取总结自《Unity3D脚本编程与游戏开发》(马遥,沈琰)第三章——物理系统脚本编程
搬运自我的博客:浮生如梦 岁月如歌
using UnityEngine;
public class Test : MonoBehaviour
{
Rigidbody rigid;
void Start()
{
rigid = GetComponent<Rigidbody>();
}
}
private void Update()
{
if (Input.GetButtonDown("Jump"))
{
rigid.AddForce(new Vector3(0, 100, 0));
}
}
以上代码的作用是在玩家按下空格键时,对刚体施加一个向上的力,大小为100牛,持续时间是一个物理帧间隔(默认0.02秒)。如果物体只有1千克,重力不到10牛,那么这个力会让它跳起一定高度。
// 获取当前物体速度
Vector3 vel = rigid.velocity;
// 将当前速度沿z轴增加1m/s
rigid.velocity = vel + new Vector3(0, 0, 1);
using UnityEngine; public class SimpleJump : MonoBehaviour { Rigidbody rigid; void Start() { rigid = GetComponent<Rigidbody>(); } private void Update() { if (Input.GetButtonDown("Jump")) { rigid.velocity = new Vector3(rigid.velocity.x, 0, rigid.velocity.z); rigid.AddForce(new Vector3(0, 100, 0)); } } }
bool Raycast(Vector3 origin, Vector3 direction); bool Raycast(Vector3 origin, Vector3 direction, float maxDistance); bool Raycast(Vector3 origin, Vector3 direction, float maxDistance, int layerMask); bool Raycast(Ray ray, 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 = new Ray(Vector3.zero, Vector3.up); // 获得当前鼠标指针在屏幕上的位置(单位是像素) Vector2 mousePos = Input.mousePosition; // 创建一条射线,起点是摄像机位置,方向指向鼠标指针所在的点(隐含了从屏幕到世界的坐标转换) Ray ray2 = Camera.main.ScreenPointToRay(mousePos); // 之后可以将ray或ray2发射出去,例如: Physics.Raycast(ray, 10000, LayerMask.GetMask("Default"));
int mask = LayerMask.GetMask("Ground", "Player", "Obstacle");
if (Physics.Raycast(transform.position, Vector3.forward, mask))
{
// 碰到了物体
}
mask = ~mask; // 英文波浪线,代表二进制取反
gameObject.layer = LayerMask.NameToLayer("Default");
以下几个Raycast()函数的重载可以获取到碰撞信息
bool Raycast(Vector3 origin, Vector3 direction, out RaycastHit hitInfo, float maxDistance); bool Raycast(Vector3 origin, Vector3 direction, out RaycastHit hitInfo, float maxDistance, int layerMask); bool Raycast(Ray ray, out RaycastHit hitInfo, float maxDistance, int layerMask); //演示 private void TestRay() { // 声明变量,用于保存碰撞信息 RaycastHit hitInfo; // 发射射线,起点是当前物体的位置,方向是世界前方 if (Physics.Raycast(transform.position, Vector3.forward, out hitInfo)) { // 如果确实碰到物体,会运行到这里。没碰到物体就不会 // 获取碰撞点的坐标(世界坐标) Vector3 point = hitInfo.point; // 获取对方的碰撞体组件 Collider coll = hitInfo.collider; // 获取对方的Transform组件 Transform trans = hitInfo.transform; // 获取对方的物体名称 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);
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);
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);
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);
// 以一个简单的射线为例
Raycast(起点, 方向向量, 长度);
// 对应的可视化线条
DrawLine(起点, 起点+方向向量.normalized * 长度, Color.red);
// 其中nomalized是将向量标准化,即方向不变长度变为1
在Project窗口中单击鼠标右键,选择Create→Physics Material,就可以创建一个物理材质。物理材质的参数被简单定义为Dynamic Friction(动态摩擦系数)、Static Friction(静态摩擦系数)、Bounciness(弹性系数)、与其他物体接触时的Friction Combine(摩擦力系数算法)和Bounce Combine(弹性系数算法),如图所示。
动态摩擦系数就是物体之间正在相对滑动时的摩擦系数。例如0.1代表很光滑的表面,0.9代表很粗糙的表面。
静态摩擦系数就是物体之间没有相对滑动时的摩擦系数。现实生活中,物体的静态摩擦力一般略大于动态摩擦力,当然在游戏世界中可以随意调节它们的大小。
弹性系数可以调节物体反弹力的大小。例如0.8可以代表充气很足的篮球,0则代表没有任何反弹力。弹性系数一般不能高于0.9,否则会导致物体反弹的速度比撞击前的速度还快,这样它会变得越来越快,没有止境。
最后两个参数决定了两个物体表面都具有摩擦系数和弹性系数时,如何计算综合的摩擦系数和弹性系数。可选择取平均值、取最大值、取最小值或相乘4种方式。
最后,有两点值得说明。
一是物理材质是配合碰撞体使用的。碰撞体有一个“材质”(Material)的属性,这里自然不是指渲染材质,而是指物理材质。将创建好的物理材质拖曳到该属性上即可指定该属性。
二是不指定任何物理材质时,碰撞体具有默认的物理材质。
当设备运行不流畅、帧率下降时,会发现Time.deltaTime变大了(即帧与帧之间的时间间隔变长),但是Time.fixedDeltaTime却不会。一般Time.fixedDeltaTime会是一个固定的值(默认为0.02秒,可以通过选择主菜单的Edit→Project Settings→Time来修改)。
物理更新不仅要保证频率高,还要保证频率稳。不稳定的频率一样会带来糟糕的效果,因此所有的物理系统处理都在引擎循环中的一个专门环节上完成。
游戏世界的时间是一个虚拟的概念,一定程度上可以人为控制。如果在某个时刻T,硬件卡顿了0.06秒,正好错过了3次FixedUpdate()的调用时机,那么在下一次有机会运行的时候,FixedUpdate()函数会补上之前错过的3次,连续执行4次,而且还会“假装”这4次的调用时间点分别是T+0.02s、T+0.04s、T+0.06s、T+0.08s。通过这样的机制,就能确保无论硬件运行是否稳定,游戏都能保证“稳定”的物理更新,避免出现奇怪的结果。作为对比,Update()函数则没有这个特性。
对于摄像机抖动问题:
由于刚体因速度或受力而产生的运动,属于物理更新。而Update()函数和LateUpdate()函数不属于物理更新,这其中有着微妙的时间差。要解决这个问题并不难,针对物理移动的刚体,只要将跟随摄像机的移动也编写到FixedUpdate()里,抖动的问题就会消失了。
void Update()
{
if (Input.GetKeyDown(KeyCode.R))
{
rigid.angularVelocity = new Vector3(0, 60, 0);
}
}
public class Tumbler : MonoBehaviour
{
Rigidbody rigid;
void Start () {
rigid = GetComponent<Rigidbody>();
// 设置centerOfMass就可以指定重心了(本地坐标系)
rigid.centerOfMass = new Vector3(0, -1, 0);
}
}
void AddForceAtPosition(Vector3 force, Vector3 position); void AddForceAtPosition(Vector3 force, Vector3 position, ForceMode mode); //之前讲解函数AddForce()时,忽略了它的最后一个参数——ForceMode(力的模式),AddForceAtPostion()函数同样也有该参数。 //“力的模式”参数是一个枚举类型,定义如下 public enum ForceMode { // 默认方式为持续施加力,符合牛顿力学 Force = 0, // 设置为瞬间爆发力,适合表现快速猛烈的力,例如爆炸 // 力的持续时间有区别,但仍然符合牛顿力学 Impulse = 1, // 瞬时改变刚体速度,不考虑物体质量 VelocityChange = 2, // 直接改变加速度,不考虑物体质量 Acceleration = 5 }
// 冻结所有的缩放和旋转
rigid.constraints = RigidbodyConstraints.FreezeAll;
// 仅冻结沿x轴的位移,取消所有其他约束
rigid.constraints = RigidbodyConstraints.FreezePositionX;
// 仅冻结所有旋转,取消位移约束
rigid.constraints = RigidbodyConstraints.FreezeRotation;
// 冻结沿x轴和z轴的旋转,冻结沿y轴的位移
rigid.constraints = RigidbodyConstraints.FreezeRotationX
| RigidbodyConstraints.FreezeRotationZ
| RigidbodyConstraints.FreezePositionY;
Rayhirox于2022-04-08使用Notion整理完毕
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。