Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





Opinion de nuevo formato de mapas

Iniciado por El_Chavo, 11 de Marzo de 2011, 05:52:48 PM

« anterior - próximo »

El_Chavo

Hola.

Quería saber sus opiniones acerca de una formato de mapas que voy a usar en un juego cRPG que llevo programando desde hace un tiempo.
El juego tendrá un aspecto clásico como The Magic Candle, Gates of Delirium o incluso el juego Lenges/Legends 2 de las viejas TI-99.

Para aquellos que no conozcais dichos juegos, son juegos 2D que consisten básicamente en un enorme mapa del mundo por el que libremente puedes moverte. El mapa del mundo contiene ciudades, catacumbas y etc... Además no suelen ser juegos muy lineales.

La cuestión es que los mapas todos están ya preconstruidos, no son como un Roguelike que todo es al azar, así que he creado un formato de mapas para este tipo de juegos, hecho en simple texto plano.
Os describo como lo he hecho:

los mapas se pueden abrir con un editor de texto plano, y se verá algo como esto (sin las líneas horizontales)

---------------------------------PARTE 1
Bosque Maldito
10
10
---------------------------------PARTE 2
0103,0103,0103,0103,0103,0103,0103,0103,0103,0103
0103,0001,0001,0001,0001,0001,0001,0001,0001,0103
0103,0001,0001,0001,0001,0001,0001,0001,0001,0103
0103,0001,0001,0001,0001,0001,0001,0001,0001,0103
0103,0001,0001,2001,0001,0001,0001,0001,0001,0103
0103,0001,0001,0001,0001,0001,0001,0001,0001,0103
0103,0001,0001,0001,0001,0001,1001,0001,0001,0103
0103,0001,0001,0001,0001,0001,0001,0001,0001,0103
0103,0001,0001,0001,0001,0001,0001,0001,0001,0103
0103,0103,0103,0103,0103,0103,0103,0103,0103,0103
----------------------------------PARTE 3
1,2
2,1
----------------------------------PARTE 4
2,3,1
4,6,2
2,5,1
7,2,3


La explicación es la siguiente, aunque seguro que ya os imaginais qué son algunas de las partes:

Parte 1: definiciones básicas del mapa: nombre, anchura y altura, en este caso, 10x10 tiles de superficie.

Parte 2: defición de cada celda del mapa. Cada número guarda información de la celda a la que corresponde.
Estos números tienen el siguiente formato:

<A><B><MM>

En donde:

<A> es la acción a la que se llamará cuando el jugador entre en dicha celda. Por ejemplo, en la celda cuya posición es [3,4] (empezamos a contar por el cero), vemos que se realiza la acción número 2.

<B> celda que bloquea o no el paso del jugador u otros monstruos o NPCs. 0 es no bloquea, y 1 es que sí bloquea.

<MM> tipo de material, o lo que es lo mismo, imagen que se colocará en el mapa. Viene definida por números de dos dígitos, desde el 01 hasta el 99. Cuando el juego empieza, carga la imagen con ese índice. El valor 0000 no está permitido, ya que se usará para informar al juego de que esa es una celda vacía.

Por lo tanto: 0002 es que es una celda que no bloquea, no realiza ninguna acción y usará la imagen número 02
Otro caso: 5014 es una celda que tampoco bloquea, y que usará la imagen 14, y cuando el jugador entre en ella, se realizará la acción 5
Otro más: 0101 es una celda que bloquea y usará la imagen numero 01.


Parte 3:
Es la lista de acciones.
Cuando el mapa se inicia se carga toda la lista. Cada vez que el jugador entre en una celda que contenga un indicador de acción, entonces se buscará entre la lista de acciones la que se indica en la celda, y se ejecutará. Es decir, si la celda del mapa tiene la acción 0, entonces no se hace nada, pero si es mayor que 0, pues se buscará en la lista de acciones esa acción y se ejecutará.
Tiene el siguiente formato:

<A><P>

En donde:

<A> es el número de acción a ejecutar
<P> el parámetro que se puede usar si fuese necesario.

Por ejemplo: 1,2 llamará a la acción 1, con el parámetro 2
3,0 llamará a la acción 3 con el parámetro 0. En este caso, el 0 siempre hay que ponerlo para indicar que no se necesita parámetro.



Parte 4:

Es la lista de objetos, NPCs y demás.
Tiene el siguiente formato:

<N><X><Y>

En donde:

<N> es el número de objeto a colocar
<X> e <Y> son las coordenadas del mapa en el que se colocará.

En este caso, los NPCs se almacenan en una matriz a parte, no en la del mapa.
Para comprobar colisiones entre jugador y objetos, cada vez que el jugador quiera moverse a una nueva casilla, se comprueva si en dicha casilla de la matriz de objetos hay algo o no.






Eso es básicamente la definición del mapa y todos sus elementos.
Me interesa mucho sus opiniones para que así pueda mejorarlo.



Otra cosa que quería preguntar:
me he creado un editor de mapas sencillo para crear los mapas.
Cuando creo un mapa en blanco, en realidad está relleno de celdas con valor "0000".
El problema que tengo es que cuando quiero crear el mapa del mundo, que será unos 300x300, tengo que crear una matriz de esas mismas dimensiones, y rellenarlas de "0000"
El problema reside en que usando bucles for..next tarda demasiado, y cuando digo demasiado, hablo de varios minutos.
Y la pregunta es: ¿hay alguna manera para evitar esto, quizás algún otro método que no sean bucles for...next?

Gracias por las respuestas.

Hechelion

Se ve bien el formato, lo importante es que cumpla lo que necesitas para el juego, algunas cosas a considerar a mi juicio serán las siguientes.

