Jump to content

How to walk from a Unity primitive to the KSP object it's contained in?


Recommended Posts

I have a laser distance measurer that does a ray cast and returns the distance to the first item the laser hits along its beam. Finding that distance works great.

But now the problem is that Physics.Raycast is a Unity routine at a low level underneath KSP. Thus when it finds a hit object, the hit object it finds is a low level Unity primitive like "Cylinder". But what I'd like to report to the user is the name of the KSP object that was hit. i.e. not "Cylnder" but "FL-T200 Fuel Tank".

I know that I can walk up the tree of GameObject's, as in "this object is part of this other object which is part of this other object which is….".

The problem is that I don't know how to detect when to stop that walk. Ideally what I'd like is a flag that tells me "this GameObject is one of the GameObjects the user sees in the user interface, like a Part from the parts bin, or a building name, or a planet name, rather than a low-level primitive they never see like "terrain polygon na039103r12"

Link to comment
Share on other sites

As a place to start, I use the following to detect the part that is under the mouse with the following code. As written it only returns valid data if the ray hits a Part, if it hits anything else it returns nothing. (Note the LayerMask so the ray is only capable of hitting layer 0 which is the Part layer.)

It is part specific though, but I would think it could be modified to do what you need, maybe with multiple cases, one for if the ray hits a Part, one for Terrain, and one for Buildings. I don't think there is any other type of object you would need to worry about hitting?

public Part SelectPartUnderMouse()
{
FlightCamera CamTest = new FlightCamera();
CamTest = FlightCamera.fetch;
Ray ray = CamTest.mainCamera.ScreenPointToRay(Input.mousePosition);
LayerMask RayMask = new LayerMask();
RayMask = 1 << 0;
RaycastHit hit;
if (Physics.Raycast(ray, out hit,Mathf.Infinity,RayMask))
{

[b]return FlightGlobals.ActiveVessel.Parts.Find(p => p.gameObject == hit.transform.gameObject);[/b]
//The critical bit. Note I'm generating a list of possible objects hit and then asking if I hit one of them. I'm not starting with the object hit and trying to work my way up.
}
return null;
}

Edited by Diazo
Link to comment
Share on other sites

I have a laser distance measurer that does a ray cast and returns the distance to the first item the laser hits along its beam. Finding that distance works great.

But now the problem is that Physics.Raycast is a Unity routine at a low level underneath KSP. Thus when it finds a hit object, the hit object it finds is a low level Unity primitive like "Cylinder". But what I'd like to report to the user is the name of the KSP object that was hit. i.e. not "Cylnder" but "FL-T200 Fuel Tank".

I know that I can walk up the tree of GameObject's, as in "this object is part of this other object which is part of this other object which is….".

There was a recent post that will be helpful to you. Once you have your RaycastHit info, you already have a GameObject entry into the hierarchy of whatever you hit. Let's say I want to find out if it's a part or the ground terrain (non-scaled space) that I've hit:

            LayerMask mask = (1 << 0) | (1 << 15);

if (Physics.Raycast(ray, out hit, 10f, mask))
{
if (hit.transform.gameObject.layer == 15)
{
// real terrain was hit
}
else
{
Part target = Part.FromGO(hit.transform.gameObject) ?? hit.transform.gameObject.GetComponentInParent<Part>();
if (target != null) Log.Write("we hit {0}", target.name);
}
}

It would be useful to write a method that prints the components attached to the GameObject hierarchy you've hit. Mine's come in handy on many occasions

Link to comment
Share on other sites

@xEvilReeperx - It looks like the existence of the Part.FromGO() static method, and it's purpose, is what I was missing.

So much undocumented stuff in KSP's API.. sooooo annoying at times. They appear to have included very little explanation in their xml-comment tags so most of the documentation is fan-made by guesswork and trial and error.

"Some sort of way to get the Part that a Unity GameObject is a component of" is exactly what I needed and it looks like that's exactly what that does.

