Search Unity

Calculating Shortest distance to target with ricochets

Discussion in 'Scripting' started by Nomad_Artisan, Apr 15, 2014.

  1. Nomad_Artisan

    Nomad_Artisan

    Joined:
    Jun 29, 2013
    Posts:
    20
    Hi,
    I have objects that will only move in a straight line and will ricochet off of any surface that they hit.

    I'm trying to calculate the angle at which to launch the object, taking ricochet's into account, that will result in the shortest path taken to reach a target.

    I'm using the following line of code to apply force to the object

    rigidbody.AddForce(GetLaunchDir() * 500);

    Code (csharp):
    1. Vector3 GetLaunchDir()
    2.     {
    3.         Vector3 bestDir = new Vector3(0,0,0); // this is used to store the best direction to launch this rigidbody
    4.         collider.enabled = false; // I turn off the collider on this object so it isn't caught by my sphere casting
    5.  
    6.         for(float deg = 0; deg < 360; deg += 1) // I'm checking every euler angle between 0 and 360
    7.         {
    8.             Vector3 dir = (Quaternion.Euler(0,deg,0) * Vector3.forward).normalized; // this sets the starting angle to be checked this loop
    9.             Vector3 start = transform.position; // start out at enemy's current position
    10.             Vector3 launchDir = dir;  //store the starting angle for this check, not to be changed
    11.  
    12.             float distance = -Mathf.Infinity; // store the shortest distance found
    13.  
    14.             float travelDist = 1000; // this is the maximum distance that will be checked
    15.  
    16.  
    17.             for(float dist = 0; dist < travelDist; dist += 0) // for each angle, calculate ricochets so long as the distance 'traveled' by the check hasn't exceeded travelDist, i have travelDist decrement within the loop rather than dist increment
    18.             {
    19.                 RaycastHit tempHit; // store raycast hit from spherecast this loop
    20.                 if(Physics.SphereCast(start,objRad,dir, out tempHit, travelDist))   // get the next object this enemy will hit if traveling in direction dir from position start
    21.                                 // objRad is the object's collider radius
    22.                 {
    23.                     travelDist -= Vector3.Distance(start, tempHit.point); //decrement travelDist by the distance of this check
    24.  
    25.                     if(tempHit.transform.gameObject == target.gameObject)   //if the hit object is the target, see if this is the best direction so far
    26.                     {
    27.                         if(travelDist > distance) //if travelDist is greater than distance, then this is currently the shortest path
    28.                         {
    29.                             bestDir = launchDir;// set bestDir to the initial launchDir
    30.                             distance = travelDist;//set distance to travelDist as this is the new best to beast
    31.                         }
    32.  
    33.  
    34.                         travelDist = 0;
    35.                     }
    36.                                         else // if this hit an object other than the target
    37.                                        {
    38.                     start = tempHit.point;  // set start to the currently hit position so the next check starts from there
    39.                     dir = Vector3.Reflect(dir,tempHit.normal).normalized;   // reflect dir off of the normal of raycast hit
    40.                                         }
    41.                 }
    42.                 else // if the spherecast didn't hit anything, set travelDist to 0 to cancel this angle's check
    43.                 {
    44.                     travelDist = 0;
    45.                 }
    46.             }
    47.  
    48.         }
    49.         collider.enabled = true; //after all checks, set this object's collider back on
    50.         return bestDir; //return the bestDir found to be used for the force
    51.     }
    This is giving me unexpected and unwanted behavior, launching the object in odd directions. What am I doing wrong?
    I am searching for a solution and have yet to find one.

    Thanks for your time!
     
    Last edited: Apr 15, 2014
  2. Graham-Dunnett

    Graham-Dunnett

    Administrator

    Joined:
    Jun 2, 2009
    Posts:
    4,287
    Well, the conceptual way you do this is:

    a) You have a start position A, and a destination position B.

    b) You have an infinite line W.

    c) You calculate the position, C on the line W, which gives the shortest path from A to B via C.

    d) Reflect the position B about the line W. (So B to B' intersects W at right angles.)

    e) Compute C which is where A->B' crosses W.

    $reflect.gif
     
  3. Nomad_Artisan

    Nomad_Artisan

    Joined:
    Jun 29, 2013
    Posts:
    20
    How do you calculate W's position or C's position on W?

    Also, I need to allow any number of ricochets. Will this allow for that?
     
  4. Nomad_Artisan

    Nomad_Artisan

    Joined:
    Jun 29, 2013
    Posts:
    20
    I got the functionality I was looking for.
    I ended up splitting the function in two and, rather than using a for loop to check if the direction was good, I had the spherecast function return a bool if it hits the player, and if it hits something else, I have it return another iteration of the spherecast function. In this way, it continues spherecasting with a new starting position and direction until it doesn't hit anything or it hits the player.