Unity碰撞检测(射线投射、胶囊体投射)、交互(图层、掩码)

发布于:2025-05-18 ⋅ 阅读:(20) ⋅ 点赞:(0)

一、碰撞检测

射线投射(Raycasting):

射线投射是一种技术,通过在计算机图形和游戏中发射一条虚拟射线来检测与物体的交点。

特点:

1. 直线检测:射线是直线,只检测与直线相交的物体。

2. 简单快速:实现简单,计算速度快。

3. 单点相交:通常用来获取射线与物体的第一个交点信息。

应用场景:

1.   点击检测 :通过鼠标点击或触摸屏操作来选择物体或触发事件。

2.   视线检测 :模拟角色视线,用于目标锁定或观察。

3.   射击和投射物 :模拟子弹或箭矢的轨迹,检测碰撞。

胶囊体投射(Capsule Casting):

胶囊体投射是一种技术,模拟一个具有圆柱形主体和两个半球形端点的胶囊体在空间中的移动,并检测它与其他物体的碰撞。

特点:

 1. 形状检测:模拟更符合角色或物体真实形状的碰撞检测。

 2. 高度宽度:  考虑了物体的高度和宽度,比射线投射更精确。

 3. 路径检测:通常用来检测物体移动路径上的碰撞。

应用场景:

1.   角色控制器:在角色移动时检测路径上的障碍物,防止穿墙或碰撞。

2.   平台检测:检测角色是否能站在平台上,常用于平台跳跃游戏。

3.   物理模拟:模拟物体在空间中的碰撞,用于物理交互游戏。

---------------------------------------------------------------------------------------------------------------------------------

通过发射射线检测看看是否有任何物体挡在路上

(射线检测是一种物理检测,它从某个点向某个方向发射一条激光来检测是否击中了某个物体)

//处理玩家角色的移动,同时检查移动路径上是否有障碍物
float moveDistance=moveSpeed*Time.deltaTime;
float playerRadius = 0.7f;
float playerHeight = 2f;
//使用Physics.CapsuleCast进行胶囊检测,检查玩家是否可以在指定的方向和距离内移动而不碰到任何障碍物
bool canMove=!Physics.CapsuleCast(transform.position,transform.position+Vector3.up*playerHeight, playerRadius,moveDir,moveDistance);
if (canMove)
{
    //更新玩家位置
    transform.position += moveDir * moveSpeed * Time.deltaTime;
}

 在两个正交方向上移动玩家

if (!canMove)
{
    Vector3 moveDirX = new Vector3(moveDir.x, 0, 0);
    //使用Physics.CapsuleCast在X轴方向上进行胶囊检测,检查玩家是否可以在该方向上移动
    canMove = !Physics.CapsuleCast(transform.position, transform.position + Vector3.up * playerHeight,playerRadius, moveDirX, moveDistance);

    if (canMove)
    {
        moveDir = moveDirX.normalized;
    }else
    {
        Vector3 moveDirZ=new Vector3(0, 0,moveDir.z);
        //使用Physics.CapsuleCast在Z轴方向上进行胶囊检测,检查玩家是否可以在该方向上移动
        canMove = !Physics.CapsuleCast(transform.position, transform.position + Vector3.up * playerHeight,
        playerRadius, moveDirZ, moveDistance);
        if (canMove)
        {
            moveDir = moveDirZ.normalized;
        }
    }
}

二、交互

图层(Layer):

在Unity中,图层是用来对游戏对象进行分类的系统。每个图层可以包含多个游戏对象,这些对象可以是场景中的任何元素,如角色、敌人、背景、UI元素等。可以为每个游戏对象指定一个图层,也可以创建新的图层来满足特定的需求。

图层的特点:

1. 组织性:图层可以帮助你组织和管理复杂的场景,使得查找和操作特定类型的游戏对象更加容易。

2. 控制性:通过图层,可以控制游戏对象之间的交互,例如碰撞检测、射线投射等。

3. 灵活性:可以创建任意数量的图层,并且可以轻松地改变游戏对象的图层分配。

图层的应用场景:

1. 碰撞检测:可以设置物理碰撞检测,使得只有特定图层的对象之间可以发生碰撞。

2. 射线投射:使用射线投射时,你可以指定只检测特定图层的对象。

3. 渲染控制:可以为不同图层的对象设置不同的渲染设置,如光照、阴影等。

4. UI管理:UI元素通常放在单独的图层中,以便于管理和控制它们的渲染顺序和行为。

5. 脚本控制:可以在脚本中根据对象的图层来执行不同的逻辑。

层掩码(Layer Mask):

层掩码是一种基于图层的过滤工具。用来选择性地包含或排除这些图层,以控制游戏对象之间的交互,如碰撞检测、射线投射等。

层掩码的特点:

1. 选择性交互:通过层掩码,可以指定哪些图层的对象应该参与交互,哪些应该被忽略。

2. 位操作:层掩码使用位运算来快速确定哪些图层被选中,这使得它非常高效。

3. 灵活性:可以动态地改变层掩码,以适应不同的游戏逻辑和交互需求。