El formato no tiene mucha versatilidad u opción de escalar, por  lo cual, antes de implementarlo tienes que estar seguro que cumplirá con todas las expectativas de diseño del juego, me explico. Sólo tienes un carácter para acciones, si sólo usas números, significa que cada mapa sólo podrá tener 9 acciones diferentes y si en medio del juego quieres agregar una décima acción tendrás que cambiar toda lo referente al mapa.

El mapa solo te soporta 99 tile diferentes y no tiene multicapa, o sea, no puedes hacer combinaciones. Si me hablas de un mapa de 300*300 tienes 90.000 tiles y solo 99 "texturas" para llenarlos. ¿Estás seguro de esas cantidades?.

Básicamente lo que tienes que pensar, es si las limitaciones del mapa se adaptan o no a lo que quieres del juego, el formato que presentas es simple, robusto pero no se ve escalable ni versátil y si a mitad del juego se te ocurre que 99 tiles son pocos y quieres meter 101 te vas a dar cuenta que es imposible sin modificar todo lo que ya llevas hecho.

Personalmente, creo que sería mejor dotar al mapa de algunas capas, normalmente unas 3 capas de dibujo, una para colisión y otra para la lógica. Así por ejemplo, puedes tener la primera capa como suelo y  en la segunda capa colocar un árbol, de está forma el  árbol es independiente de la primera capa y puedes tener un árbol en un suelo de pasto, un suelo de gravilla, un suelo de colina, etc.

La capa de colisión puede ser un mapa de dureza y al tener una capa independiente para las acciones no tienes limitación al número, la otra ventaja es que cada tile en cada mapa sería un número en vez de un string con lo cual no necesitas hacer nada para que inicien en 0.

Ejemplos de tilemap con la librería tienes un par:
http://www.stratos-ad.com/forums/index.php?topic=12892.0
http://www.stratos-ad.com/forums/index.php?topic=12760.0

Lo del bucle me llama mucho la atención, 90.000 acciones (dos bucle de 300) no debería consumirte tanto tiempo, salvo que tengas una máquina muuuuuuuuuuuuuuuuuy antigua o que la tengas muy saturada, tal vez tengas algo más metido en el bucle que te consume recursos pero algo así

For i = 0 to 299
     For e = 0 to 299
          Mapa(i,e) = "0000"
     Next
Next

No debería tomarte más de un par de segundos. (yo tengo un Turion II de 2.4 Ghz y ese bucle le toma menos de un segundo)


El_Chavo

Antes de nada, gracias por tu respuesta.


Hay un par de cosas que comentas:

Cita de: Hechelion en 11 de Marzo de 2011, 06:35:18 PM
Se ve bien el formato, lo importante es que cumpla lo que necesitas para el juego, algunas cosas a considerar a mi juicio serán las siguientes.

El formato no tiene mucha versatilidad u opción de escalar, por  lo cual, antes de implementarlo tienes que estar seguro que cumplirá con todas las expectativas de diseño del juego, me explico. Sólo tienes un carácter para acciones, si sólo usas números, significa que cada mapa sólo podrá tener 9 acciones diferentes y si en medio del juego quieres agregar una décima acción tendrás que cambiar toda lo referente al mapa.

Es que en realidad, no creo que hagan falta las nueve acciones. Aún no he llegado a ese punto, pero acciones como "ir", "hablar", "usar", "teletransportar" y quizás alguna más podrían ser más que suficientes.
No sé si te habrás equivocado, pero son acciones independientes del jugador, es decir, no son acciones que el jugador pueda realizar cuando quiera pulsando una tecla, si no cuando entra en la casilla en cuestión.

Cita de: Hechelion en 11 de Marzo de 2011, 06:35:18 PM
El mapa solo te soporta 99 tile diferentes y no tiene multicapa, o sea, no puedes hacer combinaciones. Si me hablas de un mapa de 300*300 tienes 90.000 tiles y solo 99 "texturas" para llenarlos. ¿Estás seguro de esas cantidades?.

Básicamente lo que tienes que pensar, es si las limitaciones del mapa se adaptan o no a lo que quieres del juego, el formato que presentas es simple, robusto pero no se ve escalable ni versátil y si a mitad del juego se te ocurre que 99 tiles son pocos y quieres meter 101 te vas a dar cuenta que es imposible sin modificar todo lo que ya llevas hecho.

Igual que antes, 99 tipos diferentes de texturas creo que incluso sobrarían.
El mapa del juego es decir, el del mundo, no el de las ciudades o mazmorras, será algo así:

(es una imagen muy grande)
http://www.adamantyr.com/crpg/images/WorldMap.png

En ese mapa no hay ni 20 tipos diferentes de texturas.

también están las texturas de ciudades, mazmorras y demás, para las cuales voy a tener otras tantas texturas, o quizás menos.
99 creo que sobrará.
Pero en caso de que necesite más sólo tengo que aumentar de dos a tres dígitos y ya tendría 999 como máximo.


Cita de: Hechelion en 11 de Marzo de 2011, 06:35:18 PM
Personalmente, creo que sería mejor dotar al mapa de algunas capas, normalmente unas 3 capas de dibujo, una para colisión y otra para la lógica. Así por ejemplo, puedes tener la primera capa como suelo y  en la segunda capa colocar un árbol, de está forma el  árbol es independiente de la primera capa y puedes tener un árbol en un suelo de pasto, un suelo de gravilla, un suelo de colina, etc.

La capa de colisión puede ser un mapa de dureza y al tener una capa independiente para las acciones no tienes limitación al número, la otra ventaja es que cada tile en cada mapa sería un número en vez de un string con lo cual no necesitas hacer nada para que inicien en 0.

Ejemplos de tilemap con la librería tienes un par:
http://www.stratos-ad.com/forums/index.php?topic=12892.0
http://www.stratos-ad.com/forums/index.php?topic=12760.0

