(Unity)关于Rigidbody,Collider和CharacterController三者之间的关系和用法的总结(转)

Rigidbody:多用在“物体”上,因为“物体”都是“死”的,他们的运动一般都是靠物理系统。所以对于Rigidbody的移动,不要用Translate(),要用各种“力”, 比如:Rigidbody的AddForce()方法,通过“力”来让它移动。另外,Rigidbody可以和NavMeshObstacle合用,因为后者也是用在“物”上的,但是一般不要和NavMeshAgent合用,因为这两个都会控制物体的运动,这样会有race condition。如果非要合用,请勾选Is Kinematic,由此看来如果Rigidbody的Is Kinematic属性被勾选了,那么Rigidbody就可以和任何其他的Component合用了,比如:NavMeshAgent,CharacterController。

Collider:定义了一个物体的形状,而我们的碰撞检测就是根据这个形状来进行的,随意Collider是碰撞检测的基础,几乎Unity中所有的物体都需要Collider。就算我们使用CharacterController一般会删掉原来的Collider,不过CharacterController本身自带了一个Capsule Collider。Collider分为static collider和dynamic collider,前者表示没有Rigidbody的collider,后者表示有Rigidbody的collider,static collider的物体最好是静止的物体,不要通过改变他的transform的position来使它运动,这样不仅会给物理引擎造成很多性能损失,而且会产生一些无法预测的行为。所以如果我们非要一个有collider的物体通过我们的脚本(比如通过Translate()方法)移动,并且不希望被物理引擎控制,那么我们需要给这个collider配上一个勾选了Is Kinematic的Rigidbody。因为Rigidbody的Is Kinematic就是让这个Rigidbody不受物理引擎的控制。结论:在游戏中,对于那些可能将来会移动的物体,我们不仅要加Collider,还要加上Rigidbody,如果希望是被物理引擎控制,就不勾选Rigidbody的Is Kinematic,如果希望不被物理引擎控制,希望通过自己的代码或者动画来控制,就勾选Rigidbody的Is Kinematic,而且这个属性还可以在脚本中随时改变,从而实现“ragdoll effect”,“ragdoll effect”类似于CS里面的效果,大部分时间物体的运动是通过脚本和动画控制的,如果遇到爆炸等,物体的运动就可以被物理系统接管了,这个时候开启Is Kinematic最合适了。

CharacterController:多用在“角色”上,因为“角色”多是“活”的(比如:游戏中的主角,NPC什么的),他们的运动要么是玩家控制,要么是脚本控制,所以一般不需要由物理系统来控制,如果由物理系统控制,反而使得游戏的操作性下降了。由玩家控制的“角色”肯定不要用Rigidbody,要用CharacterController。如果NPC非要用Rigidbody,那么请勾选Is Kinematic。使用了CharacterController以后,就不要用Translate()来移动物体了,要用它自己提供的Move()方法来移动物体。否则会有一些“诡异”的问题。

CharacterController不对任何“力”产生作用,同时也不对然和物体施加“力”,即使那个物体是Rigidbody。

Rigidbody会对“力”产生响应,如果一个物体加上了Rigidbody,首先会直接对“重力”产生响应,如果再加上Collider,就会对其他“力”产生响应了。同时如果使用Translate()移动Rigidbody,那么它会对其他的Rigidbody在碰撞的时候自动施加一个“力”。


Collider中Is Trigger的优先级高于Rigidbody(不管这个被标记为Is Trigger的Collider和Rigidbody是不是在同一个GameObject上)。因为Rigidbody是根据Collider来进行物理模拟的,Collider是Rigidbody的基础,所以如果这个Collider是Trigger,由于Trigger只是通过调用OnTrigger*()函数来响应碰撞这个行为,而不会去响应任何实质性的碰撞(所谓的实质碰撞,就是能被观察到的碰撞,比如碰撞之后产生运动,碰撞之后阻止物体穿过等等)。所以就算Rigidbody在,也发挥不了作用(OnCollision*()函数不会调用,同时也不会产生物理运动,也不会阻止物体穿过,反正任何可见的碰撞效果都没有)。

同理,Collider中Is Trigger的优先级也高于CharacterController,不过Collider和CharacterController一般不放在一起,所以这里指的是对方的物体上Collider的Is Trigger。

CharacterController(不要再加Collider,CharacterController有自带的Capsule Collider。并且使用Move()方法移动物体) 
用CharacterController去碰撞另一个物体,如果被碰撞的物体有Collider,并且Is Trigger没有勾选,那么无论这个物体是不是Rigidbody,CharacterController都会被这个物体“挡”下来,并且只会触发他自己的OnControllerColliderHit()这个回调函数(也就是说,如果被碰撞的物体或者它自己有OnTrigger*(),OnCollision*()这种函数,都不会被触发)。

