- Home /
How to raycast correctly on a generated mesh in both play and edit modes
Hello.
In the Start method of a script, I do these steps:
1) Create a new game object with the DontSave hideFlag,
2) Attach a MeshCollider to it,
3) Generate a mesh on the fly and assigning it to the new MeshCollider,
4) Perform a raycast directed to the generated mesh to be able to position other objects onto it.
DontSave flag is important because the script is marked as [ExecuteAlways]. The start method I described above works in editor when I load the scene.
My problem is that, the raycast fails to hit the generated mesh probably because the physics engine is not ready for it yet since I'm doing all these things in Start method.
For fixing the problem in play mode, I moved the step 4 to FixedUpdate method and it worked fine but I still have a huge problem in edit mode because FixedUpdate doesn't work in edit mode.
Please don't ask why this script must run in edit mode. Just to feed the curious cats, I can say that: The scene is used as map editor tool in edit mode. It loads map file and creates the objects with DontSave flag then it saved them back to the map file after user edits the map content. To be able to position the objects correctly, I'm raycasting from sky to ground on the generated ground mesh.
Back to the question...
How can I force the physics engine to prepare itself after I create the mesh and assign it to the mesh collider in both play and edit modes without using FixedUpdate? (I want to avoid it in play mode too)
That's odd, I tried to reproduce your problem but it works fine for me. Is this more or less the same as what you're doing?
using UnityEngine;
[ExecuteAlways]
public class EditorRaycast : $$anonymous$$onoBehaviour {
public bool hit;
void Start() {
var gameObject = new GameObject();
gameObject.hideFlags = HideFlags.HideAndDontSave;
var collider = gameObject.AddComponent<$$anonymous$$eshCollider>();
var mesh = new $$anonymous$$esh();
mesh.vertices = new [] {
new Vector3(-1, 0, -1),
new Vector3(-1, 0, +1),
new Vector3(+1, 0, -1),
new Vector3(+1, 0, +1)
};
mesh.triangles = new [] { 0, 1, 2, 3, 2, 1 };
collider.shared$$anonymous$$esh = mesh;
if (Physics.Raycast(new Vector3(0, 10, 0), Vector3.down)) {
hit = true;
}
DestroyImmediate(gameObject);
}
}
$$anonymous$$y logic is more complex but yes, this looks very similar to what I do. Thank you very much for giving it a try.
I'll test this and try to find what's different between your and my code.
Hi again @BastianUrbach.
Your isolated code example helped me to figure out what I was doing wrong. I had 2 different problems at the same time, one being simple and the other being complex.
The simple problem was about setting the shared$$anonymous$$esh of the collider in wrong order. I was assigning the mesh object to it as soon as I create the $$anonymous$$esh even before setting up the vertices.
The complex problem was not about raycast or mesh at all. It was about my additive scene loading logic. In play mode, the scene is loaded as async because of Addressable system and in edit mode it's loaded as sync because of EditorScene$$anonymous$$anager class. This difference causes my object creation code to be run in different order in edit and play modes.
I actually tried to fix the simple problem by changing its order correctly but because of the hidden and complex second problem, I wasn't able to see the simple problem was fixed or not so it remained unfixed for a while.
Since you helped me to figure out my problems, I'd like to mark your answer as the correct answer. I'll do it if you copy paste your comment into the answer section.
Hi there,
I will test this as soon as I am on my development machine, but give it a think:
Depending on the complexity of your use case, this might not work, or it might be a little bit overkill. I would try things in this order (you may have already done this, but humor me:
First, try simply disablin and re-enabling the collider. That might do the trick.
If it doesn't, you ma be able to flush the mesh by assigning it to null and re-assigning it: eg:
collider.shared$$anonymous$$esh = null;
collider.shared$$anonymous$$esh = mesh;
If that doesn't work, you could potentially go even more nuclear:
DestroyImmediate(collider);
gameObject.AddComponent();
collider.shared$$anonymous$$esh = mesh;
Let us know how those work for you!
Kind Regards, $$anonymous$$ander Hogan