¿Qué es un mapa de durezas?
Deduzco que es algo así como una matriz que contenga en posiciones absolutas los objetos del mapa.
Pero ¿para qué crear otra matriz (si es que es eso) si ya tengo la información de todos los objetos en el propio archivo del mapa? Sólo tendría que comprobar si la casilla a la que se quiere mover el jugador contiene algún objeto, es decir, el "bit" de objeto es diferente de cero.


El juego se verá como esto mas o menos:


O quizás como esto:


Realmente no son necesarias las capas, ya que primero se pinta el mapa, luego los objetos y jugador, y finalmente el LOS.
Como ya sé en qué casillas está cada objeto por que tengo sus coordenadas en el fichero de definición del mapa, sólo tendría que dibujar los objetos en sus coordenadas apropiadas.


Cita de: Hechelion en 11 de Marzo de 2011, 06:35:18 PM
Lo del bucle me llama mucho la atención, 90.000 acciones (dos bucle de 300) no debería consumirte tanto tiempo, salvo que tengas una máquina muuuuuuuuuuuuuuuuuy antigua o que la tengas muy saturada, tal vez tengas algo más metido en el bucle que te consume recursos pero algo así

For i = 0 to 299
     For e = 0 to 299
          Mapa(i,e) = "0000"
     Next
Next

No debería tomarte más de un par de segundos. (yo tengo un Turion II de 2.4 Ghz y ese bucle le toma menos de un segundo)



No es muy antiguo, del 2004. Un AMD no_me_acuerdo_qué a 2.5 Ghz. No me acuerdo ahora mismo del modelo, pero es algo por el estilo.

En realidad el código sería como el siguiente:



Type CellType
    action as string
    isBlocking as string
    material as string
End Type

dim mapa() as CellType
dim mapHeight as integer, mapWidth as integer

mapHeight = 300
mapWidth = 300

Redim mapa(mapWidth, mapHeight) as CellType

for i = 0 to mapHeight - 1
    for j = 0 to mapWidth -1
        mapa(j,i).action = "0"
        mapa(j,i).isBlocking = "0"
        mapa(j,i).material = "00"
    next j
next i


Quizás tenga alguna cosa que se me ha pasado por alto. Le daré un vistazo.

Gracias por la ayuda.

Un saludo.
Larga vida y prosperidad. ;-)

Hechelion

#3
Ojo, no digo que el diseño sea malo, al contrario, es simple y robusto. Lo que decía y por experiencia es que te puede quedar corto (que a mi ya me ha pasado varias veces, por eso programo pesando en que sea lo más versátil posible) si para tu diseño estás seguro que con 9 acciones y 99 tiles estás bien, pues entonces adelante, no le veo problemas al formato.

Sobre modificar un dígito, no es tan simple (o por lo menos, lo que yo entiendo), asumiendo que creas el mapa y tienes la información:

0195,01294,0195,0040,0040 etc

Y quedaste corto y se te ocurre agregar un dígito más, tendrías que transformar todo el mapa:
un 0195 deberá pasar 01095, si no, el 1 de la colisión pasará a ser parte de la información del tile, con eso me refiero a que es poco escalable, porque si tienes creado un mapa y se te ocurre cambiar algo debes editar todos los mapas anteriores (hablamos de editar la estructura del mapa a mitad de camino)

La ventaja de tener múltiples capas es que puedes modificar una sin tocar el resto, por eso es un diseño más versátil y escalable, pero no lo tomes como una obligación, es un sugerencia.

A veces me ha pasado que dices, una acción "hablar basta y sobra" y de pronto te das cuenta que para que el NPC haga lo que realmente quieres que haga pues tienes que pasar otro parámetro y vamos agregando otra opción y así va sumando, por eso, personalmente creo que es mejor que la programación sea versatil y fácilmente escalable, pero nuevamente, es una opinión y no significa que sea lo mejor para todos, en especial si ya tienes bien definido tu juego.

Por cierto, los 2 ejemplos que te pasé están con sus códigos fuentes por si quieres darles una mirada.

Edit:
Acabo de probar el código que colocaste abajo y a mi me equipo le toma menos de un segundo completarlo. Algo más debes tener que está causando la demora.

El_Chavo

Cita de: Hechelion en 11 de Marzo de 2011, 10:20:30 PM
Ojo, no digo que el diseño sea malo, al contrario, es simple y robusto. Lo que decía y por experiencia es que te puede quedar corto (que a mi ya me ha pasado varias veces, por eso programo pesando en que sea lo más versátil posible) si para tu diseño estás seguro que con 9 acciones y 99 tiles estás bien, pues entonces adelante, no le veo problemas al formato.

Sobre modificar un dígito, no es tan simple (o por lo menos, lo que yo entiendo), asumiendo que creas el mapa y tienes la información:

0195,01294,0195,0040,0040 etc

Y quedaste corto y se te ocurre agregar un dígito más, tendrías que transformar todo el mapa:
un 0195 deberá pasar 01095, si no, el 1 de la colisión pasará a ser parte de la información del tile, con eso me refiero a que es poco escalable, porque si tienes creado un mapa y se te ocurre cambiar algo debes editar todos los mapas anteriores (hablamos de editar la estructura del mapa a mitad de camino)

Añadiré un dígito más ahora, pudiento tener todavía más posibilidades, aunque según lo que he estimado, no creo que vaya a utilizarlos.

Cita de: Hechelion en 11 de Marzo de 2011, 10:20:30 PM
A veces me ha pasado que dices, una acción "hablar basta y sobra" y de pronto te das cuenta que para que el NPC haga lo que realmente quieres que haga pues tienes que pasar otro parámetro y vamos agregando otra opción y así va sumando, por eso, personalmente creo que es mejor que la programación sea versatil y fácilmente escalable, pero nuevamente, es una opinión y no significa que sea lo mejor para todos, en especial si ya tienes bien definido tu juego.

