- Home /
bullet physics using raycast?
As the title says, I need a way to have bullet physics but using raycast. I search but most didn't give a answer to it. Kind of like this video at around 8:30: http://www.youtube.com/watch?v=3eYEOS4IHW8&feature=channel&list=UL
right now for all of the scripts I'm using a forward raycast from the camera, and some guns I don't want to do that. Here's the sniper rifle script I want to change:
var GunCamera : Camera;
var shotSound: AudioClip; // Shot sound
var bloodPrefab: GameObject; // Blood for enemy
var sparksPrefab: GameObject; // Sparks for groun
var muzzleFlash : GameObject;
var ShellPrefab : Transform;
var shootEnabled: boolean = true; // allows disabling the shots
var ChangeWep : boolean = true;
var MaxBullets = 9;
var Bullets = 9;
var Clip = 9000;
var shotInterval = 1.0;
var force = 50;
var RayCastDist : float = 50;
var Zoom : int;
function Shoot(){
var cam : Transform = Camera.main.transform;
var ray = new Ray(cam.position, cam.forward);
var hit : RaycastHit;
var trf = transform; // a little optimization
var hitRotation = Quaternion.FromToRotation(Vector3.up, hit.normal);
MuzzleFlashOn();
audio.PlayOneShot(shotSound); // play the shot sound...
var ShellClone = GameObject.Instantiate(ShellPrefab, GameObject.Find("ShellSpawner").transform.position, Quaternion.identity);
ShellClone.rigidbody.AddForce(transform.up + transform.right * 200);
if(Physics.Raycast (ray, hit, RayCastDist)){
var rot = Quaternion.FromToRotation(Vector3.up, hit.normal);
if(hit.collider.gameObject.tag == "Box"){
Debug.Log ("Hit A Box!");
if (sparksPrefab) Instantiate(sparksPrefab, hit.point, rot);
hit.rigidbody.AddForceAtPosition(force * transform.forward , hit.point);
hit.collider.SendMessageUpwards("ApplyDamage", 5.0);
//Instantiate(bulletHole, hit.point, rot);
}
if(hit.collider.gameObject.tag == "Enemy"){
Debug.Log ("Hit A Enemy!");
if (sparksPrefab) Instantiate(sparksPrefab, hit.point, rot);
}
if(hit.collider.gameObject.tag == "Target"){
Debug.Log ("Target hit!");
hit.rigidbody.AddForceAtPosition(force * transform.forward , hit.point);
hit.rigidbody.isKinematic = false;
hit.rigidbody.useGravity = true;
if (sparksPrefab) Instantiate(sparksPrefab, hit.point, rot);
} else {
if (sparksPrefab) Instantiate(sparksPrefab, hit.point, rot);
Debug.Log ("Hit Other!");
}
ChangeWep = false;
SendMessageUpwards("Recoil");
shootEnabled = false;
yield WaitForSeconds(shotInterval);
shootEnabled = true;
ChangeWep = true;
}
}
Answer by sman_47o · Aug 09, 2012 at 04:46 PM
what i would do is to make a Vector3 to simulate gravity multiply it by the distance between the hit and shooter and then add that vector to the hit.point. so depending on how far from the hit is from the shooter, the hit will drop a distance that is scale to its traveled path.
so for example:
//what i changed
var Gravity : Vector3 = Vector3(0,-0.5,0); //you might wanna mess around with this number
if(Physics.Raycast (ray, hit, RayCastDist)){
var rot = Quaternion.FromToRotation(Vector3.up, hit.normal);
hit.point = hit.point + hit.distance*Gravity;
//what i changed
if(hit.collider.gameObject.tag == "Box"){
Debug.Log ("Hit A Box!");
if (sparksPrefab) Instantiate(sparksPrefab, hit.point, rot);
hit.rigidbody.AddForceAtPosition(force * transform.forward , hit.point);
hit.collider.SendMessageUpwards("ApplyDamage", 5.0);
//Instantiate(bulletHole, hit.point, rot);
}
if(hit.collider.gameObject.tag == "Enemy"){
Debug.Log ("Hit A Enemy!");
if (sparksPrefab) Instantiate(sparksPrefab, hit.point, rot);
}
if(hit.collider.gameObject.tag == "Target"){
Debug.Log ("Target hit!");
hit.rigidbody.AddForceAtPosition(force * transform.forward , hit.point);
hit.rigidbody.isKinematic = false;
hit.rigidbody.useGravity = true;
if (sparksPrefab) Instantiate(sparksPrefab, hit.point, rot);
} else {
if (sparksPrefab) Instantiate(sparksPrefab, hit.point, rot);
Debug.Log ("Hit Other!");
}
This works, only thing is that when I draw the raycast it looks like it's just A instant drop from the start point and not a drop over time. Could be that and the drawline doesn't show it .
I'm not sure... What you could do is have it log the location of the hit in the debug window and shoot an object at say 10 meters and then shoot an object at 100 meters (and of course make sure to either disable your mouselook script or just try not to move the mouse at all)
Your basic idea is ok, but in some cases it may produce weird results: since you just shift down the hit.point according to distance, it may goes out of the surface hit and let a bullet hole in mid air. To fix this, you could get a valid hit.point just doing a second raycast to the shifted point:
... // original raycast: if (Physics.Raycast (ray, hit, RayCastDist)){ hit.point = hit.point + hit.distance*Gravity; // do a second raycast to the shifted point: if (Physics.Raycast(ray.origin, hit.point, hit, RayCastDist)){ // the original hit code goes here: var rot = Quaternion.FromToRotation(Vector3.up, hit.normal); if(hit.collider.gameObject.tag == "Box"){ Debug.Log ("Hit A Box!"); ... } }
Thanks I hadnt thaught of that and now that I think of it I'm not sure that if you were to aim above a targets, lts say head, weather or not it would ever register a hit. How I would fix my own silly mistake would be to actually apply the gravity vector directly to the ray in the if statement like so:
Ray.direction = ray.direction * gravity * RayCastDist
If(physics.Raycast(ray, hit, RayCastDist){
I think this will actually hit two birds with one stone and with it you wpuldnt need to change the hit position after the fact. Also remember that the Gravity vector could also include wind or other forces that would Change the trajectory of your bullet. I also believe it could be possible to dynamically change the "physics" being applied on the bullet (in order to cause it to curve or drop suddenly) but that would require some complex code and possibly several rays :P.
So how would I use that in the original code you posted?
Answer by aldonaletto · Aug 13, 2012 at 01:34 PM
The shooting technique used in Elite Squad probably uses multiple rays - the segments that appear in the linked video. You could do something similar by calculating the position each Update or FixedUpdate with speed, gravity and wind taken into account, and check collisions with Linecast between the last and the current positions. An easier possibility is to let Unity do the hard work for you: simply shoot a fast rigidbody (the bullet) and let physics handle the trajectory, but still check collisions with Linecast each FixedUpdate, or you'll probably miss the smaller targets - something like this (bullet script):
var bulletSpeed: float = 250; private var startPos: Vector3; // initial position (used for hit.distance calculation) private var lastPos: Vector3; // bullet position last physics cycle private var hit: RaycastHit; // hit info will be passed here
function Start(){ // initialize variables: startPos = rigidbody.position; lastPos = startPos; // accelerate the bullet: rigidbody.velocity = transform.forward * bulletSpeed; }
function FixedUpdate(){ var curPos = rigidbody.position; if (Physics.Linecast(lastPos, curPos, hit)){ HitSomething(); } lastPos = curPos; // update lastPos }
// regular collisions may be detected as well: function OnCollisionEnter(col: Collision){ // copy the important fields from col to hit: hit.rigidbody = col.rigidbody; hit.transform = col.transform; var contact = col.contacts[0]; hit.point = contact.point; hit.normal = contact.normal; hit.distance = Vector3.Distance(hit.point, startPos); HitSomething(); }
function HitSomething(){ Destroy(gameObject); // destroy the bullet when hit something // your hit code goes here: var rot = Quaternion.FromToRotation(Vector3.up, hit.normal); if(hit.collider.gameObject.tag == "Box"){ Debug.Log ("Hit A Box!"); if (sparksPrefab) Instantiate(sparksPrefab, hit.point, rot); hit.rigidbody.AddForceAtPosition(force * transform.forward , hit.point); hit.collider.SendMessageUpwards("ApplyDamage", 5.0); ... } Your Shoot function would become simpler:
... var bulletPrefab: GameObject; // drag the bullet prefab here
function Shoot(){ var trf = transform; // a little optimization MuzzleFlashOn(); audio.PlayOneShot(shotSound); // play the shot sound... var ShellClone = GameObject.Instantiate(ShellPrefab, GameObject.Find("ShellSpawner").transform.position, Quaternion.identity); ShellClone.rigidbody.AddForce(transform.up + transform.right * 200); Instantiate(bulletPrefab, trf.position, trf.rotation); ChangeWep = false; SendMessageUpwards("Recoil"); shootEnabled = false; yield WaitForSeconds(shotInterval); shootEnabled = true; ChangeWep = true;
} Notice that this is just a basic suggestion: adapting your current code to this will probably cause a couple headaches, because some parts (the hit detection and particles generation) were moved to the bullet script. The Shoot script should be attached to a spawn point object - usually an empty game object childed to the weapon and positioned at the weapon tip, oriented in the weapon forward direction. The original code always shot in the camera's forward direction, hitting anything at the screen center, but this ballistic-like code will shoot in the direction the weapon is aiming to.
Answer by Estevominador · Nov 01, 2012 at 03:55 PM
I did that! I shoot a raycast and within time, it'll increase the range. to simulate the trajectory I made to the transform decrease the rotation in the x-axis depending on bullet's weight, the air density, and the current speed of the bullet.
works cool, but if anything cross through the bullet's trajectory, even if the range is 1km still detecting the collision.
Your answer
Follow this Question
Related Questions
Can someone explain to me why this raycast doesn't fire straight? 1 Answer
HOW DO I USE RAYCASTHIT 2D???? 1 Answer
Applying Normal force from ground to player 1 Answer
Problem With Physics.Raycast 1 Answer
2D Slope acceleration 1 Answer