RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 603544
Accepted
Алексей Шиманский
Алексей Шиманский
Asked:2020-12-14 21:29:51 +0000 UTC2020-12-14 21:29:51 +0000 UTC 2020-12-14 21:29:51 +0000 UTC

如何使用图层蒙版(layermask)以及为什么要写1 << layer?

  • 772

再会!

我了解Unity的工作原理,我观看教程和一些脚本,有时我会遇到类似这样的结构1 << layer,其中layer是一个整数。我经常在与物理相关的方法中看到这一点,例如Raycast:

public int groundLayer = 5; 

void SomeMethod() {
    // do smth...
    if (Physics.RaycastAll(transform.position, transform.forward, 100.0f, 1 << groundLayer).Length > 0) { 
        ...
    }
    // do smth...
}

不仅限于物理学:

LayerMask Collisionmask;
int layer = 5;

void SomeMethod() {
    // do smth...
    if ((Collisionmask.value & (1 << layer)) == 0) {
        ...
    }
    // do smth...
}

我读到这些是某种面具。他们指向图层。但是不明白这些mask是什么,怎么用,和layers有什么关系?什么是1 << layer,例如?

有人可以详细解释吗?谢谢你。

c#
  • 1 1 个回答
  • 10 Views

1 个回答

  • Voted
  1. Best Answer
    Алексей Шиманский
    2020-12-14T21:29:51Z2020-12-14T21:29:51Z

    储备爆米花和可乐(馅饼和茶)。它会很长,但内容丰富。))

    它是什么,为什么?

    在Unity中,众所周知(或不仅是每个人)都知道,有层(layers)。您可以在文档中刷新您的记忆(如果您不知道,请阅读并熟悉)。它们以不同的方式使用。例如,2D在他们的帮助下,您可以指定和分离背景/中间/前景。有些会在后面,有些会在前面。在更复杂的游戏中,可能会有更多的层,并且可以通过这些层对对象进行分组。例如,您可以为敌对 NPC 和其他人创建一个图层并NPC从中选择一个图层Enemies。

    在一般情况下,使用layerMask(mask) 进行操作可以找出被选中的对象 (GameObject) 是否在指定层中(无论它是什么类型的对象以及如何检查都无关紧要)。根据检查的结果,执行预期的操作。

    例如,考虑Raycast。如果我们转向文档,我们可以看到该方法如下所示:

    public static RaycastHit2D Raycast(Vector2 origin, Vector2 direction, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers, float minDepth = -Mathf.Infinity, float maxDepth = Mathf.Infinity);
    

    选项之一:int layerMask = DefaultRaycastLayers。它是这样工作的:光束从某个方向的某个点发射(比方说这在远处是如此神奇),光束落在某种障碍物(物体)上,例如,如果这个物体在层Enemies,打它。(Collisionmask.value & (1 << layer)) == 0下面我们讲一个例子。但其本质是一样的:因面具而确定归属 > 做某事取决于……

    我希望本质是清楚的,让我们继续。

    这个怎么运作。

    Unity中的层数:32。它们从 0 到 31 编号。

    i1

    LayerMask(掩码) - 表示为 32 位整数:

    0000 0000 0000 0000 0000 0000 0000 0000
    

    为了更好的可读性,我将每 4 位分开。

    掩码使用其 32 位来表示各种标志(对象的状态,例如:1 - 启用,0 - 禁用)。在这种特殊情况下,每个标志(位)代表一个特定的层。我们先拿前8层进行分析,记下每一层的二进制表示(第一位(最低位)在右边)。

    слой №    Двоичное    Десятичное
    ----------------------------------
    Layer 0 = 0000 0001 = 1
    Layer 1 = 0000 0010 = 2
    Layer 2 = 0000 0100 = 4
    Layer 3 = 0000 1000 = 8
    Layer 4 = 0001 0000 = 16
    Layer 5 = 0010 0000 = 32
    Layer 6 = 0100 0000 = 64
    Layer 7 = 1000 0000 = 128
    

    这就是层的二进制表示形式。如您所见,特定层由一个特定位定义。

    现在让我们试着用一个例子来理解if ((Collisionmask.value & (1 << layer)) == 0)。

    我们看到一个layer整数变量。这个变量是层数(0、1、2、3、4 ...),不是十进制也不是二进制表示。Collisionmask检查器中是一个包含图层的下拉列表:

    i2

    Collisionmask.value反过来,它是所选层组合的二进制表示(因为我们不能选择一个层,而是多个层)。为了便于理解:例如,如果我们要为层 #2 和 #5 创建掩码,则二进制表示将如下所示:

    0010 0100  // #2,#5
    

    或者例如图层 #1、#2、#5、#6 将是:

    0110 0110 // #1,#2,#5,#6
    

    我们想要什么?我们想知道我们正在检查的层是否包含在这个掩码中layer?并以此为依据,做点什么。在上面的例子中,如果我们在第 5 层的对象不包含在指定的掩码中,我们想进入一个条件并做一些事情。

    比方说LayerMask = 0110 0110; // #1,#2,#5,#6,我们层的编号int layer = 5;

    有必要将第 5 层带入其二进制表示,对应于层。让我们看看上表。<<第 5 层的十进制表示形式为 32。反过来,二进制表示形式为 0010 0000。它是通过应用相对于最低有效位 (0000 0001) 恰好 5 个值的按位左移运算符获得的(太棒了! ). 向左移动 5 次:

    i3

    如您所见,数字 0010 0000 结果出来了,它确实包含在我们的蒙版0110 0110中,因为蒙版在右侧的位置编号 6 中包含一个 1,而第 5 层在相同位置也有一个。如果您不相信我的话,那么检查起来很容易:您需要在掩码和被检查的值之间执行按位乘法(按位与“&”)。顺便说一句,这是在行中完成的Collisionmask.value & (1 << layer)

    0110 0110 // #1,#2,#5,#6
    & (and)
    0010 0000 // #5
    ---------
    0010 0000
    

    我们看到面具中包含什么。如果不包含,则按位乘法将得出结果 0。作为证明示例,我们可以检查此掩码中是否包含第 7 层。将最低有效位向左移动 7 位:

    i3.2

    我们得到 1000 0000。检查

    0110 0110 // #1,#2,#5,#6
    & (and)
    1000 0000 // #7
    ---------
    0000 0000
    

    我们看到 0。不包括在内。因为它不包括在内,所以我们上面的条件被触发:

    if ((Collisionmask.value & (1 << layer)) == 0) => if (0 == 0)  => true 
    

    所以我们进入条件并做我们需要的。这就是我们要找的。


    添加 1

    除了检查特定图层是否包含在蒙版中之外,您还可以检查除指定图层之外的所有图层。可以说,您只需要使用反转运算符 (~)。它将操作数 0 的位表示更改为 1,将 1 更改为 0。因此 ~00000010 将是 11111101,它将测试除 #2 之外的所有层。如果要排除多个图层,则需要使用“或”运算符组合蒙版:

    int layer1 = 3;
    int layer2 = 5;
    int layermask = ~((1 << layer1) | (1 << layer2)) // НЕПРАВИЛЬНО: ~(1 << layer1) | ~(1 << layer2)
    

    补充2

    事实上,并不总是需要写1 << что-то. 例如,Raycast您可以传入 parameter Collisionmask.value,其中 Collisionmask它的类型为LayerMask,它又看起来像检查器中的下拉列表(参见图 2)。很舒服。

    另外,不要硬编码数字,即 不要写1 << 7or 1 << 5,因为 你可以忘记 5 和 7 是什么。为了找出所需的层数,你可以使用LayerMask.NameToLayer,例如:

    int npcLayer = LayerMask.NameToLayer("NPCLayer");   
    int npcMask = 1 << npcLayer;
    

    和“组”面具一样:

    int npcGoodLayer = LayerMask.NameToLayer("NPCGood");
    int friendsLayer = LayerMask.NameToLayer("Friends");
    int npcGoodMask = 1 << npcGoodLayer;
    int friendsMask = 1 << friendsLayer;
    int finalmask = npcGoodMask | friendsMask; // Or, (1 << npcGoodLayer) | (1 << friendsLayer)
    

    补充3

    事实上,这种构造1 << ...不仅存在于 Unity 中。这是一般编程中众所周知的做法。例如,一个制造商有一个产品,一个应用程序,它为其他程序员提供了一个 API。

    API - 一组现成的类、过程、函数、结构和常量,由应用程序(库、服务)或操作系统提供,用于外部软件产品。程序员在编写各种应用程序时使用。(c) 维基百科

    API 可以有各种访问限制。程序员指定他想要访问的掩码,然后在应用程序服务器上检查它,如果掩码不匹配,它会发出警告或其他内容。Vkontakte 及其API 访问权限的最简单示例

    i4

    那里指示了十进制表示,但它并没有改变本质。


    • 50

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    如何停止编写糟糕的代码?

    • 3 个回答
  • Marko Smith

    onCreateView 方法重构

    • 1 个回答
  • Marko Smith

    通用还是非通用

    • 2 个回答
  • Marko Smith

    如何访问 jQuery 中的列

    • 1 个回答
  • Marko Smith

    *.tga 文件的组重命名(3620 个)

    • 1 个回答
  • Marko Smith

    内存分配列表C#

    • 1 个回答
  • Marko Smith

    常规赛适度贪婪

    • 1 个回答
  • Marko Smith

    如何制作自己的自动完成/自动更正?

    • 1 个回答
  • Marko Smith

    选择斐波那契数列

    • 2 个回答
  • Marko Smith

    所有 API 版本中的通用权限代码

    • 2 个回答
  • Martin Hope
    jfs *(星号)和 ** 双星号在 Python 中是什么意思? 2020-11-23 05:07:40 +0000 UTC
  • Martin Hope
    hwak 哪个孩子调用了父母的静态方法?还是不可能完成的任务? 2020-11-18 16:30:55 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    user207618 Codegolf——组合选择算法的实现 2020-10-23 18:46:29 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    Arch ArrayList 与 LinkedList 的区别? 2020-09-20 02:42:49 +0000 UTC
  • Martin Hope
    iluxa1810 哪个更正确使用:if () 或 try-catch? 2020-08-23 18:56:13 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5