Tagged: 

This topic contains 22 replies, has 2 voices, and was last updated by  Sigil 1 year, 2 months ago.

Viewing 15 posts - 1 through 15 (of 23 total)
  • Author
    Posts
  • #38385

    learner2unity5
    Participant

    Hi, I wish for a spawn pool in my game instead of instantiating objects, when I add the following script to my enemy character nothing happens, any ideas as to what I am doing? Please do excuse me for simple mistakes I am a beginner to unity, the pool script is as follows:

    using UnityEngine;
    using System.Collections;
    using System.Collections.Generic;
    public class ObjectPool : MonoBehaviour
    {
        public static ObjectPool instance;
        /// <summary>
        /// The object prefabs which the pool can handle.
        /// </summary>
        public GameObject[] objectPrefabs;
        /// <summary>
        /// The pooled objects currently available.
        /// </summary>
        public List<GameObject>[] pooledObjects;
        /// <summary>
        /// The amount of objects of each type to buffer.
        /// </summary>
        public int[] amountToBuffer;
        public int defaultBufferAmount = 3;
        /// <summary>
        /// The container object that we will keep unused pooled objects so we dont clog up the editor with objects.
        /// </summary>
        protected GameObject containerObject;
        void Awake ()
        {
            instance = this;
        }
        // Use this for initialization
        void Start ()
        {
            containerObject = new GameObject("ObjectPool");
            //Loop through the object prefabs and make a new list for each one.
            //We do this because the pool can only support prefabs set to it in the editor,
            //so we can assume the lists of pooled objects are in the same order as object prefabs in the array
            pooledObjects = new List<GameObject>[objectPrefabs.Length];
            int i = 0;
            foreach ( GameObject objectPrefab in objectPrefabs )
            {
                pooledObjects[i] = new List<GameObject>(); 
                int bufferAmount;
                if(i < amountToBuffer.Length) bufferAmount = amountToBuffer[i];
                else
                    bufferAmount = defaultBufferAmount;
                for ( int n=0; n<bufferAmount; n++)
                {
                    GameObject newObj = Instantiate(objectPrefab) as GameObject;
                    newObj.name = objectPrefab.name;
                    PoolObject(newObj);
                }
                i++;
            }
        }
        /// <summary>
        /// Gets a new object for the name type provided.  If no object type exists or if onlypooled is true and there is no objects of that type in the pool
        /// then null will be returned.
        /// </summary>
        /// <returns>
        /// The object for type.
        /// </returns>
        /// <param name='objectType'>
        /// Object type.
        /// </param>
        /// <param name='onlyPooled'>
        /// If true, it will only return an object if there is one currently pooled.
        /// </param>
        public GameObject GetObjectForType ( string objectType , bool onlyPooled )
        {
            for(int i=0; i<objectPrefabs.Length; i++)
            {
                GameObject prefab = objectPrefabs[i];
                if(prefab.name == objectType)
                {
                    if(pooledObjects[i].Count > 0)
                    {
                        GameObject pooledObject = pooledObjects[i][0];
                        pooledObjects[i].RemoveAt(0);
                        pooledObject.transform.parent = null;
                        pooledObject.SetActiveRecursively(true);
                        return pooledObject;
                    } else if(!onlyPooled) {
                        return Instantiate(objectPrefabs[i]) as GameObject;
                    }
                    break;
                }
            }
            //If we have gotten here either there was no object of the specified type or non were left in the pool with onlyPooled set to true
            return null;
        }
        /// <summary>
        /// Pools the object specified.  Will not be pooled if there is no prefab of that type.
        /// </summary>
        /// <param name='obj'>
        /// Object to be pooled.
        /// </param>
        public void PoolObject ( GameObject obj )
        {
            for ( int i=0; i<objectPrefabs.Length; i++)
            {
                if(objectPrefabs[i].name == obj.name)
                {
                    obj.SetActiveRecursively(false);
                    obj.transform.parent = containerObject.transform;
                    pooledObjects[i].Add(obj);
                    return;
                }
            }
        }
    }
    • This topic was modified 1 year, 3 months ago by  Sigil. Reason: Added code block
    #38389

    Sigil
    Keymaster

    Well that particular script will only provide a collection of objects for spawning, so it won’t actually spawn anything. This may be why you aren’t seeing anything happen.

    If you take that script and add it to an empty GameObject in your scene, then add a commander and soldier prefab to objectPrefabs in the inspector, you can use it in our Spawner script. Here’s a modified Spawner to take advantage of that:

    using UnityEngine;
    public class Spawner : MonoBehaviour
    {
        public float spawnDelay = 10f;
        public int squadSize = 5;
        private float timer = 0f;
        public GameObject Commander = null;
        public GameObject Soldier = null;
        public void Update()
        {
            if (timer <= 0f)
            {
                //1 commander
                if ((squadSize > 0) && (Commander != null))
                {
                    GameObject tCommander = ObjectPool.instance.GetObjectForType(Commander.name, true);
                    if (tCommander != null)
                    {
                        tCommander.transform.position = gameObject.transform.position;
                        tCommander.transform.rotation = gameObject.transform.rotation;
                    }
                }
                //squadSize -1 soldiers, arrayed in line behind the spawn point
                if (Soldier != null)
                {
                    for (int i = 1; i < squadSize; i++)
                    {
                        GameObject tSoldier = ObjectPool.instance.GetObjectForType(Soldier.name, true);
                        if (tSoldier != null)
                        {
                            tSoldier.transform.position = gameObject.transform.position;
                            tSoldier.transform.rotation = gameObject.transform.rotation;
                        }
                    }
                }
                timer = spawnDelay;
            }
            else
                timer -= Time.deltaTime;
        }
    }
    #38391

    learner2unity5
    Participant

    thanks for your response, ok, just so that we are on the right lines, I have created an empty object added my spawn pool script to the empty object filled in the object prefabs, set the buffer size to 5, gone back into my spawner (seperate object) added the new spawner script as above and hurrraaay…nothing happened. Any ideas as to what is happening, and why it is not working…..

    #38398

    Sigil
    Keymaster

    You know what, after looking at the script I posted, it is kind of confusing the way it works. Lets change it up a bit:

    using UnityEngine;
    public class Spawner : MonoBehaviour
    {
        public float spawnDelay = 10f;
        public int squadSize = 5;
        private float timer = 0f;
        public string Commander = "";
        public string Soldier = "";
        public void Start()
        {
            if (Commander == "")
                Debug.LogWarning("Spawner: Commander field is empty");
            if (Soldier == "")
                Debug.LogWarning("Spawner: Soldier field is empty");
        }
        public void Update()
        {
            if (timer <= 0f)
            {
                //1 commander
                if ((squadSize > 0) && (Commander != ""))
                {
                    GameObject tCommander = ObjectPool.instance.GetObjectForType(Commander, true);
                    if (tCommander != null)
                    {
                        tCommander.transform.position = gameObject.transform.position;
                        tCommander.transform.rotation = gameObject.transform.rotation;
                    }
                }
                //squadSize -1 soldiers, arrayed in line behind the spawn point
                if (Soldier != "")
                {
                    for (int i = 1; i < squadSize; i++)
                    {
                        GameObject tSoldier = ObjectPool.instance.GetObjectForType(Soldier, true);
                        if (tSoldier != null)
                        {
                            tSoldier.transform.position = gameObject.transform.position;
                            tSoldier.transform.rotation = gameObject.transform.rotation;
                        }
                    }
                }
                timer = spawnDelay;
            }
            else
                timer -= Time.deltaTime;
        }
    }

    I changed the Commander and Soldier fields to be strings instead of GameObjects. After replacing the Spawner with this script, make sure you fill out the Commander and Soldier fields to be the name of the Prefabs you want to spawn. It should match the names of the Prefabs you added to the objectPrefabs on the ObjectPool.

    After that, note any errors/warning in the log when you play. You can come back here and post if there are some.

    You may also want to check out this page on debugging. It’ll save you a lot of time (and reduce some frustration) if you can look a little closer at what is happening with your scripts, aside from pressing play and not seeing any results.

    #38402

    learner2unity5
    Participant

    Hi…I get the following error message:

    NullReferenceException: Object reference not set to an instance of an object
    ObjectPool.GetObjectForType (System.String objectType, Boolean onlyPooled) (at Assets/SquadCommand/Scripts/new/ObjectPool.cs:90)
    Spawner.Update () (at Assets/SquadCommand/Scripts/new/Spawner.cs:29)

    #38404

    Sigil
    Keymaster

    You will have to dig a little deeper. Given the code in the GetObjectForType function, a null reference exception could be caused by this line:

    ...
        GameObject prefab = objectPrefabs[i];  // if objectPrefabs[i] is empty
        if (prefab.name == objectType) // this will throw the exception
    ...

    Or perhaps:

    ...
        // if somehow the pooledObjects list didn't get initialized
        if (pooledObjects[i].Count > 0) // this would throw the exception
    ...

    Perhaps there are more errors and you just linked the last one? Double clicking the error should open Mono Develop so you can see exactly what line the problem is caused on.

    #38432

    learner2unity5
    Participant

    k, in the original spawner script please can you tell me what code actually makes the soldiers walk off, so for instance once they are spawned in stead of standing there, what code actually makes them move

    #38472

    Sigil
    Keymaster

    Ok, give me a moment here and I will post a spawning script that does pool allocation for you. I think I will be updating the Squad Command Spawner for the asset store with similar code, as it probably should have been done this way to begin with.

    As for movement. The soldiers’ behavior trees handle the movement, and I believe they listen for commands from their commander. The original Spawner would spawn a commander first, and then several soldiers after that (which would automatically pick up the commander). The commander’s behavior tree was setup to start patrolling after the spawn, which is why all the other soldiers would follow.

    I’ll have the updated Spawner code up soon, need to test it first.

    #38474

    Sigil
    Keymaster

    Here is a new and improved spawner. This one preallocates all of the soldiers on Start, which should reduce any hitching caused by the Spawner. It also introduces a new variable, squadCount, which limits the number of squads spawned. Previously it would just spawn forever, possibly creating some funniness if you never engaged the soldiers (or always beat them).

    using System.Collections.Generic;
    using UnityEngine;
    /// <summary>
    /// Spawner is a simple component that spawns a team of AI at a specified interval
    /// </summary>
    public class Spawner : MonoBehaviour 
    {
        /// <summary>
        /// The delay between spawns in seconds
        /// </summary>
        public float spawnDelay = 10f;
        /// <summary>
        /// The number of AI to spawn.  This will include 1 commander and the rest as soldiers.
        /// </summary>
        public int squadSize = 5;
        /// <summary>
        /// The number of squads to spawn
        /// </summary>
        public int squadCount = 5;
        /// <summary>
        /// A timer tracking next spawn
        /// </summary>
        private float timer = 0f;
        /// <summary>
        /// The Commander prefab/object to replicate
        /// </summary>
        public GameObject Commander = null;
        /// <summary>
        /// The Soldier prefab/object to replicate
        /// </summary>
        public GameObject Soldier = null;
        /// <summary>
        /// The list of instantiated soldiers
        /// </summary>
        private Stack<GameObject> squads = new Stack<GameObject>();
        public void Start()
        {
            // We aren't going to work without a commander and soldier
            if (Commander == null || Soldier == null)
                return;
            Preload();
        }
        /// <summary>
        /// Update the timer and check for respawn.  Create a team of 1 commander and squadSize - 1 soldiers
        /// </summary>
        public void Update()
        {
            if (timer <= 0f)
            {
                Spawn();
                timer = spawnDelay;
            }
            else
                timer -= Time.deltaTime;
        }
        private void Preload()
        {
            squads.Clear();
            for (int i = 0; i < squadCount; i++)
            {
                // 1 commander
                if (squadSize > 0)
                {
                    squads.Push((GameObject)GameObject.Instantiate(Commander, gameObject.transform.position, gameObject.transform.rotation));
                    squads.Peek().transform.parent = gameObject.transform;
                    squads.Peek().SetActive(false);
                }
                // squadSize - 1 soldiers, arrayed in line behind the spawn point
                for (int j = 1; j < squadSize; j++)
                {
                    squads.Push((GameObject)GameObject.Instantiate(Soldier, gameObject.transform.position - (gameObject.transform.forward * (float)j), gameObject.transform.rotation));
                    squads.Peek().transform.parent = gameObject.transform;
                    squads.Peek().SetActive(false);
                }
            }
        }
        private void Spawn()
        {
            for (int i = 0; i < squadSize && i < squads.Count; i++)
            {
                squads.Peek().SetActive(true);
                squads.Pop();
            }
        }
    }
    #38522

    learner2unity5
    Participant

    Thank you for the script, yes the script is definitely moving in the right direction, however there is still some of that character lag when enemies are spawned, further more soldiers are “on top” of each other literally, so yes it is a step in the right direction, but I still feel we can do a “little” more to brush this script in particularly in terms of game play “lag”…look forward to your next input…

    #38536

    Sigil
    Keymaster

    Well if soldiers are on top of each other it is likely that they are colliding with each other. Squad Command has rigid bodies on the soldiers but it removes the Character layer from colliding with itself (in the Physics settings), allowing them to pass through each other.

    As far as lag is concerned, using this in Unity 5.1 and using the profiler, I no longer detect any lag on spawn. There is an occasional GC collection but it no longer lines up with the actual spawn. I don’t think there is anything we can do with this particular script that will change any of that.

    As far as GC collection is concerned. We do have one fix that will be in the next release to reduce these further, but it never completely goes away.

    #38574

    learner2unity5
    Participant

    I am still having the issue of lag, there must be a way of getting around this, if not then another way has to be thought of as this script is no use as it stands, worst still it cost money…

    Can we not use an Ienumerator?

    #38577

    Sigil
    Keymaster

    See if you can run the profiler and see where the lag is coming from. It should be fairly clear as it will suddenly spike. You can also click the spike and see what is running at the time.

    Try turning down the spawn delay as well when looking at the profiler, so that you can see multiple spawns on the profiler window at the same time. This is what mine looks like right now, using the same Squad Command that is on the store, with the new Spawner (set spawn delay to 1 and number of soldiers to 8).

    #38580

    Sigil
    Keymaster

    In retrospect, this is what it looked like with the old spawner:

    So this was definitely a much needed change to the spawner, and will be in the next version for sure. Now lets see if we can hunt down where your lag is coming from.

    #38740

    learner2unity5
    Participant

    Hi guys any suggestions?

Viewing 15 posts - 1 through 15 (of 23 total)

You must be logged in to reply to this topic.