始、前言
简单的角色移动已经完成了,这次来学习用Raycast 射线检测的方法让角色的状态检测变得更细致
一、Physics2D.Raycast 脚本API
1
| public static RaycastHit2D Raycast (Vector2 origin, Vector2 direction, float distance= Mathf.Infinity, int layerMask= DefaultRaycastLayers, float minDepth= -Mathf.Infinity, float maxDepth= Mathf.Infinity);
|
参数
origin |
射线在 2D 空间中的起点。 |
direction |
表示射线方向的矢量。 |
distance |
射线的最大投射距离。 |
layerMask |
过滤器,用于仅在特定层上检测碰撞体。 |
minDepth |
仅包括 Z 坐标(深度)大于或等于该值的对象。 |
maxDepth |
仅包括 Z 坐标(深度)小于或等于该值的对象。 |
返回**RaycastHit2D
** 返回的投射数量。
描述:
向场景中的碰撞体投射射线。
从概念上说,射线投射 类似于从空间中的某个点朝特定方向发射一条光束。在该过程中,可以检测并报告与光束接触的任何对象。
函数返回一个 RaycastHit
对象,该对象引用了射线命中的碰撞体(如果未命中任何对象,则结果的碰撞体属性将为 NULL)。*layerMask
* 可用于仅在特定层上有选择地检测对象(例如,这让您能够仅将检测应用于敌人角色)。
使用 contactFilter
的此方法重载可以按 ContactFilter2D
中提供的选项筛选结果。
对于确定视线、炮火击中的目标以及游戏中的许多其他目的来说,射线投射很有用。
此外,这还将检测位于射线起点的碰撞体。在这种情况下,射线从碰撞体内部开始,并且不与碰撞体表面交叠。这意味着无法计算碰撞法线,在这种情况下,返回的碰撞法线设置为正在测试的射线向量的倒数。这可轻松检测到,因为此类结果始终是 RaycastHit2D
分数为零。
二、脚部、头顶碰撞
- 在
[Header("环境检测")]
新建几个需要用到的变量
1 2 3 4 5
| [Header("环境检测")] public LayerMask groundLayer; public float footOffset = 0.35f; public float headClearance = 0.5f; public float groundDistance = 0.2f;
|
2.重写一个射线检测的方法,包含射线可视化
1 2 3 4 5 6 7
| RaycastHit2D Raycast(Vector2 offset, Vector2 rayDirection, float length, LayerMask layer)//自己封装的射线检测方法 { Vector2 pos = transform.position; RaycastHit2D hit = Physics2D.Raycast(pos + offset, rayDirection, length, layer); Debug.DrawRay(pos + offset, rayDirection, hit ? Color.red : Color.green); return hit; }
|
3.更改地面检测函数PhysicsCheck()
1 2 3 4 5 6 7
| void PhysicsCheck()//检测是否在地面,并修改状态 { RaycastHit2D leftCheck = Raycast(new Vector2(-footOffset, 0.2f), Vector2.down, groundDistance, groundLayer); RaycastHit2D rightCheck = Raycast(new Vector2(footOffset, 0.2f), Vector2.down, groundDistance, groundLayer); isOnGround = leftCheck || rightCheck;
}
|
保存回到unity,运行
角色碰撞体左右脚下方都出现了射线,并且会按照射线方向检测是否碰撞到地面,以此改变isOnGround
,可以调整参数让检测距离更 “舒服”
下面来增加头顶的检测
4.在[Header("角色状态")]
中增加头顶状态 public bool isHeadBlocked;
5.修改PhysicsCheck()
:
1 2 3 4 5 6 7 8 9
| void PhysicsCheck()//检测是否在地面,并修改状态 { RaycastHit2D leftCheck = Raycast(new Vector2(-footOffset, 0.2f), Vector2.down, groundDistance, groundLayer); RaycastHit2D rightCheck = Raycast(new Vector2(footOffset, 0.2f), Vector2.down, groundDistance, groundLayer); isOnGround = leftCheck || rightCheck;
RaycastHit2D headCheck = Raycast(new Vector2(0f, cd.size.y), Vector2.up, headClearance, groundLayer); isHeadBlocked = headCheck; }
|
同样,修改public
参数就可以调整检测距离(注意射线的长度不等于检测距离)
三、悬挂检测
判断悬挂需要三条射线,分别是头顶射线,眼部射线,垂直射线。
悬挂在边缘的条件:头顶射线无碰撞,眼部射线有碰撞,垂直射线有碰撞
1.在[Header("环境检测")]
添加变量:
1 2 3 4 5 6
| [Header("环境检测")] //悬挂 public float playerHeight = 2f; public float playerEyeHeight = 1.5f; public float grabDistance = 0.2f; public float reachOffset = 0.5f;
|
2.在[Header("角色状态")]
添加状态:
1 2
| [Header("角色状态")] public bool isHanging;
|
3.在PhysicsCheck()
中添加悬挂的各种射线生成以及悬挂状态判断:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| void PhysicsCheck()//物理检测,并修改状态 { //悬挂系列射线 float direction = transform.localScale.x; Vector2 grabDirection = new Vector2(direction, 0f); RaycastHit2D blockedCheck = Raycast(new Vector2(direction * footOffset, playerHeight), grabDirection, grabDistance, groundLayer); RaycastHit2D wallCheck = Raycast(new Vector2(direction * footOffset, playerEyeHeight), grabDirection, grabDistance, groundLayer); RaycastHit2D ledgeCheck = Raycast(new Vector2(direction * reachOffset, playerHeight), Vector2.down, grabDistance, groundLayer); if (!isOnGround && !blockedCheck && wallCheck && ledgeCheck && rb.velocity.y < 0f) { Vector3 pos = transform.position; pos.x += (wallCheck.distance - 0.05f) * direction; pos.y -= ledgeCheck.distance; transform.position = pos; rb.bodyType = RigidbodyType2D.Static; isHanging = true; } }
|
其中pos.x += (wallCheck.distance - 0.05f) * direction;
稍微调整一下悬挂的位置,可以按照图案的位置来调整
4.悬挂状态已经设置好了,现在我们希望在悬挂时按跳跃键会有一个固定的跳跃高度,并且会取消刚体的锁定
在[Header("跳跃参数")]
中添加:
1 2
| [Header("跳跃参数")] public float hangingJumpForce = 15f;
|
5.在MidAirMovement()
添加悬挂跳跃:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| void MidAirMovement()//空中移动 { if (isHanging)//悬挂时两种选择 { if (jumpPressed) { rb.bodyType = RigidbodyType2D.Dynamic; rb.AddForce(new Vector2(0f, hangingJumpForce), ForceMode2D.Impulse); //rb.velocity = new Vector2(rb.velocity.x, hangingJumpForce); isHanging = false; } if (crouchPressed) { rb.bodyType = RigidbodyType2D.Dynamic; crouchPressed = false; isHanging = false; } } }
|
其中crouchPressed
要在[Header("按键状态")]
中设置好,并且在Update()
中赋值,以此来和crouchHeld
分开
终、阶段总结
到此为止,已经完成了各种基础运动的逻辑
自我感觉有些状态的判断没有很好的归类,比如悬挂状态,应该和下蹲&站立一样新建一个悬挂的函数,在悬挂函数中完成悬挂的各种处理,最后使isHanging = true
,这样代码应该会比较 ”整齐”