List Enigma

Hello everyone, long time since I posted anything here, let’s see if you are still sharp.

Basically I get this error:

“ArgumentOutOfRangeException: Argument is out of range.
Parameter name: index
System.Collections.Generic.List`1[CampainMap_POI].get_Item (Int32 index) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Collections.Generic/List.cs:633)
Campaign_Map_FactionsAI.Start () (at Assets/_Scripts/Campaign_Map_FactionsAI.cs:23)”

Obviously it’s something like, the list is not being created, or is not finding the registration on the list, etc, etc, etc.
Well, here i bring part of the code.

using UnityEngine;
using UnityEngine.UI; //Don't forget me!
using System.Collections;

public class CampainMap_POI : MonoBehaviour {
    /*Every gameobject on the map, is actually a Point of Interest
     * some are more interesting than others though...
     */

    public GameObject UI_Element_Prefab; //The prefab for UI element we want to instantiate (Basically the text)
    GameObject UI_Element; //We want to store somewhere that instatiated prefab
    Canvas canvas;//Reference to our canvas
    public Campaign_Map_Manager cmManager; //Our campaign map manager

    public int FactionNumber; //To what faction this POI belongs to
    public Text UnitListTextAsComponent;//The text under the POI
    public int UnitListNumber = 1; //How many enemies this POI holds
    Color factionColor; //We store the faction color here

    //What type of POI this is
    public POItype Point_of_Interest_Type;

    public enum POItype
    {
        Player,
        Unit,
        Village,
        Castle,
        Terrain //Note, we never assign a POI with a terrain type
    }

    public CampainMap_POI HomeBase;

    void Start()
    {
        //Find our references
        cmManager = GameObject.FindGameObjectWithTag("CampaignMapManager").GetComponent<Campaign_Map_Manager>();
        canvas = GameObject.FindGameObjectWithTag("Canvas").GetComponent<Canvas>();

        //Instatiate our UI element and store it
        UI_Element = Instantiate(UI_Element_Prefab, transform.position, Quaternion.identity) as GameObject;
        UI_Element.transform.SetParent(canvas.transform); //For UI elements, you can't use transform.parent = , you have to use .SetParent()
        UnitListTextAsComponent = UI_Element.GetComponentInChildren<Text>();

        //Initialize the color here
        if (Point_of_Interest_Type != POItype.Player)
        {
            //If it's not a player, then it's faction's color from the campaign manager script
            factionColor = cmManager.FactionList[FactionNumber].factionColor;
        }
        else
        {
            //If it's a player just give him a white color
            factionColor = Color.white;
        }

        //Change the color
        UnitListTextAsComponent.color = factionColor;

        //Add the POI to the correct list
        InitializeFactionOwner();
    }

    void InitializeFactionOwner()
    {
        switch (Point_of_Interest_Type)
        {
            case POItype.Unit:
                cmManager.FactionList[FactionNumber].FactionCharacters.Add(GetComponent<CampaignMap_AIUnit>());
                break;
            case POItype.Village:
                cmManager.FactionList[FactionNumber].FactionVillages.Add(this);
                break;
            case POItype.Castle:
                cmManager.FactionList[FactionNumber].FactionCastles.Add(this);
                break;
        }
       
    }

    void Update()
    {
        UIFollowObject();
        UpdateUIproperties();
    }

    //Update the text of the UI element based on the type and add the number of units
    void UpdateUIproperties()
    {
        switch(Point_of_Interest_Type)
        {
            case POItype.Player:
                UnitListTextAsComponent.text = "Player " + "(" + UnitListNumber + ")";
                break;
            case POItype.Unit:
                UnitListTextAsComponent.text = "Unit " + "(" + UnitListNumber + ")";
                break;
            case POItype.Castle:
                UnitListTextAsComponent.text = "Castle " + "(" + UnitListNumber + ")";
                break;
            case POItype.Village:
                UnitListTextAsComponent.text = "Village " + "(" + UnitListNumber + ")";
                break;
        }
    }

    //This functions make the UI element to follow a 3d object on the screen  
    void UIFollowObject()
    {
        //Follow a 3d Object
        Vector2 screenPoint = RectTransformUtility.WorldToScreenPoint(Camera.main, transform.position);
        UI_Element.transform.position = screenPoint;
        //psss.. this is useful a lot!
    }
}
 using UnityEngine;
using System.Collections;

public class Campaign_Map_FactionsAI : MonoBehaviour {

    float gameTime; //The "real" time of the game, still based on deltatime
    public float DecisionTimerCycle = 30;//After how much time should we evaluate the state
    public float decisionTimer;

    bool startEvaluating;
    Campaign_Map_Manager cmManager;
    void Start ()
    {
        cmManager = GetComponent<Campaign_Map_Manager>();
        //decisionTimer = 5;

        //Create AI characters
        for(int i = 0; i < cmManager.FactionList.Count; i++)
        {
            for (int c = 0; c < cmManager.FactionList[i].startingCharacters; c++)
            {
                Vector3 SpawnPosition = cmManager.FactionList[i].FactionCastles[0].transform.position;

                GameObject tempGO = Instantiate(cmManager.FactionList[i].characterPrefab, SpawnPosition, Quaternion.identity) as GameObject;

                CampaignMap_AIUnit aiUnit = tempGO.transform.GetComponent<CampaignMap_AIUnit>();
                cmManager.FactionList[i].FactionCharacters.Add(aiUnit);

                tempGO.GetComponent<CampainMap_POI>().FactionNumber = i;

            }
        }
    }
   
    void Update ()
    {
        if (!startEvaluating)
        {
            decisionTimer += Time.deltaTime;

            if (decisionTimer > DecisionTimerCycle)
            {
                startEvaluating = true;
                decisionTimer = 0;
            }         
        }
        else
        {
            EvaluateFactionAIstate();
        }

        //ticker
        if(cmManager.tick)
        {
            cmManager.tick = false;
        }

        //We want to update faction values every "second" that's why we made this timer
        gameTime += Time.deltaTime;

        if (gameTime > 1)
        {
            for (int i = 0; i < cmManager.FactionList.Count; i++)
            {
                ExecuteAIorders(cmManager.FactionList[i]);
            }

            cmManager.tick = true;
            gameTime = 0;
        }

    }

    //For each Faction, execute their state
    void ExecuteAIorders(FactionsBase faction)
    {
        //We can't do changes directly to the faction base,
        //We have to create a new on first
        FactionsBase newFB = new FactionsBase();
        newFB = faction; //Take the values from the previous stored base and do changes to that

        switch(faction.factionAIstate)
        {
            case FactionsBase.FactionAIstate.GoToWar:
                Debug.Log("WAAAARRGGGHHHH"); //War will be dealth otherwise, we don't have to do anything here
                break;
            case FactionsBase.FactionAIstate.RaiseArmies:
                //If we have any wealth
                if (newFB.factionWealth > 0)
                {
                    //Then convert wealth to army units
                    newFB.currentArmyUnits++;
                    newFB.factionWealth--;
                }
                else//If we don't, there's no need to wait till evaluation, change the AI state
                    newFB.factionAIstate = FactionsBase.FactionAIstate.RaiseResources;
                break;
            case FactionsBase.FactionAIstate.RaiseResources:
                newFB.factionWealth++;//If we are raising resources, simply add to the wealth
                break;
        }

        //After you do that, then we have to replace it
        ReplaceFactionBase(faction, newFB);
    }

    void ReplaceFactionBase(FactionsBase faction, FactionsBase replacement)
    {
        //Basically, you can't just replace a value,
        //You have to create a new one, therefore

        //We check to see if there's an instance of this in the list
        if(cmManager.FactionList.Contains(faction))
        {
            //If there is, we store the index (where is it in the list)
            int index = cmManager.FactionList.IndexOf(faction);
            //We remove the previous instance
            cmManager.FactionList.Remove(faction);
            //And instead of adding the new one, we insert it in the position of the previous one
            cmManager.FactionList.Insert(index, replacement);
        }
    }

    void EvaluateFactionAIstate()
    {
        //Start deciding what Faction should do next
        for(int i = 0; i < cmManager.FactionList.Count; i++)
        {
            FactionAIstatesLogic(cmManager.FactionList[i]);

            //And change the AI state of each character
            AssignStates(cmManager.FactionList[i]);
        }

        startEvaluating = false;
    }

    void FactionAIstatesLogic(FactionsBase faction)
    {
        //Same as above
        FactionsBase newFB = new FactionsBase();
        newFB = faction;

        FactionsBase.FactionAIstate aiState = newFB.factionAIstate;

        switch(aiState)
        {
            case FactionsBase.FactionAIstate.GoToWar:
                //For now let's just reset the loop
               // newFB.factionAIstate = FactionsBase.FactionAIstate.RaiseResources;
                //And let's just assume we had casualties on that war
              //  int ranValue = Random.Range(1, newFB.currentArmyUnits);
               // newFB.currentArmyUnits -= ranValue;

                break;
            case FactionsBase.FactionAIstate.RaiseArmies:
                //If we haven't reached our target Units
                if (newFB.currentArmyUnits < newFB.storePreviousArmyUnits + newFB.targetArmyUnits)
                {
                    //Then see if you can raise more
                    RaiseArmiesLogic(newFB);
                }
                else
                {
                    //If we have, then we can probably go to war
                    ShouldWeGoToWar(newFB);
                }
                break;
            case FactionsBase.FactionAIstate.RaiseResources: //If our previous state was to raise resources
                if (newFB.factionWealth < newFB.factionWealthTarget) //And the ones we raised wasn't enough
                {
                    //Countinue raising resources
                    newFB.factionAIstate = FactionsBase.FactionAIstate.RaiseResources;
                }
                else
                {   //If we have enough resources, then decide if you want to go to war
                    ShouldWeGoToWar(newFB);
                }
                break;
        }

        ReplaceFactionBase(faction, newFB);
    }

    void ShouldWeGoToWar(FactionsBase faction)
    {
        FactionsBase newFB = new FactionsBase();
        newFB = faction;

        //If the faction's trait is agressive, they have more chances on going to war
        float baseWarValue = (newFB.factionTraits == FactionsBase.FactionTraits.Aggresive) ? 30 : 60;

        baseWarValue -= newFB.currentArmyUnits; //Let's just say, if they have a big army, they have more chances on going to war

        int ranValue = Random.Range(0, 100); //Get a random value

        Debug.Log(ranValue);

        if(ranValue > baseWarValue)
        {
            //Then either go to war
            newFB.factionAIstate = FactionsBase.FactionAIstate.GoToWar;
        }
        else
        {
            //Or see if you can raise more armies
            newFB.storePreviousArmyUnits = faction.currentArmyUnits; //Store the previous Units we had
            newFB.targetArmyUnits = 10; //Let's say they always need 10 more
            RaiseArmiesLogic(newFB);
        }

        ReplaceFactionBase(faction, newFB);
    }

    void RaiseArmiesLogic(FactionsBase faction)
    {
        FactionsBase newFB = new FactionsBase();
        newFB = faction;

        //If we have enough resources, then raise armies, 1 wealth equals 1 unit
        if (newFB.factionWealth >= newFB.targetArmyUnits)
        {
            newFB.factionAIstate = FactionsBase.FactionAIstate.RaiseArmies;
        }
        else
        {//If we dont, then raise some resources first
            newFB.factionAIstate = FactionsBase.FactionAIstate.RaiseResources;
        }

        ReplaceFactionBase(faction, newFB);
    }

    void AssignStates(FactionsBase faction)
    {
        foreach (CampaignMap_AIUnit AIchar in faction.FactionCharacters)
        {
            AIchar.hasDestination = false;

            //Assign a random value for every state the AI unit can have
            float patrolValue = Random.Range(0, 100 + 1);
            float guardValue = Random.Range(0, 100 + 1);
            float attackValue = Random.Range(0, 100 + 1);
            float raiseValue = Random.Range(0, 100 + 1);
           
            //The above values represent the base chances a unit has to choose a state
            //Now on the base chances, apply modifiers to further affect their decision
            //For example:

            //Since if the Unit has a big army, he has more chances to attack, so..
            attackValue += AIchar.cmPOI.UnitListNumber; //Add the unit's the POI has to the attackValue

            //And if the Faction is at war, let's just swift the balance in favour of war
            attackValue += (faction.factionAIstate == FactionsBase.FactionAIstate.GoToWar) ? 50 : -30;

            //However, add the Faction traits to it's decision
            guardValue += (faction.factionTraits == FactionsBase.FactionTraits.Aggresive) ? -30 : 10;

            //Add more modifiers here until you get what you want, this can be expanded to a ridiculous extend

            //After the modifiers stage, start comparing,
            //change the hierarchy of this logic to what seems to you, well more logical

            if(attackValue > raiseValue)
            {
                if (guardValue < attackValue && patrolValue < attackValue)
                {
                    //Attack
                    AIchar.aiState = CampaignMap_AIUnit.AiState_Unit_Campaign.Attack;
                }
                else
                {
                    if(guardValue < patrolValue)
                    {
                        //Patrol
                        AIchar.aiState = CampaignMap_AIUnit.AiState_Unit_Campaign.Patrol;
                    }
                    else
                    {
                        //Guard
                        AIchar.aiState = CampaignMap_AIUnit.AiState_Unit_Campaign.Guard;
                    }
                }
            }
            else
            {
                AIchar.aiState = CampaignMap_AIUnit.AiState_Unit_Campaign.RaiseArmy;
            }

        }
    }

}

Please someone enlighten my mind.

in the code you’ve posted line 23 is blank… which line is it complaining about when you double click on the error?

Should also point out that at no point in either of the scripts you’ve posted do you declare or initialise a list… but there are lots of references to arrays/lists on cmManager, what does that look like?

Seems like one of your factions doesn’t have any castles.

Protip; the error message shows you what line’s causing the error - in your case, “Campaign_Map_FactionsAI.cs:23” should be pretty self-explanatory. You can also double-click the error to have your editor open at that line.

You cant double click this kind of error…

anyway I know which line the error.

The big issue here is that the code looks perfect.

Instantiated some things can cause this error?

Yes, I believe the problem is the “Spawn Point” of units, which are the castles, but already went over everything and everything is setted correctly apparently

It is not exactly in line 23 but at some point when creating the units from the list. The purpose is to use a loosely based on the mount & blade map system .

Creating the list…

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class Campaign_Map_Manager : MonoBehaviour {

    /*This script is the main manager for our game
     * It will hold information about our players, enemies, factions etc.
     * Having everything in this script means that it would also be easier to save them ;)
     */

    public int PlayerArmy; //the number of units the player has
    public int currEnemies; //the number of enemies the POI we are going to has, this is used to instantiate them on the level we are loading
    public bool onMenu;//If the player is on a menu
    public bool tick;
    //We want a list with all the basic properties of our Factions, see below for more
    public List<FactionsBase> FactionList = new List<FactionsBase>();

    public GameObject VillageUI;//The UI for when the player is visiting a village
    //Do note, for my projects, I usually keep everything that has to do with the UI on a different script to avoid confusions

    void Awake()
    {
        //We never want this object to be destroyed since it holds all our informations
        DontDestroyOnLoad(transform.gameObject);

        //However, if we are loading a scene that already has a gameobject like this, then we want to destroy that
        if(GameObject.FindGameObjectWithTag("CampaignMapManager"))
        {
            if(GameObject.FindGameObjectWithTag("CampaignMapManager") != this.gameObject)
            {
                Destroy(GameObject.FindGameObjectWithTag("CampaignMapManager"));
            }
        }
        //If the above doesn't work, use a loop to get all references and destroy the one you don't want
    }

    void Start()
    {
        //Let's close the UI element at start
        VillageUI.SetActive(false);
    }

    //A simple function (for now) that increases the player's army, we have it public so that we can call it from a UI element
    public void RaiseArmy()
    {
        PlayerArmy += 3;
    }

    //Another simple function that just closes every UI menu we have open
    public void CloseMenu()
    {
       //However, we want a little delay before we give control back to the player to avoid false input, like the player moving where the button we clicked was
        //I used a coroutine here to show you a different way of doing a timer, instead of the usual way we did it before.
        StartCoroutine("GiveControlToPlayer");
        //Add all menus to close here
        VillageUI.SetActive(false);
    }

    //Our little IEnumerator
   IEnumerator GiveControlToPlayer()
    {
       //Coroutines are pretty powerful but be advised when using them, I'd usually suggest avoiding them altogether unless you have to or you know what you are doing
       //things can get messy pretty fast
        yield return new WaitForSeconds(.5f); //Do note that this is indepedent of the current Time.scale
        onMenu = false;
    }
}

//See below for more
[System.Serializable]
public struct FactionsBase
{
    public int factionId;
    public Color factionColor;
   
    public int startingCharacters;
    public GameObject characterPrefab;

    public int currentArmyUnits;
    public int storePreviousArmyUnits;
    public int targetArmyUnits;
   
    public int factionWealth;
    public int factionWealthTarget;
   
    public List<int> EnemiesWith;
    public List<CampaignMap_AIUnit> FactionCharacters;
    public List<CampainMap_POI> FactionCastles;
    public List<CampainMap_POI> FactionVillages;

    public FactionAIstate factionAIstate;
    public FactionTraits factionTraits;
    public FactionPrefers factionPrefers;

    public enum FactionAIstate
    {
        GoToWar,RaiseArmies,RaiseResources
    }

    public enum FactionTraits
    {
        Aggresive,Passive,Diplomatic
    }

    public enum FactionPrefers
    {
        Oranges,Apples,Lemons,Cake
    }
}
/* This is a struct
* If you haven't used one before, you are lying :P
* The majority of what you use is basically a struct*, as the example I gave in the video
* a Vector3 is basically this:
*
* public struct Vector3
* {
*   float x;
*   float y;
*   float z;
* }
*
* Simple aint it? Now do take it with a grain of salt since I haven't actually seen the source code behind it but I can imagine,
* If you where learning C++ (or even C# for that matter) outside of a game engine enviroment, this would have been one of the first(est) things you would learn
*
*/

Try not to point out the obvious please.