Jump to content


Photo

Mostrar información de un plano


  • Please log in to reply
16 replies to this topic

#1 cadetill

cadetill

    Advanced Member

  • Moderadores
  • PipPipPip
  • 994 posts
  • LocationEspaña

Posted 06 September 2011 - 07:31 AM

Hola a todos

Hace tiempo que no me pasaba por aquí porque he andado muy liado en el trabajo, encima no hacía trabajo de programador, así que entre el poco tiempo libre y las pocas ganas de abrir el Delphi.... pues eso jejeje

Ahora estoy haciendo una pequeña aplicación para mi mismo, para intentar no oxidarme en esto de la programación y se me presenta una duda y, como no, recurro a vosotros que siempre estáis ahí jejeje

Os explico. Tengo un plano dividido en cuadrículas de unos 650x550 y quiero mostrar este plano por pantalla en cualquier componente que tenga celdas (StringGrid, DBGrid,.......). Está claro que todas las celdas en pantalla no se verán, por lo que se tendrá que hacer scroll vertical y horizontal. Las celdas se pintarán de color según una serie de condiciones definidas por el usuario. También habrá un TScrollBar para poder hacer "zoom" en el plano (mostrar más o menos celdas tanto en horizontal como en vertical).

De momento tengo una tabla en Firebird con el plano estilo

x: integer
y: integer
editable: smallint

El campo "editable" indica si esa casilla puede o no ser editada (cambiada de color) por el usuario.

Luego hay otras tablas que contendrán las celdas y color que el usuario quiere resaltar.

Actualmente tengo un TStringGrid y en el OnDrawCell realizo una consulta a la base de datos para saber con qué color pintar esa celda. Claro, cuando hay muchas celdas visualizadas, el proceso se ralentiza mucho y queda feo.

Se os ocurre alguna cosa para poder hacer ésto algo más "ágil" o mejorarlo

Gracias

Nos leemos
cadetill

  • 0

#2 Wilson

Wilson

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2137 posts

Posted 06 September 2011 - 07:49 AM

Cadetill es un gusto leerte de nuevo, en lo poco que logro entender y a groso modo, se me ocurre agregar un campo que almacene el color  en la primera tabla que describes (x,y, editable, color), el cual llenarías con un trigger al insertar o actualizar, de tal manera que el evento OnDrawCell no tenga que consultarlo en la  base de datos (pues esto ya la habrías hecho con el trigger),  por el contrario  que lo tenga a mano en el dataset.

Saludos
  • 0

#3 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14448 posts
  • LocationMéxico

Posted 06 September 2011 - 08:08 AM

Un gusto leerte Xavi, no se que versión tengas de delphi pero desde Turbo Delphi (2006) hay un componente que se llama GridPanel y me parece que te pudiera servir.

Salud OS

Attached Files


  • 0

#4 cadetill

cadetill

    Advanced Member

  • Moderadores
  • PipPipPip
  • 994 posts
  • LocationEspaña

Posted 06 September 2011 - 09:27 AM

Buenas,

Ante todo gracias por las rápidas respuestas.

Intento explicarme mejor

Esto es para un juego en el que paso mis ratos libres y va sobre la época de los romanos en la Península Ibérica (Hispania por si a alguno le suena jejeje). Bien, el mapa de este juego es una cuadrícula de unos 679x539. Cada casilla puede ser o no colonizable (serían las celdas fijas y representan ríos, mar y montañas). Las casillas colonizables pueden estar o no ocupadas. Si están ocupadas, se necesita saber nombre de jugador, rango, puntuación y demás información. Si no está ocupada, el tipo de terreno.

En la base de datos guardo el mapa en sí (coordenas y tipo de terreno), los jugadores (nombre, puntuación, rango,....) y sus ciudades (nombre y posición). Esto es más o menos a grandes rasgos.

Luego el usuario pone una serie de condiciones (ej: resaltar de un color a los jugadores de un determinado rango o una determinada alianza).

Lo que tiene que hacer el programa es mirar en una determinada celda qué hay y si lo que hay concuerda con alguna de las condiciones del usuario para pintar o no esa celda.

