Unity学习笔记(三)如何实现角色移动、跳跃、冲刺

    前言

    本文为Udemy课程The Ultimate Guide to Creating an RPG Game in Unity学习笔记

    如何实现角色的移动

    首先实现角色移动的动画,动画的实现过程在第二篇,这里仅展示效果

    接下来就要写C#的脚本

    脚本放在Player模块下,因为这个脚本控制整个对象。

    我们需要在 Animator 上编辑动画之间的过渡,以及检测是否移动(isMoving)

    关闭过渡持续时间,因为有过渡时间会出现按键出现延迟


    初始参数: move speed = 7

    实现效果如下

    这里有一个bug:

    • 角色在奔跑中会摔倒

    冻结 Z 轴即可

    代码如下

    SQL 复制代码
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class Player2 : MonoBehaviour
    {
        protected Rigidbody2D rb;
        protected Animator anim;
    
        protected int facingDir = 1;
        protected bool facingRight = true;
    
        [Header("Move info")]
        [SerializeField] private float moveSpeed;
    
        private float xInput;
    
        // Start is called before the first frame update
        void Start()
        {
            rb = GetComponent<Rigidbody2D>();
            anim = GetComponentInChildren<Animator>();
        }
    
        // Update is called once per frame
        void Update()
        {
            Movement();
            AnimatorContronllers();
            FlipController();
        }
        private void Movement()
        {
            xInput = Input.GetAxisRaw("Horizontal");
            rb.velocity = new Vector2(xInput * moveSpeed, rb.velocity.y);
        }
    
        private void AnimatorContronllers()
        {
            bool isMoving = rb.velocity.x != 0;
            anim.SetBool("isMoving", isMoving);
        }
    
        private void FlipController()
        {
            if (rb.velocity.x > 0 && !facingRight)
            {
                Flip();
            }
            else if (rb.velocity.x < 0 && facingRight)
            {
                Flip();
            }
        }
    
        protected virtual void Flip()
        {
            facingDir = facingDir * -1;
            facingRight = !facingRight;
            // 角色 180 度转弯
            transform.Rotate(0, 180, 0);
        }
    }

    我们实现移动,需要获取角色 X 轴,角色面向的方向,移动速度

    这里有几个需要注意的地方

    1. Start 函数只会在启动的时候运行,update 是每一帧都会执行
    2. GetComponent 和 GetComponentInChildren
    3. 获取键盘 X 坐标 Input.GetAxisRaw("Horizontal") 或者 Y 坐标 Input.GetAxisRaw("Vertical")
    4. 设置动画状态的值 anim.SetBool("isMoving", isMoving),isMoving就是我们在外面定义的转换条件

    如何实现角色的跳跃

    我们先实现角色的跳跃和下落的动画

    Jump

    Fall

    跳跃条件

    • 将平台设置为地板(新增图层)
    • 角色是否在地面,只有在地面上才允许跳跃

    新增参数

    • 设置是地板(what is ground)
    • 角色离地板的距离多少才算在地板上(groud check distance)
    • 角色跳跃距离(Jump Force)
    • 角色的距离地板的检查点(ground check)
    • 检查角色是上升还是下降(yVelocity)

    步骤一:将平台设置为地板

    创建地板图层

    命名为 Ground

    将平台设置为地板

    步骤二:创建角色检查点

    isGrounded = Physics2D.Raycast(groundCheck.position, Vector2.down, groundCheckDistance, whatIsGround);

    这是监测点跟角色相对距离

    步骤三:创建角色动画

    其中 Jump/Fall 是 Blend Tree,因为我们要在这里播放上升或者下落的动画,

    Blend Tree 用于根据一个或多个参数动态混合多个动画

    1D Blend Tree

    • 使用单一参数控制动画混合。

    这里用了 yVelocity

    实现效果

    脚本代码如下,这里删除了无关代码

    SQL 复制代码
    public class Player2 : MonoBehaviour
    {
        protected Rigidbody2D rb;
        protected Animator anim;
    
    
        protected bool isGrounded = true;
    
        [Header("Move info")]
        [SerializeField] private float moveSpeed;
        [SerializeField] private float jumpForce;
    
        [Header("Collision Info")]
        [SerializeField] protected Transform groundCheck;
        [SerializeField] protected float groundCheckDistance;
        [SerializeField] protected LayerMask whatIsGround;
    
        private float xInput;
    
        // Update is called once per frame
        void Update()
        {
            CollisionChecks();
            Movement();
            if (Input.GetKeyDown(KeyCode.Space))
            {
                Jump();
            }
            AnimatorContronllers();
            FlipController();
        }
        protected virtual void CollisionChecks()
        {
            isGrounded = Physics2D.Raycast(groundCheck.position, Vector2.down, groundCheckDistance, whatIsGround);
        }
    
        private void Jump()
        {
            if (isGrounded)
            {
                rb.velocity = new Vector2(rb.velocity.x, jumpForce);
            }
        }
    
        private void AnimatorContronllers()
        {
            bool isMoving = rb.velocity.x != 0;
            anim.SetBool("isMoving", isMoving);
            anim.SetBool("isGrounded", isGrounded); 
            anim.SetFloat("yVelocity", rb.velocity.y);
        }
    
    }

    注意

    • 要在player对象中将CheckGround对象拖入 GroundCheck,设置GroundCheckDistance
    • 设置 WhatIsGround 为Ground 图层

    如何实现角色的冲刺

    我们先实现角色的冲刺动画

    冲刺条件

    • 按下左 shift 键
    • 在地板上才允许冲刺
    • 每次冲刺持续1秒,有2秒冷却时间

    新增参数

    • 冲刺速度(dashSpeed,设置的冲刺速度)
    • 冲刺持续时间(dashDuration,设置的持续时间)
    • 运行时的冲刺时间(dashTime,运行时的冲刺时间)
    • 冲刺冷却时间(dashCooldown,设置的持续时间)
    • 运行时的冷却时间(dashCooldownTimer)
  1. 调整动画器

    脚本代码如下,这里删除了与本次功能无关代码

    SQL 复制代码
    public class Player2 : MonoBehaviour
    {
     protected Rigidbody2D rb;
        protected Animator anim;
    
        protected bool isGrounded = true;
    
        [Header("Move info")]
     [SerializeField] private float moveSpeed;
        [SerializeField] private float jumpForce;
    
        [Header("Dash info")]
     [SerializeField] private float dashSpeed;
        [SerializeField] private float dashDuration;
     private float dashTime;
    
     [SerializeField] private float dashCooldown;
        private float dashCooldownTimer;
    
    
     // Update is called once per frame
        void Update()
     {
            CollisionChecks();
         ...
    
            dashTime -= Time.deltaTime;
            dashCooldownTimer -= Time.deltaTime;
    
            AnimatorContronllers();
            FlipController();
        }
    
        private void CheckInput()
        {
            if (Input.GetKeyDown(KeyCode.Space))
            {
                Jump();
            }
            if (Input.GetKeyDown(KeyCode.LeftShift))
            {
                DashAbility();
            }
        }
    
        private void DashAbility()
        {
            if (dashCooldownTimer < 0)
            {
                dashCooldownTimer = dashCooldown;
                dashTime = dashDuration;
            }
        }
        protected virtual void CollisionChecks()
        {
            isGrounded = Physics2D.Raycast(groundCheck.position, Vector2.down, groundCheckDistance, whatIsGround);
        }
    
        private void Jump()
        {
            if (isGrounded)
            {
                rb.velocity = new Vector2(rb.velocity.x, jumpForce);
            }
        }
    
        private void AnimatorContronllers()
        {
            bool isMoving = rb.velocity.x != 0;
            anim.SetBool("isMoving", isMoving);
            anim.SetBool("isGrounded", isGrounded); 
            anim.SetBool("isDashing", dashTime > 0); 
            anim.SetFloat("yVelocity", rb.velocity.y);
        }
    
    }

    注意

    • 有一些参数是暴露给外面进行设置的

    实现效果

    如何实现角色蹬墙跳(TODO)

    拓展

    GetComponent 和 GetComponentInChildren

    GetComponent

    功能:

    • 查找调用该方法的游戏对象上附加的指定类型的组件。

    特点:

    • 只会在当前对象上查找组件。
    • 如果找到组件,则返回该组件;如果没有找到,返回 null
    GetComponentInChildren

    功能:

    • 查找调用该方法的游戏对象及其所有子对象中第一个匹配的组件。

    特点:

    • 会遍历当前对象的子对象及其嵌套层级,查找第一个匹配的组件。
    • 如果设置 includeInactivetrue,会在非激活的子对象中查找。
    示例

    假设场景中有如下结构:

    scss

    复制代码

    ParentObject ``  ├── ChildObject ``  │     └── GrandChildObject (Rigidbody)
    
    1. 如果在 ParentObject 上调用 GetComponent<Rigidbody>();
    • 只会查找 ParentObject,不返回任何结果。
    1. 如果在 ParentObject 上调用 GetComponentInChildren<Rigidbody>()
    • 会查找到 GrandChildObject 上的 Rigidbody

    动画 Any State 结点

    Any State 是 Unity 动画状态机中的一个特殊节点,它可以在动画控制器中用来从任何动画状态直接过渡到目标动画状态,而不需要考虑当前的状态。它常用于实现快速、全局性的动画切换(如跳跃、受伤、死亡等)。

    1. 特点
    • 全局性 : 无论当前动画状态是什么,都可以从 Any State 节点切换到目标状态。
    • 无需显式连接: 不需要单独为每个状态建立连接,简化了动画控制器的结构。
    • 常用于突发事件: 比如触发攻击、受伤、死亡等需要即时响应的动画。
    2. 适用场景
    • 紧急动画: 当需要立即播放某个动画时,例如角色被击中、死亡或发动技能。
    • 重复触发动画: 某些动画可能需要多次触发,而不受当前动画状态的影响。
    • 减少复杂度: 可以避免为每个状态都设置过渡规则。
    3. 如何使用 Any State

    步骤 1: 打开 Animator

    1. 打开角色的 Animator Controller
    2. 确保在动画控制器中有 Any State 节点。

    步骤 2: 创建连接

    1. Any State 节点拖出一条过渡线到目标动画状态。
    2. 配置过渡条件(通常绑定到触发器或布尔参数)。

    步骤 3: 设置参数

    • 为动画创建控制参数(BoolTrigger 等)。
    • 在代码中,通过 Animator.SetTrigger() 等方法触发。
    4. Any State 优劣势总结
    优点 缺点
    简化复杂的动画状态切换 可能导致状态机难以调试
    从任何状态快速切换到目标状态 条件冲突可能引发意外的切换
    减少重复的连接和逻辑 不适合用于频繁或复杂的状态切换

    Physics2D.Raycast

    Physics2D.Raycast 是 Unity 2D 中用于发射射线并检测碰撞体的一个方法。它可以用来检测射线方向上是否有碰撞体,返回检测到的对象以及相关信息。

    1. 基本功能

    Physics2D.Raycast 发射一条射线,检测它与 2D 碰撞体的交点,可以用于以下场景:

    • 检测视线遮挡:检查两个点之间是否有障碍。
    • 鼠标点击检测:检测鼠标点击的 2D 对象。
    • 敌人/玩家感知:检测某个方向是否有目标。
    2. 基本语法
    C# 复制代码
    RaycastHit2D hit = Physics2D.Raycast(Vector2 origin, Vector2 direction, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers);

    参数说明:

    1. origin: 射线的起点(世界坐标系中的 2D 向量)。
    2. direction: 射线的方向(归一化的 2D 向量)。
    3. distance (可选): 射线检测的最大距离,默认值为无穷大。
    4. layerMask (可选): 用于过滤特定层的碰撞体,默认值为所有层。

    返回值:

    • RaycastHit2D ** 对象**: 包含射线检测的详细信息。
      • 如果没有检测到碰撞体,则返回 null 或空的 RaycastHit2D
    3. 使用示例

    检测物体

    以下代码检测从原点 (0, 0) 向右方发射的射线是否碰到任何 2D 碰撞体:

    C# 复制代码
    void Update()
    {
        Vector2 origin = new Vector2(0, 0);
        Vector2 direction = Vector2.right;
        float maxDistance = 10f;
    
        RaycastHit2D hit = Physics2D.Raycast(origin, direction, maxDistance);
    
        if (hit.collider != null)
        {
            Debug.Log("Hit object: " + hit.collider.name);
        }
    }

    过滤特定层

    可以使用 LayerMask 过滤不需要检测的层。例如,只检测名为 Obstacle 层的对象:

    C# 复制代码
    void Update()
    {
        Vector2 origin = new Vector2(0, 0);
        Vector2 direction = Vector2.right;
        float maxDistance = 10f;
        int layerMask = LayerMask.GetMask("Obstacle");
    
        RaycastHit2D hit = Physics2D.Raycast(origin, direction, maxDistance, layerMask);
    
        if (hit.collider != null)
        {
            Debug.Log("Hit obstacle: " + hit.collider.name);
        }
    }

    鼠标点击检测

    发射一条从鼠标位置垂直向上的射线,检测鼠标点击到的对象:

    C# 复制代码
    void Update()
    {
        if (Input.GetMouseButtonDown(0)) // 鼠标左键点击
        {
            Vector2 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
            RaycastHit2D hit = Physics2D.Raycast(mousePosition, Vector2.zero);
    
            if (hit.collider != null)
            {
                Debug.Log("Clicked on: " + hit.collider.name);
            }
        }
    }
    4. RaycastHit2D 属性

    RaycastHit2D 返回的对象包含以下常用信息:

    1. collider : 被击中的碰撞体(Collider2D)。
    2. point: 射线击中的世界坐标位置。
    3. normal: 碰撞体表面的法线方向。
    4. distance: 起点到碰撞点的距离。
    5. rigidbody : 碰撞体所属的刚体(Rigidbody2D)。
    6. transform : 碰撞体的变换(Transform)。

    示例:

    C# 复制代码
    if (hit.collider != null)
    {
        Debug.Log("Hit Point: " + hit.point);
        Debug.Log("Normal: " + hit.normal);
        Debug.Log("Distance: " + hit.distance);
    }
    5. 射线的可视化

    Scene 视图中可视化射线,便于调试。使用 Debug.DrawRay:

    C# 复制代码
    void Update()
    {
        Vector2 origin = new Vector2(0, 0);
        Vector2 direction = Vector2.right;
        float maxDistance = 10f;
    
        RaycastHit2D hit = Physics2D.Raycast(origin, direction, maxDistance);
        Debug.DrawRay(origin, direction * maxDistance, Color.red);
    
        if (hit.collider != null)
        {
            Debug.Log("Hit: " + hit.collider.name);
        }
    }
    6. 多****射线检测

    (1) Physics2D.RaycastAll

    • 发射一条射线,返回沿途所有碰撞的对象。

    • 语法 :
      *

      C# 复制代码
      RaycastHit2D[] hits = Physics2D.RaycastAll(Vector2 origin, Vector2 direction, float distance, int layerMask);
    • 示例 :
      *

      C# 复制代码
      RaycastHit2D[] hits = Physics2D.RaycastAll(origin, direction, maxDistance, layerMask);
      
      foreach (RaycastHit2D hit in hits)
      {
          Debug.Log("Hit object: " + hit.collider.name);
      }

    (2) Physics2D.CircleCast

    • 发射一个圆形射线,用于检测较大的区域。

    • 语法 :
      *

      C# 复制代码
      RaycastHit2D hit = Physics2D.CircleCast(Vector2 origin, float radius, Vector2 direction, float distance, int layerMask);
    • 示例 :
      *

      C# 复制代码
      RaycastHit2D hit = Physics2D.CircleCast(origin, 1.0f, direction, maxDistance, layerMask);
      if (hit.collider != null)
      {
          Debug.Log("Hit: " + hit.collider.name);
      }
    7. 注意事项
    1. 方向****归一化:

      1. 确保 direction 是一个单位向量。
      C# 复制代码
      Vector2 direction = (targetPosition - origin).normalized;
    2. LayerMask 使用:

      1. LayerMask.GetMask 返回层的整数值。
      2. ~LayerMask.GetMask("LayerName") 反转掩码。
    3. 射线未命中:

      1. 检测失败时,RaycastHit2D.collidernull
    4. 性能优化:

      1. 使用 layerMask 限制检测范围,提高性能。
      2. 尽量减少频繁调用射线的方法,特别是在复杂场景中。

    通过 Physics2D.Raycast 和相关方法,可以轻松实现 2D 游戏中的碰撞检测和逻辑处理。

    Blend Tree

    在 Unity 的动画系统中,Blend Tree 是 Animator 中的一种特殊状态,用于根据一个或多个参数动态混合多个动画。它能让角色的动画切换更加流畅,常用于角色移动、攻击动作等需要根据条件动态调整的场景。

    1. Blend Tree 的作用
    • 动态混合多个动画状态。
    • 使用参数(如速度、方向)自动调整动画权重,生成平滑的过渡效果。
    • 减少复杂的状态机连接,提高动画逻辑的效率。
    2. Blend Tree 的使用场景
    1. 角色移动 :
      1. 根据角色的速度和方向,动态切换站立、行走、跑步等动画。
    2. 角色攻击 :
      1. 根据攻击力度或方向混合不同的攻击动画。
    3. 角色姿态调整 :
      1. 根据角色的倾斜角度(如爬坡、下坡)调整动画。
    3. 如何创建 Blend Tree

    步骤 1: 创建 Blend Tree

    1. 打开 Animator 窗口。
    2. 在 Animator 的状态机中,右键选择 Create State -> From New Blend Tree
    3. 双击新创建的 Blend Tree 进入编辑界面。

    步骤 2: 添加动画到 Blend Tree

    1. 选中 Blend Tree 状态,在 Inspector 中点击 Open Blend Tree
    2. 点击 + 按钮,选择 Add Motion
    3. 将动画拖入 Motion 槽位。

    步骤 3: 配置 Blend Tree

    1. 参数 :
      1. 为 Blend Tree 添加参数(如 SpeedDirection)。
      2. Animator Parameters 面板中定义这些参数。
    2. 混合类型 :
      1. 单参数混合(1D Blend Tree):基于单一参数(如速度)混合动画。
      2. 多参数混合(2D Blend Tree):基于两个参数(如速度和方向)混合动画。
    4. Blend Tree 类型

    (1) 1D Blend Tree

    • 使用单一参数控制动画混合。
    • 适用场景 :
      • 根据速度切换站立、行走和跑步动画。
    • 设置示例 :
      • 创建一个 Speed 参数。
      • 在 Blend Tree 中设置:
        1. Speed == 0: 播放站立动画。
        2. Speed == 0.5: 混合站立和行走动画。
        3. Speed == 1: 播放跑步动画。

    animator.SetFloat("Speed", speed)

    (2) 2D Blend Tree

    • 使用两个参数控制动画混合。
    • 适用场景 :
      • 根据角色的速度和方向切换不同移动动画。
    • 混合类型 :
      • Simple Directional: 参数值代表方向(如 X 和 Y)。
      • Freeform Cartesian: 任意坐标系下参数组合的动画混合。
      • Freeform Directional: 按方向值(角度)混合动画。
    5. 动画混合权重

    Blend Tree 自动根据参数值动态计算每个动画的权重。

    例如:

    • 假设有两个动画 IdleRun,参数 Speed 的值为 0 时播放 Idle,为 1 时播放 Run,当 Speed=0.5 时,两者权重各为 0.5
    6. 可视化调试
    1. 在 Animator 窗口中选择 Blend Tree。
    2. 在 Inspector 中实时调整参数值,观察动画混合效果。
    3. 使用 Debug.Log 输出参数值,确认是否正确设置。
    7. 优点与注意事项

    优点:

    • 简化状态机: 减少多个动画状态之间的连接。
    • 动态调整: 根据实时参数值自动计算动画混合。
    • 平滑过渡: 实现更自然的动画切换。

    注意事项:

    1. 参数值范围 :
      1. 确保参数值的范围与动画权重匹配。
    2. 动画根运动 :
      1. 如果使用根运动(Root Motion),确保所有动画使用统一的根运动。
    3. 性能优化 :
      1. Blend Tree 混合多个动画时,可能会增加性能开销,复杂场景下需注意优化。
    8. 总结
    功能 描述
    1D Blend Tree 基于单一参数(如速度)动态混合动画。
    2D Blend Tree 基于两个参数(如速度和方向)混合多维动画。
    混合类型 包括简单方向、自由形式(笛卡尔和方向)混合。
    应用场景 角色移动(行走/跑步)、攻击方向、姿态调整等。

    Blend Tree 是 Unity 动画系统中强大的工具,用于动态处理多个动画,使角色动画表现更自然、灵活。

    LayerMask

    LayerMask 是 Unity 中用于管理物体层级和控制层之间交互的重要工具。它可以用来过滤对象,在物理碰撞、射线检测等场景中非常常用。

    1. 什么是 LayerMask?
    • 层 (Layer): Unity 为每个对象提供的分类系统,最多支持 32 个层(从 0 到 31)。
    • LayerMask: 一个位掩码,允许你选择或排除特定的层,用于优化检测或渲染等操作。
    2. 使用 LayerMask 的场景
    1. 射线检测: 只检测特定层的对象。
    2. 碰撞检测: 限制哪些层可以发生碰撞。
    3. 相机渲染: 控制相机只渲染特定层的对象。
    4. 性能优化: 忽略无关的层,减少开销。
    3. 设置层 (Layer)
    1. 添加新层 :
      1. 选择菜单 Edit -> Project Settings -> Tags and Layers
      2. Layers 部分添加自定义层。
    2. 分配层 :
      1. 选择游戏对象,在 Inspector 面板中,将对象的 Layer 设置为指定的层。
    4. 创建 LayerMask

    LayerMask 是一个整数,可以表示多个层的组合(使用位掩码)。

    (1) 通过 LayerMask.GetMask

    C# 复制代码
    int layerMask = LayerMask.GetMask("Player", "Enemy");
    • 功能: 获取层名对应的 LayerMask。
    • 返回值: 一个整数,表示这些层的组合。

    (2) 直接使用整数值

    C# 复制代码
    int layerMask = 1 << LayerIndex;
    • 功能: 用位运算将特定层转换为 LayerMask。
    • 示例 :
      • 第 8 层的 LayerMask:
        *

        C# 复制代码
        int layerMask = 1 << 8;

    (3) 排除某些层

    C# 复制代码
    int excludeMask = ~LayerMask.GetMask("Player");
    • 功能 : 使用按位取反 (~) 排除特定层。
    5. 使用 LayerMask 的场景

    (1) 射线检测

    • 只检测特定层 :
      *

      C# 复制代码
      int layerMask = LayerMask.GetMask("Obstacle");
      RaycastHit2D hit = Physics2D.Raycast(origin, direction, maxDistance, layerMask);
      
      if (hit.collider != null)
      {
          Debug.Log("Hit object: " + hit.collider.name);
      }
    • 忽略某些层 :
      *

      C# 复制代码
      int layerMask = ~LayerMask.GetMask("IgnoreRaycast");
      RaycastHit2D hit = Physics2D.Raycast(origin, direction, maxDistance, layerMask);

    (2) 碰撞过滤

    • 设置物理层交互规则:

    Edit -> Project Settings -> Physics 2D 中,调整 Layer Collision Matrix,选择哪些层可以发生碰撞。

    • 动态控制碰撞:

    使用 Physics2D.IgnoreLayerCollision 忽略某些层之间的碰撞:

    C# 复制代码
    Physics2D.IgnoreLayerCollision(8, 9, true);  // 忽略第 8 层和第 9 层之间的碰撞

    (3) 相机渲染

    • 控制相机只渲染特定层:

    在相机的 Culling Mask 中选择需要渲染的层:

    C# 复制代码
    Camera.main.cullingMask = LayerMask.GetMask("Player", "Environment");
    6. 常用操作

    (1) 检查对象是否属于某层

    C# 复制代码
    if ((layerMask & (1 << gameObject.layer)) != 0)
    {
        Debug.Log("GameObject is in the LayerMask");
    }
    • 原理 : 使用按位与 (&) 检查对象的层是否包含在 LayerMask 中。

    (2) 设置 LayerMask 为特定层

    C# 复制代码
    gameObject.layer = LayerMask.NameToLayer("Player");

    (3) 从整数还原层名

    C# 复制代码
    string layerName = LayerMask.LayerToName(layerIndex);
    Debug.Log("Layer Name: " + layerName);
    7. 示例代码

    以下代码展示了如何发射射线并检测 Obstacle 层上的对象,同时忽略 IgnoreRaycast 层:

    C# 复制代码
    void Update()
    {
        Vector2 origin = new Vector2(0, 0);
        Vector2 direction = Vector2.right;
        float maxDistance = 10f;
    
        // 定义 LayerMask
        int layerMask = LayerMask.GetMask("Obstacle");
        int excludeMask = ~LayerMask.GetMask("IgnoreRaycast");
    
        // 发射射线
        RaycastHit2D hit = Physics2D.Raycast(origin, direction, maxDistance, layerMask & excludeMask);
    
        if (hit.collider != null)
        {
            Debug.Log("Hit object: " + hit.collider.name);
        }
    
        // 可视化射线
        Debug.DrawRay(origin, direction * maxDistance, Color.red);
    }
    8. 常见问题
    Q: 为什么 LayerMask 不生效?
    1. 确保对象的 Layer 设置正确。
    2. 确保射线的 layerMask 参数包含目标层。
    3. 检查物理层交互规则(Physics 2D -> Layer Collision Matrix)。
    Q: 如何排除多个层?

    使用按位取反操作:

    C# 复制代码
    int excludeMask = ~LayerMask.GetMask("IgnoreRaycast", "UI");
    Q: LayerMask.GetMask ** 和 ****LayerMask.NameToLayer** 的区别?
    • GetMask: 返回多个层的位掩码(整型)。
    • NameToLayer: 返回单个层的索引(整型)。
    总结

    LayerMask 是 Unity 层管理的重要工具,通过位运算、过滤规则和物理设置,可以灵活优化射线检测、碰撞处理和相机渲染等操作,是开发高效游戏逻辑的基础工具之一。

相关推荐
cdut_suye13 分钟前
踏浪而行,2024年技术创作的星光轨迹
经验分享·笔记·学习方法·热榜·博客之星·学习历程·回顾2024
百流36 分钟前
scala文件编译相关理解
开发语言·学习·scala
雁于飞3 小时前
c语言贪吃蛇(极简版,基本能玩)
c语言·开发语言·笔记·学习·其他·课程设计·大作业
梅见十柒3 小时前
计算机系统原理:一些断言
经验分享·笔记
青椒大仙KI114 小时前
25/1/21 算法笔记<ROS2> 服务通信,参数
笔记
bohu8311 小时前
OpenCV笔记3-图像修复
笔记·opencv·图像修复·亮度增强·图片磨皮
墨笺染尘缘11 小时前
Unity——鼠标是否在某个圆形Image范围内
unity·c#·游戏引擎
大丈夫立于天地间12 小时前
ISIS基础知识
网络·网络协议·学习·智能路由器·信息与通信
doubt。12 小时前
【BUUCTF】[RCTF2015]EasySQL1
网络·数据库·笔记·mysql·安全·web安全