- Home /
Radial/omni directional shooting Problem
Hey guys.
Edit: Added images to bottom of post
I've currently managed to script my enemy shot upto a certain point, however I wish to have the enemy shoot in all directions, like a ring around it. From what I appear to understand, this will require s quick math algorithm, inputting the distance from the object, the number of projectiles and the angle for them.
So far I have this:
using UnityEngine; using System.Collections;
public class Enemy : MonoBehaviour { public float MinSpeed; public float MaxSpeed;
public float currentSpeed;
private float x, y, z;
public GameObject Projectile;
float firingRate = 1.0f; //delay between shots, in seconds
float lastFired = -100f; //absolute time of last fired shot
// Use this for initialization
void Start()
{
//currentSpeed = Random.Range(MinSpeed, MaxSpeed);
currentSpeed = 1;
//x = Random.Range(1f, 1f);
y = 7.0f;
z = 0.0f;
transform.position = new Vector3 (x, y, z);
}
// Update is called once per frame
void Update()
{
float amtToMove = currentSpeed * Time.deltaTime;
transform.Translate(Vector3.down * amtToMove);
if (Time.time < lastFired + firingRate)
{
return;
}
if (transform.position.y <= -6.0)
{
currentSpeed = 1 ;
transform.position = new Vector3(x, y, z);
}
lastFired = Time.time;
if (transform.position.y >= 0 && transform.position.y <= 1)
{
Vector3 rightposition = new Vector3(transform.position.x + transform.localScale.x * 8, transform.position.y + transform.localScale.y * 5);
Instantiate(Projectile, rightposition, transform.rotation);
Vector3 right2position = new Vector3(transform.position.x + transform.localScale.x * -8, transform.position.y + transform.localScale.y * 5);
Instantiate(Projectile, right2position, Quaternion.Euler(0, 0, 20));
Vector3 right3position = new Vector3(transform.position.x + transform.localScale.x * -8, transform.position.y + transform.localScale.y * 5);
Instantiate(Projectile, right3position, Quaternion.Euler(0, 0, 40));
Vector3 right4position = new Vector3(transform.position.x + transform.localScale.x * -8, transform.position.y + transform.localScale.y * 5);
Instantiate(Projectile, right4position, Quaternion.Euler(0, 0, 60));
Vector3 right5position = new Vector3(transform.position.x + transform.localScale.x * -8, transform.position.y + transform.localScale.y * 5);
Instantiate(Projectile, right5position, Quaternion.Euler(0, 0, 70));
Vector3 right6position = new Vector3(transform.position.x + transform.localScale.x * -8, transform.position.y + transform.localScale.y * 5);
Instantiate(Projectile, right6position, Quaternion.Euler(0, 0, 80));
Vector3 right7position = new Vector3(transform.position.x + transform.localScale.x * -8, transform.position.y + transform.localScale.y * 5);
Instantiate(Projectile, right7position, Quaternion.Euler(0, 0, 85));
Vector3 right8position = new Vector3(transform.position.x + transform.localScale.x * -8, transform.position.y + transform.localScale.y * 5);
Instantiate(Projectile, right8position, Quaternion.Euler(0, 0, 90));
}
}
}
After searching through the forums I came upon the following topic, in which someone asked a very similar question. Shooting in Multiple Directions
He was provided with the following code.
var speed : float = 1.0;
function Update () { transform.Translate(Vector3.forward Time.deltaTime speed); }
var projectile : GameObject; var amount : int = 16;
function Update () { if( Input.GetButtonUp( "Jump" ) ) { for (var i = 1; i <= amount; i++){
var instantiatedProjectile : GameObject = Instantiate( projectile, transform.position, transform.rotation );
instantiatedProjectile.transform.eulerAngles = Vector3(0, 360/amount*i, 0);
}
} }
var projectile : GameObject; var amount : int = 3;
function Update () { if( Input.GetButtonUp( "Jump" ) ) { for (var i = 0; i <= amount; i++){
var instantiatedProjectile : GameObject = Instantiate( projectile, transform.position + transform.forward*5, transform.rotation );
instantiatedProjectile.transform.eulerAngles = Vector3(0, (-30)+(60/amount*i) , 0);
}
} }
Its clear that this script is precisely what I'm after, but unfortunately I've attempted to implement it and failed a few times. If someone could please help me to translate this script into my script it would be greatly appreciated.
Answer by skovacs1 · Sep 15, 2010 at 04:03 PM
You should consider describing what you want and what exactly about your results is not what you expect. It's very hard to provide solutions for "I tried but it isn't working" without knowing how it isn't working.
Your code
You're looking to generate a pattern of projectiles in 2D space around a center point? Well then you would do something much as you were before, but you would ensure that you are instantiating not only with new rotations, but at positions that are rotated about the center point as well.
I'm not sure if this was intentional, but as your code is posted, you are instantiating all projectiles but one on the top-left of the object (assuming positive x is right and positive y is up), one oriented the same as the object to which the script is attached and the others oriented at angles rotated about the z axis.
The following is designed with your posted implementation:
using UnityEngine; using System.Collections;
public class Enemy : MonoBehaviour { public float MinSpeed = 1f; //min speed to move down at public float MaxSpeed = 1f; //max speed to move down at
public float currentSpeed; //speed to move down at
public Transform Projectile; //what to shoot
public float[] angles = new float[] {-40f, -35f, -25f, -10f, 0, 10f, 25f, 35f, 40f};
private float x; //random x position
float firingRate = 1.0f; //delay between shots, in seconds
float lastFired = -100f; //absolute time of last fired shot
void Start() { //Initialization
currentSpeed = Random.Range(MinSpeed, MaxSpeed); //Random down speed
x = Random.Range(0f, 1f); //random x position
transform.position = new Vector3(x, 7.0f, 0.0f);
}
void Update() { //Every Frame
float amtToMove = currentSpeed * Time.deltaTime; //how far to move this frame
transform.Translate(-Vector3.up * amtToMove); //move down
if (Time.time < lastFired + firingRate) return; //we just fired
//reset position if we've fallen too far
//You might consider a level manager script elsewhere to destroy enemies
//that go off the screen and spawn new ones as appropriate
if (transform.position.y <= -6.0) {
currentSpeed = Random.Range(MinSpeed, MaxSpeed);
transform.position = new Vector3(x, 7.0f, 0.0f);
}
lastFired = Time.time; //We're firing
//If we're off the screen, should we really be firing?
if (transform.position.y < 0f || transform.position.y > 1f) return;
//We'll only ever fire once with this condition at a speed of 1
//And we'll fire at the exact same time every pass as the fire rate is also 1
//Rotating the shot position and orientation is easier if they are aligned
//get the distance from the center
//I assume this is to avoid the projectile overlapping the object
//and uses scale to appropriately scale as the object scales
Vector3 localShotPos = new Vector3(0, -((new Vector2(transform.localScale.x*8f,
transform.localScale.y*5f)).magnitude));
//Since your scene appears to only be 1-7 units large,
//aren't these sizes (8 and 5) a little big?
foreach(float angle in angles) {
Quaternion rotation = Quaternion.AngleAxis(angle, transform.forward);
Vector3 shotPosition = transform.position + rotation * localShotPos;
Instantiate(Projectile, shotPosition, rotation * transform.rotation);
}
}
}
If you wanted a circular spread, you would only need to set a number of shots and change the foreach loop to something like the following:
float degree = 360f/numberOfShots;
for(float i = -180f; i < 180f; i += degree) {
Quaternion rotation = Quaternion.AngleAxis(i, transform.forward);
Vector3 shotPosition = transform.position + rotation * localShotPos;
Instantiate(Projectile, shotPosition, rotation * transform.rotation);
}
To constrain the circle by some amount and evenly distribute the shots, you would just calculate based on the constraint:
float degree = constraint/ numberOfShots;
for(float i = -constraint/2f; i < constraint/2f; i += degree) {
Quaternion rotation = Quaternion.AngleAxis(i, transform.forward);
Vector3 shotPosition = transform.position + rotation * localShotPos;
Instantiate(Projectile, shotPosition, rotation * transform.rotation);
}
Essentially by controlling your angle increments, you could divine any kind of shot pattern. If you wanted a spiral, at each shot you would just add the extra rotation to your loop controls (or rotate your enemy).
for(float i = -constraint/2f + adjustment; i < constraint/2f; i += degree){
Quaternion rotation = Quaternion.AngleAxis(i, transform.forward);
Vector3 shotPosition = transform.position + rotation * localShotPos;
Instantiate(Projectile, shotPosition, rotation * transform.rotation);
}
adjustment += degree/spiralSteps; //number of rotations before returning to the original
If you wanted your shots centered on some target, there are slightly smarter ways, but the simple approach is to just preform the rotation by the amount to aim at the target first by setting it as the adjustment above:
adjustment = Vector3.Angle(-transform.up, target.position - transform.position);
For the first example, you would add the adjustment to the angle when you calculate the rotation:
Quaternion rotation = Quaternion.AngleAxis(angle+adjustment, transform.forward);
The code you found
As with the code you found and included though, it is actually quicker and easier to instantiate the objects at the center of the transform and then move/rotate from there rather than instantiating at the correct position/rotation. You would do much the same, but with less work:
//You don't need to worry about calculating any positions or anything first
foreach(float angle in angles) {
Transform instance = (Instantiate(Projectile, transform.position, transform.rotation)
as GameObject).transform;
instance.eulerAngles.z += angle; //rotate
instance.position += -instance.up * (new Vector2(transform.localScale.x * 8f,
transform.localScale.y * 5f)).magnitude; //offset
}
Also, using that method, translating the script included would give you something like:
//Circle for(int i = 0; i < numberOfShots; i++) { Transform instance = (Instantiate(Projectile, transform.position, transform.rotation) as GameObject).transform; instance.eulerAngles.z += i * 360 / numberOfShots; //rotate //They didn't offset and they are dividing and multiplying for every shot }
//Circle subsection for(int i = 0; i < numberOfShots; i++) { Transform instance = (Instantiate(Projectile, transform.position, transform.rotation) as GameObject).transform; instance.eulerAngles.z += i * sectionSize/numberOfShots - sectionSize/2; //rotate //The extra division is so that the section aligns with your original direction }
//Note that the method in the previous section does fewer divisions/multiplications //at the expense of using floating point numbers to control the loop. //Circle which only does one division float degree = 360f/numberOfShots; for(float i = 0f; i < 360f; i += degree) { Transform instance = (Instantiate(Projectile, transform.position, transform.rotation) as GameObject).transform; instance.eulerAngles.z += i; //rotate } //Likewise for the subsection
$$anonymous$$y code works perfectly fine, though as you said, its surely not the most sensible way of doing it, but as I mentioned in my post ONLY the top code is my own, the rest is all someone elses and I have pasted it in to ask HOW it works before I adapt it to my need's. $$anonymous$$y code(top section) compiles and runs fine, I've had it running for over a week and there are no problems with it at all. I'll try what you said, adapt it etc, I'm not thinking of doing random spread, I just want a few specific patterns, though I may have some random spread at some point so that will be useful to know too.
I have now edited my 1st post with an image (http://mirror05.x264.nl/Dark/x264vsElecard/xvid.png) to explain exactly what I mean, appologies if that may have been a little vague.
Also http://1.bp.blogspot.com/_vg2SEZfWIz4/TDitNh$$anonymous$$A_bI/AAAAAAAAD-Y/XnHz-Hk9PeQ/s400/XBLA-SPELUN$$anonymous$$Y-DIA$$anonymous$$OND.jpg you can clearly see here that the bullets are firing omni from the center of the enemy(or whatever you wish to call it)
$$anonymous$$y apologies, I didn't realize that the Vector3 here had a 2 component constructor and that you were assu$$anonymous$$g a 2D space, looking down the z axis. What I meant by bizarre was your instantiation with the transformation's rotation followed by the subsequent instantiations at set rotations, rather than being relative to the original rotation.
I have adjusted the answer to better reflect your use case. In subsequent questions please specify that you are assu$$anonymous$$g a 2D space when applicable (this is a 3D engine after all). Also, a clearer description of what you want or of what the code is actually producing that you didn't expect is meaningful to getting correct answers to questions.