How can I create Raycast bullet innaccuracy as a cone?
The earliest solutions I had come up with for having deviation from perfect accuracy involved varying the direction up and down as well as left and right by a random number. While this worked (and it was on a much earlier project that I no longer have), it produced a square pattern. While I have figured out how to make the possible area a cone, I now cannot figure out how to get it to point in the appropriate direction.
float absDir = Random.value * Mathf.PI * 2;
float offShoot = Random.value * accuracy;
Vector3 Dir = new Vector3(Mathf.Cos(absDir) * offShoot,Mathf.Sin(absDir) * offShoot, 100);
Dir.Normalize();
RaycastHit wasShot;
if (Physics.Raycast(Muzzle.position, Dir, out wasShot, 1000.0f))
//Etc
I can't multiply it by another Vector3, such as Muzzle.forward, and none of the Vector3 class functions look like they'd be of much use, either.
convert the dir vector into a quaternion rotation with Quaternion.LookRotation() then multiply that rotation by the direction you want to fire.
Quaternion randomRot = Quaternion.LookRotation(randomDir); Vector3 exactDir = randomRot * generalDir;
Answer by Chronos-L · Jun 03, 2013 at 02:03 AM
I extracted this from my scripts, it might not be what you want or work with your project, but I am sure it will help you in one way or another.
using UnityEngine;
using System.Collections;
public class Shotgun : MonoBehaviour {
//These 2 controls the spread of the cone
public float scaleLimit = 2.0f;
public float z = 10f;
//Shoots multiple rays to check the programming
public int count = 30;
void Update () {
for( int i = 0; i < count; ++i ) {
ShootRay();
}
}
void ShootRay() {
// Try this one first, before using the second one
// The Ray-hits will form a ring
float randomRadius = scaleLimit;
// The Ray-hits will be in a circular area
float randomRadius = Random.Range( 0, scaleLimit );
float randomAngle = Random.Range ( 0, 2 * Mathf.PI );
//Calculating the raycast direction
Vector3 direction = new Vector3(
randomRadius * Mathf.Cos( randomAngle ),
randomRadius * Mathf.Sin( randomAngle ),
z
);
//Make the direction match the transform
//It is like converting the Vector3.forward to transform.forward
direction = transform.TransformDirection( direction.normalized );
//Raycast and debug
Ray r = new Ray( transform.position, direction );
RaycastHit hit;
if( Physics.Raycast( r, out hit ) ) {
Debug.DrawLine( transform.position, hit.point );
}
}
}
After closer inspection to your code, the only thing that is different between our code is the transform.TransformDirection()
. I think your code will work properly when you use the TransformDirection()
.
A good alternative is to use Random.insideUnitCircle:
void ShootRay() {
// Generate a random XY point inside a circle:
Vector3 direction = Random.insideUnitCircle * scaleLimit;
direction.z = z; // circle is at Z units
direction = transform.TransformDirection( direction.normalized );
//Raycast and debug
Ray r = new Ray( transform.position, direction );
RaycastHit hit;
if( Physics.Raycast( r, out hit ) ) {
Debug.DrawLine( transform.position, hit.point );
}
}
@aldonaletto, just doing so will just make it a hemisphere, we will have no control over the cone.
Not so, the size of the random vector (the scaleLimit) gives complete control over the size of the cone.
Oh, my mistake. I was thinking that Random.insideUnitSphere
and we are just flipping the z
over one side.
@aldonaletto's alternative is way better and cleaner.
Answer by sotirosn · Jun 03, 2013 at 07:25 AM
This seems like a geometry problem where the center axis of the cone is where the player fires, i.e. the cross hairs, then you apply a random rotation to point that offset that direction. So, I would first tilt the center axis between zero and your max cone angle along a single axis perpendicular to the center. Then I would spin the result between zero and 360 degrees along the original center axis. So,
Quaternion GetRandomInsideCone(float conicAngle) {
// random tilt right (which is a random angle around the up axis)
Quaternion randomTilt = Quaternion.AngleAxis(Random.Range(0f, conicAngle), Vector3.up);
// random spin around the forward axis
Quaternion randomSpin = Quaternion.AngleAxis(Random.Range(0f, 360f), Vector3.forward);
// tilt then spin
return (randomSpin * randomTilt);
}
you can then do
// fire in direction with random 10 degree offset
Vector3 fireInDirection = new Vector3(3, 4, 5);
Vector3 fireInRandomDirection = GetRandomInsideCone(10) * fireInDirection;
// or rotate an object within the cone
Transform weapon = GetSomeTransform();
weapon.rotation = GetRandomInsideCone(conicAngle) * originalRotation;
The important part here, and in the OPs partial solution, is you have complete control of how the random is rolled. You can roll uniform 0-max. Or can get a more centered spread by adding 2 dice, or can force yourself to miss by rolling max/2 to max (for enemies who miss the first few shots and get better.)
For the Quaternions, can also use Quaternion.EulerAngle(0,randomSpin,randomTilt)
then multiply that by weapon.forward;
to get the line.
Your answer is not uniform, and will not yield good results at high angles. A fully uniform function would be this:
public static Vector3 RandomInsideCone(float radius)
{
//(sqrt(1 - z^2) * cosϕ, sqrt(1 - z^2) * sinϕ, z)
float radradius = radius * $$anonymous$$athf.PI / 360;
float z = Random.Range($$anonymous$$athf.Cos(radradius), 1);
float t = Random.Range(0, $$anonymous$$athf.PI * 2);
return new Vector3($$anonymous$$athf.Sqrt(1 - z * z) * $$anonymous$$athf.Cos(t), $$anonymous$$athf.Sqrt(1 - z * z) * $$anonymous$$athf.Sin(t), z);
}
source: http://math.stackexchange.com/questions/56784/generate-a-random-direction-within-a-cone
Your answer
Follow this Question
Related Questions
Test if a bullet will hit before shooting it? 2 Answers
Bullet goes sideways when the player gets hit 1 Answer
Range not working c# - shooting script using raycast 0 Answers
Rotate Vector to hit.normal only by set degrees 0 Answers
Doom/Wolfenstein 3D-style targeting system. Detect enemies in camera view? 0 Answers