Cómo hacer saltar los fusibles de tu casa al ejecutar Internet Explorer

Mira que he visto cosas raras en esto "de los ordenadores". Pero lo que me ha pasado hoy supera con creces todo lo que conocía.
Desde hace tiempo pertenezco al club de "el tio que sabe de ordenadores", el familiar al que llama el primo del cuñado del hermano de tu tía, o directamente el "bicho raro" que te instala Windows un sabado por la tarde a cambio de un par de cervezas 😉

Total, llego hoy al curro y después del cafelito de rigor, me dispongo a mirar si tengo algún correo nuevo.
Después de borrar los mensajes para alargar el miembro viril y los de las "pastillitas azules" me encuentro con el e-mail de un colega:

"Oye tronco, tienes que pasarte por casa. No se que koño pasa, pero cada vez que pulso en el icono del internet se va la luz de casa. A ver si puedes llamar o algo xq estoy mosca y no se si he roto algo del ordenador. Un abrazo del loco de la colina"

He de reconocer que tuve que leer el mensaje un par de veces. ¿Qué se va la luz de casa cuando entras en internet? Pero que cojo…

Total, salgo de curro y voy directo a su casa. Después de un rato charlando tomando unas cervezas, le digo. ¿bueno qué, vamos al "lio"?

Le digo a mi colega que haga como si yo no estuviese alli y que repita los pasos. Se sienta delante del ordenador, lo pone en marcha, y después de mas o menos un minuto ya estaba en el escritorio de Windows.

-Oye tronco, que como le de aquí (refiriendóse al icono del Internet Explorer) se va a ir la luz de toda la casa.
-Bueno, tú dale, le dije yo
-Pos fale,…

Total, que hace doble click en el icono del Internet Explorer…y PUM!!!!!! saltan los plomos de casa y nos quedamos a oscuras.

-Ves, te lo dije, si es que los informáticos lo escacharrais todo…

La cara de WTF! que puse fue para enmarcar. Si hay una cosa que me mosquea es no entender el porque de ciertas cosas. Normalmente todo tiene una explicación científica y lógica (bueno, todo menos que Chikilicuatre no ganase Eurovisión, pero eso merece un capitulo aparte 😉

El colega va al cuadro eléctrico y vuelve a conectar los plomos. Enchufamos de nuevo el PC. Carga Windows, y esta vez le atizo yo al icono del Internet Explorer…y ale….a tomar por culo otra vez la luz. Yo flipando y mi colega cagándose en mis muelas por darle sin avisar xDDDD

Desconecto el cable de alimentación del PC, lo vuelvo a conectar y probamos otra vez. Nada, por increible que parezca la luz se volvió a ir.

-¿Has instalado algo nuevo?
-No
-Pues macho, esto es mas raro que un perro verde. Donde coño se ha visto que al pulsar en el intenet explorer se vaya la luz.
-Pues si no lo sabes tú que eres el experto apága y vamonos

A todo esto se me ilumina la neurona que me queda y le pregunto:

-Oye tronco, ¿Cómo tienes los cables así?
-¿Los cables de qué? -me dice mi colega-

Lástima de no tener un móvil o algo para sacar una foto a todo aquel engendro de cables que tenía mi colega debajo de la mesa. Era peor que esta imagen que he encontrado por inet: 

Total, "arreglamos" un poco los cables y le digo a mi colega:

-Unas cañas a que ahora no se va la luz.
-(risas) no sé yo, no sé yo…

Enchufamos el PC. Cargando Windows…-Pues parece que va a llover-,-Con tal de que no llueva este domingo para bajar a Jerez- (mi colega es un motero de los de la "vieja escuela")…

-Venga, va, dale tú. -le digo a mi colega-

Hace click en el icono y sorpresa….la página del Google ante sus narices.

-No sé que coño has hecho pero vamos al bar de abajo a por esas cañas. Invito yo
-(risas) faltaría más.

Por lo menos de algo valieron los 5 años de electrónica industrial de la universidad ;). Cualquier cable "enrollado" al que se le aplica una corriente eléctrica es susceptible en convertirse en una bobina eléctrica que genera inductancias en cualquier circuito eléctrico. Y es precisamente esto lo que estaba ocurriendo. Con tal engendro de cables que tenía allí montado se podía "liar parda", tal y cómo estaba ocurriendo. Total, que de nuevo había una explicación lógica y científica.

Así que si alguna vez algún colega os pide ayuda sobre estos "menesteres", por raro (pero raro de narices) que parezca siempre hay solución, o si no como dicen en /B/ "Hay que amputar!!!!"

 

Saludos.
mov eax,ollydbg; Int 13h 

.NET Tutorial 6. Animación sprites (Parte I)

Pues bien, ha llegado la hora de hacer un resumen de todo lo que hemos ido aprendiendo en los anteriores tutoriales. LLegados a este puento ya deberíamos ser capaces de hacer algo como esto:

Una animación 2D consta básicamente de una serie de fotogramas, que mostrados uno detrás de otro dan la sensación de movimiento.

