Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

What is a workaround for multiple layers on gameobject?

Discussion in 'Scripting' started by HiddenMonk, Aug 21, 2015.

  1. HiddenMonk

    HiddenMonk

    Joined:
    Dec 19, 2014
    Posts:
    987
    - I have my player object with a capsule collider for basic collisions, as well as a collider tree for hitboxes.
    - I have a script that needs its raycast to ignore anything in layer "Moveable"
    - I have a shooting script that needs its raycast to ignore anything in layer "IgnoreShooting"

    Since I am using hitboxes, I need my guns raycast to ignore my capsule collider (which will be covering most of my hitboxes) so that I can hit the hitboxes.
    The script that needs to avoid moveable objects is a script that corrects my capsules collision. It does so by shooting a capsulecast from my previous position to my current. Since a moveable object can technically move in between my previous and current position within a frame, I need to ignore it.

    I cant just have my shooting script ignore anything in the Moveable layer, since not everything in the Moveable layer needs to be ignored by the shooting script and vise versa.

    My player object capsule collider needs to be in both layers, but we can only use 1 layer.

    Workaround that I can think of...
    1 - Utilizing another layer called "MoveableAndIgnoreShooting" and incorporating it in my Moveable layermask and IgnoreShooting layermask.
    pros - Simple
    cons - Annoying and due to the 32 layer limit, this solution is limited (I shouldnt need that many layers anyways)

    2 - Instead of using layermasks in the raycast, have a custom layer component that the raycasts will check upon collision, and if the layers in the component is a layer to be ignored, start a new raycast a tiny bit past the hitpoint.
    pros - seemingly limitless and flexible.
    - cons -
    - Not simple. Will need to create a new raycast method for every raycast extension method I made.
    - Assumingly less performant
    - May not be safe. Past experience with spherecast and capsulecast show that they can be unreliable when starting inside a collider. (I think they still have issues).

    3 - do a raycastall, order them based on distance, then filter out until you get the first desired collider.
    pros - simpler than solution #2
    - cons -
    - Assumingly less performant than solution #2 (though the simplicity probably outweighs the slight performance issue).
    - Would still need to create a new raycast method for every raycast extension method I made.
    - I think I tried something like this for my script that needs to avoid moving objects, but had issues with capsulecastall / spherecastall and starting within a collider.
    Edit - After checking again, at least in Unity 5.02 it seems the castalls remember their previous hit and dont clear it if starting inside a collider.

    4 - Create your own collision system that allows the use of multiple layers (Not doing this ^_^)

    I am curious as to what others have to suggest. I am currently leaning towards the 1st solution and hoping that I dont run into needing two layers on an object again.
    I appreciate any help!
     
    Last edited: Dec 20, 2016
  2. DonLoquacious

    DonLoquacious

    Joined:
    Feb 24, 2013
    Posts:
    1,667
    I didn't read any of that (sorry, too much and I just woke up), but if you're looking to attach multiple layers to one object it's actually not that hard- just make a child object and attach it to it. Colliders have an interesting effect when you have one on a parent object and attach more to children of that object- they act as ONE collider (called a compound collider). It's how we get colliders to cover large irregular shapes without having to deal with the overhead of a mesh collider. Any of those colliders being touched will trigger the OnCollisionEnter for the parent and all children simultaneously, since it's one collider, and more importantly in your case children can have different layers.

    If you copy the collider from your parent object onto an empty child object, you'll have an exact representation of your parent collider, but you can change the layer to whatever you want. You can also disable the child object (or its collider, specifically) so that those extra interactions it provides no longer occur.
     
  3. HiddenMonk

    HiddenMonk

    Joined:
    Dec 19, 2014
    Posts:
    987
    Thanks for your reply =), but I dont think what you said will help with my case.
    I edited my original post to make it seem less scary to read.
     
  4. DonLoquacious

    DonLoquacious

    Joined:
    Feb 24, 2013
    Posts:
    1,667
    You can have specific colliders/rigidbodies ignore specific colliders/rigidbodis using the CollisionIgnore function. If you want a raycast to ignore a specific object rather than a specific layer, then you'll have to use RaycastAll and iterate over the results, finding the closest result that isn't an object you want to ignore. These two functions used properly in combination with compound colliders and using the collision matrix properly means you can do just about anything you want with colliders without a whole lot of fuss, provided that you're not going out of your way to make things harder than they need to be.

    I like this:
    "If you find that you've hit a roadblock, the three options you have are to give up, find a way through it, or to use a different road entirely. If none of the "solutions" seem viable, then your entire method is probably off."
     
  5. HiddenMonk

    HiddenMonk

    Joined:
    Dec 19, 2014
    Posts:
    987
    That is pretty much exactly what my solution number 3 was (in the spoiler of my original post), however, due to some bugs with castalls, as explained in my original post, I am shying away from this solution.

    Based on my scenario in my original post, what other road can I take?
    I feel like im in one of those situations where there are just limits and the only way through is just to kind of hack at it and just get it working.
     
    Last edited: Aug 21, 2015
  6. DonLoquacious

    DonLoquacious

    Joined:
    Feb 24, 2013
    Posts:
    1,667
    The limits weren't made by Unity though, they were made by you. I've read your original post 5 times now and I still understand only half of what you're saying, because you've overcomplicated the hell out of this. I'm going to go over it one point at a time here, though.

    You sound like you want to use multiple layers so that an object can be specifically excluded based on whether a layer is in their "layer list", and that's the exact opposite of how the system works- you need to work based off of inclusion instead of exclusion- if you belong to even one layer that the object can see, then that object can (and will) hit you. In order to avoid being hit, you need to be completely invisible.

    You have one big collider that you're using for general collision detection. All of your smaller "hitbox" colliders are making up a compound collider with the big collider, if they're children of your character object. That means any hit on the children will hit the big collider, and any hit on the big collider will hit the children. That's fine, as long as you understand what's happening here.

    You can give yourself a little more freedom here by having a "core" collider on your main object, small and somewhere around your center of gravity so it doesn't need to move at all relative to the character. Make your player the same layer as your hitboxes- no point in being different here. This will keep everything as a compound collider and give you OnCollision functionality on your parent object (this will receive the collisions for ALL child objects with colliders). That compound collider is going to trigger whenever you hit anything, like the ground or an object or whatever, so the bigger collider, as far as I can tell, isn't actually doing anything except making it more uniform all around (and smothering the children, if it's on the same layer). I'd probably get rid of the big one entirely, but there's an alternative I'll go over in a moment.

    If you're worried about not being able to tell which collider in a compound collider is hit in a collision, you can check the contact details.
    Code (csharp):
    1. void OnCollisionEnter(Collision hit)
    2. {
    3.    GameObject hitboxHit = hit.contacts[0].thisCollider.gameObject;
    4. }
    And you can then have all of your damage or whatever functionality in one place, using the main character object like a manager, rather than individually on each child object.

    If you absolutely need a big uniform collider for a more uniform collision detection, then that collider needs to be a different layer from the hitbox colliders, which is easily accomplished in this new setup by giving the player a child object in a different layer and slapping the big collider on that instead. This makes it easy to disable the collider through a function on the player like "DisableBigCollider()" without losing your compound-collider status on your hitboxes, since your main object still has its little "core" collider, so it's still linking together the colliders of all of the children and creating a compound collider. The big collider is also a part of that compound collider, so it's sole job is to make a uniform area for hits that aren't hitbox-specific. Anything that needs to hit hitboxes can ignore the layer the big collider is on.

    Note that your original setup still works fine, really, so long as you're making the big collider a child and not attaching it to the main character object (if you attach it to the primary object, then any children will be compound colliders, whether you want them to be or not). The bigger problem is the following:

    It shouldn't be ignoring a specific layer- that can be how a layer mask works but that's not how layers work. It should be hitting specific layers. By inclusion, not by exclusion. It's easy enough to change objects from one layer to another to avoid a raycast if that raycast is only supposed to hit one or two or six layers, but if you're only excluding one layer then that layer (and only that layer) is the only option for avoiding it. If you have an object with more than one layer, using compound colliders, it becomes absolutely impossible to avoid being hit, period. That's a non-starter.

    More to follow after a short commercial break...
     
    Last edited: Aug 21, 2015
    Kiwasi likes this.
  7. DonLoquacious

    DonLoquacious

    Joined:
    Feb 24, 2013
    Posts:
    1,667
    More in a moment, but in the meantime I'd really like to get @BoredMormon and @LeftyRighty to weigh in on this if they're not too busy, as I think it's an interesting discussion, once you cut through the confusion. ^_^
     
  8. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Refine the logic of your use case first. Here is how I see it.
    • You have colliders for movement
    • You have colliders for hit boxes
    Both of these should be on separate layers.

    Entities will be made up of
    • A parent object with a rigidbody and associated logic
    • A child object on the movement layer with all of the movement colliders
    • A child object on the hotbox layer with all of the hit box colliders
    Any collider that needs to be on both layers just gets duplicated in both child objects. If you are really concerned this can be done automatically via a script.

    There is no need for multiple ray casting scripts, just pass in the appropriate layermask as an argument.
     
    LeftyRighty likes this.
  9. HiddenMonk

    HiddenMonk

    Joined:
    Dec 19, 2014
    Posts:
    987
    So, based on my scenario, I will need to put the capsule collider in layer Moveable, and the hitboxes in a layer Shootable.
    I now want my gun to be able to hit anything that is in layer shootable, the default layer, and the moveable layer except certain objects that are moveable such as my player, because that collider is blocking my hitboxes. I am assuming the next option would be to make a new layer for this specific case? That is pretty much my workaround #1 in my original post.
    Or am I not understanding something?
    Making multiple colliders on separate layers wont change anything either. Put the capsule collider on moveable, make a copy and put it on layer NonShootable, and the hitboxes on shootable. I still have that one capsule collider on layer moveable that is blocking the hitboxes.

    Inclusion vs exclusion. Seems to me like a "Is the glass half full or half empty" type of situation. It doesnt matter if you include all except 1, or just exclude 1.
    I am not sure as to what you mean by only being able to exclude 1 layer. With a public layermask field, in the inspector I select all the layers I want to ignore, then in code I just put a ~ symbol in front of the layermask to invert it. So technically I am working with inclusion as far as the raycast is concerned.

    I want my gun to hit the walls, moving objects, hitboxes, etc... pretty much everything except the capsule collider on players. So, my raycast is to include all layers except a special layer I put on those capsule colliders. This is similar to my workaround #1 in that I need to create a special layer. That special layer now must be included in any layermask I use for moveable layers.

    Unity seems to force me to use layers in a more specific type of way instead of a generic way. Layers such as Player, Wall, Animals instead of Moveable, Shootable, Damageable
    Its like unity layers are classes, but I want them to be interfaces.

    With interface type layers, I can define what it is and if it contains a layer I want, or a layer I dont want, I can do what is needed.
    If its just in a layer called Player, I need to then treat the layers individually.
    All of this can work fine in regards to cameras only seeing certain layers, but it seems limiting in regards to raycasts.

    I could probably get what I want by creating global layermasks such as LayerMask Moveable, Shootable, etc.. and assign the appropriate layers. So I can create a Player layer and assign it to moveable, assign Animals layer to moveable and shootable, etc... Then instead of choosing layermasks to ignore, I choose a custom layermask class and select from there.
    (although, I guess there can be issues if you have a layer in moveable and shootable, and you want a raycast to ignore moveables but not shootables)

    For simplicity, I would like to have a uniform collider.
    You can also, at least for my case, leave out anything that has to do with collisions unrelated to raycasts. I am just concerned about raycasts at the moment, though you can include collision type stuff for other people if youd like ^^.

    Whether the hitboxes are parented to the same object to the capsule collider is irrelevant. For our case, lets just say they are parented under 2 separate objects. We know we can give them both their own layers.

    All you need to focus on is this...
    I have a script called FixCollision that has a raycast that MUST avoid anything that moves.
    I have another script, Shooting, that MUST avoid the capsulecollider on my player, this capsulecollider is also an object that moves. The Shooting script can shoot things that move.

    How do I have my shooting script avoid this one moveable collider?

    The solution you seem to be leaning towards is my workaround #1, except maybe we should change the name of the layer to "Player" instead of "MoveableAndIgnoreShooting". Now anything that needs to ignore something that moves, I put in Moveable and Player, and my shooting script will need to ignore Player. (or use the global layer mask idea written above)


    ooooor I am just being a fool and am not seeing things properly, which I hope so since it could make things easier.
    You both might need to hold my hands with this one ^^.
     
    Last edited: Aug 21, 2015
  10. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    holy wall of... purely based on the length disparity between your and BoredMormon's posts I'd say you're over-thinking it :p
    brain barely workie today, I'll try to be more helpful later :)
     
    HiddenMonk likes this.
  11. DonLoquacious

    DonLoquacious

    Joined:
    Feb 24, 2013
    Posts:
    1,667
    You add an exception to the collision between the bullet's rigidbody and the collider you want to miss using an IgnoreCollision. If you're doing it purely through raycasting rather than actually instantiating and firing a projectile, then you have to use RaycastAll and work around any bugs using booleans and catching errors- though I suppose you could also log the position hit and then re-raycast from just ahead of that point for the remainder of the distance. If you're within the collider of the object, it shouldn't hit it again, right? I've never tried something like that before, though.

    Anyways, nothing about RaycastAll, Sphere or normal or w/e, is broken to the point where you can't use it (and use it easily), which is why there aren't angry programmers lining up to punch the Unity devs- you just have to patch it a bit and it's good to go. Given the benefits that it provides, a little finickiness is understandable. That's all there is to it.

    I'd say if you have a subset of objects in one layer that need to be interacted with differently by the physics engine on a regular basis than another subset, and you can't use RaycastAll to separate them out when needed, and compound colliders with different layers aren't going to help in your case, then you need to put that subset into a new layer- call them "movingStuffA" and "movingStuffB" or whatever you like. There might be a limited number of layers, but you can't be scared to break things into two groups like this if there's a big enough reason to. 32 is still a lot, and if you hit the cap then you've got bigger methodology problems than this.

    It's really really hard to visualize this situation you're in, so if you want additional help beyond that, you'll really need to lay it out using visuals (screenshots) and detailing your specific needs rather than generalizing with the "moving layer" and stuff. Like, for instance, what exactly is the "fixCollisions" thing? I'm assuming it's in case you're moving so fast that a collision should have occurred between two objects and didn't (rigidbodies have a built-in option that takes care of this)- that's the absolutely perfect scenario to use a RaycastAll, but you appear to want to narrow down the possibilities using layers alone. It's not going to work that way, sadly.
     
    Last edited: Aug 21, 2015
  12. HiddenMonk

    HiddenMonk

    Joined:
    Dec 19, 2014
    Posts:
    987
    We have a Player with a moveable collider (in layer moveable) and hitbox collider (in no layer).
    We have a Shooter gameobject (red) and a RaycasterIgnoreMoveable object (blue).
    We also have a MoveableSphere (in layer moveable).

    The shooter wants to be able to hit everything except the capsule collider, while the RaycasterIgnoreMoveable wants to hit everything except moveable objects.

    The Raycast script is this
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class Shooting : MonoBehaviour
    4. {
    5.     public LayerMask ignoreLayers;
    6.     public Color color;
    7.  
    8.     void Update()
    9.     {
    10.         RaycastHit hitInfo;
    11.         if(Physics.Raycast(transform.position, transform.forward, out hitInfo, 100f, ~ignoreLayers)) //Needs distance in order to use layermask
    12.         {
    13.             Debug.Log("Hit!");
    14.             Debug.DrawLine(transform.position, hitInfo.point, color);
    15.         }
    16.     }
    17. }

    This script is on the Shooter and RaycasterIgnoreMoveable.
    Currently, the shooter has ignoreLayers set to None, and RaycasterIgnoreMoveable has it set to Moveable.

    This is how it looks.

    Situation.png

    The issue here is that the shooter is not hitting the hitbox, we need to ignore this capsule collider.
    So, solution #1 is to create a new layer (we will call it PlayerMovingCollider), put the capsule collider in that new layer, and remember to go to our RaycastIgnoreMoveable and add the PlayerMovingCollider layer to the ignoreLayers, as well as to our Shooter and add PlayerMovingCollider to its ignore list as well.

    This is how it now looks.
    AfterSolution1.png

    We can see now that everything is hitting what it was meant to hit and even if we move the MoveableSphere in the way, the Shooter can still properly hit this moveable object.

    SphereIntersection.png

    So, my question was basically just asking if anyone has other ways of handling situations like this, instead of having to basically go and remember to add a new layer to the ignore layers list like we had to do for the RaycasterIgnoreMoveable.

    We could create global LayerMasks such as Moveable, Shootable, what ever we want and assign each individual layer to them.
    That can be a fine solution.

    Another would be to use castAlls and handle special cases that way. Though, this is bad for capsulecastall and spherecastall due to a bug (at least in version 5.02) as seen in this video.

    The code used in that video was just this...
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class DoCast : MonoBehaviour
    4. {
    5.     public Transform target;
    6.  
    7.     void Update()
    8.     {
    9.         RaycastHit[] hitInfos = Physics.SphereCastAll(transform.position, .5f, target.position - transform.position, 100f);
    10.         Debug.Log(hitInfos.Length);
    11.  
    12.         if(hitInfos.Length > 0)
    13.         {
    14.             for(int i = 0; i < hitInfos.Length; i++)
    15.             {
    16.                 Debug.DrawLine(transform.position, hitInfos[i].point);
    17.             }
    18.         }
    19.     }
    20. }

    All in all, I am not here telling you guys there is no solution to any this, it was more so just asking what you may do in a situation like this, since making a bunch of layers and what not seemed annoying to me.
     
  13. TheFlash56

    TheFlash56

    Joined:
    Apr 4, 2021
    Posts:
    1
    so um i had a similar problem i don't know if it will work here or not. SO you have to make a script lets say MoveableObject, and one lets say HitBox, now when you are raycasting leave layermask empty thus it will hit everything now when it hits everything in whatever object it is get the script component and call a function that would normally be in the script with the raycast, so if it hits a layer mask you dont want it to hit when you try to get the component it will not be able to get the component and thus nothing will happen. One of the cons is that you will have to attach a script to every object of that type, oh wait this is 5 years ago LMAO just saw that lol