Search Unity

Raycast in the prefab scene

Discussion in 'Prefabs' started by Silly_Rollo, Mar 20, 2019.

  1. Silly_Rollo

    Silly_Rollo

    Joined:
    Dec 21, 2012
    Posts:
    501
    This is 2018.3.9f1

    I have a function that gets called in various OnDrawGizmosSelected to help positioning. It works perfectly in the normal editor, but in the prefab scene the Physics.Raycast never hits anything. I can see the Gizmos.Drawline firing but it goes right through colliders in the correct layer. Is there some reason this shouldn't work in the scene?

    edit: I tried it with an everything layermask just to make sure and it can't hit anything.

    Example:

    Code (CSharp):
    1. public static void CheckAdjust(Transform tr, Stickiness positioning) {
    2.             Gizmos.color = Color.red;
    3.             switch (positioning) {
    4.                 case Stickiness.Floors:
    5.                     if (Physics.Raycast(tr.position, Vector3.down, out var floorHit, RayLength, LayerMasks.Floor)) {
    6.                         Gizmos.DrawLine(tr.position, floorHit.point);
    7.                         tr.position = floorHit.point;
    8.                     }
    9.                     else {
    10.                         Gizmos.DrawRay(tr.position, Vector3.down * RayLength);
    11.                     }
    12.                     break;
    13.                 case Stickiness.Ceiling:
    14.                     if (Physics.Raycast(tr.position, Vector3.up, out var ceilingHit, RayLength, LayerMasks.Ceiling)) {
    15.                         Gizmos.DrawLine(tr.position, ceilingHit.point);
    16.                         tr.position = ceilingHit.point;
    17.                     }
    18.                     else {
    19.                         Gizmos.DrawRay(tr.position, Vector3.up * RayLength);
    20.                     }
    21.                     break;
    22.                 case Stickiness.BackWalls:
    23.                     if (Physics.Raycast(tr.position, -tr.forward, out var backWallHit, RayLength, LayerMasks.Walls)) {
    24.                         Gizmos.DrawLine(tr.position, backWallHit.point);
    25.                         tr.position = backWallHit.point;
    26.                     }
    27.                     else {
    28.                         Gizmos.DrawRay(tr.position, -tr.forward * RayLength);
    29.                     }
    30.                     break;
    31.                 case Stickiness.FrontWalls:
    32.                     if (Physics.Raycast(tr.position, tr.forward, out var frontWallHit, RayLength, LayerMasks.Walls)) {
    33.                         tr.position = frontWallHit.point;
    34.                         Gizmos.DrawLine(tr.position, frontWallHit.point);
    35.                     }
    36.                     else {
    37.                         Gizmos.DrawRay(tr.position, tr.forward * RayLength);
    38.                     }
    39.                     break;
    40.             }
    41.         }
    and example of it being called

    Code (CSharp):
    1.         private void OnDrawGizmosSelected() {
    2.             if (SnapToFloor) {
    3.                 LevelAdjustmentExtension.CheckAdjust(transform, Stickiness.Floors);
    4.             }
    5.             else {
    6.                 LevelAdjustmentExtension.CheckAdjust(transform, FaceAwayFromWall ? Stickiness.BackWalls : Stickiness.FrontWalls);
    7.             }
    8.         }
    upload_2019-3-19_22-12-51.png
     
    Last edited: Mar 20, 2019
  2. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,334
    A guess, but I assume that the raycasts are not happening in the prefab scene, but in the scene active in the background instead.

    You can check if that's the case - position something in the scene around the origin, and see if those objects are hit.
     
  3. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    Runtime APIs always work against the normal scenes by default. We have no way of knowing if they are called from within an object that exists in the Prefab Mode, or from an object in the normal scenes.

    in order to be able to do raycasts in Prefab Mode, you'll need to use APIs that are aware of the different contexts.

    To get the scene a given GameObject is part of (this can be the Prefab Mode preview scene for objects in Prefab Mode):
    https://docs.unity3d.com/ScriptReference/GameObject-scene.html

    To get the Physics Scene from that scene:
    https://docs.unity3d.com/ScriptReference/PhysicsSceneExtensions.GetPhysicsScene.html

    To raycast against a specific physics scene:
    https://docs.unity3d.com/ScriptReference/PhysicsScene.Raycast.html
     
  4. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,334
    Did not know about GetPhysicsScene or PhysicsScene.Raycast. That's neat!
     
  5. Silly_Rollo

    Silly_Rollo

    Joined:
    Dec 21, 2012
    Posts:
    501
    That worked, thanks! I didn't know about the GetPhysicsScene/SceneRaycast either. I've been in the process of converting from custom nested prefabs to the new official ones and it has been pretty smooth. Being able to edit directly in the prefab scene is much faster than dragging things into a scene.
     
    runevision likes this.
  6. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    I haven't used these APIs a lot myself either, and it took me a little while to realize they can be chained like this:

    Code (CSharp):
    1. gameObject.scene.GetPhysicsScene().Raycast(...);
     
  7. illinar

    illinar

    Joined:
    Apr 6, 2011
    Posts:
    863
    I'm trying this, and I have this in a script in my prefab:
    Code (CSharp):
    1.     public void OnValidate()
    2.     {
    3.         Debug.Log(gameObject.scene.GetPhysicsScene());
    4.     }
    And I'm getting errors:
    upload_2019-10-31_15-52-33.png

    This happens in prefab mode and on asset modification in Project tab. It logs physics world correctly only when I open the prefab, and when I begin to modify it it logs errors again. Is the context different in OnValidate()? What's going on?

    I realized that it calls OnValidate for every instance of the prefab as well, but still it cant get a valid scene from a prefab in prefab edit mode.
     
    Last edited: Oct 31, 2019
  8. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    Assets are not in scenes so you'll need to check that gameObject.scene is not null before attempting any raycasts.

    I don't know what you mean that it happens in Prefab Mode, when you also say it works when you open the Prefab, since that's the same as going in Prefab Mode. But perhaps all the errors come from Assets, so try fixing that first.
     
  9. illinar

    illinar

    Joined:
    Apr 6, 2011
    Posts:
    863
    Here is what is actually happening.

    Code (CSharp):
    1.     public void OnValidate()
    2.     {
    3.         if (gameObject.scene.IsValid())
    4.         {
    5.             Debug.Log("Scene is valid", gameObject);
    6.             // var physicsScene = gameObject.scene.GetPhysicsScene();
    7.         }
    8.         else
    9.         {
    10.             Debug.Log("Scene is not valid", gameObject);
    11.         }
    12.     }
    I only have the prefab in assets. When I modify as an asset, OnValidate() gets called three times.

    upload_2019-10-31_20-17-0.png

    Two logs cant be traced to any game object, and the third one highlights the asset.

    But when I open that prefab, it calls OnValidate() just once, logs "valid", and highlights opened prefab.

    upload_2019-10-31_20-18-47.png

    If I modify the opened prefab, it will now do two "not valid" logs, both without a traceable caller. Or it will do three "not valid" logs as before, last of which is from the asset.

    Sorry, it's a bit off topic.
     
  10. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    The calls where the scene is not valid are from the Prefab Asset, because the Asset gets modified. If you turn Auto Save off, then you shouldn't get those in Prefab Mode until the point where you choose the save the Prefab. In both cases, it's expected and by design (apart from the fact that it's called twice on the Asset instead of just once, which I don't know the reason for).
     
  11. illinar

    illinar

    Joined:
    Apr 6, 2011
    Posts:
    863
    It's definitely called three times for me, which is not a problem but I'm curious why that happens.

    The problem is that when prefab is opened and auto save is off, OnValidate() is never called apart from the exact moment its opened. So I can't make physics related queries when asset is modified (or maybe I can cache the physics scene when the prefab is opened).

    But it's not a problem I got a workaround that works better for me anyway (as janky as it is). I'll just instantiate prefab somewhere, do the physics, and then remove it.

    Thank you for the replies.
     
    Last edited: Nov 1, 2019
  12. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    OnValidate is called on all components when they are loaded. Apart from that, it's only called on a component when that component's own data is modified.

    All the calls you've seen so far is due to loading. The reason you get calls on the Asset when changing values with auto-save on is that the Asset gets saved, which triggers the loading OnValidate. However, in Prefab Mode, changing a value (without auto-save) won't call OnValidate on other components. And similarly, if you have a Prefab instance (or any GameObject really) in a Scene, you'll only get OnValidate for all components when the scene is loaded. After that you only get OnValidate for components if you change those specific components.
     
    illinar likes this.
  13. illinar

    illinar

    Joined:
    Apr 6, 2011
    Posts:
    863
    @runevision I see, thanks.

    It's probably a lot to ask, but an API to create separate physics worlds would be nice. My use case: "voxelizing" some colliders in runtime or the editor, so I need to do box casts to see which "voxels" are not empty for a given collider asset. Although a simple separate layer for those would probably be enough to do it without anything interfering.
     
  14. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    illinar likes this.
  15. SUfIaNAHMAD_

    SUfIaNAHMAD_

    Joined:
    Jun 19, 2019
    Posts:
    18
    I used two scripts, one for destroy the dimonds and second is for destroying the platform and for that I use trigger but now i also want to destroy dimond whenere raycast is -2f on y axis as same i used for my player and its work for player...
    But as you can see i fail.,, I wonder that it may be because of the less knowledege about (prefeb vs normal Object )!!!
     

    Attached Files:

  16. ratking

    ratking

    Joined:
    Feb 24, 2010
    Posts:
    350
    Just wanted to chime in to complain that, even though I use the correct Physics scene, raycasting just doesn't work reliably in the Prefab stage - at least not with meshes, box colliders seem to work fine. Is there any best practice for this?
     
    Last edited: Dec 20, 2021
  17. eliteforcevn

    eliteforcevn

    Joined:
    Oct 25, 2018
    Posts:
    47
    how about raycastall guys :D
     
  18. ratking

    ratking

    Joined:
    Feb 24, 2010
    Posts:
    350
    @eliteforcevn Did you actually test it? Compared it to Physics.Raycast in a prefab scene?
     
  19. eliteforcevn

    eliteforcevn

    Joined:
    Oct 25, 2018
    Posts:
    47
    in prefabs scene it not work :v just work in editor view :v
     
  20. Mads-Nyholm

    Mads-Nyholm

    Unity Technologies

    Joined:
    Aug 19, 2013
    Posts:
    217
    I will ask the physics team why RaycastAll was not added to the interface for PhysicsScene, might be an oversight.
     
  21. Mads-Nyholm

    Mads-Nyholm

    Unity Technologies

    Joined:
    Aug 19, 2013
    Posts:
    217
    Physics.Raycast()
    -> Always raycasts in the main scenes (the MainStage)
    vs
    gameObject.scene.GetPhysicsScene().Raycast()
    -> raycasts in the physics scene that the GameObject belongs to. Handles tooling scenes, like the PrefabStage scene.
     
  22. ratking

    ratking

    Joined:
    Feb 24, 2010
    Posts:
    350
    It doesn't work for me, that's what I'm talking about. At least not with mesh colliders. Did you actually test it?
    Or was this finally fixed?
     
  23. yant

    yant

    Unity Technologies

    Joined:
    Jul 24, 2013
    Posts:
    596
    In fact, the last variant of PhysicsScene.Raycast at the bottom of this page is exactly like the good old Physics.RaycastNonAlloc -- it will put all the hits into a user-provided buffer.

    The reason it's done that way is that back when we designed PhysicsScene, the NonAlloc postfix looked quite dated, so we just added a Raycast overload that can return multiple results. We didn't have unbound RaycastAll because we no longer wanted to encourage using the API that allocates managed memory.

    anthony
     
  24. mailfromthewilds

    mailfromthewilds

    Joined:
    Jan 31, 2020
    Posts:
    217
    Is that also the case for PhysicsScene.(SphereCast/CapsuleCast/BoxCast) ?
    As in, is the last variant in documentation/manual for these functions, equal to original Raycast non alloc versions? or at least it does have the nonalloc functionality in them?

    I was kinda panicking and tried to find any info about this in google and theres only 10 results. Its hard to get this kind of information
     
  25. BigToe

    BigToe

    Joined:
    Nov 1, 2010
    Posts:
    208
    Looks like ComputePenetration isn't accessible in the PhysicsScene. Is there a way around that?