Existen varias formas de almacenar dichos fotogramas. Nosotros vamos a utilizar lo que en el argot se conoce como sprite sheets. Los sprite sheets son una "mega-imagen" con todos los fotogramas que componen la animación, en nuestro caso usaremos la siguiente imagen:

Alguno/a puede estar pensando que también es posible utilizar una imagen por fotograma. De hecho es perfectamente posible, pero cuando se empiezan a tener muchas secuencias, puede ser un poco "caótico" buscar entre 2000 ficheros, no sé si me entendéis 😉

Pues bien, ¿cómo funcionará nuestro programa?:

  • Cargamos en un "bitmap" la imagen que contiene toda la secuencia/s de la animación 2D
  • "Copiamos" de ese "bitmap" solo la "región" que corresponde al fotograma 1. Esa la región la copiamos en el "bitmap" de un array de sprites, en este caso en el elmento 1 del array
  • "Copiamos" del bitmap solo la región que corresponde al fotograma 2 y lo metemos en el bitmap del elemento 2 del array de sprites
  • Una vez copiados todos los elementos, cargamos un Timer y vamos mostrando todos los bitmaps del array de sprites. Esto dará la sensación de movimiento que podemos apreciar en el vídeo.

Bien, cómo véis, la "única" complicación es como demonios copiar un "trozo" de un "bitmap" y pegarlo en otro "bitmap".

La idea es esta (más vale una imagen que mil palabras):

La imagen grande representa el bitmap de nuestro sprite sheet. Seleccionaremos solo la región oscurecida y dicha región la copiaremos en el bitmap del elemento 1 de nuestro array de sprites.

Para el elemento 2 pues lo mismo:

Supongo que no hace falta que siga con el resto, ya que la mecánica es la misma.

Para realizar esta "copia" necesitaremos 3 elementos:

  • un objeto "bitmap" que contiene el sprite sheet.
  • un array de "bitmaps" que contendrán sólo la "porción" del fotograma en cuestión. Esto serán nuestros sprites.
  • un objeto "Graphics" que nos permitirá copiar  las regiones del sprite sheet a los sprites.

Bien, veamos un poco del código, que como veréis no tiene ningún misterio:

Private spriteIdle1(8) As Bitmap

Esta variable representa a nuestros sprites. Como véis en esta ocasión nuestros sprites únicamente serán "bitmpas", no tienen coordenadas (x, y) ni nada.

Dim bmpSpriteSheet As Bitmap

Esta variable de tipo bitmap contiene nuestro sprite sheet. La cargamos cómo ya hemos visto en los anteriores tutoriales:

bmpSpriteSheet = Image.FromFile(App_Path() & "bitmapsidlepose1_sheet.png")

Pues bien, ahora lo único que tenemos que hacer es un bucle FOR para ir recorriendo el sprite sheet, he ir jugando con la región. Pongo el código y lo comentamos luego:

For i = 1 To 8
   spriteIdle1(i) =
New Bitmap(162, 204)
   
Dim objGraphics As Graphics = Graphics.FromImage(spriteIdle1(i))
   objGraphics = Graphics.FromImage(spriteIdle1(i))
   Dim orgRect As Rectangle =
New Rectangle(aux, 1, spriteIdle1(i).Width, spriteIdle1(i).Height)
   
Dim destRect As Rectangle = New Rectangle(0, 0, 162, 204)
   objGraphics.DrawImage(bmpSpriteSheet, destRect, orgRect, GraphicsUnit.Pixel)
   aux += spriteIdle1(i).Width + 1
Next

No creo que este bucle tenga mucho misterio:

  • Se establece la dimensión para cada uno de los elementos del array de sprites
  • Se crea un objeto graphics y se "asocia" al elemento del array de sprites
  • Se calcula la región que hay que copiar
  • Se calcula la región de donde se tiene que pegar (que como véis es el tamaño completo del sprite)
  • Se copia usando el método DrawImage
  • Finalmente se incrementa una variable para que en la siguiente iteración del bucle FOR la región de origen sea la correcta

Básicamente aquí el "truco" está en cómo calcular la región de origen.

Dos apuntes para finalizar:

  1. En el código veréis que usamos una etiqueta con el fondo transparente para ir pintando los sprites uno detrás de otro
  2. Tambíen veréis que utilizamos un Timer para ir haciendo lo que comento en 1.

En la práctica esto no lo haremos así. Me explico. En la práctica usaremos el método "Overrideable" OnPaint / Invalidate que hemos visto en los tutoriales anteriores para que los sprites se dibujen a toda pastilla. Y en lugar de timers, utilizaremos "Contadores de alta precisión" con llamadas a la API del sistema (o también conocidos como QueryPerformance counters). Esto último requiere llamar a las API de Windows (pulsa aquí si quieres saber más), en concreto usando funciones de la librería kernel32.dll.

Espero que esta primera toma de contacto con la animación de sprites os haya sido útil y que por lo menos el tema de cómo copiar "trozos" entre bitmaps haya quedado aclarado 😉

 

Saludos.
mov eax,ollydbg; Int 13h

 

Descargar proyecto .NET Tutorial 6
(566 KB. Visual Studio 2008)

 

Actualización 14/11/2010: Tutorial 30. Animación sprites (Parte II)

 

.NET Tutorial 5. Sprites, gráficos y bitmaps (Parte III)

Ahora que sabemos cómo dibujar un sprite, ¿Os parece que dibujemos, no sé, 500?

Cuando querramos manejar "más de una cosa", tendremos que empezar a pensar en usar Arrays.

Arrays? WTF!!!. ¿De qué me estás hablando tronco? 😉

Un array es un conjunto de elementos que pertenecen a un mismo tipo. Por poner un ejemplo simple y fácil de entender:

Lunes, martes, miercoles, jueves, viernes, sabado, domingo son elementos de un mismo tipo, en este caso, todos son días de la semana.
Imaginad por un momento que necesitais asignar un determinado valor a cada día:
Lunes = 1
Martes = 2
Miercoles = 3
Jueves = 4
Viernes = 5
Sabado = 6
Domingo = 7

Bien, podemos hacerlo como arriba o podemos definir un array de 7 elementos, y rellenar dicho array con un bucle:

For i = 1 to 7
DiasSemana (i) = i
Next

DiasSemana es un array de 7 elementos. Según el bucle FOR, el elemento 1 vale 1, el elmento 2 vale 2…

Bien, ahora deteneos y pensad por un instante: ¿y si en lugar de 7 elementos tengo 1000?:

Habría alguien que haría lo siguiente:

Valor_1 = X
Valor_2 = X
Valor_3 = X
… y así hasta 1000   O EME GE!!!!

Si utilizamos un Array, me da lo mismo llenar 7, 70, o 1000 elementos:

For i = 1 to 1000
MiArray (i) = i
Next

No sé si lo veis pero es una grandísima ventaja.

  • Podemos tener arrays de una sola dimensión. Imagina una lista de la compra: muchas filas y una sola columna: cosas que comprar
  • Podemos tener arrays de más de una dimensión: Imagina una tabla: Varias "filas" y varias "columnas"

Los arrays también pueden tener:

  • Un número fijo de elementos
  • Un número variable de elementos

Ejemplo chorra, pero muy descriptivo:
Un array de "dias de la semana" tendrá un número fijo de elementos, ya que días hay 7 (y de momento, por mis informaciones no tienen pensado quitar ni añadir días)
Un array de "empleados de una empresa" será variable, ya que se pueden contratar y/o despedir empleados.

Quizás alguno/a esta pensando…."Pues mira, me creo un array fijo de 5000 empleados y arreando, ya que sé que nunca se va a llegar a los 5000 empleados".

Pues eso, querido lector/a, además de ser un despilfarro de memória, es programar de forma muy poco eficiente, que a la postre lo único que da son dolores de cabeza.

Cómo declarar arrays (sintaxis VB.NET).

*** Array fijo de una dimensión:
Dim MiArray (1000) As Tipo

Donde Tipo puede ser cualesquiera de los tipos de variables (Integer, Char, String, …) o incluso estructuras (incluso nuestras propias estructuras)

— Ejemplos —
Ejemplo 1:
Dim Titulos (120) As String
Titulos (1) = "Bohemian rhapsody"
Titulos (34) = "We are the champions"

Ejemplo 2:
Dim Coordenadas (500) as Point
"Point" es una estructura del .NET Framework. Esta estructura tiene dos elementos, X e Y, ambos de tipo Integer.
Coordenadas (12).X = 23
Coordenadas (12).Y = 15

Coordenadas (45).X = 67
Coordenadas (45).Y = 189

También podemos acceder a  los elementos de un array cuando este sea de tipo "estructura" de la siguiente forma:

With Coordenadas (12)
.X = 23
.Y = 15
End With

With Coordenadas (45)
.X = 67
.Y = 189
End With

Ambas formas son correctas, usad la que más os guste

Ejemplo 3: agárrense que vienen curvas 😉
Private Enum MisColores
Rojo = 1
Azul = 2
Verde = 3
End Enum
Private Structure sCaracteristicas
Dim TodoTerreno As Boolean
Dim Urbano as Boolean
Dim Deportivo As Boolean
Dim Precio As Integer
End Structure
Private Structure MiCoche
Dim Marca As String
Dim Modelo As String
Dim ColorCoche As MisColores
Dim Caracteristicas As sCaracteristicas
End Structure

Dim Coche (50) As MiCoche

Coche (2).Marca = "Seat"
Coche (2).Modelo = "Leon FR"
Coche (2).ColorCoche = MisColores.Rojo
Coche (2).Caracteristicas.TodoTerreno = False
Coche (2).Caracteristicas.Urbano = False
Coche (2).Caracteristicas.Deportivo = True
Coche (2).Caracteristicas.Precio = 20000

o bien usando With:

With Coche(2)
   .Marca = "Seat"
   .Modelo = "Leon FR"
   .ColorCoche = MisColores.Rojo
   With .Caracteristicas
      .TodoTerreno = False
      .Urbano = False
      .Deportivo = True
      .Precio = 20000
   End With
End With

***Array variable de una dimensión:
Dim MiArray () As Tipo

Donde Tipo puede ser cualesquierade los tipos de variables (Integer, Char, String, …) o inclusoestructuras (incluso nuestras propias estructuras)

Como véis, en el array variable NO se especifíca el número de elementos (por algo se llama "variable")

La ventaja de usar arrays variables es que podemos ajustar en tiempo real el tamaño para almacenar más elementos. Una vez que el tamaño está establecido accederemos al elemento n de igual forma que si se tratase de un array fijo.

Por lo tanto, lo que nos interesa aquí es como redimensionar el Array. Hay que tener en cuenta dos cosas:

  • Se puede redimensionar un array y "perder" todo lo alamcenado en el array
  • Se puede redimensionar (en este caso, aumentar)  un array y NO "perder" todo lo almacenado en el array

Para ambos casos las instrucciones son parecidas, en un caso se utiliza Redim y en otro Redim Preserve. No creo que sea necesario decir que en caso de utilizar Redim Preserve y la nueva dimensión es menor que la dimensión actual, los datos se pierden:
Tenemos un array de 20 elementos y lo "redimensionamos" a 14: Pues cicho, has perdido los últimos 6 elementos.
Sin embargo, si tenemos un array de 20 elementos y lo "redimensionamos" a 21, pues los 20 anteriores no los "pierdes".

NOTA: Por último mencionar que tanto los arrays variables como fijos, el primer elemento siempre será el cero. (esto se puede cambiar en las opciones del IDE, pero vamos a dejarlo por "defecto")
Esto quiere decir que:

Dim Titulos (120) As String

El primer elemento de este array es Titulos (0) y el último es Titulos (120). Por lo tanto, ese array tiene 121 elementos.
Cuando recorras el array con el tipico For i =, puedes empezar desde el 0 o desde el 1, si usas el elemento 0 o no. Eso ya depende de ti.

— Ejemplos —
Ejemplo 1:
Dim Titulos () As String

Redim Titulos (120)

Una vez establecida la dimensión, puedes acceder a los elementos como si se tratase de un array fijo:

Titulo (1) = "Bohemian rhapsody"
Titulo (34) = "We are the champions"

Ejemplo 2:
Dim Titulos () As String

Redim Titulos (120)

Código que "rellena" ese array

Redim Preserve Titulos (200)

Lo que hacemos aquí es primero definir que el array tiene 120 (121 si contamos el 0) elementos. Rellenamos esos 120 elementos y posteriormente aumentamos la capacidad del array a 200 elementos, pero ojo, sin perder los 120 elementos que rellenamos antes.

— Observaciones finales —

Hay que tener en cuenta un par de cosas al trabajar con arrays variables:

  • No podrás usar un Redim Preserve si el array no está inicializado, es decir, que para poder usar Redim Preserve el array tiene que tener al menos un elemento
  • Puedes usar la propiedad Length para saber cuantos elementos tiene el array. 

La propiedad Length nos servirá para saber cuantos elementos tiene nuestro array variable, pero debes tener en cuenta lo siguiente:

Dim Titulos (2) As String

¿Qué dirías que vale Titulos.Length? La respuesta correcta es 3 y no 2, ya que como hemos dicho antes, el primer elemento de cualquier array es el 0.

En este caso tenemos:

Titulos (0)
Titulos (1)
Titulos (2)

Ten esto en cuenta a la hora de rellenar o redimensionar un array variable.

Bueno, como habéis visto, lo de los 500 sprites era solo una excusa para una primera toma de contacto con los arrays. Puedo llegar a decir sin equivocarme demasiado que el uso de los arrays es "básico" en cualquier programa. Cuando estéis haciendo un programa y notéis que estáis "repitiendo" mucho código "parecido", seguro, pero casí al 99.99999999…….9%, eso mismo que estáis haciendo se podría hacer con arrays: mucho más corto, mucho más elegante, mucho más eficiente y con muchas menos posibilidades de error.

Hemos dejado en el tintero los arrays multidimensionales para otro tutorial, iremos poco a poco pero con buena letra 😉

 

Saludos.
mov eax,ollydbg; Int 13h

 

Descargar proyecto .NET Tutorial 5
(45 KB. Visual Studio 2008)

Pulsa + para añadir un sprite
Pulsa para eliminar un sprite
Pulsa ESC para finalizar

.NET Tutorial 4. Controlando lo incontrolable: Unhandled Exceptions

Ocurre en ocasiones que no se puede evitar lo inevitable. Ya lo decía Murphy: "Cualquier cosa que pueda ir mal, … irá mal".

Como norma general es buena práctica poner bloques Try / Catch en nuestro código. Esto no evitará el error en sí, pero podremos tomar "contramedidas" frente a dicho error.

Está claro que no podemos ir poniendo bloques Try / Catch linea sí  y línea no en nuestro código, ya que sino seía un caos. Pondremos estos bloques donde a priori pensamos que puede producirse un error,cómo por ejemplo aqui:

Try
Escribe_Cosas_En_El_Disco_Duro ( )
Catch

Mensaje "Ops, ocurrió el error X al escribir en el disco"
End Try

En ese caso podemos preveer que puede ocurrír cualquier "desastre" que nos impida escribir en el disco duro: El disco duro está lleno, no tienes permiso de escritura o yo que sé.

Sin embargo, a veces ocurren cosas "no esperadas", en el sitio menos esperado y donde a priori "aquello debería haber funcionado bien". En esta situación, Windows nos muestra este "bonito" mensaje:

Pues bien, lo que os mostraré hoy es bastante útil a la par que sencillo: Como guardar la información de este error "no controlado" (Unhandled Exception en inglés) en un fichero de texto que podrá ser evaluado con posterioridad.

Nota sabionda: Si distribuimos además de nuestro ejecutable, llámale Tutorial4.exe por ejemplo, distribuimos también el fichero Tutorial4.pdb, en el fichero que vamos a generar, mostrará incluso en que línea del código se ha producido el error, más ayudas imposible, oiga 😉

Pues bien, cómo decía no sé quien, menos hablar y más trabajar 😉

He creado un módulo, llamado ModUnhandledExpection. Dentro de este módulo hay un único método Publico:

Public Sub SetUP_LogUnhandledException()
AddHandler currentDomain.UnhandledException, AddressOf MYExnHandler

AddHandler Application.ThreadException, AddressOf MYThreadHandler
End Sub

Lo único que tendremos que hacer en nuestro programa es llamar al método SetUP_LogUnhandledException cuando iniciemos nuestro programa, en mi caso lo he llamado en el Form_Load.
No importa si teneis 40 formularios, 20 clases, 30 módulos, etc. Con llamar a éste método una sola vez es suficiente.

Cuando bien se produce una excepción no controlada o una excepción en un hilo de programación (Thread) se ejecutan los métodos MYExnHandler y MYThreadHandler respectivamente.

Dentro de ambos métodos lo que hay es una llamada a un procedimiento que va añadiendo la pila de llamadas (Stack Trace) en un fichero de texto llamado UnhandledLOG.txt y que se guarda en la misma carpeta donde se está ejecutando la aplicación.

Pues bien, vamos a probar que realmente esto funciona:

Nos creamos dos botones, y en ambos vamos a poner algo "mal a proposito": una división entre cero. Pero con una salvedad, en un botón pondremos un bloque Try / Catch y en el otro botón "nos hemos olvidado" de poner el bloque Try / Catch.

Obviamente al hacer esto:

Dim i As Integer = 1
Dim j As Integer = 0

Dim r As Integer

r = i /  j

El programa falla, ya que dividir entre 0 cómo que no le "gusta" al ordenador. Bueno, al ordenador y a las matemáticas en general, dividir entre 0 es una cosa "chunga", como los números chungos de Lost, pero más chungo aún 😉

Cómo vereis en el botón que tiene el bloque Try / Catch nos "enteramos" del error.
En el botón que no tiene el bloque Try / Catch se genera el fichero UnhandledLOG.txt.

Abre el fichero UnhandledLOG.txt y sorpresa: Toda la Stack Trace está almacenada, con lo cual, puedes seguir la pista de que demonios ha pasado. Es entonces cuando dices….anda oño, es verdad, pero que burro soy, cómo es que no había caído que cuando Jupiter está en conjunción con Saturno, el usuario ha puesto el valor de 4 en vez de 24 y esto ha provocado el error…Pa’vernos matao!!! 😉

 

Saludos.
mov eax,ollydbg; Int 13h 

 

Descargar proyecto .NET Tutorial 4
(67 KB. Visual Studio 2008)

.NET Tutorial 3. Sprites, gráficos y bitmaps (Parte II)

Ahora que ya sabemos dibujar un sprite sobre un fondo, vamos a dibujarlo "bien". Con "bien" me refiero a que el proceso se va a acelerar entre un 200% y un 400%.
Aunque los ordenadores hoy en día son muy potentes no está de más optimizar un poco el código, ya que en juegos o programas más complejos van a intervenir multitud de secuencias algorítmicas que tendrán en cuenta multitud de situaciones posibles. Por lo tanto, cuanto más optimicemos mejor, ya que todo irá más fluido.

Tal y cómo vimos en el Tutorial 2, utilizabamos un objeto Graphics y un objeto Bitmap.

Teniamos un Timer que cada ‘x’ milisegundos copiaba el bitmap del fondo y el bitmap del sprite al "bitmap" asociado con el objeto Graphics. Finalmente copiabamos este "bitmap" en la propiedad Image de nuestro PictureBox.

Pues bien, existe una forma muchísimo más eficiente (y por lo tanto más rápida) de conseguir el mismo resultado.

Por lo pronto vamos a prescindir del PictureBox y vamos a "pintar" directamente sobre el formulario.

También vamos a prescindir del Timer que se encargaba que llamar a la rutina de "Renderizado".

Alguno puede que se esté preguntando…¿Hmmm…si prescindimos del Timer que llama a la rutina de "Renderizado", cómo demonios vamos a ver el background y el sprite?

La respuesta de nuevo es sencilla: Lo que haremos es "Overreidear" (toma patada al diccionario de la Real Academia Española 😀 ) el procedimiento OnPaint del formulario

Si recordais en el Tutorial 1 utilizabamos esta caracterisitica de la herencia y el polimorfismo.

Pues bien, aquí haremos lo mismo, vamos a usar "nuestro" OnPaint particular para dibujar las cosas.

El evento OnPaint se ejecuta cada vez que se dibuja el formulario (¿parece de perogrullo, no?). Cuando minimizamos y luego volvemos a maximizar un formulario, se ejecuta el evento OnPaint. Está claro que no nos interesa ir minimizando y maximizando el formulario para que se ejecute el evento OnPaint.
¿Entonces, cómo haremos para que se "dispare" el evento OnPaint ?

De nuevo hay solución para esto y se llama: Invalidate.

El método Invalidate fuerza que una determinada región de un control se "repinte" para el próximo ciclo de reloj. Pues con esto ya lo tenemos. Utilizaremos Invalidate cada vez que se "mueva" nuestro sprite.

El programa funcionará de la siguiente forma:

  • Se ejecuta el Form_Load ()
  • Se cargan los gráficos de nuestro "Background" y de nuestro "Sprite"
  • Se ejecuta el Overrides OnPaint () del Formulario

Dentro del Overrides OnPaint ( ) tenemos lo siguiente:

Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
Dim dc As Graphics = e.Graphics
MoverSprite()
Draw(dc)
MyBase.OnPaint(e)
End Sub

Dentro de MoverSprite () tenemos lo siguiente:

With miSprite1
aqui se hacen cuatro cuentas para calcular la nueva posición del sprite
Invalidate(New Rectangle(.Posicion.X, .Posicion.Y, .Imagen.Width, .Imagen.Height))
End With

Donde podeis ver la llamada al método Invalidate.

Finalmente en el método Draw( ) tenemos lo siguiente:

Private Sub Draw(ByVal g As Graphics)
g.DrawImage(BackGround, 0, 0)
With miSprite1
g.DrawImage(.Imagen, .Posicion)
End With
End Sub

Como observais  lo único que se hace es copiar el background y el sprite en la posición que se calculó en el método MoverSprite.

 

Si comparais los resultados con el ejemplo del Tutorial 2, la ganancia de velocidad es muy, pero que muy apreciable 😉

 

Saludos.
mov eax,ollydbg; Int 13h 

 

Descargar proyecto .NET Tutorial 3
(145 KB. Visual Studio 2008)

Tutoriales relaccionados:
.NET Tutorial 2. Sprites, gráficos y bitmpas (Parte I)

.NET Tutorial 2. Sprites, gráficos y bitmaps (Parte I)

Pues bien, ha llegado el momento de hacer algo más "vistoso".

En esta entrega vamos a ver como es posible mover un "gráfico" sobre un determinado fondo. Tenemos que tener claro una serie de conceptos:

Background: Será la imagen de fondo de nuestro "juego"
Sprite: Serán todos aquellos elementos (en este ejemplo sólo uno) que pueden "moverse" o con los que el jugador puede interaccionar.

Todo esto lo haremos usando el GDI+ del .NET Framework. Ya habrá tiempo de meterse en camisas de once varas con XNA, OpenGL, DirectX, etc. Obviamente los resultados no son ni por asombro parecidos a usar XNA o similares, tanto en calidad como en rendimiento, pero mi idea es ir paso a paso pero con buena letra 😉

Nuestro "Background":

Nuestro "Sprite":

Resultado: (el sprite se va moviendo por la pantalla)

Para usar el GDI+ tendremos que definir dos cosas:

La superficie del GDI+ y el bitmap asociado a dicha superficie.
Básicamente lo que haremos será "copiar cosas" en el bitmap de la superficie para posteriormente copiar la superficie del GDI+ en algun sitio, por ejemplo en un PictureBox, Formulario, etc.

En este caso en particular copiaremos la superficie del GDI+ en un PictureBox, tal y cómo veremos más tarde.

Dicho esto, empecemos.

Crearemos un nuevo proyecto y en nuestro formulario que se crea por defecto insertaremos un control PictureBox, con el nombre PicRender.
Haremos un poco de "trampa" y estableceremos a mano el ancho y el alto de PicRender, en nuestro caso Size = 800; 600, es decir, 800 pixels de ancho y 600 de alto.

El programa funcionará de la siguiente forma:

  • Se ejecuta el Form_Load ()
  • Se cargan los gráficos de nuestro "Background" y de nuestro "Sprite"
  • Se inicializa un Timer que será el que se encarga de la "lógica" del juego

Dentro del Timer de la "lógica del juego" ocurre lo siguiente:

  • Se calculan las nuevas posiciones de nuestro "Sprite"
  • Se llama al método de "Renderizar" o más bien, dibujar las "cosas"

Finalmente en el método de "Renderizar" ocurre lo siguiente:

  • Copiamos nuestro "Background" al bitmap de la superfice del GDI+
  • Copiamos nuestro "Sprite" en la posición calculada en el Timer de la "lógica del juego" al bitmap de la superficie del GDI+
  • Por último, copiamos el bitmap de la superficie del GDI+ en la propiedad Image de PicRender

 

