Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
0
Question by Rangr · Oct 29, 2015 at 11:35 AM · c#findgameobjectswithtagclosestcover

Finding closest object to player

I'm working on a cover system for a stealth game. In general it's working fairly well except for one thing. When the player goes into cover, I have a script (below) that finds the closest cover object (walls, pedestals, etc.). The problem is, with long walls, the transform of the cover object is sometimes farther away than other cover pieces so the detection determines that other cover pieces are closer which is, less than helpful. If anyone has any thoughts on how to efficiently find the closest object based on it's colliders or vertices or bounds or something else I'm not thinking of, that'd be awesome. Thanks!

 void FindNear()
     {
         GameObject[] gos = GameObject.FindGameObjectsWithTag("Cover");
         Vector3 position = transform.position;
         float coverDist = coverDistance;
         foreach (GameObject go in gos)
         {
             Vector3 diff = go.transform.position /*Find Closest point on go*/ - position;
             float curDistance = diff.sqrMagnitude;
             if (curDistance < coverDist)
             {
                 closest = go;
                 coverDist = curDistance;
             }
         }
     }
Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

3 Replies

· Add your reply
  • Sort: 
avatar image
5
Best Answer

Answer by fafase · Oct 29, 2015 at 12:20 PM

Instead of using the models use some cover points. For instance, instead of defining your wall as a cover, place two empty objects at both ends of the wall.

Comment
Add comment · Show 5 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Rangr · Oct 29, 2015 at 09:04 PM 0
Share

The problem with that as I see it, is either I have the same problem but ins$$anonymous$$d of having the problem at the ends of the wall with the transform in the middle, I'd have the problem in the middle with the objects on the end, or I would need an unnecessarily large amount of empty game objects to make sure the detection was always accurate, slowing the process down a lot. I appreciate the answer, but, in my case, I don't think it'll work.

avatar image fafase · Oct 30, 2015 at 05:43 AM 0
Share

A large amount is not unnecessary. Those are empty so they don't take much. You could even have a script that collect their position and remove them so you are left with a list of Vector3.

       P                 |x3
                         |
 ------------------------------------------------------
 x1                       $$anonymous$$                              x2


See above, $$anonymous$$ is the center, you use x1 or x2 position and you get x1 being the closest to P while your original system would have given x3 as closest. Your call.

avatar image Rangr fafase · Nov 01, 2015 at 03:51 AM 0
Share

A huge amount might not be necessary but I would still need a lot to be perfectly accurate, which, being a s$$anonymous$$lth game, is one of my core objectives. Even on the arcade-y style tile based world I'm working in, there would be areas where it would be possible to be closer to an object on a wall you don't want to be on than an object on a wall you do want to be on until you get to a point of the empty objects being about a quarter of the player size (rough estimation) apart from each other. In addition, the way I currently work the movement while in cover would need complete revamping if I used this method, which, while it may be a good idea in the long run because it's not exactly the best method, I'm currently not super inclined to do if I can help it. I'll think on how I could change it to work using this method though. I have not decided on a solution as of yet though.

avatar image ttRevan · Oct 30, 2015 at 08:11 PM 0
Share

I also suggest you stick with this solution and not OverlapSphere, because:

  1. OverlapSphere only checks against the bounding volumes

  2. OverlapSphere doesn't give you point of collision. to abtain that you need to do raycasting

  3. special cover points objects give you more control over covering, e.g. you can put a script with additional parameters on a point object, which can be different for 2 corners of the same wall.

  4. you decouple your level's visuals from gameplay logic, which, also, give you more flexibility

avatar image meat5000 ♦ ttRevan · Nov 03, 2015 at 11:50 AM 0
Share

There is no point of collision in this case. One is checking for distance. fafase hit the nail on the head with ClosestPointOnBounds so you can find the closest point to the player, or any object.

The only reason I would personally not use empty objects is that it simply comes across as a total ballache. Thats the only reason. If I can avoid 100 unnecessary editor operations Id be a happy man :P For me, procedural wins every time.

avatar image
4

Answer by meat5000 · Oct 29, 2015 at 09:09 PM

Simply use OverlapSphere to obtain all the colliders (use layermask!) and iterate the list for the closest one based on other.transform.position - transform.position

Edit: @fafase's suggestion of ClosetPointToBounds makes this solution work well.

Here's a test class I threw together. Player and Terrain (8/9) are exluded from the OverlapSphere. Press G to fire off the test. It should spit out the closest object at the end.

There is a fair amount of calculation which increases as more objects are involved, rather dramatically. To improve on calculation it doesnt compare all objects together but just against the current closest object. To make this work I specified a Max Search Distance which acts as the threshold for the 'lastLength' variable to work against. You can also put this into OverlapSphere's radius to limit the objects which are entered in to the array, but I haven't bothered. Its pretty crude but it seems to work.

 using UnityEngine;
 using System.Collections;
 
 //OverlapSphere Test
 public class OSTest : MonoBehaviour {
 
     float maxSearchDistance = 50.0f;
     Collider thisOne;
     LayerMask lm = ~(1<<8 | 1<<9); //Player and Terrain
 
     void Update ()
     {
         Collider[] myColArray;
         float lastLength = maxSearchDistance;
         bool flagSet = false;
         
         if(Input.GetKeyDown(KeyCode.G))
         {
             myColArray = Physics.OverlapSphere(transform.position, 50.0f, lm);
             for(int i = 0; i < myColArray.Length; i++)
             {
                 Vector3 temp = myColArray[i].ClosestPointOnBounds(transform.position);
                 Vector3 tempLength = temp - transform.position;
                 float sqrLength = tempLength.sqrMagnitude;
                 
                 if(sqrLength < lastLength*lastLength)
                 {
                     thisOne = myColArray[i];
                     lastLength = tempLength.magnitude;
                     flagSet = true;
                 }
             }
             if(flagSet)
             {
                 Debug.Log(thisOne.gameObject.name);
             }
         }
     }
 }

