Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 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 sevensixtytwo · Aug 28, 2014 at 02:43 PM · raycastprojectileyield

Simulating proper projectile travel time using Raycast

Hi all. I'm currently working on a tank game in order to become more familiar with Unity. In the game, there will be projectiles moving at high speeds (1,500 m/s or more). At first, I was content with using Raycasts to represent these high speed shots, but as engagement ranges slowly expanded to 2-3 km, discrepancies began to occur.

First of all, I use a modified version of the TrajectorySimulation, and CalculateLeadForProjectiles scripts on the wiki along with a modified CalculateElevation function I kitbashed together from various sources. I don't have the modified scripts with me right now as I am using a different computer. I'll try to post them as soon as I can.

To the main question: Is there a way to properly simulate travel time while retaining the use of Raycasts?

Attached is an illustration of my current methods and problemalt text

I have tried adding a delay in between Raycasts via yield WaitForSeconds(TimeProjectileMoves1Meter) but this only results in the simulation slowing to an unacceptable crawl, most likely limited by Update.

Thoughts on the matter would be most appreciated. :)

UPDATE: here is my code for firing the gun:

 static function ShootProjectile(bullet:Projectile,muzzle:Transform,velocity:Vector3) {
     var numVertices : int = 3000;
     var positions = new Vector3[numVertices];
     // The first line point is wherever the player's cannon, etc is
     positions[0] = muzzle.position;
  
     // Time it takes to traverse one segment of length segScale
     var segmentScale : float = 1.0;
     var segTime : float;
  
     // The velocity of the current segment
     var segVelocity : Vector3 = (muzzle.forward * bullet.speed) + (velocity);
  
     var hit : RaycastHit;
  
     for (var i=1; i<numVertices; i++) {
 //        Debug.Log(segTime);
         // worry about if velocity has zero magnitude
         if(segVelocity.sqrMagnitude != 0)
             segTime = segmentScale/segVelocity.magnitude;
         else
             segTime = 0;
         // Add velocity from gravity for this segment's timestep
         segVelocity = segVelocity + Physics.gravity*segTime;
  
         // Check to see if we're going to hit a physics object
         if(Physics.Raycast(positions[i-1], segVelocity, hit, segmentScale)){
             Debug.DrawLine (positions[i-1], hit.point, Color.red,30,false);
             // set next position to the position where we hit the physics object
             positions[i] = positions[i-1] + segVelocity.normalized*hit.distance;
             ballistics.HitProjectile(segTime,positions,i,segVelocity,segmentScale,bullet);
             return;
             }
         // If our raycast hit no objects, then set the next position to the last one plus v*t
         else {
             positions[i] = positions[i-1] + segVelocity*segTime;
             Debug.DrawLine (positions[i-1], positions[i-1] + segVelocity*segTime, Color.red,30,false);
             }
            }
     }
     
 static function HitProjectile(time:float,positions:Vector3[],length:int,velocity:Vector3,scale:float,bullet:Projectile) {
     for (var i = 1; i < length+2; i++) {
         var hit : RaycastHit;
         if(Physics.Raycast(positions[i-1], velocity, hit, scale)){
             Debug.DrawLine (positions[i-1], hit.point, Color.yellow,30,false);
             var boom = new Instantiate(bullet.explosion,hit.point,Quaternion.identity);
             var hitInfo : BulletHit = new BulletHit(20,10,hit.point);
             hit.transform.root.SendMessage("TakeDamage",hitInfo,SendMessageOptions.DontRequireReceiver);
             return;
             }
            else {
                Debug.DrawLine (positions[i-1], positions[i-1]+velocity, Color.yellow,30,false);
                }
             yield WaitForSeconds(time);
         }
         Debug.Log("No hit?");    
     }

As you can see, at the start of the for loop, segTime is defined as unit (segmentScale) divided by segVelocity (speed). The yield wait is in the HitProjectile function where I believed it would delay casting the next ray by the time it takes the bullet to move 1 segment.

To clarify, I am looking for a way to add a delay in between the Rays casted in the HitProjectile function. This should simulate the actual time it takes for the bullet to travel across the previously calculated trajectory in ShootProjectile.

The script is given the tank's equipped bullet type, muzzle Transform and current velocity.

problem.png (78.9 kB)
Comment
Add comment · Show 3
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 robertbu · Aug 28, 2014 at 02:54 PM 0
Share

You did not implement your wait correctly. Post your code with your 'WaitForSeconds' code attempt. Also it seems to me the way should be (distance to target) / (projectile speed).

avatar image sevensixtytwo · Aug 28, 2014 at 11:20 PM 0
Share

aha, yes, I did use the totalDistance/Speed in my 2nd $$anonymous$$ethod. The problem is that on flat terrain, the Time is calculated from the first Ray's hitpoint, which due to the $$anonymous$$ing function is most probably further back from the target. This causes a miss as the second Ray casts only when the target has passed the intercept window.

avatar image sevensixtytwo · Sep 05, 2014 at 01:06 AM 0
Share

Huh, with the current problems, it's impossible to hit something silhouetted against a sky (on an elevated position) since the raycast reads the max distance and calculates travel time appropriately. Back to projectiles and upping the solver count for me.

2 Replies

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

Answer by sevensixtytwo · Sep 06, 2014 at 11:54 AM

I'll post this here in case any other noobs like me get the same problem.