No has entendido bien. Las acciones no son de los NPCs si no de las casillas o celdas.
Para hablar con los NPCs y en general para interactuar con cualquier objeto, ya es otra historia.
Las acciones son sucesos que ocurren cuando el jugador entra en esa casilla, y no tiene nada que ver con los NPCs.
Por ejemplo, sería teletransportar a otro mapa, mostrar un texto, etc

Cita de: Hechelion en 11 de Marzo de 2011, 10:20:30 PM
Edit:
Acabo de probar el código que colocaste abajo y a mi me equipo le toma menos de un segundo completarlo. Algo más debes tener que está causando la demora.

Gracias, pero ya he encontrado el problema.
No estaba exactamente en ese bucle, si no en otro que se ejecuta a continuación de ese.
El código es el siguiente:


Type CellType
    action as string
    isBlocking as string
    material as string
End Type

Dim map() as CellType
Dim mapWidth as integer, mapHeight as integer
Dim asciiMap as string

mapWidth = 300
mapHeight = 300

asciiMap = ""

Redim map(mapWidth, mapHeight) as CellType

for i = 0 to mapHeight -1
    for j = 0 to mapWidth - 1
        'Concateno en asciiMap la información de cada celda del mapa.
        asciiMap = asciiMap & map(j,i).action & map(j,i).isBlocking & map(j,i).material
        'En este if...endif compruebo si no es el último elemento, y le añado una coma.
        if j < mapwidth - 1 then asciiMap = asciiMap & ","
    next j
    'al finalizar con cada fila, añado un retorno de carro.
    asciiMap = asciiMap & vbcrlf
next i



Pues ese código aparentemente tan sencillo, ralentiza eternamente al programa.
He realizado varias pruebas, y la conclusión es que las operaciones de concatenación de caracteres son tremendamente lentas.

En principio podría omitir esto, ya que en realidad esta función sirve para mostrar en un textbox una previsualización del fichero del mapa.
De todas maneras, me gustaría, a ser posible, si alguien me pudiese ofrecer alguna solución para esto.

Ahora bién, para escribir en el fichero el mapa también voy a tener que concatenar así que aunque elimine la previsualización ascii, me voy a encontrar el mismo problema.

¿Alguna solución para esto?

Un saludo y gracias.

[EX3]

Me extraña que el salto de linea sea la causa de que te vaya mas lento el bucle cuando en mi trabajo generamos y trabajamos con archivos de texto mucho mas grandes que lo que estas generando con ese codigo y tardamos escasos segundos. Algun otro proceso en tu codigo que estas ejecutando tiene que ser la causa pero agregar un salto de linea desde luego no.

Por otro lado, si estas usando un editor para generar los mapas significa que no te hace falta tener un formato "legible" a la vista (para que si usas el editor). Podrias entonces hacer algo tan sencillo como volcar toda la estructura que contiene el nivel y sus elementos en un archivo en formato binario y despues volver a cargarlo tal cual en memoria. No tengo Visual Basic 6.0 instalado en mi maquina pero el codigo deberia ser algo asi:

Para guardar los datos:
Dim map() as CellType ' Tu array para el mapa.
' Llenas los datos en la estructura...

Dim file As Integer: file = FreeFile()
Open "archivo.dat" For Binary Access Read As #file
    Put #file, , map ' Volcamos todo el contenido del array al archivo.
Close #file


Para cargar los datos:
Dim map() as CellType ' Tu array para el mapa.

Dim file As Integer: file = FreeFile()
Open "archivo.dat" For Binary Access Read As #file
    Get #file, , map ' Leemos y volcamos todo el contenido del archivo al array.
Close #file

Años atras tenia algo similar a lo que estas haciendo tu pero con estructuras de multiples niveles de informacion (como un arbol de nodos) y recurria a este sistema para almacenarlo de forma rapida y precisa (es como un volcado directo de memoria a disco) y lo mas parecido a serializacion binaria que existe en Visual Basic 6.0.

Salu2...
José Miguel Sánchez Fernández
.NET Developer | Game Programmer | Unity Developer

Blog | Game Portfolio | LinkedIn | Twitter | Itch.io | Gamejolt

El_Chavo

Cita de: [EX3] en 12 de Marzo de 2011, 09:42:02 PM
Me extraña que el salto de linea sea la causa de que te vaya mas lento el bucle cuando en mi trabajo generamos y trabajamos con archivos de texto mucho mas grandes que lo que estas generando con ese codigo y tardamos escasos segundos. Algun otro proceso en tu codigo que estas ejecutando tiene que ser la causa pero agregar un salto de linea desde luego no.

No es el salto de línea la causa, si no la concatenación de caracteres.

He googleado un poco acerca del tema (concretamente he buscado visual basic slow concatenation) y parece ser que efectivamente VB es muy lento a la hora de realizar operaciones de concatenación.

Cita de: [EX3] en 12 de Marzo de 2011, 09:42:02 PM
Por otro lado, si estas usando un editor para generar los mapas significa que no te hace falta tener un formato "legible" a la vista (para que si usas el editor). Podrias entonces hacer algo tan sencillo como volcar toda la estructura que contiene el nivel y sus elementos en un archivo en formato binario y despues volver a cargarlo tal cual en memoria. No tengo Visual Basic 6.0 instalado en mi maquina pero el codigo deberia ser algo asi:

Para guardar los datos:
Dim map() as CellType ' Tu array para el mapa.
' Llenas los datos en la estructura...