The Enlongated cube is successfully detected despite the origin being clearly much further away than the other objects.

alt text

If this solution works for you make sure to give fafase a +1 on his ClosestBounds comment below this answer.


example.jpg (42.3 kB)
Comment
Add comment · Show 12 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image FirePlantGames · Oct 30, 2015 at 12:45 AM 1
Share

Shoot! beat me to it! But, overlap sphere's are your friend.

avatar image fafase · Oct 30, 2015 at 05:38 AM 1
Share

How is that fixing the origin issue? The problem was that if you have a long wall, the position is in the middle while the closest part may be one of the end. This is just a more simple way to get the same result isn't it?

avatar image RharryR · Oct 30, 2015 at 05:55 AM 0
Share

Overlap spheres make me happy :D

avatar image meat5000 ♦ · Oct 30, 2015 at 09:07 AM 0
Share

@fafase Answered on mobile last night. I may have missed something. Bit tricky to negotiate UA on a 4" screen :P I'd say your method would work the most reliably.

But then, OverlapSphere detects bounding volumes, so its just worth trying it.

avatar image fafase meat5000 ♦ · Oct 30, 2015 at 10:36 AM 0
Share

$$anonymous$$y guess is that using OverlapSphere will optimise the search, since it will return fewer item to iterate through.

On the other hand, there is an overhead since the physics engine cannot just figure out what is around it.

But my main concern is that the collider array will provide the position of the colliders and not the closest part of the collider. So I would think, it is not really solving the initial problem.

It is possible to use ClosestPointOnBounds on the collider array though which will just add up to the process.

avatar image FirePlantGames fafase · Oct 30, 2015 at 06:02 PM 0
Share

Well, if I remember correctly overlapsphere is an array, and when it finds a collider that will add it to the array. $$anonymous$$eaning that when you reach the first (nearest) collider it will have an index of [0] this is the collider you would want to use?? Could be wrong, but pretty sure that's how it works.

Show more comments
avatar image wibble82 · Oct 30, 2015 at 11:30 AM 0
Share

This doesn't seem like a solution to me - its just a faster way to hit the same problem.

avatar image meat5000 ♦ wibble82 · Nov 03, 2015 at 11:45 AM 0
Share

$$anonymous$$odified that. Now its a solution (and it works).

avatar image fafase meat5000 ♦ · Nov 03, 2015 at 12:43 PM 0
Share

But now how do you find the hiding point? I mean this is surely giving the closest point to the player, but looking at your pic, if the cylinder is the enemy, you want to go behind the wall, but your method will return the corner that is facing the enemy.

In any level design, you would define the enemy to come from a specific part of the map, allowing the designer to place safe points. I still think the hassle of hundreds empty objects is worth it.

Or you have one on each side and you check if safe (with linecasting) before placing as closest. Then you get closest and safest.

It may be wise to come up with some editor script to store them in JSON for instance, and have some Load, Save editor buttons. Then you can load them in the scene to visualize them and save them if you apply modifications.

Also, the JSON would save only the positions, so it is faster and smaller than all the Transforms. At runtime, you just run the array of positions through the method.

Show more comments
avatar image
2

Answer by wibble82 · Oct 30, 2015 at 11:29 AM

Hi there

There are varying approaches to this that balance accuracy vs cost.

First up, using OverlapSphere is a good optimisation - it helps find all the objects you actually care about, rather than having to check the entire world. For example, if the player can only 'find cover' in objects < 1m away, you want to start with a list of objects within 1m!

Once you have them, the 'hard core' approach would be to check every vertex of the mesh, but that's a little crazy. My gut instinct would be:

  • iterate over all game objects with mesh renderers you find

  • transform your test point (i.e. the player transform) into local space of the renderer

  • clamp it into within the bounds of the mesh

  • transform it back into world space

This little trick will give you the closest bound between your object and the local bounding box of the mesh. Assuming your objects are fairly boxy, testing the distance between the player and this point will be a good approximation.

Note that if you're happy to drive off colliders instead of the meshes themselves, you could use Collider.ClosestPointOnBounds instead of my clamping process. Either way, the idea is to find the closest point on the target object to the player, then test the distance to it.

Also, use the debug renderer to draw the closest points you find, just in case its not working and you can't understand why!

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Rangr · Nov 01, 2015 at 03:56 AM 0
Share

Would it be possible for you to break that down a little more? As much as I like to think I am a decent programmer, I am still fairly new to Unity, C#, and program$$anonymous$$g in general, and am not quite getting what you are saying. I have not decided on a solution as of yet though.

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

8 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Multiple Cars not working 1 Answer

Distribute terrain in zones 3 Answers

How to check for an empty array? 1 Answer

Game lagging while looking for closest Enemy 2 Answers

Getcomponent error? 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges