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. Dismiss Notice

Question How can I track the hit position of a raycast on an object after moving it?

Discussion in 'Scripting' started by RafaelGomes00, Oct 5, 2023.

  1. RafaelGomes00

    RafaelGomes00

    Joined:
    Aug 13, 2020
    Posts:
    73
    I have a Raycast that hits an object once, and I'd like to know how to always determine the position of the hit, after the object has been moved.

    For example:
    If I made an hit where is the red dot...
    upload_2023-10-5_14-37-46.png

    ...And then move the object to that location, I want to know the position that the raycast had previously hitted. But based on the new object's position.
    upload_2023-10-5_14-38-48.png

    I tried using transform.InverseTransformPoint as said in some forums but it returned a location outside the object that my raycast hitted.
     
  2. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    4,019
    It calculates the point in the local space of the transform you are calling this method on.

    So be sure to run this on the transform of the object you are hitting by using the hit object's transform. Then it should give you the correct location.
     
  3. RafaelGomes00

    RafaelGomes00

    Joined:
    Aug 13, 2020
    Posts:
    73
    I have made exactly that, and as I expected, it returned a value next to 0,0. Then I thought that I should add the result of transform.InverseTransformPoint to the position of the object, and that end value was a little ahead the moving object.
     
  4. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    541
    Code (CSharp):
    1.       RaycastHit hit;
    2.       if (Physics.Raycast(start,direction,out hit,distance))
    3.       {
    4.          Vector3 pointOfInterest=hit.transform.InverseTransformPoint(hit.point);
    5.       }
     
  5. RafaelGomes00

    RafaelGomes00

    Joined:
    Aug 13, 2020
    Posts:
    73
    The red dot is where I clicked on, and the black dot is the sum of the Transform.InverseTransformPoint() and the movingPiece position
    upload_2023-10-5_15-0-49.png
     
  6. RafaelGomes00

    RafaelGomes00

    Joined:
    Aug 13, 2020
    Posts:
    73
    If I do only that, it returns a point near to 0,0 as I said on the question.
    And if I force the object to 0,0 this is where the point is:

    upload_2023-10-5_15-6-48.png
     
  7. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    4,019
    So the target keeps moving?

    Do a quick test if it's correct when the target isn't moving. If so, you have a one-frame-off issue where the object moves after you calculated the hit point position (eg script execution order).

    Or it may be a mismatch between FixedUpdate or physics movement vs Update movement, possibly applying delta time too.
     
  8. Chubzdoomer

    Chubzdoomer

    Joined:
    Sep 27, 2014
    Posts:
    106
    Here's an admittedly stupid, very likely sub-optimal solution that would nonetheless work:
    When the raycast hits an object, create a new, empty GameObject at the hit location and parent it to the object that was hit.

    Parenting it to the 'hit' object will ensure it always moves with that object, and its position will always represent the last location where the 'hit' object was... well, hit by the raycast.
     
    Kurt-Dekker, CodeSmile and Nad_B like this.
  9. RafaelGomes00

    RafaelGomes00

    Joined:
    Aug 13, 2020
    Posts:
    73
    Just found out that the problem is being caused by rotation, these moving objects may be rotated. and the InverseTransformPoint seems to mes up with the rotated ones.
     
  10. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    541
    Code (CSharp):
    1.       RaycastHit hit;
    2.       if (Physics.Raycast(start,direction,out hit,distance))
    3.       {
    4.          Vector3 localHitPoint=hit.transform.InverseTransformPoint(hit.point);
    5.       }
    6.  
    7.       Vector3 worldHitPoint=hit.transform.TransformPoint(localHitPoint);
     
    Owen-Reynolds likes this.
  11. RafaelGomes00

    RafaelGomes00

    Joined:
    Aug 13, 2020
    Posts:
    73
    I really thought about this, but I didn't made it because I was afraid of messing it all up and doing a big memory leak xD
     
    Nad_B likes this.
  12. Nad_B

    Nad_B

    Joined:
    Aug 1, 2021
    Posts:
    326
    If it's just a GameObject with no scripts that subscribe to some event handlers or some crazy static stuff... it won't cause any leak and will get destroyed when the parent object get destroyed. And if you're worrying about GC (for eg you're creating and destroying A LOT of those objects), just use a pooling mechanism. Bonus: just attach a MonoBehavior to that hit object (HitPointScript) and now you can query all the hits of the parent object (
    Parent.GetComponents<HitPointScript>()
    ) if you ever need this.

    Personally, this is the solution I'd use, as I prefer ease-of-use vs. trigonometric math any time of day. After all, this is why I chose to use a game engine like Unity instead of using my old custom built engine on top of FNA.

    It's not stupid as long as it works (and you know its limitations).
     
    Last edited: Oct 5, 2023
  13. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,917
    To follow up on what zulo wrote, using inverseTransform is a 2-step process. Use it to get the offset (raycast hit to center of object) in local space. Then apply that value to convert it back to world space when-ever you want to track it.

    For example, suppose the raycast hits at exactly +1 x from the object center, based on how it's tilted. Since the object is rotated, that point will really be at +1y or -1z, or more likely some funny xyz combination. Using transform.inverseWhatever converts that into (+1,0,0). That's known as local coords -- the distance from the object center if the object wasn't rotated. It's very useful. But clearly we can't just add that (+1,0,0) to the object's center to get back the original point, since the object is still rotated. We have to re-apply the rotation -- convert the local offset back to global by multiplying by object's rotation. There are a few ways of doing that, and I'm sure plenty of wrong ways.

    I'd say that if you want to practice the math, since it comes in handy for other things, this is doable for a determined beginner. But if you just want it to work, yeah, an empty gameObject is fine (I use empties in reverse -- when I want to mark a preset spot on a cube I create an empty there. I could type in the xyz and save some space, but empties are just easier).
     
    Nad_B likes this.
  14. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,711
    Seriously, came here to say this, just do the above and be done, move on.

    If you ever find it is a performance issue (I highly doubt it would ever be!), then revisit it.

    Unity is a beast. It makes and destroys more GameObjects before coffee than you can even imagine or put into words.
     
    Nad_B likes this.
  15. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,917
    One drawback I've found with the "child a gameObject to it" method of finding local offsets ... is being confused by all of the random empties that can be in the scene.

    For example, I make lots of empties -- or near empties -- for debugging. I leave them until the end since I always need to re-debug it a week later, and so on. These things can add up. And then I often have several different empties used for real to record positions. I try to rename the debugging ones to start with TEST, but often I "know" I'll delete it 5 minute later so don't bother, then forget to delete. At the end, I'm often staring a several mysterious empties and wondering it they're safe to delete (and if you have to wonder, they aren't).

    The other drawback is when rebuilding or copying a set-up. I need the extra steps of finding the empties and dragging the reference into the slot. Seems easy, but it's worse when I re-use an old script for a new project and have to remember (and is it dragged into a slot or will the script find it? needs a particular name? needs to be a sibling under a common empty parent? starts disabled?)

    So that's why I say, if you have a math background and an hour to mess around, doing this stuff with pure math and no extra gameObject markers is worth it. But if the idea that a point can be either in world space, or as a local offset seems confusing -- use an empty gameObject. And in that case it can be a big help to have an error check like "if(emptyINeed==null) Debug.Log("this requires an empty in spot X with other requirements Y and Z")"
     
  16. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,711
    I take your point, clutter always makes things harder.

    But from reading this post it sounds like these extra objects might be transient "marks" or "bullet hits" or something, and as such they could be named as such, or hidden with hide flags, and in any case, they go away when the parent dies.

    However, reading OP's previous thread:

    https://forum.unity.com/threads/how-to-get-a-face-on-given-direction-of-a-cube.1501043/#post-9384893

    Makes me think these are actually "adaptor points" or "hookup points" in which case they should be named as such and then they really do need to be known about after the point, such as to hook the little "Int-A-Lok" discs (or as they call them today "Brain Flakes") together.

    Or connecting dungeon rooms / corridors, which is more my style... :)