Unity行人随机行走不碰撞
Posted strawberry47
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity行人随机行走不碰撞相关的知识,希望对你有一定的参考价值。
目录
1.基础知识:
1.1 刚体RigidBody
Unity零基础到入门 ☀️|刚体(RigidBody)
当一个游戏对象被赋予RigidBody组件之后,游戏引擎就会对其进行物理效果的计算和模拟。同时我们也可以给这个对象施加各种作用力,让它运动起来。会受到重力影响。
属性:Mass(质量)、Drag(阻力)、Angular Drag(角阻力)、Use Gravity(是否使用重力)、Is Kinematic(是否受物理影响)、Collision Detection(碰撞检测)等。
注意:刚体对于系统的开销是很大的,所以在使用刚体时,根据可能发生的碰撞触发事件,适当的减少刚体,是一个减少资源消耗的好办法。 比如地面就可以不设置刚体,因为地面是永远不动的,把人物设置刚体就可以实现真实的物理碰撞效果了。
1.2 碰撞体Collider
Unity零基础到入门 ☀️| Collider (物体碰撞器)
Collider (物体碰撞器)碰撞器是Unity引擎为模拟物理效果的组件功能:可以让两个带有碰撞器的游戏对象相互接触的时候不会穿透过去,会根据物理规则运动。
需要定义物理形状,是隐形的,并非模型的形状哦!相当于检测碰撞范围。可以用多种形状组合碰撞体。
不需要动起来的物体(地板、墙壁)都可以设置为Collider,可以阻止物体穿过。不过,创建基础模型是自带collider的。
1.3 二者区别
- Collider只是给物体加上了碰撞,让物体拥有了实体,而不是一个可以穿过去‘全息投影’。而Rigidbody是让物体拥有了动态受力的功能。
- 不需要动起来的物体,只需要阻挡别的物体别穿过他们就行了,所以它们只需要添加Collider就行。因为添加了Rigidbody组件后会带来很多的物理计算,所以只需要给会移动的物体添加Rigidbody就行。
1.4 触发碰撞条件
产生碰撞的条件是:
① 2 个游戏对象都有 Collider
② 至少有一个游戏对象有 Rigidbody
③ 2 个游戏对象保持相对运动(一个 Cube 放在 Plane 上,不会产生碰撞,因为没有相对运动)
有两种回调方法,碰撞器回调OnCollisionEnter和触发器回调OnTriggerEnter:
碰撞器回调方法:
// 碰撞开始
void OnCollisionEnter(Collision other)
// 碰撞过程中,每帧调用一次
void OnCollisionStay(Collision other)
// 碰撞结束
void OnCollisionExit(Collision other)
触发器回调方法:
// 触发开始
void OnTriggerEnter(Collider other)
// 触发过程中,每帧调用一次
void void OnTriggerStay(Collider other)
// 触发结束
void OnTriggerExit(Collider other)
当A,B都添加刚体(Rigidbody)时:
- OnCollisionEnter方法:A和B相互碰撞时,无论是谁碰撞的谁,两者都能触发OnCollisionEnter方法,前提是两者都没有勾选isTrigger。
- OnTriggerEnter方法:A或者B中有一个勾选isTrigger或者两者都勾选isTrigger后,A和B都可以触发OnTriggerEnter方法,但是不可进入OnCollisionEnter方法。
当A,B有一个添加了刚体(Rigidbody)时:
- OnCollisionEnter方法:① 若A添加了刚体,B没有添加刚体,A去碰撞B,则A会被弹开,B不会运动,此时A、B都会触发OnCollisionEnter方法。② 若A添加了刚体,B没有添加刚体,B去碰撞A,不会发生碰撞效果,此时A和B都不会触发OnCollisionEnter方法。
- OnTriggerEnter方法:只要A和B中有一个添加了刚体,无论谁碰撞谁,两者都会触发 OnTriggerEnter方法。
总结:
- OnCollisionEnter方法要求碰撞的发起方必须拥有刚体,而被碰撞方有没有刚体并不重要。OnTriggerEnter方法则对此没有要求,只需要碰撞双方有一个具有刚体即可触发。即刚体是一个判断是否实现碰撞的是与否的标志
- 如果想实现两个刚体物理的实际碰撞效果时候用OnCollisionEnter,Unity引擎会自动处理刚体碰撞的效果。如果想在两个物体碰撞后自己处理碰撞事件用OnTriggerEnter。
2. 思路:
组件:人群都需要添加Rigidbody,Collider组件;其他障碍物添加Collider组件。
2.1 法一:自己编写规则
碰撞时触发OnTriggerEnter方法,需要自己编写避让规则。一般规则是销毁其中一方,或者反弹。暂时没找到避让方法代码,有点难度。
2.1 法二:碰撞规避
将行人对象的Layer设置为相同层级,再调整碰撞矩阵,就会互相不碰撞了。这里的不碰撞并不是“避障”,只是不检测碰撞了。
Unity碰撞检测 视频, unity 设置layer 使碰撞器不碰撞
也可以通过 勾选isTrigger属性,并在OnTriggerEnter函数中判断相撞对象是否是行人,是的话就不采取任何操作 来实现不碰撞的效果。but 这种方法需要检测碰撞,再执行回调函数,消耗更大。
这类方法很简单,可以作为保底方法。
2.3 法三:避障算法
运用避障算法实现:
- unity 简易雷达避障效果实现,扩展到行人场景有点困难,不太推荐这种方法。
- Unity RVO动态避障实战系列【1】,避让效果非常贴近现实场景,给了代码。推荐尝试!
关于Unity中ARPG游戏人物移动
ARPG:动作型角色扮演类游戏
大多数的ARPG游戏都是使用摇杆操作,以第三人称摄像机的方式来跟随主角,实际上人物只走八个方向,上,下,左,右,左上,左下,右下,右上
控制角色移动的思路
1: 在ARPG游戏中,主角人物在摇杆下控制行走;
2: 主角人物遇到障碍物(碰撞器)将不会穿越过去;
3: 摇杆控制主角人物8个方向的行走;
4: 使用CharacterController 角色控制器组件: 让你在受制于碰撞的情况下很容易的进行运动,而不用处理刚体,实际上没有刚体的物理特性。角色控制器不受力的影响,仅当你调用Move函数时才运动。它执行运动,但是受制于其他碰撞器。
本来以前都是在角色上面挂载刚体(用里面的重力)和碰撞器组件,如果碰到其他的刚体还会受力会受到一些不好的影响,用了CharacterController 就不会有这种不相关的物理力的影响了。
5: 调用角色控制器的Move函数移动角色;
6: 根据摇杆的方向旋转人物动画;
CharacterController组件
1: 属性面板属性:
Slope Limit: 角色碰撞器只能爬比这个指定角度低的斜坡:(单位是degree)
Step Offset: 上楼梯模式,小于Step Offset 的台阶,可以直接上去;
Skin Width: 两个碰撞器可以互相渗透深入皮肤宽度,一般设置成radius的10%;
Min Move Distance: 调用Move函数移动的最小移动量,如果移动距离比这个小,将不移动;
center: 相对与transform的位置角色叫胶囊体中心;
height: 胶囊体高度;
Radius: 胶囊体的半径;
2: 碰撞检测:
void OnControllerColliderHit(ControllerColliderHit hit) {},和一般的碰撞器一样。有碰撞后会调用这个接口。只会在和其他带有CharacterController组件的物体发生碰撞时才调用。
目前已知这个角色控制器的碰撞接口只有这个,如果要持续碰撞,我会加一个BoxCollider组件或者Capsule之类的碰撞器,调用三个碰撞接口
3: 重要方法:
Move(Vec3 offset): 移动的距离;
如果一个角色挂载了CharacterController组件。那么要控制这个角色的移动,其实不必改变角色节点的位置,只需对这个组件进行Move操作,角色就会跟着走。
我把这个组件理解为一个可以牵动节点的可以设置特殊移动属性的胶囊体碰撞器组件Capsule Collider
遥杆编写的基本思路与原理
1:以8个方向为例,将整个圆分为 下,右下,右,右上,上,左上, 左, 左下;
2:当我们的遥感中心点位于某一个方向的范围内,那么就属于这个方向;
3.排除一个无效的摇杆区域,在这个区域内主角不会跟着移动
4.角色移动的距离是一个标量,等于速度乘于时间,而角色移动的整个行为是一个向量,所以还要考虑方向,获得摇杆的角度得知运动方向后还要把角色到最终目的地的标量距离分解成X和Z轴方向上的距离。
根据摇杆的方向算出当前距离所对应的向量的分解的系数
5.前面都是摇杆的坐标轴解析,这里是从人物的Y轴的角度看各个欧拉角,也就是编辑器上的Transform组件的Rotation里面的Y的值对应的该主角节点的旋转方向角度,0度是角色面朝前方
实例
1.创建Unity工程项目和文件目录
2.导入人物模型资源和地图资源,以及摇杆包(79)
3.人物模型的材质球shader使用Mobile Diffuse,关联好贴图,设置模型---->Rig---->Animation Type---->Legacy---->Apply
4.在人物模型的Animation里添加跑动的动画,136-161帧是跑动的帧,Wrap Mode---->Loop---->Apply,角色配置完毕,拖进场景中
5.创建一个平面Plane,关联材质,放大10倍
6.不需要添加碰撞器,只要给主角添加CharacterController组件,调整组件的胶囊体到完全盖住主角,它有碰撞区域但是不受力的影响,千万别再加Rigidbody组件了,会使得CharacterController组件无效。
7.添加摇杆,Hedgehog Team---->Easy Touch---->Add Easy Touch For C#,Hedgehog Team---->Easy Touch---->Extension---->Adding a new joystick
8.调整摄像机的位置,到可以看见主角运动的最佳位置
9.在主角下面挂载一个脚本Person来通过摇杆控制角色移动
Person.cs
using System.Collections; using System.Collections.Generic; using UnityEngine; enum DIR//定义一个枚举来区分角色移动的八个方向 { INVALID_DIR = -1, UP = 0, DOWN = 1, LEFT = 2, RIGHT = 3, RU = 4, LU = 5, LD = 6, RD = 7, } public class person : MonoBehaviour { float move_speed = 8.0f;//角色移动速度 CharacterController c_ctrl;//角色控制器组件 public EasyJoystick joystick;//摇杆 float[] x_set;//八个方向在X轴上的分解系数 float[] z_set;//八个方向在Z轴上的分解系数 float[] rot_set;//人物面的朝向的角度表 Vector3 camera_offset; // Use this for initialization void Start() { this.c_ctrl = this.GetComponent<CharacterController>(); //按照上,下,左,右,右上,左上,左下,右下的顺序配置 this.x_set = new float[8] { 0, 0, -1, 1, 0.707f, -0.707f, -0.707f, 0.707f };//cos45=0.707,sin45=0.707 this.z_set = new float[8] { 1, -1, 0, 0, 0.707f, 0.707f, -0.707f, -0.707f }; this.rot_set = new float[8] { 0, 180, -90, 90, 45, -45, -135, 135 }; this.camera_offset = Camera.main.transform.position - this.transform.position;//获取当前摄像机和人物的三维距离 } int get_dir(float r) { if (r >= -Mathf.PI && r < -7 * Mathf.PI / 8) { // 左的一部分 return (int)DIR.LEFT; } else if (r >= -7 * Mathf.PI / 8 && r < -5 * Mathf.PI / 8) {//左下 return (int)DIR.LD; } else if (r >= -5 * Mathf.PI / 8 && r < -3 * Mathf.PI / 8) {//下 return (int)DIR.DOWN; } else if (r >= -3 * Mathf.PI / 8 && r < -1 * Mathf.PI / 8) {//右下 return (int)DIR.RD; } else if (r >= -1 * Mathf.PI / 8 && r < 1 * Mathf.PI / 8) {//右 return (int)DIR.RIGHT; } else if (r >= 1 * Mathf.PI / 8 && r < 3 * Mathf.PI / 8) {//右上 return (int)DIR.RU; } else if (r >= 3 * Mathf.PI / 8 && r < 5 * Mathf.PI / 8) {//上 return (int)DIR.UP; } else if (r >= 5 * Mathf.PI / 8 && r < 7 * Mathf.PI / 8) {//左上 return (int)DIR.LU; } else if (r >= 7 * Mathf.PI / 8 && r < 8 * Mathf.PI / 8) {//左的另一部分 return (int)DIR.LEFT; } return (int)DIR.INVALID_DIR;//无效的区域 } void walk_update() { float x = this.joystick.JoystickTouch.x;//摇杆坐标系的X坐标 float y = this.joystick.JoystickTouch.y;//摇杆坐标系的Y坐标 float len = (x * x + y * y);//不开根号是因为开销太大,而且是在Update里面,每帧都开根号受不了 if (len < (0.5f * 0.5f))//摇杆移动到这片区域是无效的 { return; } // 获取这个方向 float r = Mathf.Atan2(y, x); // 使用反三角函数, 获取向量的角度, [-PI, PI] int dir = this.get_dir(r); if (dir != (int)DIR.INVALID_DIR) { float s = this.move_speed * Time.deltaTime;//每一秒要移动的距离 Vector3 offset = new Vector3(s * this.x_set[dir], 0, s * this.z_set[dir]);//把这个距离分解到X和Z方向上 this.c_ctrl.Move(offset);//每一帧都移动 // 切换人物行走的朝向 Vector3 e_rot = this.transform.eulerAngles; e_rot.y = this.rot_set[dir]; this.transform.eulerAngles = e_rot; // end } // end } // Update is called once per frame void Update() { this.walk_update(); Camera.main.transform.position = this.transform.position + this.camera_offset;//保持人物和摄像机的距离不变 } }
10.运行效果
以上是关于Unity行人随机行走不碰撞的主要内容,如果未能解决你的问题,请参考以下文章