墙和物体都要加上collider(碰撞体),一般注意以下几点:
- 墙加mesh collider,在Convex上打勾
- 物体加box collider
- 加刚体(rigidbody)的物体不要选Is Kenimatic
基础 最简单的Raycast方法
void Update()
{
RaycastHit hitInfo;
Vector3 fwd = Camera.main.transform.TransformDirection(Vector3.forward);
if (Physics.Raycast(Camera.main.transform.position, fwd, out hitInfo, 0.5f))
{
float dis = hitInfo.distance;
Vector3 correction = Vector3.Normalize(Camera.main.transform.TransformDirection(Vector3.back)) * dis;
Camera.main.transform.position += correction;
}
}
上面的代码,在撞墙时会有抖动的效果,如果要平滑后退,可以把
Vector3.Normalize(Camera.main.transform.TransformDirection(Vector3.back)) * dis;
改为:
Vector3.Normalize(Camera.main.transform.TransformDirection(Vector3.back)) * dis * Time.deltaTime;
但是,这样可能会出现“移动物体过快导致穿墙”的问题。
关于 TransformDirection 参考资料: Unity TransformPoint、InverseTransformPoint、TransformDirection
Vector3 worldPosition = transform.TransformPoint ( Vector3 relativePosition )
将相对 “当前游戏对象” 的坐标转化为基于世界坐标系的坐标
Vector3 relativePosition = transform.InverseTransformPoint ( Vector3 worldPosition )
将世界坐标转化为相对 “当前游戏对象” 的基于世界坐标系的坐标
Vector3 worldDirection = Transform.TransformDirection ( Vector3 relativeDirection )
将相对 “当前游戏对象” 的方向转化为基于世界坐标系的方向(不受父对象缩放的影响)
进阶1 四个方向Raycast
在上一种方法中,显然,只有一个方向(前方)能不穿墙,没有考虑如下情况:
- 摄像机倒着走,从背后穿墙
- 摄像机横着向左/右走,从左/右穿墙
这里用了一个比较蠢的方法,就是往前、后、左、右四个方向投射线,作四次检测。把这个检测过程封装成函数detect()。
Vector3.forward实际是一个固定值,unity 内置的vector3还有以下一些方向:
参考资料 Unity - Scripting API: Vector3
back | Shorthand for writing Vector3(0, 0, -1). |
down | Shorthand for writing Vector3(0, -1, 0). |
forward | Shorthand for writing Vector3(0, 0, 1). |
left | Shorthand for writing Vector3(-1, 0, 0). |
negativeInfinity | Shorthand for writing Vector3(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity). |
one | Shorthand for writing Vector3(1, 1, 1). |
positiveInfinity | Shorthand for writing Vector3(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity). |
right | Shorthand for writing Vector3(1, 0, 0). |
up | Shorthand for writing Vector3(0, 1, 0). |
zero | Shorthand for writing Vector3(0, 0, 0). |
void detect(Vector3 dir, Vector3 inverse_dir)
{
RaycastHit hitInfo;
Vector3 _dir = Camera.main.transform.TransformDirection(dir);
if (Physics.Raycast(Camera.main.transform.position, _dir, out hitInfo, 0.5f))
{
float dis = hitInfo.distance;
Vector3 correction = Vector3.Normalize(Camera.main.transform.TransformDirection(inverse_dir)) * dis;
Camera.main.transform.position += correction;
}
}
// Update is called once per frame
void Update()
{
detect(Vector3.forward, Vector3.back);
detect(Vector3.back, Vector3.forward);
detect(Vector3.left, Vector3.right);
detect(Vector3.right, Vector3.left);
}
进阶2 SphereCast
这里,射线只有一个像素点,尽管已经覆盖四个方向,但是可能还是会有疏漏的情况。可以用Physics.SphereCast()方法。
可参考下列指南:http://www.ceeger.com/Script/Physics/Physics.SphereCast.html
void detect(Vector3 dir, Vector3 inverse_dir)
{
RaycastHit hitInfo;
Vector3 _dir = Camera.main.transform.TransformDirection(dir);
if (Physics.SphereCast(Camera.main.transform.position, 0.5f, _dir, out hitInfo, 0.5f))
{
float dis = hitInfo.distance;
Vector3 correction = Vector3.Normalize(Camera.main.transform.TransformDirection(inverse_dir)) * dis;
Camera.main.transform.position += correction;
}
}
// Update is called once per frame
void Update()
{
detect(Vector3.forward, Vector3.back);
detect(Vector3.back, Vector3.forward);
detect(Vector3.left, Vector3.right);
detect(Vector3.right, Vector3.left);
}
进阶3 层遮罩LayerMask
某些碰撞体(collider) 可能只被用作trigger,但是却受到了碰撞检测。
一个比较简单的方法:
把物体放置到 Ignore Raycast层
或者用LayerMask,以下为示例:
void detect(Vector3 dir, Vector3 inverse_dir)
{
RaycastHit hitInfo;
Vector3 _dir = Camera.main.transform.TransformDirection(dir);
LayerMask _ignoreLayer = 1 << LayerMask.NameToLayer("mylayer");
if (Physics.Raycast(Camera.main.transform.position, _dir, out hitInfo, 0.5f, _ignoreLayer))
{
float dis = hitInfo.distance;
Vector3 correction = Vector3.Normalize(Camera.main.transform.TransformDirection(inverse_dir)) * dis;
Camera.main.transform.position += correction;
}
}
更详细资料可参考:U3D开发学习之路--RayCast中layerMask的使用