Search Unity

  1. We've closed the job boards. If you're looking for work, or looking to hire check out Unity Connect. You can see more information here.
    Dismiss Notice
  2. We're running great holiday deals on subscriptions, swag and Asset Store packages! Take a peek at this blog for more information!
    Dismiss Notice
  3. Check out our Unite Austin 2017 YouTube playlist to catch up on what you missed. More videos coming soon.
    Dismiss Notice
  4. Unity 2017.2 is now released.
    Dismiss Notice
  5. The Unity Gear Store is here to help you look great at your next meetup, user group or conference. With all new Unity apparel, stickers and more!
    Dismiss Notice
  6. Introducing the Unity Essentials Packs! Find out more.
    Dismiss Notice
  7. Want to see the most recent patch releases? Take a peek at the patch release page.
    Dismiss Notice
  8. Unity 2017.3 beta is now available for download.
    Dismiss Notice

Shooting multiple raycasts to the exact same location each time

Discussion in 'Scripting' started by Pixelch3f, Sep 12, 2017.

  1. Pixelch3f

    Pixelch3f

    Joined:
    Sep 2, 2017
    Posts:
    7
    Hi there,

    I'm trying to add a shotgun in my game by adding multiple raycasts at the same time but I'm having trouble offsetting them. I have found several solutions for shooting raycasts in random locations but that's not what I want, I want the spread to be exactly the same each time. I.e. the first bullet in the exact centre of the screen, 2nd bullet just above, 3rd just below, 4th to the left, and 5th to the right.

    This is the code for one ray cast:
    Code (csharp):
    1.  
    2. public float fireRate = 1.0f;
    3.     private float nextFire;
    4.     public int shotgunForce = 50;
    5.     public int shotgunDamage = 10;
    6.  
    7.     public Transform gunEnd;
    8.     public Camera fpsCam;
    9.     public ParticleSystem muzzleFlash;
    10.     public GameObject impactEffect;
    11.  
    12.     void Update()
    13.     {
    14.         // Check if the player has pressed the fire button and if enough time has elapsed since they last fired
    15.         if (Input.GetKey(KeyCode.Mouse0) && Time.time > nextFire)
    16.         {
    17.             // Update the time when our player can fire next
    18.             nextFire = Time.time + fireRate;
    19.             PlayerFireWeapon();
    20.         }
    21.         if (Input.GetKey(KeyCode.Mouse1))
    22.             PlayerFireAlternative();
    23.     }
    24.  
    25.     void PlayerFireWeapon()
    26.     {
    27.             FindObjectOfType<AudioManager>().Play("ShotgunFire");
    28.             muzzleFlash.Play();
    29.             // Declare a raycast hit to store information about what our raycast has hit
    30.             RaycastHit hit;
    31.             RaycastHit hit1;
    32.  
    33.             Vector3 rayOrigin = fpsCam.ViewportToWorldPoint(new Vector3(0.5f, 0.5f, 0.0f));
    34.  
    35.             /* ------------------FIRST SHOT--------------------- */
    36.             // Check if our raycast has hit anything
    37.             if (Physics.Raycast(rayOrigin, fpsCam.transform.forward, out hit, Mathf.Infinity))
    38.             {
    39.                 // if the object hit has as target script attached, run the take damage function
    40.                 Target target = hit.transform.GetComponent<Target>();
    41.                 if (target != null)
    42.                 {
    43.                     target.takeDamage(shotgunDamage);
    44.                 }
    45.                 GameObject impactEffectInstance = Instantiate(impactEffect, hit.point, Quaternion.LookRotation(hit.normal));
    46.                 Destroy(impactEffectInstance, 1f);
    47.             }
    48.  
    49.             // Check if the object we hit has a rigidbody attached
    50.             if (hit.rigidbody != null)
    51.             {
    52.                 // Add force to the rigidbody we hit, in the direction from which it was hit
    53.                 hit.rigidbody.AddForce(-hit.normal * shotgunForce);
    54.             }
    55.  
    Then I have added another raycast by delcaring another Raycasthit varialbe 'hit1' then copy / pasting the same shooting code and editing the Physics.Raycast function to offset it, but I can't work it out. This is what I'm trying now for the 2nd raycast:
    Code (csharp):
    1.  
    2. if (Physics.Raycast(fpsCam.transform.up * 2.5f, fpsCam.transform.forward, out hit1, Mathf.Infinity))
    3.  
    And this seems to kinda work but it's not always in the same place? I think I'm in the right direction but need some help please!

    Thanks in advance!
     
  2. RoMax92

    RoMax92

    Joined:
    May 29, 2017
    Posts:
    135
    Why you dont do more Raycast with a different offset?
    In this case if the shot hit in part, you can have less damage.
     
    Pixelch3f likes this.
  3. jeffreyschoch

    jeffreyschoch

    Joined:
    Jan 21, 2015
    Posts:
    2,030
    You probably want to shoot using Camera.ViewportPointToRay. What you have looks like it would probably work for the first raycast, but then your second one doesn't use the same method for calculating the ray origin.

    Since the center of the viewport is (0.5f, 0.5f), your second raycast, if you wanted it just above the last, would be like (0.5f, 0.55f) or some small offset vertically.
     
    Pixelch3f and Lethn like this.
  4. Lethn

    Lethn

    Joined:
    May 18, 2015
    Posts:
    518


    I just realised though looking at your post you wanted something a way more specific than this but the tutorial should help you regardless, @jeffreyschoch seems to have a handle on it anyway.
     
    Last edited: Sep 12, 2017
    Pixelch3f likes this.
  5. Pixelch3f

    Pixelch3f

    Joined:
    Sep 2, 2017
    Posts:
    7
    I've just tried making more raycast origins for each like so:
    Code (csharp):
    1.  
    2. Vector3 rayOrigin1 = fpsCam.ViewportToWorldPoint(new Vector3(0.5f, 0.6f, 0.0f));
    3.             Vector3 rayOrigin2 = fpsCam.ViewportToWorldPoint(new Vector3(0.5f, 0.4f, 0.0f));
    4.             Vector3 rayOrigin3 = fpsCam.ViewportToWorldPoint(new Vector3(0.6f, 0.5f, 0.0f));
    5.             Vector3 rayOrigin4 = fpsCam.ViewportToWorldPoint(new Vector3(0.4f, 0.5f, 0.0f));
    6.  
    but they all seem to go to the exact same point still.. which makes no sense to me!

    BUT I have also just tried using 'ViewportPointToRay' as you suggested and it seems to work using these:
    Code (csharp):
    1.  
    2. Ray rayOrigin1 = fpsCam.ViewportPointToRay(new Vector3(0.5f, 0.55f, 0.0f));
    3.             Ray rayOrigin2 = fpsCam.ViewportPointToRay(new Vector3(0.5f, 0.45f, 0.0f));
    4.             Ray rayOrigin3 = fpsCam.ViewportPointToRay(new Vector3(0.55f, 0.5f, 0.0f));
    5.             Ray rayOrigin4 = fpsCam.ViewportPointToRay(new Vector3(0.45f, 0.5f, 0.0f));
    Will do some further testing and report back any issues :) Thanks for that!

    Also I have one more question, the code is a bit messy as it requires a lot of duplicated code for the rays, is there anyway I can improve on this?

    Yeah that video was useful for a random spread shotgun, but not quite what I was after, thanks though :)
     
    Last edited: Sep 13, 2017
  6. jeffreyschoch

    jeffreyschoch

    Joined:
    Jan 21, 2015
    Posts:
    2,030
    If you have duplicate code, make a function and pass in the things that change. Feel free to post your script and I'll see if I can suggest more ways to clean it up.
     
  7. Pixelch3f

    Pixelch3f

    Joined:
    Sep 2, 2017
    Posts:
    7
    I'm playing around with it, trying to use an array of Rays and then a foreach loop but can't quite work it out:
    Code (csharp):
    1.  
    2. var shots = new Ray[5];
    3.         shots[0] = fpsCam.ViewportPointToRay(new Vector3(0.5f, 0.5f, 0.0f));
    4.         shots[1] = fpsCam.ViewportPointToRay(new Vector3(0.5f, 0.55f, 0.0f));
    5.         shots[2] = fpsCam.ViewportPointToRay(new Vector3(0.5f, 0.45f, 0.0f));
    6.         shots[3] = fpsCam.ViewportPointToRay(new Vector3(0.53f, 0.5f, 0.0f));
    7.         shots[4] = fpsCam.ViewportPointToRay(new Vector3(0.47f, 0.5f, 0.0f));
    8.  
    9.         Ray currentShot = shots[0];
    10.         Ray rayOrigin = fpsCam.ViewportPointToRay(new Vector3(0.5f, 0.5f, 0.0f));
    11.         Ray rayOrigin1 = fpsCam.ViewportPointToRay(new Vector3(0.5f, 0.55f, 0.0f));
    12.         Ray rayOrigin2 = fpsCam.ViewportPointToRay(new Vector3(0.5f, 0.45f, 0.0f));
    13.         Ray rayOrigin3 = fpsCam.ViewportPointToRay(new Vector3(0.53f, 0.5f, 0.0f));
    14.         Ray rayOrigin4 = fpsCam.ViewportPointToRay(new Vector3(0.47f, 0.5f, 0.0f));
    15.      
    16.         foreach (var shot in shots)
    17.         {
    18.             if (Physics.Raycast(shots[0], out hit, Mathf.Infinity))
    19.             {
    20.                 // if the object hit has as target script attached, run the take damage function
    21.                 Target target = hit.transform.GetComponent<Target>();
    22.                 if (target != null)
    23.                 {
    24.                     target.takeDamage(shotgunDamage);
    25.                 }
    26.  
    27.                 GameObject impactEffectInstance = Instantiate(impactEffect, hit.point, Quaternion.LookRotation(hit.normal));
    28.                 Destroy(impactEffectInstance, 1f);
    29.             }
    30.  
    I don't think it'll work like that. Also trying with a function as you suggested like this:
    Code (csharp):
    1.  
    2. void shotgunShot(Ray rayOrigin, RaycastHit hit)
    3.     {
    4.      
    5.     }
    6.  
    But again I can't quite work out how to implement it, i.e. how to pass the Ray and RaycastHit into the function

    Sorry I'm quite new to this! (as you can probably tell)

    Here's my full script minus the stuff I'm testing, maybe you can let me know what you think :)

    Code (csharp):
    1.  
    2.  
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5.  
    6. public class Shotgun : MonoBehaviour
    7. {
    8.     public float fireRate = 1.0f;
    9.     private float nextFire;
    10.     public int shotgunForce = 50;
    11.     public int shotgunDamage = 10;
    12.  
    13.     public Transform gunEnd;
    14.     public Camera fpsCam;
    15.     public ParticleSystem muzzleFlash;
    16.     public GameObject impactEffect;
    17.  
    18.     void Update()
    19.     {
    20.         // Check if the player has pressed the fire button and if enough time has elapsed since they last fired
    21.         if (Input.GetKey(KeyCode.Mouse0) && Time.time > nextFire)
    22.         {
    23.             // Update the time when our player can fire next
    24.             nextFire = Time.time + fireRate;
    25.             PlayerFireWeapon();
    26.         }
    27.         if (Input.GetKey(KeyCode.Mouse1))
    28.             PlayerFireAlternative();
    29.     }
    30.  
    31.     void PlayerFireWeapon()
    32.     {
    33.         FindObjectOfType<AudioManager>().Play("ShotgunFire");
    34.         muzzleFlash.Play();
    35.         // Declare a raycast hit to store information about what our raycast has hit
    36.         RaycastHit hit;
    37.         RaycastHit hit1;
    38.         RaycastHit hit2;
    39.         RaycastHit hit3;
    40.         RaycastHit hit4;
    41.  
    42.         Ray rayOrigin = fpsCam.ViewportPointToRay(new Vector3(0.5f, 0.5f, 0.0f));
    43.         Ray rayOrigin1 = fpsCam.ViewportPointToRay(new Vector3(0.5f, 0.55f, 0.0f));
    44.         Ray rayOrigin2 = fpsCam.ViewportPointToRay(new Vector3(0.5f, 0.45f, 0.0f));
    45.         Ray rayOrigin3 = fpsCam.ViewportPointToRay(new Vector3(0.53f, 0.5f, 0.0f));
    46.         Ray rayOrigin4 = fpsCam.ViewportPointToRay(new Vector3(0.47f, 0.5f, 0.0f));
    47.  
    48.         /* ------------------FIRST SHOT--------------------- */
    49.         // Check if our raycast has hit anything
    50.         if (Physics.Raycast(rayOrigin, out hit, Mathf.Infinity))
    51.             {
    52.                 // if the object hit has as target script attached, run the take damage function
    53.                 Target target = hit.transform.GetComponent<Target>();
    54.                 if (target != null)
    55.                 {
    56.                     target.takeDamage(shotgunDamage);
    57.                 }
    58.              
    59.                 GameObject impactEffectInstance = Instantiate(impactEffect, hit.point, Quaternion.LookRotation(hit.normal));
    60.                 Destroy(impactEffectInstance, 1f);
    61.             }
    62.  
    63.         // Check if the object we hit has a rigidbody attached
    64.         if (hit.rigidbody != null)
    65.         {
    66.             // Add force to the rigidbody we hit, in the direction from which it was hit
    67.             hit.rigidbody.AddForce(-hit.normal * shotgunForce);
    68.         }
    69.  
    70.         /* ------------------SECOND SHOT--------------------- */
    71.         // Check if our raycast has hit anything
    72.         if (Physics.Raycast(rayOrigin1, out hit1, Mathf.Infinity))
    73.         {
    74.             // if the object hit has as target script attached, run the take damage function
    75.             Target target = hit1.transform.GetComponent<Target>();
    76.             if (target != null)
    77.             {
    78.                 target.takeDamage(shotgunDamage);
    79.             }
    80.             GameObject impactEffectInstance = Instantiate(impactEffect, hit1.point, Quaternion.LookRotation(hit1.normal));
    81.             Destroy(impactEffectInstance, 1f);
    82.         }
    83.      
    84.         // Check if the object we hit has a rigidbody attached
    85.         if (hit1.rigidbody != null)
    86.         {
    87.             // Add force to the rigidbody we hit, in the direction from which it was hit
    88.             hit1.rigidbody.AddForce(-hit1.normal * shotgunForce);
    89.         }
    90.          
    91.         /* ------------------THIRD SHOT---------------------*/
    92.         // Check if our raycast has hit anything
    93.         if (Physics.Raycast(rayOrigin2, out hit2, Mathf.Infinity))
    94.         {
    95.             // if the object hit has as target script attached, run the take damage function
    96.             Target target = hit2.transform.GetComponent<Target>();
    97.             if (target != null)
    98.             {
    99.                 target.takeDamage(shotgunDamage);
    100.             }
    101.             GameObject impactEffectInstance = Instantiate(impactEffect, hit2.point, Quaternion.LookRotation(hit2.normal));
    102.             Destroy(impactEffectInstance, 1f);
    103.         }
    104.      
    105.         // Check if the object we hit has a rigidbody attached
    106.         if (hit2.rigidbody != null)
    107.         {
    108.             // Add force to the rigidbody we hit, in the direction from which it was hit
    109.             hit2.rigidbody.AddForce(-hit2.normal * shotgunForce);
    110.         }
    111.  
    112.         /* ------------------FOURTH SHOT---------------------*/
    113.         // Check if our raycast has hit anything
    114.         if (Physics.Raycast(rayOrigin3, out hit3, Mathf.Infinity))
    115.         {
    116.             // if the object hit has as target script attached, run the take damage function
    117.             Target target = hit3.transform.GetComponent<Target>();
    118.             if (target != null)
    119.             {
    120.                 target.takeDamage(shotgunDamage);
    121.             }
    122.             GameObject impactEffectInstance = Instantiate(impactEffect, hit3.point, Quaternion.LookRotation(hit3.normal));
    123.             Destroy(impactEffectInstance, 1f);
    124.         }
    125.      
    126.         // Check if the object we hit has a rigidbody attached
    127.         if (hit3.rigidbody != null)
    128.         {
    129.         // Add force to the rigidbody we hit, in the direction from which it was hit
    130.             hit3.rigidbody.AddForce(-hit3.normal * shotgunForce);
    131.         }
    132.  
    133.         /* ------------------FIFTH SHOT---------------------*/
    134.         // Check if our raycast has hit anything
    135.         if (Physics.Raycast(rayOrigin4, out hit4, Mathf.Infinity))
    136.         {
    137.             // if the object hit has as target script attached, run the take damage function
    138.             Target target = hit4.transform.GetComponent<Target>();
    139.             if (target != null)
    140.             {
    141.                 target.takeDamage(shotgunDamage);
    142.             }
    143.             GameObject impactEffectInstance = Instantiate(impactEffect, hit4.point, Quaternion.LookRotation(hit4.normal));
    144.             Destroy(impactEffectInstance, 1f);
    145.         }
    146.      
    147.         // Check if the object we hit has a rigidbody attached
    148.         if (hit4.rigidbody != null)
    149.         {
    150.         // Add force to the rigidbody we hit, in the direction from which it was hit
    151.             hit4.rigidbody.AddForce(-hit4.normal * shotgunForce);
    152.         }
    153.     }
    154.  
    155.     void PlayerFireAlternative()
    156.     {
    157.         Debug.Log("You fired your weapon's alternate fire");
    158.     }
    159. }
    160.  
     
  8. jeffreyschoch

    jeffreyschoch

    Joined:
    Jan 21, 2015
    Posts:
    2,030
    Take a look through this:
    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine;
    3.  
    4. public class Shotgun : MonoBehaviour
    5. {
    6.     public float fireRate = 1.0f;
    7.     private float nextFire;
    8.     public int shotgunForce = 50;
    9.     public int shotgunDamage = 10;
    10.  
    11.     public Transform gunEnd;
    12.     public Camera fpsCam;
    13.     public ParticleSystem muzzleFlash;
    14.     public GameObject impactEffect;
    15.  
    16.     private List<Vector3> bulletOffsets;
    17.  
    18.     private void Awake()
    19.     {
    20.         bulletOffsets = new List<Vector3>()
    21.         {
    22.             new Vector3(0.5f, 0.5f, 0.0f),
    23.             new Vector3(0.5f, 0.55f, 0.0f),
    24.             new Vector3(0.5f, 0.45f, 0.0f),
    25.             new Vector3(0.53f, 0.5f, 0.0f),
    26.             new Vector3(0.47f, 0.5f, 0.0f),
    27.         };
    28.     }
    29.  
    30.  
    31.     private void Update()
    32.     {
    33.         // Check if the player has pressed the fire button and if enough time has elapsed since they last fired
    34.         if(Input.GetKey(KeyCode.Mouse0) && Time.time > nextFire)
    35.         {
    36.             // Update the time when our player can fire next
    37.             nextFire = Time.time + fireRate;
    38.             PlayerFireWeapon();
    39.         }
    40.  
    41.         if(Input.GetKey(KeyCode.Mouse1))
    42.         {
    43.             PlayerFireAlternative();
    44.         }
    45.     }
    46.  
    47.     void PlayerFireWeapon()
    48.     {
    49.         FindObjectOfType<AudioManager>().Play("ShotgunFire");
    50.         muzzleFlash.Play();
    51.  
    52.         // make a new list of rays
    53.         List<Ray> rays = new List<Ray>();
    54.  
    55.         // create the rays based on the configured offsets
    56.         foreach(Vector3 bulletOffset in bulletOffsets)
    57.         {
    58.             rays.Add(fpsCam.ViewportPointToRay(bulletOffset));
    59.         }
    60.  
    61.         // fire the rays
    62.         foreach(Ray ray in rays)
    63.         {
    64.             Fire(ray);
    65.         }
    66.     }
    67.  
    68.     // assuming all the bullets will do the same thing
    69.     private void Fire(Ray ray)
    70.     {
    71.         RaycastHit hit;
    72.         // Check if our raycast has hit anything
    73.         if(Physics.Raycast(ray, out hit, Mathf.Infinity))
    74.         {
    75.             // if the object hit has as target script attached, run the take damage function
    76.             Target target = hit.transform.GetComponent<Target>();
    77.             if(target != null)
    78.             {
    79.                 target.takeDamage(shotgunDamage);
    80.             }
    81.  
    82.             // Check if the object we hit has a rigidbody attached
    83.             if(hit.rigidbody != null)
    84.             {
    85.                 // Add force to the rigidbody we hit, in the direction from which it was hit
    86.                 hit.rigidbody.AddForce(-hit.normal * shotgunForce);
    87.             }
    88.  
    89.             GameObject impactEffectInstance = Instantiate(impactEffect, hit.point, Quaternion.LookRotation(hit.normal));
    90.             Destroy(impactEffectInstance, 1f);
    91.         }
    92.     }
    93.  
    94.     void PlayerFireAlternative()
    95.     {
    96.         Debug.Log("You fired your weapon's alternate fire");
    97.     }
    98. }
    A good rule of thumb is that if you have duplicate code or find yourself numbering your variables, you should probably be using a list or array, loops, and functions.
     
    Last edited: Sep 13, 2017
    Pixelch3f likes this.
  9. Pixelch3f

    Pixelch3f

    Joined:
    Sep 2, 2017
    Posts:
    7
    That's awesome thank you. Looks a lot cleaner now!
     
    jeffreyschoch likes this.