- Home /
Argument out of range. Parameter name: index
So I am making my cannon script function like a conveyor belt that fires what ever my fired int is but I get the aforementioned error. I think it has something to do with my list but idk.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class CannonScript : MonoBehaviour
{
int fired = 0;
float previousTime;
public GameObject
projectile, // The cannonball
target, // The cube that the cannon points at
cannonBallPos; // The spawn position of the cannon ball
public float
waitTime = 1, // Time between shots
forceAmount = 1000;
public List<GameObject> Cannons = new List<GameObject>();
void Start()
{
previousTime = Time.timeSinceLevelLoad;
var Cannons = new List<GameObject>(GameObject.FindGameObjectsWithTag("Cannon"));
transform.localScale = new Vector3(1, 1, 2);
cannonBallPos = new GameObject("cannonBallPos"); // Creates a new game object
cannonBallPos.transform.parent = transform; // Sets the parent as the cannon
cannonBallPos.transform.position = transform.position + new Vector3(0, 0, 0.5f); // Moves to the front of the cannon
}
void Update()
{
transform.LookAt(target.transform);
if (Time.timeSinceLevelLoad - previousTime > waitTime)
previousTime = Time.timeSinceLevelLoad;
Fire();
}
void Fire()
{
if (fired == 0)
{
Cannons[0] = cannonBallPos;
GameObject cannonBall;
cannonBall = (GameObject)Instantiate(projectile, cannonBallPos.transform.position, cannonBallPos.transform.rotation);
cannonBall.GetComponent<Rigidbody>().AddForce(transform.forward * forceAmount);
}
else
{
if (fired == 1)
{
Cannons[1] = cannonBallPos;
GameObject cannonBall;
cannonBall = (GameObject)Instantiate(projectile, cannonBallPos.transform.position, cannonBallPos.transform.rotation);
cannonBall.GetComponent<Rigidbody>().AddForce(transform.forward * forceAmount);
}
else
{
if (fired == 2)
{
Cannons[2] = cannonBallPos;
GameObject cannonBall;
cannonBall = (GameObject)Instantiate(projectile, cannonBallPos.transform.position, cannonBallPos.transform.rotation);
cannonBall.GetComponent<Rigidbody>().AddForce(transform.forward * forceAmount);
}
else
{
if (fired == 3)
{
Cannons[3] = cannonBallPos;
GameObject cannonBall;
cannonBall = (GameObject)Instantiate(projectile, cannonBallPos.transform.position, cannonBallPos.transform.rotation);
cannonBall.GetComponent<Rigidbody>().AddForce(transform.forward * forceAmount);
}
else
{
if (fired == 4)
{
Cannons[4] = cannonBallPos;
GameObject cannonBall;
cannonBall = (GameObject)Instantiate(projectile, cannonBallPos.transform.position, cannonBallPos.transform.rotation);
cannonBall.GetComponent<Rigidbody>().AddForce(transform.forward * forceAmount);
}
if (fired == 5)
{
Cannons[5] = cannonBallPos;
GameObject cannonBall;
cannonBall = (GameObject)Instantiate(projectile, cannonBallPos.transform.position, cannonBallPos.transform.rotation);
cannonBall.GetComponent<Rigidbody>().AddForce(transform.forward * forceAmount);
fired = 0;
}
}
}
}
}
}
}
Why is your firing event not in a For loop? Like this.
for (int i = 0; i < Cannons.Length; i++)
{
Cannons[i] = cannonBallPos;
GameObject cannonBall;
cannonBall = (GameObject)Instantiate(projectile, cannonBallPos.transform.position, cannonBallPos.transform.rotation);
cannonBall.GetComponent<Rigidbody>().AddForce(transform.forward * forceAmount);
}
If I did that wouldn't the list iterate through the cannons I have set up and ignore the timers for the rest of the cannons after the timer of the 1st cannon tho?
also I can't access Length, I can only do that with arrays ;-;
Lists have Count variable (list.Count) that gives you the ammount of elements that the list has.
Okay, so I take it that you have a total of 5 cannons in the scene. You've put them all in a list. Thing is, the indexing for a list starts at 0, not at 1. So, for example, if you are trying to access the third cannon, it's Cannons[2], because:
First cannon is Cannons[0]
Second cannon is Cannons[1]
Third cannon is Cannons[2] and so on. That's the problem in your original code I think.
changed everything accordingly and it is still out of range :/
Take a few screenshots, and describe your scene a little bit more. I think I've noticed a few mistakes in your code as to why it might not work, but it would be great to see how it all looks at the moment.
What is the script itself attached to? I am getting a little confused, reading the code.
I shall rewrite the code a little bit, comment it all out so you would understand how it should work and I'll post it under here in a sec.
Answer by Scoutas · Jan 08, 2017 at 06:49 PM
List indexing starts from 0, not from 1. If you have five objects in your Cannons list, then accessing Cannons[5] means you are going out of it's range.
Edit: I rewrote the script to make a little bit more sense and commented it all. Check if it works for you and ask me if there's a part of the script that you can't really grasp just yet.
//Okay, so here's what I see. I take it, that you want each cannon to fire one after another.
//First things first - attach the script to an empty gameObject in the scene. It will make a little bit more sense this way.
//Now, let us write the script.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class CannonScript : MonoBehaviour
{
int cannonIndex = 0; // your 'fired' variable
GameObject currentCannon; // the current cannon that will fire.
float previousTime;
public GameObject
projectile, // The cannonball
target, // The cube that the cannon points at
cannonBallPos; // The spawn position of the cannon ball
public float
waitTime = 1, // Time between shots
forceAmount = 1000;
public List<GameObject> Cannons = new List<GameObject>();
void Start()
{
previousTime = Time.timeSinceLevelLoad;
// You get every single cannon in the scene into this list.
Cannons = new List<GameObject>(GameObject.FindGameObjectsWithTag("Cannon"));
// I take it, you wanted to make the cannon to strech with this line: transform.localScale = new Vector3(1, 1, 2);
// so, instead let's make a loop that loops through the list of cannons that we have and scales it there
// changing the scale directly using transform.localScale won't work, because you don't have the script attached
// to the cannon itself anymore.
for (int i = 0; i < Cannons.Count; i++){
Cannons[i].transform.localScale = new Vector3(1, 1, 2);
}
// you are creating the cannonBallPos, where a ball will fire from.
cannonBallPos = new GameObject("cannonBallPos");
// now these two lines below are what makes it a little bit confusing.
// I do get what you tried to do, and it might work, if you'd put more work into the later script, but let's make it a little bit easier.
// cannonBallPos.transform.parent = transform;
// cannonBallPos.transform.position = transform.position + new Vector3(0, 0, 0.5f);
}
// Lets create a little method, that would move the cannonBallPos to the front of the cannon.
void MoveToFront(GameObject cannon){
// let's set the parent of the cannonBallPos to the cannon that we sent into this method
cannonBallPos.transform.parent = cannon.transform;
// and let's set the position
// the way I rewrote the code, the cannon will already be looking at the "target" gameObject
// so just moving the cannonBallPos forward bt 1 unit won't work, instead we get the "forward"
// direction of the cannon and move the cannonBallPos in that direction.
cannonBallPos.transform.position = cannon.transform.position + cannon.transform.forward;
}
void Update()
{
//Check if the waitTime has passed already
if (Time.timeSinceLevelLoad - previousTime > waitTime) {
previousTime = Time.timeSinceLevelLoad;
// Get the cannon that you want to shoot from;
currentCannon = Cannons [cannonIndex];
// make that cannon look at the target
currentCannon.transform.LookAt (target.transform);
// now, let's fire the cannon
Fire();
// increase the cannonIndex, so that the next time you'd use the next cannon in the list
cannonIndex++;
// We have to check if the index is not exceeding the size of our list.
// that "- 1" is there, because the count returns actual count of the objects in the list, when the indexation has to be one lower.
if(cannonIndex > Cannons.Count - 1){
// so if we are exceeding the ammount of cannons in the Cannons list, we reset it to the start again.
cannonIndex = 0;
}
// this should, actually keep on shooting until you stop your scene.
}
}
void Fire(){
// First of all, we have to set the cannonBallPos to the front of the cannon.
// Luckily, we have a little method that will help us do exactly that.
// we pass it the currentCannon that we want to shoot from (we got it in the Update() method, before executing Fire() method)
MoveToFront (currentCannon);
// now that the cannonBallPos is in front of the camera, we can actually shoot it.
// we instantiate the cannonball object;
GameObject cannonBall = (GameObject) Instantiate(projectile, cannonBallPos.transform.position, Quaternion.identity);
// now we shoot it
cannonBall.GetComponent<Rigidbody> ().AddForce (currentCannon.transform.forward * forceAmount);
}
I wrote the code in the answer right now. Look through it, try to implement it and see if it works.
I tried out your improvised script(the script is attached to my cannons) and they fire multiple shots. I tried to your improvised script on a empty object and the cannons had nothing to shoot at..
I have figured out the problem. I tried to actually implement it to my own code, and I got the same problem as you did. I checked the actual public list of the cannons and it had zero. So basically, the part where you are finding the cannons in the scene did not work.
The fix for the problem is this, ins$$anonymous$$d of:
var Cannons = new List<GameObject>(GameObject.FindGameObjectsWithTag("Cannon"));
write Cannons = new List<GameObject>(GameObject.FindGameObjectsWithTag("Cannon"));
you have to ommit the "var" in front.
What happens here, is that you have a List called "Cannons", but in the start method, ins$$anonymous$$d of assigning the list that you find to the "Cannons", you are creating a new list, because you wrote "var Cannons" in front of it.
O$$anonymous$$, new problem created by your script but 1 problem solved but another created. I managed to get the timed shooting and have it be 1 by 1 on point but now the cannons have nothing to target becuase they are on a empty gameobject
If they don't need actual targets to look at, and they just need to fire forwards, then comment out the line
currentCannon.transform.LookAt (target.transform);
I would love for you to elaborate on what you are creating and what the cannons are actually supposed to do. I am just shooting in the dark right now.
Ok, sorry for not elaborating, I am new to this ;-;
So i want my cannons to
Shoot in a straight line 1 after another and I want to be able to change what direction they are firing at now but idk how ;-;
create a public float cannonRotation
. in the update method, right at the start (before anything else) write a loop
// For every cannon in the list
for (int i = 0; i < Cannons.Count; i++){
// Get the cannon with index i
GameObject cannon = Cannons[i];
// set the rotation of the cannon
cannon.tranform.rotation = Quaternion.Euler(new Vector3(0, cannonRotation, 0));
// Unity engine uses Quaternions for the rotations, and they are difficult to work with, if you don't understand them (and I myself don't understand them). But alas, Unity provides you with tools that help.
// Quaternion.Euler method transforms the Vector3 rotation to a Quaternion that Unity understands.
// The Vector3(0, cannonRotation, 0) means, that the object is rotated by 0 degrees on x axis, by the cannonRotation degrees on the y axis and by 0 degrees on the z axis.
}
now, by changing the value cannonRotation in the Inspector the cannons should all rotate and fire in the direction they are facing.