Dim file As Integer: file = FreeFile()
Open "archivo.dat" For Binary Access Read As #file
    Put #file, , map ' Volcamos todo el contenido del array al archivo.
Close #file


Para cargar los datos:
Dim map() as CellType ' Tu array para el mapa.

Dim file As Integer: file = FreeFile()
Open "archivo.dat" For Binary Access Read As #file
    Get #file, , map ' Leemos y volcamos todo el contenido del archivo al array.
Close #file

Años atras tenia algo similar a lo que estas haciendo tu pero con estructuras de multiples niveles de informacion (como un arbol de nodos) y recurria a este sistema para almacenarlo de forma rapida y precisa (es como un volcado directo de memoria a disco) y lo mas parecido a serializacion binaria que existe en Visual Basic 6.0.

Salu2...

Ya sé como guardar datos en forma binaria.

De hecho he provado de esa manera y ya funciona bién. Gracias por refrescarme la memoria. :-)

Lo único malo de usar un formato binario es que no es posible editar el mapa con un editor de texto plano.
Por ejemplo, si quiero solamente cambiar un 1 por un 2 en una celda en concreto, voy a tener que abrir el mapa con el editor a narices, en vez de simplemente abrirlo con el notepad y editarlo rápidamente. Vamos, que es un pequeño inconveniente que tampoco es muy importante.



Y ya para acabar, una última duda:

como bién habéis leído, el mapa del mundo tendrá una dimensión de celdas de 300x300 o más.
La cosa es que en el editor, para dibujar el mapa, uso un picturebox, el cual puedes desplazar usando dos scrollbars, una vertical y otra horizontal.
Cada vez que muevo las scrollbars, el picturebox se actualiza mostrando la parte correspondiente del mapa.
Sin embargo no me acuerdo cuál es el algoritmo para que se muestre solamente el trozo de mapa que tiene que ser, en vez de que carge todo el mapa entero.


Durante el juego para mostrar las casillas que están a 10 celdas de distancia del jugador, solo tengo que sumar y restar desde la posición del jugador, pero en el editor la cosa es distinta.

No se si me he explicado bién lo que quiero decir.

un saludo y gracias.

[EX3]

#7
Cita de: El_Chavo en 13 de Marzo de 2011, 04:53:48 PM
No es el salto de línea la causa, si no la concatenación de caracteres.

He googleado un poco acerca del tema (concretamente he buscado visual basic slow concatenation) y parece ser que efectivamente VB es muy lento a la hora de realizar operaciones de concatenación.
Si lento es, mas que en otros lenguajes, pero lento en la mayoria de las funciones que se pueden realizar en este lenguaje xD No se, ya te digo que yo en el trabajo no sufrimos en los programas de Visual Basic 6.0 y macros de VBA problemas de ralentizacion tan severos por la concatenacion de caracteres en cadenas (componiendo archivos linea a linea y cada linea a base de distintas variables) y nuestros archivos son a veces de mas de un megabyte en algun caso (obviamente tardamos mil veces menos en Visual Basic .NET con el objeto StringBuilder pero no hay nada similar en Visual Basic 6.0 :P)

Cita de: El_Chavo en 13 de Marzo de 2011, 04:53:48 PM
Lo único malo de usar un formato binario es que no es posible editar el mapa con un editor de texto plano.
Por ejemplo, si quiero solamente cambiar un 1 por un 2 en una celda en concreto, voy a tener que abrir el mapa con el editor a narices, en vez de simplemente abrirlo con el notepad y editarlo rápidamente. Vamos, que es un pequeño inconveniente que tampoco es muy importante.
Ya, pero no se, teniendo el editor no veo "util" ni productivo tener que andar buscando donde se encuentra la informacion exacta de la celda que quieres modificar. No se tu, teniendo ya el editor que te es mas facil para cambiar 4 celdas, usar el editor que ves visualmente lo que quieres editar o ponerte a buscarlo en el bloc de notas? ;) Pero igualmente estoy deacuerdo en lo de tenerlo legible. Yo en .NET por ejemplo usamos XML para tenerlo legible por si necesitamos meterle mano rapidamente (aunque es mas facil navegar y editar un arbol de nodos en XML que una estructura en texto plano).

Cita de: El_Chavo en 13 de Marzo de 2011, 04:53:48 PM
Y ya para acabar, una última duda:

como bién habéis leído, el mapa del mundo tendrá una dimensión de celdas de 300x300 o más.
La cosa es que en el editor, para dibujar el mapa, uso un picturebox, el cual puedes desplazar usando dos scrollbars, una vertical y otra horizontal.
Cada vez que muevo las scrollbars, el picturebox se actualiza mostrando la parte correspondiente del mapa.
Sin embargo no me acuerdo cuál es el algoritmo para que se muestre solamente el trozo de mapa que tiene que ser, en vez de que carge todo el mapa entero.


Durante el juego para mostrar las casillas que están a 10 celdas de distancia del jugador, solo tengo que sumar y restar desde la posición del jugador, pero en el editor la cosa es distinta.
Pues deberia ser exactamente el mismo codigo que uses en el juego para mostrar la region visible del mapa la que uses en el editor. En tu caso, que trabajas con un array unidimensional, podrias trabajar cada tile como una unidad (para trabajar como si fuera un mapa de bits), por lo que si quisieras visualizar los tiles comprendidos desde el tile de la posicion x33 y44 y una region de 16 tiles de alto por 32 de ancho tu lectura, en un array unidimensional, seria algo asi:
Const mapWidth As Long = 256 ' Ancho del mapa completo.
Const mapHeight As Long = 256 ' Alto del mapa completo.

Dim map() As CellType ' Celdas del mapa.

' Posicion a leer:
Dim x As Long: x = 33
Dim y As Long: y = 44