Las variables  que vamos a usar serán las que muestro a continuación.

Private objRender As Graphics      ‘Nuestra surperficie pricipal del GDI+
Private objBmpRender As Bitmap  ‘Bitmap que se copiará en la superficie

Private BackGround As Bitmap    ‘Bitmap con la imagen de fondo

Private Structure sSprite    ‘Estructura para los sprites
Dim Imagen As Bitmap  ‘Bitmap del sprite
Dim Posicion As Point     ‘Posicion (x,y) del sprite en la pantalla
End Structure

Private miSprite1 As sSprite  

Creo que las variables están claras pero hare incapié en la estructura sSprite.

Combiene que os acostumbreis a usar estructuras o en su defecto clases, o que oño, usar estructuras de clases 😉

Trabajar con este tipo de construcciones (estructuras, clases, estructuras/clases) facilita enormemente tanto la compresión del código cómo su mantenimiento. En este ejemplo de apenas 100 lineas quizas no tenga mucha importancia, pero cuando tengais códigos de cientos o incluso miles de lineas ya me contareis, ya 😉

Los gráficos los cargamos de la siguiente forma:

BackGround = Image.FromFile(App_Path() & "bitmapsbackground1.jpg")
With miSprite1
.Imagen = Image.FromFile(App_Path() & "bitmapsglow.png")
End With

Cómo veis no tiene mucho misterio, BackGround y miSprite.Imagen son objetos de tipo Bitmap, pues usamos Image.FromFile para cargar dichos "bitmaps".

Los objetos del GDI+ los inicializamos de la siguiente forma, por ejemplo en el Form_Load () :

objRender = Me.CreateGraphics()
objBmpRender =
New Bitmap(PicRender.Width, PicRender.Height)
objRender = Graphics.FromImage(objBmpRender)

En el timer de la "logica del juego" calculamos la nueva posición del sprite y llamamos a la rutina de Renderizado/dibujado:

With miSprite1
aqui se hacen cuatro cuentas para calcular la nueva posición del sprite
End With

Render(objRender)

Y finalmente, la rutina que "pinta" es esta:

Private Sub Render(ByVal g As Graphics)
g.DrawImage(BackGround, 0, 0)
   With miSprite1
g.DrawImage(.Imagen, .Posicion)
   End With
PicRender.Image = objBmpRender
End Sub

 

Pues bien, esto es todo por hoy, que ya es mucho, si teneis alguna duda o comentario, ya sabeis lo que toca 😉

 

Saludos.
mov eax,ollydbg; Int 13h 

 

Descargar proyecto .NET Tutorial 2
(147 KB. Visual Studio 2008)

Tutoriales relaccionados:
.NET Tutorial 3. Sprites, gráficos y bitmaps (Parte II)

.NET Tutorial 1. Herencia y polimorfismo (Parte I)

En esta primera entrega de esta serie de tutoriales os mostraré una técnica que puede ser realmente interesante. Aunque el nombre pueda sonar un poco "chungo" no temáis. Tito Olly está aquí para que hasta el más burro pueda entenderlo.

Hoy aprenderemos a "heredar" formularios. Alguno de vosotros se estará preguntando, ¿Y esto para que oño sirve? Pues la respuesta es muy simple y fácil: ¿Si tengo 30 formularios que hacen una tarea "parecida", por qué demonios voy a repetir un código "parecido" 30 veces?. Además, veremos que se puede modificar, crear o eliminar un elemento, procedimiento, función del formulario "heredado" y dicho elemento "repercutirá" AUTOMÁTICAMENTE en todos los formularios "heredados".

El ejemplo que os mostraré a continuación no hace nada en especial, pero lo importante es el concepto. Así que sin más dilación, empecemos.

 

Lo primero que crearemos será un nuevo proyecto. Cambiaremos el nombre del formulario por defecto y lo llamaremos FrmMain. Éste será el formulario que se ejecurará primero al iniciar nuestra aplicación.

No añadiremos de momento ningún código al formulario FrmMain, lo haremos más adelante.

Bien, añadiremos otro formulario a nuestra aplicación. Lo llamaremos FrmBASE. Este formulario será el que vamos a heredar.

A este  formulario le meteremos dos botones, tal y como se ve en la figura anterior.

Bien, aquí empieza la "magia". En el código del formulario FrmBASE incluiremos lo siguiente:

Cómo veis hay un procedimiento "Overridable" que básicamente lo único que hace es mostrar un texto.
El botón 1 muestra el mensaje "Este mensaje 1 está en el FrmBASE"
El botón 2 muestra el mensaje que se ha pasado al procedimiento "Overridable"

Bien, pues ahora comienza lo bueno. Añadimos un nuevo formulario, que llamaremos Form1pero ojo, no cómo un formulario "normal", sino que será un formulario "heredado":

El asistente nos preguntará que formulario queremos heredar. En nuestro caso heredaremos el formulario FrmBASE.