Link to comment
Share on other sites

"Some sort of way to get the Part that a Unity GameObject is a component of" is exactly what I needed and it looks like that's exactly what that does.

Well, a "Part" is really just a component attached to a GameObject (the documentation you referred to really botched the description there), plus potentially some supporting components (like PartModules).

The second portion of that line is because the GameObject containing the collider hit by your ray might not necessarily have a Part component attached to it if the Part it's related to has a hierarchy of colliders. Consider one of the deployable solar panels or wheels, or any user-created part that has multiple primitive Box/Sphere/etc colliders.

Link to comment
Share on other sites

Well, a "Part" is really just a component attached to a GameObject (the documentation you referred to really botched the description there)

What documentation? It's not documented. I have to guess from the name of the method and its signature.

Wait, so are you saying that Part.FromGO() does NOT scan through the children of the Part's game object to see if the GO passed in is contained inside the part's GO? That it only works if the GO passed in is at *exactly* the tree depth that the Part object is connected to?

If so then what you posted wouldn't work either, would it? It only works if the tree structure looks like this:


...
|
+-- GameObject
| |
| +-- A KSP part
| |
| +-- GameObject A
| |
| +-- GameObject B <-- ray cast hits here.
| |
| +-- GameObject C
|
…

It doesn't look like it would work if KSP modeled the part in a more complex way with grandchildren or great-grandchildren, like so:


...
|
+-- GameObject
| |
| +-- A KSP part
| |
| +-- GameObject A
| |
| +-- GameObject B
| | |
| | +-- GameObject BA <--- ray cast hits here
| | |
| | +-- GameObject BB
| |
| +-- GameObject C
|
…

Isn't a tree walk upward still needed to handle that case? Or is there some guarantee that KSP never models any of its parts that way - that they all have exactly one level of depth only and no more?

Edited by Steven Mading
Link to comment
Share on other sites

What documentation? It's not documented. I have to guess from the name of the method and its signature.

Whoops, misread.

Wait, so are you saying that Part.FromGO() does NOT scan through the children of the Part's game object to see if the GO passed in is contained inside the part's GO? That it only works if the GO passed in is at *exactly* the tree depth that the Part object is connected to?

Right, that's why I was explaining why you need the second half of that line. You don't need to scan the children; if the GO you hit is included in a Part's hierarchy, the Part component will always be found in the hit GO or an ancestor

If so then what you posted wouldn't work either, would it? ...

It doesn't look like it would work if KSP modeled the part in a more complex way with grandchildren or great-grandchildren...

Isn't a tree walk upward still needed to handle that case?

Yes, walking the tree upwards is exactly what Component.GetComponentInParent does. That's why I was trying to point out that you need more than just Part.FromGO, you need the hit.transform.gameObject.GetComponentInParent<Part>(); part of that line too because Part.FromGO will fail in both of your examples

Link to comment
Share on other sites

Yes, walking the tree upwards is exactly what Component.GetComponentInParent does.

If that's true then the Unity documentation needs fixing because it says "parents" rather than "ancestors", implying it doesn't go more than one generation up (it doesn't say "grandparents"), and that perhaps the reason it's a plural is because they're violating the tree structure a bit and giving some nodes more than one parent.

That's why I was trying to point out that you need more than just Part.FromGO, you need the hit.transform.gameObject.GetComponentInParent<Part>(); part of that line too because Part.FromGO will fail in both of your examples

I never wrote any examples using Part.FromGO. I was trying to work out whether or not GetComponentInParent worked like you showed it working or if it worked like the both its name and the Unity documentation implies. By using the word "Parent" rather than "Ancestor", it carries the meaning that it only goes one level up and stops. That terminology *means* something in computer science and it appears Unity is using it slightly wrongly, if what you're saying is true.

At any rate, Unity's documentation isn't your fault. Thanks for the information.

Link to comment
Share on other sites

This thread is quite old. Please consider starting a new thread rather than reviving this one.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...