所以如果我们想要实现一个CharacterController推箱子的游戏,那么首先这个箱子必须是Rigidbody,并且Collider不是Is Trigger,而这个“推力”需要在CharacterController的OnControllerColliderHit()这个函数中“发出”了。

如果被碰撞的物体Collider的Is Trigger方法勾选了,那么由于Is Trigger只是将Collider当成了一个“触发器”,而不会响应如何的实质碰撞(所谓的实质碰撞,就是能被观察到的碰撞,比如碰撞之后产生运动,碰撞之后阻止物体穿过等等),所以CharacterController会穿过被碰撞的物体,并且OnControllerColliderHit()这个回调不会起作用,取而代之的是双方的OnTrigger*()函数会被调用。

Rigidbody(如果勾选了Is Kinematic,那么这个Rigidbody就不受物理引擎控制了) 
一个物体一旦加上了Rigidbody,那么就可以立刻受到“重力”的影响,如果再加上Collider,那么就可以受到别的“力”的影响,由于Collider的Is Trigger的优先级较高,所以,如果Collider是Is Trigger(不管这个Is Trigger的Collider是不是和Rigidbody在一个物体上,反正只要碰撞双方出现了Is Trigger),那么这个Rigidbody就不会产生实质性的碰撞(所谓的实质碰撞,就是能被观察到的碰撞,比如碰撞之后产生运动,碰撞之后阻止物体穿过等等)。并且OnTrigger*()被调用,而不是OnCollision*()。

