Ver la versión completa : Intentando usar list.h de Oankali
anibarro
13/09/2005, 10:14
(Esto era una privado a Oankali pero por culpa del **** límite de 1000 caracteres...pues lo pongo aquí, y así de paso puede q alguien más me pueda ayudar :P)
Hola que tal, mirando cosas sobre colisiones, leí lo que pusiste en un post, que usabas una lista enlazada por cada plano, con todos los objetos de ese plano y los ibas añadiendo y borrando.
El caso es que mirando las cabeceras de las funciones de tu lista.h, tienen todo lo que se puede necesitar, peeero no consigo entender bien como usarla, cosas como qué tipos de datos hay que usar en una lista.
Si me dejas te hago una pregunta concreta, y a partir de ahí, yo creo que lo demás ya lo deduzco :P
Pongamos que creo una lista con:
LIST Lista;
Ahora tengo un struct que por ejemplo, por simplificar, sea:
typedef struct{
int x,y;
int speed;
} estado_sprite;
Y un puntero a una variable de ese tipo:
estado_sprite personaje;
Y quiero agregarlo a la lista "Lista", usando la función:
LISTITEM *ListAddItem(LIST *pList, void *pObject, unsigned long size);
Y ahora, lo que haría para añadirlo a la lista es:
ListAddItem(Lista,personaje)
y que añadiese "personaje" a la lista, pero ListAddItem devuelve un LISTITEM, y no sé si tengo que crear una variable de tipo LISTITEM e inicializarla con los valores de la estructura que quiero pasar o algo así...y tampoco tengo claro para que sirve el tamaño del final. He mirado el folder.c y okf.c para ver si deducía como lo usan, pero me pierdo. Con ese miniejemplo que te pido espero aclararme ya :rolleyes: Muuuchas gracias
Si intentas usar la list.h con la sdl no funciona, esta pensada para el sdk o por lo menos yo no he podido hacerla funcionar y me he hecho mi propia funcion de lista.
ni idea del list.h pero si quieres mandar tanto texto en un MP porque no lo metes en un fichero txt y se lo adjuntas?? ;)
anibarro
13/09/2005, 10:34
Eskema yo he cambiado los gp_malloc por malloc y esas cosas y luego he definido TRUE y FALSE, y supongo que debería funcionar, al menos es lo único que me ha dado error sin poder probarlo más xD
jjdrako pq no se pueden adjuntar archivos en los privados, y colgarlo de una web y darle link me parece un poco rebuscado ^^ (aunque lo he hecho en algun privado jaja)
puedo preguntar para que sirve esa lista?? simple curiosidad :D
anibarro
13/09/2005, 11:57
jeje pues no sirve para nada que no sirva una lista normal o un vector, la cosa es que esta todo muy bien implementado, con muchas operaciones y te ahorra el trabajo de teenr que hacerla tu ;P
Yo la quiero pq a la hora de dibujar todo en pantalla, tenia un vector con todos los personajes, objetos, etc, pero claro, siempre es mejor una lista enlazada que no tienes que preocuparte de redimensionarla y es mas rapido quitar y añadir elementos ^^
La única cosa que hace que list.h no sea ANSI C al 100% son, como dice anibarro el TRUE y FALSE y las funciones gm_malloc(), gm_free() y gm_memcpy().
Solo con cambiar esto, y la librería servirá perfectamente para cualquier SDK.
Si puse las funciones gm_*() es porque para el SDK oficial son las funciones que hay que utilizar para gestionar correctamente la memoria.
La función más complicada de la librería es ListAddItem(). Todas las otras se entienden bastante bien.
LISTITEM *ListAddItem(LIST *pList, void *pObject, unsigned long size);
LIST *pList: un puntero a la lista en la que queremos añadir un objeto.
void *pObject: un puntero al objeto que queremos añadir. Si size > 0, puede estar a NULL.
unsigned long size: si size > 0, se reservará automáticamente size octetos de memoria para el objeto. Además si pObject es diferente de NULL, se copiará el contenido apuntado por pObject en la memoria reservada automáticamente.
Al finalizar, la función devuelve un puntero al item que se ha creado.
Si size > 0, o sea cuando se ha reservado más memoria para el objeto, el miembro autoAllocated del item vale TRUE, sino vale FALSE.
Las funciones de supresión de items ListRemove*() tienen en cuenta este flag y si autoAllocated es igual a TRUE, antes de liberar la memoria reservada por el objeto item, primero se liberará la memoria reservada por el objeto apuntado por pObject.
Si size = 0, el programador tendrá que liberar él mismo el objeto apuntado por pObject, después de eliminar el item de la lista.
Como podéis ver, funciona de varias maneras diferentes. Todo depende de lo que queráis hacer:
Si queréis añadir un objeto que ya existe en memoria, size tiene que valer 0.
Si queréis añadir un duplicado de un objeto que ya existe en memoria, size tiene que valer sizeof(TIPOOBJETO), y pObject tiene que apuntar al objeto.
Si queréis añadir un nuevo objeto para luego inicializarlo, size tiene que valer sizeof(TIPOOBJETO), y pObject tiene que estar a NULL.
El puntero devuelto por la función no es imprescindible guardarlo, todo depende de lo que se quiera hacer.
Para acabar, recordad que no hay que confundir el objeto item con el objeto apuntado por el miembro pObject del objeto item.
Para acceder a un miembro de nuestro objeto, podemos hacerlo de dos maneras:
Directamente:
a = ((TIPOOBJETO *) item->pObject)->miembro;
A través de una variable:
TIPOOBJETO *miobjeto;
miobjeto = item->pObject;
a = miobjeto->miembro;
b = miobjeto->miembro2;
Y ahora os explicaré lo que hago yo para poder meter objetos de tipo diferente en la misma lista. Por ejemplo, imaginemos que tenemos sprites fijos y sprites animados. Crearemos 3 estructuras, una principal y dos que descienden de la principal.
typedef struct tagSPRITE {
unsigned char tipoObjeto;
unsigned short x, y;
} SPRITE;
typedef struct tagSPRITEFIJO {
unsigned char tipoObjeto;
unsigned short x, y;
...
} SPRITEFIJO;
typedef struct tagSPRITEANIMADO {
unsigned char tipoObjeto;
unsigned short x, y;
unsigned char frame;
...
} SPRITEANIMADO;
Cuando añadimos un sprite fijo haremos:
pItem = AddListItem(pListaSprites, NULL, sizeof(SPRITEFIJO));
((SPRITE *) pItem->pObject)->tipoObjeto = 1;
Cuando añadimos un sprite animado haremos:
AddListItem(pListaSprites, NULL, sizeof(SPRITEANIMADO));
((SPRITE *) pItem->pObject)->tipoObjeto = 2;
Luego, cuando los queremos animar solo buscaremos los que ((SPRITE *) pItem->pObject)->tipoObjeto == 1.
Pero para pintarlos, los buscaremos todos.
Hasta se podría crear un estructura primária para poder incluir objetos que no sean sprites en la lista:
typedef struct tagOBJETO {
unsigned char tipoObjeto;
} OBJETO;
Nunca añadiremos directamente objetos de tipo SPRITE y OBJETO, ya que son como estructuras abstractas para acceder más fácilmente a miembros de los objetos.
Para los que aún no lo hayan adivinado, es una manera de programar objeto en C puro.
Espero que la lección os haya gustado y que sobretodo os sea de alguna utilidad.
Oankali.
anibarro
13/09/2005, 12:34
Que puedo dedir :babea: muchas gracias por currartelo tanto y ponerlo tan claro, estoy aprendiendo mas con esto que en toda la carrera xDDD
Jur me estoy dando cuenta que esto es como usar clases con c, creas listas con componentes de la clase base (tipoObjeto), y luego puedes meter dentro objetos de cualquier clase que herede de esa, la caña :D
anibarro
13/09/2005, 15:48
(QUE NADIE INTENTE ESTO EN SU CASA QUE ESA MAL XD LEED MAS ABAJO)
Weno ahora q ya me funciona, pongo un ejemplo entero por si alguien mas lo quiere usar, que el detalle de añadir una estructura y usarla no lo has puesto y me he pasado un rato pensando ;P
LIST *pLista; //puntero a una lista
LISTITEM *pItem; //puntero a item de lista
typedef struct tagOBJETO {
//Struct "base", como el SPRITE de tu ejemplo
unsigned char tipoObjeto;
} OBJETO;
typedef struct{
//Struct para sprite
unsigned char tipoObjeto; //1=sprite animado, 2=sprite no animado
int x,y;
int speed;
int fotograma;
.
.
} sprite;
sprite personaje = {1,160,120,2,0,.....}; //creo e inicializo la variable personaje
Entonces ahora, para crear la lista, e introducir un objeto:
pLista= ListCreate(); // Crea lista vacia
pItem = ListAddItem(pLista, NULL, sizeof(sprite)); //añade objeto a lista
((OBJETO *) pItem->pObject)->tipoObjeto = 1; //pone tipoObjeto a 1 en el objeto q acabo de añadir
Ahora lo que no ha comentado Oankali antes:
((OBJETO *) pItem->pObject = &personaje; //Puntero del elemento que acabo de añadir, apuntando a la direccion de "personaje"
Ahora cada vez que queramos acceder a los elementos de la lista (por ejemplo a las coordenadas de los que sean sprites animados (tipoObjeto=1)) podemos hacer lo siguiente, teniendo cuidado de hacer el casting de pObject al tipo de la variable "personaje" (sprite):
int i=0; //parto del objeto con indice 0
while ((pItem = ListGetItemByIndex(pLista, i)) != NULL){//mientras hay objetos en la lista
if ( (((OBJETO *) pItem->pObject)->tipoObjeto) ==1 ){
printf ("%d,%d \n ", ((sprite *) pItem->pObject)->x, ((sprite *) pItem->pObject)->y );
}
i++; //sumo 1 al indice de la lista
}
Hay un par de funciones que no entiendo que hacen, ni como se podrían usar, a ver si tienes un rato y me lo explicas ^^:
LISTITEM *ListGetItemByObject(LIST *pList, void *pObject);
void *ListGetObjectByHandle(LIST *pList, int handle);
Huy, me parece que no ha quedado todo muy claro.
A ver, si tu declaras el objeto personaje de tipo sprite, significa que ya has reservado memoria para dicho objeto. Eso quiere decir que no hace falta reservar más memoria para él.
Lo que has hecho tu es un error ya que has declarado personaje al principio de la función, aquí se ha reservado memoria en la pila (local a la función).
Luego has añadido un item en la lista pidiendo que te reserve espacio para un objeto de tipo sprite, aquí se ha vuelto a reservar memoria (general a la aplicación).
Finalmente, has modificado pObject para que apunte a personaje, que te recuerdo que la visibilidad es de ambito local a la función. En este momento se pierde la dirección de la zona de memoria reservada por ListAddItem() de forma automática. El programa ya no será capaz de liberar correctamente esa memoria.
Además si suprimes el item, como que es autoAllocated, la librería intentará liberar la memória de personaje que no has reservado tu y que se encuentra en la pila: beep, error...
Entonces la instrucción correcta era:
pItem = ListAddItem(pLista, &personaje, 0);
Con size a 0, no se reserva memoria pero sí que se indica en donde se encuentra el objeto real.
Pero yo no lo haría así ya que, como he dicho antes, si la lista es una lista global, a la que salgas de la función que añade un personaje a la lista, la memoria de la estructura se liberará, aunque en la lista pObject seguirá apuntando a la misma dirección de memoria.
Si realmente quieres utilizar tu sistema de declarar personaje y luego introducirlo en la lista, tienes que hacerlo de la siguiente manera:
LIST *pLista; //puntero a una lista
LISTITEM *pItem; //puntero a item de lista
typedef struct tagOBJETO {
//Struct "base", como el SPRITE de tu ejemplo
unsigned char tipoObjeto;
} OBJETO;
typedef struct{
//Struct para sprite
unsigned char tipoObjeto; //1=sprite animado, 2=sprite no animado
int x,y;
int speed;
int fotograma;
.
.
} sprite;
sprite personaje = {1,160,120,2,0,.....}; //creo e inicializo la variable personaje
//Entonces ahora, para crear la lista, e introducir un objeto:
pLista= ListCreate(); // Crea lista vacia
pItem = ListAddItem(pLista, &personaje, sizeof(sprite)); //añade objeto a lista e inicializa el objeto con el contenido de personaje
((OBJETO *) pItem->pObject)->tipoObjeto = 1; //pone tipoObjeto a 1 en el objeto q acabo de añadir
Con esto creas un item, reservas espacio para un nuevo objeto sprite, y lo inicializas automáticamente con el contenido del objeto personaje.
En este caso la estructura solo te sirve como base para la inicialización.
Pero tampoco lo haría así, esta es mi solución:
LIST *pLista; //puntero a una lista
LISTITEM *pItem; //puntero a item de lista
typedef struct tagOBJETO {
//Struct "base", como el SPRITE de tu ejemplo
unsigned char tipoObjeto;
} OBJETO;
typedef struct{
//Struct para sprite
unsigned char tipoObjeto; //1=sprite animado, 2=sprite no animado
int x,y;
int speed;
int fotograma;
.
.
} sprite;
sprite *personaje; //declaro una variable temporal
//Entonces ahora, para crear la lista, e introducir un objeto:
pLista= ListCreate(); // Crea lista vacia
pItem = ListAddItem(pLista, NULL, sizeof(sprite)); //añade objeto a lista
// inicializo los miembros del objeto
personaje = item->pObject;
personaje->tipoObjeto = 1; //pone tipoObjeto a 1 en el objeto q acabo de añadir
personaje->x = 160;
personaje->y = 120;
personaje->speed = 2;
personaje->fotograma = 0;
...
Finalmente para recorrer la lista te complicas mucho la vida. No utilizas la lista como una lista, sino como un array con un íindice. Si te fijas en ListGetItemByIndex() te darás cuenta que ya se recorre la lista para buscar el índice que indicas, con lo que a cada iteración recorres una parte de la lista.
Lo correcto sería lo siguiente:
pItem = pLista->pFirst;
while (pItem) //mientras hay objetos en la lista
{
if ( (((OBJETO *) pItem->pObject)->tipoObjeto) ==1 ){
printf ("%d,%d \n ", ((sprite *) pItem->pObject)->x, ((sprite *) pItem->pObject)->y );
}
pItem = pItem->pNext; // paso al objeto siguiente
}
Este código es mucho más rápido que el tuyo, sobretodo en una lista larga.
Espero que ahora sí que haya quedado claro. Como puedes ver, en mi explicación inicial no me había olvidado nada. :)
Por cierto, la sintaxis para inicializar la estructura que utilizas tu en VC++ no consigo compilarla.
Oankali.
anibarro
13/09/2005, 17:43
Vaaaya avería que estaba haciendo jur Menos mal que me ha dado por ponerlo antes de seguir :rolleyes:
Ahora sí que me ha quedado claro **espero** xD Voy a ver si lo arreglo...que bonita es la programación cuando se hacen bien las cosas ^^
ah y la sintaxis para inicializar un struct puede que sólo funcione con algunos compiladores, nu sé, yo uso gcc
Powered by vBulletin® Version 4.2.5 Copyright © 2025 vBulletin Solutions Inc. All rights reserved.