Como decía, ahora tengo un TStringGrid (podría valerme el GridPanel si está en D2010 pero tendría el mismo problema). He pensado dos formas de hacer lo que necesito.

1.- Al iniciar el programa rellenar cada celda del mapa con un objeto con la información y en el OnDrawCell comparar la información del objeto con las condiciones del usuario. Problema: tarda en arrancar el programa y consume mucho recurso (son 365.981 celdas a rellenar con sus respectivos objetos).

2.- Dejar el TStringGrid sin información y en el OnDrawCell hacer una consulta a la base de datos para sacar la información de esa celda, compararla con las condiciones del usuario y actuar en consecuencia (pintar o no la celda). Con esto solucionamos el problema del arranque lento pero si el usuario quiere ver muchas celdas a la vez, se ralentiza el dibujado del TStringGrid.

No se si ahora se entiende más jejejejeje

Alguna idea para mejorar el rendimiento de ésto?

Gracias de nuevo

Nos leemos
cadetill

  • 0

#5 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4107 posts
  • LocationMadrid - España

Posted 06 September 2011 - 10:42 AM

¿Necesariamente debes pintar en un Grid?.
¿Y si pintas en un TImage por cuadrículas directamente en su canvas (CopyRect) o te construyes un array de imágenes?

Saludos.

  • 0

#6 cadetill

cadetill

    Advanced Member

  • Moderadores
  • PipPipPip
  • 994 posts
  • LocationEspaña

Posted 06 September 2011 - 01:26 PM

Hola escafandra

No, no es necesario el uso de un Grid, simplemente me decidí por este componente por la facilidad de uso para el usuario final (scroll horizontal y vertical, selección de una celda, ....).

La idea del TImage no me desagrada, es más, dado que sería yo el que se encargara de pintar, podría mostrar un TProgressBar durante el proceso de pintado o algo similar para "entretener" al usuario mientras se pinta el mapa.

Dado que la consulta de cada "celda" no me la quita nadie y ese tiempo lo tengo que perder sí o sí, la carga inicial del TImage está clara, si al iniciar la aplicación se muestras 25x25 posiciones, son 625 consultas. La duda me llega con el scroll. Si en la carga inicial se visualizan las columnas de 1 a 25 y me desplazo una posición para ver las columnas 2 a 26 (mediante un TButton, un TScrollBar o lo que sea), ¿tendría que volver a realizar las consultas de las columnas 2 a 25? ¿O existe alguna manera de "desplazar" la imagen para no tener que volver a lanzar las consultas?

Gracias

Nos leemos
cadetill
  • 0

#7 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14448 posts
  • LocationMéxico

Posted 06 September 2011 - 01:29 PM

Puedes checar este hilo, me parece que te viene bien

http://www.delphiacc...lphi-24/puzzle/

Salud OS
  • 0

#8 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4107 posts
  • LocationMadrid - España

Posted 06 September 2011 - 01:47 PM

El enlace que propone egostar es digno de tener en cuenta...


Si conoces de antemano el tamaño máximo del TImage puedes introducirlo en un  TScrollBox, por ejemplo. De todas formas siempre puedes agrandarlo y rellenarlo.




Saludos.
  • 0

#9 cadetill

cadetill

    Advanced Member

  • Moderadores
  • PipPipPip
  • 994 posts
  • LocationEspaña

Posted 06 September 2011 - 03:37 PM

Buenas de nuevo

Me miraré con detenimiento el ejemplo de chackall, pero me parece que se va de mis conocimientos jejejejeje Así que me toca "estudiarlo" a fondo.

Gracias por los comentarios

Nos leemos
cadetill
  • 0

#10 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14448 posts
  • LocationMéxico

Posted 06 September 2011 - 04:03 PM

Buenas de nuevo

Me miraré con detenimiento el ejemplo de chackall, pero me parece que se va de mis conocimientos jejejejeje Así que me toca "estudiarlo" a fondo.

Gracias por los comentarios

Nos leemos
cadetill


No te preocupes, que se va del conociento muchos de por aquí. :D

Salud OS
  • 0

#11 Sergio

Sergio

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1092 posts
  • LocationMurcia, España