' Dimensiones del area a leer:
Dim width As Long: width = 32
Dim height As Long: height = 16

Dim firstTile As Long: firstTile = x + (width * y) ' Primer tile a leer.
Dim lastTile As Long: lastTile = firstTile + (width * height) ' Ultimo tile a leer.

' Leemos solo los tiles dentro del area definida desde la posicion indicada:
Dim i As Long
For i = firstTile To lastTile
    drawTile(map(i))
Next


Este codigo te deberia servir igual para el editor que para la representación en pantalla en el juego. Lo unico sera, que imagino que es a lo que te referias, es que en el juego, la posicion de lectura la tendras que derivar de la posicion del jugador, que supongo estara centrado en la vista del mapa, sencillamente tendrias que restar a la posicion de lectura del mapa la mitad de la anchura y altura del area a leer:
' Posicion a leer en referencia al jugador:
Dim x As Long: x = jugador.x - (mapWidth / 2)
Dim y As Long: y = jugador.y - (mapHeight / 2)

Con esto deberia quedar tu jugador centrado siempre en la vista del mapa.

Salu2...
José Miguel Sánchez Fernández
.NET Developer | Game Programmer | Unity Developer

Blog | Game Portfolio | LinkedIn | Twitter | Itch.io | Gamejolt

El_Chavo

Cita de: [EX3] en 13 de Marzo de 2011, 09:30:45 PM
Cita de: El_Chavo en 13 de Marzo de 2011, 04:53:48 PM
No es el salto de línea la causa, si no la concatenación de caracteres.

He googleado un poco acerca del tema (concretamente he buscado visual basic slow concatenation) y parece ser que efectivamente VB es muy lento a la hora de realizar operaciones de concatenación.
Si lento es, mas que en otros lenguajes, pero lento en la mayoria de las funciones que se pueden realizar en este lenguaje xD No se, ya te digo que yo en el trabajo no sufrimos en los programas de Visual Basic 6.0 y macros de VBA problemas de ralentizacion tan severos por la concatenacion de caracteres en cadenas (componiendo archivos linea a linea y cada linea a base de distintas variables) y nuestros archivos son a veces de mas de un megabyte en algun caso (obviamente tardamos mil veces menos en Visual Basic .NET con el objeto StringBuilder pero no hay nada similar en Visual Basic 6.0 :P)

He visto por algún lado, no sé si en vbextreme o bigresource o en algún sitio parecido, alguien que escribió una función para vb6 que mejoraba considerablemente la concatenación de caracteres.
Según he visto, lo que hacía era crear espacios en memoria para concatenar las cadenas, pero lo que hacía era mover bits de unas direcciones de memoria a otras, y por lo que parece en VB es más rápido eso que hacer "a" & "b". :-P

Cita de: [EX3] en 13 de Marzo de 2011, 09:30:45 PM
Cita de: El_Chavo en 13 de Marzo de 2011, 04:53:48 PM
Lo único malo de usar un formato binario es que no es posible editar el mapa con un editor de texto plano.
Por ejemplo, si quiero solamente cambiar un 1 por un 2 en una celda en concreto, voy a tener que abrir el mapa con el editor a narices, en vez de simplemente abrirlo con el notepad y editarlo rápidamente. Vamos, que es un pequeño inconveniente que tampoco es muy importante.
Ya, pero no se, teniendo el editor no veo "util" ni productivo tener que andar buscando donde se encuentra la informacion exacta de la celda que quieres modificar. No se tu, teniendo ya el editor que te es mas facil para cambiar 4 celdas, usar el editor que ves visualmente lo que quieres editar o ponerte a buscarlo en el bloc de notas? ;) Pero igualmente estoy deacuerdo en lo de tenerlo legible. Yo en .NET por ejemplo usamos XML para tenerlo legible por si necesitamos meterle mano rapidamente (aunque es mas facil navegar y editar un arbol de nodos en XML que una estructura en texto plano).

Pero si lo que quiero es cambiar todas las celdas que sean "1073" por "0141", con el bloc de notas lo tengo fácil.
También podría currarme una sencilla función para hacer esto, pero es que tampoco me quiero centrar mucho en el editor ya que el juego apenas lo tengo empezado. Con las funciones básicas de mapeo me sería suficiente. Más adelante cuando el juego esté terminado pues ya sí que podría meterle mano al editor para hacerlo más potente.

Cita de: [EX3] en 13 de Marzo de 2011, 09:30:45 PM
Pues deberia ser exactamente el mismo codigo que uses en el juego para mostrar la region visible del mapa la que uses en el editor. En tu caso, que trabajas con un array unidimensional, podrias trabajar cada tile como una unidad (para trabajar como si fuera un mapa de bits), por lo que si quisieras visualizar los tiles comprendidos desde el tile de la posicion x33 y44 y una region de 16 tiles de alto por 32 de ancho tu lectura, en un array unidimensional, seria algo asi:
Const mapWidth As Long = 256 ' Ancho del mapa completo.
Const mapHeight As Long = 256 ' Alto del mapa completo.

Dim map() As CellType ' Celdas del mapa.

' Posicion a leer:
Dim x As Long: x = 33
Dim y As Long: y = 44

' Dimensiones del area a leer:
Dim width As Long: width = 32
Dim height As Long: height = 16

Dim firstTile As Long: firstTile = x + (width * y) ' Primer tile a leer.
Dim lastTile As Long: lastTile = firstTile + (width * height) ' Ultimo tile a leer.

' Leemos solo los tiles dentro del area definida desde la posicion indicada:
Dim i As Long
For i = firstTile To lastTile
    drawTile(map(i))
Next


