El trabajo propuesto

Deberá aparecer un enemigo (por ejemplo, el escorpión), que se mueva al azar por la pantalla. Sólo podrá moverse por los "huecos" de cada pantalla (espacio en blanco y escaleras). Deberá moverse más despacio que nuestro personaje (por ejemplo, cambiar de posición sólo una vez cada 10 "fotogramas" del juego). Cuando el personaje cambie de pantalla, el enemigo deberá aparecer en una posición al azar (libre) de la nueva pantalla.

De momento, no se comprobarán colisiones entre nuestro personaje y el enemigo.

Forma de conseguirlo

La función de colocar el enemigo al azar se limitará a calcular unas coordenadas X e Y para el enemigo, y volver a intentar si esta posición no es "pisable" (si no es un espacio en blanco o una escalera):

static void colocarEnemigo() 
{ 
    // Coloca el enemigo en cualquier posición "libre" de la pantalla actual 
    do  
    { 
        xEnemigo = (short) (numAleatorio.Next(0,columnasPantalla-2)+1); 
        yEnemigo = (short) (numAleatorio.Next(0,filasPantalla-2)+1); 
    } 
    while ((mapa[filaHabitacion, columnaHabitacion,  
          yEnemigo,xEnemigo] != 0)  // Espacio en blanco 
        && (mapa[filaHabitacion, columnaHabitacion,  
          yEnemigo,xEnemigo] != 4)); // Escalera 
 
}

Para mover al enemigo de forma independiente del jugador, usaremos la función "moverElementos", que se llama en cada pasada del "buclePrincipal". Si queremos que el enemigo no se mueva tan rápido como nosotros, una forma sencilla de conseguirlo es que sólo se mueva cada cierta cantidad de fotogramas, así:

// El enemigo solo se mueve un fotograma de cada varios
contadorFotogramasEnemigo ++;
if (contadorFotogramasEnemigo == pausaFotogramasEnemigo)
{ 
    contadorFotogramasEnemigo = 0;
    xEnemigo += incrXEnemigo;
    yEnemigo += incrYEnemigo;
}

Y si queremos que se mueva al azar, podemos elegir una posición cada vez que tenga que moverse (por ejemplo, con un número al azar del 1 al 4). Si la siguiente casilla en esa posición está libre, cambiamos los valores de los incrementos de X y de Y para que se mueva hacia allá. En un caso real, puede ser mejor tomar un número del 1 al 100, para poder ajustar las probabilidades si queremos que sea más probable que el enemigo se mueva en un sentido o en otro (por ejemplo, un 35% de posibilidades de ir a la derecha, un 35% a la izquierda, pero sólo un 20% hacia arriba y un 10% hacia abajo). Nosotros por ahora generaremos un número del 1 al 100, pero dejaremos un 25% de probabilidad de moverse en cada dirección, para que ningún movimiento predomine sobre los demás:

