Hola, soy un programador con alguna experiencia con XNA, y estoy haciendo un blog sobre programación que ultimamente se centra bastante en las tecnologias .NET, pues viene a ser lo que mas utilizo. La cuestión es que me he puesto a hacer un tutorial sobre XNA (Haciendo un jueguito de naves) y lo intento explicar todo paso por paso.
El tutorial esta orientado tanto a la gente que quiera aprender a utilizar XNA como a la gente que es nueva programando videojuegos.
http://codecrab.blogspot.com/ (http://codecrab.blogspot.com/)
De momento solo tengo hechas las 2 primeras partes.
Las criticas constructivas son bienvenidas :P
Interesante. Ánimo 8)
Hola,
ta gracioso la verdad, un comentario de algo que a mi personalmente me saca de quicio: las cosas en inglés o en español, pero mezcladas no :S Es decir, si tu clase se llama Nave, y tus variables cosas como posicion,... luego no tengas un método que se llama UpdatePosition :p
Un saludo,
Vicente
Muy bueno, a ver si lo consigues acabar, thx me es muy util.
cuando lo tenga completo le ponemos una chincheta? o ponemos un post recopilatorio de tutoriales y articulos?
tercera parte del tutorial subida. A partir de ahora actualizare con más frecuencia :) http://codecrab.blogspot.com/
Parte 4, fondo con scroll, añadida.
Parte 5, detección de colisiones, añadida
Mmm, me he estado leyendo la parte 5, voy a ser un poco pijotero al respecto:
- Utiliza readonly mejor que const (da un poco igual, pero es un poco mejor).
- Deberías ser más constante en la nomenclatura. A veces a las variables privadas las llamas "imagen" y otras "_posicion". Yo no pondría el guión bajo nunca.
- Podrías usar propiedades autogeneradas (a gusto del consumidor la verdad):
public Vector2 Posicion { get; private set;}
- Deberías también ser constante en el uso de atributos o propiedades para detalles internos de la clase.
_posicion.Y += 3;
bounds = new Rectangle((int)Posicion.X, (int)Posicion.Y, anchoImagen, altoImagen);
if (_posicion.Y >= altoVentana)
Primero usas el atributo para la asignación, luego las propiedades para la lectura, y luego el atributo otra vez para leer, queda fatal.
- Puedes actualizar las coordenadas del rectángulo sin tener que crear uno nuevo, igual que haces con el Vector2 (da un poco igual la verdad, pero bueno, como solo actualizas la Y a veces...).
- "Además añadimos varias propiedades para poder acceder a imagen, _posicion y bounds sin necesidad de implementar un método get (Gracias a esto se podra acceder simplemente con enemigo.Imagen o enemigo.Bounds"
Entiendo lo que quieres intentar explicar, pero no es correcto. Una propiedad es un método get, cuando compilas se traducen a eso, si tu tutorial va para gente que sabe .NET, saben seguramente lo que es una propiedad también, y si no lo saben la explicación es un poco rara (asumes que saben lo que es un método get...).
- "enemigos.ForEach(x => x.Update());" Esta línea es muy discutible, yo usaría un foreach normal:
foreach(Enemigo1 e in enemigos) { e.Update() };
Es igual de corto, y se entiende bastante bien. La discusión a fondo de porque esa línea es tan problemática está aquí: http://blogs.msdn.com/b/ericlippert/archive/2009/05/18/foreach-vs-foreach.aspx
- Este código:
private List<disparo>disparos;
public List<disparo> _disparos
{
get { return disparos; }
}
De nuevo la nomenclatura, que es un lío terrible :S También estaría bien que probaras a devolver un IEnumerable<Disparo> en tu propiedad, aunque tendrías que cambiar algunas cosillas.
- Ten un Random general para toda la aplicación, no uno que creas continuamente en la función UpdateEnemigos.
- Esto:
bounds = new Rectangle((int)Posicion.X, (int)Posicion.Y, anchoImagen, altoImagen);
Rectangle rectanguloA = new Rectangle(Convert.ToInt32(posicionA.X), Convert.ToInt32(posicionA.Y), texturaA.Width, texturaA.Height);
A veces usas un cast, a veces el Convert, yo castearía siempre (aunque tendría que mirar que hace Convert.ToInt32 por dentro).
- - - - - - - - - - - -
Y poco más creo, en general eso, el código es bastante lioso porque no es nada uniforme, si lo pules un poco más te queda chapeau.
Un saludo,
Vicente
Ya que estamos otra cosa que ví, no digo que tenga mucho remedio, pero haces dos new en el método para la colision por pixel:
uint[] bitsA = new uint[texturaA.Width * texturaA.Height];
uint[] bitsB = new uint[texturaB.Width * texturaB.Height];
Esto en la Xbox 360 sería un asesinato, estaría saltando todo el rato el recolector de basura. En un tutorial está bien, pero yo advertiría al respecto.
Un poco difícil de remediar a mi entender, a menos que se use un array con el tamaño máximo de antemano y cosas así.
Cierto WaaghMan, buen apunte. Dado que las naves y los disparos tienen tamaño constante yo utilizaría siempre los dos mismos arrays y a correr.
Dais miedo... no pienso enlazar un código mio a stratos en la vida. :P
Bueno, el apunte más importante es el que ha hecho WaaghMan, que estás creando dos referencias de tamaño considerable cada vez que tienes que comprobar una colisión por pixel. No son muchas pero estás generando basura y trabajo para el pobre recolector y eso en la Xbox se paga caro.
Mis apuntes eran más de estilo, que en un tutorial se agradece que el código sea fácil de seguir y esté ordenadito :)
Cita de: blau en 09 de Julio de 2010, 01:19:30 AM
Dais miedo... no pienso enlazar un código mio a stratos en la vida. :P
Yo si, a ver si alguien tiene cojones a revisar y corregir codigo en Visual Basic 6.0 >:D
Salu2...
Edit: Bueno, en verdad ya lo hizo años atras Zupervaca xDDD
http://www.stratos-ad.com/forums/index.php?topic=5120.0
Hay muchas cosas que tengo que cambiar, pero el problema es que tendria que modificar todas las partes del tutorial de forma retrospectiva. No obstante hay muchas cosas que estan ahi por algun motivo. En cuanto a los array que creo cada vez que se llama el metodo, si fuesen atributos de la clase el metodo solo valdria para colisiones de enemigos y disparos, lo que sera un problema más adelante.
En cuanto a la expresion lambda que utilizo, si usase un foreach me daría un error en tiempo de ejecución por borrar un elemento de la colección en medio de la colección (como explico en partes anteriores del tutorial). Las propiedades que dices que cambie.. pos bueno, están, y el convert, esa parte del código no la he hecho yo y por eso es algo incoherente.
No obstante tengo que cambiar algunas cosas, especialmente la nomenclatura.
No decimos que no estén por algún motivo, pero no quita que sigan estando mal o sean cuestionables, hay formas mejor de hacer las cosas.
En los arrays el problema es que ese tipo de código en la Xbox va a saltos. Puedes tener un sitio general donde guardas arrays con el tamaño de las entidades colisionables y reutilizarlos (una clase estática, un singleton,...). Es mucho mejor para la Xbox usar mucha memoria siempre que pedirla poco a poco de forma continuada, el garbage collector es muy simple en la consola y ese tipo de cosas le sientan fatal. En PC no lo vas a notar porque el GC es diferente y es mucho más listo.
Sobre lo del foreach, sigue estando mal de concepto: un bucle foreach (o el método ForEach) no debería modificar la colección como dices que haces. Si necesitas borrar tendrás que hacerlo de otra forma. Fíjate en el código del método ForEach de Microsoft:
public void ForEach(Action<T> action)
{
if (action == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
}
for (int i = 0; i < this._size; i++)
{
action(this._items[i]);
}
}
Seguramente tienes un bug en tu código porque al ir borrando elementos de la lista el size cambia y los últimos no se visitan. Y esto es así porque no se espera que action modifique el tamaño de la lista, no debería hacerlo.
Y lo demás es nomenclatura que en .NET está claramente estandarizada. Tienes las "Guidelines for Names" dentro de la "Design Guidelines for Developing Class Libraries" de Microsoft:
http://msdn.microsoft.com/en-us/library/ms229002.aspx
Y tienes herramientas que te ayudan a detectar que normas de nomenclatura y diseño no cumples y como corregirlas de forma automática al compilar:
http://code.msdn.microsoft.com/sourceanalysis
Ale, un saludo!
Vicente
Cita de: Vicente en 09 de Julio de 2010, 11:20:51 AM
Sobre lo del foreach, sigue estando mal de concepto: un bucle foreach (o el método ForEach) no debería modificar la colección como dices que haces. Si necesitas borrar tendrás que hacerlo de otra forma.
Al margen de que este mal o no, a mi en su dia me toco demasiado las narices el no poder eliminar elementos mientras recorria una coleccion con un foreach en .NET cuando en el infame Visual Basic 6.0 siempre lo he podido hacer sin problema alguno. Borrabas el elemento actual y el cursor automaticamente se situaba en el siguiente. En .NET me toco hacer dos pasadas, una para seleccionar los indices de los elementos a borrar y otra para ir uno a uno borrando elementos por su indice, lo que me parecio un tanto feo :P Ya digo, no se si es correcto o no hacer esto como norma general en el resto de lenguajes, pero desde mi punto de vista, si con el tiempo se van actualizando los lenguajes de programacion para facilitar tareas que antaño eran tediosas y complejas, el poder eliminar al vuelo un elemento de una coleccion desde un foreach que lo recorra deberia ser algo mas que posible y basico ;)
Salu2...
Un bucle foreach representa un iterador. Un iterador es un patrón que te permite recorrer de forma uniforme diferentes estructuras de datos. En .NET esto significa que las estructuras que quieres iterar tienen que implementar una interfaz llamada IEnumerable, y si os fijáis la interfaz IEnumerable no tiene ningún método llamado Remove. Es decir, esto:
IEnumerable<object> list = CreateList();
foreach (object o in list)
{
list.Remove(o);
}
No compila. Es un error de concepto querer modificar la colección que se itera en un foreach, porque no tienes ni idea de si esa colección permite la modificación, ya que está oculta bajo un IEnumerable. Por ejemplo esta función crea una lista infinita de números positivos:
public static IEnumerable<int> InfiniteList()
{
int i = 0;
while (true)
yield return i++;
}
¿Qué sentido tiene el querer borrar un elemento de esa colección?
Si quieres borrar elementos de una lista recórrela con un bucle for invertido (del último al primero) y listo, más sencillo imposible. Pero pedirle a un foreach que permita hacer eso es no entender cual es la responsabilidad de un iterador.
Un saludo!
Vicente
P.D.: obviamente VB6 es infame y por eso dejaba hacer cosas infames como esa :p
Cita de: Vicente en 09 de Julio de 2010, 01:02:20 PM
P.D.: obviamente VB6 es infame y por eso dejaba hacer cosas infames como esa :p
xDDDD Bueno, oviando lo ovio :P a mi me sucede a veces que implemento un codigo para recorrer una lista de elementos a los cuales les aplico un criterio concreto para una accion llamemosla x. Ese criterio puede dar como resultado que el elemento ejecute un metodo, por ejemplo, o bien que se descarte o se anule, y precise cepillarmelo de la lista por que ya no sirve por x o y. A mi me resulta mas comodo cepillarmelo en ese mismo momento que ya lo tengo localizado, teniendo asi que evitarme hacer otra pasa a la lista para eliminar todos los elementos que haya podido descartar utilizando otra lista mas para almacenar los indices de cada elemento descartado. Si la solucion es que deberia usar un for en vez de un foreach te diria que a mi como programador, me resulta mas comodo utilizar un foreach que un for por razones obvias, y mas todavia si estamos hablando de un lenguaje de alto nivel de abstraccion en vez de uno de bajo nivel como podria ser C++. Piensa que este caso es intuitivo y le ha pasado a mucha gente.
Si en .NET el problema es que la interfaz IEnumerable no implementa dicho metodo Remove() quizas se deba diseñar otra inferfaz que si lo permitiera. No se, es que si no veo dar muchas vueltas para algo tan sencillo como es simplemente eliminar un elemento y pasar el cursor al siguiente :P
Salu2...
Sigues sin entenderlo :( No van a hacer un enumerador que permita borrar porque es imposible de hacer: el iterador resuelve el problema de poder recorrer de la misma forma (foreach) cosas que son muy diferentes (una lista, un árbol, un heap,...). Y nada obliga a que esas estructuras sean modificables, es un problema totalmente diferente.
Es decir, si mi problema es tener que recorrer algo, por ejemplo un List, es mejor usar foreach en vez de for ya que si más adelante cambio de List a por ejemplo Tree, mi código de recorrer seguiría funcionando sin tener que cambiar ni una línea. Si usara un for seguramente mi código no funcionaría porque para un árbol un bucle for no tiene sentido.
Pero si tu problema es que quieres modificar una colección, foreach no es lo que buscas porque foreach asume que las colecciones son inmutables. Si quieres usar foreach, trabaja con esa idea en la cabeza y genera una nueva colección de cosas a partir de la vieja (igual que al modificar un string lo que haces realmente es generar un string nuevo porque los strings son inmutables).
La confusión de mucha gente (supongo) es que usas como clase List, y como List permite borrar, pero además implementa IEnumerable con lo que se puede usar en un foreach, pues parece lógico el juntar las dos cosas y borrar en el foreach. Pero de lógico no tiene nada.
Un saludo!
Vicente
Cita de: Vicente en 09 de Julio de 2010, 01:43:15 PM
Sigues sin entenderlo :( No van a hacer un enumerador que permita borrar porque es imposible de hacer: el iterador resuelve el problema de poder recorrer de la misma forma (foreach) cosas que son muy diferentes (una lista, un árbol, un heap,...). Y nada obliga a que esas estructuras sean modificables, es un problema totalmente diferente.
Vale, este punto si lo he entendido, pero antes no me he explicado bien creo (creo que me estas explicando que el enumerador no tiene capacidad de eliminar el elemento de la lista que recorre). En mi caso, creo que no deberia importar que IEnumerable implementase o no un metodo Remove(), yo elimino el elemento llamando a la propia coleccion desde dentro del foreach:
object lista[];
foreach object ite in lista
{
...
lista.Remove(ite);
...
}
Se supone que ahi no deberia intervenir el iterador salvo para pasar la referencia del elemento de la lista, no? ???
Cita de: Vicente en 09 de Julio de 2010, 01:43:15 PM
La confusión de mucha gente (supongo) es que usas como clase List, y como List permite borrar, pero además implementa IEnumerable con lo que se puede usar en un foreach, pues parece lógico el juntar las dos cosas y borrar en el foreach. Pero de lógico no tiene nada.
Olvidandote de si es un List<> o un Colecction o un simple Array, el programador de calle solo ve que tiene una lista de elementos que puede recorrer comodamente con un foreach en donde el iterador toma la instancia del objeto que representa el elemento. Con ello al igual que puede acceder al objeto y sus metodos entiende que puede llamar in situ a la propia coleccion, lista o array para decirle en ese instante "borrame el objeto al que el iterador hace referencia". A eso me refiero con intuitivo que no logico (estoy seguro que muchas cosas que hago rompen con el esquema logico de cualquier programador xDDDDDDDDDDDDDDDDDDDDD). Igualmente entiendo que en la mayoria de los casos (por ejemplo, un metodo para buscar y eliminar un elemento de la lista) un simple for bastaria de sobra.
Salu2...
Cita de: [EX3] en 09 de Julio de 2010, 02:07:30 PM
Vale, este punto si lo he entendido, pero antes no me he explicado bien creo (creo que me estas explicando que el enumerador no tiene capacidad de eliminar el elemento de la lista que recorre). En mi caso, creo que no deberia importar que IEnumerable implementase o no un metodo Remove(), yo elimino el elemento llamando a la propia coleccion desde dentro del foreach:
object lista[];
foreach object ite in lista
{
...
lista.Remove(ite);
...
}
Se supone que ahi no deberia intervenir el iterador salvo para pasar la referencia del elemento de la lista, no? ???
Sí que importa: foreach trabaja sobre IEnumerables que son enumeraciones de elementos. A ti te parece que no es un IEnumerable porque sabes que tres líneas más arriba tienes que es una lista o un array o lo que sea, pero para el foreach es un IEnumerable. Ten en cuenta que un bucle for y un bucle foreach se traducen en cosas muy diferentes al compilarse. Un foreach se traduce a algo como esto:
IEnumerator<object> enumerator = list.GetEnumerator();
while (enumerator.MoveNext())
{
object o = enumerator.Current;
// hacer algo con o
}
Y ahí el framework no tiene ni idea de si el IEnumerable originalmente era un List, un array, un yield,...
Cita de: [EX3] en 09 de Julio de 2010, 02:07:30 PM
Olvidandote de si es un List<> o un Colecction o un simple Array, el programador de calle solo ve que tiene una lista de elementos que puede recorrer comodamente con un foreach en donde el iterador toma la instancia del objeto que representa el elemento. Con ello al igual que puede acceder al objeto y sus metodos entiende que puede llamar in situ a la propia coleccion, lista o array para decirle en ese instante "borrame el objeto al que el iterador hace referencia". A eso me refiero con intuitivo que no logico (estoy seguro que muchas cosas que hago rompen con el esquema logico de cualquier programador xDDDDDDDDDDDDDDDDDDDDD). Igualmente entiendo que en la mayoria de los casos (por ejemplo, un metodo para buscar y eliminar un elemento de la lista) un simple for bastaria de sobra.
Tu tienes una lista, array,... de elementos fuera del foreach, pero dentro del foreach lo que tienes es una enumeración de elementos.
Yo implemento mi version de IDisposable en mis objetos.
Al añadir un elemento a una coleccion, me engancho al evento Disposed y cuando se invoca el evento, guardo el elemento a eliminar en una lista ToRemove, en el siguiente Update() recorres la lista ToRemove y eliminas todas las instancias.
Y esto lo tengo transparente usando una clase que me he hecho llamada GenericCollection<T> donde desato un monton de eventos para cada operacion. (InserBefore, InsertAfter, RemoveAfter, RemoveBefore....)
@Blau: interesante idea, me la apunto para futuras implementaciones :)
@Vicente: gracias por la explicacion. Ahora entiendo un poco mas el mecanismo del IEnumerable en los foreach de .NET y esas restricciones tecnicas que me comentas. Lo del VB6.0 supongo que tendra sentido al trabajar unicamente con colecciones genericas (foreach no nos sirve para arrays por ejemplo). Supongo que al tener que trabajar solo con una estructura unica y simple no habran tenido problema de adaptar su implementacion del iterador para tener control completo sobre la coleccion, permitiendo la eliminacion de elementos por ejemplo. En fin, VB6.0 y sus malas artes xDDDD
Salu2...
Un último ejemplo para ver si lo entiendes mejor.
Un foreach/IEnumerable es el equivalante en base de datos a un SELECT. Tu lo que me estás diciendo es que en medio de un SELECT quieres hacer un UPDATE o un DROP o un DELETE. Lo mismo en BBDD se puede hacer semejante cosa (ando un poco oxidao de SQL), pero estoy tambión seguro que cualquier DBA te cortaría las manos por escribir una query así :p Si haces un SELECT es porque quieres obtener un subconjunto de datos.
En .NET directamente no te dejan, por si acaso :p
Cita de: blau en 09 de Julio de 2010, 05:03:12 PM
Yo implemento mi version de IDisposable en mis objetos.
Al añadir un elemento a una coleccion, me engancho al evento Disposed y cuando se invoca el evento, guardo el elemento a eliminar en una lista ToRemove, en el siguiente Update() recorres la lista ToRemove y eliminas todas las instancias.
Y esto lo tengo transparente usando una clase que me he hecho llamada GenericCollection<T> donde desato un monton de eventos para cada operacion. (InserBefore, InsertAfter, RemoveAfter, RemoveBefore....)
Lo mismo esta clase te valdría para tu GenericCollection: ObservableCollection
http://msdn.microsoft.com/en-us/library/ms668604.aspx
Cita de: Vicente en 09 de Julio de 2010, 10:27:39 PM
Un último ejemplo para ver si lo entiendes mejor.
El concepto de foreach, crear una enumeracion de una lista de elementos, lo tengo claro, Vicente. En VB6.0 igualmente se genera una enumeracion a parte de la coleccion a la que referencia mediante esta implementacion en las clases que hacen de colecciones personalizadas:
Public Property Get NewEnum() As IUnknown
'esta propiedad permite enumerar
'esta colección con la sintaxis For...Each
Set NewEnum = varCol.[_NewEnum]
End Property
Que en .NET no te dejen por que ven logico que recorrer una coleccion no debe permitir interactuar con ella, bueno, aceptamos barco con tu ejemplo del SQL, pero como todo, si tecnicamente es posible, deberian dejar la puerta abierta a esa posibilidad a cuenta y riesgo del programador por que por ejemplo a mi me sigue parecio mas logico e intuitivo operar desde un bucle que tener que hacerme una lista con los elementos descartados para luego tener que recorrer esa lista cargandome los elementos antes visitados, en resumen, hacer dos vueltas (gasto de tiempo) y gastar mas memoria (dos listas en vez de una), esto lo veo dar vueltas como un tonto, pero bueno.
Salu2...
Cita de: Vicente en 09 de Julio de 2010, 10:32:11 PM
Cita de: blau en 09 de Julio de 2010, 05:03:12 PM
Yo implemento mi version de IDisposable en mis objetos.
Al añadir un elemento a una coleccion, me engancho al evento Disposed y cuando se invoca el evento, guardo el elemento a eliminar en una lista ToRemove, en el siguiente Update() recorres la lista ToRemove y eliminas todas las instancias.
Y esto lo tengo transparente usando una clase que me he hecho llamada GenericCollection<T> donde desato un monton de eventos para cada operacion. (InserBefore, InsertAfter, RemoveAfter, RemoveBefore....)
Lo mismo esta clase te valdría para tu GenericCollection: ObservableCollection
http://msdn.microsoft.com/en-us/library/ms668604.aspx
Sabia que tenia que existir... :o estuve buscando y no lo encontre. Tomo nota... :)
Me pasa igual que con el delegado Func, sabia que tenia que estar definido , al igual que Action y Predicate pero no lo encontraba hasta que lo vi en un código.
Cita de: blau en 10 de Julio de 2010, 12:18:27 AM
Sabia que tenia que existir... :o estuve buscando y no lo encontre. Tomo nota... :)
Me pasa igual que con el delegado Func, sabia que tenia que estar definido , al igual que Action y Predicate pero no lo encontraba hasta que lo vi en un código.
Ya, el framework es infinito :(
Cita de: [EX3] en 09 de Julio de 2010, 11:00:06 PM
Cita de: Vicente en 09 de Julio de 2010, 10:27:39 PM
Un último ejemplo para ver si lo entiendes mejor.
El concepto de foreach, crear una enumeracion de una lista de elementos, lo tengo claro, Vicente. En VB6.0 igualmente se genera una enumeracion a parte de la coleccion a la que referencia mediante esta implementacion en las clases que hacen de colecciones personalizadas:
Public Property Get NewEnum() As IUnknown
'esta propiedad permite enumerar
'esta colección con la sintaxis For...Each
Set NewEnum = varCol.[_NewEnum]
End Property
Que en .NET no te dejen por que ven logico que recorrer una coleccion no debe permitir interactuar con ella, bueno, aceptamos barco con tu ejemplo del SQL, pero como todo, si tecnicamente es posible, deberian dejar la puerta abierta a esa posibilidad a cuenta y riesgo del programador por que por ejemplo a mi me sigue parecio mas logico e intuitivo operar desde un bucle que tener que hacerme una lista con los elementos descartados para luego tener que recorrer esa lista cargandome los elementos antes visitados, en resumen, hacer dos vueltas (gasto de tiempo) y gastar mas memoria (dos listas en vez de una), esto lo veo dar vueltas como un tonto, pero bueno.
Salu2...
Si me respondes eso es porque sigues sin entenderlo :( Pero más ejemplos ya no se me ocurren, solo me queda linkarte a la MSDN y que te la creas porque sí.
http://msdn.microsoft.com/en-us/library/ttw7t8t6%28VS.71%29.aspx
Citar
The foreach statement repeats a group of embedded statements for each element in an array or an object collection. The foreach statement is used to iterate through the collection to get the desired information, but should not be used to change the contents of the collection to avoid unpredictable side effects.
Por cierto, respecto al gasto de memoria y de tiempo, lo que hay que hacer en estos casos, y es el camino hacia donde va .NET (ya lo apunta con LINQ) es que cuando actualices, en vez de modificar la lista actual, devuelves una nueva lista que tiene solo los objetos que han sobrevivido la actualización.
Esto por un lado te ahorra lo de las dos vueltas, pero por otro lado queda el "problema" de tener dos listas. Pero realmente la memoria que duplicas es ínfima porque lo que contienen las listas son referencias a los objetos, no los objetos en sí. Es decir, el famoso:
enemies.ForEach(x => x.Update());
Debería reescribirse correctamente como:
enemies = enemies.Where(e => { e.Update(); return e.IsAlive; }).ToList();
En la Xbox de todas formas lo mejor sería seguir usando un bucle for invertido (el primer código tiene el bug que comentábamos antes y tira una referencia a la basura, el segundo código es correcto pero tira dos referencias).
Cita de: Vicente en 10 de Julio de 2010, 01:06:00 AM
Por cierto, respecto al gasto de memoria y de tiempo, lo que hay que hacer en estos casos, y es el camino hacia donde va .NET (ya lo apunta con LINQ) es que cuando actualices, en vez de modificar la lista actual, devuelves una nueva lista que tiene solo los objetos que han sobrevivido la actualización.
Interesante. Tomo nota de esto ultimo :) Y ya que sacas el tema, que tal va el soporte LINQ en XNA? Lo implementaron en la ultima version si no ando desencaminado, no? De ser asi, rinde decentemente? Lo de poder usar LINQ para hacer consultas sobre las colecciones es un recurso que veo muy interesante para el tema de juegos (a mi ahora mismo en VB6.0 me vendria de perlas para lo que tengo montado).
Salu2...
Estooo, LINQ va igual de bien en XNA que en un programa normal :p En PC no hay ninguna razón que justifique el no usarlo, en la Xbox tienes que tener en cuenta todas las referencias que el GC va a tener que recolectar (la nueva lista y las lambdas que pases a la query).
Perdon, se me olvido concretar. Que tal va en XBox360? En PC es ovbio que ira igual uses o no XNA :P
Salu2...
En la Xbox también va bien, pero el problema es que normalmente genera mucha basura y matas al GC :(
Cita de: Vicente en 10 de Julio de 2010, 12:40:24 AM
Cita de: blau en 10 de Julio de 2010, 12:18:27 AM
Sabia que tenia que existir... :o estuve buscando y no lo encontre. Tomo nota... :)
Me pasa igual que con el delegado Func, sabia que tenia que estar definido , al igual que Action y Predicate pero no lo encontraba hasta que lo vi en un código.
Ya, el framework es infinito :(
ObservableCollection no esta soportado para la Xbox360. :(
Y en PC desde un proyecto XNA tampoco me aparece, aunque se supone que esta en System.dll ???
(http://astro.estanuestraweb.com/ObservableCollection.jpg)
Cierto, que raro, en XNA 4.0 (en Windows) se ve sin problemas. Viendo las referencias el VS2008 usa System del Framework 2.0 mientras que VS2010 usa System del Framework 4.0, pero en teoría por la documentación debería estar disponible para el Framework 3.0 y 3.5 (ya que es una parte fundamental de WPF...).
Raro, raro.
Ya sé que ha pasado, en 3.5 esta clase estaba en WindowsBase.dll, pero en 4.0 la movieron a System.dll :)
Totalmente de acuerdo con Vicente, esos cambios harian el tutorial mucho mas legible y correcto, en cuanto a los comentarios para optimizar las cosas en XBOX (como los de WaaghMan) yo los pondira como opcionales en un recuadro de otro color con la advertencia de "Contenido avanzado" para que la gente que esta empezando y no tiene tanta idea de como compila por dentro .NET (o las diferncias entre los GC) no se pierda.
Al ser un tutorial las importante es que sea lo mas legible posible, por lo que te recomiendo que evites el uso de Lamda expressions, ya que la mayoria de la gente que usa .NET no las entiende ni de broma.
Ademas yo añadiria un: "Si todavia no has leido le capitulo N-1, te recomendamos que le eches un vistazo antes de seguir <link>" al principio de cada post y un: "Nuestro tutorial sigue en el capitulo N+1, a que estas esperando para leerlo <link>" al final de cada post.
Asi haces la lectura mas facil, ya que al estar en un blog entre muchos mas post, el lector pierde su precioso tiempo al tener que irlos buscando uno a uno (y puede que le de pereza y no los lea), ademas asi ganas que con cualquier enlace a cualquier parte del tutorial todos van a poder leerlo entero facilmente (muy util para referencias en otros blogs o para los resultados de los buscadores).
Yo uso un montón LINQ para las bases de datos. Es otra forma de trabajar, lees todos los datos y luego haces las consultas en cliente. Va muy bien para informes grandes :D
Eso sí es un traga memoria... :'(
Por cierto, muy chulo el blog, da gusto leer cosas en castellano :P
Esto, LINQ no es un traga memoria, si no todo lo contrario :p De hecho es una de sus ventajas, que consume mucha menos memoria que las formas tradicionales de operar con colecciones.
Lo malo es que con todas las lambdas genera bastante mini-basura para el GC.
Corrijo, es un tragamemoria porque como no se mucho de LINQ a veces haga consultas y luego consultas sobre esas consultas... Si lo hago en menos 'subconsultas' seguro que no come tanto :P