关于Rigidbody的OnCollision*()回调被调用的补充说明(来自:https://docs.unity3d.com/Manual/CollidersOverview.html): 
With normal, non-trigger collisions, there is an additional detail that at least one of the objects involved must have a non-kinematic Rigidbody (ie, Is Kinematic must be switched off). If both objects are kinematic Rigidbodies then OnCollisionEnter, etc, will not be called. With trigger collisions, this restriction doesn’t apply and so both kinematic and non-kinematic Rigidbodies will prompt a call to OnTriggerEnter when they enter a trigger collider.

Rigidbody:OnCollisionEnter(Collision c), OnCollisionStay(Collision c), OnCollisionExit(Collision c)

Collider Is Trigger: OnTriggerEnter(Collider c), OnTriggerStay(Collider c), OnTriggerExit(Collider c)

CharacterController: OnControllerColliderHit(ControllerColliderHit c)

产生碰撞(可以看见效果的碰撞或者像Is Trigger的Collider那样不可见效果的碰撞)的一般条件(不是必要条件,因为有一个例外,见后面的解释)是碰撞双方都要有Collider(不管是否是Is Trigger),并且其中至少有一个有Rigidbody或者CharacterController。这个时候碰撞产生,并且调用相应的回调函数。如果出现多个函数都可以调用的情况,看下面的优先级。

最高优先级:有Is Trigger的Collider参与的碰撞只会调用双方的OnTrigger*()函数,且不会产生实质碰撞(所谓的实质碰撞,就是能被观察到的碰撞,比如碰撞之后产生运动,碰撞之后阻止物体穿过等等)。

次高优先级:有CharacterController参与且没有Is Trigger的碰撞只会调用CharacterController的(如果被碰撞的也是CharacterController,那么他的OnControllerColliderHit()也会被调用)OnControllerColliderHit()函数。且会产生CharacterController被物体“档”下来的效果,不过除此之外没有其他任何物理效果。

最低优先级:有Rigidbody参与的碰撞,并且没有CharacterController和Is Trigger的Collider,会调用碰撞双方的OnCollision*()回调,并且由物理系统模拟碰撞之后的物理运动。

关于Rigidbody的OnCollision*()回调被调用的补充说明(来自:https://docs.unity3d.com/Manual/CollidersOverview.html): 
With normal, non-trigger collisions, there is an additional detail that at least one of the objects involved must have a non-kinematic Rigidbody (ie, Is Kinematic must be switched off). If both objects are kinematic Rigidbodies then OnCollisionEnter, etc, will not be called. With trigger collisions, this restriction doesn’t apply and so both kinematic and non-kinematic Rigidbodies will prompt a call to OnTriggerEnter when they enter a trigger collider.

为何Collider的Is Trigger的优先级最高?因为Collider是一切碰撞的基础。

例外:如果一个运动的物体A(通过改变Transform来使得它运动)和静止的物体B发生碰撞,A只有Collider,并且不是Is Trigger,B是Rigidbody,并且Collider也不是Is Trigger,那么任何回调函数都不回被触发,并且没有物理模拟碰撞效果,A会穿过B。但是如果A和B都是静止的,并且也开始就“挨着”,那么这个时候又会发生碰撞了,由于没有Is Trigger,所以是双方的OnCollisionEnter()被调用。

这个例外在官方文档被称为fail to sleep: 
https://docs.unity3d.com/Manual/RigidbodiesOverview.html 中关于Sleeping的第二段。

在官方文档中提到了static collider,所谓static collider,就是那些不和Rigidbody一起使用的Collider(相反的,那些和Rigidbody一起使用的Collider就叫做dynamic collider)。这些Collider一般用在静止的物体上,不要试图去直接改变他们Transform的position(不要使用Translate()方法或者直接给position赋值),因为这样会对物理引擎造成很大的性能影响。(参见:https://docs.unity3d.com/Manual/CollidersOverview.html)。CharacterController是没有额外加Collider的,而NavMeshAgent的运动是由Navigation System来掌管的,也不是属于我们说的情况。

拓展阅读(非常重要): 
https://docs.unity3d.com/Manual/CollidersOverview.html


  • 15
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
数据来源:中经数据库 主要指标110多个(全部都是纯粹的 市辖区 指标),大致是: GDP GDP增速 第一产业增加值占GDP比重 第二产业增加值占GDP比重 第三产业增加值占GDP比重 人均GDP 社会消费品零售总额 固定资产投资(不含农户) 新设外商投资企业数_外商直接投资 实际利用外资金额(美元) 一般公共预算收入 一般公共预算支出 一般公共预算支出_教育 一般公共预算支出_科学技术 金融机构人民币各项存款余额_个人储蓄存款 金融机构人民币各项存款余额 金融机构人民币各项贷款余额 规模以上工业企业单位数 规模以上工业企业单位数_内资企业 规模以上工业企业单位数_港澳台商投资企业 规模以上工业企业单位数_外商投资企业 规模以上工业总产值 规模以上工业总产值_内资企业 规模以上工业总产值_港澳台商投资企业 规模以上工业总产值_外商投资企业 规模以上工业企业流动资产合计 规模以上工业企业固定资产合计 规模以上工业企业利润总额 规模以上工业企业应交增值税 规模以上工业企业主营业务税金及附加 户籍人口数 年均户籍人口数 户籍人口自然增长率 第一产业就业人员占全部城镇单位就业人员比重 第二产业就业人员占全部城镇单位就业人员比重 第三产业就业人员占全部城镇单位就业人员比重 城镇非私营单位就业人员数 城镇非私营单位就业人员数_第一产业 城镇非私营单位就业人员数_第二产业 城镇非私营单位就业人员数_第三产业 城镇非私营单位就业人员数_农、林、牧、渔业 城镇非私营单位就业人员数_采矿业 城镇非私营单位就业人员数_制造业 城镇非私营单位就业人员数_电力、热力、燃气及水生产和供应业 城镇非私营单位就业人员数_建筑业 城镇非私营单位就业人员数_批发和零售业 城镇非私营单位就业人员数_交通运输、仓储和邮政业 城镇非私营单位就业人员数_住宿和餐饮业 城镇非私营单位就业人员数_信息传输、软件和信息技术服务业 城镇非私营单位就业人员数_金融业 城镇非私营单位就业人员数_房地产业 城镇非私营单位就业人员数_租赁和商务服务业 城镇非私营单位就业人员数_科学研究和技术服务业 城镇非私营单位就业人员数_水利、环境和公共设施管理业 城镇非私营单位就业人员数_居民服务、修理和其他服务业 城镇非私营单位就业人员数_教育 城镇非私营单位就业人员数_卫生和社会工作 城镇非私营单位就业人员数_文化、体育和娱乐业 城镇非私营单位就业人员数_公共管理、社会保障和社会组织 城镇非私营单位在岗职工平均人数 城镇就业人员数_私营企业和个体 城镇非私营单位在岗职工工资总额 城镇非私营单位在岗职工平均工资 城镇登记失业人员数 建成区面积 建设用地面积 建设用地面积_居住用地 液化石油气供气总量 液化石油气供气总量_居民家庭 人工煤气、天然气供气总量 人工煤气、天然气供气总量_居民家庭 液化石油气用气人口 人工煤气、天然气用气人口 城市公共汽电车运营车辆数 城市出租汽车运营车辆数 城市公共汽电车客运总量 道路面积 排水管道长度 建成区绿化覆盖面积 建成区绿化覆盖率 绿地面积 公园绿地面积 维护建设资金支出 土地面积 生活用水供水量 供水总量 全社会用电量 城乡居民生活用电量 工业生产用电量 房地产开发投资 房地产开发投资_住宅 限额以上批发和零售业法人单位数 限额以上批发和零售业商品销售总额 普通中学学校数 中等职业教育学校数 普通小学学校数 普通高等学校专任教师数 普通中学专任教师数 中等职业教育专任教师数 普通小学专任教师数 普通高等学校在校生数 普通中学在校生数 中等职业教育在校生数 普通小学在校生数 电视节目综合人口覆盖率 公共图书馆总藏量_图书 医疗卫生机构数_医院和卫生院 卫生人员数_执业(助理)医师 医疗卫生机构床位数_医院和卫生院 城镇职工基本养老保险参保人数 职工基本医疗保险参保人数 失业保险参保人数
### 回答1: 当然可以使用Unity编写一个移动的CharacterController。下面是一个基本的示例代码,可在Unity中创建一个名为"Player"的GameObject,然后将以下代码添加到该对象的脚本组件中,即可实现角色的移动: ``` using UnityEngine; public class PlayerController : MonoBehaviour { public float moveSpeed = 5.0f; // 移动速度 private CharacterController controller; // CharacterController组件 private Vector3 moveDirection = Vector3.zero; // 移动方向 void Start() { controller = GetComponent<CharacterController>(); // 获取CharacterController组件 } void Update() { float horizontal = Input.GetAxis("Horizontal"); // 获取水平方向上的输入 float vertical = Input.GetAxis("Vertical"); // 获取垂直方向上的输入 // 计算移动方向 moveDirection = new Vector3(horizontal, 0.0f, vertical); moveDirection = transform.TransformDirection(moveDirection); moveDirection *= moveSpeed; // 将移动方向应用到CharacterController组件上 controller.Move(moveDirection * Time.deltaTime); } } ``` 这个示例代码中,我们定义了一个名为“PlayerController”的脚本,该脚本会在每一帧中获取玩家的输入,然后根据输入计算出移动方向,并将移动方向应用到CharacterController组件上。注意,这里我们使用了Time.deltaTime来控制移动速度,这样可以保证在不同的帧率下,角色的移动速度保持一致。 ### 回答2: Unity是一款强大的游戏开发引擎,可以用来创建各种不同类型的游戏。编写一个移动的character controller可以让玩家在游戏中控制角色的移动。 在Unity中,编写一个character controller需要以下几个步骤: 1. 创建一个新的GameObject并将其重命名为"Player",用来表示角色。将该GameObject添加一个Capsule Collider组件,以便其他物体与角色互动碰撞。 2. 添加一个Rigidbody组件,以便在角色移动时受到物理引擎影响。确保将Rigidbody的"Use Gravity"选项禁用,这样角色就不会受到重力影响。 3. 创建一个C#脚本,并将其附加到Player对象上。在脚本中,声明一个变量来控制角色的移动速度,例如"moveSpeed"。 4. 在脚本中,使用Input.GetAxis()函数来获取玩家的输入,例如水平和垂直轴的输入。将输入乘以移动速度,得到角色在每个方向上的移动速度。 5. 使用Rigidbody组件的velocity属性来将计算得到的移动速度应用到角色上。通过调用Rigidbody的MovePosition()函数并传递角色的新位置,使角色移动。 6. 为了让角色在空中能跳跃,你可以添加一个额外的检测机制,检测角色是否与地面接触。当玩家按下跳跃键时,在脚本中添加一个适当的力量到角色的Y轴速度上。 7. 如果你还想要角色的旋,你可以使用Input.GetAxis()函数来获取玩家的向输入,并将其应用到角色上的Rigidbody组件的rotation属性上。 通过以上步骤,你就可以使用Unity编写一个简单的移动的character controller了。当你在游戏场景中运行游戏并玩家点击键盘上的WASD键时,角色就可以在场景中移动了。 ### 回答3: 使用Unity写一个移动的Character Controller可以通过以下步骤完成: 1. 创建一个新的Unity项目并导入所需的资源(例如角色模型、动画等)。 2. 创建一个空的Game Object作为角色控制器,并将其添加到场景中。 3. 添加一个Rigidbody组件到角色控制器上,用于处理物理运动。 4. 添加一个Capsule Collider组件到角色控制器上,以便处理碰撞。 5. 创建脚本文件并将其附加到角色控制器上。 6. 在脚本中,定义角色的移动速度、旋速度等变量,并使用Input.GetAxis函数获取玩家输入。 7. 在Update函数中,根据玩家输入控制角色的移动和旋。 8. 使用Rigidbody的AddForce函数将力应用于角色控制器,以使其移动。 9. 在角色控制器的脚本中,可以添加额外的功能,例如跳跃、攻击等。 10. 测试和调试角色控制器,确保角色能够按照预期移动和旋。 11. 可以根据需要添加更多功能,例如动画、摄像机跟随等。 通过以上步骤,你可以使用Unity编写一个移动的Character Controller,并通过脚本控制角色的移动和旋

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值