// Para la siguiente posición, escojo al azar y miro si  realmente se puede mover 
bool posibleMover = false;
do 
{
    int numeroAzar = (int) numAleatorio.Next(0,100);
    if (numeroAzar < 25) // Intento derecha
    {
        if ((xEnemigo < maxX) &&  // Si no ha llegado al borde
            ((mapa[filaHabitacion, columnaHabitacion, 
              yEnemigo,xEnemigo+1] == 0)  // Espacio en blanco
            || (mapa[filaHabitacion, columnaHabitacion, 
              yEnemigo,xEnemigo+1] == 4)) )// Escalera
        {
            incrXEnemigo = 1;
            incrYEnemigo = 0;
            posibleMover = true;
        }
    }  
    else if (numeroAzar < 50) // Intento izquierda 
    { 
        ... 
 
} while (! posibleMover);


Todo junto, podría ser algo como

/** 
 *   Juego: Logica de juego
 *  
 *   @see Hardware
 *   @author 1-DAI IES San Vicente 2009/10
 */
 
/* --------------------------------------------------         
   Parte de Death Pit - Remake
   Versiones hasta la fecha:
 
   Num.   Fecha       Por / Cambios
   ---------------------------------------------------
   0.01  08-Sep-2009  Nacho Cabanes
                      Version inicial: muestra una imagen
   0.02  07-Oct-2009  Nacho Cabanes
                      Mueve el personaje hacia la derecha
                        hasta llegar al margen
   0.03  13-Oct-2009  Nacho Cabanes
                      El personaje se mueve con flechas
                      Un primer enemigo que se mueve a la vez
   0.04  16-Oct-2009  Nacho Cabanes
                      Primera pantalla de presentacion
                      El primer enemigo rebota en los lados
                      Un segundo enemigo nos persigue
   0.05  20-Oct-2009  Nacho Cabanes
                      Array de enemigos (murcielagos)
                       que rebotan en los lados
   0.06  28-Oct-2009  Nacho Cabanes
                      Array bidimensional para imagen de fondo
   0.07  04-Nov-2009  Nacho Cabanes
                      El personaje se mueve solo por los "huecos"
                      Eliminados los enemigos (por ahora)
                      Renombradas variables i,j a fila,colum
   0.08  10-Nov-2009  Nacho Cabanes
                      Varias funciones para que el fuente sea mas modular
   0.09  16-Nov-2009  Nacho Cabanes
                      Varias pantallas conectadas
   0.10  24-Nov-2009  Nacho Cabanes
                      Un enemigo que se mueve "por los huecos"
 ---------------------------------------------------- */
 
 using System; // Para numeros aleatorios: System.Random
 
public class Juego
{
 
    // Variables que usaremos: una imagen y un tipo de letra
    static ElemGrafico imagenPersonaje,imagenEnemigo,
      presentacion;
    static ElemGrafico imagenFondoHoriz,imagenFondoHorizAcido,
      imagenFondoVert, imagenEscalera;
    static Fuente fuente18;
    static int fila, colum;  // Para recorrer 
 
    static short x=1, y=1;
    static short xEnemigo=1, yEnemigo=1;
    static short incrXEnemigo=1, incrYEnemigo=0;
    static short contadorFotogramasEnemigo = 0;
    static short pausaFotogramasEnemigo = 10;
    static short minX = 0, maxX = 6,
      minY = 0, maxY = 6;
 
    // Tamaño de cada pantalla de juego
    static int filasPantalla = 7;    
    static int columnasPantalla = 7;
 
    // Situación de la pantalla actual dentro del mapa de habitaciones
    static int filaHabitacion = 0;
    static int columnaHabitacion = 0;
 
    static Random numAleatorio; // Para que el enemigo se mueva al azar
 
    static int[,,,] mapa = 
    {      // Comienzo del mapa de casillas
      {    // Primera fila de habitaciones
        {  // Habitacion (0,0)
          {1,2,1,1,1,2,1},
          {3,0,0,0,0,0,0},
          {3,4,3,4,3,1,1},
          {3,4,0,4,0,0,0},
          {3,4,3,4,3,4,3},
          {3,4,0,0,0,4,3},
          {3,1,1,4,1,4,3}
        },
        {  // Habitacion (0,1)
          {1,2,1,1,1,2,1},
          {0,0,0,0,0,0,3},
          {1,1,3,4,3,4,3},
          {0,0,3,4,3,4,3},
          {3,4,3,4,3,4,3},
          {3,4,3,4,3,4,3},
          {3,1,3,4,3,4,3}
        },
      },
      {    // Segunda fila de habitaciones
        {  // Habitacion (1,0)
          {3,2,1,4,1,4,1},
          {3,0,0,0,0,0,0},
          {3,1,1,4,3,1,1},
          {3,0,0,4,3,0,0},
          {3,4,3,4,3,4,3},
          {3,4,3,4,0,4,3},
          {1,1,1,1,1,1,3}
        },
        {  // Habitacion (1,1)
          {1,2,1,4,1,4,1},
          {0,0,0,0,0,0,3},
          {1,1,1,4,1,1,3},
          {0,0,0,4,0,0,3},
          {1,4,3,4,3,4,3},
          {3,4,3,4,0,4,3},
          {1,1,1,1,1,1,3}
        },
      },
    };
 
    static bool partidaTerminada;
 
    static void inicializar()
    {
        // Inicializo modo grafico 800x600 puntos, 24 bits de color
        Hardware.Inicializar(800, 600, 24);
 
        // Cargo imagenes y tipos de letra
        imagenPersonaje = new ElemGrafico("imagenes/personaje.png");
        imagenEnemigo = new ElemGrafico("imagenes/escorpion.png");
 
        imagenFondoHoriz = new ElemGrafico("imagenes/fondo1.png");
        imagenFondoHorizAcido = new ElemGrafico("imagenes/fondo2.png");
        imagenFondoVert = new ElemGrafico("imagenes/fondo3.png");
        imagenEscalera = new ElemGrafico("imagenes/escalera.png");
 
        fuente18 = new Fuente("FreeSansBold.ttf", 18);
        presentacion = new ElemGrafico("imagenes/present.png");
 
        // Para que el enemigo se mueva al azar
        numAleatorio = new Random( DateTime.Now.Millisecond );
        colocarEnemigo();
    }
 
    static void mostrarPresentacion()
    {
        //dibujo la presentacion
        presentacion.DibujarOculta();
 
        // Finalmente, muestro en pantalla
        Hardware.VisualizarOculta();  
 
        //hasta que se pulse espacio
        do {
 
        } while (! Hardware.TeclaPulsada(Hardware.TECLA_ESP) );
 
        //borro la presentacion
        Hardware.BorrarPantallaOculta(0,0,0);    
    }
 
    static void comprobarTeclas()
    {
          // Y lo muevo si se pulsa alguna flecha del teclado
          if (Hardware.TeclaPulsada(Hardware.TECLA_DER) )
            if (x == maxX)
            {
              x = 0;
              columnaHabitacion++;
              colocarEnemigo();
            }
            else if (x < maxX)
              if ((mapa[filaHabitacion, columnaHabitacion, y,x+1] == 0)  // Espacio en blanco
                || (mapa[filaHabitacion, columnaHabitacion, y,x+1] == 4) )// Escalera
                  x++;
 
          if(Hardware.TeclaPulsada(Hardware.TECLA_ARR) )
            if (y == 0)
            {
              y = maxY;
              filaHabitacion--;
              colocarEnemigo();
            }
            else if ( y > 0)
              if ((mapa[filaHabitacion, columnaHabitacion, y-1,x] == 0)  // Espacio en blanco
                || (mapa[filaHabitacion, columnaHabitacion, y-1,x] == 4) )// Escalera
                  y--;
 
          if (Hardware.TeclaPulsada(Hardware.TECLA_IZQ) )
            if (x == 0)
            {
              x = maxX;
              columnaHabitacion--;
              colocarEnemigo();
            }
            else if ( x > 0)            
              if ((mapa[filaHabitacion, columnaHabitacion, y,x-1] == 0)  // Espacio en blanco
                || (mapa[filaHabitacion, columnaHabitacion, y,x-1] == 4) )// Escalera
                  x--;
 
 
          if (Hardware.TeclaPulsada(Hardware.TECLA_ABA) )
            if (y == maxY)
            {
              y = 0;
              filaHabitacion++;
              colocarEnemigo();
            }
            else 
            if (y < maxY)
              if ((mapa[filaHabitacion, columnaHabitacion, y+1,x] == 0)  // Espacio en blanco
                || (mapa[filaHabitacion, columnaHabitacion, y+1,x] == 4) )// Escalera
                  y++;   
 
          if (Hardware.TeclaPulsada(Hardware.TECLA_ESC))
              partidaTerminada = true;
    }
 
    static void colocarEnemigo()
    {
        // Coloca el enemigo en cualquier posición "libre" de la pantalla actual
        do 
        {
            xEnemigo = (short) (numAleatorio.Next(0,columnasPantalla-2)+1);
            yEnemigo = (short) (numAleatorio.Next(0,filasPantalla-2)+1);
        } 
        while ((mapa[filaHabitacion, columnaHabitacion, 
              yEnemigo,xEnemigo] != 0)  // Espacio en blanco
            && (mapa[filaHabitacion, columnaHabitacion, 
              yEnemigo,xEnemigo] != 4)); // Escalera
 
    }
 
    static void moverElementos()
    {
        // El enemigo solo se mueve un fotograma de cada varios
        contadorFotogramasEnemigo ++;
        if (contadorFotogramasEnemigo == pausaFotogramasEnemigo)
        { 
            contadorFotogramasEnemigo = 0;
            xEnemigo += incrXEnemigo;
            yEnemigo += incrYEnemigo;
        }
 
        // Para la siguiente posición, escojo al azar y miro si realmente se puede mover
        bool posibleMover = false;
        do 
        {
            int numeroAzar = (int) numAleatorio.Next(0,100);
            if (numeroAzar < 25) // Intento derecha
            {
                if ((xEnemigo < maxX) &&  // Si no ha llegado al borde
                    ((mapa[filaHabitacion, columnaHabitacion, 
                      yEnemigo,xEnemigo+1] == 0)  // Espacio en blanco
                    || (mapa[filaHabitacion, columnaHabitacion, 
                      yEnemigo,xEnemigo+1] == 4)) )// Escalera
                {
                    incrXEnemigo = 1;
                    incrYEnemigo = 0;
                    posibleMover = true;
                }
            } 
            else if (numeroAzar < 50) // Intento izquierda
            {
                if ((xEnemigo > 0) &&  // Si no ha llegado al borde
                     ((mapa[filaHabitacion, columnaHabitacion, 
                      yEnemigo,xEnemigo-1] == 0)  // Espacio en blanco
                     || (mapa[filaHabitacion, columnaHabitacion, 
                      yEnemigo,xEnemigo-1] == 4)) )// Escalera
                {
                    incrXEnemigo = -1;
                    incrYEnemigo = 0;
                    posibleMover = true;
                }
            } 
            else if (numeroAzar < 75) // Intento arriba
            {
                if ((yEnemigo > 0) &&  // Si no ha llegado al borde
                    ((mapa[filaHabitacion, columnaHabitacion, 
                      yEnemigo-1,xEnemigo] == 0)  // Espacio en blanco
                    || (mapa[filaHabitacion, columnaHabitacion, 
                      yEnemigo-1,xEnemigo] == 4)) )// Escalera
                {
                    incrXEnemigo = 0;
                    incrYEnemigo = -1;
                    posibleMover = true;
                }
            }
            else // Intento abajo
            {
                if ((yEnemigo < maxY) &&  // Si no ha llegado al borde
                    ((mapa[filaHabitacion, columnaHabitacion, 
                      yEnemigo+1,xEnemigo] == 0)  // Espacio en blanco
                    || (mapa[filaHabitacion, columnaHabitacion, 
                      yEnemigo+1,xEnemigo] == 4)) )// Escalera
                {
                    incrXEnemigo = 0;
                    incrYEnemigo = 1;
                    posibleMover = true;
                }
            }
 
        } while (! posibleMover);
    }
 
    static void comprobarColisiones()
    {
        // Nada que comprobar en esta version
    }
 
    static void dibujarElementos()
    {
          Hardware.BorrarPantallaOculta(0,0,0);
 
          // Dibujo el fondo de la pantalla
          for (fila=0; fila<filasPantalla; fila++)
            for (colum=0; colum<columnasPantalla; colum++)
              switch (mapa[filaHabitacion, columnaHabitacion, fila,colum]) {
 
                case 0:  // Espacio en blanco, no dibujo nada
                    break;
 
                case 1:
                    imagenFondoHoriz.MoverA( (short) (colum*70) , (short) (fila*50));
                    imagenFondoHoriz.DibujarOculta();
                    break;
 
                case 2:
                    imagenFondoHorizAcido.MoverA( (short) (colum*70) , (short) (fila*50));
                    imagenFondoHorizAcido.DibujarOculta();
                    break;
 
                case 3:
                    imagenFondoVert.MoverA( (short) (colum*70) , (short) (fila*50));
                    imagenFondoVert.DibujarOculta();
                    break;
 
                case 4:
                    imagenEscalera.MoverA( (short) (colum*70) , (short) (fila*50));
                    imagenEscalera.DibujarOculta();
                    break;
 
              }
 
          // Dibujo el personaje, el enemigo y un texto en la pantalla oculta
          imagenPersonaje.MoverA( (short) (x*70) , (short) (y*50) );
          imagenPersonaje.DibujarOculta();
 
          imagenEnemigo.MoverA( (short) (xEnemigo*70) , (short) (yEnemigo*50) );
          imagenEnemigo.DibujarOculta();
 
          Hardware.EscribirTextoOculta(
                "Pulsa ESC para salir",
                300, 500, 0xAA, 0xAA, 0xAA, fuente18);
 
          // Finalmente, muestro en pantalla
          Hardware.VisualizarOculta();        
 
    }
 
    static void pausaFotograma() 
    {
        // Pausa para 25 fps
        Hardware.Pausa( 40 );
    }
 
 
    static void buclePrincipal() 
    {
      partidaTerminada = false;
      do {
        comprobarTeclas();
        moverElementos();
        comprobarColisiones();
        dibujarElementos();
        pausaFotograma();
      } while (! partidaTerminada);
    }
 
    public static void Main() 
    {
      inicializar();
      mostrarPresentacion();
      buclePrincipal();
    }
 
} /* fin de la clase Juego */
 

Siguiente entrega...