Al crearse este formulario vereis que AUTOMÁTICAMENTE nos ha colocado los dos botones que tenemos en el FrmBASE. Además observamos que no podemos hacer "click" en estos botones.

Añadimos una etiqueta con el texto "FORM1" a este formulario

Observar que en este formulario NO VAMOS A ESCRIBIR ninguna línea de código.

Añadimos otro formulario, Form2, del mismo modo que Form1, es decir, heredado del FrmBASE.

Después de añadir una etiqueta con el texto "FORM2" iremos al código y escribieremos esto:

Aquí podeis ver que se hace hace una llamada al procedimiento "Overridable" que estaba en el formulario FrmBASE, con la particularidad de que en un formulario que hereda a otro se especifica como "Overrides".

Lo que hace es "cambiar" el argumento del prodecimiento Mensaje. Luego lo vereis mejor.

Volvemos a nuestro formulario principal, FrmMain. Añadimos dos botones:

En el código del formulario FrmMain, para cada botón llamamos al Form1 y al Form2 respectivamente:

Pues bien, llegó el gran momento, vamos a compilar y ejecutar esta "mega" aplicación.

Se muestra el formulario FrmMain. Pulsamos sobre el botón "Form1". Se muestra el Form1. Pulsamos sobre el botón "Mensaje 1" y obtenemos esto:

Al pulsar sobre el botón "Mensaje 2" vemos lo siguiente:

WTF!!!. ¿Cómo oño es esto posible si el Form1 NO TIENE NADA DE CÓDIGO?

La explicación corta: Herencia.

La explicación "larga": Cómo recordareis cuando creamos el Form1 le dijimos que era un formulario heredado de FrmBASE. Pues bien, la gracia de la herencia es esa. Form1 ha "heredado" todos los compartamientos de FrmBASE. Por lo tanto, si hacemos "click" en el botón "Mensaje 1" del Form1 es como si realmente estuviesemos haciendo "click" en el botón "Mensaje 1" del FrmBASE.

Bien, vamos a acabar de "liarla parda". Pulsamos sobre el botón "Form2". Se muestra el Form2. Pulsamos sobre el botón "Mensaje 1" y obtenemos esto:

Cómo veis se muestra el mismo mensaje que se mostraba en el Form1. Lo cachondo es que en el Form2 tampoco tenemos ningún código para el "click" del botón del "Mensaje 1". Se hereda de FrmBASE.

Y ahora lo realmente lo bueno. Si pulsamos el botón del "Mensaje 2" vemos lo siguiente:

WTF!!!. ¿Cómo oño es esto posible si el Form2 NO TIENE NADA DE CÓDIGO para el "click" del botón "Mensaje 2"?

La explicación corta: Polimorfismo.

La explicación "larga": Al igual que el Form1, el Form2 se ha heredado del FrmBASE. Si recordais, dentro del FrmBASE teniamos un procedimiento "Overridable". Este procedimiento se llamaba al hacer "click" en el botón del "Mensaje 2". Pues bien, lo que hemos hecho nosotros en nuestro Form2 ha sido "cambiar" este procedimiento "Overridable" y pasarle un argumento particular para el Form2.

Para finalizar, deciros que si ahora me da por cambiar, no sé, el color de fondo del formulario FrmBASE y dejarlo así:

Al ejecutar de nuevo el programa, Y SIN TOCAR NI UNA LÍNEA DE CÓDIGO, tanto el Form1 como el Form2 están de color verde, gracias a la "Herencia"

 

COROLARIO.
Cómo comenté al principio esta aplicación "no hace nada", pero lo importante es la idea, el concepto.
Imaginaos por un momento que teneis no dos formularios, sino cuarenta.
Imaginaos que esos 40 formularios son formularios de "altas" de productos, de clientes, de proveedores, de empleados, etc. Básicamente todos esos formularios son "iguales", en uno se da de "alta" (en una base de datos, por ejemplo) productos, en otro se dan de "alta" clientes, en otro proveedores, etc.
Podemos tener un formulario "base" que se encarga de INSERTar los datos en la base de datos, con un procedimiento "Overridable" que en función de uno u otro formulario cargue la sentencia SQL adecuada: Un formulario usará un "Overrides" para el alta de productos, otro para el alta de clientes, etc.

Además, imaginaos que a posteriori quereis que en esos cuarenta formularios exista una opción para borrar un determinado producto, un determinado cliente, un determinado proveedor, etc. Pues bien, con solo añadir un botón eliminar y un "Overridable" para hacer un DELETE de la base de datos, automáticamente se "propagará" a los cuarenta formularios.

O yo que sé, que a tu jefe le da por decir, "Oye, quiero un logotipo todo chulo en esos cuarenta formularios", esas letras las quiero azules, aquí esto lo quiero en negrita, alli lo quiero de color verde, etc, etc. Pues fácil y rápido, Metes el logotipo en el formulario "Base" o lo que sea y ha correr que es gerundio….¿o no era así el refran? 😉

 

Saludos.
mov eax,ollydbg; Int 13h

 

Descargar proyecto .NET Tutorial 1
(74 Kb. Visual Studio 2008)