Este codigo te deberia servir igual para el editor que para la representación en pantalla en el juego. Lo unico sera, que imagino que es a lo que te referias, es que en el juego, la posicion de lectura la tendras que derivar de la posicion del jugador, que supongo estara centrado en la vista del mapa, sencillamente tendrias que restar a la posicion de lectura del mapa la mitad de la anchura y altura del area a leer:
' Posicion a leer en referencia al jugador:
Dim x As Long: x = jugador.x - (mapWidth / 2)
Dim y As Long: y = jugador.y - (mapHeight / 2)

Con esto deberia quedar tu jugador centrado siempre en la vista del mapa.

Salu2...

No, pero es que del editor al juego hay una gran diferencia en cuanto a la presentación del juego.
En el juego, el jugador no está siempre centrado en el medio de la visualización, si no que se mueve libremente y cuando alcanza el borde de la ventana de juego, se desplaza el mapa para mostrar la parte correspondiente.
Es decir, la vista del mapa sólo se desplaza cuando el jugador alcanza el borde.

Sin embargo en el editor, también quiero que se muestre una parte del mapa correspondiente a unas dimensiones de 44x44 celdas.
El problema es que no tengo un punto de referencia como en el caso del juego, que sería la posición actual del jugador, si no que tengo dos scrollbars, horizontal y vertical.
Ambas scrollbars tienen un valor máximo de mapWidth/8 y mapHeight/8 y uno mínimo de cero.
El movimiento de las scrollbars desplaza la visualización del mapa una sola casilla o celda, y haciendo AvPag o RePag, mueve 8 casillas en vez de una.
Yo había hecho algo así:


for i = 0 + HorizontalScrollbar*8 to 44 + horizontalScrollbar*8
    'y ahora para columnas
   if i > 44 then i = 44 'no quiero que se dibujen más de 44 casillas horizontales
    for j = 0 + verticalscrollbar*8 to 44 + horizontalscrollbar*8
        if j > 44 then j = 44 'lo mismo que con i
        'código para dibujar casillas en la posición (i , j)
    next j
next i


Pero no funciona como desearía....

[EX3]

#9
Cita de: El_Chavo en 13 de Marzo de 2011, 09:57:19 PM
He visto por algún lado, no sé si en vbextreme o bigresource o en algún sitio parecido, alguien que escribió una función para vb6 que mejoraba considerablemente la concatenación de caracteres.
Según he visto, lo que hacía era crear espacios en memoria para concatenar las cadenas, pero lo que hacía era mover bits de unas direcciones de memoria a otras, y por lo que parece en VB es más rápido eso que hacer "a" & "b". :-P
Si, tengo varios ejemplos por ahi que mediante llamadas a la API de Windows a las funciones de manejo de memoria realizan lo que dices. Eso seria un camino para hacerte una clase tipo StringBuilder de .NET que seguramente por debajo funcione de forma similar.

Cita de: El_Chavo en 13 de Marzo de 2011, 09:57:19 PMPero si lo que quiero es cambiar todas las celdas que sean "1073" por "0141", con el bloc de notas lo tengo fácil.
Touche! En este caso te doy la razon :)

Cita de: El_Chavo en 13 de Marzo de 2011, 09:57:19 PM
No, pero es que del editor al juego hay una gran diferencia en cuanto a la presentación del juego.
En el juego, el jugador no está siempre centrado en el medio de la visualización, si no que se mueve libremente y cuando alcanza el borde de la ventana de juego, se desplaza el mapa para mostrar la parte correspondiente.
Es decir, la vista del mapa sólo se desplaza cuando el jugador alcanza el borde.
El codigo que te he puesto arriba te sirve tal cual para lo mismo, tu solo tienes que pasarle la posicion de la primera celda a leer y el ancho y alto, el como desplaces la x e y es ajeno al codigo de visualizacion, me refiero, yo te he puesto el ejemplo de tomar al jugador como posicion de referencia pero no quita que tu programes el desplazamiento de esa coordenada por otras vias, como el caso de ahora de que el jugador toque el borde. Seria simplemente sumar o restar a x el ancho o a y el alto segun el borde que haya tocado, pero esto no afecta ni te obliga a modificar mi codigo de antes :)

Cita de: El_Chavo en 13 de Marzo de 2011, 09:57:19 PMSin embargo en el editor, también quiero que se muestre una parte del mapa correspondiente a unas dimensiones de 44x44 celdas.
El problema es que no tengo un punto de referencia como en el caso del juego, que sería la posición actual del jugador, si no que tengo dos scrollbars, horizontal y vertical.
Entonces si tienes un punto de referencia, tus dos barras de desplazamiento. Una te dara la x y la otra la y del tile de referencia.

Echa un ojo al codigo que te puse por que te puede simplificar mucho el asunto de navegar el mapa para leer lo que necesites, que como te decia, lo puedes adaptar igualmente al editor y que el codigo no es muy complejo y es muy optimizado para trabajar con un mapa muy grande ya que apenas realizas calculos (la obtencion del primer y ultimo tile, dos operaciones sencillas) y que solo recorres los elementos que vas a leer del array sin tener que recorrerlo entero buscando los tiles mediante comparaciones ni nada.

Salu2...
José Miguel Sánchez Fernández
.NET Developer | Game Programmer | Unity Developer

Blog | Game Portfolio | LinkedIn | Twitter | Itch.io | Gamejolt

El_Chavo

Y he solucionado el problema, aunque con otro algoritmo similar, no esxactamente con el código que has puesto arriba.

Gracias por la ayuda.

Mars Attacks

Siempre puedes exportar en texto plano y añadir a tu workflow un paso intermedio de conversión de texto plano a binario (e incluso exportar inicialmente a ambos pero mantener un programa conversor en el registro de windows para usarlo con el botón derecho sobre la extensión que elijas, o alguna cosa por el estilo). Así te quedas con lo mejor de los dos mundos :)

