Jump to content
Sign in to follow this  
nomoregames

ANSWERED Gestión de listas

Recommended Posts


He estado un buen rato investigando por la red pero no encuentro una solución a mi problema. Así que diccionario y dudas en mano me dispongo a preguntar

Tengo una pequeña IA que utilizo para controlar a los enemigos y la verdad es que es muy resultona, pero al rato de luchar se hace aburrido. Así que he pensado en hacer que, cuando aya más de un enemigo en escena estos creen estrategias. Para lograr esto, creo un script controlador, que actuará como el cerebro de una mente colmena... para eso quiero usar una lista de GameObjects, esta tendrá dentro todos los enemigos que se encuentren en escena en ese momento, la lista se actualizaría cada frame, y luego según la cantidad de enemigos que se encuentren en la lista, estos aran una estrategia u otra


seria algo así como 

if (ListaEnemigos.Count == 2)
{
   ListaEnemigos.FindIndex(1) = ataca por esta zona
   ListaEnemigos.FindIndex(2) = ataca por esta otra
}

Bien.... el problema está en que necesito actualizar en cada fotograma la lista, para saber en tiempo real la cantidad de enemigos que se encuentran en la escena, y si simplemente pongo en el Update ListaEnemigos.Add(GameObject.FindObjectsWithTag("Enemigo")), la lista crece y crece por que no se actualiza, se añade.

Para solucionar esto ser me ocurrió que al iniciar el un nuevo fotograma se eliminaran todos los elementos de la lista para luego añadirlos de nuevo, lo cual es un poco, bastante, absurdo. Además, por lo que e investigado tendría que usar un foreach recorriendo cada uno de los objetos de la lista y eliminándolos, lo cual tengo entendido no es muy eficiente....


¿Ay algún modo de, simplemente; actualizar la lista y no de añadir?

¿Realmente es tan ineficiente lo del foreach?

¿Exsiste algún modo de eliminar todos los objetos de una lista (no me refiero a eliminar el obj en si, sino de borrarlo de la lista) sin necesidad de un bucle?


Espero que no os duelan mucho los ojos, y gracias.

Salud.

Share this post


Link to post
Share on other sites

Otra bomba atómica. Revisa primero las faltas de ortografía por favor. Los "ay" y los "e" por ejemplo. Que se vea que trabajas los post y no es en plan "viva la vida".

Venga. Un saludo

  • Like 1

Share this post


Link to post
Share on other sites

@nomoregames Vamos mejorando la forma de escribir y explicar !Sigue esforzandote¡

Para este tipo de implementación se suele utilizar una lista estática de la entidad que se modifica en los eventos de instancia y destrucción del componente.

¿Como se realiza?
Esto es bastante simple, pero igual lo explicare para que puedas estudiarlo y asimilarlo. Cualquier campo, propiedad, función, etc estático es accesible desde el nombre de clase (sin instancia), esto nos permite gestionar información de forma centralizada (se trata de un resumen únicamente didáctico para personas que no entienden el concepto estático).

public class Enemy : MonoBehaviour
{
    private static List<Enemy> m_instanceEnemys { get; set; } = new List<Enemy>();
    public static ReadOnlyList<Enemy> Instances { get { return m_instanceEnemys; } }
    
    private void Awake()
    {
        m_instanceEnemys.Add(this);
    }
    
    private void Destroy()
    {
        m_instanceEnemys.Remove(this);
    }
}

Ahora, cada vez que necesites acceder a la lista de enemigos simplemente puede hacer uso de la propiedad pública estática Instances.

Para entender más a fondo la diferencia y el rendimiendo de las diferentes estructuras de datos que nos ofrece C# te recomiendo pasar por el blog que escribi hablando sobre esto.

Share this post


Link to post
Share on other sites

@iRobb

Pues mira que le he pasado el corrector...

 

@francoe1Excelente explicación, investigaré un poco más el tema, gracias.

Ok... he estado leyendo el blog, y sobra decir que no he entendido nada... y, por favor, no lo intentes, no creo que lo pueda asimilar aún... Y es que sufro de la enfermedad del Astiriño, que no me entra en la cabeza ni con un martillo.

Así que he copiado directamente el código que me has enseñado, para hacer pruebas y trastear un poco, haber si conseguía entender lo suficiente como para implementarlo a mi código... he creado un script de pruebas y lo he añadido; tal que así

 

image.png

 

y me aparece ese error...

En qué la estoy liando parda?  

Edited by nomoregames

Share this post


Link to post
Share on other sites

Esa Instances entiendo que está definida para restringir el acceso a m_instanceEnemys y que desde 'fuera' de la clase no se pueda modificar.

Para solucionar ese error tienes la opción de:

public static ReadOnlyCollection<string> Instances { get { return new ReadOnlyCollection<Enemy>(m_instanceEnemys); } }

que no la veo muy óptima ya que crea una nueva colección cada vez que accedes a Instances.

Yo pondría la opción:

public static List<Enemy> m_instanceEnemys { get; private set; } = new List<Enemy>();

y de esta forma también proteges de cambiar la lista desde otro sitio. Ahora sólo trabajas con Enemy.m_instanceEnemys.

Podrías cambiar el nombre.

 

Yo que conste que soy más de llevar esto (el arrray) a la clase controladora (GameController) y gestionarlo desde allí....

Edited by AngelFG

Share this post


Link to post
Share on other sites

@AngelFG No, estás equivocado, proteger la asignación no protege el objeto. Del modo que propones se puede modificar la lista desde cualquier parte y es justamente el comportamiento que se intenta evitar.

