unity进阶篇4-物理系统(碰撞体和刚体)的使用
关于物理系统的使用
关于物理系统的使用
Collider (碰撞体)
- 如何实现两个物体的碰撞:例, 主角和箱子/树木的碰撞
点击Hierarchy里的物体,查看Inspector界面,然后Add Component,选择Box Collider 2D
Box Collider 2D 和 Box Collider 是不一样的,没有写2D的默认是3D
- 如何调整Collider的区域
- 在Inspector展开Box Collider 2D
- 点击Edit Collider,然后会在物体处出现绿色边框
- 可以手动点击绿色边框进行调整,或在offset和size处调整绿色边框大小
- 两个物体发生碰撞后,一个物体进行了旋转,如果不想让它旋转怎么办
- 在物体的Inspector里展开Rigidbody 2D(刚体)
- Constraints(约束/限制)里有Freeze Rotation x/y/z轴
- 旋转时因为2D画像沿着z轴旋转,所以我们将Freeze Rotation Z 打勾就行了
- 为什么两个物体相互碰撞时会出现剧烈抖动。
由于在移动的人物/物体里使用的移动代码时Transform,这是一个变化位置组件,它不会影响到物理碰撞的组件,它只会影响到它下一步的位置,它不管这个position在不在碰撞范围内,Transform的组件先进去,然后Rigidbody再将Transform还原到之前位置(挤出),所以一进一出就产生了抖动。
Vector2 position = transform.position;//更改position的x坐标值,让它每帧加0.1,update就是一帧一帧循环的。position.x = position.x + Speed*horizontal*Time.deltaTime;position.y = position.y + Speed*vertical*Time.deltaTime;//更新位置的数据后,我们需要讲更新后的数据输入到新的位置(更新transform)transform.position = position;所以我们不能用transform来变化它的位置
我们需要用刚体组件来变化它的位置
这是用Transform组件编写的移动代码:
public class RubyController : MonoBehaviour{// Start is called before the first frame updatevoid Start(){}public float Speed = 0.1f;// Update is called once per framevoid Update(){//获取水平输入float horizontal = Input.GetAxis("Horizontal");//获取垂直输入float vertical = Input.GetAxis("Vertical");//创建一个Vector2的对象position,用来获取当前角色位置。Vector2 position = transform.position;//更改position的x坐标值,让它每帧加0.1,update就是一帧一帧循环的。position.x = position.x + Speed*horizontal*Time.deltaTime;position.y = position.y + Speed*vertical*Time.deltaTime;//更新位置的数据后,我们需要讲更新后的数据输入到新的位置(更新transform)transform.position = position;}}这是用Rigidbody便携的移动代码:
using System;using System.Collections;using System.Collections.Generic;using UnityEngine;public class RubyController : MonoBehaviour{// Start is called before the first frame updateprivate Rigidbody2D rigidbody2D;float horizontal;float vertical;//将速度暴露出来,使其可调public float Speed = 0.1f;private void Start(){rigidbody2D = GetComponent<Rigidbody2D>();}// Update is called once per framevoid Update(){//获取水平输入horizontal = Input.GetAxis("Horizontal");//获取垂直输入vertical = Input.GetAxis("Vertical");}//固定时间间隔执行的更新方法private void FixedUpdate(){//创建一个Vector2的对象position,用来获取当前角色位置。Vector2 position = transform.position;//更改position的x坐标值,让它每帧加0.1,update就是一帧一帧循环的。position.x = position.x + Speed*horizontal*Time.deltaTime;position.y = position.y + Speed*vertical*Time.deltaTime;//更新位置的数据后,我们需要讲更新后的数据输入到新的位置(更新transform)rigidbody2D.position = position;}}每次游戏计算新图像时都会调用 Update,问题是调用速度不确定。在一台速度较慢的计算机上,调用速度可能是每秒 20 张图像,而在一台非常快的计算机上,调用速度可能是每秒 3000 张图像。
为了使物理计算保持稳定,需要定期进行更新(例如,每隔 16ms)。Unity 还有另一个名为 FixedUpdate 的函数,只要你想直接影响物理组件或对象(例如刚体),就需要使用该函数。
不要把水平输入和垂直输入放到FixedUpdate里
void Update(){//获取水平输入horizontal = Input.GetAxis("Horizontal");//获取垂直输入vertical = Input.GetAxis("Vertical");}如果将水平输入和垂直输入放到FixedUpdate,由于它会在一定时间间隔内更新,所以会产生跳帧现象。
**Compute Physics system calculations after FixedUpdate. 0.02 seconds (50 calls per second) is the default time between calls. **
默认是每0.02秒刷新一次,每秒50帧。
- 如果我们想更改FIxedUpdate 的时间间隔怎么办
- Edit
- Project Settings…
- Time
- Fixed Timestep
- 如果我们想给Tilemap添加碰撞体如何添加,如:我们想让主角不能闯过某些场景
- 点击自己创建的Tilemap
- Add Component
- Tilemap Collider 2D
- 然后会发现在Scene里布置的Tilemap全部都有绿色的方格
- 如何只选择一些特殊的不应该通过的场景保留绿色方格呢。
- 点击创造场景的预制件(如果放到统一一个文件夹可以直接全选),然后将Sprite改成None
- 选择你想要主角不通过的场景的预制件,将其选择Sprite就行了。
- 有一些是Rule Override Tile,我要如何一旦前面的Tile改变属性,被Override Tile 也会改变属性。
- 将Rule Override Tile删除
- 建立Advance Rule Override Tile,这个Override tile 能直接改变属性不因为前面覆盖的所影响。
- 更改也是像之前的Rule Override Tile更改
- 如何优化Tilemap的碰撞体
目前,正如在 Scene 视图中看到的,每个瓦片都是一个单独的碰撞体。 这种方法效果良好,但会产生两个问题:
- 物理系统的计算量更大;如果你的世界很大,可能会减慢你的游戏速度。
- 在瓦片之间的边界上会产生小问题。由于瓦片是两个并排的碰撞体,并且两者之间存在微小间隙,因此有时计算上的微小误差也可能导致仍会发生碰撞的罕见情况。
为了解决这些问题,Unity 提供了一个名为 Composite Collider 2D 的组件。此组件可以获取对象(或对象的子对象)上的所有碰撞体,并由此创建一个大碰撞体。
- 在Tilemap上 Add Component
- 添加Composite Collider 2D,它会自动添加Rigidbody2D
- 在之前添加的Tilemap Collider 2D 里勾选Used by Composite
- 将Rigidbody2D 从Dynamic -> Static(将动态设置成静态) ,不然它会因为碰撞而产生图像旋转。