Posted 07 September 2011 - 10:31 AM

Independientemente de que uses un componente u otro, creo que el problema principal te viene de tener que consultar tanto a la base de datos, asi que te propongo esto:

Cada vez que pidas un color de una casilla, almacenala en un stringlist o donde mejor veas, un array dinamico tambien valdria, y luego, en sucesivas consultas, usa el color almacenado en la matriz si encuentras esas coordenadas en tu lista, y solo si no lo encuentra, lo lees de la base de datos, lo almacena en el array para evitar volver a pedirlo, y entonces lo dibujas.

Esto hace que cada cuadricula se lea de la base de datos una vez, y justo en el momento que se necesita, no antes.

Es solo una idea, igual necesitas refrescos continuos a la base de datos porque el mapa cambia en tiempo real... pero bueno, la idea podria ser modificada segun el caso.

Otro problema es que el redibujado en si del mapa aun sea lento:

Puedes  no tratar de dibujar el mapa puntito a puntito: En temas de ray tracing pasa igual, se tardan horas en obtener la imagen completa, pero puedes dibujar cuadriculas de 4x4 inicialmente, con el color correspondiente a la primera casilla -esquina sup. izda.- y en una segunda pasada "refinar" el dibujo consultando los colores de las otras 3 esquinas de 2x2, luego de 1x1, y terminarias.

Yo lo hacia con pixeles, asi que empezaba digamos con cuadros de 64x64, bajaba a 32x32... hasta llegar a 1x1. La imagen era visible pero borrosa pasados solo unos minutos, y solo si querias ver mas detalle lo dejabas continuar refinando. Tu podrias probar a inciar con 8x8, solo tiene que ser una potencia de 2.

Visto asi, un usuario puede hacer un zoom, y cuando la imagen aun se ve a 4x4, ya puede hacer un segundo zoom, lo que agiliza mucho el juego.

Un ultimo problema es que el redibujado, aun siendo ahora mas agil, te bloquee el uso del juego mientras ocurre.

Una idea al respecto: Pon un "spooler" de cuadriculas a ponerles color, podria ser un ObjectList relleno cobn objetos a medida tipo (x, y, tamaño), donde inicialmente pones todos los recuadros de 4x4 (o 8x8, segun decidas) y un proceso aparte, en otro hilo, puedes ir leyendo la primera cuadricula a definir su color, pedir el color -de la esquina sup. izda.- a la base de datos o el array de puntos ya leidos, dibujar el recuadro, eliminar la cuadricula ya dibjada y crear 3 nuevas de 2x2 (de la mitad de tamaño de la que borras) en las esquinas que dejamos sin "refinar".

Como esos nuevos recuadros mas chicos van al final de la lista, el dibujado se hara en pasadas cada vez mas finas, pero al ser otro hilo, este proceso no interfiere en el juego, el jugador puede en cualquier momento cambiar el zoom: Las cuadriculas pendientes se borran y se añaden de nuevo los recuadros de 4x4 con el nuevo zoom. El otro hilo se percatara, borrara todo -o no- y comenzara a dibujar cuadros nuevos grandes tapando lo anterior (por eso no es necesario borrar el mapa anterior realmente)... el redibujado del mapa se haria totalmente en "paralelo" al juego en si, y en pasadas cada vez mas "finas".
  • 0

#12 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4107 posts
  • LocationMadrid - España

Posted 07 September 2011 - 02:42 PM

Yo me pregunto si realmente hace falta usar una base de datos...


Saludos.
  • 0

#13 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6295 posts
  • LocationArgentina

Posted 08 September 2011 - 05:32 PM

Yo diría que consideres la posibilidad de utilizar la potencia de alguna biblioteca gráfica como OpenGL.
Quizá tengas que batallar más en lograr darle forma a tus mapas pero te darán la seguridad y velocidad de que estará hecho perfectamente y luego por debajo de esa gráfica lo demás es pura lógica.


Saludos,
  • 0

#14 andres1569

andres1569

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 431 posts

Posted 09 September 2011 - 11:10 AM

Hola Cadetill:

Desarrollando la idea de Sergio, de usar un array y consultar los datos sólo bajo demanda, creo que no es descabellado definir una matriz de punteros con esas dimensiones, 650 x 550. Suponiendo que la información de cada celda ocupe unos 100 bytes (echando por arriba), el conjunto una vez relleno ocuparía unos 35 MBytes, algo que cualquier máquina moderna tiene de sobra, y si es dedicada a juegos como el tuyo más aún  :) . Y en el caso de no estar rellena de inicio se quedaría en unos 3 MBytes que es lo que ocuparían sólo los punteros (pensando ya en punteros de 8 bytes en sistemas de 64 bits).

Ejemplo de estructuras:


delphi
  1. PCellInfo = ^TCellInfo;
  2. TCellInfo = record
  3.   Color : TColor;
  4.   Editable : Boolean;
  5.   etc ...
  6.   end;
  7.  
  8. TPlano : array [650, 550] of PCellInfo;



Al arrancar, inicializas todos los elementos de la matriz a nil, y cuando necesitas dibujar o acceder a una celda compruebas si ya ha sido asignada, en caso contrario creas un puntero PCellInfo, lo asignas a esa celda y haces la consulta a la BBDD de esa/s celda/s, de esa forma sólo se ralentiza lo justo y cuando las celdas no han sido todavía visualizadas o accedidas.

Si sigues este sistema creo que es mejor usar un TDrawGrid, dibujando como pensabas mediante el evento OnDrawCell accediendo a esa matriz, ya que el TStringGrid consume más memoria ya que está pensado para almacenar las cadenas de cada celda, aunque también puedes usar el TStringGrid accediendo a su propiedad Objects[ACol, ARow] para almacenar esa información y comprobando igualmente si es nil o no cada vez.

Saludos
  • 0

#15 cadetill

cadetill

    Advanced Member

  • Moderadores
  • PipPipPip
  • 994 posts
  • LocationEspaña

Posted 19 September 2011 - 10:49 AM

Hola de nuevo

Pues muchas gracias por todas las sugerencias.

El tema de la matriz que habéis expuesto es algo que "hacía" al principio y creo que volveré a ello. Lo que pasa es que no usaba una matriz, sino un objeto que era asignado a cada celda del TStringGrid. Tardaba un poco en arrancar el programa, pero luego el redibujado era más o menos ligero (dependiendo de la cantidad de celdas que se visualizaban). Eso sí, como dice Andrés hay un consumo de memoria.

Iré mirándome las distintas opciones y cuando tenga algo que más o menos funcione, os lo cuelgo para que me hagáis críticas jejejeje

Nos leemos
cadetill
  • 0

#16 Sergio

Sergio

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1092 posts
  • LocationMurcia, España

Posted 20 September 2011 - 02:49 AM

Un añadido por si te quedas sin memoria con tanto objetito: Si usas una lista para almacenarlos, cada vez que accedas a uno de ellos puedes moverlo al primero de la lista, de esta forma, podrías limitar el numeor de bloques en memoria a, por ejemplo, 5000, de forma que al tener que añadir una nuevo, si ya tienes 5000, primero borarias el último de ellos, que es que hace mas tiempo que tubiste que leer para dibujar.

Eso quiere decir que partes del mapa que hace rato que no visitas podrias ser borradas si hace falta almacenar nuevas zonas, y te aseguras de que la vcantidad de memoria usada por este invento estara por debajo de la cantidad que tengas disponible, que incluso podrias hacer configurable o definirla dinamicamente en funcion de la memoria libre que haya en el sistema.

Y ademas es muy simple de programar!
  • 0

#17 andres1569

andres1569

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 431 posts

Posted 20 September 2011 - 09:38 AM

Sergio, programándolo en una lista ¿cómo hacemos para saber qué elemento de la lista representa a cada celda? Pongamos que tengo que dibujar la celda que hay en (Col=100, Row=234), ¿recorro la lista hasta encontrarla? Tengamos en cuenta que posiblemente hay que dibujar cada vez un puñado de celdas, pongamos unas 20 x 20 = 400.

Saludos
  • 0




IP.Board spam blocked by CleanTalk.