应用场景:

1. 碰撞检测:在物理引擎中,可以使用层掩码来指定哪些图层的对象应该发生碰撞。

2. 射线投射:在射线投射中,层掩码可以用来过滤射线检测的结果,只关注特定的图层。

3. 触发器:在触发器事件中,层掩码可以用来决定哪些图层的对象可以触发事件。

4. UI交互:在用户界面交互中,层掩码可以用来控制哪些图层的对象可以响应用户的点击。

// 假设有两个图层,分别命名为 "Layer1" 和 "Layer2"

// 创建一个层掩码,只包含 "Layer1"
LayerMask layerMask1 = LayerMask.GetMask("Layer1");

// 创建一个层掩码,包含 "Layer1" 和 "Layer2"
LayerMask layerMask2 = LayerMask.GetMask("Layer1", "Layer2");

// 使用层掩码进行射线检测
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

// 只检测 "Layer1"
if (Physics.Raycast(ray, out hit, Mathf.Infinity, layerMask1)) {
    // 检测到 "Layer1" 中的物体
}

// 检测 "Layer1" 或 "Layer2"
if (Physics.Raycast(ray, out hit, Mathf.Infinity, layerMask2)) {
    // 检测到 "Layer1" 或 "Layer2" 中的物体
}

位掩码(Bit Mask):

位掩码是一种使用二进制位(0或1)来表示一组状态。每个位代表一个状态,0表示关闭(或不包括),1表示打开(或包括)。在Unity中,每个层都有一个唯一的整数ID,这个ID对应一个位。通过设置或清除这些位,你可以指定哪些层应该被包含在操作中。

位掩码的特点:

1. 高效性:位掩码使用二进制位来表示状态,非常节省内存和计算资源。

2. 灵活性:可以通过位运算(如AND、OR、NOT等)来组合和操作位掩码。

3. 可扩展性:可以轻松地添加或删除状态,只需修改位掩码的值。

应用场景:

1. 图层控制:在Unity中,每个图层都有一个对应的位掩码值。通过设置位掩码,可以控制哪些图层的对象应该参与交互。

2. 权限管理:在软件系统中,可以使用位掩码来表示用户的权限,每个位代表一种权限。

3. 状态管理:在游戏开发中,可以使用位掩码来表示游戏对象的状态,如是否可见、是否可交互等。

// 定义一些状态
const int STATE_VISIBLE = 1;       // 0001
const int STATE_INTERACTABLE = 2;  // 0010
const int STATE_PICKABLE = 4;      // 0100

// 创建一个位掩码,包含所有状态
int objectState = STATE_VISIBLE | STATE_INTERACTABLE | STATE_PICKABLE; // 0111

// 检查对象的状态
bool isVisible = (objectState & STATE_VISIBLE) != 0;       // true
bool isInteractable = (objectState & STATE_INTERACTABLE) != 0; // true
bool isPickable = (objectState & STATE_PICKABLE) != 0;      // true

// 修改对象的状态
objectState &= ~STATE_PICKABLE; // 移除 PICKABLE 状态,结果为 0011

------------------------------------------------------------------------------------------------------------------------------

使用射线投射,目标是任何具有物理碰撞器的物体,因此可能是我们的目标柜台,但如果柜台前面有其他物体时,下面这段代码将无法工作,这是因为射线投射和射线命中只会返回它击中的第一个物体,所以如果柜台在其他物体的后面,这里的射线命中对象将会是其他物体的引用,如果想要射线投射目标柜台,可以使用层掩码

float interacDistance = 2f;
if(Physics.Raycast(transform.position, moveDir,out RaycastHit raycastHit, interacDistance))
{ 
    if(raycastHit.transform.TryGetComponent(out ClearCounter clearCounter))
    {
        //有clearCounter,就与之交互
        clearCounter.Interact();
    }
}

可以将某个游戏对象设置为特定的层,使用层掩码可以确保这个射线投射只会击中该层内的对象,任何不在该层内的对象都会被忽略。

float interacDistance = 2f;
if(Physics.Raycast(transform.position, lastInteracDir,out RaycastHit raycastHit, interacDistance,countersLayerMask))
{ 
    if(raycastHit.transform.TryGetComponent(out ClearCounter clearCounter))
    {
        //有clearCounter
        clearCounter.Interact();
    }
}

进入柜台的预制体添加并修改图层为Counters柜台

交互输入动作 

当玩家按下E键时才会进行交互

    private void Awake()
    {
        playerInputActions = new PlayerInputActions();
        playerInputActions.Player.Enable();
        //设置一个事件监听器,当玩家执行“Interact”动作时(按下E键),触发Interact_performed方法
        playerInputActions.Player.Interact.performed += Interact_performed;
    }
    
    private void Interact_performed(UnityEngine.InputSystem.InputAction.CallbackContext obj)
    {
        //如果OnInteractAction事件不为空(即有订阅者),则触发该事件
        OnInteractAction?.Invoke(this,EventArgs.Empty);
    }


网站公告

今日签到

点亮在社区的每一天
去签到