Problems with C# lists, and ArgumentOutOfRangeException

I am in the process of making a farming/tower defense game and I am very new at programming. I seem to have a major problem with using Lists<> or arrays in XNA. I cannot get it to return the index that I want from the list.

The main question is inside my planting engine. I have successfully implemented a planting system that can generate a list of plants (spriteobjects) with varying properties and place them on the map. Now, I need a way to access a specific plant in the plant list based upon mouseclicking on that plant. I feel like I am very close, but I ended up with a ArgumentOutOfRangeException that I cannot solve. Here is a walkthrough of the code:

Initialization

 public void Addplants()
        {
            switch (Mode)
            {
            case "Wotalemon":
            NewPlant = new Plant(Texture, msRect);
            NewPlant.AddAnimation("seed", 0, 16, 64, 64, 1, 0.1f);
            NewPlant.AddAnimation("sprout", 64, 16, 64, 64, 1, 0.1f);
            NewPlant.AddAnimation("wota", 128, 16, 64, 64, 1, 1.0f);
            NewPlant.CurrentAnimation = "seed";
            NewPlant.DrawOffset = new Vector2(32, 48);
            NewPlant.Position = Position;
            NewPlant.Type = "wotalemon";
            NewPlant.Birthday = Days;
            NewPlant.IsSelected = false;
            plants.Add(NewPlant);
            thisPlant = NewPlant;
            //various plants after this

Update/Draw

I use some simple foreach loops to update and draw the plants, no problems here.

GetInfo (this method uses the spriteobject's hitbox property and a mouseRectangle)

public void GetInfo(Rectangle ms)
        {
            msRect = ms;
            for (int i = 0; i < plants.Count; i++)
            {
                foreach (Plant NewPlant in plants)
                {
                    if (NewPlant.BoundingBox.Intersects(msRect))
                    {
                        SelectedIndex = i;
                        NewPlant.Tint = Color.Black;
                    }
                    else
                        NewPlant.Tint = Color.White;
                }

            }
        }

finally, here is the problem:

 public void SelectPlant()
        {
            //if (SelectedIndex != null)
            if (SelectedIndex > plants.Count | SelectedIndex < 0)
                SelectedIndex = plants.Count;
            SelectedPlant = plants[SelectedIndex];

        }

The exception is thrown in this line:

SelectedPlant = plants[SelectedIndex];

The debugger shows the value as 0. I have tried various methods to try to prevent the index from being null. I feel like something in the Getinfo() method is key here. I am convinced that I am very close to success because the color test that I have inserted in there works perfectly. When I mouseover a plant, it turns black, when I remove the mouse, it returns to normal.

This is EXACTLY the type of behavior I want except that I want it to set selectedIndex to the index of the plant that I am mousing over. Any advice would be greatly appreciated.

Answers


I'll add this as a new answer since this is tackling a whole different issue. Taking a look at this code:

msRect = ms;
for (int i = 0; i < plants.Count; i++)
{
    foreach (Plant NewPlant in plants) // <-- this is redundant
    {
        if (NewPlant.BoundingBox.Intersects(msRect))
        {
            SelectedIndex = i;
            NewPlant.Tint = Color.Black;
        }
        else
            NewPlant.Tint = Color.White;
    }

}

You are looping through 'plants' twice inside each other! Once using an index (for (int i = 0 ...) and then inside that again using an iterator (foreach (Plant NewPlant ...).

Your options are to either change GetInfo to set the right index by using a single loop:

msRect = ms;
for (int i = 0; i < plants.Count; i++)
{
    Plant NewPlant = plants[i];
    if (NewPlant.BoundingBox.Intersects(msRect))
    {
        SelectedIndex = i;
        NewPlant.Tint = Color.Black;
    }
    else
        NewPlant.Tint = Color.White;
}

Or do the same thing and short circuit the need for SelectPlant() and SelectedIndex in the first place:

msRect = ms;
foreach (Plant NewPlant in plants) // no need for indexes
{
    if (NewPlant.BoundingBox.Intersects(msRect))
    {
        SelectedPlant = NewPlant; // this is everything you need
        NewPlant.Tint = Color.Black;
    }
    else
        NewPlant.Tint = Color.White;
}

You do however need to be careful using a 'global' variable like SelectedPlant to capture this logic. You're better off changing the whole GetInfo method to return the selected plant, rather than having it modify SelectedPlant directly. That is, change the method signature to return Plant not void, and change SelectPlant = NewPlant in the code above to return NewPlant. Or for even more fun as a single line:

return plants.Where(p => p.BoundingBox.Intersects(ms))

Firstly make this a proper or || and check for >= plants.Count - remember that the list is indexed at 0. Then as suggested set it to count - 1:

if (SelectedIndex >= plants.Count || SelectedIndex < 0)
    SelectedIndex = plants.Count - 1

Need Your Help

Restrict width on element to width of first child

html css css3 flexbox

I have a pretty tricky situation here which I am trying to solve with CSS only.

How to change the data types for Liquibase's own tables

timestamp oracle9i liquibase

When running Liquibase on a database for the first time it will try to create two tables that it uses to manage its changesets.

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.