-----

7 hours ago, AngelFG said:

Para solucionar ese error tienes la opción de:

Esto no es un error, es un comportamiento, o regla de restricción.

-----

Por otro lado "new ReadOnlyCollection" fue tu propuesta, en mi propuesta no sugiero crear una instancia, ya que todos las clases que hereden de IEnumerator se pueden convertir implícitamente o realizar Boxing and Unboxing.

También es posible exponer únicamente el Enumerador, aún que para estos casos siempre sugiero recorrer la lista con el índice, de este modo evitaras la excepción de modificar la lista mientras estás iterando.

public IEnumerable<Enemy> Instances
{
  get 
  {
    for (int i = 0; i < m_instanceEnemys.Count; i++)
      yield return m_instanceEnemys[i];
  }
}

-----

¿Llevar esto a GameController?
Esto no tiene ningún sentido, es importante mantener todo separado, quizás funciona si tienes 2 o 3 entidades, pero no es una implementación escalable, se la considera una mala práctica. Es importante que cada componente se comporte de forma independiente y que cualquier interacción que exista entre componentes se debe realizar desde un tercero, esto no quiere decir que se debería cumplir sin excepciones, existen múltiples componentes que componen un "sistema" y de ser así, todos los componentes se requieren entre si, por esto no requieren un tercer componente para la comunicación.

Por otro lado, al llevar el arreglo a otra clase fuera de la objetiva, se pierde el acceso por nivel de seguridad, esto quiere decir que si la propiedad tiene el acceso GET private o protegido no podrías recrear el ejercicio.

-----

Siempre intento responder interpretando el conocimiento de la persona con la duda, por esto, los más acercado a una buena practica y fácil de comprender fue lo que le sugerí a@nomoregames.

 

Saludos.

Share this post


Link to post
Share on other sites
13 hours ago, francoe1 said:

@AngelFG No, estás equivocado, proteger la asignación no protege el objeto. Del modo que propones se puede modificar la lista desde cualquier parte y es justamente el comportamiento que se intenta evitar.

-----

Esto no es un error, es un comportamiento, o regla de restricción.

-----

Por otro lado "new ReadOnlyCollection" fue tu propuesta, en mi propuesta no sugiero crear una instancia, ya que todos las clases que hereden de IEnumerator se pueden convertir implícitamente o realizar Boxing and Unboxing.

También es posible exponer únicamente el Enumerador, aún que para estos casos siempre sugiero recorrer la lista con el índice, de este modo evitaras la excepción de modificar la lista mientras estás iterando.


public IEnumerable<Enemy> Instances
{
  get 
  {
    for (int i = 0; i < m_instanceEnemys.Count; i++)
      yield return m_instanceEnemys[i];
  }
}

-----

¿Llevar esto a GameController?
Esto no tiene ningún sentido, es importante mantener todo separado, quizás funciona si tienes 2 o 3 entidades, pero no es una implementación escalable, se la considera una mala práctica. Es importante que cada componente se comporte de forma independiente y que cualquier interacción que exista entre componentes se debe realizar desde un tercero, esto no quiere decir que se debería cumplir sin excepciones, existen múltiples componentes que componen un "sistema" y de ser así, todos los componentes se requieren entre si, por esto no requieren un tercer componente para la comunicación.

Por otro lado, al llevar el arreglo a otra clase fuera de la objetiva, se pierde el acceso por nivel de seguridad, esto quiere decir que si la propiedad tiene el acceso GET private o protegido no podrías recrear el ejercicio.

-----

Siempre intento responder interpretando el conocimiento de la persona con la duda, por esto, los más acercado a una buena practica y fácil de comprender fue lo que le sugerí a@nomoregames.

 

Saludos.

Gracias por las correcciones.

Siempre se aprende. En la solución que doy yo es verdad que se puede modificar el contenido de la lista desde cualquier parte.


Simplemente comentarte que la solución que das tú lleva un error (mira el mensaje de quien inició el hilo). Yo puse 'una posible mala solución' que no la única.

¿ Podría valer esta ?

   private static List<Enemy> m_instanceEnemys { get; set; } = new List<Enemy>();
   public static ReadOnlyCollection<Enemy> Instances { get { return m_instanceEnemys.AsReadOnly(); } }

 

 

Aunque no es parte del hilo, podría 'abusar' de tu conocimiento y me podrías aconsejar sobre lo siguiente:

En lo de no llevar la gestión de los enemigos a un 'GameController' dices que es un error. ¿ Me podrías confirmar si llevarlo a un GameObject que lleve asociado un script con dicha gestión es lo adecuado ?. Estoy acostumbrado a decir que todo se lleva el GameController ya que vengo de Libgdx y es desde dicha clase desde donde se gestiona (llama) al resto de métodos que controlan el comportamiento del juego. 

Por otro lado, ¿ no consideras que el uso de variables public static es un recurso que se debiera evitar ? En este caso, ¿ no sería más lógico tener una clase de GestionEnemigos (la que digo como GameController) en la que se encuentre definidos los métodos add(Enemigo) y remove(Enemigo) y cuya lista sea privada y de instancia y dicha clase lleve la gestión de cómo deben comportarse los enemigos en función de su número como preguntaba nomoregames ?

Un saludo y gracias por compartir tu conocimiento.

Edited by AngelFG

Share this post


Link to post
Share on other sites
Sign in to follow this  

UnitySpain © Todos los derechos reservados 2020
×
×
  • Create New...