Projectile.js

 #pragma strict
 
 public var speed : float;
 public var trace : Transform;
 public var explosion : GameObject;
 public var explosionAudio : AudioClip;
 
 public var penetration : int = 20;
 public var damage : int = 10;
 
 public var layerMask : LayerMask; //make sure we aren't in this layer
 private var previousPos : Vector3;
 private var thisPos : Vector3;
 private var stepDirection;
 private var stepSize;
 
 function Awake() { 
     }
 
 function Start () {
     
     Destroy(this.gameObject,10);
     Shoot();
     
     }
 
 function Update () {
 
     }
     
 function FixedUpdate() { 
     var hitInfo : RaycastHit;
     thisPos =  transform.position;
     stepDirection = this.rigidbody.velocity.normalized;
     stepSize = (thisPos - previousPos).magnitude;
     if (stepSize > 0.1) {
         if (Physics.Raycast(previousPos, stepDirection, hitInfo, stepSize, layerMask)) {
             Destruct(hitInfo.point,hitInfo.normal,hitInfo.transform.root);
             }
         else {
             previousPos = thisPos;
             }
         }
     }
     
 function Destruct(point:Vector3,normal:Vector3,target:Transform) {
     var hitNormal = Quaternion.Euler(normal);
     var boom = new Instantiate(explosion,point,hitNormal);
     AudioSource.PlayClipAtPoint(explosionAudio,transform.position,2f);
     Destroy(this.gameObject);
     }
 
 function Shoot () {
 
     previousPos = this.transform.position;
     this.rigidbody.AddForce(this.transform.forward * speed, ForceMode.VelocityChange);
     
     }

So finally got around this by going back to using instantiated prefabs as projectiles and then modifying the DontGoThroughThings script on the wiki.

For those who don't know about it, it essentially checks the area behind the projectile every frame using a raycast. If the raycast hits something, it teleports the rigidbody to the hit point.

Unfortunately, this caused a bit of a problem since it would most likely pick up the collider it passed through last, leading to one-shot killing tanks from the front or invulnerable rear armor. So what I did was change the direction of the cast. Instead of going backwards, the ray is shot from the projectile's previous position. Works flawlessly for me.

Comment
Add comment · Show 4 · 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 sevensixtytwo · Sep 06, 2014 at 11:58 AM 0
Share

With this, projectiles going 1550m/s are totally possible.

avatar image Ruffles182 · Mar 01, 2018 at 12:24 PM 1
Share

it still works :)

c#

 public float speed;
         public Transform trace;
         public GameObject explosion;
         public AudioClip explosionAudio;
     
         public int  penetration = 20;
         public int  damage = 10;
     
         public Layer$$anonymous$$ask layer$$anonymous$$ask; //make sure we aren't in this layer
         private Vector3 previousPos;
         private Vector3 thisPos;
         private Vector3 stepDirection;
         private float stepSize;
     
         private Rigidbody rb;
     
         // Use this for initialization
         void Start () {
     
             rb = gameObject.GetComponent<Rigidbody> ();
             Destroy(gameObject,10);
             Shoot();
         }
         
         void FixedUpdate() { 
             RaycastHit hitInfo;
             thisPos =  transform.position;
             stepDirection = rb.velocity.normalized;
             stepSize = (thisPos - previousPos).magnitude;
             if (stepSize > 0.1) {
                 if (Physics.Raycast(previousPos, stepDirection, out hitInfo, stepSize, layer$$anonymous$$ask)) {
                     Destruct(hitInfo.point,hitInfo.normal,hitInfo.transform.root);
                 }
                 else {
                     previousPos = thisPos;
                 }
             }
         }
     
         void Destruct(Vector3 point,Vector3 normal,Transform target) {
             Quaternion hitNormal = Quaternion.Euler(normal);
             //GameObject boom = Instantiate(explosion,point,hitNormal);
             Instantiate(explosion,point,hitNormal);
             AudioSource.PlayClipAtPoint(explosionAudio,transform.position,2f);
             Destroy(gameObject);
         }
     
         void Shoot () {
             previousPos = transform.position;
             rb.AddForce(transform.forward * speed, Force$$anonymous$$ode.VelocityChange);
         }
 

avatar image Leverine36 Ruffles182 · Sep 16, 2020 at 12:33 AM 0
Share

Hello, you wrote this 2 years ago but I was wondering how you use it. How do you call the script? I'm very new to coding :/

avatar image Soulice Leverine36 · Nov 05, 2020 at 02:56 PM 0
Share

Create a projectile, add this script to it, then make it a prefab and instantiate on fire.

I added a debug ray and can see the raycast of the bullet, but it never fires a hit in the scan.

avatar image
0

Answer by marsfan · Sep 05, 2014 at 02:00 AM

I just instantiate a bullet prefab, the rigidbody calculates the drop and collisions automatically. Plus, you can add the bullet's mesh to that prefab.

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 sevensixtytwo · Sep 05, 2014 at 06:00 AM 0
Share

Well, yeah. That's what I usually do. Projectiles and collisions usually don't work well together when moving at 1500+ units per seconds, even on continuous dynamic collisions and whatnot. Which is why I tried using Raycasts since they collide (mostly) reliably.

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

7 People are following this question.

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

Related Questions

Need urgent help to create projectile shooting script with raycasting 1 Answer

Launching a Projectile to a Raycast 1 Answer

Raycast Projectile w/ Physics 2 Answers

Rotate RayCast/Object right on instantiation? 1 Answer

Air Strike shoot aiming at cross hairs in middle of the screen 2 Answers


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