Depende de la intensidad de uso que le des, te puede salir a cuento invertir el tiempo en esta herramienta.

El_Chavo

Cita de: Mars Attacks en 15 de Marzo de 2011, 11:03:13 PM
Siempre puedes exportar en texto plano y añadir a tu workflow un paso intermedio de conversión de texto plano a binario (e incluso exportar inicialmente a ambos pero mantener un programa conversor en el registro de windows para usarlo con el botón derecho sobre la extensión que elijas, o alguna cosa por el estilo). Así te quedas con lo mejor de los dos mundos :)

Depende de la intensidad de uso que le des, te puede salir a cuento invertir el tiempo en esta herramienta.

El problema reside en la lentitud de Visual Basic a la hora de concatenar cadenas alfanuméricas.
Con matrices de pocos elementos (menos de 100), apenas se nota mucho, pero con 200 o más elementos, ya sí se nota.
De todas manera, implementaré un sistema para buscar y reemplazar dentro del propio editor, cosa que no veo para nada difícil, aunque lo dejaré para el final ya que es algo accesorio.

Gracias por tu feedback :-)

[EX3]

Cita de: El_Chavo en 16 de Marzo de 2011, 04:54:06 PM
El problema reside en la lentitud de Visual Basic a la hora de concatenar cadenas alfanuméricas.
Con matrices de pocos elementos (menos de 100), apenas se nota mucho, pero con 200 o más elementos, ya sí se nota.
Lo de tu problema con las cadenas me sigue sonando mas que raro cuando nosotros aqui en el banco donde trabajo generamos autenticos archivos pesados de texto (formatos CSV, valores separados por comas concatenando campos de una consulta SQL y demas) y en maquinas antiguas nos tarda relativamente poco contando que entre consultas SQL, conversiones y formateo de valores no pasamos de 4 segundos en el proceso mas pesado. A parte, en 10 años trabajando con este lenguaje nunca he tenido serios problemas con el tema de las cadenas (si no seguramente hubiera incorporado alguna clase con funciones de manejo de cadenas mediante la API de Windows en dx_lib32 como apoyo).

El siguiente codigo es una prueba que he realizado en una de las maquinas del trabajo, que tendra a lo poco 5 años y no es que sea una maravilla en cuanto a potencia y rendimiento:
Option Explicit

Private Sub Form_Load()
    Debug.Print Time
    Open "archivo.txt" For Output As #1
        Dim i As Long, j As Long
        Dim linea As String
       
        For i = 0 To 1000
            linea = ""
            For j = 0 To 1000
                linea = linea & CStr(Timer * 2 / 0.25) & ";"
            Next j
            linea = linea & vbNewLine
            Print #1, linea
        Next i
    Close #1
    Debug.Print Time
End Sub

Esto genera un archivo de texto de 10MB con 1000 lineas de 1000 valores calculados al vuelo (en total 1000000 campos) y concatenados mas un salto de linea al final (se queda en 2000 lineas ya que la instrucción Print # genera el salto de linea automáticamente). Solo ha tardado 6 segundos en generarse, en un portatil de hace 3 años, que va mejor que el sobremesa de antes, solo 2 segundos. Como veras, para la cantidad de datos que mueves en tus mapas, 200 elementos no es nada (menos de 1MB), no deberias tener los problemas que comentas de lentitud al generar tu archivo, y creeme, concatenar un salto de linea no puede ser la causa de que te vaya tan lento como mencionabas mas arriba (si no en el banco no hubiéramos usado durante años este lenguaje para ciertos procesos críticos de volcado de datos).

En serio, no te obsesiones con el temas de las cadenas por que es una de las pocas cosas que Visual Basic 6.0 permite manejar comodamente y prescindir de ellas es perder potencia y productividad en este lenguaje ya de por si mermado por sus fallas y  escaso rendimiento. Si realmente fueran un serio problema de rendimiento yo hubiera sido el primero en comentártelo y te habria recomendado alguna alternativa mejor, a parte de la serialziacion binaria de antes, para trabajar con cadenas de texto, pero créeme, no es el caso :)

CitarEn principio podría omitir esto, ya que en realidad esta función sirve para mostrar en un textbox una previsualización del fichero del mapa.
De todas maneras, me gustaría, a ser posible, si alguien me pudiese ofrecer alguna solución para esto.
Quizas sea el volcado al TextBox lo que te ralentiza, que eso si puede llegar a ser lento (los controles de Visual Basic si que son lentos trabajando con cadenas y demás elementos), y no la concatenación de cadenas en si. A parte que estarías generando dos bucles para algo que con uno te basta. Si lo que buscas es previsualizar la información podrías simplemente abrir el archivo que generas con la información del mapa en el primer bucle en el bloc de notas mediante un Shell().

Salu2...
José Miguel Sánchez Fernández
.NET Developer | Game Programmer | Unity Developer

Blog | Game Portfolio | LinkedIn | Twitter | Itch.io | Gamejolt

El_Chavo

No tengo ningún proceso en segundo plano que esté ocupado con el procesador, ni ninguna otra operación en el bucle más que la de concatenación.

He hecho el bucle con una simple suma (aux = i +j) y apenas invierte un segundo, por lo que la causa de la lentitud está claro que es por las operaciones de concatenación.

Pero da igual, al final he aplicado tu otra solución (guardar los datos en formato binario) y así me ahorro tener que tirar líneas de código adicionales.

La previsualización tampoco es tan importante. En realidad, incluso prescindible.

De todas maneras, gracias por los consejos.






Stratos es un servicio gratuito, cuyos costes se cubren en parte con la publicidad.
Por favor, desactiva el bloqueador de anuncios en esta web para ayudar a que siga adelante.
Muchísimas gracias.